欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

FFmpeg4.0笔记:本地媒体文件解码

程序员文章站 2022-04-28 15:42:45
Github https://github.com/gongluck/FFmpeg4.0 study/blob/master/official%20example/my_example.cpp C++ include include // define NOVIDEO // define NOAUD ......

github

https://github.com/gongluck/ffmpeg4.0-study/blob/master/official%20example/my_example.cpp

#include <iostream>
#include <fstream>

//#define novideo
//#define noaudio

#ifdef __cplusplus

extern "c"
{

#endif

// ffmpeg 头文件
#include "libavformat/avformat.h"

#ifdef __cplusplus

}
// c++中使用av_err2str宏
char av_error[av_error_max_string_size] = { 0 };
#define av_err2str(errnum) \
    av_make_error_string(av_error, av_error_max_string_size, errnum)

#endif


int main(int argc, char* argv[])
{
    avformatcontext* fmt_ctx = nullptr;
    avcodeccontext *vcodectx = nullptr, *acodectx = nullptr;
    avcodecparameters *vcodecpar = nullptr, *acodecpar = nullptr;
    avcodec *vcodec = nullptr, *acodec = nullptr;
    avpacket* pkt = nullptr;
    avframe* frame = nullptr;
    std::ofstream out_yuv, out_pcm;
    const char* in = "in.flv";
    int vindex = -1, aindex = -1;
    int ret = 0;

    out_yuv.open("out.yuv", std::ios::binary | std::ios::trunc);
    out_pcm.open("out.pcm", std::ios::binary | std::ios::trunc);
    if (!out_yuv.is_open() || !out_pcm.is_open())
    {
        std::cerr << "创建/打开输出文件失败" << std::endl;
        goto end;
    }

    // 日志
    av_log_set_level(av_log_error);

    // 打开输入
    ret = avformat_open_input(&fmt_ctx, in, nullptr, nullptr);
    if (ret < 0)
    {
        std::cerr << "avformat_open_input err : " << av_err2str(ret) << std::endl;
        goto end;
    }

    // 查找流信息,对输入进行预处理
    ret = avformat_find_stream_info(fmt_ctx, nullptr);
    if (ret < 0)
    {
        std::cerr << "avformat_find_stream_info err : " << av_err2str(ret) << std::endl;
        goto end;
    }

    // 打印输入信息
    av_dump_format(fmt_ctx, 0, fmt_ctx->url, 0);

    //查找流
    for (int i = 0; i < fmt_ctx->nb_streams; ++i)
    {
        if (fmt_ctx->streams[i]->codecpar->codec_type == avmedia_type_video)
        {
            vindex = i;
        }   
        else if (fmt_ctx->streams[i]->codecpar->codec_type == avmedia_type_audio)
        {
            aindex = i;
        }    
    }
    if (vindex == -1)
    {
        std::cerr << "找不到视频流" << std::endl;
    }
    if (aindex == -1)
    {
        std::cerr << "找不到音频流" << std::endl;
    }

    //查找解码器    
    vcodecpar = fmt_ctx->streams[vindex]->codecpar;
    vcodec = avcodec_find_decoder(vcodecpar->codec_id);
    if (vcodec == nullptr)
    {
        std::cerr << "avcodec_find_decoder err : " << av_err2str(ret) << std::endl;
        goto end;
    }
    acodecpar = fmt_ctx->streams[aindex]->codecpar;
    acodec = avcodec_find_decoder(acodecpar->codec_id);
    if (acodec == nullptr)
    {
        std::cerr << "avcodec_find_decoder err : " << av_err2str(ret) << std::endl;
        goto end;
    }

    //打开解码器
    vcodectx = avcodec_alloc_context3(vcodec);
    ret = avcodec_parameters_to_context(vcodectx, vcodecpar);// 参数拷贝
    if (ret < 0)
    {
        std::cerr << "avcodec_parameters_to_context err : " << av_err2str(ret) << std::endl;
        goto end;
    }
    ret = avcodec_open2(vcodectx, vcodec, nullptr);
    if (ret < 0)
    {
        std::cerr << "avcodec_open2 err : " << av_err2str(ret) << std::endl;
        goto end;
    }
    acodectx = avcodec_alloc_context3(acodec);
    ret = avcodec_parameters_to_context(acodectx, acodecpar);// 参数拷贝
    if (ret < 0)
    {
        std::cerr << "avcodec_parameters_to_context err : " << av_err2str(ret) << std::endl;
        goto end;
    }
    ret = avcodec_open2(acodectx, acodec, nullptr);
    if (ret < 0)
    {
        std::cerr << "avcodec_open2 err : " << av_err2str(ret) << std::endl;
        goto end;
    }

    // 创建avpacket
    pkt = av_packet_alloc();
    if (pkt == nullptr)
    {
        std::cerr << "av_packet_alloc err : " << std::endl;
        goto end;
    }
    av_init_packet(pkt);

    // 创建avframe
    frame = av_frame_alloc();
    if (frame == nullptr) 
    {
        std::cerr << "av_frame_alloc err : " << std::endl;
        goto end;
    }

    // 从输入读取数据
    while (av_read_frame(fmt_ctx, pkt) >= 0)
    {
        if (pkt->stream_index == vindex)
        {
#ifndef novideo
            // 解码视频帧
            ret = avcodec_send_packet(vcodectx, pkt);
            if (ret < 0)
            {
                std::cerr << "avcodec_send_packet err : " << av_err2str(ret) << std::endl;
                break;
            }
            while (ret >= 0) 
            {
                ret = avcodec_receive_frame(vcodectx, frame);
                if (ret == averror(eagain) || ret == averror_eof)
                {
                    break;
                }
                else if (ret < 0) 
                {
                    std::cerr << "avcodec_receive_frame err : " << av_err2str(ret) << std::endl;
                    break;
                }
                else
                {
                    // 得到解码数据
                    if (frame->format == av_pix_fmt_yuv420p)
                    {
                        out_yuv.write(reinterpret_cast<const char*>(frame->data[0]), frame->linesize[0] * frame->height);
                        out_yuv.write(reinterpret_cast<const char*>(frame->data[1]), frame->linesize[1] * frame->height / 2);
                        out_yuv.write(reinterpret_cast<const char*>(frame->data[2]), frame->linesize[2] * frame->height / 2);
                    }
                }
            }
#endif // novideo
        }
        else if (pkt->stream_index == aindex)
        {
#ifndef noaudio
            // 解码音频帧
            ret = avcodec_send_packet(acodectx, pkt);
            if (ret < 0)
            {
                std::cerr << "avcodec_send_packet err : " << av_err2str(ret) << std::endl;
                break;
            }
            while (ret >= 0)
            {
                ret = avcodec_receive_frame(acodectx, frame);
                if (ret == averror(eagain) || ret == averror_eof)
                {
                    break;
                }
                else if (ret < 0)
                {
                    std::cerr << "avcodec_receive_frame err : " << av_err2str(ret) << std::endl;
                    break;
                }
                else
                {   
                    // 得到解码数据
                    if (frame->format == av_sample_fmt_fltp)
                    { 
                        /// 参考了https://www.cnblogs.com/my_life/articles/6841859.html
                        // 计算一个planar的有效大小,很关键!
                        auto size = av_get_bytes_per_sample(static_cast<avsampleformat>(frame->format)) * frame->nb_samples;
                        for (int i = 0; i < size; i += 4)
                        {
                            for (int j = 0; j < frame->channels; ++j)
                            {
                                out_pcm.write(reinterpret_cast<const char*>(frame->data[j] + i), 4);
                            }
                        }
                    }
                }
            }
#endif // noaudio
        }

        // 复位data和size
        av_packet_unref(pkt);
    }

end:
    std::cerr << "end..." << std::endl;
    std::cin.get();

    out_yuv.close();
    out_pcm.close();
    av_frame_free(&frame);
    av_packet_free(&pkt);
    avcodec_free_context(&vcodectx);
    avcodec_free_context(&acodectx);
    avformat_close_input(&fmt_ctx);
    return 0;
}