#include "h2645_parser.h" #include #include #include "h264_stream.h" #include "h265_stream.h" #include "struct.h" const int MAX_NAL_SIZE = 1 * 1024 * 1024; inline int findStartcode3(unsigned char* buffer) { return (buffer[0] == 0 && buffer[1] == 0 && buffer[2] == 1); } inline int findStartcode4(unsigned char* buffer) { return (buffer[0] == 0 && buffer[1] == 0 && buffer[2] == 0 && buffer[3] == 1); } static int find_first_NALU(FILE* fp, int *start_code_length) { int found = 0; int info2 = 0; int info3 = 0; int eof = 0; int pos = 0; int startcode_len = 0; unsigned char* buffer = NULL; if ((buffer = (unsigned char*)calloc(MAX_NAL_SIZE, sizeof(char))) == NULL) printf("Could not allocate buffer memory\n"); while (!found && !feof(fp)) { buffer[pos++] = fgetc(fp);//¶ÁÒ»¸ö×Ö½Úµ½BUFÖÐ info3 = findStartcode4(&buffer[pos - 4]);//ÅжÏÊÇ·ñΪ0x00000001 if (info3 != 1) { info2 = findStartcode3(&buffer[pos - 3]);//ÅжÏÊÇ·ñΪ0x000001 if (info2) { startcode_len = 3; } } else { startcode_len = 4; } found = (info2 == 1 || info3 == 1); if (pos >= MAX_NAL_SIZE) { free(buffer); return -1; } } // ÎļþÖ¸ÕëÒª»Ö¸´ fseek(fp, -startcode_len, SEEK_CUR); free(buffer); if (start_code_length != NULL) *start_code_length = startcode_len; return pos - startcode_len; } H2645 probe_h2645(FILE* fp) { int offset = 0; int startcode = 0; unsigned char nalHader = 0; unsigned char nalType = 0; H2645 type = TYPE_H264; // default offset = find_first_NALU(fp, &startcode); if (offset < 0) { return TYPE_UNKNOWN; } fseek(fp, offset + startcode, SEEK_SET); fread((void*)&nalHader, 1, 1, fp); // check h264 first... nalType = nalHader & 0x1f; // 5 bit if (nalType > 0 && nalType < 22) // ok { type = TYPE_H264; } else { // not h264, then check h265... nalType = (nalHader >> 1) & 0x3f; // 6 bit if (nalType >= 0 && nalType <= 47) // ok { type = TYPE_H265; } } fseek(fp, 0, SEEK_SET); return type; } typedef struct { int type; // 0 -- h.264; 1 -- h.265 unsigned int num; // ÐòºÅ unsigned int len; // º¬ÆðʼÂëµÄ×ܵij¤¶È unsigned int offset; // nal°üÔÚÎļþÖÐµÄÆ«ÒÆ int sliceType; // Ö¡ÀàÐÍ int nalType; // NALÀàÐÍ int startcodeLen; // start code³¤¶È char startcodeBuffer[16]; // ÆðʼÂ룬×Ö·û´®ÐÎʽ } NALU_t; typedef struct { int profile_idc; int level_idc; int width; int height; int crop_left; int crop_right; int crop_top; int crop_bottom; float max_framerate; // ÓÉSPS¼ÆËãµÃµ½µÄÖ¡ÂÊ£¬Îª0±íʾSPSÖÐûÓÐÏàÓ¦µÄ×ֶμÆËã int chroma_format_idc; // YUVÑÕÉ«¿Õ¼ä 0: monochrome 1:420 2:422 3:444 }SPSInfo_t; typedef struct { int encoding_type; // Ϊ1±íʾCABAC 0±íʾCAVLC }PPSInfo_t; static int getAnnexbNALU(FILE* fp, NALU_t* nalu, h264_stream_t *h264, h265_stream_t *hevc, uint8_t **wholeNal) { int pos = 0; int found, rewind; unsigned char* buffer; int info2 = 0, info3 = 0; int eof = 0; if ((buffer = (unsigned char*)calloc(MAX_NAL_SIZE, sizeof(char))) == NULL) printf("Could not allocate buffer memory\n"); if (3 != fread(buffer, 1, 3, fp))//´ÓÂëÁ÷ÖжÁ3¸ö×Ö½Ú { free(buffer); return 0; } info2 = findStartcode3(buffer);//ÅжÏÊÇ·ñΪ0x000001 if (info2 != 1) { //Èç¹û²»ÊÇ£¬ÔÙ¶ÁÒ»¸ö×Ö½Ú if (1 != fread(buffer + 3, 1, 1, fp))//¶ÁÒ»¸ö×Ö½Ú { free(buffer); return 0; } info3 = findStartcode4(buffer);//ÅжÏÊÇ·ñΪ0x00000001 if (info3 != 1)//Èç¹û²»ÊÇ£¬·µ»Ø-1 { free(buffer); return -1; } else { //Èç¹ûÊÇ0x00000001,µÃµ½¿ªÊ¼Ç°×ºÎª4¸ö×Ö½Ú nalu->startcodeLen = 4; } } else { //Èç¹ûÊÇ0x000001,µÃµ½¿ªÊ¼Ç°×ºÎª3¸ö×Ö½Ú nalu->startcodeLen = 3; } pos = nalu->startcodeLen; //²éÕÒÏÂÒ»¸ö¿ªÊ¼×Ö·ûµÄ±ê־λ found = 0; info2 = 0; info3 = 0; while (!found) { if (feof(fp))//ÅжÏÊÇ·ñµ½ÁËÎļþβ { eof = 1; goto got_nal; } buffer[pos++] = fgetc(fp);//¶ÁÒ»¸ö×Ö½Úµ½BUFÖÐ info3 = findStartcode4(&buffer[pos - 4]);//ÅжÏÊÇ·ñΪ0x00000001 if (info3 != 1) info2 = findStartcode3(&buffer[pos - 3]);//ÅжÏÊÇ·ñΪ0x000001 found = (info2 == 1 || info3 == 1); } // startcode¿ÉÄÜΪ3£¬Ò²¿ÉÄÜΪ4£¬¹ÊÒªÈç´ËÅÐ¶Ï rewind = (info3 == 1) ? -4 : -3; if (0 != fseek(fp, rewind, SEEK_CUR))//°ÑÎļþÖ¸ÕëÖ¸Ïòǰһ¸öNALUµÄĩβ { free(buffer); printf("Cannot fseek in the bit stream file"); } got_nal: // µ±´ïµ½Îļþĩβʱ£¬»ØÍË1¸öλÖà if (eof) { rewind = -1; } // °üÀ¨ÆðʼÂëÔÚÄÚµÄ5¸ö×Ö½Ú if (nalu->startcodeLen == 3) sprintf(nalu->startcodeBuffer, "%02x%02x%02x%02x", buffer[0], buffer[1], buffer[2], buffer[3]); else sprintf(nalu->startcodeBuffer, "%02x%02x%02x%02x%02x", buffer[0], buffer[1], buffer[2], buffer[3], buffer[4]); nalu->len = pos + rewind; uint8_t nal_header = 0; if (nalu->type) { hevc->sh->read_slice_type = 1; h265_read_nal_unit(hevc, &buffer[nalu->startcodeLen], nalu->len - nalu->startcodeLen); nalu->nalType = hevc->nal->nal_unit_type; nalu->sliceType = hevc->sh->slice_type; hevc->sh->read_slice_type = 0; } else { // simple version #if 0 nal_header = buffer[nalu->startcodeLen]; nalu->nalType = nal_header & 0x1f;// 5 bit // »ñÈ¡sliceÀàÐÍ£ºIÖ¡¡¢PÖ¡¡¢BÖ¡ // ×¢£ºÔÚnalÀàÐÍΪ1~5ʱ»ñÈ¡ if (nalu->nalType <= 5 && nalu->nalType >= 1) { int start_bit = 0; int first_mb_in_slice = ue((char*)buffer + nalu->startcodeLen + 1, 8, start_bit); nalu->sliceType = ue((char*)buffer + nalu->startcodeLen + 1, 8, start_bit); } if (nalu->nalType == 7 || nalu->nalType == 8) // sps pps { read_nal_unit(m_hH264, &buffer[nalu->startcodeLen], nalu->len - nalu->startcodeLen); } #else uint8_t* nal_buf = (uint8_t*)malloc(nalu->len); memcpy(nal_buf, buffer, nalu->len); *wholeNal = nal_buf; h264->sh->read_slice_type = 1; read_nal_unit(h264, &buffer[nalu->startcodeLen], nalu->len - nalu->startcodeLen); nalu->nalType = h264->nal->nal_unit_type; nalu->sliceType = h264->sh->slice_type; h264->sh->read_slice_type = 0; #endif } free(buffer); return (pos + rewind);//·µ»ØÁ½¸ö¿ªÊ¼×Ö·ûÖ®¼ä¼ä¸ôµÄ×Ö½ÚÊý£¬¼´°üº¬ÓÐǰ׺µÄNALUµÄ³¤¶È } static void showNalInfo(NALU_t* nalu); static int nalu_type(NALU_t* nalu) { if (nalu->type == 0) { // NALµ¥ÔªÀàÐÍ switch (nalu->nalType) { case 0: case 1: case 2: case 3: case 4:case 5:case 6: case 7:case 8:case 9:case 10:case 11:case 12:case 13: case 19: return nalu->nalType; default: return -1; } } else { // NALµ¥ÔªÀàÐÍ switch (nalu->nalType) { // to confirm type... case NAL_UNIT_CODED_SLICE_TRAIL_N: case NAL_UNIT_CODED_SLICE_TRAIL_R: case NAL_UNIT_CODED_SLICE_TSA_N: case NAL_UNIT_CODED_SLICE_TSA_R: case NAL_UNIT_CODED_SLICE_RADL_N: case NAL_UNIT_CODED_SLICE_RADL_R: case NAL_UNIT_CODED_SLICE_IDR_W_RADL: case NAL_UNIT_CODED_SLICE_IDR_N_LP: case NAL_UNIT_CODED_SLICE_CRA: case NAL_UNIT_PREFIX_SEI: case NAL_UNIT_SUFFIX_SEI: case NAL_UNIT_VPS: case NAL_UNIT_SPS: case NAL_UNIT_PPS: case NAL_UNIT_AUD: case NAL_UNIT_EOS: case NAL_UNIT_EOB: case NAL_UNIT_FILLER_DATA: return nalu->nalType; default: return -1; } } return -1; } int h2645_fileParse(FILE* fp, DataCallback* dct) { H2645 type = probe_h2645(fp); if (type == TYPE_UNKNOWN) { return 0; } h264_stream_t* h264_s = NULL; h265_stream_t* hevc_s = NULL; if (type == TYPE_H264) { h264_s = h264_new(); } if (type == TYPE_H265) { hevc_s = h265_new(); } NALU_t n; int nal_num = 0; int offset = 0; int nalLen = 0; memset(&n, '\0', sizeof(NALU_t)); int startcodelen = 0; offset = find_first_NALU(fp, &startcodelen); if (offset < 0) { return 0; } fseek(fp, offset, SEEK_SET); uint8_t* nalu_buf = NULL; while (!feof(fp)) { if (dct && dct->is_quit()) break; //printf("\n\n"); nalLen = getAnnexbNALU(fp, &n, h264_s, hevc_s, &nalu_buf);//ÿִÐÐÒ»´Î£¬ÎļþµÄÖ¸ÕëÖ¸Ïò±¾´ÎÕÒµ½µÄNALUµÄĩ⣬ÏÂÒ»¸öλÖü´ÎªÏ¸öNALUµÄÆðʼÂë0x000001 n.offset = offset; n.num = nal_num; offset = offset + nalLen; //showNalInfo(&n); if (n.len > 0) { if (dct) { packet_t* pkt = (packet_t*)malloc(sizeof(packet_t)); pkt->data = nalu_buf; pkt->length = n.len; pkt->pts = -1; pkt->dts = -1; if (nalu_type(&n) >= 0) { pkt->type = AV_VIDEO; } else { pkt->type = AV_AUDIO; } dct->push_packet(pkt, pkt->type); } } //printf("\n\n"); nal_num++; } return 0; } static void showNalInfo(NALU_t* nalu) { if (nalu->type == 0) { // NALµ¥ÔªÀàÐÍ switch (nalu->nalType) { case 0: printf("Unspecified\n"); break; case 1: printf("Coded slice of a non-IDR picture\n"); switch (nalu->sliceType) { case 0: case 5: printf("P Slice #%d\n"); break; case 1: case 6: printf("B Slice #%d\n"); break; case 2: case 7: printf("I Slice #%d\n"); break; } break; case 2: printf("DPA\n"); break; case 3: printf("DPB\n"); break; case 4: printf("DPC\n"); break; case 5: printf("Coded slice of an IDR picture\n"); printf("IDR #%d\n"); break; case 6: printf("Supplemental enhancement information\n"); printf("SEI\n"); break; case 7: printf("Sequence parameter set\n"); printf("SPS\n"); break; case 8: printf("Picture parameter set\n"); printf("PPS\n"); break; case 9: printf("Access UD\n"); printf("AUD\n"); break; case 10: printf("END_SEQUENCE\n"); break; case 11: printf("END_STREAM\n"); break; case 12: printf("FILLER_DATA\n"); break; case 13: printf("SPS_EXT\n"); break; case 19: printf("AUXILIARY_SLICE\n"); break; default: printf("Other\n"); break; } } else { // NALµ¥ÔªÀàÐÍ switch (nalu->nalType) { // to confirm type... case NAL_UNIT_CODED_SLICE_TRAIL_N: case NAL_UNIT_CODED_SLICE_TRAIL_R: printf("Coded slice segment of a non-TSA, non-STSA trailing picture\n"); switch (nalu->sliceType) { case H265_SH_SLICE_TYPE_B: printf("B Slice #%d\n"); break; case H265_SH_SLICE_TYPE_P: printf("P Slice #%d\n"); break; case H265_SH_SLICE_TYPE_I: printf("I Slice #%d\n"); break; } break; case NAL_UNIT_CODED_SLICE_TSA_N: case NAL_UNIT_CODED_SLICE_TSA_R: printf("Coded slice segment of a TSA picture\n"); switch (nalu->sliceType) { case H265_SH_SLICE_TYPE_B: printf("B Slice #%d\n"); break; case H265_SH_SLICE_TYPE_P: printf("P Slice #%d\n"); break; case H265_SH_SLICE_TYPE_I: printf("I Slice #%d\n"); break; } break; case NAL_UNIT_CODED_SLICE_RADL_N: case NAL_UNIT_CODED_SLICE_RADL_R: printf("Coded slice segment of a TSA picture\n"); switch (nalu->sliceType) { case H265_SH_SLICE_TYPE_B: printf("B Slice #%d\n"); break; case H265_SH_SLICE_TYPE_P: printf("P Slice #%d\n"); break; case H265_SH_SLICE_TYPE_I: printf("I Slice #%d\n"); break; } break; case NAL_UNIT_CODED_SLICE_IDR_W_RADL: case NAL_UNIT_CODED_SLICE_IDR_N_LP: printf("Coded slice of an IDR picture\n"); printf("IDR #%d\n"); break; case NAL_UNIT_CODED_SLICE_CRA: printf("Coded slice segment of a CRA picture\n"); printf("CRA #%d\n"); break; case NAL_UNIT_PREFIX_SEI: case NAL_UNIT_SUFFIX_SEI: printf("Supplemental enhancement information\n"); printf("SEI\n"); break; case NAL_UNIT_VPS: printf("Video parameter set\n"); printf("VPS\n"); break; case NAL_UNIT_SPS: printf("Sequence parameter set\n"); printf("SPS\n"); break; case NAL_UNIT_PPS: printf("Picture parameter set\n"); printf("PPS\n"); break; case NAL_UNIT_AUD: printf("Access UD\n"); printf("AUD\n"); break; case NAL_UNIT_EOS: printf("END_SEQUENCE\n"); break; case NAL_UNIT_EOB: printf("END_STREAM\n"); break; case NAL_UNIT_FILLER_DATA: printf("FILLER_DATA\n"); break; default: printf("Unknown\n"); break; } } // ÐòºÅ printf("number: %d\n", nalu->num + 1); // ÏòÁ¿ÖеÄÐòºÅ´Ó0¿ªÊ¼ // Êý¾ÝÆ«ÒÆ printf("offset: %08X\n", nalu->offset); // ³¤¶È printf("length: %d\n", nalu->len); // ÆðʼÂë printf("start code: %s\n", nalu->startcodeBuffer); }