ref: 2cff3a9f96eabd25287392db3454bdb40d14f4b2
dir: /common/mp4av/mpeg4.cpp/
/* * The contents of this file are subject to the Mozilla Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is MPEG4IP. * * The Initial Developer of the Original Code is Cisco Systems Inc. * Portions created by Cisco Systems Inc. are * Copyright (C) Cisco Systems Inc. 2000-2002. All Rights Reserved. * * Contributor(s): * Dave Mackie dmackie@cisco.com */ /* * Notes: * - file formatted with tabstops == 4 spaces */ #include <mp4av_common.h> extern "C" uint8_t *MP4AV_Mpeg4FindVosh (uint8_t *pBuf, uint32_t buflen) { while (buflen > 4) { if (pBuf[0] == 0x0 && pBuf[1] == 0x0 && pBuf[2] == 0x1 && pBuf[3] == MP4AV_MPEG4_VOSH_START) { return pBuf; } pBuf++; buflen--; } return NULL; } extern "C" bool MP4AV_Mpeg4ParseVosh( u_int8_t* pVoshBuf, u_int32_t voshSize, u_int8_t* pProfileLevel) { CMemoryBitstream vosh; vosh.SetBytes(pVoshBuf, voshSize); try { vosh.GetBits(32); // start code *pProfileLevel = vosh.GetBits(8); } catch (int e) { return false; } return true; } extern "C" bool MP4AV_Mpeg4CreateVosh( u_int8_t** ppBytes, u_int32_t* pNumBytes, u_int8_t profileLevel) { CMemoryBitstream vosh; try { if (*ppBytes) { // caller must guarantee buffer against overrun memset((*ppBytes) + (*pNumBytes), 0, 5); vosh.SetBytes(*ppBytes, (*pNumBytes) + 5); vosh.SetBitPosition((*pNumBytes) << 3); } else { vosh.AllocBytes(5); } vosh.PutBits(MP4AV_MPEG4_SYNC, 24); vosh.PutBits(MP4AV_MPEG4_VOSH_START, 8); vosh.PutBits(profileLevel, 8); *ppBytes = vosh.GetBuffer(); *pNumBytes = vosh.GetNumberOfBytes(); } catch (int e) { return false; } return true; } extern "C" bool MP4AV_Mpeg4CreateVo( u_int8_t** ppBytes, u_int32_t* pNumBytes, u_int8_t objectId) { CMemoryBitstream vo; try { if (*ppBytes) { // caller must guarantee buffer against overrun memset((*ppBytes) + (*pNumBytes), 0, 9); vo.SetBytes(*ppBytes, *pNumBytes + 9); vo.SetBitPosition((*pNumBytes) << 3); } else { vo.AllocBytes(9); } vo.PutBits(MP4AV_MPEG4_SYNC, 24); vo.PutBits(MP4AV_MPEG4_VO_START, 8); vo.PutBits(0x08, 8); // no verid, priority, or signal type vo.PutBits(MP4AV_MPEG4_SYNC, 24); vo.PutBits(objectId - 1, 8); *ppBytes = vo.GetBuffer(); *pNumBytes = vo.GetNumberOfBytes(); } catch (int e) { return false; } return true; } extern "C" uint8_t *MP4AV_Mpeg4FindVol (uint8_t *pBuf, uint32_t buflen) { while (buflen > 4) { if (pBuf[0] == 0x0 && pBuf[1] == 0x0 && pBuf[2] == 0x1 && (pBuf[3] & 0xf0) == MP4AV_MPEG4_VOL_START) { return pBuf; } pBuf++; buflen--; } return NULL; } extern "C" bool MP4AV_Mpeg4ParseVol( u_int8_t* pVolBuf, u_int32_t volSize, u_int8_t* pTimeBits, u_int16_t* pTimeTicks, u_int16_t* pFrameDuration, u_int16_t* pFrameWidth, u_int16_t* pFrameHeight) { CMemoryBitstream vol; vol.SetBytes(pVolBuf, volSize); try { vol.SkipBits(32); // start code vol.SkipBits(1); // random accessible vol vol.SkipBits(8); // object type id u_int8_t verid = 1; if (vol.GetBits(1)) { // is object layer id verid = vol.GetBits(4); // object layer verid vol.SkipBits(3); // object layer priority } if (vol.GetBits(4) == 0xF) { // aspect ratio info vol.SkipBits(8); // par width vol.SkipBits(8); // par height } if (vol.GetBits(1)) { // vol control parameters vol.SkipBits(2); // chroma format vol.SkipBits(1); // low delay if (vol.GetBits(1)) { // vbv parameters vol.SkipBits(15); // first half bit rate vol.SkipBits(1); // marker bit vol.SkipBits(15); // latter half bit rate vol.SkipBits(1); // marker bit vol.SkipBits(15); // first half vbv buffer size vol.SkipBits(1); // marker bit vol.SkipBits(3); // latter half vbv buffer size vol.SkipBits(11); // first half vbv occupancy vol.SkipBits(1); // marker bit vol.SkipBits(15); // latter half vbv occupancy vol.SkipBits(1); // marker bit } } u_int8_t shape = vol.GetBits(2); // object layer shape if (shape == 3 /* GRAYSCALE */ && verid != 1) { vol.SkipBits(4); // object layer shape extension } vol.SkipBits(1); // marker bit *pTimeTicks = vol.GetBits(16); // vop time increment resolution u_int8_t i; u_int32_t powerOf2 = 1; for (i = 0; i < 16; i++) { if (*pTimeTicks < powerOf2) { break; } powerOf2 <<= 1; } *pTimeBits = i; vol.SkipBits(1); // marker bit if (vol.GetBits(1)) { // fixed vop rate // fixed vop time increment *pFrameDuration = vol.GetBits(*pTimeBits); } else { *pFrameDuration = 0; } if (shape == 0 /* RECTANGULAR */) { vol.SkipBits(1); // marker bit *pFrameWidth = vol.GetBits(13); // object layer width vol.SkipBits(1); // marker bit *pFrameHeight = vol.GetBits(13);// object layer height vol.SkipBits(1); // marker bit } else { *pFrameWidth = 0; *pFrameHeight = 0; } // there's more, but we don't need it } catch (int e) { return false; } return true; } extern "C" bool MP4AV_Mpeg4CreateVol( u_int8_t** ppBytes, u_int32_t* pNumBytes, u_int8_t profile, float frameRate, bool shortTime, bool variableRate, u_int16_t width, u_int16_t height, u_int8_t quantType, u_int8_t* pTimeBits) { CMemoryBitstream vol; try { if (*ppBytes) { // caller must guarantee buffer against overrun memset((*ppBytes) + (*pNumBytes), 0, 20); vol.SetBytes(*ppBytes, *pNumBytes + 20); vol.SetBitPosition((*pNumBytes) << 3); } else { vol.AllocBytes(20); } /* VOL - Video Object Layer */ vol.PutBits(MP4AV_MPEG4_SYNC, 24); vol.PutBits(MP4AV_MPEG4_VOL_START, 8); /* 1 bit - random access = 0 (1 only if every VOP is an I frame) */ vol.PutBits(0, 1); /* * 8 bits - type indication * = 1 (simple profile) * = 4 (main profile) */ vol.PutBits(profile, 8); /* 1 bit - is object layer id = 1 */ vol.PutBits(1, 1); /* 4 bits - visual object layer ver id = 1 */ vol.PutBits(1, 4); /* 3 bits - visual object layer priority = 1 */ vol.PutBits(1, 3); /* 4 bits - aspect ratio info = 1 (square pixels) */ vol.PutBits(1, 4); /* 1 bit - VOL control params = 0 */ vol.PutBits(0, 1); /* 2 bits - VOL shape = 0 (rectangular) */ vol.PutBits(0, 2); /* 1 bit - marker = 1 */ vol.PutBits(1, 1); u_int16_t ticks; if (shortTime /* && frameRate == (float)((int)frameRate) */) { ticks = (u_int16_t)(frameRate + 0.5); } else { ticks = 30000; } /* 16 bits - VOP time increment resolution */ vol.PutBits(ticks, 16); /* 1 bit - marker = 1 */ vol.PutBits(1, 1); u_int8_t rangeBits = 1; while (ticks > (1 << rangeBits)) { rangeBits++; } if (pTimeBits) { *pTimeBits = rangeBits; } /* 1 bit - fixed vop rate = 0 or 1 */ if (variableRate) { vol.PutBits(0, 1); } else { vol.PutBits(1, 1); u_int16_t frameDuration = (u_int16_t)((float)ticks / frameRate); /* 1-16 bits - fixed vop time increment in ticks */ vol.PutBits(frameDuration, rangeBits); } /* 1 bit - marker = 1 */ vol.PutBits(1, 1); /* 13 bits - VOL width */ vol.PutBits(width, 13); /* 1 bit - marker = 1 */ vol.PutBits(1, 1); /* 13 bits - VOL height */ vol.PutBits(height, 13); /* 1 bit - marker = 1 */ vol.PutBits(1, 1); /* 1 bit - interlaced = 0 */ vol.PutBits(0, 1); /* 1 bit - overlapped block motion compensation disable = 1 */ vol.PutBits(1, 1); #if 0 /* 2 bits - sprite usage = 0 */ vol.PutBits(0, 2); #else vol.PutBits(0, 1); #endif /* 1 bit - not 8 bit pixels = 0 */ vol.PutBits(0, 1); /* 1 bit - quant type = 0 */ vol.PutBits(quantType, 1); if (quantType) { /* 1 bit - load intra quant mat = 0 */ vol.PutBits(0, 1); /* 1 bit - load inter quant mat = 0 */ vol.PutBits(0, 1); } #if 0 /* 1 bit - quarter pixel = 0 */ vol.PutBits(0, 1); #endif /* 1 bit - complexity estimation disable = 1 */ vol.PutBits(1, 1); /* 1 bit - resync marker disable = 1 */ vol.PutBits(1, 1); /* 1 bit - data partitioned = 0 */ vol.PutBits(0, 1); #if 0 /* 1 bit - newpred = 0 */ vol.PutBits(0, 1); /* 1 bit - reduced resolution vop = 0 */ vol.PutBits(0, 1); #endif /* 1 bit - scalability = 0 */ vol.PutBits(0, 1); /* pad to byte boundary with 0 then as many 1's as needed */ vol.PutBits(0, 1); if ((vol.GetBitPosition() & 7) != 0) { vol.PutBits(0xFF, 8 - (vol.GetBitPosition() & 7)); } *ppBytes = vol.GetBuffer(); *pNumBytes = vol.GetBitPosition() >> 3; } catch (int e) { return false; } return true; } extern "C" bool MP4AV_Mpeg4ParseGov( u_int8_t* pGovBuf, u_int32_t govSize, u_int8_t* pHours, u_int8_t* pMinutes, u_int8_t* pSeconds) { CMemoryBitstream gov; gov.SetBytes(pGovBuf, govSize); try { gov.SkipBits(32); // start code *pHours = gov.GetBits(5); *pMinutes = gov.GetBits(6); gov.SkipBits(1); // marker bit *pSeconds = gov.GetBits(6); } catch (int e) { return false; } return true; } static bool Mpeg4ParseShortHeaderVop( u_int8_t* pVopBuf, u_int32_t vopSize, u_char* pVopType) { CMemoryBitstream vop; vop.SetBytes(pVopBuf, vopSize); try { // skip start code, temporal ref, and into type vop.SkipBits(22 + 8 + 5 + 3); if (vop.GetBits(1) == 0) { *pVopType = 'I'; } else { *pVopType = 'P'; } } catch (int e) { return false; } return true; } extern "C" bool MP4AV_Mpeg4ParseVop( u_int8_t* pVopBuf, u_int32_t vopSize, u_char* pVopType, u_int8_t timeBits, u_int16_t timeTicks, u_int32_t* pVopTimeIncrement) { CMemoryBitstream vop; vop.SetBytes(pVopBuf, vopSize); try { vop.SkipBits(32); // skip start code switch (vop.GetBits(2)) { case 0: /* Intra */ *pVopType = 'I'; break; case 1: /* Predictive */ *pVopType = 'P'; break; case 2: /* Bidirectional Predictive */ *pVopType = 'B'; break; case 3: /* Sprite */ *pVopType = 'S'; break; } if (!pVopTimeIncrement) { return true; } u_int8_t numSecs = 0; while (vop.GetBits(1) != 0) { numSecs++; } vop.SkipBits(1); // skip marker u_int16_t numTicks = vop.GetBits(timeBits); *pVopTimeIncrement = (numSecs * timeTicks) + numTicks; } catch (int e) { return false; } return true; } // Map from ISO IEC 14496-2:2000 Appendix G // to ISO IEC 14496-1:2001 8.6.4.2 Table 6 extern "C" u_int8_t MP4AV_Mpeg4VideoToSystemsProfileLevel(u_int8_t videoProfileLevel) { switch (videoProfileLevel) { // Simple Profile case 0x01: // L1 return 0x03; case 0x02: // L2 return 0x02; case 0x03: // L3 return 0x01; // Simple Scalable Profile case 0x11: // L1 return 0x05; case 0x12: // L2 return 0x04; // Core Profile case 0x21: // L1 return 0x07; case 0x22: // L2 return 0x06; // Main Profile case 0x32: // L2 return 0x0A; case 0x33: // L3 return 0x09; case 0x34: // L4 return 0x08; // N-bit Profile case 0x42: // L2 return 0x0B; // Scalable Texture case 0x51: // L1 return 0x12; case 0x52: // L2 return 0x11; case 0x53: // L3 return 0x10; // Simple Face Animation Profile case 0x61: // L1 return 0x14; case 0x62: // L2 return 0x13; // Simple FBA Profile case 0x63: // L1 case 0x64: // L2 return 0xFE; // Basic Animated Texture Profile case 0x71: // L1 return 0x0F; case 0x72: // L2 return 0x0E; // Hybrid Profile case 0x81: // L1 return 0x0D; case 0x82: // L2 return 0x0C; // Advanced Real Time Simple Profile case 0x91: // L1 case 0x92: // L2 case 0x93: // L3 case 0x94: // L4 // Core Scalable Profile case 0xA1: // L1 case 0xA2: // L2 case 0xA3: // L3 // Advanced Coding Efficiency Profle case 0xB1: // L1 case 0xB2: // L2 case 0xB3: // L3 case 0xB4: // L4 // Advanced Core Profile case 0xC1: // L1 case 0xC2: // L2 // Advanced Scalable Texture Profile case 0xD1: // L1 case 0xD2: // L2 case 0xD3: // L3 // from draft amendments // Simple Studio case 0xE1: // L1 case 0xE2: // L2 case 0xE3: // L3 case 0xE4: // L4 // Core Studio Profile case 0xE5: // L1 case 0xE6: // L2 case 0xE7: // L3 case 0xE8: // L4 // Advanced Simple Profile case 0xF1: // L0 case 0xF2: // L1 case 0xF3: // L2 case 0xF4: // L3 case 0xF5: // L4 // Fine Granularity Scalable Profile case 0xF6: // L0 case 0xF7: // L1 case 0xF8: // L2 case 0xF9: // L3 case 0xFA: // L4 default: return 0xFE; } } extern "C" u_char MP4AV_Mpeg4GetVopType(u_int8_t* pVopBuf, u_int32_t vopSize) { u_char vopType = 0; if (vopSize <= 4) { return vopType; } if (pVopBuf[0] == 0 && pVopBuf[1] == 0 && (pVopBuf[2] & 0xFC) == 0x08 && (pVopBuf[3] & 0x03) == 0x02) { // H.263, (MPEG-4 short header mode) Mpeg4ParseShortHeaderVop(pVopBuf, vopSize, &vopType); } else { // MPEG-4 (normal mode) MP4AV_Mpeg4ParseVop(pVopBuf, vopSize, &vopType, 0, 0, NULL); } return vopType; }