/********** This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. (See .) This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA **********/ // "liveMedia" // Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved. // A simplified version of "MPEG4VideoStreamFramer" that takes only complete, // discrete frames (rather than an arbitrary byte stream) as input. // This avoids the parsing and data copying overhead of the full // "MPEG4VideoStreamFramer". // Implementation #include "MPEG4VideoStreamDiscreteFramer.hh" MPEG4VideoStreamDiscreteFramer* MPEG4VideoStreamDiscreteFramer::createNew(UsageEnvironment& env, FramedSource* inputSource, Boolean leavePresentationTimesUnmodified) { // Need to add source type checking here??? ##### return new MPEG4VideoStreamDiscreteFramer(env, inputSource, leavePresentationTimesUnmodified); } MPEG4VideoStreamDiscreteFramer ::MPEG4VideoStreamDiscreteFramer(UsageEnvironment& env, FramedSource* inputSource, Boolean leavePresentationTimesUnmodified) : MPEG4VideoStreamFramer(env, inputSource, False/*don't create a parser*/), fLeavePresentationTimesUnmodified(leavePresentationTimesUnmodified), vop_time_increment_resolution(0), fNumVTIRBits(0), fLastNonBFrameVop_time_increment(0) { fLastNonBFramePresentationTime.tv_sec = 0; fLastNonBFramePresentationTime.tv_usec = 0; } MPEG4VideoStreamDiscreteFramer::~MPEG4VideoStreamDiscreteFramer() { } void MPEG4VideoStreamDiscreteFramer::doGetNextFrame() { // Arrange to read data (which should be a complete MPEG-4 video frame) // from our data source, directly into the client's input buffer. // After reading this, we'll do some parsing on the frame. fInputSource->getNextFrame(fTo, fMaxSize, afterGettingFrame, this, FramedSource::handleClosure, this); } void MPEG4VideoStreamDiscreteFramer ::afterGettingFrame(void* clientData, unsigned frameSize, unsigned numTruncatedBytes, struct timeval presentationTime, unsigned durationInMicroseconds) { MPEG4VideoStreamDiscreteFramer* source = (MPEG4VideoStreamDiscreteFramer*)clientData; source->afterGettingFrame1(frameSize, numTruncatedBytes, presentationTime, durationInMicroseconds); } void MPEG4VideoStreamDiscreteFramer ::afterGettingFrame1(unsigned frameSize, unsigned numTruncatedBytes, struct timeval presentationTime, unsigned durationInMicroseconds) { // Check that the first 4 bytes are a system code: if (frameSize >= 4 && fTo[0] == 0 && fTo[1] == 0 && fTo[2] == 1) { fPictureEndMarker = True; // Assume that we have a complete 'picture' here unsigned i = 3; if (fTo[i] == 0xB0) { // VISUAL_OBJECT_SEQUENCE_START_CODE // The next byte is the "profile_and_level_indication": if (frameSize >= 5) fProfileAndLevelIndication = fTo[4]; // The start of this frame - up to the first GROUP_VOP_START_CODE // or VOP_START_CODE - is stream configuration information. Save this: for (i = 7; i < frameSize; ++i) { if ((fTo[i] == 0xB3 /*GROUP_VOP_START_CODE*/ || fTo[i] == 0xB6 /*VOP_START_CODE*/) && fTo[i-1] == 1 && fTo[i-2] == 0 && fTo[i-3] == 0) { break; // The configuration information ends here } } fNumConfigBytes = i < frameSize ? i-3 : frameSize; delete[] fConfigBytes; fConfigBytes = new unsigned char[fNumConfigBytes]; for (unsigned j = 0; j < fNumConfigBytes; ++j) fConfigBytes[j] = fTo[j]; // This information (should) also contain a VOL header, which we need // to analyze, to get "vop_time_increment_resolution" (which we need // - along with "vop_time_increment" - in order to generate accurate // presentation times for "B" frames). analyzeVOLHeader(); } if (i < frameSize) { u_int8_t nextCode = fTo[i]; if (nextCode == 0xB3 /*GROUP_VOP_START_CODE*/) { // Skip to the following VOP_START_CODE (if any): for (i += 4; i < frameSize; ++i) { if (fTo[i] == 0xB6 /*VOP_START_CODE*/ && fTo[i-1] == 1 && fTo[i-2] == 0 && fTo[i-3] == 0) { nextCode = fTo[i]; break; } } } if (nextCode == 0xB6 /*VOP_START_CODE*/ && i+5 < frameSize) { ++i; // Get the "vop_coding_type" from the next byte: u_int8_t nextByte = fTo[i++]; u_int8_t vop_coding_type = nextByte>>6; // Next, get the "modulo_time_base" by counting the '1' bits that // follow. We look at the next 32-bits only. // This should be enough in most cases. u_int32_t next4Bytes = (fTo[i]<<24)|(fTo[i+1]<<16)|(fTo[i+2]<<8)|fTo[i+3]; i += 4; u_int32_t timeInfo = (nextByte<<(32-6))|(next4Bytes>>6); unsigned modulo_time_base = 0; u_int32_t mask = 0x80000000; while ((timeInfo&mask) != 0) { ++modulo_time_base; mask >>= 1; } mask >>= 2; // Then, get the "vop_time_increment". unsigned vop_time_increment = 0; // First, make sure we have enough bits left for this: if ((mask>>(fNumVTIRBits-1)) != 0) { for (unsigned i = 0; i < fNumVTIRBits; ++i) { vop_time_increment |= timeInfo&mask; mask >>= 1; } while (mask != 0) { vop_time_increment >>= 1; mask >>= 1; } } // If this is a "B" frame, then we have to tweak "presentationTime": if (!fLeavePresentationTimesUnmodified && vop_coding_type == 2/*B*/ && (fLastNonBFramePresentationTime.tv_usec > 0 || fLastNonBFramePresentationTime.tv_sec > 0)) { int timeIncrement = fLastNonBFrameVop_time_increment - vop_time_increment; if (timeIncrement<0) timeIncrement += vop_time_increment_resolution; unsigned const MILLION = 1000000; double usIncrement = vop_time_increment_resolution == 0 ? 0.0 : ((double)timeIncrement*MILLION)/vop_time_increment_resolution; unsigned secondsToSubtract = (unsigned)(usIncrement/MILLION); unsigned uSecondsToSubtract = ((unsigned)usIncrement)%MILLION; presentationTime = fLastNonBFramePresentationTime; if ((unsigned)presentationTime.tv_usec < uSecondsToSubtract) { presentationTime.tv_usec += MILLION; if (presentationTime.tv_sec > 0) --presentationTime.tv_sec; } presentationTime.tv_usec -= uSecondsToSubtract; if ((unsigned)presentationTime.tv_sec > secondsToSubtract) { presentationTime.tv_sec -= secondsToSubtract; } else { presentationTime.tv_sec = presentationTime.tv_usec = 0; } } else { fLastNonBFramePresentationTime = presentationTime; fLastNonBFrameVop_time_increment = vop_time_increment; } } } } // Complete delivery to the client: fFrameSize = frameSize; fNumTruncatedBytes = numTruncatedBytes; fPresentationTime = presentationTime; fDurationInMicroseconds = durationInMicroseconds; afterGetting(this); } Boolean MPEG4VideoStreamDiscreteFramer::getNextFrameBit(u_int8_t& result) { if (fNumBitsSeenSoFar/8 >= fNumConfigBytes) return False; u_int8_t nextByte = fConfigBytes[fNumBitsSeenSoFar/8]; result = (nextByte>>(7-fNumBitsSeenSoFar%8))&1; ++fNumBitsSeenSoFar; return True; } Boolean MPEG4VideoStreamDiscreteFramer::getNextFrameBits(unsigned numBits, u_int32_t& result) { result = 0; for (unsigned i = 0; i < numBits; ++i) { u_int8_t nextBit; if (!getNextFrameBit(nextBit)) return False; result = (result<<1)|nextBit; } return True; } void MPEG4VideoStreamDiscreteFramer::analyzeVOLHeader() { // Begin by moving to the VOL header: unsigned i; for (i = 3; i < fNumConfigBytes; ++i) { if (fConfigBytes[i] >= 0x20 && fConfigBytes[i] <= 0x2F && fConfigBytes[i-1] == 1 && fConfigBytes[i-2] == 0 && fConfigBytes[i-3] == 0) { ++i; break; } } fNumBitsSeenSoFar = 8*i + 9; do { u_int8_t is_object_layer_identifier; if (!getNextFrameBit(is_object_layer_identifier)) break; if (is_object_layer_identifier) fNumBitsSeenSoFar += 7; u_int32_t aspect_ratio_info; if (!getNextFrameBits(4, aspect_ratio_info)) break; if (aspect_ratio_info == 15 /*extended_PAR*/) fNumBitsSeenSoFar += 16; u_int8_t vol_control_parameters; if (!getNextFrameBit(vol_control_parameters)) break; if (vol_control_parameters) { fNumBitsSeenSoFar += 3; // chroma_format; low_delay u_int8_t vbw_parameters; if (!getNextFrameBit(vbw_parameters)) break; if (vbw_parameters) fNumBitsSeenSoFar += 79; } fNumBitsSeenSoFar += 2; // video_object_layer_shape u_int8_t marker_bit; if (!getNextFrameBit(marker_bit)) break; if (marker_bit != 1) break; // sanity check if (!getNextFrameBits(16, vop_time_increment_resolution)) break; if (vop_time_increment_resolution == 0) break; // shouldn't happen // Compute how many bits are necessary to represent this: fNumVTIRBits = 0; for (unsigned test = vop_time_increment_resolution; test>0; test /= 2) { ++fNumVTIRBits; } } while (0); }