参考链接: 1. MPEG-2码流结构分析 https://www.cnblogs.com/CoderTian/p/9246225.html(本文语法采用这里的截图,代码原创)
1. mpeg2的码流结构,如下图:
2. Sequence Header,如下图:
3. Sequence Extention Header,如下图:
4. Sequence Extention Header,如下图:
5. Group Of Picture Header,如下图:
6. Picture Header,如下图:
7. Picture Coding Extension,如下图:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 5 #define TAB44 " " 6 #define PRINTF_DEBUG 7 8 #define MAX_GROUP_HEADER_LEN 8 9 #define MAX_TIME_STRING_LEN 12 10 #define MAX_SEQEXTEN_HEADER_LEN 10 /* worng, need more info can get len, base is 10 */ 11 #define MAX_SEQHEADER_MATRIX_LEN 64 12 13 typedef enum e_mpeg2_sc_type 14 { 15 E_SC_MPEG2_SEQ_HEADER = 0x000001B3, 16 E_SC_MPEG2_SEQ_PIC_EXTEN_HEADER = 0x000001B5, 17 E_SC_MPEG2_SEQ_END = 0x000001B7, 18 E_SC_MPEG2_GROUP_HEADER = 0x000001B8, 19 E_SC_MPEG2_PICTURE_HEADER = 0x00000100 20 } E_MPEG2_SC_TYPE; 21 22 typedef enum e_mpeg2_coding_type 23 { 24 E_MPEG2_CODING_I = 1, 25 E_MPEG2_CODING_P = 2, 26 E_MPEG2_CODING_B = 3 27 } E_MPEG2_CODING_TYPE; 28 29 typedef struct t_mpeg2_seq_header 30 { 31 int horizontal_size; 32 int vertical_size; 33 34 unsigned char load_intra_quantiser_matrix:1; 35 unsigned char load_non_intra_quantiser_matrix:1; 36 } T_MPEG2_SEQ_HEADER; 37 38 /********************************************************************************************************** 39 group_of_pictures_header() { 40 group_start_code 32 bits 41 time_code 25 bits 42 closed_gop 1 bit 43 broken_link 1 bit 44 next_start_code 45 } 46 47 ** time_code(25bits): drop_frame_flag(1) + time_code_hours(5) + time_code_minutes(6) + marker_bit(1) + time_code_seconds(6) + time_code_pictures(6) 48 49 ** closed_gop: 指明紧挨着在group of picture header后的I帧的连续的B帧的编码方式, 如果被设置为1, 50 表示该B帧只采用backward prediction或intra coding(Close GOP是指帧间的预测都是在GOP中进行的. 51 而使用open GOP, 后一个GOP会参考前一个GOP的信息. 使用这种方式就大大降低了码率). 52 **********************************************************************************************************/ 53 typedef struct t_mpeg2_group_header 54 { 55 unsigned char time_code_hours:5; 56 unsigned char time_code_minutes:6; 57 unsigned char time_code_seconds:6; 58 unsigned char time_code_pictures:6; 59 60 unsigned char timeStr[MAX_TIME_STRING_LEN+1]; 61 } T_MPEG2_GROUP_HEADER; 62 63 /*************************************************************** 64 ** pic_coding_type: 65 001 (I帧) 66 010 (P帧) 67 011 (B帧) 68 69 ** temporal_reference: 指明该帧的参考属性(个人理解是显示标识) 70 71 ** 结合group_header中的time_code就能算出显示时间 72 ****************************************************************/ 73 typedef struct t_mpeg2_pic_header 74 { 75 unsigned short temporal_reference; 76 unsigned char pic_coding_type:3; 77 } T_MPEG2_PIC_HEADER; 78 79 /* now n max is 4 */ 80 static int NBytes2Int(int n, unsigned char* const byte) 81 { 82 int i = 0; 83 int retInt = 0; 84 85 for (i=0; i<n; i++) 86 { 87 retInt += (byte[i]<<((n-i-1)*8)); 88 } 89 90 return retInt; 91 } 92 93 static int FindStartCode(const E_MPEG2_SC_TYPE mpeg2ScType, unsigned char *scData) 94 { 95 int isFind = 0; 96 97 if (mpeg2ScType == NBytes2Int(4, scData)) 98 { 99 isFind = 1; 100 } 101 102 return isFind; 103 } 104 105 static int GetMpeg2DataLen(const E_MPEG2_SC_TYPE mpeg2ScType, const int startPos, const int mpeg2BitsSize, unsigned char* const mpeg2Bits) 106 { 107 int parsePos = 0; 108 109 parsePos = startPos; 110 111 while (parsePos < mpeg2BitsSize) 112 { 113 if (E_SC_MPEG2_SEQ_HEADER == mpeg2ScType) 114 { 115 if (FindStartCode(mpeg2ScType, &mpeg2Bits[parsePos])) 116 { 117 return parsePos - startPos; 118 } 119 else 120 { 121 parsePos++; 122 } 123 } 124 else if (E_SC_MPEG2_SEQ_PIC_EXTEN_HEADER == mpeg2ScType) 125 { 126 if (FindStartCode(E_SC_MPEG2_GROUP_HEADER, &mpeg2Bits[parsePos]) 127 || FindStartCode(E_SC_MPEG2_PICTURE_HEADER, &mpeg2Bits[parsePos])) 128 { 129 return parsePos - startPos; 130 } 131 else 132 { 133 //printf("parsePos: %d\n", parsePos); 134 135 parsePos++; 136 } 137 } 138 else if (E_SC_MPEG2_GROUP_HEADER == mpeg2ScType) 139 { 140 if (FindStartCode(E_SC_MPEG2_PICTURE_HEADER, &mpeg2Bits[parsePos])) 141 { 142 return parsePos - startPos; 143 } 144 else 145 { 146 parsePos++; 147 } 148 } 149 else if (E_SC_MPEG2_PICTURE_HEADER == mpeg2ScType) 150 { 151 if (FindStartCode(E_SC_MPEG2_SEQ_PIC_EXTEN_HEADER, &mpeg2Bits[parsePos])) 152 { 153 return parsePos - startPos; 154 } 155 else 156 { 157 parsePos++; 158 } 159 } 160 } 161 162 return parsePos - startPos; // if file is end 163 } 164 165 static void ParseSeqData(const unsigned int seqLen, unsigned char* const seqData) 166 { 167 static int groupNum = 0; 168 static int picNum = 0; 169 170 int parsePos = 0; 171 int seqExtenLen = 0; 172 int picHeaderLen = 0; 173 int picCodingExtenLen = 0; 174 175 unsigned char *data = NULL; 176 177 T_MPEG2_SEQ_HEADER mpeg2SeqHeader = {0}; 178 T_MPEG2_GROUP_HEADER mpeg2GroupHeader = {0}; 179 T_MPEG2_PIC_HEADER mpeg2PicHeader = {0}; 180 181 data = seqData; 182 183 memset(&mpeg2SeqHeader, 0x0, sizeof(T_MPEG2_SEQ_HEADER)); 184 185 mpeg2SeqHeader.horizontal_size = ((data[0]<<4) | ((data[1]>>4)&0xf)); 186 mpeg2SeqHeader.vertical_size = ((data[1]&0xf)<<8) | data[2]; 187 188 data += 7; 189 parsePos += 7; 190 191 mpeg2SeqHeader.load_intra_quantiser_matrix = (data[0]&0x10)>>1; 192 mpeg2SeqHeader.load_non_intra_quantiser_matrix = data[0]&0x1; /* here maybe wrong, two 1bits don't know how save in bitstream */ 193 194 data += 1; 195 parsePos += 1; 196 197 if (mpeg2SeqHeader.load_intra_quantiser_matrix) 198 { 199 data += MAX_SEQHEADER_MATRIX_LEN; 200 parsePos += MAX_SEQHEADER_MATRIX_LEN; 201 } 202 203 if (mpeg2SeqHeader.load_non_intra_quantiser_matrix) 204 { 205 data += MAX_SEQHEADER_MATRIX_LEN; 206 parsePos += MAX_SEQHEADER_MATRIX_LEN; 207 } 208 209 #ifdef PRINTF_DEBUG 210 printf("Seqence Header: [width: %d, height: %d]\n", mpeg2SeqHeader.horizontal_size, mpeg2SeqHeader.vertical_size); 211 #endif 212 213 while (parsePos< (seqLen-4)) 214 { 215 if (FindStartCode(E_SC_MPEG2_SEQ_END, data)) 216 { 217 return; 218 } 219 220 /********************************************************************************** 221 1. mpeg2 have seq exten, mpeg1 have no; 222 2. 这里的数据长度不能直接用MAX_SEQEXTEN_HEADER_LEN(10), 此处需根据扩展序列头中的 223 extension_start_code_identifier所指示的类型做具体的判断; 224 3. 此处的做法, 直接找下一个起始码, 对扩展头不做解析. 225 *************************************************************************************/ 226 if (FindStartCode(E_SC_MPEG2_SEQ_PIC_EXTEN_HEADER, data)) 227 { 228 seqExtenLen = GetMpeg2DataLen(E_SC_MPEG2_SEQ_PIC_EXTEN_HEADER, 4, seqLen-parsePos-4, data); 229 230 #ifdef PRINTF_DEBUG 231 printf("%sSeqence extention\n", TAB44); 232 #endif 233 data += 4; 234 parsePos += 4; 235 236 data += seqExtenLen; 237 parsePos += seqExtenLen; 238 239 } 240 241 if (FindStartCode(E_SC_MPEG2_GROUP_HEADER, data)) 242 { 243 memset(&mpeg2GroupHeader, 0x0, sizeof(T_MPEG2_GROUP_HEADER)); 244 245 /* 4 bytes startcode */ 246 mpeg2GroupHeader.time_code_hours = (data[4]>>2) & 0x1f; 247 mpeg2GroupHeader.time_code_minutes = ((data[4]&0x3)<<4) | ((data[5]>>4)&0xf); 248 mpeg2GroupHeader.time_code_seconds = ((data[5]&0x7)<<3) | ((data[6]>>5)&0x7); 249 mpeg2GroupHeader.time_code_pictures = ((data[6]&0x1f)<<1) | ((data[7]>>7)&0x1); 250 251 sprintf(mpeg2GroupHeader.timeStr, "%02d:%02d:%02d:%02d", mpeg2GroupHeader.time_code_hours, mpeg2GroupHeader.time_code_minutes, mpeg2GroupHeader.time_code_seconds, mpeg2GroupHeader.time_code_pictures); 252 253 data += MAX_GROUP_HEADER_LEN; 254 parsePos += MAX_GROUP_HEADER_LEN; 255 256 #ifdef PRINTF_DEBUG 257 printf("%sGroup Of Picture Header #%d, time: %s\n", TAB44, groupNum, mpeg2GroupHeader.timeStr); 258 259 groupNum++; 260 #endif 261 } 262 else if (FindStartCode(E_SC_MPEG2_PICTURE_HEADER, data)) 263 { 264 memset(&mpeg2PicHeader, 0x0, sizeof(T_MPEG2_PIC_HEADER)); 265 266 /* seqLen-parsePos-4, 数据的剩余长度 */ 267 picHeaderLen = GetMpeg2DataLen(E_SC_MPEG2_PICTURE_HEADER, 4, seqLen-parsePos-4, data); 268 269 mpeg2PicHeader.temporal_reference = (data[4]<<2) | ((data[5]>>6)&0x3); 270 mpeg2PicHeader.pic_coding_type = (data[5]>>3)&0x7; 271 272 data += 4; 273 parsePos += 4; 274 275 data += picHeaderLen; 276 parsePos += picHeaderLen; 277 278 #ifdef PRINTF_DEBUG 279 switch (mpeg2PicHeader.pic_coding_type) 280 { 281 case E_MPEG2_CODING_I: 282 printf("%s%sPicture Header-I Frame #%d, display: %d\n", TAB44, TAB44, picNum, mpeg2PicHeader.temporal_reference); 283 284 break; 285 286 case E_MPEG2_CODING_P: 287 printf("%s%sPicture Header-P Frame #%d, display: %d\n", TAB44, TAB44, picNum, mpeg2PicHeader.temporal_reference); 288 289 break; 290 291 case E_MPEG2_CODING_B: 292 printf("%s%sPicture Header-B Frame #%d, display: %d\n", TAB44, TAB44, picNum, mpeg2PicHeader.temporal_reference); 293 294 break; 295 296 default: 297 printf("%s%sPicture Header-%d Frame #%d, display: %d\n", TAB44, TAB44, mpeg2PicHeader.pic_coding_type, picNum, mpeg2PicHeader.temporal_reference); 298 299 break; 300 } 301 302 picNum++; 303 #endif 304 305 if (FindStartCode(E_SC_MPEG2_SEQ_PIC_EXTEN_HEADER, data)) 306 { 307 picCodingExtenLen = GetMpeg2DataLen(E_SC_MPEG2_SEQ_PIC_EXTEN_HEADER, 4, seqLen-parsePos-4, data); 308 309 data += 4; 310 parsePos += 4; 311 312 data += picCodingExtenLen; 313 parsePos += picCodingExtenLen; 314 315 #ifdef PRINTF_DEBUG 316 printf("%s%sPicture Coding Extention\n", TAB44, TAB44); 317 #endif 318 } 319 } 320 } 321 322 return; 323 } 324 325 int main(int argc, char *argv[]) 326 { 327 int fileLen = 0; 328 int seqLen = 0; 329 int mpeg2BitsPos = 0; 330 331 unsigned char *mpeg2Bits = NULL; 332 unsigned char *seqData = NULL; 333 334 FILE *fp = NULL; 335 336 if (2 != argc) 337 { 338 printf("Usage: mpeg2parse **.mpg\n"); 339 340 return -1; 341 } 342 343 fp = fopen(argv[1], "rb"); 344 if (!fp) 345 { 346 printf("open file[%s] error!\n", argv[1]); 347 348 return -1; 349 } 350 351 fseek(fp, 0, SEEK_END); 352 353 fileLen = ftell(fp); 354 355 fseek(fp, 0, SEEK_SET); 356 357 mpeg2Bits = (unsigned char*)malloc(fileLen); 358 if (!mpeg2Bits) 359 { 360 printf("maybe file is too long, or memery is not enough!\n"); 361 362 fclose(fp); 363 364 return -1; 365 } 366 367 memset(mpeg2Bits, 0x0, fileLen); 368 369 if (fread(mpeg2Bits, 1, fileLen, fp) < 0) 370 { 371 printf("read file data to mpeg2Bits error!\n"); 372 373 fclose(fp); 374 free(mpeg2Bits); 375 376 mpeg2Bits = NULL; 377 378 return -1; 379 } 380 381 fclose(fp); 382 383 while (mpeg2BitsPos < (fileLen-4)) 384 { 385 if (FindStartCode(E_SC_MPEG2_SEQ_HEADER, &mpeg2Bits[mpeg2BitsPos])) 386 { 387 seqLen = GetMpeg2DataLen(E_SC_MPEG2_SEQ_HEADER, mpeg2BitsPos+4, fileLen, mpeg2Bits); 388 389 seqData = (unsigned char*)malloc(seqLen); 390 if (seqData) 391 { 392 memset(seqData, 0x0, seqLen); 393 394 memcpy(seqData, mpeg2Bits+mpeg2BitsPos+4, seqLen); 395 396 ParseSeqData(seqLen, seqData); 397 398 free(seqData); 399 seqData = NULL; 400 } 401 402 mpeg2BitsPos += (seqLen+4); 403 } 404 else 405 { 406 mpeg2BitsPos++; 407 } 408 } 409 }