经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » C++ » 查看文章
FFmpeg4.0笔记:本地媒体文件解码
来源:cnblogs  作者:gongluck  时间:2019/5/8 9:32:22  对本文有异议

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

原文链接:http://www.cnblogs.com/gongluck/p/10827950.html

 友情链接: NPS