音视频入门基础:MPEG2-TS专题(21)——FFmpeg源码中,获取TS流的视频信息的实现

发布于:2024-12-22 ⋅ 阅读:(10) ⋅ 点赞:(0)

一、引言

通过FFmpeg命令可以获取到TS文件/TS流的视频压缩编码格式、色彩格式(像素格式)、分辨率、帧率信息:

./ffmpeg -i XXX.ts

本文以H.264为例讲述FFmpeg到底是从哪个地方获取到这些视频信息的。 

二、视频压缩编码格式

FFmpeg获取TS文件/TS流的视频压缩编码格式,是从PMT表的stream_type属性中获取的。由《音视频入门基础:MPEG2-TS专题(16)——PMT简介》可以知道,TS文件/TS流的PMT表中存在一个占8位的stream_type属性,表示媒体流的类型,即音视频压缩编码格式:

由《音视频入门基础:MPEG2-TS专题(17)——FFmpeg源码中,解析TS program map section的实现》可以知道,FFmpeg源码中使用pmt_cb函数解析PMT表中的TS program map section。pmt_cb函数会通过下面代码块将stream_type属性提取出来,赋值给变量stream_type:

    for (i = 0; i < MAX_STREAMS_PER_PROGRAM; i++) {
        st = 0;
        pes = NULL;
        stream_type = get8(&p, p_end);
        if (stream_type < 0)
            break;
    //...
}

然后pmt_cb函数中,会调用mpegts_set_stream_info函数:

        if (pes && !pes->stream_type)
            mpegts_set_stream_info(st, pes, stream_type, prog_reg_desc);

mpegts_set_stream_info函数内部又会将上述得到的stream_type属性赋值给变量pes->stream_type,然后调用mpegts_find_stream_type函数:

static int mpegts_set_stream_info(AVStream *st, PESContext *pes,
                                  uint32_t stream_type, uint32_t prog_reg_desc)
{
//...
    pes->stream_type = stream_type;
//...
    mpegts_find_stream_type(st, pes->stream_type, ISO_types);
//...
}

mpegts_find_stream_type函数代码如下:

static void mpegts_find_stream_type(AVStream *st,
                                    uint32_t stream_type,
                                    const StreamType *types)
{
    FFStream *const sti = ffstream(st);
    for (; types->stream_type; types++)
        if (stream_type == types->stream_type) {
            if (st->codecpar->codec_type != types->codec_type ||
                st->codecpar->codec_id   != types->codec_id) {
                st->codecpar->codec_type = types->codec_type;
                st->codecpar->codec_id   = types->codec_id;
                sti->need_context_update = 1;
            }
            sti->request_probe = 0;
            return;
        }
}

ISO_types是一个数组,根据上述表格将stream_type属性和音视频压缩编码格式对应起来。所以调用mpegts_find_stream_type函数后,st->codecpar->codec_type,即AVCodecParameters的codec_id会得到该stream_type属性对应的音视频压缩编码格式。比如stream_type的值为0x1B,那对应的视频压缩编码格式就是H.264:

static const StreamType ISO_types[] = {
    { 0x01, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG2VIDEO },
    { 0x02, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG2VIDEO },
    { 0x03, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP3        },
    { 0x04, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP3        },
    { 0x0f, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AAC        },
    { 0x10, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG4      },
    /* Makito encoder sets stream type 0x11 for AAC,
     * so auto-detect LOAS/LATM instead of hardcoding it. */
#if !CONFIG_LOAS_DEMUXER
    { 0x11, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AAC_LATM   }, /* LATM syntax */
#endif
    { 0x1b, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H264       },
    { 0x1c, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AAC        },
    { 0x20, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H264       },
    { 0x21, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_JPEG2000   },
    { 0x24, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_HEVC       },
    { 0x33, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_VVC        },
    { 0x42, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_CAVS       },
    { 0xd1, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_DIRAC      },
    { 0xd2, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_AVS2       },
    { 0xd4, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_AVS3       },
    { 0xea, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_VC1        },
    { 0 },
};

然后在pmt_cb函数外部,通过avcodec_parameters_to_context函数将AVCodecParameters的codec_id赋值给AVCodecContext的codec_id:

int avcodec_parameters_to_context(AVCodecContext *codec,
                                  const AVCodecParameters *par)
{
//...
    codec->codec_id   = par->codec_id;
//...
}

然后在dump_stream_format函数中,通过avcodec_string函数中的语句:codec_name = avcodec_get_name(enc->codec_id) 拿到AVCodecContext的codec_id对应的视频压缩编码格式名称。最后再在dump_stream_format函数中将视频压缩编码格式打印出来:

void avcodec_string(char *buf, int buf_size, AVCodecContext *enc, int encode)
{
//...
    codec_name = avcodec_get_name(enc->codec_id);
//...
}

所以FFmpeg获取TS文件/TS流的视频压缩编码格式,是根据PMT表的stream_type属性获取的:

三、视频压缩编码格式的profile

如果TS文件/TS流中的视频压缩编码格式为H.264,FFmpeg获取其视频压缩编码格式的profile,是通过SPS的profile_idc属性获取到的,具体可以参考:《音视频入门基础:H.264专题(17)——FFmpeg源码中,获取H.264视频的profile的实现》:

四、视频的色彩格式

如果TS文件/TS流中的视频压缩编码格式为H.264,FFmpeg获取其视频的色彩格式,是通过SPS中的属性chroma_format_idc获取到的,具体可以参考:《音视频入门基础:H.264专题(13)——FFmpeg源码中通过SPS属性获取视频色彩格式的实现》:

五、视频分辨率

如果TS文件/TS流中的视频压缩编码格式为H.264,FFmpeg获取其视频分辨率,是通过SPS中的属性获取的,具体可以参考:《音视频入门基础:H.264专题(12)——FFmpeg源码中通过SPS属性计算视频分辨率的实现》:

六、视频码率

由于TS文件/TS流的文件格式(包括TS Header、PES packet header)不包含视频码率信息,所以无法通过FFmpeg直接获取到其视频码率。与之对应,由于FLV文件的Script Tag中包含视频码率信息,所以FFmpeg可以直接打印FLV文件的视频码率,具体可以参考:《音视频入门基础:FLV专题(24)——FFmpeg源码中,获取FLV文件视频信息的实现》。

七、视频帧率

如果TS文件/TS流中的视频压缩编码格式为H.264,对其视频进行编解码时,FFmpeg源码内部使用的是通过SPS中的属性计算得到的视频帧率(具体可以参考:《音视频入门基础:H.264专题(15)——FFmpeg源码中通过SPS属性获取视频帧率的实现》)。