- 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 PRTNTF_STR_LEN 10
- 10
- 11 /************************************************************************************************************
- 12 ** nalu header: 负责将VCL产生的比特字符串适配到各种各样的网络和多元环境中,
- 13 覆盖了所有片级以上的语法级别(NALU的作用, 方便网络传输)
- 14 **
- 15 -------------------------------------------------------------------------------------------------------------
- 16 ** 字段名称 | 长度(bits) | 有关描述
- 17 -------------------------------------------------------------------------------------------------------------
- 18 ** forbidden_bit | 1 | 编码中默认值为0, 当网络识别此单元中存在比特错误时, 可将其设为1, 以便接收方丢掉该单元
- 19 ** nal_reference_idc | 2 | 0~3标识这个NALU的重要级别
- 20 ** nal_unit_type | 5 | NALU的类型(类型1~12是H.264定义的, 类型24~31是用于H.264以外的,
- 21 RTP负荷规范使用这其中的一些值来定义包聚合和分裂, 其他值为H.264保留)
- 22
- 23 ** nal_unit_type:
- 24 0 未使用
- 25 1 未使用Data Partitioning, 非IDR图像的Slice
- 26 2 使用Data Partitioning且为Slice A
- 27 3 使用Data Partitioning且为Slice B
- 28 4 使用Data Partitioning且为Slice C
- 29 5 IDR图像的Slice(立即刷新)
- 30 6 补充增强信息(SEI)
- 31 7 序列参数集(sequence parameter set, SPS)
- 32 8 图像参数集(picture parameter set, PPS)
- 33 9 分界符
- 34 10 序列结束
- 35 11 码流结束
- 36 12 填充
- 37 13...23 保留
- 38 24...31 未使用
- 39
- 40 ** SPS, PPS. SLICE等信息就不解析了. 为了减少bits, 用了哥伦布编码(自己解析比较麻烦, 但是网上有很多).
- 41
- 42 ** SPS信息说明:
- 43 1. 视频宽高, 帧率等信息;
- 44 2. seq_parameter_set_id, 指明本序列参数集的id号, 这个id号将被picture参数集引用;
- 45 3. pic_width_in_mbs_minus1, 加1指定以宏块(16*16)为单位的每个解码图像的宽度, 即width = (pic_width_in_mbs_minus1 + 1) * 16
- 46 4. pic_height_in_map_units_minus1;
- 47 5. pic_order_cnt_type, 视频的播放顺序序号叫做POC(picture order count), 取值0,1,2;
- 48 6. time_scale, fixed_frame_rate_flag, 计算帧率(fps).
- 49 视频帧率信息在SPS的VUI parameters syntax中, 需要根据time_scale, fixed_frame_rate_flag计算得到: fps = time_scale / num_units_in_tick.
- 50 但是需要判断参数timing_info_present_flag是否存在, 若不存在表示FPS在信息流中无法获取.
- 51 同时还存在另外一种情况: fixed_frame_rate_flag为1时, 两个连续图像的HDR输出时间频率为单位, 获取的fps是实际的2倍.
- 52
- 53 ** PPS信息说明:
- 54 1. pic_parameter_set_id, 用以指定本参数集的序号, 该序号在各片的片头被引用;
- 55 2. seq_parameter_set_id, 指明本图像参数集所引用的序列参数集的序号;
- 56 3. 其他高深的暂时还不理解, 指明参考帧队列等.
- 57
- 58 ** SLICE信息说明:
- 59 1. slice_type, 片的类型;
- 60 2. pic_parameter_set_id, 引用的图像索引;
- 61 3. frame_num, 每个参考帧都有一个连续的frame_num作为它们的标识, 它指明了各图像的解码顺序. 非参考帧也有,但没有意义;
- 62 4. least significant bits;
- 63 5. 综合三种poc(pic_order_cnt_type), 类型2应该是最省bit的, 因为直接从frame_num获得, 但是序列方式限制最大;
- 64 类型1, 只需要一定的bit量在sps标志出一些信息还在slice header中表示poc的变化, 但是比类型0要节省bit, 但是其序列并不是随意的, 要周期变化;
- 65 对于类型0因为要对poc的lsb(pic_order_cnt_lsb, last bit)进行编码所以用到的bit最多, 优点是序列可以随意.
- 66 ** 自我理解, 不一定准确(这边算显示顺序, 要根据SPS中的pic_order_cnt_type, 为2, 意味着码流中没有B帧, frame_num即为显示顺序;
- 67 为1, 依赖frame_num求解POC; 为0, 把POC的低位编进码流内, 但这只是低位, 而POC的高位PicOrderCntMsb则要求解码器自行计数,
- 68 计数方式依赖于前一编码帧(PrevPicOrderCntMsb与PrevPicOrderCntLsb.
- 69
- 70 ** 一般的码流分析所见(未仔细证实): pic_order_cnt_type=2, 只有frame_num(无B帧);
- 71 pic_order_cnt_type=1, 暂未分析到;
- 72 pic_order_cnt_type=0, pic_order_cnt_lsb指示显示顺序, 一般为偶数增长(0, 2, 4, 6, 据说是什么场方式和帧方式, 场时其实是0 0 2 2 4 4).
- 73
- 74 ** 编码与显示的原因: 视频编码顺序与视频的播放顺序, 并不完全相同, 视频编码时, 如果采用了B帧编码, 由于B帧很多时候都是双向预测得来的,
- 75 这时会先编码B帧的后向预测图像(P帧), 然后再进行B帧编码, 因此会把视频原来的播放顺序打乱, 以新的编码顺序输出码流,
- 76 而在解码断接收到码流后, 需要把顺序还原成原本的播放顺序, 以输出正确的视频. 在编解码中, 视频的播放顺序序号叫做POC(picture order count).
- 77
- 78 ** 总结: 1. 码流中有很多SPS(序列), 一个序列中有多个图像, 一个图像中有多个片, 一个片中有多个块;
- 79 2. SPS中有seq_parameter_set_id. PPS中有pic_parameter_set_id, 并通过seq_parameter_set_id指明关联的序列.
- 80 SLICE中有pic_parameter_set_id, 指明关联的图像;
- 81 3. SPS中可计算宽高以及帧率, pic_order_cnt_type(显示顺序的类型);
- 82 SLICE HEADER中可算出解码的顺序, 以及根据pic_order_cnt_type算出显示顺序.
- 83 ************************************************************************************************************/
- 84 typedef enum e_h264_nalu_priority
- 85 {
- 86 NALU_PRIORITY_DISPOSABLE = 0,
- 87 NALU_PRIORITY_LOW = 1,
- 88 NALU_PRIORITY_HIGH = 2,
- 89 NALU_PRIORITY_HIGHEST = 3,
- 90 } E_H264_NALU_PRIORITY;
- 91
- 92 typedef enum e_h264_nalu_type
- 93 {
- 94 NALU_TYPE_SLICE = 1,
- 95 NALU_TYPE_DPA = 2,
- 96 NALU_TYPE_DPB = 3,
- 97 NALU_TYPE_DPC = 4,
- 98 NALU_TYPE_IDR = 5,
- 99 NALU_TYPE_SEI = 6,
- 100 NALU_TYPE_SPS = 7,
- 101 NALU_TYPE_PPS = 8,
- 102 NALU_TYPE_AUD = 9,
- 103 NALU_TYPE_EOSEQ = 10,
- 104 NALU_TYPE_EOSTREAM = 11,
- 105 NALU_TYPE_FILL = 12,
- 106 } E_H264_NALU_TYPE;
- 107
- 108 typedef struct t_h264_nalu_header
- 109 {
- 110 unsigned char forbidden_bit:1, nal_reference_idc:2, nal_unit_type:5;
- 111 } T_H264_NALU_HEADER;
- 112
- 113 typedef struct t_h264_nalu
- 114 {
- 115 int startCodeLen;
- 116
- 117 T_H264_NALU_HEADER h264NaluHeader;
- 118
- 119 unsigned int bodyLen;
- 120
- 121 unsigned char *bodyData;
- 122 } T_H264_NALU;
- 123
- 124 /**********************************************************************************
- 125 1. h264的起始码: 0x000001(3 Bytes)或0x00000001(4 Bytes);
- 126 2. 文件流中用起始码来区分NALU.
- 127 ***********************************************************************************/
- 128 static int FindStartCode3Bytes(unsigned char *scData)
- 129 {
- 130 int isFind = 0;
- 131
- 132 if ((0==scData[0]) && (0==scData[1]) && (1==scData[2]))
- 133 {
- 134 isFind = 1;
- 135 }
- 136
- 137 return isFind;
- 138 }
- 139
- 140 static int FindStartCode4Bytes(unsigned char *scData)
- 141 {
- 142 int isFind = 0;
- 143
- 144 if ((0==scData[0]) && (0==scData[1]) && (0==scData[2]) && (1 == scData[3]))
- 145 {
- 146 isFind = 1;
- 147 }
- 148
- 149 return isFind;
- 150 }
- 151
- 152 static int GetNaluDataLen(int startPos, int h264BitsSize, unsigned char *h264Bits)
- 153 {
- 154 int parsePos = 0;
- 155
- 156 parsePos = startPos;
- 157
- 158 while (parsePos < h264BitsSize)
- 159 {
- 160 if (FindStartCode3Bytes(&h264Bits[parsePos]))
- 161 {
- 162 return parsePos - startPos;
- 163 }
- 164 else if (FindStartCode4Bytes(&h264Bits[parsePos]))
- 165 {
- 166 return parsePos - startPos;
- 167 }
- 168 else
- 169 {
- 170 parsePos++;
- 171 }
- 172 }
- 173
- 174 return parsePos - startPos; // if file is end
- 175 }
- 176
- 177 static void ParseNaluData(const unsigned int naluLen, unsigned char* const nuluData)
- 178 {
- 179 static int naluNum = 0;
- 180
- 181 unsigned char *data = NULL;
- 182 unsigned char priorityStr[PRTNTF_STR_LEN+1] = {0};
- 183 unsigned char typeStr[PRTNTF_STR_LEN+1] = {0};
- 184
- 185 T_H264_NALU_HEADER h264NaluHeader = {0};
- 186
- 187 data = nuluData;
- 188
- 189 memset(&h264NaluHeader, 0x0, sizeof(T_H264_NALU_HEADER));
- 190
- 191 h264NaluHeader.nal_reference_idc = data[0]>>5 & 0x3;
- 192 h264NaluHeader.nal_unit_type = data[0] & 0x1f;
- 193
- 194 naluNum++;
- 195
- 196 #ifdef PRINTF_DEBUG
- 197 switch (h264NaluHeader.nal_reference_idc)
- 198 {
- 199 case NALU_PRIORITY_DISPOSABLE:
- 200 sprintf(priorityStr, "DISPOS");
- 201 break;
- 202
- 203 case NALU_PRIORITY_LOW:
- 204 sprintf(priorityStr, "LOW");
- 205 break;
- 206
- 207 case NALU_PRIORITY_HIGH:
- 208 sprintf(priorityStr, "HIGH");
- 209 break;
- 210
- 211 case NALU_PRIORITY_HIGHEST:
- 212 sprintf(priorityStr, "HIGHEST");
- 213 break;
- 214
- 215 default:
- 216 break;
- 217 }
- 218
- 219 switch (h264NaluHeader.nal_unit_type)
- 220 {
- 221 case NALU_TYPE_SLICE:
- 222 sprintf(typeStr,"SLICE");
- 223 break;
- 224
- 225 case NALU_TYPE_DPA:
- 226 sprintf(typeStr,"DPA");
- 227 break;
- 228
- 229 case NALU_TYPE_DPB:
- 230 sprintf(typeStr,"DPB");
- 231 break;
- 232
- 233 case NALU_TYPE_DPC:
- 234 sprintf(typeStr,"DPC");
- 235 break;
- 236
- 237 case NALU_TYPE_IDR:
- 238 sprintf(typeStr,"IDR");
- 239 break;
- 240
- 241 case NALU_TYPE_SEI:
- 242 sprintf(typeStr,"SEI");
- 243 break;
- 244
- 245 case NALU_TYPE_SPS:
- 246 sprintf(typeStr,"SPS");
- 247 break;
- 248
- 249 case NALU_TYPE_PPS:
- 250 sprintf(typeStr,"PPS");
- 251 break;
- 252
- 253 case NALU_TYPE_AUD:
- 254 sprintf(typeStr,"AUD");
- 255 break;
- 256
- 257 case NALU_TYPE_EOSEQ:
- 258 sprintf(typeStr,"EOSEQ");
- 259 break;
- 260
- 261 case NALU_TYPE_EOSTREAM:
- 262 sprintf(typeStr, "EOSTREAM");
- 263 break;
- 264
- 265 case NALU_TYPE_FILL:
- 266 sprintf(typeStr, "FILL");
- 267 break;
- 268
- 269 default:
- 270 break;
- 271 }
- 272
- 273 printf("%5d| %7s| %6s| %8d|\n",naluNum,priorityStr,typeStr,naluLen);
- 274 #endif
- 275
- 276 }
- 277
- 278 int main(int argc, char *argv[])
- 279 {
- 280 int fileLen = 0;
- 281 int naluLen = 0;
- 282 int h264BitsPos = 0;
- 283
- 284 unsigned char *h264Bits = NULL;
- 285 unsigned char *naluData = NULL;
- 286
- 287 FILE *fp = NULL;
- 288
- 289 if (2 != argc)
- 290 {
- 291 printf("Usage: flvparse **.flv\n");
- 292
- 293 return -1;
- 294 }
- 295
- 296 fp = fopen(argv[1], "rb");
- 297 if (!fp)
- 298 {
- 299 printf("open file[%s] error!\n", argv[1]);
- 300
- 301 return -1;
- 302 }
- 303
- 304 fseek(fp, 0, SEEK_END);
- 305
- 306 fileLen = ftell(fp);
- 307
- 308 fseek(fp, 0, SEEK_SET);
- 309
- 310 h264Bits = (unsigned char*)malloc(fileLen);
- 311 if (!h264Bits)
- 312 {
- 313 printf("maybe file is too long, or memery is not enough!\n");
- 314
- 315 fclose(fp);
- 316
- 317 return -1;
- 318 }
- 319
- 320 memset(h264Bits, 0x0, fileLen);
- 321
- 322 if (fread(h264Bits, 1, fileLen, fp) < 0)
- 323 {
- 324 printf("read file data to h264Bits error!\n");
- 325
- 326 fclose(fp);
- 327 free(h264Bits);
- 328
- 329 h264Bits = NULL;
- 330
- 331 return -1;
- 332 }
- 333
- 334 fclose(fp);
- 335
- 336 printf("-----+-------- NALU Table ------+\n");
- 337 printf(" NUM | IDC | TYPE | LEN |\n");
- 338 printf("-----+--------+-------+---------+\n");
- 339
- 340 while (h264BitsPos < (fileLen-4))
- 341 {
- 342 if (FindStartCode3Bytes(&h264Bits[h264BitsPos]))
- 343 {
- 344 naluLen = GetNaluDataLen(h264BitsPos+3, fileLen, h264Bits);
- 345
- 346 naluData = (unsigned char*)malloc(naluLen);
- 347 if (naluData)
- 348 {
- 349 memset(naluData, 0x0, naluLen);
- 350
- 351 memcpy(naluData, h264Bits+h264BitsPos+3, naluLen);
- 352
- 353 ParseNaluData(naluLen, naluData);
- 354
- 355 free(naluData);
- 356 naluData = NULL;
- 357 }
- 358
- 359 h264BitsPos += (naluLen+3);
- 360 }
- 361 else if (FindStartCode4Bytes(&h264Bits[h264BitsPos]))
- 362 {
- 363 naluLen = GetNaluDataLen(h264BitsPos+4, fileLen, h264Bits);
- 364
- 365 naluData = (unsigned char*)malloc(naluLen);
- 366 if (naluData)
- 367 {
- 368 memset(naluData, 0x0, naluLen);
- 369
- 370 memcpy(naluData, h264Bits+h264BitsPos+4, naluLen);
- 371
- 372 ParseNaluData(naluLen, naluData);
- 373
- 374 free(naluData);
- 375 naluData = NULL;
- 376 }
- 377
- 378 h264BitsPos += (naluLen+4);
- 379 }
- 380 else
- 381 {
- 382 h264BitsPos++;
- 383 }
- 384 }
- 385
- 386 return 0;
- 387 }