📄 mpeg4videostreamframer.cpp
字号:
Boolean MPEG4VideoStreamParser::getNextFrameBit(u_int8_t& result) { if (fNumBitsSeenSoFar/8 >= curFrameSize()) return False; u_int8_t nextByte = fStartOfFrame[fNumBitsSeenSoFar/8]; result = (nextByte>>(7-fNumBitsSeenSoFar%8))&1; ++fNumBitsSeenSoFar; return True;}Boolean MPEG4VideoStreamParser::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 MPEG4VideoStreamParser::analyzeVOLHeader() { // Extract timing information (in particular, // "vop_time_increment_resolution") from the VOL Header: fNumBitsSeenSoFar = 41; 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) { // sanity check usingSource()->envir() << "MPEG4VideoStreamParser::analyzeVOLHeader(): marker_bit 1 not set!\n"; break; } if (!getNextFrameBits(16, vop_time_increment_resolution)) break;#ifdef DEBUG fprintf(stderr, "vop_time_increment_resolution: %d\n", vop_time_increment_resolution);#endif if (vop_time_increment_resolution == 0) { usingSource()->envir() << "MPEG4VideoStreamParser::analyzeVOLHeader(): vop_time_increment_resolution is zero!\n"; break; } // Compute how many bits are necessary to represent this: fNumVTIRBits = 0; for (unsigned test = vop_time_increment_resolution; test>0; test /= 2) { ++fNumVTIRBits; } if (!getNextFrameBit(marker_bit)) break; if (marker_bit != 1) { // sanity check usingSource()->envir() << "MPEG4VideoStreamParser::analyzeVOLHeader(): marker_bit 2 not set!\n"; break; } if (!getNextFrameBit(fixed_vop_rate)) break; if (fixed_vop_rate) { // Get the following "fixed_vop_time_increment": if (!getNextFrameBits(fNumVTIRBits, fixed_vop_time_increment)) break;#ifdef DEBUG fprintf(stderr, "fixed_vop_time_increment: %d\n", fixed_vop_time_increment); if (fixed_vop_time_increment == 0) { usingSource()->envir() << "MPEG4VideoStreamParser::analyzeVOLHeader(): fixed_vop_time_increment is zero!\n"; }#endif } // Use "vop_time_increment_resolution" as the 'frame rate' // (really, 'tick rate'): usingSource()->fFrameRate = (double)vop_time_increment_resolution;#ifdef DEBUG fprintf(stderr, "fixed_vop_rate: %d; 'frame' (really tick) rate: %f\n", fixed_vop_rate, usingSource()->fFrameRate);#endif return; } while (0); if (fNumBitsSeenSoFar/8 >= curFrameSize()) { char errMsg[200]; sprintf(errMsg, "Not enough bits in VOL header: %d/8 >= %d\n", fNumBitsSeenSoFar, curFrameSize()); usingSource()->envir() << errMsg; }}unsigned MPEG4VideoStreamParser::parseVideoObjectLayer() {#ifdef DEBUG fprintf(stderr, "parsing VideoObjectLayer\n");#endif // The first 4 bytes must be a "video_object_layer_start_code". // If not, this is a 'short video header', which we currently // don't support: u_int32_t next4Bytes = get4Bytes(); if (!isVideoObjectLayerStartCode(next4Bytes)) { usingSource()->envir() << "MPEG4VideoStreamParser::parseVideoObjectLayer(): This appears to be a 'short video header', which we current don't support\n"; } // Now, copy all bytes that we see, up until we reach // a GROUP_VOP_START_CODE or a VOP_START_CODE: do { saveToNextCode(next4Bytes); } while (next4Bytes != GROUP_VOP_START_CODE && next4Bytes != VOP_START_CODE); analyzeVOLHeader(); setParseState((next4Bytes == GROUP_VOP_START_CODE) ? PARSING_GROUP_OF_VIDEO_OBJECT_PLANE : PARSING_VIDEO_OBJECT_PLANE); // Compute this frame's presentation time: usingSource()->computePresentationTime(fTotalTicksSinceLastTimeCode); // This header ends the 'configuration' information: usingSource()->appendToNewConfig(fStartOfFrame, curFrameSize()); usingSource()->completeNewConfig(); return curFrameSize();}unsigned MPEG4VideoStreamParser::parseGroupOfVideoObjectPlane() {#ifdef DEBUG fprintf(stderr, "parsing GroupOfVideoObjectPlane\n");#endif // Note that we've already read the GROUP_VOP_START_CODE save4Bytes(GROUP_VOP_START_CODE); // Next, extract the (18-bit) time code from the next 3 bytes: u_int8_t next3Bytes[3]; getBytes(next3Bytes, 3); saveByte(next3Bytes[0]);saveByte(next3Bytes[1]);saveByte(next3Bytes[2]); unsigned time_code = (next3Bytes[0]<<10)|(next3Bytes[1]<<2)|(next3Bytes[2]>>6); unsigned time_code_hours = (time_code&0x0003E000)>>13; unsigned time_code_minutes = (time_code&0x00001F80)>>7;#if defined(DEBUG) || defined(DEBUG_TIMESTAMPS) Boolean marker_bit = (time_code&0x00000040) != 0;#endif unsigned time_code_seconds = (time_code&0x0000003F);#if defined(DEBUG) || defined(DEBUG_TIMESTAMPS) fprintf(stderr, "time_code: 0x%05x, hours %d, minutes %d, marker_bit %d, seconds %d\n", time_code, time_code_hours, time_code_minutes, marker_bit, time_code_seconds);#endif // Now, copy all bytes that we see, up until we reach a VOP_START_CODE: u_int32_t next4Bytes = get4Bytes(); while (next4Bytes != VOP_START_CODE) { saveToNextCode(next4Bytes); } // Compute this frame's presentation time: usingSource()->computePresentationTime(fTotalTicksSinceLastTimeCode); // Record the time code: usingSource()->setTimeCode(time_code_hours, time_code_minutes, time_code_seconds, 0, 0); // Note: Because the GOV header can appear anywhere (not just at a 1s point), we // don't pass "fTotalTicksSinceLastTimeCode" as the "picturesSinceLastGOP" parameter. fSecondsSinceLastTimeCode = 0; if (fixed_vop_rate) fTotalTicksSinceLastTimeCode = 0; setParseState(PARSING_VIDEO_OBJECT_PLANE); return curFrameSize();}unsigned MPEG4VideoStreamParser::parseVideoObjectPlane() {#ifdef DEBUG fprintf(stderr, "#parsing VideoObjectPlane\n");#endif // Note that we've already read the VOP_START_CODE save4Bytes(VOP_START_CODE); // Get the "vop_coding_type" from the next byte: u_int8_t nextByte = get1Byte(); saveByte(nextByte); 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 = get4Bytes(); 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 >>= 1; // Check the following marker bit: if ((timeInfo&mask) == 0) { usingSource()->envir() << "MPEG4VideoStreamParser::parseVideoObjectPlane(): marker bit not set!\n"; } mask >>= 1; // Then, get the "vop_time_increment". // First, make sure we have enough bits left for this: if ((mask>>(fNumVTIRBits-1)) == 0) { usingSource()->envir() << "MPEG4VideoStreamParser::parseVideoObjectPlane(): 32-bits are not enough to get \"vop_time_increment\"!\n"; } unsigned vop_time_increment = 0; for (unsigned i = 0; i < fNumVTIRBits; ++i) { vop_time_increment |= timeInfo&mask; mask >>= 1; } while (mask != 0) { vop_time_increment >>= 1; mask >>= 1; }#ifdef DEBUG fprintf(stderr, "vop_coding_type: %d(%c), modulo_time_base: %d, vop_time_increment: %d\n", vop_coding_type, "IPBS"[vop_coding_type], modulo_time_base, vop_time_increment);#endif // Now, copy all bytes that we see, up until we reach a code of some sort: saveToNextCode(next4Bytes); // Update our counters based on the frame timing information that we saw: if (fixed_vop_time_increment > 0) { // This is a 'fixed_vop_rate' stream. Use 'fixed_vop_time_increment': usingSource()->fPictureCount += fixed_vop_time_increment; if (vop_time_increment > 0 || modulo_time_base > 0) { fTotalTicksSinceLastTimeCode += fixed_vop_time_increment; // Note: "fSecondsSinceLastTimeCode" and "fPrevNewTotalTicks" are not used. } } else { // Use 'vop_time_increment': unsigned newTotalTicks = (fSecondsSinceLastTimeCode + modulo_time_base)*vop_time_increment_resolution + vop_time_increment; if (newTotalTicks == fPrevNewTotalTicks && fPrevNewTotalTicks > 0) { // This is apparently a buggy MPEG-4 video stream, because // "vop_time_increment" did not change. Overcome this error, // by pretending that it did change.#ifdef DEBUG fprintf(stderr, "Buggy MPEG-4 video stream: \"vop_time_increment\" did not change!\n");#endif // The following assumes that we don't have 'B' frames. If we do, then TARFU! usingSource()->fPictureCount += vop_time_increment; fTotalTicksSinceLastTimeCode += vop_time_increment; fSecondsSinceLastTimeCode += modulo_time_base; } else { if (newTotalTicks < fPrevNewTotalTicks && vop_coding_type != 2/*B*/ && modulo_time_base == 0 && vop_time_increment == 0) { // This is another kind of buggy MPEG-4 video stream, in which // "vop_time_increment" wraps around, but without // "modulo_time_base" changing. Overcome this by pretending that // "vop_time_increment" really did wrap around:#ifdef DEBUG fprintf(stderr, "Buggy MPEG-4 video stream: \"vop_time_increment\" wrapped around, but without \"modulo_time_base\" changing!\n");#endif ++fSecondsSinceLastTimeCode; newTotalTicks += vop_time_increment_resolution; } fPrevNewTotalTicks = newTotalTicks; if (vop_coding_type != 2/*B*/) { int pictureCountDelta = newTotalTicks - fTotalTicksSinceLastTimeCode; if (pictureCountDelta <= 0) pictureCountDelta = fPrevPictureCountDelta; // ensures that the picture count is always increasing usingSource()->fPictureCount += pictureCountDelta; fPrevPictureCountDelta = pictureCountDelta; fTotalTicksSinceLastTimeCode = newTotalTicks; fSecondsSinceLastTimeCode += modulo_time_base; } } } // The next thing to parse depends on the code that we just saw, // but we are assumed to have ended the current picture: usingSource()->fPictureEndMarker = True; // HACK ##### switch (next4Bytes) { case VISUAL_OBJECT_SEQUENCE_END_CODE: { setParseState(PARSING_VISUAL_OBJECT_SEQUENCE_END_CODE); break; } case VISUAL_OBJECT_SEQUENCE_START_CODE: { setParseState(PARSING_VISUAL_OBJECT_SEQUENCE_SEEN_CODE); break; } case GROUP_VOP_START_CODE: { setParseState(PARSING_GROUP_OF_VIDEO_OBJECT_PLANE); break; } case VOP_START_CODE: { setParseState(PARSING_VIDEO_OBJECT_PLANE); break; } default: { usingSource()->envir() << "MPEG4VideoStreamParser::parseVideoObjectPlane(): Saw unexpected code " << (void*)next4Bytes << "\n"; setParseState(PARSING_VIDEO_OBJECT_PLANE); // the safest way to recover... break; } } // Compute this frame's presentation time: usingSource()->computePresentationTime(fTotalTicksSinceLastTimeCode); return curFrameSize();}unsigned MPEG4VideoStreamParser::parseVisualObjectSequenceEndCode() {#ifdef DEBUG fprintf(stderr, "parsing VISUAL_OBJECT_SEQUENCE_END_CODE\n");#endif // Note that we've already read the VISUAL_OBJECT_SEQUENCE_END_CODE save4Bytes(VISUAL_OBJECT_SEQUENCE_END_CODE); setParseState(PARSING_VISUAL_OBJECT_SEQUENCE); // Treat this as if we had ended a picture: usingSource()->fPictureEndMarker = True; // HACK ##### return curFrameSize();}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -