音视频入门基础:FLV专题(20)——FFmpeg源码中,获取FLV文件major_brand、minor_version、compatible_brands、encoder、Duration的实现

一、引言

通过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。

猜你喜欢

转载自blog.csdn.net/u014552102/article/details/143377987