第五章 音视频-FFmpeg实现播放器解封装、读AVPacket包

发布于:2024-05-23 ⋅ 阅读:(25) ⋅ 点赞:(0)

第五章 音视频-FFmpeg实现播放器解封装、读AVPacket包

第一章 音视频-FFmpeg解码流程和对应结构参数意
第二章 音视频-FFmpeg对应解析格式说明
第三章 音视频-FFmpeg对应AVFrame解码处理思路和用途
第四章 音视频-FFmpeg实现播放器思维
第五章 音视频-FFmpeg实现播放器解封装、读AVPacket包
第六章 音视频-FFmpeg实现播放器解码和对应数据处理

ffmpeg解封装

媒体文件进行解封装,在做播放器步骤前一个章节进行解封装,解封装可以自己进行C++ 或者c语言封装一个通过方法,获取相关结构体,结构体在第一章有做介绍,下面代码本人封装的一个通过方法,读取媒体文件可能只有音频或者视频,也可能同时有音视频,也可以只读取你要音频或者视频。

typedef struct {
    AVCodecContext *video_dec_ctx;
    AVCodecContext *audio_dec_ctx;
    AVFormatContext *dec_fmt_ctx;
    AVCodecContext *video_enc_ctx;
    AVCodecContext *audio_enc_ctx;
    double video_rotate;

    int in_video_stream_index;
    int in_audio_stream_index;
    int out_video_stream_index;     // 0
} media_info;


static double video_get_rotation(AVStream *st)
{
    uint8_t* displaymatrix = av_stream_get_side_data(st,
                                                     AV_PKT_DATA_DISPLAYMATRIX, NULL);
    double theta = 0;
    if (displaymatrix)
        theta = -av_display_rotation_get((int32_t*) displaymatrix);

    theta -= 360*floor(theta/360 + 0.9/360);

    if (fabs(theta - 90*round(theta/90)) > 2)
        av_log(NULL, AV_LOG_WARNING, "Odd rotation angle.\n"
                                     "If you want to help, upload a sample "
                                     "of this file to ftp://upload.ffmpeg.org/incoming/ "
                                     "and contact the ffmpeg-devel mailing list. (ffmpeg-devel@ffmpeg.org)");

    return theta;
}



/**
 * 打开文件
 * @param filename 文件路径
 * @param info 读取文件数据
 * @param type选择读取是什么AVStream,0是读取所有
 * @return
 */
 int open_input_video(const char *filename,media_info **info,int type){
    AVFormatContext* fmt_ctx =NULL;

    AVDictionary *opts = NULL;
    media_info* streamEntity;
    int ret;

    streamEntity = (media_info*)av_mallocz_array(1, sizeof(media_info));
    if (!streamEntity){
        MGTED_ERROR("=======av_mallocz_array=error==");
        return AVERROR(ENOMEM);
    }

    streamEntity->in_video_stream_index = -1;
    streamEntity->out_video_stream_index = 0;
    streamEntity->video_rotate = 0;
    
    if((ret=avformat_open_input(&fmt_ctx,filename,NULL,NULL))<0){
        MGTED_ERROR("=======avformat_open_input=ret=%d",ret);
        return ret;
    }
    fmt_ctx->probesize = 5000000 << 1;//从源文件中读取的最大字节数,单位为字节
    fmt_ctx->max_analyze_duration  = 90*AV_TIME_BASE;//是从文件中读取的最大时长,单位为 AV_TIME_BASE
    if((ret=avformat_find_stream_info(fmt_ctx,NULL))<0){
        MGTED_ERROR("=======avformat_find_stream_info==ret=%d",ret);
        return ret;
    }
    for (int i = 0; i < fmt_ctx->nb_streams; ++i) {
        AVStream* stream= fmt_ctx->streams[i];
        if (type==1){
            if(stream->codecpar->codec_type==AVMEDIA_TYPE_VIDEO){
                continue;
            }
        } else if (type==2){
            if(stream->codecpar->codec_type==AVMEDIA_TYPE_AUDIO){
                continue;
            }
        }
        if(stream->codecpar->codec_type==AVMEDIA_TYPE_VIDEO||
           stream->codecpar->codec_type==AVMEDIA_TYPE_AUDIO){
            AVCodec* codec=avcodec_find_decoder(stream->codecpar->codec_id);
            if(!codec){
                MGTED_ERROR("====avcodec_find_decoder=error=i=%d",i);
                return AVERROR_DECODER_NOT_FOUND;
            }
            AVCodecContext* avCodecContext=avcodec_alloc_context3(codec);
            if(!avCodecContext){
                MGTED_ERROR("====avcodec_alloc_context3=error=i=%d",i);
                return AVERROR(ENOMEM);
            }
           // avCodecContext->thread_count = 4; //设置解码器线程数
            ret=avcodec_parameters_to_context(avCodecContext,stream->codecpar);
            if(ret<0){
                MGTED_ERROR("====avcodec_parameters_to_context=ret=%d=i=%d",ret,i);
                return ret;
            }
            if(avCodecContext->codec_type==AVMEDIA_TYPE_VIDEO){
                /* Open decoder */
                if (!av_dict_get(opts, "threads", NULL, 0))
                    av_dict_set(&opts, "threads", "auto", 0);
                av_dict_set(&opts, "refcounted_frames", "0", 0);
                ret = avcodec_open2(avCodecContext,codec,NULL);
                if(ret<0){
                    MGTED_ERROR("====avcodec_open2=ret=%d=i=%d",ret,i);
                    return ret;
                }
                int angle=video_get_rotation(stream);
                streamEntity[0].video_dec_ctx = avCodecContext;
                streamEntity[0].in_video_stream_index = i;
                streamEntity[0].video_rotate = angle;
            } else{
                ret = avcodec_open2(avCodecContext,codec,NULL);
                if(ret<0){
                    MGTED_ERROR("====avcodec_open2=ret=%d=i=%d",ret,i);
                    return ret;
                }

                streamEntity[0].audio_dec_ctx = avCodecContext;
                streamEntity[0].in_audio_stream_index = i;
            }

        }
    }
    streamEntity[0].dec_fmt_ctx=fmt_ctx;
    *info = streamEntity;
    return ret;
}

ffmpeg读AVPacket包

下面对媒体文件进行读包,下面代码封装媒体读包处理,直播流要进行改,正常读包可以参考,为什么要读包,对相应包可以做对应解码,通过上面代码解封装可以获取dec_fmt_ctx格式上下文就可以读报。

        while(1){
           AVPacket packet = { .data = NULL, .size = 0 };
           ret=av_read_frame(info->dec_fmt_ctx,&packet);
           if(ret>=0){
               if(packet.stream_index==AVMEDIA_TYPE_VIDEO ){//视频包
                   av_packet_unref(&packet);
               } else if(packet.stream_index==AVMEDIA_TYPE_AUDIO){//音频包
                   av_packet_unref(&packet);
               } else{
                   av_packet_unref(&packet);
               }
           } else{
               av_packet_unref(&packet);
               if (ret == AVERROR(EAGAIN)) {
                   av_usleep(10000);//延迟10ms
               }else if (ret ==AVERROR_EOF) {//结束
                   break;
               }  else{//错误
                   break;
               }
           }
       }