音视频开发12 FFmpeg 解复用详情

解封装

封装格式相关函数

avformat_alloc_context(); 负责申请一个AVFormatContext
结构的内存,并进行简单初始化
avformat_free_context(); 释放该结构里的所有东西以及该
结构本身
avformat_close_input(); 关闭解复用器。关闭后就不再需要
使用avformat_free_context 进行释放。
avformat_open_input(); 打开输入视频文件
avformat_find_stream_info() :获取视频文件信息
av_read_frame(); 读取音视频包
avformat_seek_file(); 定位文件
av_seek_frame(): 定位文件

解封装流程

代码和关键信息说明

#include <stdio.h>
#include <libavformat/avformat.h>


int main(int argc, char **argv)
{
    //打开网络流。这里如果只需要读取本地媒体文件,不需要用到网络功能,可以不用加上这一句
//    avformat_network_init();

//    const char *default_filename = "D:/AllInformation/qtworkspacenew/07-01-ffmpeg-demux/believe.mp4";
 //   const char *default_filename = "D:/AllInformation/qtworkspacenew/07-01-ffmpeg-demux/believe.flv";
    const char *default_filename = "D:/AllInformation/qtworkspacenew/07-01-ffmpeg-demux/believe.ts";

    char *in_filename = NULL;

    if(argv[1] == NULL)
    {
        in_filename = default_filename;
    }
    else
    {
        in_filename = argv[1];
    }
    printf("in_filename = %s\n", in_filename);

    //AVFormatContext是描述一个媒体文件或媒体流的构成和基本信息的结构体
    AVFormatContext *ifmt_ctx = NULL;           // 输入文件的demux

    int videoindex = -1;        // 视频索引
    int audioindex = -1;        // 音频索引


    // 1.打开文件,主要是探测协议类型,如果是网络文件则创建网络链接
    int ret = avformat_open_input(&ifmt_ctx, in_filename, NULL, NULL);
    if (ret < 0)  //如果打开媒体文件失败,打印失败原因
    {
        char buf[1024] = { 0 };
        av_strerror(ret, buf, sizeof(buf) - 1);
        printf("open %s failed:%s\n", in_filename, buf);
        goto failed;
    }

    //2.读取媒体的部分数据包以获取码流信息。这行代码在不同的 文件格式下mp4,flv,ts下,注释这段代码和不注释这段代码 效果不同
    //这应该和 mp4,flv,ts 的格式相关
    ret = avformat_find_stream_info(ifmt_ctx, NULL);
    if (ret < 0)  //如果打开媒体文件失败,打印失败原因
    {
        char buf[1024] = { 0 };
        av_strerror(ret, buf, sizeof(buf) - 1);
        printf("avformat_find_stream_info %s failed:%s\n", in_filename, buf);
        goto failed;
    }

    //3.av_dump_format 用于打印 输入AVFormatContext 的关键信息。
    printf_s("\n==== av_dump_format in_filename:%s ===\n", in_filename);
    av_dump_format(ifmt_ctx, 0, in_filename, 0);
    printf_s("\n==== av_dump_format finish =======\n\n");

    //4.打印其他相关的信息
    // 4.1 url: 调用avformat_open_input读取到的媒体文件的路径/名字
    printf("media name:%s\n", ifmt_ctx->url);
    // 4.2 nb_streams: nb_streams媒体流数量
    printf("stream number:%d\n", ifmt_ctx->nb_streams);
    // 4.3 bit_rate: 媒体文件的码率,单位为bps
    printf("media average ratio:%lldkbps\n",(int64_t)(ifmt_ctx->bit_rate/1024));
    // 时间
    int total_seconds, hour, minute, second;
    // 4.4 duration: 媒体文件时长,单位微妙
    total_seconds = (ifmt_ctx->duration) / AV_TIME_BASE;  // 1000us = 1ms, 1000ms = 1秒
    hour = total_seconds / 3600;
    minute = (total_seconds % 3600) / 60;
    second = (total_seconds % 60);
    //4.5 通过上述运算,可以得到媒体文件的总时长
    printf("total duration: %02d:%02d:%02d\n", hour, minute, second);
    printf("\n");
    /*
     * 5.获得 音频和视频的信息 -- 老版本通过遍历的方式读取媒体文件视频和音频的信息
     * 新版本的FFmpeg新增加了函数 av_find_best_stream ,也可以取得同样的效果
     */
    for (uint32_t i = 0; i < ifmt_ctx->nb_streams; i++)
    {
        AVStream *in_stream = ifmt_ctx->streams[i];// 音频流、视频流、字幕流
        //如果是音频流,则打印音频的信息
        if (AVMEDIA_TYPE_AUDIO == in_stream->codecpar->codec_type)
        {
            printf("----- Audio info:\n");
            // index: 每个流成分在ffmpeg解复用分析后都有唯一的index作为标识
            printf("index:%d\n", in_stream->index);
            // sample_rate: 音频编解码器的采样率,单位为Hz
            printf("samplerate:%dHz\n", in_stream->codecpar->sample_rate);
            // codecpar->format: 音频采样格式
            if (AV_SAMPLE_FMT_FLTP == in_stream->codecpar->format)
            {
                printf("sampleformat:AV_SAMPLE_FMT_FLTP\n");
            }
            else if (AV_SAMPLE_FMT_S16P == in_stream->codecpar->format)
            {
                printf("sampleformat:AV_SAMPLE_FMT_S16P\n");
            }
            // channels: 音频信道数目
            printf("channel number:%d\n", in_stream->codecpar->ch_layout.nb_channels);
            // codec_id: 音频压缩编码格式
            if (AV_CODEC_ID_AAC == in_stream->codecpar->codec_id)
            {
                printf("audio codec:AAC\n");
            }
            else if (AV_CODEC_ID_MP3 == in_stream->codecpar->codec_id)
            {
                printf("audio codec:MP3\n");
            }
            else
            {
                printf("audio codec_id:%d\n", in_stream->codecpar->codec_id);
            }
            // 音频总时长,单位为秒。注意如果把单位放大为毫秒或者微妙,音频总时长跟视频总时长不一定相等的
            if(in_stream->duration != AV_NOPTS_VALUE)
            {
                int duration_audio = (in_stream->duration) * av_q2d(in_stream->time_base);
                //将音频总时长转换为时分秒的格式打印到控制台上
                printf("audio duration: %02d:%02d:%02d\n",
                       duration_audio / 3600, (duration_audio % 3600) / 60, (duration_audio % 60));
            }
            else
            {
                printf("audio duration unknown");
            }

            printf("\n");

            audioindex = i; // 获取音频的索引
        }
        else if (AVMEDIA_TYPE_VIDEO == in_stream->codecpar->codec_type)  //如果是视频流,则打印视频的信息
        {
            printf("----- Video info:\n");
            printf("index:%d\n", in_stream->index);
            // avg_frame_rate: 视频帧率,单位为fps,表示每秒出现多少帧
            printf("fps:%lffps\n", av_q2d(in_stream->avg_frame_rate));
            if (AV_CODEC_ID_MPEG4 == in_stream->codecpar->codec_id) //视频压缩编码格式
            {
                printf("video codec:MPEG4\n");
            }
            else if (AV_CODEC_ID_H264 == in_stream->codecpar->codec_id) //视频压缩编码格式
            {
                printf("video codec:H264\n");
            }
            else
            {
                printf("video codec_id:%d\n", in_stream->codecpar->codec_id);
            }
            // 视频帧宽度和帧高度
            printf("width:%d height:%d\n", in_stream->codecpar->width,
                   in_stream->codecpar->height);
            //视频总时长,单位为秒。注意如果把单位放大为毫秒或者微妙,音频总时长跟视频总时长不一定相等的
            if(in_stream->duration != AV_NOPTS_VALUE)
            {
                int duration_video = (in_stream->duration) * av_q2d(in_stream->time_base);
                printf("video duration: %02d:%02d:%02d\n",
                       duration_video / 3600,
                       (duration_video % 3600) / 60,
                       (duration_video % 60)); //将视频总时长转换为时分秒的格式打印到控制台上
            }
            else
            {
                printf("video duration unknown");
            }

            printf("\n");
            videoindex = i;
        }
    }

    // 5. 新版使用 av_find_best_stream 方法达到对应的目的。如下的第五个参数是NULL,还是指定的AVCodec ,返回值都一样,对应的参数也一样。
//    int bestvideoindex =  av_find_best_stream(ifmt_ctx,AVMEDIA_TYPE_VIDEO,-1,-1,&(ifmt_ctx->video_codec),0);
        int bestvideoindex =  av_find_best_stream(ifmt_ctx,AVMEDIA_TYPE_VIDEO,-1,-1,NULL,0);
   AVStream* bestvideostream =  ifmt_ctx->streams[bestvideoindex];
   {
       printf("----- av_find_best_stream Video info:\n");
       printf("bestvideoindex:%d\n", bestvideoindex);
       // avg_frame_rate: 视频帧率,单位为fps,表示每秒出现多少帧
       printf("bestvideoindex fps:%lffps\n", av_q2d(bestvideostream->avg_frame_rate));
       if (AV_CODEC_ID_MPEG4 == bestvideostream->codecpar->codec_id) //视频压缩编码格式
       {
           printf("bestvideoindex video codec:MPEG4\n");
       }
       else if (AV_CODEC_ID_H264 == bestvideostream->codecpar->codec_id) //视频压缩编码格式
       {
           printf("bestvideoindex video codec:H264\n");
       }
       else
       {
           printf("bestvideoindex video codec_id:%d\n", bestvideostream->codecpar->codec_id);
       }
       // 视频帧宽度和帧高度
       printf("bestvideoindex width:%d height:%d\n", bestvideostream->codecpar->width,
              bestvideostream->codecpar->height);
       //视频总时长,单位为秒。注意如果把单位放大为毫秒或者微妙,音频总时长跟视频总时长不一定相等的
       if(bestvideostream->duration != AV_NOPTS_VALUE)
       {
           int duration_video = (bestvideostream->duration) * av_q2d(bestvideostream->time_base);
           printf("bestvideoindex video duration: %02d:%02d:%02d\n",
                  duration_video / 3600,
                  (duration_video % 3600) / 60,
                  (duration_video % 60)); //将video stream的总时长 转换为时分秒的格式打印到控制台上
       }
       else
       {
           printf("bestvideoindex video duration unknown");
       }

       printf("\n");
       videoindex = bestvideoindex;
   }


   //6. 从文件中读取数据,使用 av_read_frame 读取,读取到的内存要存储到 AVPacket 中,因此要先 使用 av_packet_alloc函数创建一个 AVPacket,当然因为是alloc出来的,用完后要free
    AVPacket *pkt = av_packet_alloc();

    int pkt_count = 0;
    int print_max_count = 10;
    printf("\n-----av_read_frame start\n");
    while (1)
    {
        //6.1 使用 av_read_frame 开始读取,读取的数据如果是图片,则packet中是一帧,如果是音频 packet中则是很多帧,这里要参考aac 和mp3的spec
        //音频aac、mp3文档规定:
        //AAC:帧大小1024个sample,采样率为44100Hz ,帧播放时长:acc dur=1024/44100 = 0.02322s=23.22ms
        //MP3:帧大小1152个sample,采样率为44100Hz ,帧播放时长:  mp3 dur=1152/44100 = 0.02608s=26.08ms
        ret = av_read_frame(ifmt_ctx, pkt);
        if (ret < 0)
        {
            printf("av_read_frame end\n");
            break;
        }

        if(pkt_count++ < print_max_count)
        {
            if (pkt->stream_index == audioindex)
            {
//                PTS:Presentation Time Stamp。PTS 主要用于度量解码后的视频帧什么时候被显示出来。
//                DTS:Decode Time Stamp。DTS 主要是标识读入内存中的Bit流在什么时候开始送入解码器中进行解码。
//                虽然 DTS、PTS 是用于指导播放端的行为,但它们是在编码的时候由编码器生成的。当视频流中没有 B 帧时,通常 DTS 和 PTS 的顺序是一致的。
//                但如果有 B 帧时,解码顺序和播放顺序不一致了。
                printf("audio pts: %lld\n", pkt->pts);
                printf("audio dts: %lld\n", pkt->dts);
                printf("audio size: %d\n", pkt->size);//该packet的大小
                printf("audio pos: %lld\n", pkt->pos);//该packet 在mp4/flv/ts文件中的大致位置
                //duration表示 此数据包的持续时间,单位为微妙,要转化为秒。
                //注意和前面的
                printf("audio duration: %lf\n\n",
                       pkt->duration * av_q2d(ifmt_ctx->streams[audioindex]->time_base));
            }
            else if (pkt->stream_index == videoindex)
            {
                printf("video pts: %lld\n", pkt->pts);
                printf("video dts: %lld\n", pkt->dts);
                printf("video size: %d\n", pkt->size);
                printf("video pos: %lld\n", pkt->pos);
                printf("video duration: %lf\n\n",
                       pkt->duration * av_q2d(ifmt_ctx->streams[videoindex]->time_base));
            }
            else
            {
                //注意,如果我们当前测试的文件是有两个 音频流的,因此使用 audiobestIndex会将一个漏掉
                printf("unknown stream_index:\n", pkt->stream_index);
            }
        }

        av_packet_unref(pkt);
    }

    if(pkt)
        av_packet_free(&pkt);
failed:
    if(ifmt_ctx)
        avformat_close_input(&ifmt_ctx);


    getchar(); //加上这一句,防止程序打印完信息马上退出
    return 0;
}

关键信息说明

不同类型文件的log :

  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值