一、引言
通过FFmpeg命令可以获取到FLV文件的major_brand、minor_version、compatible_brands、encoder、Duration信息:
./ffmpeg -i XXX.flv
其中:
1.major_brand:表示“最好”基于哪种格式来解析当前的文件。
2.minor_version:提供 major_brand 的说明信息,比如版本号。
3.compatible_brands:文件兼容的brand列表。
4.encoder:编码器。
5.Duration:该FLV文件的总时长,以秒为单位。
所以FFmpeg怎样获取到上述信息的呢?它其实是通过解析名称为“onMetadata”的Script Tag获取的。FLV文件中存在一个特殊的Script Tag,它的ScriptTagBody中的Name属性中的StringData属性存贮“onMetadata”字符串,而它的ScriptTagBody中的Value属性中的Variables数组中的每个元素都分别为一个元数据属性。用来存贮当前文件的一些基本信息。具体可以参考:《音视频入门基础:FLV专题(9)——Script Tag简介》:
FFmpeg源码中通过flv_read_packet函数解码Script Tag,而flv_read_packet函数中又会调用flv_read_metabody函数,flv_read_metabody函数内部又会通过amf_parse_object函数将名称为“onMetadata”的Script Tag中的major_brand、minor_version、compatible_brands、encoder、Duration属性解析出来。具体可以参考:《音视频入门基础:FLV专题(13)——FFmpeg源码中,解析任意Type值的SCRIPTDATAVALUE类型的实现》、《音视频入门基础:FLV专题(14)——FFmpeg源码中,解码Script Tag的实现》。
二、FFmpeg源码中,获取FLV文件major_brand、minor_version、compatible_brands、encoder的实现
amf_parse_object函数中通过下面代码块将名称为“onMetadata”的Script Tag中的major_brand、minor_version、compatible_brands、encoder属性解析出来,存到s->metadata中:
static int amf_parse_object(AVFormatContext *s, AVStream *astream,
AVStream *vstream, const char *key,
int64_t max_pos, int depth)
{
//...
char str_val[1024];
//...
switch (amf_type) {
case AMF_DATA_TYPE_STRING:
if (amf_get_string(ioc, str_val, sizeof(str_val)) < 0) {
av_log(s, AV_LOG_ERROR, "AMF_DATA_TYPE_STRING parsing failed\n");
return -1;
}
break;
//...
}
//..
if (key) {
//...
else if (amf_type == AMF_DATA_TYPE_STRING) {
av_dict_set(&s->metadata, key, str_val, 0);
}
//...
}
}
然后在av_dump_format函数中,通过语句:dump_metadata(NULL, ic->metadata, " ", AV_LOG_INFO)将major_brand、minor_version、compatible_brands、encoder这些属性打印出来。形参ic和上面的变量s都是指针,指向同一个AVFormatContext变量。所以s->metadata和上面的ic->metadata的数据一样:
void av_dump_format(AVFormatContext *ic, int index,
const char *url, int is_output)
{
//...
dump_metadata(NULL, ic->metadata, " ", AV_LOG_INFO);
//...
三、FFmpeg源码中,获取FLV文件Duration的实现
amf_parse_object函数中通过下面代码块将名称为“onMetadata”的Script Tag中的Duration属性解析出来,存到存到s->duration中:
static int amf_parse_object(AVFormatContext *s, AVStream *astream,
AVStream *vstream, const char *key,
int64_t max_pos, int depth)
{
//...
if (key) {
//...
if (depth == 1) {
if (amf_type == AMF_DATA_TYPE_NUMBER ||
amf_type == AMF_DATA_TYPE_BOOL) {
if (!strcmp(key, "duration"))
s->duration = num_val * AV_TIME_BASE;
// ..
}
//...
}
}
}
然后在av_dump_format函数中,通过下面代码块将Duration属性打印出来:
void av_dump_format(AVFormatContext *ic, int index,
const char *url, int is_output)
{
//...
if (!is_output) {
av_log(NULL, AV_LOG_INFO, " Duration: ");
if (ic->duration != AV_NOPTS_VALUE) {
int64_t hours, mins, secs, us;
int64_t duration = ic->duration + (ic->duration <= INT64_MAX - 5000 ? 5000 : 0);
secs = duration / AV_TIME_BASE;
us = duration % AV_TIME_BASE;
mins = secs / 60;
secs %= 60;
hours = mins / 60;
mins %= 60;
av_log(NULL, AV_LOG_INFO, "%02"PRId64":%02"PRId64":%02"PRId64".%02"PRId64"", hours, mins, secs,
(100 * us) / AV_TIME_BASE);
}
//...
}
四、总结
通过命令:./ffmpeg -i XXX.flv,显示的FLV文件的major_brand、minor_version、compatible_brands、encoder、Duration信息,来源于FLV文件中名称为“onMetadata”的Script Tag。