=================================================================
音视频入门基础:FLV专题系列文章:
音视频入门基础:FLV专题(2)——使用FFmpeg命令生成flv文件
音视频入门基础:FLV专题(3)——FLV header简介
音视频入门基础:FLV专题(4)——使用flvAnalyser工具分析FLV文件
音视频入门基础:FLV专题(5)——FFmpeg源码中,判断某文件是否为FLV文件的实现
音视频入门基础:FLV专题(6)——FFmpeg源码中,解码FLV header的实现
音视频入门基础:FLV专题(7)——Tag header简介
音视频入门基础:FLV专题(8)——FFmpeg源码中,解码Tag header的实现
音视频入门基础:FLV专题(9)——Script Tag简介
音视频入门基础:FLV专题(10)——Script Tag实例分析
音视频入门基础:FLV专题(11)——FFmpeg源码中,解析SCRIPTDATASTRING类型的ScriptDataValue的实现
音视频入门基础:FLV专题(12)——FFmpeg源码中,解析DOUBLE类型的ScriptDataValue的实现
音视频入门基础:FLV专题(13)——FFmpeg源码中,解析任意Type值的SCRIPTDATAVALUE类型的实现
音视频入门基础:FLV专题(14)——FFmpeg源码中,解码Script Tag的实现
音视频入门基础:FLV专题(15)——Video Tag简介
音视频入门基础:FLV专题(16)——FFmpeg源码中,解码Video Tag的VideoTagHeader的实现
音视频入门基础:FLV专题(17)——FFmpeg源码中,提取Video Tag的VIDEODATA的实现
音视频入门基础:FLV专题(18)——Audio Tag简介
音视频入门基础:FLV专题(19)——FFmpeg源码中,解码Audio Tag的AudioTagHeader,并提取AUDIODATA的实现
音视频入门基础:FLV专题(20)——FFmpeg源码中,获取FLV文件major_brand、minor_version、compatible_brands、encoder、Duration的实现
音视频入门基础:FLV专题(21)——FFmpeg源码中,获取FLV文件音频信息的实现(上)
音视频入门基础:FLV专题(22)——FFmpeg源码中,获取FLV文件音频信息的实现(中)
音视频入门基础:FLV专题(23)——FFmpeg源码中,获取FLV文件音频信息的实现(下)
音视频入门基础:FLV专题(24)——FFmpeg源码中,获取FLV文件视频信息的实现
音视频入门基础:FLV专题(25)——通过FFprobe显示FLV文件每个packet的信息
=================================================================
一、引言
通过FFmpeg命令可以获取到FLV文件的视频压缩编码格式、色彩格式(像素格式)、分辨率、码率、帧率信息:
而由《音视频入门基础:FLV专题(9)——Script Tag简介》和《音视频入门基础:FLV专题(15)——Video Tag简介》可以知道,FLV文件中名称为“onMetadata”的Script Tag、每个Video Tag的VideoTagHeader、AVCDecoderConfigurationRecord(extradata)都会包含视频信息。
名称为“onMetadata”的Script Tag:
每个Video Tag的VideoTagHeader:
AVCDecoderConfigurationRecord:
可以看到,FLV文件中的上述三个地方都会存在视频信息,有些信息比如:视频压缩编码格式、视频帧率、视频分辨率甚至是重复的。所以FFmpeg到底获取的是哪个地方的视频信息呢,本文为大家揭开谜底。
二、视频压缩编码格式
FLV文件中名称为“onMetadata”的Script Tag、每个Video Tag的VideoTagHeader都包含视频压缩编码格式信息。但是FFmpeg获取FLV文件的视频压缩编码格式,是从Video Tag的VideoTagHeader的CodecID属性中获取的、而忽略onMetadata中的:
FFmpeg源码从Video Tag的VideoTagHeader中获取视频压缩编码格式的内部实现原理可以参考:《音视频入门基础:FLV专题(16)——FFmpeg源码中,解码Video Tag的VideoTagHeader的实现》。
三、视频压缩编码格式的profile
FFmpeg获取FLV文件的视频压缩编码格式的profile,是通过SPS的profile_idc属性获取到的,具体可以参考:《音视频入门基础:H.264专题(17)——FFmpeg源码中,获取H.264视频的profile的实现》:
四、视频的色彩格式
FFmpeg获取FLV文件视频的色彩格式,是通过SPS中的属性chroma_format_idc获取到的,具体可以参考:《音视频入门基础:H.264专题(13)——FFmpeg源码中通过SPS属性获取视频色彩格式的实现》:
五、视频分辨率
FLV文件中名称为“onMetadata”的Script Tag、AVCDecoderConfigurationRecord中的SPS都包含视频分辨率信息。但是FFmpeg获取FLV文件的视频分辨率,是通过SPS中的属性获取的、而忽略onMetadata中的。具体可以参考:《音视频入门基础:H.264专题(12)——FFmpeg源码中通过SPS属性计算视频分辨率的实现》:
六、视频码率
FFmpeg获取FLV文件中的视频码率是通过解析名称为“onMetadata”的Script Tag获取的。具体原理跟获取FLV文件中的音频码率一致,具体可以参考:《音视频入门基础:FLV专题(23)——FFmpeg源码中,获取FLV文件音频信息的实现(下)》。由于FFmpeg源码有bug,所以通过FFmpeg命令显示的视频码率不准确:
七、视频帧率
FLV文件中名称为“onMetadata”的Script Tag、SPS中的属性都包含视频帧率。对FLV文件中的视频进行编解码时,FFmpeg源码内部使用的是通过SPS中的属性计算得到的视频帧率(具体可以参考:《音视频入门基础:H.264专题(15)——FFmpeg源码中通过SPS属性获取视频帧率的实现》),但是通过“ffmpeg -i XXX.flv”命令打印FLV文件的视频帧率时,使用的是“onMetadata”中的视频帧率:
由《音视频入门基础:FLV专题(13)——FFmpeg源码中,解析任意Type值的SCRIPTDATAVALUE类型的实现》可以知道,amf_parse_object函数中通过下面代码块将名称为“onMetadata”的Script Tag中的framerate属性解析出来。将该属性存到flv->framerate中:
// stream info doesn't live any deeper than the first object
if (depth == 1) {
if (amf_type == AMF_DATA_TYPE_NUMBER ||
amf_type == AMF_DATA_TYPE_BOOL) {
//...
else if (!strcmp(key, "framerate")) {
flv->framerate = av_d2q(num_val, 1000);
if (vstream)
vstream->avg_frame_rate = flv->framerate;
}
//...
}
if (amf_type == AMF_DATA_TYPE_STRING) {
if (!strcmp(key, "encoder")) {
int version = -1;
if (1 == sscanf(str_val, "Open Broadcaster Software v0.%d", &version)) {
if (version > 0 && version <= 655)
flv->broken_sizes = 1;
}
} else if (!strcmp(key, "metadatacreator")) {
if ( !strcmp (str_val, "MEGA")
|| !strncmp(str_val, "FlixEngine", 10))
flv->broken_sizes = 1;
}
}
}
然后在libavformat/flvdec.c的create_stream函数中,通过语句:st->avg_frame_rate = flv->framerate 将flv->framerate赋值给AVStream的avg_frame_rate:
static AVStream *create_stream(AVFormatContext *s, int codec_type)
{
FLVContext *flv = s->priv_data;
AVStream *st = avformat_new_stream(s, NULL);
if (!st)
return NULL;
//...
if (codec_type == AVMEDIA_TYPE_VIDEO) {
//...
st->avg_frame_rate = flv->framerate;
}
return st;
}
最后再在dump_stream_format函数中,将这个AVStream的avg_frame_rate打印出来:
static void dump_stream_format(const AVFormatContext *ic, int i,
int group_index, int index, int is_output,
int log_level)
{
//...
if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
int fps = st->avg_frame_rate.den && st->avg_frame_rate.num;
int tbr = st->r_frame_rate.den && st->r_frame_rate.num;
int tbn = st->time_base.den && st->time_base.num;
if (fps || tbr || tbn)
av_log(NULL, log_level, "%s", separator);
if (fps)
print_fps(av_q2d(st->avg_frame_rate), tbr || tbn ? "fps, " : "fps", log_level);
}
//...
}
标签:FFmpeg,音视频,Tag,FLV,源码,入门
From: https://blog.csdn.net/u014552102/article/details/143460886