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

第五章 音视频-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;
               }
           }
       }
  • 7
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,针对您的问题,我来简单回答一下。 首先,要明确的是,FFmpeg 是一个开源的跨平台音视频处理库,支持码、编码、封装封装等功能。而封装(Demuxing)是指将音视频封装格式中的音视频析出来,以便后续对音视频流进行码、编码等操作。 在 Android 基于 FFmpeg 开发简易播放器中,我们可以使用 FFmpeg 提供的 API 来进行封装。具体步骤如下: 1. 打开输入文件 使用 FFmpegavformat_open_input() 函数打开要封装音视频文件,该函数返回一个 AVFormatContext 结构体指针,该指针含了输入文件的相关信息。 2. 寻找音视频流 使用 FFmpegavformat_find_stream_info() 函数取输入文件的文件头信息,并寻找其中含的音视频流。该函数会将每个音视频流的信息保存在 AVStream 结构体中。 3. 选择音视频流 根据需要播放的音视频流类型,在所有寻找到的音视频流中选择对应的流。可以通过判断 AVStream 结构体中的 codecpar->codec_type 来确定该流是音频流还是视频流。 4. 获取码器 使用 FFmpegavcodec_find_decoder() 函数获取对应的码器,并使用 avcodec_open2() 函数打开码器。 5. 循环取数据 使用 FFmpegav_read_frame() 函数循环音视频数据AVPacket 结构体),并将数据送到码器进行码。 6. 关闭码器和输入文件 在播放完成后,需要使用 avcodec_free_context() 函数释放码器占用的资源,并使用 avformat_close_input() 函数关闭输入文件。 以上就是基于 FFmpeg 进行封装的大致步骤。当然,在实际开发中还有很多细节需要注意,比如错误处理、内存管理等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

baoyu45585

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值