音視頻學(xué)習(xí)筆記

音視頻一些筆記

  • 序列參數(shù)集SPS:作用于一系列連續(xù)的編碼圖像研铆;
  • 圖像參數(shù)集PPS:作用于編碼視頻序列中一個或多個獨立的圖像埋同;

1.調(diào)用VTCompressionSessionCreate創(chuàng)建編碼session,然后調(diào)用VTSessionSetProperty設(shè)置參數(shù)棵红,最后調(diào)用VTCompressionSessionPrepareToEncodeFrames開始編碼凶赁;

  int width = 480, height = 640;
  OSStatus status = VTCompressionSessionCreate(NULL, width, height, kCMVideoCodecType_H264, NULL, NULL, NULL, didCompressH264, (__bridge void *)(self),  &EncodingSession);

2.調(diào)用VTCompressionSessionEncodeFrame傳入需要編碼的視頻幀,如果返回失敗逆甜,調(diào)用VTCompressionSessionInvalidate銷毀session虱肄,然后釋放session;

CVImageBufferRef imageBuffer = (CVImageBufferRef)CMSampleBufferGetImageBuffer(sampleBuffer);
    // 幀時間交煞,如果不設(shè)置會導(dǎo)致時間軸過長咏窿。
   CMTime presentationTimeStamp = CMTimeMake(frameID++, 1000);
   VTEncodeInfoFlags flags;
   OSStatus statusCode = VTCompressionSessionEncodeFrame(EncodingSession,
                                                          imageBuffer,
                                                        presentationTimeStamp,
                                                        kCMTimeInvalid,
                                                         NULL, NULL, &flags);

3、每一幀視頻編碼完成后會調(diào)用預(yù)先設(shè)置的編碼函數(shù)didCompressH264素征,如果是關(guān)鍵幀需要用CMSampleBufferGetFormatDescription獲取CMFormatDescriptionRef集嵌,然后用CMVideoFormatDescriptionGetH264ParameterSetAtIndex取得PPS和SPS;最后把每一幀的所有NALU數(shù)據(jù)前四個字節(jié)變成0x00 00 00 01之后再寫入文件稚茅。

       OSStatus statusCode = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(format, 1, &pparameterSet, &pparameterSetSize, &pparameterSetCount, 0 );
            if (statusCode == noErr)
            {
                // Found pps
                NSData *sps = [NSData dataWithBytes:sparameterSet length:sparameterSetSize];
                NSData *pps = [NSData dataWithBytes:pparameterSet length:pparameterSetSize];
                if (encoder)
                {
                    [encoder gotSpsPps:sps pps:pps];
                }
            }

4纸淮、調(diào)用VTCompressionSessionCompleteFrames完成編碼,然后銷毀session:VTCompressionSessionInvalidate亚享,釋放session咽块。

把原始碼流包裝成CMSampleBuffer

1.用CMBlockBuffer把NALUnit包裝起來

CMBlockBufferRef blockBuffer = NULL;
        OSStatus status  = CMBlockBufferCreateWithMemoryBlock(kCFAllocatorDefault,
                                                              (void*)packetBuffer, packetSize,
                                                              kCFAllocatorNull,
                                                              NULL, 0, packetSize,
                                                              0, &blockBuffer);

2、把SPS和PPS包裝成CMVideoFormatDescription欺税;

 const uint8_t* parameterSetPointers[2] = {mSPS, mPPS};
 const size_t parameterSetSizes[2] = {mSPSSize, mPPSSize};
 OSStatus status = CMVideoFormatDescriptionCreateFromH264ParameterSets(kCFAllocatorDefault                                                                             2, //param count                                                                      parameterSetPointers,                                                                           parameterSetSizes,                                                                           4, //nal start code size                                                                            &mFormatDescription);

3.創(chuàng)建CMSampleBuffer侈沪;

  CMSampleBufferRef sampleBuffer = NULL;
  const size_t sampleSizeArray[] = {packetSize};
  status = CMSampleBufferCreateReady(kCFAllocatorDefault,blockBuffer,
                                             mFormatDescription,
                                         1, 0, NULL, 1, sampleSizeArray,
                                           &sampleBuffer);

解碼并顯示

1揭璃、傳入CMSampleBuffer

 VTDecodeFrameFlags flags = 0;
                VTDecodeInfoFlags flagOut = 0;
                // 默認是同步操作。
                // 調(diào)用didDecompress亭罪,返回后再回調(diào)
                OSStatus decodeStatus = VTDecompressionSessionDecodeFrame(mDecodeSession,
                                                                          sampleBuffer,
                                                                          flags,
                                                                          &outputPixelBuffer,
                                                                          &flagOut);

2瘦馍、回調(diào)didDecompress

void didDecompress(void *decompressionOutputRefCon, void *sourceFrameRefCon, OSStatus status, VTDecodeInfoFlags infoFlags, CVImageBufferRef pixelBuffer, CMTime presentationTimeStamp, CMTime presentationDuration ){
    CVPixelBufferRef *outputPixelBuffer = (CVPixelBufferRef *)sourceFrameRefCon;
    *outputPixelBuffer = CVPixelBufferRetain(pixelBuffer);
}

3、顯示解碼的結(jié)果

[self.mOpenGLView displayPixelBuffer:pixelBuffer];

當(dāng)遇到IDR幀時应役,更合適的做法是通過

VTDecompressionSessionCanAcceptFormatDescription判斷原來的session是否能接受新的SPS和PPS情组,如果不能再新建session。

PCM通過抽樣箩祥、量化院崇、編碼三個步驟將連續(xù)變化的模擬信號轉(zhuǎn)換為數(shù)字編碼。

  • 抽樣:對模擬信號進行周期性掃描袍祖,把時間上連續(xù)的信號變成時間上離散的信號礁苗;
  • 量化:用一組規(guī)定的電平感凤,把瞬時抽樣值用最接近的電平值來表示,通常是用二進制表示;
  • 編碼:用一組二進制碼組來表示每一個有固定電平的量化值;

iOS上把PCM音頻編碼成AAC音頻流

  • 1卦溢、設(shè)置編碼器(codec)箫荡,并開始錄制特咆;
  • 2泽腮、收集到PCM數(shù)據(jù),傳給編碼器村视;
  • 3官套、編碼完成回調(diào)callback,寫入文件蚁孔。

1奶赔、創(chuàng)建并配置AVCaptureSession

  • (void)startCapture {
    self.mCaptureSession = [[AVCaptureSession alloc] init];
    mCaptureQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    mEncodeQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
       
    AVCaptureDevice *audioDevice = [[AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio] lastObject];
    self.mCaptureAudioDeviceInput = [[AVCaptureDeviceInput alloc]initWithDevice:audioDevice error:nil];
    if ([self.mCaptureSession canAddInput:self.mCaptureAudioDeviceInput]) {
        [self.mCaptureSession addInput:self.mCaptureAudioDeviceInput];
    }
    self.mCaptureAudioOutput = [[AVCaptureAudioDataOutput alloc] init];
    
    if ([self.mCaptureSession canAddOutput:self.mCaptureAudioOutput]) {
        [self.mCaptureSession addOutput:self.mCaptureAudioOutput];
    }
    [self.mCaptureAudioOutput setSampleBufferDelegate:self queue:mCaptureQueue];
    
    NSString *audioFile = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"abc.aac"];
    [[NSFileManager defaultManager] removeItemAtPath:audioFile error:nil];
    [[NSFileManager defaultManager] createFileAtPath:audioFile contents:nil attributes:nil];
    audioFileHandle = [NSFileHandle fileHandleForWritingAtPath:audioFile];
    
    [self.mCaptureSession startRunning];
    }
    

    2、創(chuàng)建轉(zhuǎn)換器
    AudioStreamBasicDescription是輸出流的結(jié)構(gòu)體描述杠氢,
    配置好outAudioStreamBasicDescription后站刑,
    根據(jù)AudioClassDescription(編碼器),
    調(diào)用AudioConverterNewSpecific創(chuàng)建轉(zhuǎn)換器鼻百。
    (void) setupEncoderFromSampleBuffer:(CMSampleBufferRef)sampleBuffer {
    AudioStreamBasicDescription inAudioStreamBasicDescription = *CMAudioFormatDescriptionGetStreamBasicDescription((CMAudioFormatDescriptionRef)CMSampleBufferGetFormatDescription(sampleBuffer));

    AudioClassDescription *description = [self  getAudioClassDescriptionWithType:kAudioFormatMPEG4AAC
                    fromManufacturer:kAppleSoftwareAudioCodecManufacturer]; //軟編
    
    OSStatus status = AudioConverterNewSpecific(&inAudioStreamBasicDescription, &outAudioStreamBasicDescription, 1, description, &_audioConverter); // 創(chuàng)建轉(zhuǎn)換器
    
    }
    

編解碼器(codec)指的是一個能夠?qū)σ粋€信號或者一個數(shù)據(jù)流進行變換的設(shè)備或者程序绞旅。這里指的變換既包括將 信號或者數(shù)據(jù)流進行編碼(通常是為了傳輸、存儲或者加密)或者提取得到一個編碼流的操作温艇,也包括為了觀察或者處理從這個編碼流中恢復(fù)適合觀察或操作的形式的操作因悲。編解碼器經(jīng)常用在視頻會議和流媒體等應(yīng)用中。

3勺爱、獲取到PCM數(shù)據(jù)并傳入編碼器

用CMSampleBufferGetDataBuffer獲取到CMSampleBufferRef里面的CMBlockBufferRef晃琳,

再通過CMBlockBufferGetDataPointer獲取到pcmBufferSize和pcmBuffer;

調(diào)用AudioConverterFillComplexBuffer傳入數(shù)據(jù),并在callBack函數(shù)調(diào)用填充buffer的方法卫旱。

CMBlockBufferRef blockBuffer = CMSampleBufferGetDataBuffer(sampleBuffer);
       CFRetain(blockBuffer);
OSStatus status = CMBlockBufferGetDataPointer(blockBuffer, 0, NULL, &_pcmBufferSize, &_pcmBuffer);
status = AudioConverterFillComplexBuffer(_audioConverter, inInputDataProc, (__bridge void *)(self), &ioOutputDataPacketSize, &outAudioBufferList, outPacketDescription);
  • */
    

    OSStatus inInputDataProc(AudioConverterRef inAudioConverter, UInt32 *ioNumberDataPackets, AudioBufferList *ioData, AudioStreamPacketDescription **outDataPacketDescription, void *inUserData)
    {
    AACEncoder *encoder = (__bridge AACEncoder *)(inUserData);
    UInt32 requestedPackets = *ioNumberDataPackets;

      size_t copiedSamples = [encoder copyPCMSamplesIntoBuffer:ioData];
      if (copiedSamples < requestedPackets) {
          //PCM 緩沖區(qū)還沒滿
          *ioNumberDataPackets = 0;
          return -1;
      }
      *ioNumberDataPackets = 1;
      
      return noErr;
    

    }

    /**

    • 填充PCM到緩沖區(qū)
      */

    • (size_t) copyPCMSamplesIntoBuffer:(AudioBufferList*)ioData {
      size_t originalBufferSize = _pcmBufferSize;
      if (!originalBufferSize) {
      return 0;
      }
      ioData->mBuffers[0].mData = _pcmBuffer;
      ioData->mBuffers[0].mDataByteSize = (int)_pcmBufferSize;
      _pcmBuffer = NULL;
      _pcmBufferSize = 0;
      return originalBufferSize;
      }

    
    
    
    

Audio Queue Services的播放步驟如下:

  • 1人灼,給buffer填充數(shù)據(jù),并把buffer放入就緒的buffer queue顾翼;
  • 2投放,應(yīng)用通知隊列開始播放;
  • 3适贸、隊列播放第一個填充的buffer灸芳;
  • 4、隊列返回已經(jīng)播放完畢的buffer拜姿,并開始播放下面一個填充好的buffer耗绿;
  • 5、隊列調(diào)用之前設(shè)置的回調(diào)函數(shù)砾隅,填充播放完畢的buffer;
  • 6债蜜、回調(diào)函數(shù)中把buffer填充完畢晴埂,并放入buffer queue中。

/********FFMpeg從入門到精通********/

花上幾天學(xué)一個可以用幾十年的技術(shù)是何等高的學(xué)習(xí)“性價比”寻定。

//生成的視頻結(jié)果是保留視頻的上半部分儒洛,同時上半部分會鏡像到視頻的下半部分,二者合成之后作為輸出視頻
   ?相同的Filter線性鏈之間用逗號分隔   
   ?不同的Filter線性鏈之間用分號分隔


./ffmpeg -i INPUT -vf "split [main][tmp]; [tmp] crop=iw:ih/2:0:0, vflip [flip]; [main][flip] overlay=0:H/2" OUTPUT

ffmpeg -i /Users/zly/Desktop/login_video.mp4 -vf "split [main][tmp]; [tmp] crop=iw:ih/2:0:0, vflip [flip]; [main][flip] overlay=0:H/2" /Users/zly/Desktop/login.mp4

ffmpeg--help查看到的help信息是ffmpeg命令的基礎(chǔ)信息

想獲得高級參數(shù)部分狼速,那么可以通過使用ffmpeg--help long參數(shù)來查看

希望獲得全部的幫助信息琅锻,那么可以通過使用ffmpeg--help full參數(shù)來獲得。

如果要進行格式化的顯示向胡,這樣就需要用到ffprobe-print_format或者ffprobe-of參數(shù)來進行相應(yīng)的格式輸出恼蓬,而-print_format支持多種格式輸出,包括XML僵芹、INI处硬、JSON、CSV拇派、FLAT等荷辕。

ffprobe -of xml -show_streams input.flv
ffprobe-of ini-show_streams input.flv
ffprobe-of flat-show_streams input.flv
ffprobe-of json-show_packets input.flv
ffprobe-of csv-show_packets input.flv

ffplay常用的命令:

如果希望從視頻的第30秒開始播放,播放10秒鐘的文件件豌,則可以使用如下命令:

ffplay -ss 30 -t 10 input.mp4

如果希望視頻播放時播放器的窗口顯示標題為自定義標題疮方,則可以使用如下命令:

ffplay -window_title "Hello World, This is a sample" output.mp4

如果希望使用ffplay打開網(wǎng)絡(luò)直播流,則可以使用如下命令:

ffplay -window_title "播放測試" rtmp://up.v.test.com/live/stream

ffplay -window_title "播放測試" http://hdl.9158.com/live/b791155d46ff5e8940c795a41e43f67b.flv

vismv參數(shù)則是用來顯示圖像解碼時的運動向量信息的

ffplay -vismv pf output.mp4

ffplay -debug vis_mb_type -window_title "show vis_mb_type" -ss 20 -t 10 -autoexit output.mp4

FFmpeg轉(zhuǎn)封裝

moov音視頻數(shù)據(jù)的metadata信息

mdat media數(shù)據(jù)容器

因為MP4的標準中描述的moov與mdat的存放位置前后并沒有進行強制要求茧彤,所以有些時候moov這個Box在mdat的后面骡显,有些時候moov被存放在mdat的前面。在互聯(lián)網(wǎng)的視頻點播中,如果希望MP4文件被快速打開蟆盐,則需要將moov存放在mdat的前面承边;如果放在后面,則需要將MP4文件下載完成后才可以進行播放石挂。

常規(guī)的從文件轉(zhuǎn)換HLS直播時博助,使用的參數(shù)如下:

./ffmpeg -re -i input.mp4 -c copy -f hls -bsf:v h264_mp4toannexb output.m3u8

hls_time參數(shù)用于設(shè)置M3U8列表中切片的duration;例如使用如下命令行控制轉(zhuǎn)碼切片長度為10秒鐘左右一片痹愚,該切片規(guī)則采用的方式是從關(guān)鍵幀處開始切片富岳,所以時間并不是很均勻,如果先轉(zhuǎn)碼再進行切片拯腮,則會比較規(guī)律:

./ffmpeg -re -i input.mp4 -c copy -f hls -bsf:v h264_mp4toannexb -hls_time 10 output.m3u8

hls_list_size參數(shù)用于設(shè)置M3U8列表中TS切片的個數(shù)窖式,通過hls_list_size可以控制M3U8列表中TS分片的個數(shù),命令行如下:

./ffmpeg -re -i input.mp4 -c copy -f hls -bsf:v h264_mp4toannexb -hls_list_size 3 output.m3u8

hls_base_url參數(shù)用于為M3U8列表中的文件路徑設(shè)置前置基本路徑參數(shù)动壤,因為在FFmpeg中生成M3U8時寫入的TS切片路徑默認為與M3U8生成的路徑相同萝喘,但是實際上TS所存儲的路徑既可以為本地絕對路徑,也可以為當(dāng)前相對路徑琼懊,還可以為網(wǎng)絡(luò)路徑

./ffmpeg -re -i input.mp4 -c copy -f hls -hls_base_url http://192.168.0.1/live/ -bsf:v h264_mp4toannexb output.m3u8

method參數(shù)用于設(shè)置HLS將M3U8及TS文件上傳至HTTP服務(wù)器阁簸,使用該功能的前提是需要有一臺HTTP服務(wù)器,支持上傳相關(guān)的方法哼丈,例如PUT启妹、POST等,method方法的PUT方法可用于實現(xiàn)通過HTTP推流HLS的功能醉旦,首先需要配置一個支持上傳文件的HTTP服務(wù)器饶米,本例使用Nginx來作為HLS直播的推流服務(wù)器,并且需要支持WebDAV功能车胡,Nginx配置如下:

location / {    

client_max_body_size 10M;    

 dav_access            

group:rw  all:rw;    

dav_methods PUT DELETE MKCOL COPY MOVE;

 root   html/;

}  

配置完成后啟動Nginx即可檬输。通過ffmpeg執(zhí)行HLS推流命令行如下:

./ffmpeg -i input.mp4 -c copy -f hls -hls_time 3 -hls_list_size 0 -method PUT -t 30 http://127.0.0.1/test/output_test.m3u8

FFmpeg抽取音視頻文件中的AAC音頻流:

FFmpeg提取MP4文件中的AAC音頻流的方法:

./ffmpeg -i input.mp4 -vn -acodec copy output.aac

FFmpeg抽取音視頻文件中的H.264視頻流

./ffmpeg -i input.mp4 -vcodec copy -an output.h264

FFmpeg抽取音視頻文件中的H.265數(shù)據(jù)

./ffmpeg -i input.mp4 -vcodec copy -an -bsf hevc_mp4toannexb -f hevc output.hevc

使用FFmpeg進行封裝轉(zhuǎn)換時并不會占用大量的CPU資源,因為使用FFmpeg進行封裝轉(zhuǎn)換時主要是以讀取音視頻數(shù)據(jù)匈棘、寫入音視頻數(shù)據(jù)為主褪猛,并不會涉及復(fù)雜的計算。如果使用FFmpeg進行編碼轉(zhuǎn)換羹饰,則需要進行大量的計算伊滋,從而將會占用大量的CPU資源。

/*************************iOS音視頻開發(fā)進階*************************/
所謂采樣就是在時間軸上對信號進行數(shù)字化队秩。按比聲音最高頻率高2倍以上的頻率對聲音進行采樣,(人耳能夠聽到的頻率范圍)是20Hz~20kHz笑旺,所以采樣頻率一般為44.1kHz,而所謂的44.1kHz就是代表1秒會采樣44100次.

量化是指在幅度軸上對信號進行數(shù)字化,比如用16比特的二進制信號來表示聲音的一個采樣馍资,而16比特(一個short)所表示的范圍是[-32768筒主,32767],共有65536個可能取值,因此最終模擬的音頻信號在幅度上也分為了65536層

既然每一個量化都是一個采樣乌妙,那么這么多的采樣該如何進行存儲呢使兔?這就涉及將要講解的第三個概念:編碼。所謂編碼藤韵,就是按照一定的格式記錄采樣和量化后的數(shù)字數(shù)據(jù).

通常所說的音頻的裸數(shù)據(jù)格式就是脈沖編碼調(diào)制(Pulse Code Modulation虐沥,PCM)數(shù)據(jù)。描述一段PCM數(shù)據(jù)一般需要以下幾個概念:量化格式(sampleFor-mat)泽艘、采樣率(sampleRate)欲险、聲道數(shù)(channel)。以CD的音質(zhì)為例:量化格式(有的地方描述為位深度)為16比特(2字節(jié))匹涮,采樣率為44100天试,聲道數(shù)為2,這些信息就描述了CD的音質(zhì)然低。

比特率喜每,即1秒時間內(nèi)的比特數(shù)目,它用于衡量音頻數(shù)據(jù)單位時間內(nèi)的容量大小雳攘。44100 * 16 * 2 = 1378.125kbps

那么在1分鐘里灼卢,這類CD音質(zhì)的數(shù)據(jù)需要占據(jù)多大的存儲空間呢?計算如下:1378.125 * 60 / 8 / 1024 = 10.09MB

當(dāng)然来农,如果sampleFormat更加精確(比如用4字節(jié)來描述一個采樣),或者sampleRate更加密集(比如48kHz的采樣率)崇堰,那么所占的存儲空間就會更大沃于,同時能夠描述的聲音細節(jié)就會越精確。存儲的這段二進制數(shù)據(jù)即表示將模擬信號轉(zhuǎn)換為數(shù)字信號了海诲,以后就可以對這段二進制數(shù)據(jù)進行存儲繁莹、播放、復(fù)制特幔,或者進行其他任何操作咨演。

麥克風(fēng)是如何采集聲音的:麥克風(fēng)里面有一層碳膜,非常薄而且十分敏感蚯斯。聲音其實是一種縱波薄风,會壓縮空氣也會壓縮這層碳膜,碳膜在受到擠壓時也會發(fā)出振動拍嵌,在碳膜的下方就是一個電極遭赂,碳膜在振動的時候會接觸電極,接觸時間的長短和頻率與聲波的振動幅度和頻率有關(guān)横辆,這樣就完成了聲音信號到電信號的轉(zhuǎn)換撇他。之后再經(jīng)過放大電路處理,就可以實施后面的采樣量化處理了。

壓縮編碼的原理實際上是壓縮掉冗余信號困肩,冗余信號是指不能被人耳感知到的信號划纽,包含人耳聽覺范圍之外的音頻信號以及被掩蔽掉的音頻信號等。

各色光因其所形成的折射角不同而彼此分離锌畸,就像彩虹一樣勇劣,所以白光能夠分解成多種色彩的光。后來人們通過實驗證明蹋绽,紅綠藍三種色光無法被分解芭毙,故稱為三原色光。

假設(shè)一部手機屏幕的分辨率是1280×720卸耘,說明水平方向有720個像素點退敦,垂直方向有1280個像素點,所以整個手機屏幕就有1280×720個像素點(這也是分辨率的含義)蚣抗。每個像素點都由三個子像素點組成(如圖1-7所示)侈百,這些密密麻麻的子像素點在顯微鏡下可以看得一清二楚。

RGB表示方式:取值范圍為0~255或者00~FF翰铡,8個比特表示一個子像素钝域,32個比特表示一個像素,An-droid平臺上RGB_565的表示方法為16比特模式表示一個像素,R用5個比特來表示锭魔,G用6個比特來表示例证,B用5個比特來表示。對于一幅圖像迷捧,一般使用整數(shù)表示方法來進行描述织咧,比如計算一張1280×720的RGBA_8888圖像的大小,可采用如下方式:1280 * 720 * 4 = 3.516MB

YUV表示方式:YUV最常用的采樣格式是4:2:0漠秋,4:2:0并不意味著只有Y笙蒙、Cb而沒有Cr分量。它指的是對每行掃描線來說庆锦,只有一種色度分量是以2:1的抽樣率來存儲的捅位。相鄰的掃描行存儲著不同的色度分量,也就是說搂抒,如果某一行是4:2:0艇搀,那么其下一行就是4:0:2,再下一行是4:2:0求晶,以此類推中符。

I幀:幀內(nèi)編碼幀(intra picture),I幀通常是每個GOP(MPEG所使用的一種視頻壓縮技術(shù))的第一個幀誉帅,經(jīng)過適度地壓縮淀散,作為隨機訪問的參考點右莱,可以當(dāng)成靜態(tài)圖像。

I幀自身可以通過視頻解壓算法解壓成一張單獨的完整視頻畫面档插,所以I幀去掉的是視頻幀在空間維度上的冗余信息慢蜓。

P幀:前向預(yù)測編碼幀(predictive-frame),通過將圖像序列中前面已編碼幀的時間冗余信息充分去除來壓縮傳輸數(shù)據(jù)量的編碼圖像郭膛,也稱為預(yù)測幀晨抡。

P幀需要參考其前面的一個I幀或者P幀來解碼成一張完整的視頻畫面。

B幀:雙向預(yù)測內(nèi)插編碼幀(bi-directional interpolatedprediction frame)则剃,既考慮源圖像序列前面的已編碼幀耘柱,又顧及源圖像序列后面的已編碼幀之間的時間冗余信息,來壓縮傳輸數(shù)據(jù)量的編碼圖像棍现,也稱為雙向預(yù)測幀调煎。

B幀則需要參考其前一個I幀或者P幀及其后面的一個P幀來生成一張完整的視頻畫面,所以P幀與B幀去掉的是視頻幀在時間維度上的冗余信息己肮。

IDR幀與I幀的理解:IDR的英文全稱instantaneous decoding refresh picture士袄,因為H264采用了多幀預(yù)測,所以I幀之后的P幀有可能會參考I幀之前的幀谎僻,這就使得在隨機訪問的時候不能以找到I幀作為參考條件娄柳,因為即使找到I幀,I幀之后的幀還是有可能解析不出來艘绍,而IDR幀就是一種特殊的I幀赤拒,即這一幀之后的所有參考幀只會參考到這個IDR幀,而不會再參考前面的幀诱鞠。

對于視頻來說挎挖,AVFrame就是視頻的一幀圖像,這幀圖像什么時候顯示給用戶般甲,取決于它的PTS(Presentation Time Stamp)。DTS(Decoding Time Stamp)是AVPacket里的一個成員鹅颊,表示該壓縮包應(yīng)該在什么時候被解碼敷存,如果視頻里各幀的編碼是按輸入順序(顯示順序)依次進行的,那么解碼和顯示時間應(yīng)該是一致的堪伍,但是事實上锚烦,在大多數(shù)編解碼標準(如H.264或HEVC)中,編碼順序和輸入順序并不一致帝雇,于是才會需要PTS和DTS這兩種不同的時間戳涮俄。

GOP的概念:兩個I幀之間形成的一組圖片,就是GOP(Group Of Pic-ture)的概念尸闸。通常在為編碼器設(shè)置參數(shù)的時候彻亲,必須要設(shè)置gop_size的值孕锄,其代表的是兩個I幀之間的幀數(shù)目。解碼端必須從接收到的第一個I幀開始才可以正確解碼出原始圖像苞尝,否則會無法正確解碼畸肆。

增加C++支持

在單獨編寫一個C或C++的項目時,如果該項目需要引用到第三方庫宙址,那么編譯階段需要配置參數(shù)“extra-cflags轴脐,-I”來指定引用頭文件的位置,鏈接階段需要配置參數(shù)“l(fā)d-flags抡砂,-L”來指定靜態(tài)庫的位置大咱,并且使用-l來指定引用的是哪一個庫。

首先對應(yīng)于-I來指定頭文件的目錄注益,Xcode使用Search Paths來設(shè)置頭文件的搜索路徑碴巾,通常會用到Header Search Paths選項來指定頭文件的搜索路徑。其中預(yù)定義變量$(SRCROOT)和$(PROJECT_DIR)都是項目的根目錄聊浅,可以基于這兩個預(yù)定義變量再加上相對路徑來指定頭文件所在的具體位置餐抢;

在指定第三方庫時,-L對應(yīng)到Xcode中就是other Link flags選項低匙,其中可以寫入需要鏈接的庫文件旷痕;在Xcode項目中直接添加一個靜態(tài)庫文件,Xcode會默認在Build phases選項的Link Binary withLibrary里加入該靜態(tài)庫顽冶,但是如果Xcode沒有自動加入該靜態(tài)庫的話欺抗,就需要開發(fā)者手動加一下,這里其實也是-L的一種表示方式强重。

使用本機器的編譯器绞呈,將源代碼編譯鏈接成為一個可以在本機器上運行的程序。這就是正常的編譯過程间景,也稱為Native Compilation佃声,中文譯作本機編譯。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末倘要,一起剝皮案震驚了整個濱河市圾亏,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌封拧,老刑警劉巖志鹃,帶你破解...
    沈念sama閱讀 216,324評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異泽西,居然都是意外死亡曹铃,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評論 3 392
  • 文/潘曉璐 我一進店門捧杉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來陕见,“玉大人秘血,你說我怎么就攤上這事〈就妫” “怎么了直撤?”我有些...
    開封第一講書人閱讀 162,328評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長蜕着。 經(jīng)常有香客問我谋竖,道長,這世上最難降的妖魔是什么承匣? 我笑而不...
    開封第一講書人閱讀 58,147評論 1 292
  • 正文 為了忘掉前任蓖乘,我火速辦了婚禮,結(jié)果婚禮上韧骗,老公的妹妹穿的比我還像新娘嘉抒。我一直安慰自己,他們只是感情好袍暴,可當(dāng)我...
    茶點故事閱讀 67,160評論 6 388
  • 文/花漫 我一把揭開白布些侍。 她就那樣靜靜地躺著,像睡著了一般政模。 火紅的嫁衣襯著肌膚如雪岗宣。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,115評論 1 296
  • 那天淋样,我揣著相機與錄音耗式,去河邊找鬼。 笑死趁猴,一個胖子當(dāng)著我的面吹牛刊咳,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播儡司,決...
    沈念sama閱讀 40,025評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼娱挨,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了捕犬?” 一聲冷哼從身側(cè)響起跷坝,我...
    開封第一講書人閱讀 38,867評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎或听,沒想到半個月后探孝,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體笋婿,經(jīng)...
    沈念sama閱讀 45,307評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡誉裆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,528評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了缸濒。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片足丢。...
    茶點故事閱讀 39,688評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡粱腻,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出斩跌,到底是詐尸還是另有隱情绍些,我是刑警寧澤,帶...
    沈念sama閱讀 35,409評論 5 343
  • 正文 年R本政府宣布耀鸦,位于F島的核電站柬批,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏袖订。R本人自食惡果不足惜氮帐,卻給世界環(huán)境...
    茶點故事閱讀 41,001評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望洛姑。 院中可真熱鬧上沐,春花似錦、人聲如沸楞艾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽硫眯。三九已至蕴侧,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間舟铜,已是汗流浹背戈盈。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留谆刨,地道東北人塘娶。 一個月前我還...
    沈念sama閱讀 47,685評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像痊夭,于是被迫代替她去往敵國和親刁岸。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,573評論 2 353

推薦閱讀更多精彩內(nèi)容