ffmpeg播放器12(从7-11的实现)-下

97 阅读3分钟

1背景

上面这几篇,是从线程开始,加载视频流。上一篇处理了开始初始化线程,这一篇继续后续。

源码存放位置 github.com/xcyxiner/xp…

2从线程开始读取流

这次在之前的基础上,继续(分支 ffmpeg_play_7_11)

2.1打开媒体文件

先在hffplayer.h头文件添加fmt_opts

private:
    AVFormatContext* fmt_ctx;

    AVDictionary* fmt_opts;

然后在hffplayer.cpp的构造函数里初始化

HFFPlayer::HFFPlayer() {

    fmt_opts=NULL;
    fmt_ctx=NULL;
}

然后在hffplayer.cpp的open里添加如下代码

    av_dict_set(&fmt_opts,"buffer_size","2048000",0);
    ret = avformat_open_input(&fmt_ctx, ifile.c_str(), ifmt, &fmt_opts);
    if (ret != 0) {
        if(fmt_ctx){
            avformat_close_input(&fmt_ctx);
            avformat_free_context(fmt_ctx);
            fmt_ctx=NULL;
        }
        return ret;
    }

运行调试,正常应该是能返回0

image.png

2.2 查找是否有流

添加如下代码

    //查找是否有流
    ret=avformat_find_stream_info(fmt_ctx,NULL);
    if(ret!=0){
        if(fmt_ctx){
            avformat_close_input(&fmt_ctx);
            avformat_free_context(fmt_ctx);
            fmt_ctx=NULL;
        }
        return ret;
    }

调试并检查是否ret为0

image.png

2.3 获取视频流音频流索引

头文件添加如下代码

private:

    int video_stream_index;
    int audio_stream_index;
    int subtitle_stream_index;

在open中添加如下代码

    //获取视频流音频流索引
    video_stream_index=av_find_best_stream(fmt_ctx,AVMEDIA_TYPE_VIDEO,-1,-1,NULL,0);
    audio_stream_index=av_find_best_stream(fmt_ctx,AVMEDIA_TYPE_AUDIO,-1,-1,NULL,0);
    subtitle_stream_index=av_find_best_stream(fmt_ctx,AVMEDIA_TYPE_SUBTITLE,-1,-1,NULL,0);

    if(video_stream_index<0){
        if(fmt_ctx){
            avformat_close_input(&fmt_ctx);
            avformat_free_context(fmt_ctx);
            fmt_ctx=NULL;
        }
        ret=-20;
        return ret;
    }

调试并运行

image.png

2.4 根据索引获取视频流

    AVStream* video_stream=fmt_ctx->streams[video_stream_index];

2.5 获取视频源文件编码,直接使用软件解码器

    //获取视频源文件编码
    AVCodecParameters* codec_param=video_stream->codecpar;

    //解码器
    const AVCodec* codec=NULL;
    codec=avcodec_find_decoder(codec_param->codec_id);
    if(codec==NULL){
        if(fmt_ctx){
            avformat_close_input(&fmt_ctx);
            avformat_free_context(fmt_ctx);
            fmt_ctx=NULL;
        }
        ret=-30;
        return ret;
    }

调试并运行

image.png

2.6 解码器上下文

先在头文件添加codec_ctx的成员变量

private:
    AVFormatContext* fmt_ctx;
    AVDictionary* fmt_opts;

    AVCodecContext* codec_ctx;
    AVDictionary*   codec_opts;

然后在构造函数初始化

HFFPlayer::HFFPlayer() {

    fmt_opts=NULL;
    fmt_ctx=NULL;
    codec_ctx = NULL;
    codec_opts=NULL;
}

再在open中添加如下代码

    // 解码器上下文
    codec_ctx=avcodec_alloc_context3(codec);
    if(codec_ctx==NULL){
        if(fmt_ctx){
            avformat_close_input(&fmt_ctx);
            avformat_free_context(fmt_ctx);
            fmt_ctx=NULL;
        }
        ret=-40;
        return ret;
    }

追加断点并调试

image.png

2.7 配置解码上下文的解码参数 以及引用计数

    // 配置解码上下文的解码参数
    ret=avcodec_parameters_to_context(codec_ctx,codec_param);
    if(ret!=0){
        if(fmt_ctx){
            avformat_close_input(&fmt_ctx);
            avformat_free_context(fmt_ctx);
            fmt_ctx=NULL;
        }
        if(codec_ctx){
            avcodec_free_context(&codec_ctx);
            codec_ctx=NULL;
        }
        return ret;
    }

    //配置引用计数
    if(codec_ctx->codec_type==AVMEDIA_TYPE_VIDEO||
        codec_ctx->codec_type==AVMEDIA_TYPE_AUDIO){
        av_dict_set(&codec_opts,"refcounted_frames","1",0);
    }

2.8 解码

    //解码
    ret=avcodec_open2(codec_ctx,codec,&codec_opts);
    if(ret!=0){
        if(fmt_ctx){
            avformat_close_input(&fmt_ctx);
            avformat_free_context(fmt_ctx);
            fmt_ctx=NULL;
        }
        if(codec_ctx){
            avcodec_free_context(&codec_ctx);
            codec_ctx=NULL;
        }
        return ret;
    }

image.png

未完待续