unsigned H264or5VideoStreamParser::parse() { static int aaaa = 0 ; aaaa++; qDebug()<<"H264or5VideoStreamParser::parse() { 868 aaaa" <<aaaa; try { // The stream must start with a 0x00000001: if (!fHaveSeenFirstStartCode) { // Skip over any input bytes that precede the first 0x00000001: u_int32_t first4Bytes; while ((first4Bytes = test4Bytes()) != 0x00000001) { get1Byte(); setParseState(); // ensures that we progress over bad data } skipBytes(4); // skip this initial code setParseState(); fHaveSeenFirstStartCode = True; // from now on } if (fOutputStartCodeSize > 0 && curFrameSize() == 0 && !haveSeenEOF()) { // Include a start code in the output: save4Bytes(0x00000001); } // Then save everything up until the next 0x00000001 (4 bytes) or 0x000001 (3 bytes), or we hit EOF. // Also make note of the first byte, because it contains the "nal_unit_type": if (haveSeenEOF()) { // We hit EOF the last time that we tried to parse this data, so we know that any remaining unparsed data // forms a complete NAL unit, and that there's no 'start code' at the end: unsigned remainingDataSize = totNumValidBytes() - curOffset(); #ifdef DEBUG unsigned const trailingNALUnitSize = remainingDataSize; #endif while (remainingDataSize > 0) { u_int8_t nextByte = get1Byte(); if (!fHaveSeenFirstByteOfNALUnit) { fFirstByteOfNALUnit = nextByte; fHaveSeenFirstByteOfNALUnit = True; } saveByte(nextByte); --remainingDataSize; } #ifdef DEBUG if (fHNumber == 264) { u_int8_t nal_ref_idc = (fFirstByteOfNALUnit&0x60)>>5; u_int8_t nal_unit_type = fFirstByteOfNALUnit&0x1F; fprintf(stderr, "Parsed trailing %d-byte NAL-unit (nal_ref_idc: %d, nal_unit_type: %d (\"%s\"))\n", trailingNALUnitSize, nal_ref_idc, nal_unit_type, nal_unit_type_description_h264[nal_unit_type]); } else { // 265 u_int8_t nal_unit_type = (fFirstByteOfNALUnit&0x7E)>>1; fprintf(stderr, "Parsed trailing %d-byte NAL-unit (nal_unit_type: %d (\"%s\"))\n", trailingNALUnitSize, nal_unit_type, nal_unit_type_description_h265[nal_unit_type]); } #endif (void)get1Byte(); // forces another read, which will cause EOF to get handled for real this time return 0; } else {
unsigned MPEG1or2VideoStreamParser ::parseVideoSequenceHeader(Boolean haveSeenStartCode) { #ifdef DEBUG fprintf(stderr, "parsing video sequence header\n"); #endif unsigned first4Bytes; if (!haveSeenStartCode) { while ((first4Bytes = test4Bytes()) != VIDEO_SEQUENCE_HEADER_START_CODE) { #ifdef DEBUG fprintf(stderr, "ignoring non video sequence header: 0x%08x\n", first4Bytes); #endif get1Byte(); setParseState(PARSING_VIDEO_SEQUENCE_HEADER); // ensures we progress over bad data } first4Bytes = get4Bytes(); } else { // We've already seen the start code first4Bytes = VIDEO_SEQUENCE_HEADER_START_CODE; } save4Bytes(first4Bytes); // Next, extract the size and rate parameters from the next 8 bytes unsigned paramWord1 = get4Bytes(); save4Bytes(paramWord1); unsigned next4Bytes = get4Bytes(); #ifdef DEBUG unsigned short horizontal_size_value = (paramWord1&0xFFF00000)>>(32-12); unsigned short vertical_size_value = (paramWord1&0x000FFF00)>>8; unsigned char aspect_ratio_information = (paramWord1&0x000000F0)>>4; #endif unsigned char frame_rate_code = (paramWord1&0x0000000F); usingSource()->fFrameRate = frameRateFromCode[frame_rate_code]; #ifdef DEBUG unsigned bit_rate_value = (next4Bytes&0xFFFFC000)>>(32-18); unsigned vbv_buffer_size_value = (next4Bytes&0x00001FF8)>>3; fprintf(stderr, "horizontal_size_value: %d, vertical_size_value: %d, aspect_ratio_information: %d, frame_rate_code: %d (=>%f fps), bit_rate_value: %d (=>%d bps), vbv_buffer_size_value: %d\n", horizontal_size_value, vertical_size_value, aspect_ratio_information, frame_rate_code, usingSource()->fFrameRate, bit_rate_value, bit_rate_value*400, vbv_buffer_size_value); #endif // Now, copy all bytes that we see, up until we reach a GROUP_START_CODE // or a PICTURE_START_CODE: do { saveToNextCode(next4Bytes); } while (next4Bytes != GROUP_START_CODE && next4Bytes != PICTURE_START_CODE); setParseState((next4Bytes == GROUP_START_CODE) ? PARSING_GOP_HEADER_SEEN_CODE : PARSING_PICTURE_HEADER); // Compute this frame's timestamp by noting how many pictures we've seen // since the last GOP header: usingSource()->computePresentationTime(fPicturesSinceLastGOP); // Save this video_sequence_header, in case we need to insert a copy // into the stream later: saveCurrentVSH(); return curFrameSize(); }
void MatroskaFileParser::deliverFrameBytes() { do { MatroskaTrack* track = fOurFile.lookup(fBlockTrackNumber); if (track == NULL) break; // shouldn't happen MatroskaDemuxedTrack* demuxedTrack = fOurDemux->lookupDemuxedTrack(fBlockTrackNumber); if (demuxedTrack == NULL) break; // shouldn't happen unsigned const BANK_SIZE = bankSize(); while (fCurFrameNumBytesToGet > 0) { // Hack: We can get no more than BANK_SIZE bytes at a time: unsigned numBytesToGet = fCurFrameNumBytesToGet > BANK_SIZE ? BANK_SIZE : fCurFrameNumBytesToGet; getBytes(fCurFrameTo, numBytesToGet); fCurFrameTo += numBytesToGet; fCurFrameNumBytesToGet -= numBytesToGet; fCurOffsetWithinFrame += numBytesToGet; setParseState(); } while (fCurFrameNumBytesToSkip > 0) { // Hack: We can skip no more than BANK_SIZE bytes at a time: unsigned numBytesToSkip = fCurFrameNumBytesToSkip > BANK_SIZE ? BANK_SIZE : fCurFrameNumBytesToSkip; skipBytes(numBytesToSkip); fCurFrameNumBytesToSkip -= numBytesToSkip; fCurOffsetWithinFrame += numBytesToSkip; setParseState(); } #ifdef DEBUG fprintf(stderr, "\tdelivered frame #%d: %d bytes", fNextFrameNumberToDeliver, demuxedTrack->frameSize()); if (track->haveSubframes()) fprintf(stderr, "[offset %d]", fCurOffsetWithinFrame - track->subframeSizeSize - demuxedTrack->frameSize() - demuxedTrack->numTruncatedBytes()); if (demuxedTrack->numTruncatedBytes() > 0) fprintf(stderr, " (%d bytes truncated)", demuxedTrack->numTruncatedBytes()); fprintf(stderr, " @%u.%06u (%.06f from start); duration %u us\n", demuxedTrack->presentationTime().tv_sec, demuxedTrack->presentationTime().tv_usec, demuxedTrack->presentationTime().tv_sec+demuxedTrack->presentationTime().tv_usec/1000000.0-fPresentationTimeOffset, demuxedTrack->durationInMicroseconds()); #endif if (!track->haveSubframes() || fCurOffsetWithinFrame + track->subframeSizeSize >= fFrameSizesWithinBlock[fNextFrameNumberToDeliver]) { // Either we don't have subframes, or there's no more room for another subframe => We're completely done with this frame now: ++fNextFrameNumberToDeliver; fCurOffsetWithinFrame = 0; } if (fNextFrameNumberToDeliver == fNumFramesInBlock) { // We've delivered all of the frames from this block. Look for another block next: fCurrentParseState = LOOKING_FOR_BLOCK; } else { fCurrentParseState = DELIVERING_FRAME_WITHIN_BLOCK; } setParseState(); FramedSource::afterGetting(demuxedTrack); // completes delivery return; } while (0); // An error occurred. Try to recover: #ifdef DEBUG fprintf(stderr, "deliverFrameBytes(): Error parsing data; trying to recover...\n"); #endif fCurrentParseState = LOOKING_FOR_BLOCK; }
void MPEGProgramStreamParser::parsePackHeader() { #ifdef DEBUG fprintf(stderr, "parsing pack header\n"); fflush(stderr); #endif unsigned first4Bytes; while (1) { first4Bytes = test4Bytes(); // We're supposed to have a pack header here, but check also for // a system header or a PES packet, just in case: if (first4Bytes == PACK_START_CODE) { skipBytes(4); break; } else if (first4Bytes == SYSTEM_HEADER_START_CODE) { #ifdef DEBUG fprintf(stderr, "found system header instead of pack header\n"); #endif setParseState(PARSING_SYSTEM_HEADER); return; } else if (isPacketStartCode(first4Bytes)) { #ifdef DEBUG fprintf(stderr, "found packet start code 0x%02x instead of pack header\n", first4Bytes); #endif setParseState(PARSING_PES_PACKET); return; } setParseState(PARSING_PACK_HEADER); // ensures we progress over bad data if ((first4Bytes&0xFF) > 1) { // a system code definitely doesn't start here skipBytes(4); } else { skipBytes(1); } } // The size of the pack header differs depending on whether it's // MPEG-1 or MPEG-2. The next byte tells us this: unsigned char nextByte = get1Byte(); MPEG1or2Demux::SCR& scr = fUsingSource->fLastSeenSCR; // alias if ((nextByte&0xF0) == 0x20) { // MPEG-1 fUsingSource->fMPEGversion = 1; scr.highBit = (nextByte&0x08)>>3; scr.remainingBits = (nextByte&0x06)<<29; unsigned next4Bytes = get4Bytes(); scr.remainingBits |= (next4Bytes&0xFFFE0000)>>2; scr.remainingBits |= (next4Bytes&0x0000FFFE)>>1; scr.extension = 0; scr.isValid = True; skipBits(24); #if defined(DEBUG_TIMESTAMPS) || defined(DEBUG_SCR_TIMESTAMPS) fprintf(stderr, "pack hdr system_clock_reference_base: 0x%x", scr.highBit); fprintf(stderr, "%08x\n", scr.remainingBits); #endif } else if ((nextByte&0xC0) == 0x40) { // MPEG-2
void MPEG1or2VideoStreamParser::flushInput() { reset(); StreamParser::flushInput(); if (fCurrentParseState != PARSING_VIDEO_SEQUENCE_HEADER) { setParseState(PARSING_GOP_HEADER); // start from the next GOP } }
Boolean MatroskaFileParser::parseEBMLNumber(EBMLNumber& num) { unsigned i; u_int8_t bitmask = 0x80; for (i = 0; i < EBML_NUMBER_MAX_LEN; ++i) { while (1) { if (fLimitOffsetInFile > 0 && fCurOffsetInFile > fLimitOffsetInFile) return False; // We've hit our pre-set limit num.data[i] = get1Byte(); ++fCurOffsetInFile; // If we're looking for an id, skip any leading bytes that don't contain a '1' in the first 4 bits: if (i == 0/*we're a leading byte*/ && !num.stripLeading1/*we're looking for an id*/ && (num.data[i]&0xF0) == 0) { setParseState(); // ensures that we make forward progress if the parsing gets interrupted continue; } break; } if ((num.data[0]&bitmask) != 0) { // num[i] is the last byte of the id if (num.stripLeading1) num.data[0] &=~ bitmask; break; } bitmask >>= 1; } if (i == EBML_NUMBER_MAX_LEN) return False; num.len = i+1; return True; }
unsigned MPEG4VideoStreamParser ::parseVisualObjectSequence(Boolean haveSeenStartCode) { #ifdef DEBUG fprintf(stderr, "parsing VisualObjectSequence\n"); #endif usingSource()->startNewConfig(); u_int32_t first4Bytes; if (!haveSeenStartCode) { while ((first4Bytes = test4Bytes()) != VISUAL_OBJECT_SEQUENCE_START_CODE) { #ifdef DEBUG fprintf(stderr, "ignoring non VS header: 0x%08x\n", first4Bytes); #endif get1Byte(); setParseState(PARSING_VISUAL_OBJECT_SEQUENCE); // ensures we progress over bad data } first4Bytes = get4Bytes(); } else { // We've already seen the start code first4Bytes = VISUAL_OBJECT_SEQUENCE_START_CODE; } save4Bytes(first4Bytes); // The next byte is the "profile_and_level_indication": u_int8_t pali = get1Byte(); #ifdef DEBUG fprintf(stderr, "profile_and_level_indication: %02x\n", pali); #endif saveByte(pali); usingSource()->fProfileAndLevelIndication = pali; // Now, copy all bytes that we see, up until we reach // a VISUAL_OBJECT_START_CODE: u_int32_t next4Bytes = get4Bytes(); while (next4Bytes != VISUAL_OBJECT_START_CODE) { saveToNextCode(next4Bytes); } setParseState(PARSING_VISUAL_OBJECT); // Compute this frame's presentation time: usingSource()->computePresentationTime(fTotalTicksSinceLastTimeCode); // This header forms part of the 'configuration' information: usingSource()->appendToNewConfig(fStartOfFrame, curFrameSize()); return curFrameSize(); }
void MatroskaFileParser::lookForNextBlock() { #ifdef DEBUG fprintf(stderr, "looking for Block\n"); #endif // Read and skip over each Matroska header, until we get to a 'Cluster': EBMLId id; EBMLDataSize size; while (fCurrentParseState == LOOKING_FOR_BLOCK) { while (!parseEBMLIdAndSize(id, size)) {} #ifdef DEBUG fprintf(stderr, "MatroskaFileParser::lookForNextBlock(): Parsed id 0x%s (%s), size: %lld\n", id.hexString(), id.stringName(), size.val()); #endif switch (id.val()) { case MATROSKA_ID_SEGMENT: { // 'Segment' header: enter this break; } case MATROSKA_ID_CLUSTER: { // 'Cluster' header: enter this break; } case MATROSKA_ID_TIMECODE: { // 'Timecode' header: get this value unsigned timecode; if (parseEBMLVal_unsigned(size, timecode)) { fClusterTimecode = timecode; #ifdef DEBUG fprintf(stderr, "\tCluster timecode: %d (== %f seconds)\n", fClusterTimecode, fClusterTimecode*(fOurFile.fTimecodeScale/1000000000.0)); #endif } break; } case MATROSKA_ID_BLOCK_GROUP: { // 'Block Group' header: enter this break; } case MATROSKA_ID_SIMPLEBLOCK: case MATROSKA_ID_BLOCK: { // 'SimpleBlock' or 'Block' header: enter this (and we're done) fBlockSize = (unsigned)size.val(); fCurrentParseState = PARSING_BLOCK; break; } case MATROSKA_ID_BLOCK_DURATION: { // 'Block Duration' header: get this value (but we currently don't do anything with it) unsigned blockDuration; if (parseEBMLVal_unsigned(size, blockDuration)) { #ifdef DEBUG fprintf(stderr, "\tblock duration: %d (== %f ms)\n", blockDuration, (float)(blockDuration*fOurFile.fTimecodeScale/1000000.0)); #endif } break; } default: { // skip over this header skipHeader(size); #ifdef DEBUG fprintf(stderr, "\tskipped %lld bytes\n", size.val()); #endif break; } } setParseState(); } }
unsigned MPEG1or2VideoStreamParser::parsePictureHeader() { #ifdef DEBUG fprintf(stderr, "parsing picture header\n"); #endif // Note that we've already read the PICTURE_START_CODE // Next, extract the temporal reference from the next 4 bytes: unsigned next4Bytes = get4Bytes(); unsigned short temporal_reference = (next4Bytes & 0xFFC00000) >> (32 - 10); unsigned char picture_coding_type = (next4Bytes & 0x00380000) >> 19; #ifdef DEBUG unsigned short vbv_delay = (next4Bytes & 0x0007FFF8) >> 3; fprintf(stderr, "temporal_reference: %d, picture_coding_type: %d, vbv_delay: %d\n", temporal_reference, picture_coding_type, vbv_delay); #endif fSkippingCurrentPicture = fIFramesOnly && picture_coding_type != 1; if (fSkippingCurrentPicture) { // Skip all bytes that we see, up until we reach a slice_start_code: do { skipToNextCode(next4Bytes); } while (!isSliceStartCode(next4Bytes)); } else { // Save the PICTURE_START_CODE that we've already read: save4Bytes(PICTURE_START_CODE); // Copy all bytes that we see, up until we reach a slice_start_code: do { saveToNextCode(next4Bytes); } while (!isSliceStartCode(next4Bytes)); } setParseState(PARSING_SLICE); fCurrentSliceNumber = next4Bytes & 0xFF; // Record the temporal reference: fCurPicTemporalReference = temporal_reference; // Compute this frame's timestamp: usingSource()->computePresentationTime(fCurPicTemporalReference); if (fSkippingCurrentPicture) { return parse(); // try again, until we get a non-skipped frame } else { return curFrameSize(); } }
void MPEG4VideoStreamParser::flushInput() { fSecondsSinceLastTimeCode = 0; fTotalTicksSinceLastTimeCode = 0; fPrevNewTotalTicks = 0; fPrevPictureCountDelta = 1; StreamParser::flushInput(); if (fCurrentParseState != PARSING_VISUAL_OBJECT_SEQUENCE) { setParseState(PARSING_VISUAL_OBJECT_SEQUENCE); // later, change to GOV or VOP? ##### } }
unsigned H264VideoStreamParser::parseStartSequence() { u_int32_t test = test4Bytes(); while (test != 0x00000001) { skipBytes(1); test = test4Bytes(); } setParseState(PARSING_START_SEQUENCE); skipBytes(4); return parseNALUnit(); }
unsigned H264VideoStreamParser::parse() { try { // The stream must start with a 0x00000001: if (!fHaveSeenFirstStartCode) { // Skip over any input bytes that precede the first 0x00000001: u_int32_t first4Bytes; while ((first4Bytes = test4Bytes()) != 0x00000001) { get1Byte(); setParseState(); // ensures that we progress over bad data } skipBytes(4); // skip this initial code setParseState(); fHaveSeenFirstStartCode = True; // from now on } if (fOutputStartCodeSize > 0 && curFrameSize() == 0 && !haveSeenEOF()) { // Include a start code in the output: save4Bytes(0x00000001); } // Then save everything up until the next 0x00000001 (4 bytes) or 0x000001 (3 bytes), or we hit EOF. // Also make note of the first byte, because it contains the "nal_unit_type": if (haveSeenEOF()) { // We hit EOF the last time that we tried to parse this data, so we know that any remaining unparsed data // forms a complete NAL unit, and that there's no 'start code' at the end: unsigned remainingDataSize = totNumValidBytes() - curOffset(); #ifdef DEBUG unsigned const trailingNALUnitSize = remainingDataSize; #endif while (remainingDataSize > 0) { u_int8_t nextByte = get1Byte(); if (!fHaveSeenFirstByteOfNALUnit) { fFirstByteOfNALUnit = nextByte; fHaveSeenFirstByteOfNALUnit = True; } saveByte(nextByte); --remainingDataSize; } #ifdef DEBUG u_int8_t nal_ref_idc = (fFirstByteOfNALUnit&0x60)>>5; u_int8_t nal_unit_type = fFirstByteOfNALUnit&0x1F; fprintf(stderr, "Parsed trailing %d-byte NAL-unit (nal_ref_idc: %d, nal_unit_type: %d (\"%s\"))\n", trailingNALUnitSize, nal_ref_idc, nal_unit_type, nal_unit_type_description[nal_unit_type]); #endif (void)get1Byte(); // forces another read, which will cause EOF to get handled for real this time return 0; } else { u_int32_t next4Bytes = test4Bytes(); if (!fHaveSeenFirstByteOfNALUnit) { fFirstByteOfNALUnit = next4Bytes>>24; fHaveSeenFirstByteOfNALUnit = True; } while (next4Bytes != 0x00000001 && (next4Bytes&0xFFFFFF00) != 0x00000100) { // We save at least some of "next4Bytes". if ((unsigned)(next4Bytes&0xFF) > 1) { // Common case: 0x00000001 or 0x000001 definitely doesn't begin anywhere in "next4Bytes", so we save all of it: save4Bytes(next4Bytes); skipBytes(4); } else { // Save the first byte, and continue testing the rest: saveByte(next4Bytes>>24); skipBytes(1); } setParseState(); // ensures forward progress next4Bytes = test4Bytes(); } // Assert: next4Bytes starts with 0x00000001 or 0x000001, and we've saved all previous bytes (forming a complete NAL unit). // Skip over these remaining bytes, up until the start of the next NAL unit: if (next4Bytes == 0x00000001) { skipBytes(4); } else { skipBytes(3); } }
Boolean MatroskaFileParser::deliverFrameWithinBlock() { #ifdef DEBUG fprintf(stderr, "delivering frame within SimpleBlock or Block\n"); #endif do { MatroskaTrack* track = fOurFile.lookup(fBlockTrackNumber); if (track == NULL) break; // shouldn't happen MatroskaDemuxedTrack* demuxedTrack = fOurDemux->lookupDemuxedTrack(fBlockTrackNumber); if (demuxedTrack == NULL) break; // shouldn't happen if (!demuxedTrack->isCurrentlyAwaitingData()) { // Someone has been reading this stream, but isn't right now. // We can't deliver this frame until he asks for it, so punt for now. // The next time he asks for a frame, he'll get it. #ifdef DEBUG fprintf(stderr, "\tdeferring delivery of frame #%d (%d bytes)", fNextFrameNumberToDeliver, fFrameSizesWithinBlock[fNextFrameNumberToDeliver]); if (track->haveSubframes()) fprintf(stderr, "[offset %d]", fCurOffsetWithinFrame); fprintf(stderr, "\n"); #endif restoreSavedParserState(); // so we read from the beginning next time return False; } unsigned frameSize = fFrameSizesWithinBlock[fNextFrameNumberToDeliver]; if (track->haveSubframes()) { // The next "track->subframeSizeSize" bytes contain the length of a 'subframe': if (fCurOffsetWithinFrame + track->subframeSizeSize > frameSize) break; // sanity check unsigned subframeSize = 0; for (unsigned i = 0; i < track->subframeSizeSize; ++i) { u_int8_t c; getCommonFrameBytes(track, &c, 1, 0); if (fCurFrameNumBytesToGet > 0) { // it'll be 1 c = get1Byte(); ++fCurOffsetWithinFrame; } subframeSize = subframeSize*256 + c; } if (subframeSize == 0 || fCurOffsetWithinFrame + subframeSize > frameSize) break; // sanity check frameSize = subframeSize; } // Compute the presentation time of this frame (from the cluster timecode, the block timecode, and the default duration): double pt = (fClusterTimecode+fBlockTimecode)*(fOurFile.fTimecodeScale/1000000000.0) + fNextFrameNumberToDeliver*(track->defaultDuration/1000000000.0); if (fPresentationTimeOffset == 0.0) { // This is the first time we've computed a presentation time. Compute an offset to make the presentation times aligned // with 'wall clock' time: struct timeval timeNow; gettimeofday(&timeNow, NULL); double ptNow = timeNow.tv_sec + timeNow.tv_usec/1000000.0; fPresentationTimeOffset = ptNow - pt; } pt += fPresentationTimeOffset; struct timeval presentationTime; presentationTime.tv_sec = (unsigned)pt; presentationTime.tv_usec = (unsigned)((pt - presentationTime.tv_sec)*1000000); unsigned durationInMicroseconds = track->defaultDuration/1000; if (track->haveSubframes()) { // If this is a 'subframe', use a duration of 0 instead (unless it's the last 'subframe'): if (fCurOffsetWithinFrame + frameSize + track->subframeSizeSize < fFrameSizesWithinBlock[fNextFrameNumberToDeliver]) { // There's room for at least one more subframe after this, so give this subframe a duration of 0 durationInMicroseconds = 0; } } if (track->defaultDuration == 0) { // Adjust the frame duration to keep the sum of frame durations aligned with presentation times. if (track->prevPresentationTime.tv_sec != 0) { // not the first time for this track track->durationImbalance += (presentationTime.tv_sec - track->prevPresentationTime.tv_sec)*1000000 + (presentationTime.tv_usec - track->prevPresentationTime.tv_usec); } int adjustment = 0; if (track->durationImbalance > 0) { // The duration needs to be increased. int const adjustmentThreshold = 100000; // don't increase the duration by more than this amount (in case there's a mistake) adjustment = track->durationImbalance > adjustmentThreshold ? adjustmentThreshold : track->durationImbalance; } else if (track->durationImbalance < 0) { // The duration needs to be decreased. adjustment = (unsigned)(-track->durationImbalance) < durationInMicroseconds ? track->durationImbalance : -(int)durationInMicroseconds; } durationInMicroseconds += adjustment; track->durationImbalance -= durationInMicroseconds; // for next time track->prevPresentationTime = presentationTime; // for next time } demuxedTrack->presentationTime() = presentationTime; demuxedTrack->durationInMicroseconds() = durationInMicroseconds; // Deliver the next block now: if (frameSize > demuxedTrack->maxSize()) { demuxedTrack->numTruncatedBytes() = frameSize - demuxedTrack->maxSize(); demuxedTrack->frameSize() = demuxedTrack->maxSize(); } else { // normal case demuxedTrack->numTruncatedBytes() = 0; demuxedTrack->frameSize() = frameSize; } getCommonFrameBytes(track, demuxedTrack->to(), demuxedTrack->frameSize(), demuxedTrack->numTruncatedBytes()); // Next, deliver (and/or skip) bytes from the input file: fCurrentParseState = DELIVERING_FRAME_BYTES; setParseState(); return True; } while (0); // An error occurred. Try to recover: #ifdef DEBUG fprintf(stderr, "deliverFrameWithinBlock(): Error parsing data; trying to recover...\n"); #endif fCurrentParseState = LOOKING_FOR_BLOCK; return True; }
void MatroskaFileParser::parseBlock() { #ifdef DEBUG fprintf(stderr, "parsing SimpleBlock or Block\n"); #endif do { unsigned blockStartPos = curOffset(); // The block begins with the track number: EBMLNumber trackNumber; if (!parseEBMLNumber(trackNumber)) break; fBlockTrackNumber = (unsigned)trackNumber.val(); // If this track is not being read, then skip the rest of this block, and look for another one: if (fOurDemux->lookupDemuxedTrack(fBlockTrackNumber) == NULL) { unsigned headerBytesSeen = curOffset() - blockStartPos; if (headerBytesSeen < fBlockSize) { skipBytes(fBlockSize - headerBytesSeen); } #ifdef DEBUG fprintf(stderr, "\tSkipped block for unused track number %d\n", fBlockTrackNumber); #endif fCurrentParseState = LOOKING_FOR_BLOCK; setParseState(); return; } MatroskaTrack* track = fOurFile.lookup(fBlockTrackNumber); if (track == NULL) break; // shouldn't happen // The next two bytes are the block's timecode (relative to the cluster timecode) fBlockTimecode = (get1Byte()<<8)|get1Byte(); // The next byte indicates the type of 'lacing' used: u_int8_t c = get1Byte(); c &= 0x6; // we're interested in bits 5-6 only MatroskaLacingType lacingType = (c==0x0)?NoLacing : (c==0x02)?XiphLacing : (c==0x04)?FixedSizeLacing : EBMLLacing; #ifdef DEBUG fprintf(stderr, "\ttrack number %d, timecode %d (=> %f seconds), %s lacing\n", fBlockTrackNumber, fBlockTimecode, (fClusterTimecode+fBlockTimecode)*(fOurFile.fTimecodeScale/1000000000.0), (lacingType==NoLacing)?"no" : (lacingType==XiphLacing)?"Xiph" : (lacingType==FixedSizeLacing)?"fixed-size" : "EBML"); #endif if (lacingType == NoLacing) { fNumFramesInBlock = 1; } else { // The next byte tells us how many frames are present in this block fNumFramesInBlock = get1Byte() + 1; } delete[] fFrameSizesWithinBlock; fFrameSizesWithinBlock = new unsigned[fNumFramesInBlock]; if (fFrameSizesWithinBlock == NULL) break; if (lacingType == NoLacing) { unsigned headerBytesSeen = curOffset() - blockStartPos; if (headerBytesSeen > fBlockSize) break; fFrameSizesWithinBlock[0] = fBlockSize - headerBytesSeen; } else if (lacingType == FixedSizeLacing) { unsigned headerBytesSeen = curOffset() - blockStartPos; if (headerBytesSeen > fBlockSize) break; unsigned frameBytesAvailable = fBlockSize - headerBytesSeen; unsigned constantFrameSize = frameBytesAvailable/fNumFramesInBlock; for (unsigned i = 0; i < fNumFramesInBlock; ++i) { fFrameSizesWithinBlock[i] = constantFrameSize; } // If there are any bytes left over, assign them to the last frame: fFrameSizesWithinBlock[fNumFramesInBlock-1] += frameBytesAvailable%fNumFramesInBlock; } else { // EBML or Xiph lacing unsigned curFrameSize = 0; unsigned frameSizesTotal = 0; unsigned i; for (i = 0; i < fNumFramesInBlock-1; ++i) { if (lacingType == EBMLLacing) { EBMLNumber frameSize; if (!parseEBMLNumber(frameSize)) break; unsigned fsv = (unsigned)frameSize.val(); if (i == 0) { curFrameSize = fsv; } else { // The value we read is a signed value, that's added to the previous frame size, to get the current frame size: unsigned toSubtract = (fsv>0xFFFFFF)?0x07FFFFFF : (fsv>0xFFFF)?0x0FFFFF : (fsv>0xFF)?0x1FFF : 0x3F; int fsv_signed = fsv - toSubtract; curFrameSize += fsv_signed; if ((int)curFrameSize < 0) break; } } else { // Xiph lacing curFrameSize = 0; u_int8_t c; do { c = get1Byte(); curFrameSize += c; } while (c == 0xFF); } fFrameSizesWithinBlock[i] = curFrameSize; frameSizesTotal += curFrameSize; } if (i != fNumFramesInBlock-1) break; // an error occurred within the "for" loop // Compute the size of the final frame within the block (from the block's size, and the frame sizes already computed):) unsigned headerBytesSeen = curOffset() - blockStartPos; if (headerBytesSeen + frameSizesTotal > fBlockSize) break; fFrameSizesWithinBlock[i] = fBlockSize - (headerBytesSeen + frameSizesTotal); } // We're done parsing headers within the block, and (as a result) we now know the sizes of all frames within the block. // If we have 'stripped bytes' that are common to (the front of) all frames, then count them now: if (track->headerStrippedBytesSize != 0) { for (unsigned i = 0; i < fNumFramesInBlock; ++i) fFrameSizesWithinBlock[i] += track->headerStrippedBytesSize; } #ifdef DEBUG fprintf(stderr, "\tThis block contains %d frame(s); size(s):", fNumFramesInBlock); unsigned frameSizesTotal = 0; for (unsigned i = 0; i < fNumFramesInBlock; ++i) { fprintf(stderr, " %d", fFrameSizesWithinBlock[i]); frameSizesTotal += fFrameSizesWithinBlock[i]; } if (fNumFramesInBlock > 1) fprintf(stderr, " (total: %u)", frameSizesTotal); fprintf(stderr, " bytes\n"); #endif // Next, start delivering these frames: fCurrentParseState = DELIVERING_FRAME_WITHIN_BLOCK; fCurOffsetWithinFrame = fNextFrameNumberToDeliver = 0; setParseState(); return; } while (0); // An error occurred. Try to recover: #ifdef DEBUG fprintf(stderr, "parseBlock(): Error parsing data; trying to recover...\n"); #endif fCurrentParseState = LOOKING_FOR_BLOCK; }
Boolean MatroskaFileParser::parseCues() { #if defined(DEBUG) || defined(DEBUG_CUES) fprintf(stderr, "parsing Cues\n"); #endif EBMLId id; EBMLDataSize size; // Read the next header, which should be MATROSKA_ID_CUES: if (!parseEBMLIdAndSize(id, size) || id != MATROSKA_ID_CUES) return True; // The header wasn't what we expected, so we're done fLimitOffsetInFile = fCurOffsetInFile + size.val(); // Make sure we don't read past the end of this header double currentCueTime = 0.0; u_int64_t currentClusterOffsetInFile = 0; while (fCurOffsetInFile < fLimitOffsetInFile) { while (!parseEBMLIdAndSize(id, size)) {} #ifdef DEBUG_CUES if (id == MATROSKA_ID_CUE_POINT) fprintf(stderr, "\n"); // makes debugging output easier to read fprintf(stderr, "MatroskaFileParser::parseCues(): Parsed id 0x%s (%s), size: %lld\n", id.hexString(), id.stringName(), size.val()); #endif switch (id.val()) { case MATROSKA_ID_CUE_POINT: { // 'Cue Point' header: enter this break; } case MATROSKA_ID_CUE_TIME: { // 'Cue Time' header: get this value unsigned cueTime; if (parseEBMLVal_unsigned(size, cueTime)) { currentCueTime = cueTime*(fOurFile.fTimecodeScale/1000000000.0); #ifdef DEBUG_CUES fprintf(stderr, "\tCue Time %d (== %f seconds)\n", cueTime, currentCueTime); #endif } break; } case MATROSKA_ID_CUE_TRACK_POSITIONS: { // 'Cue Track Positions' header: enter this break; } case MATROSKA_ID_CUE_TRACK: { // 'Cue Track' header: get this value (but only for debugging; we don't do anything with it) unsigned cueTrack; if (parseEBMLVal_unsigned(size, cueTrack)) { #ifdef DEBUG_CUES fprintf(stderr, "\tCue Track %d\n", cueTrack); #endif } break; } case MATROSKA_ID_CUE_CLUSTER_POSITION: { // 'Cue Cluster Position' header: get this value u_int64_t cueClusterPosition; if (parseEBMLVal_unsigned64(size, cueClusterPosition)) { currentClusterOffsetInFile = fOurFile.fSegmentDataOffset + cueClusterPosition; #ifdef DEBUG_CUES fprintf(stderr, "\tCue Cluster Position %llu (=> offset within the file: %llu (0x%llx))\n", cueClusterPosition, currentClusterOffsetInFile, currentClusterOffsetInFile); #endif // Record this cue point: fOurFile.addCuePoint(currentCueTime, currentClusterOffsetInFile, 1/*default block number within cluster*/); } break; } case MATROSKA_ID_CUE_BLOCK_NUMBER: { // 'Cue Block Number' header: get this value unsigned cueBlockNumber; if (parseEBMLVal_unsigned(size, cueBlockNumber) && cueBlockNumber != 0) { #ifdef DEBUG_CUES fprintf(stderr, "\tCue Block Number %d\n", cueBlockNumber); #endif // Record this cue point (overwriting any existing entry for this cue time): fOurFile.addCuePoint(currentCueTime, currentClusterOffsetInFile, cueBlockNumber); } break; } default: { // We don't process this header, so just skip over it: skipHeader(size); #ifdef DEBUG_CUES fprintf(stderr, "\tskipped %lld bytes\n", size.val()); #endif break; } } setParseState(); } fLimitOffsetInFile = 0; // reset #if defined(DEBUG) || defined(DEBUG_CUES) fprintf(stderr, "done parsing Cues\n"); #endif #ifdef DEBUG_CUES fprintf(stderr, "Cue Point tree: "); fOurFile.printCuePoints(stderr); fprintf(stderr, "\n"); #endif return True; // we're done parsing Cues }
Boolean MatroskaFileParser::parseTrack() { #ifdef DEBUG fprintf(stderr, "parsing Track\n"); #endif // Read and process each Matroska header, until we get to the end of the Track: MatroskaTrack* track = NULL; EBMLId id; EBMLDataSize size; while (fCurOffsetInFile < fLimitOffsetInFile) { while (!parseEBMLIdAndSize(id, size)) {} #ifdef DEBUG if (id == MATROSKA_ID_TRACK_ENTRY) fprintf(stderr, "\n"); // makes debugging output easier to read fprintf(stderr, "MatroskaFileParser::parseTrack(): Parsed id 0x%s (%s), size: %lld\n", id.hexString(), id.stringName(), size.val()); #endif switch (id.val()) { case MATROSKA_ID_TRACK_ENTRY: { // 'Track Entry' header: enter this // Create a new "MatroskaTrack" object for this entry: if (track != NULL && track->trackNumber == 0) delete track; // We had a previous "MatroskaTrack" object that was never used track = new MatroskaTrack; break; } case MATROSKA_ID_TRACK_NUMBER: { unsigned trackNumber; if (parseEBMLVal_unsigned(size, trackNumber)) { #ifdef DEBUG fprintf(stderr, "\tTrack Number %d\n", trackNumber); #endif if (track != NULL && trackNumber != 0) { track->trackNumber = trackNumber; fOurFile.fTracks.add(track, trackNumber); } } break; } case MATROSKA_ID_TRACK_TYPE: { unsigned trackType; if (parseEBMLVal_unsigned(size, trackType) && track != NULL) { // We convert the Matroska 'track type' code into our own code (which we can use as a bitmap): track->trackType = trackType == 1 ? MATROSKA_TRACK_TYPE_VIDEO : trackType == 2 ? MATROSKA_TRACK_TYPE_AUDIO : trackType == 0x11 ? MATROSKA_TRACK_TYPE_SUBTITLE : MATROSKA_TRACK_TYPE_OTHER; #ifdef DEBUG fprintf(stderr, "\tTrack Type 0x%02x (%s)\n", trackType, track->trackType == MATROSKA_TRACK_TYPE_VIDEO ? "video" : track->trackType == MATROSKA_TRACK_TYPE_AUDIO ? "audio" : track->trackType == MATROSKA_TRACK_TYPE_SUBTITLE ? "subtitle" : "<other>"); #endif } break; } case MATROSKA_ID_FLAG_ENABLED: { unsigned flagEnabled; if (parseEBMLVal_unsigned(size, flagEnabled)) { #ifdef DEBUG fprintf(stderr, "\tTrack is Enabled: %d\n", flagEnabled); #endif if (track != NULL) track->isEnabled = flagEnabled != 0; } break; } case MATROSKA_ID_FLAG_DEFAULT: { unsigned flagDefault; if (parseEBMLVal_unsigned(size, flagDefault)) { #ifdef DEBUG fprintf(stderr, "\tTrack is Default: %d\n", flagDefault); #endif if (track != NULL) track->isDefault = flagDefault != 0; } break; } case MATROSKA_ID_FLAG_FORCED: { unsigned flagForced; if (parseEBMLVal_unsigned(size, flagForced)) { #ifdef DEBUG fprintf(stderr, "\tTrack is Forced: %d\n", flagForced); #endif if (track != NULL) track->isForced = flagForced != 0; } break; } case MATROSKA_ID_DEFAULT_DURATION: { unsigned defaultDuration; if (parseEBMLVal_unsigned(size, defaultDuration)) { #ifdef DEBUG fprintf(stderr, "\tDefault duration %f ms\n", defaultDuration/1000000.0); #endif if (track != NULL) track->defaultDuration = defaultDuration; } break; } case MATROSKA_ID_MAX_BLOCK_ADDITION_ID: { unsigned maxBlockAdditionID; if (parseEBMLVal_unsigned(size, maxBlockAdditionID)) { #ifdef DEBUG fprintf(stderr, "\tMax Block Addition ID: %u\n", maxBlockAdditionID); #endif } break; } case MATROSKA_ID_NAME: { char* name; if (parseEBMLVal_string(size, name)) { #ifdef DEBUG fprintf(stderr, "\tName: %s\n", name); #endif if (track != NULL) { delete[] track->name; track->name = name; } else { delete[] name; } } break; } case MATROSKA_ID_LANGUAGE: { char* language; if (parseEBMLVal_string(size, language)) { #ifdef DEBUG fprintf(stderr, "\tLanguage: %s\n", language); #endif if (track != NULL) { delete[] track->language; track->language = language; } else { delete[] language; } } break; } case MATROSKA_ID_CODEC: { char* codecID; if (parseEBMLVal_string(size, codecID)) { #ifdef DEBUG fprintf(stderr, "\tCodec ID: %s\n", codecID); #endif if (track != NULL) { delete[] track->codecID; track->codecID = codecID; } else { delete[] codecID; } } break; } case MATROSKA_ID_CODEC_PRIVATE: { u_int8_t* codecPrivate; unsigned codecPrivateSize; if (parseEBMLVal_binary(size, codecPrivate)) { codecPrivateSize = (unsigned)size.val(); #ifdef DEBUG fprintf(stderr, "\tCodec Private: "); for (unsigned i = 0; i < codecPrivateSize; ++i) fprintf(stderr, "%02x:", codecPrivate[i]); fprintf(stderr, "\n"); #endif if (track != NULL) { delete[] track->codecPrivate; track->codecPrivate = codecPrivate; track->codecPrivateSize = codecPrivateSize; } else { delete[] codecPrivate; } } break; } case MATROSKA_ID_VIDEO: { // 'Video settings' header: enter this break; } case MATROSKA_ID_PIXEL_WIDTH: { unsigned pixelWidth; if (parseEBMLVal_unsigned(size, pixelWidth)) { #ifdef DEBUG fprintf(stderr, "\tPixel Width %d\n", pixelWidth); #endif } break; } case MATROSKA_ID_PIXEL_HEIGHT: { unsigned pixelHeight; if (parseEBMLVal_unsigned(size, pixelHeight)) { #ifdef DEBUG fprintf(stderr, "\tPixel Height %d\n", pixelHeight); #endif } break; } case MATROSKA_ID_DISPLAY_WIDTH: { unsigned displayWidth; if (parseEBMLVal_unsigned(size, displayWidth)) { #ifdef DEBUG fprintf(stderr, "\tDisplay Width %d\n", displayWidth); #endif } break; } case MATROSKA_ID_DISPLAY_HEIGHT: { unsigned displayHeight; if (parseEBMLVal_unsigned(size, displayHeight)) { #ifdef DEBUG fprintf(stderr, "\tDisplay Height %d\n", displayHeight); #endif } break; } case MATROSKA_ID_AUDIO: { // 'Audio settings' header: enter this break; } case MATROSKA_ID_SAMPLING_FREQUENCY: { float samplingFrequency; if (parseEBMLVal_float(size, samplingFrequency)) { if (track != NULL) { track->samplingFrequency = (unsigned)samplingFrequency; #ifdef DEBUG fprintf(stderr, "\tSampling frequency %f (->%d)\n", samplingFrequency, track->samplingFrequency); #endif } } break; } case MATROSKA_ID_OUTPUT_SAMPLING_FREQUENCY: { float outputSamplingFrequency; if (parseEBMLVal_float(size, outputSamplingFrequency)) { #ifdef DEBUG fprintf(stderr, "\tOutput sampling frequency %f\n", outputSamplingFrequency); #endif } break; } case MATROSKA_ID_CHANNELS: { unsigned numChannels; if (parseEBMLVal_unsigned(size, numChannels)) { #ifdef DEBUG fprintf(stderr, "\tChannels %d\n", numChannels); #endif if (track != NULL) track->numChannels = numChannels; } break; } case MATROSKA_ID_CONTENT_ENCODINGS: case MATROSKA_ID_CONTENT_ENCODING: { // 'Content Encodings' or 'Content Encoding' header: enter this break; } case MATROSKA_ID_CONTENT_COMPRESSION: { // 'Content Compression' header: enter this // Note: We currently support only 'Header Stripping' compression, not 'zlib' compression (the default algorithm). // Therefore, we disable this track, unless/until we later see that 'Header Stripping' is supported: if (track != NULL) track->isEnabled = False; break; } case MATROSKA_ID_CONTENT_COMP_ALGO: { unsigned contentCompAlgo; if (parseEBMLVal_unsigned(size, contentCompAlgo)) { #ifdef DEBUG fprintf(stderr, "\tContent Compression Algorithm %d (%s)\n", contentCompAlgo, contentCompAlgo == 0 ? "zlib" : contentCompAlgo == 3 ? "Header Stripping" : "<unknown>"); #endif // The only compression algorithm that we support is #3: Header Stripping; disable the track otherwise if (track != NULL) track->isEnabled = contentCompAlgo == 3; } break; } case MATROSKA_ID_CONTENT_COMP_SETTINGS: { u_int8_t* headerStrippedBytes; unsigned headerStrippedBytesSize; if (parseEBMLVal_binary(size, headerStrippedBytes)) { headerStrippedBytesSize = (unsigned)size.val(); #ifdef DEBUG fprintf(stderr, "\tHeader Stripped Bytes: "); for (unsigned i = 0; i < headerStrippedBytesSize; ++i) fprintf(stderr, "%02x:", headerStrippedBytes[i]); fprintf(stderr, "\n"); #endif if (track != NULL) { delete[] track->headerStrippedBytes; track->headerStrippedBytes = headerStrippedBytes; track->headerStrippedBytesSize = headerStrippedBytesSize; } else { delete[] headerStrippedBytes; } } break; } case MATROSKA_ID_CONTENT_ENCRYPTION: { // 'Content Encrpytion' header: skip this // Note: We don't currently support encryption at all. Therefore, we disable this track: if (track != NULL) track->isEnabled = False; // Fall through to... } default: { // We don't process this header, so just skip over it: skipHeader(size); #ifdef DEBUG fprintf(stderr, "\tskipped %lld bytes\n", size.val()); #endif break; } } setParseState(); } fLimitOffsetInFile = 0; // reset if (track != NULL && track->trackNumber == 0) delete track; // We had a previous "MatroskaTrack" object that was never used return True; // we're done parsing track entries }
void MatroskaFileParser::lookForNextTrack() { #ifdef DEBUG fprintf(stderr, "looking for Track\n"); #endif EBMLId id; EBMLDataSize size; // Read and skip over (or enter) each Matroska header, until we get to a 'Track'. while (fCurrentParseState == LOOKING_FOR_TRACKS) { while (!parseEBMLIdAndSize(id, size)) {} #ifdef DEBUG fprintf(stderr, "MatroskaFileParser::lookForNextTrack(): Parsed id 0x%s (%s), size: %lld\n", id.hexString(), id.stringName(), size.val()); #endif switch (id.val()) { case MATROSKA_ID_SEGMENT: { // 'Segment' header: enter this // Remember the position, within the file, of the start of Segment data, because Seek Positions are relative to this: fOurFile.fSegmentDataOffset = fCurOffsetInFile; break; } case MATROSKA_ID_SEEK_HEAD: { // 'Seek Head' header: enter this break; } case MATROSKA_ID_SEEK: { // 'Seek' header: enter this break; } case MATROSKA_ID_SEEK_ID: { // 'Seek ID' header: get this value if (parseEBMLNumber(fLastSeekId)) { #ifdef DEBUG fprintf(stderr, "\tSeek ID 0x%s:\t%s\n", fLastSeekId.hexString(), fLastSeekId.stringName()); #endif } break; } case MATROSKA_ID_SEEK_POSITION: { // 'Seek Position' header: get this value u_int64_t seekPosition; if (parseEBMLVal_unsigned64(size, seekPosition)) { u_int64_t offsetInFile = fOurFile.fSegmentDataOffset + seekPosition; #ifdef DEBUG fprintf(stderr, "\tSeek Position %llu (=> offset within the file: %llu (0x%llx))\n", seekPosition, offsetInFile, offsetInFile); #endif // The only 'Seek Position's that we care about are for 'Cluster' and 'Cues': if (fLastSeekId == MATROSKA_ID_CLUSTER) { fOurFile.fClusterOffset = offsetInFile; } else if (fLastSeekId == MATROSKA_ID_CUES) { fOurFile.fCuesOffset = offsetInFile; } } break; } case MATROSKA_ID_INFO: { // 'Segment Info' header: enter this break; } case MATROSKA_ID_TIMECODE_SCALE: { // 'Timecode Scale' header: get this value unsigned timecodeScale; if (parseEBMLVal_unsigned(size, timecodeScale) && timecodeScale > 0) { fOurFile.fTimecodeScale = timecodeScale; #ifdef DEBUG fprintf(stderr, "\tTimecode Scale %u ns (=> Segment Duration == %f seconds)\n", fOurFile.timecodeScale(), fOurFile.fileDuration()); #endif } break; } case MATROSKA_ID_DURATION: { // 'Segment Duration' header: get this value if (parseEBMLVal_float(size, fOurFile.fSegmentDuration)) { #ifdef DEBUG fprintf(stderr, "\tSegment Duration %f (== %f seconds)\n", fOurFile.segmentDuration(), fOurFile.fileDuration()); #endif } break; } case MATROSKA_ID_TRACKS: { // enter this, and move on to parsing 'Tracks' fLimitOffsetInFile = fCurOffsetInFile + size.val(); // Make sure we don't read past the end of this header fCurrentParseState = PARSING_TRACK; break; } default: { // skip over this header skipHeader(size); #ifdef DEBUG fprintf(stderr, "\tskipped %lld bytes\n", size.val()); #endif break; } } setParseState(); } }
unsigned MPEG1or2VideoStreamParser::parseGOPHeader(Boolean haveSeenStartCode) { // First check whether we should insert a previously-saved // 'video_sequence_header' here: if (needToUseSavedVSH()) return useSavedVSH(); #ifdef DEBUG fprintf(stderr, "parsing GOP header\n"); #endif unsigned first4Bytes; if (!haveSeenStartCode) { while ((first4Bytes = test4Bytes()) != GROUP_START_CODE) { #ifdef DEBUG fprintf(stderr, "ignoring non GOP start code: 0x%08x\n", first4Bytes); #endif get1Byte(); setParseState(PARSING_GOP_HEADER); // ensures we progress over bad data } first4Bytes = get4Bytes(); } else { // We've already seen the GROUP_START_CODE first4Bytes = GROUP_START_CODE; } save4Bytes(first4Bytes); // Next, extract the (25-bit) time code from the next 4 bytes: unsigned next4Bytes = get4Bytes(); unsigned time_code = (next4Bytes & 0xFFFFFF80) >> (32 - 25); #if defined(DEBUG) || defined(DEBUG_TIMESTAMPS) Boolean drop_frame_flag = (time_code & 0x01000000) != 0; #endif unsigned time_code_hours = (time_code & 0x00F80000) >> 19; unsigned time_code_minutes = (time_code & 0x0007E000) >> 13; unsigned time_code_seconds = (time_code & 0x00000FC0) >> 6; unsigned time_code_pictures = (time_code & 0x0000003F); #if defined(DEBUG) || defined(DEBUG_TIMESTAMPS) fprintf(stderr, "time_code: 0x%07x, drop_frame %d, hours %d, minutes %d, seconds %d, pictures %d\n", time_code, drop_frame_flag, time_code_hours, time_code_minutes, time_code_seconds, time_code_pictures); #endif #ifdef DEBUG Boolean closed_gop = (next4Bytes & 0x00000040) != 0; Boolean broken_link = (next4Bytes & 0x00000020) != 0; fprintf(stderr, "closed_gop: %d, broken_link: %d\n", closed_gop, broken_link); #endif // Now, copy all bytes that we see, up until we reach a PICTURE_START_CODE: do { saveToNextCode(next4Bytes); } while (next4Bytes != PICTURE_START_CODE); // Record the time code: usingSource()->setTimeCode(time_code_hours, time_code_minutes, time_code_seconds, time_code_pictures, fPicturesSinceLastGOP); fPicturesSinceLastGOP = 0; // Compute this frame's timestamp: usingSource()->computePresentationTime(0); setParseState(PARSING_PICTURE_HEADER); return curFrameSize(); }
unsigned H264VideoStreamParser::parse() { try { // The stream must start with a 0x00000001: if (!fHaveSeenFirstStartCode) { // Skip over any input bytes that precede the first 0x00000001: u_int32_t first4Bytes; while ((first4Bytes = test4Bytes()) != 0x00000001) { get1Byte(); setParseState(); // ensures that we progress over bad data } skipBytes(4); // skip this initial code setParseState(); fHaveSeenFirstStartCode = True; // from now on } if (fOutputStartCodeSize > 0) { // Include a start code in the output: save4Bytes(0x00000001); } // Then save everything up until the next 0x00000001 (4 bytes) or 0x000001 (3 bytes), or we hit EOF. // Also make note of the first byte, because it contains the "nal_unit_type": if (haveSeenEOF()) { // We hit EOF the last time that we tried to parse this data, so we know that any remaining unparsed data // forms a complete NAL unit, and that there's no 'start code' at the end: unsigned remainingDataSize = totNumValidBytes() - curOffset(); while (remainingDataSize > 0) { saveByte(get1Byte()); --remainingDataSize; } if (!fHaveSeenFirstByteOfNALUnit) { // There's no remaining NAL unit. (void)get1Byte(); // forces another read, which will cause EOF to get handled for real this time return 0; } #ifdef DEBUG fprintf(stderr, "This NAL unit (%d bytes) ends with EOF\n", curFrameSize()-fOutputStartCodeSize); #endif } else { u_int32_t next4Bytes = test4Bytes(); if (!fHaveSeenFirstByteOfNALUnit) { fFirstByteOfNALUnit = next4Bytes>>24; fHaveSeenFirstByteOfNALUnit = True; } while (next4Bytes != 0x00000001 && (next4Bytes&0xFFFFFF00) != 0x00000100) { // We save at least some of "next4Bytes". if ((unsigned)(next4Bytes&0xFF) > 1) { // Common case: 0x00000001 or 0x000001 definitely doesn't begin anywhere in "next4Bytes", so we save all of it: save4Bytes(next4Bytes); skipBytes(4); } else { // Save the first byte, and continue testing the rest: saveByte(next4Bytes>>24); skipBytes(1); } setParseState(); // ensures forward progress next4Bytes = test4Bytes(); } // Assert: next4Bytes starts with 0x00000001 or 0x000001, and we've saved all previous bytes (forming a complete NAL unit). // Skip over these remaining bytes, up until the start of the next NAL unit: if (next4Bytes == 0x00000001) { skipBytes(4); } else { skipBytes(3); } } u_int8_t nal_ref_idc = (fFirstByteOfNALUnit&0x60)>>5; u_int8_t nal_unit_type = fFirstByteOfNALUnit&0x1F; fHaveSeenFirstByteOfNALUnit = False; // for the next NAL unit that we parse #ifdef DEBUG fprintf(stderr, "Parsed %d-byte NAL-unit (nal_ref_idc: %d, nal_unit_type: %d (\"%s\"))\n", curFrameSize()-fOutputStartCodeSize, nal_ref_idc, nal_unit_type, nal_unit_type_description[nal_unit_type]); #endif switch (nal_unit_type) { case 6: { // Supplemental enhancement information (SEI) analyze_sei_data(); // Later, perhaps adjust "fPresentationTime" if we saw a "pic_timing" SEI payload??? ##### break; } case 7: { // Sequence parameter set // First, save a copy of this NAL unit, in case the downstream object wants to see it: usingSource()->saveCopyOfSPS(fStartOfFrame + fOutputStartCodeSize, fTo - fStartOfFrame - fOutputStartCodeSize); // Parse this NAL unit to check whether frame rate information is present: unsigned num_units_in_tick, time_scale, fixed_frame_rate_flag; analyze_seq_parameter_set_data(num_units_in_tick, time_scale, fixed_frame_rate_flag); if (time_scale > 0 && num_units_in_tick > 0) { usingSource()->fFrameRate = time_scale/(2.0*num_units_in_tick); #ifdef DEBUG fprintf(stderr, "Set frame rate to %f fps\n", usingSource()->fFrameRate); if (fixed_frame_rate_flag == 0) { fprintf(stderr, "\tWARNING: \"fixed_frame_rate_flag\" was not set\n"); } #endif } else { #ifdef DEBUG fprintf(stderr, "\tThis \"Sequence Parameter Set\" NAL unit contained no frame rate information, so we use a default frame rate of %f fps\n", usingSource()->fFrameRate); #endif } break; } case 8: { // Picture parameter set // Save a copy of this NAL unit, in case the downstream object wants to see it: usingSource()->saveCopyOfPPS(fStartOfFrame + fOutputStartCodeSize, fTo - fStartOfFrame - fOutputStartCodeSize); } } usingSource()->setPresentationTime(); #ifdef DEBUG unsigned long secs = (unsigned long)usingSource()->fPresentationTime.tv_sec; unsigned uSecs = (unsigned)usingSource()->fPresentationTime.tv_usec; fprintf(stderr, "\tPresentation time: %lu.%06u\n", secs, uSecs); #endif // If this NAL unit is a VCL NAL unit, we also scan the start of the next NAL unit, to determine whether this NAL unit // ends the current 'access unit'. We need this information to figure out when to increment "fPresentationTime". // (RTP streamers also need to know this in order to figure out whether or not to set the "M" bit.) Boolean thisNALUnitEndsAccessUnit = False; // until we learn otherwise if (haveSeenEOF()) { // There is no next NAL unit, so we assume that this one ends the current 'access unit': thisNALUnitEndsAccessUnit = True; } else { Boolean const isVCL = nal_unit_type <= 5 && nal_unit_type > 0; // Would need to include type 20 for SVC and MVC ##### if (isVCL) { u_int32_t first4BytesOfNextNALUnit = test4Bytes(); u_int8_t firstByteOfNextNALUnit = first4BytesOfNextNALUnit>>24; u_int8_t next_nal_ref_idc = (firstByteOfNextNALUnit&0x60)>>5; u_int8_t next_nal_unit_type = firstByteOfNextNALUnit&0x1F; if (next_nal_unit_type >= 6) { // The next NAL unit is not a VCL; therefore, we assume that this NAL unit ends the current 'access unit': #ifdef DEBUG fprintf(stderr, "\t(The next NAL unit is not a VCL)\n"); #endif thisNALUnitEndsAccessUnit = True; } else { // The next NAL unit is also a VCL. We need to examine it a little to figure out if it's a different 'access unit'. // (We use many of the criteria described in section 7.4.1.2.4 of the H.264 specification.) Boolean IdrPicFlag = nal_unit_type == 5; Boolean next_IdrPicFlag = next_nal_unit_type == 5; if (next_IdrPicFlag != IdrPicFlag) { // IdrPicFlag differs in value #ifdef DEBUG fprintf(stderr, "\t(IdrPicFlag differs in value)\n"); #endif thisNALUnitEndsAccessUnit = True; } else if (next_nal_ref_idc != nal_ref_idc && next_nal_ref_idc*nal_ref_idc == 0) { // nal_ref_idc differs in value with one of the nal_ref_idc values being equal to 0 #ifdef DEBUG fprintf(stderr, "\t(nal_ref_idc differs in value with one of the nal_ref_idc values being equal to 0)\n"); #endif thisNALUnitEndsAccessUnit = True; } else if ((nal_unit_type == 1 || nal_unit_type == 2 || nal_unit_type == 5) && (next_nal_unit_type == 1 || next_nal_unit_type == 2 || next_nal_unit_type == 5)) { // Both this and the next NAL units begin with a "slice_header". // Parse this (for each), to get parameters that we can compare: // Current NAL unit's "slice_header": unsigned frame_num, pic_parameter_set_id, idr_pic_id; Boolean field_pic_flag, bottom_field_flag; analyze_slice_header(fStartOfFrame + fOutputStartCodeSize, fTo, nal_unit_type, frame_num, pic_parameter_set_id, idr_pic_id, field_pic_flag, bottom_field_flag); // Next NAL unit's "slice_header": #ifdef DEBUG fprintf(stderr, " Next NAL unit's slice_header:\n"); #endif u_int8_t next_slice_header[NUM_NEXT_SLICE_HEADER_BYTES_TO_ANALYZE]; testBytes(next_slice_header, sizeof next_slice_header); unsigned next_frame_num, next_pic_parameter_set_id, next_idr_pic_id; Boolean next_field_pic_flag, next_bottom_field_flag; analyze_slice_header(next_slice_header, &next_slice_header[sizeof next_slice_header], next_nal_unit_type, next_frame_num, next_pic_parameter_set_id, next_idr_pic_id, next_field_pic_flag, next_bottom_field_flag); if (next_frame_num != frame_num) { // frame_num differs in value #ifdef DEBUG fprintf(stderr, "\t(frame_num differs in value)\n"); #endif thisNALUnitEndsAccessUnit = True; } else if (next_pic_parameter_set_id != pic_parameter_set_id) { // pic_parameter_set_id differs in value #ifdef DEBUG fprintf(stderr, "\t(pic_parameter_set_id differs in value)\n"); #endif thisNALUnitEndsAccessUnit = True; } else if (next_field_pic_flag != field_pic_flag) { // field_pic_flag differs in value #ifdef DEBUG fprintf(stderr, "\t(field_pic_flag differs in value)\n"); #endif thisNALUnitEndsAccessUnit = True; } else if (next_bottom_field_flag != bottom_field_flag) { // bottom_field_flag differs in value #ifdef DEBUG fprintf(stderr, "\t(bottom_field_flag differs in value)\n"); #endif thisNALUnitEndsAccessUnit = True; } else if (next_IdrPicFlag == 1 && next_idr_pic_id != idr_pic_id) { // IdrPicFlag is equal to 1 for both and idr_pic_id differs in value // Note: We already know that IdrPicFlag is the same for both. #ifdef DEBUG fprintf(stderr, "\t(IdrPicFlag is equal to 1 for both and idr_pic_id differs in value)\n"); #endif thisNALUnitEndsAccessUnit = True; } } } } } if (thisNALUnitEndsAccessUnit) { #ifdef DEBUG fprintf(stderr, "*****This NAL unit ends the current access unit*****\n"); #endif usingSource()->fPictureEndMarker = True; ++usingSource()->fPictureCount; // Note that the presentation time for the next NAL unit will be different: struct timeval& nextPT = usingSource()->fNextPresentationTime; // alias nextPT = usingSource()->fPresentationTime; double nextFraction = nextPT.tv_usec/1000000.0 + 1/usingSource()->fFrameRate; unsigned nextSecsIncrement = (long)nextFraction; nextPT.tv_sec += (long)nextSecsIncrement; nextPT.tv_usec = (long)((nextFraction - nextSecsIncrement)*1000000); } setParseState(); return curFrameSize(); } catch (int /*e*/) {
unsigned MPEG1or2VideoStreamParser::parseSlice() { // Note that we've already read the slice_start_code: unsigned next4Bytes = PICTURE_START_CODE | fCurrentSliceNumber; #ifdef DEBUG_SLICE fprintf(stderr, "parsing slice: 0x%08x\n", next4Bytes); #endif if (fSkippingCurrentPicture) { // Skip all bytes that we see, up until we reach a code of some sort: skipToNextCode(next4Bytes); } else { // Copy all bytes that we see, up until we reach a code of some sort: saveToNextCode(next4Bytes); } // The next thing to parse depends on the code that we just saw: if (isSliceStartCode(next4Bytes)) // common case { setParseState(PARSING_SLICE); fCurrentSliceNumber = next4Bytes & 0xFF; } else { // Because we don't see any more slices, we are assumed to have ended // the current picture: ++fPicturesSinceLastGOP; ++usingSource()->fPictureCount; usingSource()->fPictureEndMarker = True; // HACK ##### switch (next4Bytes) { case SEQUENCE_END_CODE: { setParseState(PARSING_VIDEO_SEQUENCE_HEADER); break; } case VIDEO_SEQUENCE_HEADER_START_CODE: { setParseState(PARSING_VIDEO_SEQUENCE_HEADER_SEEN_CODE); break; } case GROUP_START_CODE: { setParseState(PARSING_GOP_HEADER_SEEN_CODE); break; } case PICTURE_START_CODE: { setParseState(PARSING_PICTURE_HEADER); break; } default: { usingSource()->envir() << "MPEG1or2VideoStreamParser::parseSlice(): Saw unexpected code " << (void *)next4Bytes << "\n"; setParseState(PARSING_SLICE); // the safest way to recover... break; } } } // Compute this frame's timestamp: usingSource()->computePresentationTime(fCurPicTemporalReference); if (fSkippingCurrentPicture) { return parse(); // try again, until we get a non-skipped frame } else { return curFrameSize(); } }