Ubuntu 14.04LTS FFmpeg H264解码实战

/ C++ / 没有评论 / 1800浏览

Ubuntu 14.04LTS FFmpeg H264解码实战

安装FFmpeg

sudo add-apt-repository ppa:kirillshkrogalev/ffmpeg-next
sudo apt-get update 
sudo apt-get install ffmpeg
sudo apt-get install libavcodec-ffmpeg-dev

解码过程

1. 包含头文件,C++中,需要extern "C"起来

extern "C"
{
    #include <libavcodec/avcodec.h>
    #include <libswscale/swscale.h> //如果不做图像处理时,不需要包含
    #include <libavutil/imgutils.h> //如果不做图像处理时,不需要包含 
};

2. 声明变量

AVCodec* codec;
AVCodecParserContext *parser;
AVCodecContext* context;
AVFrame* frame;
AVPacket* pkt;
//如果需要做图像处理,比如需要转换成位图,再声明两个变量。
SwsContext* img_convert_ctx; //图像处理上下文
AVFrame* rgb_frame; //转换后的图片数据
bool is_init_output_buffer = false;

3. 初始化

int Init()
{
    avcodec_register_all();
    pkt = av_packet_alloc();
    codec = avcodec_find_decoder(AV_CODEC_ID_H264);
    if (!codec) {
        fprintf(stderr, "Codec not found\n");
        return -1;
    }
    parser = av_parser_init(codec->id);
    if (!parser) {
        fprintf(stderr, "parser not found\n");
        return -1;
    }
    context = avcodec_alloc_context3(codec);
    if (!context) {
        fprintf(stderr, "Could not allocate video codec context\n");
        exit(1);
    }
    if (avcodec_open2(context, codec, NULL) < 0) {
        fprintf(stderr, "Could not open codec\n");
        return -1;
    }
    frame = av_frame_alloc();
    rgb_frame = av_frame_alloc();
    if (!frame || !rgb_frame) {
        fprintf(stderr, "Could not allocate video frame\n");
        return -1;
    }
}

4. 解码

int decode(unsigned char* data, int data_size)
{
    while (data_size > 0) {
        int ret = av_parser_parse2(parser, context, &pkt->data, &pkt->size,
                               data, data_size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
        if (ret < 0) {
            fprintf(stderr, "Error while parsing\n");
            return -1;
        }
        data += ret;
        data_size -= ret;
        if (pkt->size) {
            ret = avcodec_send_packet(context, pkt);
            if (ret < 0) {
                fprintf(stderr, "Error sending a packet for decoding\n");
                return -1;
            }
            while (ret >= 0) {
                ret = avcodec_receive_frame(context, frame);
                if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
                    break;
                else if (ret < 0) {
                    fprintf(stderr, "Error during decoding\n");
                    return -1;
                }
                //至此解码已经成功,下面进行YUV到RGB的转换
                if (!is_init_output_buffer) {
                    //初试化转换后的图像数据buffer,只要初始化一次就行。
                    uint8_t *out_buffer = (unsigned char *)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_RGB24, context->width, context->height, 1));
                    av_image_fill_arrays(rgb_frame->data, rgb_frame->linesize, out_buffer, AV_PIX_FMT_RGB24, context->width, context->height, 1);
                    is_init_output_buffer = true;
                }
                //获取图像转换上下文
                img_convert_ctx = sws_getCachedContext(img_convert_ctx, context->width, context->height,
                        context->pix_fmt, context->width, context->height, AV_PIX_FMT_RGB24,
                        SWS_X, NULL, NULL, NULL);
                //图像转换
                sws_scale(img_convert_ctx, frame->data, frame->linesize, 0, context->height, rgb_frame->data, rgb_frame->linesize);
                //下面是3种数据的获取方式
                //YUV数据
                int y_size = context->width * context->height;
                fwrite(rgb_frame->data[0], 1, y_size, fp_yuv);        //Y
                fwrite(rgb_frame->data[1], 1, y_size / 4, fp_yuv);    //U
                fwrite(rgb_frame->data[2], 1, y_size / 4, fp_yuv);    //V
                // RGB数据
                fwrite(rgb_frame->data[0], (context->width) * (context->height) * 3, 1, fp);
                // UYVY数据
                //fwrite(rgb_frame->data[0], (context->width) * (context->height), 2, fp);
            }
        }
    }
    return 0;
}

5. 编译包含的库

LIBS += /usr/local/lib/libavcodec.a /usr/local/lib/libavutil.a /usr/local/lib/libswresample.a /usr/local/lib/libswscale.a -lz -ldl