FLV文件的第一个Tag: onMetaData

onMetaData是FLV文件中的第一个Tag, 用来表示当前文件的一些基本信息: 比如视音频的编码类型id、视频的宽和高、文件大小、视频长度、创建日期等。

onMetaData基本信息

***onMetaData in Adobe Flash Video File Format Specification Version 10.1***

可以看出onMetaData是一个SCRIPTDATA类型的Tag.

用于表示视音频编码的两个属性: audiocodecidvideocodecid

  • audiocodecid
    音频的编码类型的id,如AACaudiocodecid=10
***audiocodecid list***
  • videocodecid
    视频的编码类型的id,如AVCvideocodecid=7
***videocodecid list***

SCRIPTDATA

SCRIPTDATA

可以看出在没有加密的情况下,SCRIPTDATAScriptTagBody组成, 主要包括NameValue两部分:

  • Name
    一个String类型的SCRIPTDATAVALUE

  • Value
    一个ECMA Array类型的SCRIPTDATAVALUE

SCRIPTDATA

SCRIPTDATAVALUE

***Part 1 of SCRIPTDATAVALUE***
***Part 2 of SCRIPTDATAVALUE***
  • SCRIPTDATASTRING
SCRIPTDATASTRING
  • SCRIPTDATAECMAARRAY
SCRIPTDATAECMAARRAY
  • ECMAArrayLength
    数组中的元素个数。
  • Variables
    SCRIPTDATAOBJECTPROPERTY的数组。
***SCRIPTDATAOBJECTPROPERTY***
***属性的格式***
  • List Terminator
    标志着SCRIPTDATAOBJECTPROPERTY数组的结束。
***SCRIPTDATAOBJECTEND***

示意代码

// CPxFLVMuxer.h
enum ePxAMFDataType
{ 
    AMF_NUMBER = 0, AMF_BOOLEAN, AMF_STRING, AMF_OBJECT,
    AMF_MOVIECLIP,      /* reserved, not used */
    AMF_NULL, AMF_UNDEFINED, AMF_REFERENCE, AMF_ECMA_ARRAY, AMF_OBJECT_END,
    AMF_STRICT_ARRAY, AMF_DATE, AMF_LONG_STRING, AMF_UNSUPPORTED,
    AMF_RECORDSET,      /* reserved, not used */
    AMF_XML_DOC, AMF_TYPED_OBJECT,
    AMF_AVMPLUS,        /* switch to AMF3 */
    AMF_INVALID = 0xff
};

char inline *put_byte( char *out_chPut, unsigned char in_uchVal )
{
    out_chPut[0] = in_uchVal;
    return out_chPut+1;
}

char inline *put_be16(char *out_chPut, unsigned short in_usVal )
{
    out_chPut[1] = in_usVal & 0xff;
    out_chPut[0] = in_usVal >> 8;
    return out_chPut+2;
}

char inline *put_be32(char *out_chPut, unsigned int in_unVal )
{
    out_chPut[3] = in_unVal & 0xff;
    out_chPut[2] = in_unVal >> 8;
    out_chPut[1] = in_unVal >> 16;
    out_chPut[0] = in_unVal >> 24;
    return out_chPut+4;
}

char inline *put_amf_string( char *out_chPut, const char *in_chBuf )
{
    uint16_t len = (uint16_t)strlen( in_chBuf );
    out_chPut=put_be16( out_chPut, len );
    memcpy(out_chPut,in_chBuf,len);
    return out_chPut+len;
}

char inline *put_amf_double( char *out_chPut, double in_dBuf )
{
    *out_chPut++ = AMF_NUMBER;  // type: Number
    {
        unsigned char *ci, *co;
        ci = (unsigned char *)&in_dBuf;
        co = (unsigned char *)out_chPut;
        co[0] = ci[7];
        co[1] = ci[6];
        co[2] = ci[5];
        co[3] = ci[4];
        co[4] = ci[3];
        co[5] = ci[2];
        co[6] = ci[1];
        co[7] = ci[0];
    }
    return out_chPut+8;
}

const int g_nMetaData = 1024 * 320;

// CPxFLVMuxer.cpp:
HRESULT CPxFLVMuxer::WriteOnMetaData()
{
    HRESULT hr = NS_NOERROR;

    ...

    //char chMetaData[g_nMetaData];
    char *chMetaData = new char[g_nMetaData];
    if (chMetaData == NULL)
    {
        ...
    }

    char *chMetaDataTemp = (char *)chMetaData;

    // SCRIPTDATASTRING
    chMetaDataTemp = put_byte(chMetaDataTemp, AMF_STRING);
    chMetaDataTemp = put_amf_string(chMetaDataTemp, "onMetaData");

    // SCRIPTDATAECMAARRAY
    chMetaDataTemp = put_byte(chMetaDataTemp, AMF_ECMA_ARRAY);

    // ECMAArrayLength : UI32 属性数组长度
    chMetaDataTemp = put_be32(chMetaDataTemp, 21); 

    // PropertyName
    chMetaDataTemp = put_amf_string(chMetaDataTemp, "MetaDataCreator"); 
    // Type:UI8 数据类型
    chMetaDataTemp = put_byte(chMetaDataTemp, AMF_STRING); 
    // ScriptDataValue: 数据
    chMetaDataTemp = put_amf_string(chMetaDataTemp, "created by ZL Guo @2016"); 

    // PropertyName
    chMetaDataTemp = put_amf_string(chMetaDataTemp, "hasKeyFrames"); 
    // Type:UI8 数据类型
    chMetaDataTemp = put_byte(chMetaDataTemp, AMF_BOOLEAN); 
    // ScriptDataValue: 数据
    chMetaDataTemp = put_byte(chMetaDataTemp, 0); 

    chMetaDataTemp = put_amf_string(chMetaDataTemp, "hasVideo");
    chMetaDataTemp = put_byte(chMetaDataTemp, AMF_BOOLEAN);
    chMetaDataTemp = put_byte(chMetaDataTemp, 1);

    chMetaDataTemp = put_amf_string(chMetaDataTemp, "hasAudio");
    chMetaDataTemp = put_byte(chMetaDataTemp, AMF_BOOLEAN);
    chMetaDataTemp = put_byte(chMetaDataTemp, 1);

    chMetaDataTemp = put_amf_string(chMetaDataTemp, "hasMatadata");
    chMetaDataTemp = put_byte(chMetaDataTemp, AMF_BOOLEAN);
    chMetaDataTemp = put_byte(chMetaDataTemp, 1);

    chMetaDataTemp = put_amf_string(chMetaDataTemp, "canSeekToEnd");
    chMetaDataTemp = put_byte(chMetaDataTemp, AMF_BOOLEAN);
    chMetaDataTemp = put_byte(chMetaDataTemp, 0);

    // PropertyName
    chMetaDataTemp = put_amf_string( chMetaDataTemp, "duration"); 
    // Type:UI8 数据类型 + ScriptDataValue: 数据
    chMetaDataTemp = put_amf_double( chMetaDataTemp, (double)0.0);

    chMetaDataTemp = put_amf_string( chMetaDataTemp, "width");
    chMetaDataTemp = put_amf_double( chMetaDataTemp, (double)m_sPxRecordFileProperty.aStreamProperty[m_nVideoStreamNum].nVideoWidth);

    chMetaDataTemp = put_amf_string( chMetaDataTemp, "height");
    chMetaDataTemp = put_amf_double( chMetaDataTemp, (double)m_sPxRecordFileProperty.aStreamProperty[m_nVideoStreamNum].nVideoHeight);

    chMetaDataTemp = put_amf_string( chMetaDataTemp, "videodatarate");
    chMetaDataTemp = put_amf_double( chMetaDataTemp, (double)m_sPxRecordFileProperty.aStreamProperty[m_nVideoStreamNum].nVideoBitrate);

    chMetaDataTemp = put_amf_string( chMetaDataTemp, "framerate");
    chMetaDataTemp = put_amf_double( chMetaDataTemp, (double)m_sPxRecordFileProperty.aStreamProperty[m_nVideoStreamNum].fVideoFrameRate);

    chMetaDataTemp = put_amf_string( chMetaDataTemp, "videocodecid");
    chMetaDataTemp = put_amf_double( chMetaDataTemp, (double)7.0);

    chMetaDataTemp = put_amf_string( chMetaDataTemp, "audiosamplerate");
    chMetaDataTemp = put_amf_double( chMetaDataTemp, (double)m_sPxRecordFileProperty.aStreamProperty[m_nVideoStreamNum].eAudioSamplesPerSecond);

    chMetaDataTemp = put_amf_string( chMetaDataTemp, "audiosamplesize");
    chMetaDataTemp = put_amf_double( chMetaDataTemp, (double)m_sPxRecordFileProperty.aStreamProperty[m_nVideoStreamNum].nAudioBitsPerSampleint);

    chMetaDataTemp = put_amf_string(chMetaDataTemp, "stereo");
    chMetaDataTemp = put_byte(chMetaDataTemp, AMF_BOOLEAN);
    chMetaDataTemp = put_byte(chMetaDataTemp, 1);

    chMetaDataTemp = put_amf_string( chMetaDataTemp, "audiocodecid");
    chMetaDataTemp = put_amf_double( chMetaDataTemp, (double)10.0);

    chMetaDataTemp = put_amf_string( chMetaDataTemp, "filesize");
    chMetaDataTemp = put_amf_double( chMetaDataTemp, (double)0.0);

    // List Terminator: SCRIPTDATAOBJECTEND 表示属性数组的结束
    // SCRIPTDATAOBJECTEND 即 0x00 0x00 0x09
    chMetaDataTemp = put_be24( chMetaDataTemp, 9);
    ...
}

References:

Adobe Flash Video File Format Specification Version 10.1

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,100评论 18 139
  • 实时消息协议---流的分块 版权声明: 版权(c)2009 Adobe系统有限公司。全权所有。 摘要: 本备忘录描...
    一个人zy阅读 1,801评论 0 9
  • http://www.jb51.net/article/29950.htm 文件后缀MIME备注 *.avivid...
    ink9979阅读 20,650评论 0 0
  • 教程一:视频截图(Tutorial 01: Making Screencaps) 首先我们需要了解视频文件的一些基...
    90后的思维阅读 4,491评论 0 3
  • 突然的想法…这念头萌生的好快!快得都没时间来考虑后果! 自从有了美颜相机,自己都变的不是自己了! 去年时这盘花眼看...
    语心语心阅读 253评论 0 1