天天看點

iOS平台使用ffmpeg解碼h264視訊流

對于視訊檔案和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上即可顯示出來!