h264視頻
收到的純視頻或者音頻的數(shù)據(jù)應該先保存到本地,用vlc先測試,如果能播放證明數(shù)據(jù)沒有問題,才好進行下一步解碼播放
關(guān)于VideoToolBox也有很多的東西,接口和屬性的定義等,找到兩篇文章做了介紹
使用VideoToolbox硬解碼H.264
VideoToolbox解析
當初自己注釋的代碼不小心刪了,現(xiàn)在對比著demo在捋一下流程decode demo
- 聲明用到的幾個成員
//sps 和 pps的內(nèi)存區(qū)以及大小
uint8_t *_sps;
NSInteger _spsSize;
uint8_t *_pps;
NSInteger _ppsSize;
//解碼會話的引用
VTDecompressionSessionRef _deocderSession;
//描述媒體數(shù)據(jù)和其他各樣的類型的引用
CMVideoFormatDescriptionRef _decoderFormatDescription;
- 解碼的入口
#pragma mark - 對外提供解碼接口
-(void) decodeNalu:(uint8_t *)frame withSize:(uint32_t)frameSize;
1.此函數(shù)中,將前四個字節(jié)替換為數(shù)據(jù)幀的大小減去00 00 00 01四個字節(jié);這一步我記得有一個方法可以直接轉(zhuǎn)換的(CFSwapInt32HostToBig),現(xiàn)在是手動一個字節(jié)一個字節(jié)轉(zhuǎn)的。
2.判斷,nalu類型,如果是5代表關(guān)鍵幀初始化解碼器,7和8分別初始化sps和pps其他的就是數(shù)據(jù)幀調(diào)用解碼函數(shù)。
- 初始化解碼器
按流程倒著來:
為解碼視頻幀創(chuàng)建一個解碼會話,VTDecompressionSessionCreate,解碼的幀會通過回調(diào)函數(shù)發(fā)出。函數(shù)要傳遞一些參數(shù)
1.分配器,傳NULL使用默認的即可
2.videoFormatDescription就是成員描述媒體數(shù)據(jù)的那個引用
3.指定一個專門的視頻解碼器,傳入NULL讓video toolbox選擇一個解碼器
4.想要輸出的圖像數(shù)據(jù)的屬性等
5.解碼后的回調(diào)函數(shù)
6.成員中的那個deocderSession來接收創(chuàng)建的這個解碼會話
然后可以為這個會話設(shè)置一些屬性
//kVTDecompressionPropertyKey_RealTime 解碼實時輸出,后面那個參數(shù)具體沒弄清呢
VTSessionSetProperty(_deocderSession, kVTDecompressionPropertyKey_RealTime, kCFBooleanTrue);
這樣的話,在initH264Decoder方法中最開始那些操作當然就是為VTDecompressionSessionCreate這個做的一些準備了??纯淳投恕?/p>
- 解碼
通過CMBlockBufferCreateWithMemoryBlock方法創(chuàng)建CMBlockBufferRef,在用BlockBuffer通過CMSampleBufferCreateReady創(chuàng)建CMSampleBufferRef,然后使用VTDecompressionSessionDecodeFrame進行解碼,解碼后會調(diào)用回調(diào)函數(shù)。在回調(diào)函數(shù)中,使用了代理傳出解碼后的數(shù)據(jù),進行顯示。
在這個解碼方法中有個sourceFrameRefCon參數(shù),傳入了一開始定義的CVPixelBufferRef outputPixelBuffer = NULL;這里這個參數(shù)跟回調(diào)函數(shù)中那個sourceFrameRefCon是一個。
- 顯示的方法:這里直接使用了apple寫好的一個AAPLEAGLLayer來顯示了,貌似還可以轉(zhuǎn)為圖片顯示哦
//先創(chuàng)建
-(void)createLayer{
_playLayer = [[AAPLEAGLLayer alloc] initWithFrame:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
_playLayer.backgroundColor = [UIColor blackColor].CGColor;
[self.layer addSublayer:_playLayer];
}
//代理
#pragma mark - 解碼回調(diào)
- (void)displayDecodedFrame:(CVImageBufferRef )imageBuffer{
if(imageBuffer)
{
_playLayer.pixelBuffer = imageBuffer;
CVPixelBufferRelease(imageBuffer);
}
return;
}
- 關(guān)于一些數(shù)據(jù)結(jié)構(gòu)定義
1、CVPixelBuffer:編碼前和解碼后的圖像數(shù)據(jù)結(jié)構(gòu);
2、CMBlockBuffer:編碼后圖像的數(shù)據(jù)結(jié)構(gòu);
而CMSampleBuffer就相當于一個容器,他存放上面兩種類型的一種以及cmtime等一些參數(shù)。供編解碼器使用。
我們還可以看到一些結(jié)構(gòu)如下:
typedef CVImageBufferRef CVPixelBufferRef;
typedef CVBufferRef CVImageBufferRef;
一開始納悶這是啥意思,這樣有啥用,后來看文檔說的很清楚:CVBuffer就像抽象基類一樣,他定義了如何與緩沖區(qū)的數(shù)據(jù)進行交互。這個buffer可以包含視頻,音頻等,像CVImageBuffer,CVPixelBuffer都是由它衍生出來的。這一點和CMVideoFormatDescriptionRef也類似,CMFormatDescriptions代表一些描述信息,可以用來描述音頻視頻等類型,也是基類一樣,具體明確的description就有CMVideoFormatDescription和CMAudioFormatDescription。