// Return TRUE if all was read. FALSE if a problem occured: // If a bitstream syntax problem occured the bitstream will // point to after the problem, in case we run out of data the bitstream // will point to where we want to restart after getting more. static int read_pic_data(struct bitstream *esstream) { dbg_print(DMT_VERBOSE, "Read PIC Data\n"); uint8_t startcode = next_start_code(esstream); // Possibly the last call to this function ended with the last // bit of the slice? I.e. in_pic_data is still true, but we are // seeing the next start code. // We only get here after seeing that start code if (startcode < 0x01 || startcode > 0xAF) { dbg_print(DMT_VERBOSE, "Read Pic Data - processed0\n"); return 1; } // If we get here esstream points to the start of a slice_start_code // should we run out of data in esstream this is where we want to restart // after getting more. unsigned char *slice_start = esstream->pos; do { startcode = next_start_code(esstream); // Syntax check if (startcode == 0xB4) { if (esstream->bitsleft < 0) init_bitstream(esstream, slice_start, esstream->end); if ( esstream->error ) dbg_print(DMT_VERBOSE, "read_pic_data: syntax problem.\n"); else dbg_print(DMT_VERBOSE, "read_pic_data: reached end of bitstream.\n"); return 0; } slice_start = esstream->pos; // No need to come back if ( startcode >= 0x01 && startcode <= 0xAF ) { read_u32(esstream); // Advance bitstream search_start_code(esstream); // Skip this slice } } while(startcode >= 0x01 && startcode <= 0xAF); if (esstream->bitsleft < 0) { init_bitstream(esstream, slice_start, esstream->end); return 0; } dbg_print(DMT_VERBOSE, "Read Pic Data - processed\n"); return 1; }
const unsigned char* H264FileReader::ReadNextFrame() { size_t n = 0; const unsigned char* p; assert(m_ptr+m_offset == search_start_code(m_ptr+m_offset, m_bytes-m_offset)); do { p = search_start_code(m_ptr + m_offset + 3, m_bytes - m_offset - 3); if(!p) { if(m_offset > 0) { memmove(m_ptr, m_ptr+m_offset, m_bytes-m_offset); m_bytes -= m_offset; m_offset = 0; // try read file assert(m_bytes < m_capacity); n = fread(m_ptr + m_bytes, 1, m_capacity - m_bytes, m_fp); m_bytes += n; } else { // 1. more memory unsigned char* ptr = NULL; ptr = (unsigned char*)realloc(m_ptr, m_capacity + m_capacity/2); if(ptr) { m_ptr = ptr; m_capacity += m_capacity/2; // 2. read file assert(0 == m_offset); n = fread(m_ptr + m_bytes, 1, m_capacity - m_bytes, m_fp); m_bytes += n; } else { break; // don't have enough memory } } } } while(!p && m_ptr && m_capacity < 10*1024*1024 && n > 0); // Max frame size 10MB return p; }
int H264FileReader::Init() { //assert(IsOpened()); //assert(0 == ftell(m_fp)); assert(m_ptr == search_start_code(m_ptr, m_bytes)); long offset = 0; size_t count = 0; bool spspps = true; const unsigned char* nalu = m_ptr; do { const unsigned char* nalu2 = ReadNextFrame(); nalu = m_ptr + m_offset; int nal_unit_type = h264_nal_type(nalu); assert(0 != nal_unit_type); if(nal_unit_type <= 5) { if(m_sps.size() > 0) spspps = false; // don't need more sps/pps long n = ftell(m_fp); vframe_t frame; frame.offset = offset; frame.bytes = (nalu2 ? toOffset(nalu2) : n) - offset; frame.time = 40 * count++; frame.idr = 5 == nal_unit_type; // IDR-frame m_videos.push_back(frame); offset += frame.bytes; } else if(NAL_SPS == nal_unit_type || NAL_PPS == nal_unit_type) { assert(nalu2); if(spspps && nalu2) { size_t n = 0x01 == nalu[2] ? 3 : 4; sps_t sps(nalu2 - nalu - n); memcpy(&sps[0], nalu+n, nalu2-nalu-n); // filter last 0x00 bytes while(sps.size() > 0 && !*sps.rbegin()) sps.resize(sps.size()-1); m_sps.push_back(sps); } } nalu = nalu2; m_offset = nalu - m_ptr; } while(nalu); m_duration = 40 * count; return 0; }
// Return TRUE if the data parsing finished, FALSE otherwise. // estream->pos is advanced. Data is only processed if esstream->error // is FALSE, parsing can set esstream->error to TRUE. static int extension_and_user_data(struct bitstream *esstream, int udtype) { dbg_print(DMT_VERBOSE, "Extension and user data(%d)\n", udtype); if (esstream->error || esstream->bitsleft <= 0) return 0; // Remember where to continue unsigned char *eau_start = esstream->pos; uint8_t startcode; do { startcode = next_start_code(esstream); if ( startcode == 0xB2 || startcode == 0xB5 ) { read_u32(esstream); // Advance bitstream unsigned char *dstart = esstream->pos; // Advanve esstream to the next startcode. Verify that // the whole extension was available and discard blocks // followed by PACK headers. The latter usually indicates // a PS treated as an ES. uint8_t nextstartcode = search_start_code(esstream); if (nextstartcode == 0xBA) { mprint("\nFound PACK header in ES data. Probably wrong stream mode!\n"); esstream->error = 1; return 0; } if (esstream->error) { dbg_print(DMT_VERBOSE, "Extension and user data - syntax problem\n"); return 0; } if (esstream->bitsleft < 0) { dbg_print(DMT_VERBOSE, "Extension and user data - inclomplete\n"); // Restore to where we need to continue init_bitstream(esstream, eau_start, esstream->end); esstream->bitsleft = -1; // Redundant return 0; } if (startcode == 0xB2) { struct bitstream ustream; init_bitstream(&ustream, dstart, esstream->pos); user_data(&ustream, udtype); } else { dbg_print(DMT_VERBOSE, "Skip %d bytes extension data.\n", esstream->pos - dstart); } // If we get here esstream points to the end of a block // of extension or user data. Should we run out of data in // this loop this is where we want to restart after getting more. eau_start = esstream->pos; } } while(startcode == 0xB2 || startcode == 0xB5); if (esstream->error) { dbg_print(DMT_VERBOSE, "Extension and user data - syntax problem\n"); return 0; } if (esstream->bitsleft < 0) { dbg_print(DMT_VERBOSE, "Extension and user data - inclomplete\n"); // Restore to where we need to continue init_bitstream(esstream, eau_start, esstream->end); esstream->bitsleft = -1; // Redundant return 0; } dbg_print(DMT_VERBOSE, "Extension and user data - processed\n"); // Read complete return 1; }
// Return TRUE if the video sequence was finished, FALSE // Otherwise. estream->pos shall point to the position where // the next call will continue, i.e. the possible begin of an // unfinished video sequence or after the finished sequence. static int es_video_sequence(struct bitstream *esstream) { // Avoid "Skip forward" message on first call and later only // once per search. static int noskipmessage = 1; uint8_t startcode; dbg_print(DMT_VERBOSE, "es_video_sequence()\n"); esstream->error = 0; // Analyze sequence header ... if (!no_bitstream_error) { // We might start here because of a syntax error. Discard // all data until a new sequence_header_code or group_start_code // is found. if (!noskipmessage) // Avoid unnecessary output. mprint("\nSkip forward to the next Sequence or GOP start.\n"); else noskipmessage = 0; uint8_t startcode; while(1) { // search_start_code() cannot produce esstream->error startcode = search_start_code(esstream); if (esstream->bitsleft < 0) { noskipmessage = 1; return 0; } if (startcode == 0xB3 || startcode == 0xB8) // found it break; skip_bits(esstream, 4*8); } no_bitstream_error = 1; saw_seqgoppic = 0; in_pic_data = 0; } do { startcode = next_start_code(esstream); dbg_print(DMT_VERBOSE, "\nM2V - next start code %02X %d\n", startcode, in_pic_data); // Syntax check - also returns on bitsleft < 0 if (startcode == 0xB4) { if (esstream->error) { no_bitstream_error = 0; dbg_print(DMT_VERBOSE, "es_video_sequence: syntax problem.\n"); } dbg_print(DMT_VERBOSE, "es_video_sequence: return on B4 startcode.\n"); return 0; } // Sequence_end_code if (startcode == 0xB7) { read_u32(esstream); // Advance bitstream no_bitstream_error = 0; break; } if (!in_pic_data && startcode == 0xB3) { if (!read_seq_info(esstream)) { if (esstream->error) no_bitstream_error = 0; return 0; } saw_seqgoppic = 1; continue; } if (!in_pic_data && startcode == 0xB8) { if (!read_gop_info(esstream)) { if (esstream->error) no_bitstream_error = 0; return 0; } saw_seqgoppic = 2; continue; } if (!in_pic_data && startcode == 0x00) { if (!read_pic_info(esstream)) { if (esstream->error) no_bitstream_error = 0; return 0; } saw_seqgoppic = 3; in_pic_data = 1; continue; } // Only looks for extension and user data if we saw sequence, gop // or picture info before. // This check needs to be before the "in_pic_data" part. if ( saw_seqgoppic && (startcode == 0xB2 || startcode == 0xB5)) { if (!read_eau_info(esstream, saw_seqgoppic-1)) { if (esstream->error) no_bitstream_error = 0; return 0; } saw_seqgoppic = 0; continue; } if (in_pic_data) // See comment in read_pic_data() { if (!read_pic_data(esstream)) { if (esstream->error) no_bitstream_error = 0; return 0; } saw_seqgoppic = 0; in_pic_data = 0; continue; } // Nothing found - bitstream error if (startcode == 0xBA) { mprint("\nFound PACK header in ES data. Probably wrong stream mode!\n"); } else { mprint("\nUnexpected startcode: %02X\n", startcode); } no_bitstream_error = 0; return 0; } while(1); return 1; }