当前位置: 萬仟网 > IT编程>开发语言>C/C++ > FFmpeg4.0笔记:本地媒体文件解码

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

2019年05月08日  | 萬仟网IT编程  | 我要评论

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;
}

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
Copyright © 2017-2020  萬仟网 保留所有权利. 粤ICP备17035492号-1
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com