iOS平台使用FFmpeg解码H.264视频流

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/humiaor/article/details/67633189

对于视频文件和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", 0);
    }
    /*打开指定的解析器*/
    int ret = avcodec_open2(pCodecCtx, pCodec, NULL);
    if (ret != 0) {
        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,
              0,
              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,
                                       8,
                                       24,
                                       avPicture.linesize[0],
                                       colorSpace,
                                       bitmapInfo,
                                       provider,
                                       NULL,
                                       NO,
                                       kCGRenderingIntentDefault);
    UIImage *image = [UIImage imageWithCGImage:cgImage];
    CGImageRelease(cgImage);
    CGColorSpaceRelease(colorSpace);
    CGDataProviderRelease(provider);
    CFRelease(data);

    return image;
}

done,把转换出来的UIImage画到UIImgeView上即可显示出来!

展开阅读全文

没有更多推荐了,返回首页