[FFmpeg] 软解码将mp4解码为yuv420文件

本文详细介绍了如何使用FFmpeg软件将mp4文件解码为yuv420p格式,包括打开输入流、获取流信息、解码上下文创建、解码器选择和数据处理等步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概要

使用ffmpeg软件解码mp4文件,解码为yuv420p类型的数据。

整体架构流程

  • 1.打开输入流文件                                                         avformat_open_input
  • 2.获取输入流信息                                                         avformat_find_stream_info
  • 3.获取视频流信息                                                         av_find_best_stream
  • 4.创建一个解码的上下文                                              avcodec_alloc_context3
  • 5.获取原文件解码参数                                                  avcodec_parameters_to_context
  • 6.找到当前ffmpeg的解码器                                         avcodec_find_decoder
  • 7.打开解码器                                                                avcodec_open2
  • 8.读取源文件的帧数据获得                                          packet av_read_frame
  • 9.发送packet到解码器进行解码                                  avcodec_send_packet
  • 10.解码器把解码后的数据发送回来                             avcodec_receive_frame

代码

    //软解码MP4为yuv文件
    //ffplay big_buck_bunny.yuv -s 640x360 -pix_fmt yuv420p播放yuv视频
    av_register_all();
    //1.打开输入流文件 avformat_open_input
    AVFormatContext *inpFormatCtx = NULL;
    const char *inFilename = "/home/16THDD/xieyingbo/xieyingbo/big_buck_bunny.mp4";
    const char *outFilename = "/home/16THDD/xieyingbo/xieyingbo/big_buck_bunny.yuv";
    
    int ret = avformat_open_input(&inpFormatCtx, inFilename, NULL, NULL);
    if(ret < 0)
    {
        std::cout << "open inpFormatCtx error" << std::endl;
        return;
    }
    
    //2.获取输入流信息 avformat_find_stream_info
    ret = avformat_find_stream_info(inpFormatCtx, NULL);
    if(ret < 0)
    {
        std::cout << "find stream error" << std::endl;
        return;
    }
    
    //3.获取视频流信息 av_find_best_stream
    int videoINdex = av_find_best_stream(inpFormatCtx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
    if(videoINdex < 0)
    {
        std::cout << "find best stream" << std::endl;
        return;
    }
    else
    {
        std::cout << "videoIndex = " << videoINdex << std::endl;
    }
    
    //4.创建一个解码的上下文 avcodec_alloc_context3
    //AVCodecContext *avcodec_alloc_context3(const AVCodec *codec);
    AVCodecContext * codecCtx = avcodec_alloc_context3(NULL);
    if(codecCtx == NULL)
    {
        avcodec_free_context(&codecCtx);
        std::cout << "alloc avcodec codecCtx error" << std::endl;
        return;
    }
    
    //5.获取原文件解码参数放到新的avcodec里面来 avcodec_parameters_to_context
    //int avcodec_parameters_to_context(AVCodecContext *codec, const AVCodecParameters *par);
    ret = avcodec_parameters_to_context(codecCtx, inpFormatCtx->streams[videoINdex]->codecpar);
    if(ret < 0)
    {
        std::cout << "avcodec parameters to context error" << std::endl;
        return;
    }
    
    //6.找到当前ffmpeg支持这个流(h264)的解码器 avcodec_find_decoder
    //AVCodec *avcodec_find_decoder(enum AVCodecID id);
    AVCodec * avcodec = avcodec_find_decoder(codecCtx->codec_id);
    if(avcodec == NULL)
    {
        std::cout << "avcodec find decoder error, codec_id = " << codecCtx->codec_id << std::endl;
        return;
    }
    
    //7.打开解码器 avcodec_open2
    //int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);
    ret = avcodec_open2(codecCtx, avcodec, NULL);
    if(ret != 0)
    {
        std::cout << "avcodec_open2 error" << std::endl;
        return;
    }
    
    FILE *dst_fp = fopen(outFilename, "wb+");
    if(dst_fp == NULL)
    {
        std::cout << "open file outFilename error" << std::endl;
        return;
    }
    
    //8.读取源文件的帧数据获得packet av_read_frame
    //int av_read_frame(AVFormatContext *s, AVPacket *pkt);
    AVPacket packet;
    av_init_packet(&packet);
    AVFrame *frame = av_frame_alloc();
    while(av_read_frame(inpFormatCtx, &packet) == 0)
    {
        if(packet.stream_index == videoINdex)
        {
            
            //9.发送packet到解码器进行解码 avcodec_send_packet
            //int avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt);
            ret = avcodec_send_packet(codecCtx, &packet);
            if(ret != 0)
            {
                std::cout << "avcodec_send_packet error" << std::endl;
                av_packet_unref(&packet);
                return;
            }
            
            //10.解码器把解码后的数据发送回来 avcodec_receive_frame
            //int avcodec_receive_frame(AVCodecContext *avctx, AVFrame *frame);
            while(avcodec_receive_frame(codecCtx, frame) == 0)
            {
                if(ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
                {
                   break;
                }
                else if(ret < 0)
                {
                    return;
                }
                //11.存储数据(YUV420类型数据)
                fwrite(frame->data[0], 1, codecCtx->width *codecCtx->height, dst_fp);       //存储Y
                fwrite(frame->data[1], 1, codecCtx->width *codecCtx->height / 4, dst_fp);   //存储U
                fwrite(frame->data[2], 1, codecCtx->width *codecCtx->height / 4, dst_fp);   //存储V
            }
        }
        av_packet_unref(&packet);
    }
    std::cout << "解码成功" << std::endl;
    

小结

使用ffplay播放生成的yuv数据:

ffplay big_buck_bunny.yuv -s 640x360 -pix_fmt yuv420p
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值