Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2193475
  • 博文数量: 141
  • 博客积分: 3552
  • 博客等级: 中校
  • 技术积分: 4148
  • 用 户 组: 普通用户
  • 注册时间: 2009-07-15 14:39
个人简介

熟悉Linux下程序设计及各种应用程序 熟悉C Language 熟悉Glusterfs、FFmpeg、CDN 系统设计,计算机图形系统设计、分布式程序设计 目前主要研究方向:流媒体

文章分类

全部博文(141)

分类: LINUX

2013-11-19 18:45:10

参考flv的spec文档,写了个解析onMetaData,代码没有整理

点击(此处)折叠或打开

  1. /*
  2.  * Copyright (c) 2013 Steven Liu
  3.  *
  4.  *
  5.  * FFmpeg is free software; you can redistribute it and/or
  6.  * modify it under the terms of the GNU Lesser General Public
  7.  * License as published by the Free Software Foundation; either
  8.  * version 2.1 of the License, or (at your option) any later version.
  9.  *
  10.  * FFmpeg is distributed in the hope that it will be useful,
  11.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13.  * Lesser General Public License for more details.
  14.  *
  15.  * You should have received a copy of the GNU Lesser General Public
  16.  * License along with FFmpeg; if not, write to the Free Software
  17.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  18.  */

  19. #include <stdio.h>
  20. #include <stdlib.h>
  21. #include <string.h>
  22. #include <unistd.h>
  23. #include <fcntl.h>
  24. #include <sys/types.h>
  25. #include <sys/stat.h>
  26. #include <errno.h>

  27. #define TAG_FLV_F 'F'
  28. #define TAG_FLV_L 'L'
  29. #define TAG_FLV_V 'V'
  30. #define TAG_TYPE_SCRIPTDATA 0x12
  31. int script_type_parse(unsigned char *data);

  32. union av_intfloat64 {
  33.     uint64_t i;
  34.     double f;
  35. };

  36. enum script_data_type {
  37.     Number = 0,
  38.     Boolean,
  39.     String,
  40.     Object,
  41.     MovieClip,
  42.     Null,
  43.     Undefined,
  44.     Reference,
  45.     EcmaArray,
  46.     ObjectEndMarker,
  47.     StringArray,
  48.     Date,
  49.     LongString,
  50. };
  51. typedef enum script_data_type script_type_t;

  52. struct script_string {
  53.     char len[2];
  54. };
  55. typedef struct script_string String_t;

  56. struct flv_header {
  57.     unsigned char tag_f;
  58.     unsigned char tag_l;
  59.     unsigned char tag_v;
  60.     unsigned char version;
  61.     unsigned char audio_and_video;
  62.     unsigned char DataOffset[4];
  63. };
  64. typedef struct flv_header header_t;

  65. struct flv_tag {
  66.     unsigned char tagtype;
  67.     unsigned char size[3];
  68.     unsigned char timestamp[3];
  69.     unsigned char extent_timestamp;
  70.     unsigned char stream_id[3];
  71. };
  72. typedef struct flv_tag tag_t;

  73. struct flv_body {
  74.     tag_t tag;
  75.     // unsigned char PreviousTagSize[2];
  76. };
  77. typedef struct flv_body body_t;

  78. struct object_name {
  79.     unsigned short length;
  80.     unsigned char name;
  81. };
  82. typedef struct object_name object_name_t;

  83. struct script_object {
  84.     object_name_t name;
  85. };
  86. typedef struct script_object_name script_object_t;


  87. /**
  88.  * Reinterpret a 64-bit integer as a double.
  89.  */
  90. static double int2double(uint64_t i)
  91. {
  92.     union av_intfloat64 v;
  93.     v.i = i;
  94.     return v.f;
  95. }

  96. /*
  97.  * get_header_audio check if the flv have audio stream
  98.  * info, a byte with video and audio bit flag
  99.  *
  100.  * return 0 or 1
  101. */
  102. int get_header_audio(char info)
  103. {
  104.     return (info & 0x4) >> 2;
  105. }

  106. /*
  107.  * get_header_video check if the flv have video stream
  108.  * info, a byte with video and audio bit flag
  109.  *
  110.  * return 0 or 1
  111. */
  112. int get_header_video(char info)
  113. {
  114.     return info & 0x1;
  115. }

  116. /*
  117.  * flv_header_parse just parse the FLV VERSION tag
  118.  * fd, file describe handle
  119.  * return flv version
  120. */
  121. int flv_header_parse(int fd)
  122. {
  123.     int ret = 0;
  124.     header_t header;

  125.     memset(&header, 0, sizeof(header));
  126.     ret = read(fd, &header, sizeof(header));
  127.     if (ret <= 0) {
  128.         fprintf(stderr, "read header error, %d\n", -EINVAL);
  129.         return -EINVAL;
  130.     }


  131.     fprintf(stdout, "flv = [%c %c %c] version = [%x]\n"
  132.             "TypeFlagsAudio = [%x], TypeFlagsVideo = [%x], DataOffset = [%x %x %x %x]\n",
  133.             header.tag_f, header.tag_l, header.tag_v, header.version,
  134.             get_header_audio(header.audio_and_video), get_header_video(header.audio_and_video),
  135.             header.DataOffset[0], header.DataOffset[1], header.DataOffset[2], header.DataOffset[3]);

  136.     return (int)header.version;
  137. }


  138. /*
  139.  * flv_do_tag just parse all the flv tag
  140.  * fd, file describe handle
  141.  * size, the size of the flv tag body
  142.  *
  143.  * return tag type
  144. */
  145. int flv_do_tag(int fd, unsigned long *size)
  146. {
  147.     int ret = 0;
  148.     char *p = NULL;
  149.     char data_size[11] = {0};
  150.     body_t body;


  151.     ret = read(fd, &body, sizeof(body));
  152.     if (ret <= 0) {
  153.         fprintf(stderr, "Can not ret the fd errno [%d]\n", -EINVAL);
  154.         return -EINVAL;
  155.     }

  156.     fprintf(stdout, "size = %lu tag->type = [%x]"
  157.             "tag->size = [%0.2x %0.2x %0.2x], tag->timestamp = [%0.2x %0.2x %0.2x], "
  158.             "tag->extent_timestamp = [%0.2x], tag->streamid = [%0.2x %0.2x %0.2x]\n",
  159.             sizeof(body), body.tag.tagtype,
  160.             body.tag.size[0], body.tag.size[1],body.tag.size[2], body.tag.timestamp[0], body.tag.timestamp[1],
  161.             body.tag.timestamp[2], body.tag.extent_timestamp, body.tag.stream_id[0],
  162.             body.tag.stream_id[1], body.tag.stream_id[2]);
  163.     memcpy(data_size, body.tag.size, 3);
  164.     p = data_size;
  165.     snprintf(data_size, sizeof(data_size), "0x%x%x%x", body.tag.size[0], body.tag.size[1], body.tag.size[2]);
  166.     *size = strtoul(p, NULL, 16);
  167.     fprintf(stdout, "size = [%lu] \n", *size);

  168.     return body.tag.tagtype;
  169. }

  170. /*
  171.  * get_string_len get the key-value value string length
  172.  * data, the data will be parsed
  173.  *
  174.  * return the String length by int
  175. */
  176. int get_string_len(unsigned char *data)
  177. {

  178.     unsigned char *p = data;
  179.     unsigned char len_char[8];
  180.     int len_int = 0;

  181.     memset(len_char, 0, sizeof(len_char));
  182.     snprintf((char *)len_char, sizeof(len_char), "0x%x%x", data[0], data[1]);
  183.     p = len_char;

  184.     len_int = strtoul((char *)p, NULL, 16);

  185.     return len_int;
  186. }

  187. /*
  188.  * get_bool_value get bool type value (key-value value)
  189.  * data, the data will be parsed
  190.  *
  191.  * return bool value 0 or 1
  192. */
  193. int get_bool_value(unsigned char *data)
  194. {
  195.    return *data;
  196. }

  197. /*
  198.  * get_key_len the key-value key string length unit: four bytes
  199.  * data, the data will be parsed
  200.  *
  201.  * return the key length by int
  202. */
  203. int get_key_len(unsigned char *data)
  204. {
  205.     char len_char[16];
  206.     char *p = len_char;

  207.     memset(len_char, 0, sizeof(len_char));
  208.     snprintf(len_char, sizeof(len_char), "0x%x%x%x%x", data[0], data[1], data[2], data[3]);

  209.     return strtoul(p, NULL, 16);
  210. }

  211. int process_ecma_array_end(unsigned char *data)
  212. {
  213.     return 0;
  214. }

  215. /*
  216.  * process_ecma_array The onMetaData sub type named ECMA Array
  217.  * data, the data will be parsed
  218.  *
  219.  * return the offset of the data
  220. */
  221. int process_ecma_array(unsigned char *data)
  222. {
  223.     int ecma_array_len = 0;
  224.     int keyname_len = 0;
  225.     unsigned char keyname[32];
  226.     unsigned char *p = data;
  227.     int i = 0;

  228.     ecma_array_len = get_key_len(p);
  229.     p += 4;

  230.     for (i = 0; i < ecma_array_len; i++) {
  231.         keyname_len = get_string_len(p);
  232.         p += 2;
  233.         memset(keyname, 0, sizeof(keyname));
  234.         strncpy((char *)keyname, (const char *)p, keyname_len);
  235.         fprintf(stdout, "keyname = [%s]\n", keyname);
  236.         p += keyname_len;
  237.         p += script_type_parse(p);
  238.     }

  239.     if (*p == 0 && *(p + 1) == 0 && *(p + 2) == 9) {
  240.         p += 3;
  241.     }
  242.     return p - data;
  243. }

  244. /*
  245.  * get_double get the double number by long long type
  246.  * data, the data will be parsed
  247.  *
  248.  * return the parse out number of long long
  249. */
  250. unsigned long long get_double(unsigned char *data)
  251. {
  252.     unsigned char double_number[64];
  253.     unsigned char *p = double_number;

  254.     memset(double_number, 0, sizeof(double_number));
  255.     snprintf((char *)double_number, sizeof(double_number), "0x%0.2x%0.2x%0.2x%0.2x%0.2x%0.2x%0.2x%0.2x",
  256.              *data, *(data + 1), *(data + 2), *(data + 3), *(data + 4), *(data + 5), *(data + 6), *(data + 7));

  257.     return strtoull((char *)p, NULL, 16);

  258. }

  259. /*
  260.  * script_type_parse parse the script object type and parse the data
  261.  * data, the data will be parsed
  262.  *
  263.  * return the data offset
  264. */
  265. int script_type_parse(unsigned char *data)
  266. {
  267.     int ret = 0;
  268.     unsigned char *p = data;
  269.     unsigned char string_output[512];
  270.     unsigned long long number = 0;
  271.     double double_number = 0.0;

  272.     switch (*p) {
  273.     case Number:
  274.         p++;
  275.         number = get_double(p);
  276.         double_number = int2double(number);
  277.         fprintf(stdout, "number = [%.2f]\n", double_number);
  278.         p += 8;
  279.         break;

  280.     case Boolean:
  281.         p++;
  282.         ret = get_bool_value(p);
  283.         p++;
  284.         break;

  285.     case String:
  286.         p++;
  287.         memset(string_output, 0, sizeof(string_output));
  288.         ret = get_string_len(p);
  289.         p += 2;
  290.         strncpy((char *)string_output, (const char *)p, ret);
  291.         fprintf(stdout, "String = [%s]\n", string_output);
  292.         p += ret;
  293.         break;

  294.     case Object:
  295.         p++;
  296.         break;

  297.     case MovieClip:
  298.         p++;
  299.         break;

  300.     case Null:
  301.         p++;
  302.         break;

  303.     case Undefined:
  304.         p++;
  305.         break;

  306.     case Reference:
  307.         p++;
  308.         break;

  309.     case EcmaArray:
  310.         p++;
  311.         ret = process_ecma_array(p);
  312.         p += ret;
  313.         break;

  314.     case ObjectEndMarker:
  315.         p++;
  316.         break;

  317.     case StringArray:
  318.         p++;
  319.         break;

  320.     case Date:
  321.         p++;
  322.         break;

  323.     case LongString:
  324.         p++;
  325.         break;

  326.     default:

  327.         break;
  328.     }
  329.     return p - data;
  330. }

  331. /*
  332.  * do_tag_onMetaData parse the onMetaData tags
  333.  * fd, file describe handle
  334.  * size, will read size of the data, unit: byte
  335.  *
  336.  * return offset of the data
  337. */
  338. int do_tag_onMetaData(int fd, int size)
  339. {
  340.     int ret = 0;
  341.     unsigned char *p = NULL;

  342.     unsigned char *onMetaData = malloc(size);
  343.     fprintf(stdout, "size = [%d]\n", size);
  344.     memset(onMetaData, 0, size);
  345.     ret = read(fd, onMetaData, size);
  346.     fprintf(stdout, "ret = [%d]\n", ret);

  347.     int fuck = open("./xxx.dat", O_RDWR);
  348.     write(fuck, onMetaData, ret);
  349.     close(fuck);
  350.     p = onMetaData;

  351.     while (p) {
  352.         if (p - onMetaData >= size) {
  353.             break;
  354.         }
  355.         ret = script_type_parse(p);
  356.         fprintf(stdout, "in while 1 offset = [%d]\n", p - onMetaData);
  357.         p += ret;
  358.     }
  359.     return p - onMetaData;
  360. }

  361. /*
  362.  * dump_flv_info just print the flv onMetaData info
  363.  * filename, the flv filename
  364.  *
  365.  * return always right
  366. */
  367. int dump_flv_info(char *filename)
  368. {
  369.     int fd = 0;
  370.     int tag_type = -1;
  371.     int ret = 0;
  372.     unsigned long size = 0;
  373.     unsigned char PreviousTagSize[4];

  374.     fd = open(filename, O_RDONLY);
  375.     if (fd < 0) {
  376.         fprintf(stdout, "Can not open the file [%s]\n", filename);
  377.         return -ENOENT;
  378.     }
  379.     fprintf(stdout, "filename = [%s]\n", filename);

  380.     flv_header_parse(fd);
  381.     ret = read(fd, PreviousTagSize, sizeof(PreviousTagSize));
  382.     tag_type = flv_do_tag(fd, &size);

  383.     switch(tag_type) {
  384.     case TAG_TYPE_SCRIPTDATA:
  385.         fprintf(stdout, "data_size = [%lu]\n", size);
  386.         do_tag_onMetaData(fd, size);

  387.         break;
  388.     }
  389.     return 0;
  390. }

  391. void usage(char **argv)
  392. {
  393.     fprintf(stderr, "Usage:\n"
  394.             "\t%s flv_file_name\n", argv[0]);
  395.     return ;
  396. }

  397. int main(int argc, char *argv[])
  398. {
  399.     char *filename = NULL;

  400.     if (argc < 2) {
  401.         usage(argv);
  402.         return -EINVAL;
  403.     }

  404.     filename = argv[1];

  405.     dump_flv_info(filename);
  406.     return 0;
  407. }
Makefile为

点击(此处)折叠或打开

  1. all:
  2.     gcc -O2 -Wall -g dump_flv_info.c -o flv_info

  3. clean:
  4.     rm -rf flv_info
执行结果如下:

2.flv的内容:

主要参考文档为:
video_file_format_spec_v10_1_20131118111845.pdf

根据参考文档可以看出来

解析步骤如下

首先解析文件头

根据上面的内容可以看出来,FLV和Version占用了4个字节,而Version总是1,其中的DataOffset一般来在flv的version是1的时候总是9,如图

464c 5601为四个字节,就是FLV 和Version部分,接下来的05为一个字节,这个字节为TypeFlagsReserved, TypeFlagsAudio,TyopeFlagsReserved, TypeFlagsVideo的内容,可以将05转换为2进制,就是00000101, 也就是video是1,audio也是1,接下爱就是DataOffset,DataOffset一次读取32位,就是4个字节,内容为00 0000 09,也就是上面参考中的内容,The DataOffset field usually has a value of 9;

接下来就开始解析FLV的File Body:

部分,这部分排列方式位PreviousTagSizeN(N >= 0),如图

占用了4个字节,全部为0.在前面的spec里面可以看到,接下来的就是FLVTAG,这个FLVTAG为另外一个结构,结构如下:

通过结构可以看出,Reserved,Filter,TagType占用了一个字节,也就是前面的12,这里说的12其实不是十进制的12,而是16进制的0x12,将其转成十进制就是18,可以转成2进制进行对比,最终TagType来判断这个FLVTAG是音频,还是视频,还是脚本数据(ScriptData),如果TagType是8就是audio,如果TagType是9就是video,如果TagType是18,就是ScriptData;接下来继续读取24个字节,来判断将要读取得FLVTAG对应的数据字节数,例如onMetaData这部分是ScriptData,那么读取ScriptData的数据的时候,就要读取DataSize这么大,如图:

可以看到,需要读取0x000125这么多数据,转换成十进制的话,就是293个字节的数据,这个数据我们暂且记下,后面会用到,千万要记住!!!

接下来就是读取TimeStamp(3个字节)和Extent Timestamp(1个字节),两个字段加起来,占用了4个字节,全部为0,具体参考上(孙悟空 )面的spec可以看到为什么,过了四个字节以后,进入到StreamID部分,StreamID占用了3个字节,总是0,Spec中已经可以看到,接下来就是0200 0a6f ....部分,这部分在前面的spec中可以看到说明,由于前面我们看到的是TagType是18,那么这个地方就做ScriptData相关的处理办法,直接按照ScriptData来处理:



从上面spec中可以看到,ScriptData里面使用了ScriptTagBody,而ScriptTagBody的定义为一个Type和ScriptDataValue,其中的DataValue是根据Type来决定的,我们读数据读到offset为0x0000010 + 8位置,这时下一个字节为02,也就是说,Type为SCRIPTDATASTRING,这个时候会处理SCRIPTDATASTRING,

SCRIPTDATASTRING份两部分,一个是2两个字节的StringLength,一个是StringData,这个StringData部分占用的字节多少由StringLength部分决定,那么就继续上面的位置读取2字节,为00 0a,然后接下来读取00 0a个字节,作为String内容,也就是看到的onMetaData这个字符串,这样,onMetaData这个字符串读取 (孙悟空 ) 完毕,也就是前面提到的ScriptTagBody的Name部分读取完毕。接下来内容进入到了高潮:读取ECMA ARRAY部分,同样使用前面的办法读取对应的内容,如图:

其中08为ECMAARRAY的类型,接下来的0000 000d为ECMAARRAY中的结构的ECMAArrayLength,一次读取4个字节,得到的结果为13(0x0d)。

也就是说,ECMAARRAY由13个对应的项,可以建立一个递归或者循环,去读取这13个Variables,接下来开始解析第一个Variable,也就是SCRIPTDATAOBJECTPROPERTY。

接下来就是解析PropertyName,也是SCRIPTDATASTRING类型,对应解析办法前面有提到过,一次读两个字节,然后获得PropertyName的字符串,然后获得对应的PropertyData数据,要注意的是PropertyData里面的数据类型,读到的是00的话,就是Number方法,这个Number存的值就是个DOUBLE,通过前 (孙悟空 ) 面的spec参考可以看到对应的信息,下图是对应的字节:

接下来可以按照前面的方法继续循环或者递归,这样,就将onMetaData信息全部读取出来了,读取最后的时候不要忘记有三个字节,0000 09,为SCRIPTDATAOBJECTEND,

这样,FLV的onMetaData部分解析完毕




阅读(12581) | 评论(0) | 转发(4) |
0

上一篇:Git Tips

下一篇:RPM包制作方法

给主人留下些什么吧!~~