對于視訊檔案和rtsp之類的主流視訊傳輸協定,ffmpeg提供avformat_open_input接口,直接将檔案路徑或URL傳入即可打開。讀取視訊資料、解碼器初始參數設定等,都可以通過調用API來完成。但是對于h264流,沒有任何封裝格式,也就無法使用libavformat。是以許多工作需要自己手工完成。這裡的h264流指AnnexB,也就是每個nal unit以起始碼00 00 00 01 或 00 00 01開始的格式。
初始化ffmpeg
- (BOOL)initFFmpegDecoder
{
/*注冊所有的編碼器,解析器,碼流過濾器,隻需要初始化一次*/
static dispatch_once_t once;
dispatch_once(&once, ^{
avcodec_register_all();
});
/*查找指定格式的解析器,這裡我們使用H264*/
AVCodec *pCodec = avcodec_find_decoder(CODEC_ID_H264);
if (pCodec == NULL) {
NSLog(@"codec not found");
return NO;
}
/*初始化解析器容器*/
if (pCodecCtx == NULL) {
pCodecCtx = avcodec_alloc_context3(pCodec);
if (pCodecCtx == NULL) {
NSLog(@"Allocate codec context failed");
return NO;
}
av_opt_set(pCodecCtx->priv_data, "tune", "zerolatency", );
}
/*打開指定的解析器*/
int ret = avcodec_open2(pCodecCtx, pCodec, NULL);
if (ret != ) {
NSLog(@"open codec error :%d", ret);
return NO;
}
/*AVFrame用來描述原始的解碼音頻和視訊資料*/
if (pFrame == NULL) {
pFrame = av_frame_alloc();
if (pFrame == NULL) {
NSLog(@"av_frame_alloc failed");
return NO;
}
}
avpicture_free(&avPicture);
avpicture_alloc(&avPicture, AV_PIX_FMT_RGB24, _outputSize.width, _outputSize.height);
return YES;
}
擷取原始資料進行解碼
這裡我們主要是解碼視訊資料,使用的是avcodec_decode_video2函數,
int avcodec_decode_video2(AVCodecContext *avctx, AVFrame *picture,
int *got_picture_ptr, const AVPacket *avpkt)
該函數有4個參數,第一個是我們之前初始化的context,第二個是輸出值,解碼後的原始資料,第三個是判斷是否存在可以解碼的資料,第四個是輸入參數:
@synchronized(self) {
AVPacket packet;
av_new_packet(&packet, length);
memcpy(packet.data, pFrameData, length);
result = avcodec_decode_video2(pCodecCtx, pFrame, &decoderFrameOK, &packet);
av_free_packet(&packet);
}
在傳入參數的時候,我們要指定原始資料的buff,長度,和時間,後面轉換有需要用到。
AVFrame轉UIImage
使用ffmepg解碼視訊一般都是生成AVFrame。然後再轉換成RGB或YUV.AVFrame 轉RGB:
AVFrame —> RGB
data[0] — RGG資料
linesize[0] —- width*pixel_size for RGB
- (UIImage*)imageFromAVFrame:(AVFrame *)avFrame
{
float width = avFrame->width;
float height = avFrame->height;
avpicture_free(&avPicture);
avpicture_alloc(&avPicture, AV_PIX_FMT_RGB24, width, height);
struct SwsContext * imgConvertCtx = sws_getContext(avFrame->width,
avFrame->height,
PIX_FMT_YUV420P,
width,
height,
AV_PIX_FMT_RGB24,
SWS_FAST_BILINEAR,
NULL,
NULL,
NULL);
if(imgConvertCtx == nil) return nil;
sws_scale(imgConvertCtx,
avFrame->data,
avFrame->linesize,
,
avFrame->height,
avPicture.data,
avPicture.linesize);
sws_freeContext(imgConvertCtx);
CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;
CFDataRef data = CFDataCreate(kCFAllocatorDefault,
avPicture.data[0],
avPicture.linesize[0] * height);
CGDataProviderRef provider = CGDataProviderCreateWithCFData(data);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGImageRef cgImage = CGImageCreate(width,
height,
,
,
avPicture.linesize[],
colorSpace,
bitmapInfo,
provider,
NULL,
NO,
kCGRenderingIntentDefault);
UIImage *image = [UIImage imageWithCGImage:cgImage];
CGImageRelease(cgImage);
CGColorSpaceRelease(colorSpace);
CGDataProviderRelease(provider);
CFRelease(data);
return image;
}
done,把轉換出來的UIImage畫到UIImgeView上即可顯示出來!