1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <arpa/inet.h> 5 6 #define TAB44 " " 7 #define PRINTF_DEBUG 8 9 #define MAX ...
參考鏈接:1. FLV科普12 FLV腳本數據解析-Metadata Tag解析 https://blog.csdn.net/cabbage2008/article/details/50500021
2. FLV科普9 FLV音頻信息 https://blog.csdn.net/cabbage2008/article/details/50445023
3. FLV科普6 FLV Tag以及Tag頭信息解析 https://blog.csdn.net/cabbage2008/article/details/50374083
4. FLV科普11 FLV視頻信息 https://blog.csdn.net/cabbage2008/article/details/50449857
致敬下圖工具:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <arpa/inet.h> 5 6 #define TAB44 " " 7 #define PRINTF_DEBUG 8 9 #define MAX_SIGNATURE_LEN 3 10 #define MAX_PRE_TAG_SIZE_LEN 4 11 #define MIN_FLV_HEADER_LEN 9 12 #define MAX_TAG_HEADER_LEN 11 13 #define MAX_PARSE_TAG_NUM 15 14 #define MAX_AMF_STR_SIZE 255 15 16 /************************************************************************************************************ 17 ** flv header: 記錄了flv的類型, 版本等信息, 是flv的開頭, 一般都差不多, 占9bytes 18 ** 19 ------------------------------------------------------------------------------------------------------------- 20 ** 欄位名稱 | 長度(bytes) | 有關描述 21 ------------------------------------------------------------------------------------------------------------- 22 ** signature | 3 | 文件標識, 總是為"FLV", 0x46 0x4c 0x56 23 ** version | 1 | 版本(目前為0x01) 24 ** flag | 3 | 文件的標誌位說明. 前5位保留, 必須為0; 25 第6位為音頻Tag: 1表示有音頻; 第七位保留, 為0; 第8位為視頻Tag: 1表示有視頻 26 ** headersize | 4 | 整個header的長度, 一般為9(版本為0x01時); 大於9表示下麵還有擴展信息 27 ************************************************************************************************************/ 28 /* 29 1. unsigned char reserved5: 5, flags_audio: 1, reserved1: 1, flags_video: 1; 30 2. unsigned char : 5, flags_audio: 1, : 1, flags_video: 1; (無名說明無法使用, 僅占位) 31 3. 下麵結構體位域的另外兩種寫法. 32 */ 33 typedef struct t_flv_header 34 { 35 unsigned char signature[MAX_SIGNATURE_LEN+1]; 36 unsigned char version; 37 unsigned char : 5; 38 unsigned char flags_audio: 1; 39 unsigned char : 1; 40 unsigned char flags_video: 1; 41 42 int headersize; 43 } T_FLV_HEADER; 44 45 /************************************************************************************************************ 46 ** tag header 47 ** 48 ------------------------------------------------------------------------------------------------------------- 49 ** 欄位名稱 | 長度(bytes) | 有關描述 50 ------------------------------------------------------------------------------------------------------------- 51 ** type | 1 | 數據類型, (0x12)為腳本類型; (0x08)為音頻類型; (0x09)為視頻類型 52 ** data_size | 3 | 數據區長度 53 ** timestamp | 3 | 時間戳, 類型為(0x12)的tag時間戳一直為0, (0xFFFFFF)可以表示長度為4小時, 單位為毫秒. 54 ** timestamp_extended | 1 | 將時間戳擴展為4bytes, 代表高8位, 一般都為0, 長度為4小時的flv一般很少見了 55 ** streamid | 3 | 總為0 56 ************************************************************************************************************/ 57 typedef struct t_flv_tag_header 58 { 59 int type; 60 int data_size; 61 int timestamp; 62 int timestamp_extended; 63 int streamid; 64 } T_FLV_TAG_HEADER; 65 66 /************************************************************************************************************ 67 ** video tag header 68 ** 69 ------------------------------------------------------------------------------------------------------------- 70 ** 欄位名稱 | 長度(bytes) | 有關描述 71 ------------------------------------------------------------------------------------------------------------- 72 ** FreameType | 4(bits) | FrameType為數據類型, 1為關鍵幀, 2為非關鍵幀, 3為h263的非關鍵幀, 73 4為伺服器生成關鍵幀, 5為視頻信息或命令幀. 74 ** CodecId | 4(bits) | CodecID為包裝類型, 1為JPEG, 2為H263, 3為Screen video, 75 4為On2 VP6, 5為On2 VP6, 6為Screen videoversion 2, 7為AVC 76 77 CodecID=2, 為H263VideoPacket; 78 CodecID=3, 為ScreenVideopacket; 79 CodecID=4, 為VP6FLVVideoPacket; 80 CodecID=5, 為VP6FLVAlphaVideoPacket; 81 CodecID=6, 為ScreenV2VideoPacket; 82 CodecID=7, 為AVCVideoPacket. 83 ************************************************************************************************************/ 84 typedef struct t_flv_tag_video_header 85 { 86 unsigned char freameType:4, codecId:4; 87 } T_FLV_TAG_VIDEO_HEADER; 88 89 /************************************************************************************************************ 90 ** AVCDecoderConfigurationRecord 91 ** 92 ------------------------------------------------------------------------------------------------------------- 93 ** 欄位名稱 | 長度(bytes) | 有關描述 94 ------------------------------------------------------------------------------------------------------------- 95 ** configurationVersion | 1 | 配置版本占用8位, 一定為1 96 ** AVCProfileIndication | 1 | profile_idc占用8位, 從H.264標準SPS第一個欄位profile_idc拷貝而來, 指明所用profile 97 ** profile_compatibility | 1 | 占用8位, 從H.264標準SPS拷貝的冗餘字 98 ** AVCLevelIndication | 1 | level_idc占用8位, 從H.264標準SPS第一個欄位level_idc拷貝而來, 指明所用 level 99 ** reserved | 6b | 保留位占6位, 值一定為'111111' 100 ** lengthSizeMinusOne | 2b | 占用2位, 表示NAL單元頭的長度, 0表示1位元組, 1表示2位元組, 2表示3位元組, 3表示4位元組 101 ** reserved | 3b | 保留位占3位, 值一定為'111' 102 ** numOfSPS | 5b | numOfSequenceParameterSets占用5位, 表示當前SPS的個數 103 ** SPSLength | 2 | sequenceParameterSetLength占用16位, SPS占用的長度 104 ** SPSData | * | 105 ** numOfPPS | 5b | numOfPictureParameterSets占用8位, 表示當前PPS的個數 106 ** PPSLength | 2 | pictureParameterSetLength占用16位, PPS占用的長度 107 ** PPSData | * | numOfPictureParameterSets占用8位, 表示當前PPS的個數 108 109 AVCProfileIndication, profile_compatibility, AVCLevelIndication就是拷貝SPS的前3個位元組 110 ************************************************************************************************************/ 111 typedef struct t_flv_tag_avc_dec_cfg 112 { 113 unsigned char configurationVersion; 114 unsigned char AVCProfileIndication; 115 unsigned char profile_compatibility; 116 unsigned char AVCLevelIndication; 117 unsigned char :6, lengthSizeMinusOne:2; 118 119 unsigned char :3, numOfSequenceParameterSets:5; 120 unsigned short spsLen; 121 unsigned char *spsData; 122 123 unsigned char numOfPictureParameterSets; 124 unsigned short ppsLen; 125 unsigned char *ppsData; 126 } T_FLV_TAG_AVC_DEC_CFG; 127 128 /************************************************************************************************************ 129 ** avc video packet header 130 ** 131 ------------------------------------------------------------------------------------------------------------- 132 ** 欄位名稱 | 長度(bytes) | 有關描述 133 ------------------------------------------------------------------------------------------------------------- 134 ** AVCPacketType占用1位元組 | 1 | 135 ** CompositionTime | 3 | 136 137 AVCVideoPacket同樣包括Packet Header和Packet Body兩部分: 138 Packet Header: 139 AVCPacketType占用1位元組, 僅在AVC時有此欄位 140 0, AVC sequence header (SPS、PPS信息等) 141 1, AVC NALU 142 2, AVC end of sequence (lower level NALU sequence ender is not required or supported) 143 144 CompositionTime占用24位, 相對時間戳, 如果AVCPacketType=0x01為相對時間戳; 其它, 均為0; 145 該值表示當前幀的顯示時間, tag的時間為解碼時間, 顯示時間等於 解碼時間+CompositionTime. 146 ************************************************************************************************************/ 147 typedef struct t_flv_tag_avc_video_packet 148 { 149 unsigned char avcPacketType; 150 151 int compositionTime; 152 153 union videoPacket 154 { 155 T_FLV_TAG_AVC_DEC_CFG avcDecCfg; 156 } vp; 157 } T_FLV_TAG_AVC_VIDEO_PACKET; 158 159 typedef struct t_flv_tag_audio_header 160 { 161 unsigned char soundFormat:4, soundRate:2, soundSize:1, soundType:1; 162 } T_FLV_TAG_AUDIO_HEADER; 163 164 typedef struct t_flv_tag_aac_spec_cfg 165 { 166 unsigned char audioObjectType:5; 167 unsigned char samplingFreqIndex:4, channelCfg:2; 168 } T_FLV_TAG_AAC_SPEC_CFG; 169 170 typedef struct t_flv_tag_aac_audio_packet 171 { 172 unsigned char aacPacketType; 173 174 union audioPacket 175 { 176 T_FLV_TAG_AAC_SPEC_CFG aacSpecCfg; 177 } ap; 178 } T_FLV_TAG_AAC_AUDIO_PACKET; 179 180 typedef struct t_flv_tag 181 { 182 } T_FLV_TAG; 183 184 /* 小端轉double */ 185 static double dealAmfNumber(unsigned char *amfNum) 186 { 187 double d = 0; 188 189 unsigned char *dp = (unsigned char *)&d; 190 191 dp[0] = amfNum[7]; 192 dp[1] = amfNum[6]; 193 dp[2] = amfNum[5]; 194 dp[3] = amfNum[4]; 195 dp[4] = amfNum[3]; 196 dp[5] = amfNum[2]; 197 dp[6] = amfNum[1]; 198 dp[7] = amfNum[0]; 199 200 return d; 201 } 202 203 /* 204 1. DealHeader(const unsigned char* headerData); 205 這樣定義會報warning: assignment discards 'const' qualifier from pointer target type, 206 大意是指針丟掉"const"限定符. 207 2. 原因是: data = headerData; 這一句存在丟掉的風險(可通過給*data賦予不同的值, 使得headerData的數據也被修改, 失去const的作用) 208 3. const int *p; //這種情況表示*p是const無法進行修改, 而p是可以進行修改的; 209 int* const p; //這種情況表示p是const無法進行修改, 而*p是可以進行修改的; 210 const int* const p; //這種情況表示*p與p都無法進行修改. 211 */ 212 static void DealFlvHeader(unsigned char* const headerData) 213 { 214 unsigned char *data = NULL; 215 216 T_FLV_HEADER flvHeader = {0}; 217 218 data = headerData; 219 220 memset(&flvHeader, 0x0, sizeof(T_FLV_HEADER)); 221 222 memcpy(flvHeader.signature, data, MAX_SIGNATURE_LEN); 223 224 flvHeader.signature[MAX_SIGNATURE_LEN] = '\0'; 225 226 data += MAX_SIGNATURE_LEN; 227 228 flvHeader.version = data[0]; 229 230 data += 1; 231 232 flvHeader.flags_audio = data[0] >> 2 & 0x1; 233 flvHeader.flags_video = data[0] & 0x1; 234 235 data += 1; 236 237 flvHeader.headersize = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; 238 239 if (0x1 != flvHeader.version) 240 { 241 printf("version is not 1, todo...\n"); 242 } 243 244 #ifdef PRINTF_DEBUG 245 printf("+FLV Header\n"); 246 printf("%ssignature: %s, version: %d, flags_audio: %d, flags_video: %d, headersize: %d\n", 247 TAB44, flvHeader.signature, flvHeader.version, flvHeader.flags_audio, flvHeader.flags_video, flvHeader.headersize); 248 #endif 249 } 250 251 static void DealTagHeader(unsigned char* const headerData, T_FLV_TAG_HEADER *tagHeader) 252 { 253 static int videoTagNum = 0; 254 static int audioTagNum = 0; 255 256 unsigned char *data = NULL; 257 258 T_FLV_TAG_HEADER header = {0}; 259 260 data = headerData; 261 262 memset(&header, 0x0, sizeof(T_FLV_TAG_HEADER)); 263 264 header.type = data[0]; 265 266 data += 1; 267 268 header.data_size = (data[0] << 16) | (data[1] << 8) | data[2]; 269 270 data += 3; 271 272 header.timestamp = (data[0] << 16) | (data[1] << 8) | data[2]; 273 274 data += 3; 275 276 header.timestamp_extended = data[0]; 277 278 data += 1; 279 280 header.streamid = (data[0] << 16) | (data[1] << 8) | data[2]; 281 282 memcpy(tagHeader, &header, sizeof(T_FLV_TAG_HEADER)); 283 284 #ifdef PRINTF_DEBUG 285 switch (tagHeader->type) 286 { 287 case 0x12: 288 printf("%s+Script Tag\n", TAB44); 289 290 break; 291 292 case 0x9: 293 videoTagNum++; 294 295 printf("%s+Video Tag[%d]\n", TAB44, videoTagNum); 296 297 break; 298 299 case 0x8: 300 audioTagNum++; 301 302 printf("%s+Audio Tag[%d]\n", TAB44, audioTagNum); 303 304 break; 305 306 default: 307 break; 308 } 309 310 printf("%s%s+Tag Header\n", TAB44, TAB44); 311 printf("%s%s%stype: %d, data_size: %d, timestamp: %d, timestamp_extended: %d, streamid: %d\n", 312 TAB44, TAB44, TAB44, tagHeader->type, tagHeader->data_size, tagHeader->timestamp, tagHeader->timestamp_extended, tagHeader->streamid); 313 #endif 314 } 315 316 /* 317 第一個AMF包: 318 第1個位元組表示AMF包類型, 一般總是0x02, 表示字元串, 其他值表示意義請查閱文檔. 319 第2-3個位元組為UI16類型值, 表示字元串的長度, 一般總是0x000A("onMetaData"長度). 320 後面位元組為字元串數據, 一般總為"onMetaData". 321 322 第二個AMF包: 323 第1個位元組表示AMF包類型, 一般總是0x08, 表示數組. 324 第2-5個位元組為UI32類型值, 表示數組元素的個數. 325 後面即為各數組元素的封裝, 數組元素為元素名稱和值組成的對. 表示方法如下: 326 第1-2個位元組表示元素名稱的長度, 假設為L. 後面跟著為長度為L的字元串. 第L+3個位元組表示元素值的類型. 327 後面跟著為對應值, 占用位元組數取決於值的類型. 328 329 0 = Number type (double, 8) 330 1 = Boolean type 331 2 = String type 332 3 = Object type 333 4 = MovieClip type 334 5 = Null type 335 6 = Undefined type 336 7 = Reference type 337 8 = ECMA array type 338 10 = Strict array type 339 11 = Date type 340 12 = Long string type 341 342 1. 不要頻繁的malloc小記憶體(記憶體碎片, 代價); 343 2. 如該函數中arrayKey, arrayValue, amfStrData設置成指針, 然後malloc就有問題(字元串後殘留上述三個最大長度中的字元); 344 3. 可能的解釋: 當用free釋放的你用malloc分配的存儲空間, 釋放的存儲空間並沒有從進程的地址空間中刪除, 而是保留在可用存儲區池中, 345 當再次用malloc時只要可用存儲區池中有足夠的地址空間, 都不會再向內可申請記憶體了, 而是在可用存儲區池中分配了. 346 347 實際分析時: 8的數組後還有一串 00 00 09, 暫時不清楚, 先跳過if (tagDataSize <= 3) 348 */ 349 static void DealScriptTagData(unsigned char* const tagData, unsigned int tagDataSize) 350 { 351 int i = 0; 352 int amfType = 0; 353 int amfIndex = 0; 354 int valueType = 0; 355 int valueSize = 0; 356 int keySize = 0; 357 int arrayCount = 0; 358 int amfStringSize = 0; 359 360 double amfNum = 0; 361 362 unsigned char amfStr[MAX_AMF_STR_SIZE+1] = {0}; 363 364 unsigned char *data = NULL; 365 366 data = tagData; 367 368 for (;;) 369 { 370 if (tagDataSize <= 3) 371 { 372 break; 373 } 374 375 amfType = data[0]; 376 377 amfIndex += 1; 378 379 data += 1; 380 tagDataSize -= 1; 381 382 #ifdef PRINTF_DEBUG 383 printf("%s%s%sAMF%d type: %d\n", TAB44, TAB44, TAB44, amfIndex, amfType); 384 #endif 385 386 switch (amfType) 387 { 388 case 2: 389 amfStringSize = (data[0] << 8) | data[1]; 390 391 #ifdef PRINTF_DEBUG 392 printf("%s%s%sAMF%d String size: %d\n", TAB44, TAB44, TAB44, amfIndex, amfStringSize); 393 #endif 394 395 data += 2; 396 tagDataSize -= 2; 397 398 memset(amfStr, 0x0, sizeof(amfStr)); 399 400 memcpy(amfStr, data, amfStringSize); 401 402 amfStr[amfStringSize] = '\0'; 403 404 #ifdef PRINTF_DEBUG 405 printf("%s%s%sAMF%d String: %s\n", TAB44, TAB44, TAB44, amfIndex, amfStr); 406 #endif 407 408 data += amfStringSize; 409 tagDataSize -= amfStringSize; 410 411 break; 412 413 case 8: 414 arrayCount = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; 415 416 #ifdef PRINTF_DEBUG 417 printf("%s%s%sAMF%d Metadata count: %d\n", TAB44, TAB44, TAB44, amfIndex, arrayCount); 418 printf("%s%s%s+Metadata\n", TAB44, TAB44, TAB44); 419 #endif 420 421 data += 4; 422 tagDataSize -= 4; 423 424 for (i=0; i<arrayCount; i++) 425 { 426 keySize = (data[0] << 8) | data[1]; 427 428 data += 2; 429 tagDataSize -= 2; 430 431 memset(amfStr, 0x0, sizeof(amfStr)); 432 433 memcpy(amfStr, data, keySize); 434 435 amfStr[keySize] = '\0'; 436 437 #ifdef PRINTF_DEBUG 438 printf("%s%s%s%s%s: ", TAB44, TAB44, TAB44, TAB44, amfStr); 439 #endif 440 441 data += keySize; 442 tagDataSize -= keySize; 443 444 valueType = data[0]; 445 446 data += 1; 447 tagDataSize -= 1; 448 449 if (0 == valueType) 450 { 451 amfNum = dealAmfNumber(data); 452 #ifdef PRINTF_DEBUG 453 printf("%lf\n", amfNum); 454 #endif 455 456 data += 8; 457 tagDataSize -= 8; 458 } 459 else if (1 == valueType) 460 { 461 #ifdef PRINTF_DEBUG 462 printf("%d\n", data[0]); 463 #endif 464 data += 1; 465 tagDataSize -= 1; 466 } 467 else if (2 == valueType) 468 { 469 valueSize = (data[0] << 8) | data[1]; 470 471 data += 2; 472 tagDataSize -= 2; 473 474