ffmpeg api解码mp4文件为yuv

解码流程

avformat_open_input():               打开视频频文件,获取里面的内容(解封装)
avformat_find_stream_info():       获取视频信息
av_find_best_stream():                获取视频索引
avcodec_find_decoder():             寻找解码器
avcodec_alloc_context3():           根据解码器申请解码器相关上下文
avcodec_parameters_to_context():初始化解码器context
avcodec_open2():                        打开解码器
av_image_get_buffer_size():          计算申请存放yuv数据buf的大小
av_read_frame():                         从视频文件中读取视频帧
avcodec_send_packet():             发送一帧视频给解码器。
avcodec_receive_frame():           接收解码器解码后的一帧视频(AVFrame)
av_image_copy_to_buffer():           把解码数据copy到buf中

代码

#include<iostream>
#include<string>


extern "C"
{
    #include "libavutil/samplefmt.h"
    #include "libavutil/timestamp.h"
    #include "libavformat/avformat.h"
    #include "libavcodec/avcodec.h"
    #include "libswresample/swresample.h"
    #include "libavutil/imgutils.h"
}

using namespace std;


int main()
{
    av_log(NULL, AV_LOG_INFO, "ffmpeg decode video\n");
    string filename            = "test.mp4";
    string dst_filename        = "test.yuv";
    FILE   *dst_file           = NULL;
    AVFormatContext *fmt_ctx   = NULL;
    AVCodecContext  *codec_ctx = NULL;
    AVFrame *decoded_frame     = NULL;
    SwrContext *actx           = NULL;
    AVPacket   *pPacket        = NULL;
    
    //打开输入文件
    if (avformat_open_input(&fmt_ctx, filename.c_str(), NULL, NULL) < 0) {
        fprintf(stderr, "Could not open source file %s\n", filename.c_str());
        exit(1);
    }
    if(avformat_find_stream_info(fmt_ctx,NULL) < 0)
    {
        cout<<"get stream fail"<<endl;
        return -1;
    }
    dst_file = fopen(dst_filename.c_str(),"wb");
    if(!dst_file)
    {
        cout<<"open test.yuv fail"<<endl;
        return -1;
    }
    //查找视频索引
    int video_index = -1;
    video_index = av_find_best_stream(fmt_ctx,AVMEDIA_TYPE_VIDEO,-1,-1,NULL,0);
    if(video_index < 0)
    {
        cout<<"not find video index"<<endl;
        return -1;
    }
     //打印输入语音文件信息
    av_dump_format(fmt_ctx, 0,filename.c_str(),0);
    
    //查找解码器
    AVCodec *vCodec = NULL;
    vCodec = avcodec_find_decoder(fmt_ctx->streams[video_index]->codecpar->codec_id);
    if(!vCodec)
    {
        cout<<"find video decoder fail"<<endl;
        return -1;
    }

    //初始化解码器上下文
    codec_ctx = avcodec_alloc_context3(vCodec);
    if(!codec_ctx)
    {
        cout<<"init codec_ctx fail"<<endl;
        return -1;
    }
    int ret = avcodec_parameters_to_context(codec_ctx,fmt_ctx->streams[video_index]->codecpar);
    if(ret < 0)
    {
        cout<<"avcodec_parameters_to_contex fail"<<endl;
        return -1;
    }
    //打开解码器
    ret = avcodec_open2(codec_ctx,vCodec,NULL);
    if(ret != 0)
    {
        cout<<"avcodec_open2 fail"<<endl;
        return -1;
    }
    
    pPacket  = av_packet_alloc();
    decoded_frame = av_frame_alloc();
    if (!decoded_frame) {
        fprintf(stderr, "Could not allocate frame\n");
        ret = AVERROR(ENOMEM);
        return -1;
    }
    
    uint8_t *byte_buffer = NULL;
    int byte_buffer_size = av_image_get_buffer_size(codec_ctx->pix_fmt, codec_ctx->width, codec_ctx->height, 1);
    cout<<"width="<< codec_ctx->width<<" height="<< codec_ctx->height<<endl;

    byte_buffer = (uint8_t*)av_malloc(byte_buffer_size);
    if (!byte_buffer) 
    {
        av_log(NULL, AV_LOG_ERROR, "allocate buffer fail\n");
        return AVERROR(ENOMEM);
    }
    
    while(1)
    {
        ret = av_read_frame(fmt_ctx, pPacket);
        if (ret != 0)
        {
            cout<<"ret="<<ret<<endl;
            av_packet_unref(pPacket);
            break;
        }
 
        if (ret >= 0 && pPacket->stream_index != video_index) 
        {
            av_packet_unref(pPacket);
            continue;
        }

        // 发送待解码包
        int len = avcodec_send_packet(codec_ctx, pPacket);
        if (len < 0) 
        {
            av_log(NULL, AV_LOG_ERROR, "send packet fail\n");
            av_packet_unref(pPacket);
            continue;
        }
 
        //接收解码数据
        while (len >= 0)
        {
            len = avcodec_receive_frame(codec_ctx, decoded_frame);
            if (len == AVERROR_EOF)
            {
                break;
            }
            else if (len == AVERROR(EAGAIN)) 
            {
                len = 0;
                break;
            } 
            else if (len < 0)
            {
                av_log(NULL, AV_LOG_ERROR, "Error decoding frame\n");
                av_frame_unref(decoded_frame);
                break;
            }
 
            int number_of_written_bytes = av_image_copy_to_buffer(byte_buffer, 
                                                                  byte_buffer_size,
                                                                  (const uint8_t* const *)decoded_frame->data,
                                                                  (const int*) decoded_frame->linesize,
                                                                  codec_ctx->pix_fmt,
                                                                  codec_ctx->width, 
                                                                  codec_ctx->height, 
                                                                  1);
            if (number_of_written_bytes < 0) 
            {
                av_log(NULL, AV_LOG_ERROR, "Can't copy image to buffer\n");
                av_frame_unref(decoded_frame);
                continue;
            }
 
            // 写文件保存解码后视频数据
            fwrite(byte_buffer, number_of_written_bytes, 1, dst_file);
            fflush(dst_file);
 
            av_frame_unref(decoded_frame);
        }
        av_packet_unref(pPacket);
 
    }
    //释放
    fclose(dst_file);
    av_frame_free(&decoded_frame);
    avcodec_free_context(codec_ctx);
    av_packet_free(&pPacket);
    avformat_close_input(fmt_ctx);
    return 0;
}

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值