////////////////////////////////////////////////////////////////////////// // // Parse.cpp // MPEG-1 parsing code. // // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A // PARTICULAR PURPOSE. // // Copyright (c) Microsoft Corporation. All rights reserved. // ////////////////////////////////////////////////////////////////////////// #include "MPEG1Source.h" #include "Parse.h" // HAS_FLAG: Test if 'b' contains a specified bit flag #define HAS_FLAG(b, flag) (((b) & (flag)) == (flag)) // MAKE_WORD: Convert two bytes into a WORD inline WORD MAKE_WORD(BYTE b1, BYTE b2) { return ((b1 << 8) | b2); } // MAKE_DWORD_HOSTORDER: // Convert the first 4 bytes of an array into a DWORD in host order (ie, no byte swapping). inline DWORD MAKE_DWORD_HOSTORDER(const BYTE *pData) { return ((DWORD*)pData)[0]; } // MAKE_DWORD: // Convert the first 4 bytes of an array into a DWORD in MPEG-1 stream byte order. inline DWORD MAKE_DWORD(const BYTE *pData) { return htonl( MAKE_DWORD_HOSTORDER(pData) ); } //------------------------------------------------------------------- // AdvanceBufferPointer // Advances a byte array pointer. // // pData: The array pointer. // cbBufferSize: The array size. On output, the size remaining. // cbAdvance: Number of bytes to advance the pointer. // // This function is just a helper for keeping a valid pointer as we // walk through a buffer. //------------------------------------------------------------------- inline HRESULT AdvanceBufferPointer(const BYTE* &pData, DWORD &cbBufferSize, DWORD cbAdvance) { assert(cbBufferSize >= cbAdvance); if (cbBufferSize < cbAdvance) { return E_FAIL; } cbBufferSize -= cbAdvance; pData += cbAdvance; return S_OK; } // ValidateBufferSize: // Fails if cbBufferSize < cbMinRequiredSize. inline HRESULT ValidateBufferSize(DWORD cbBufferSize, DWORD cbMinRequiredSize) { return (cbBufferSize >= cbMinRequiredSize? S_OK : E_FAIL); } // Forward declarations. HRESULT ParseStreamData(const BYTE *pData, MPEG1StreamHeader& header); HRESULT ParseStreamId(BYTE id, StreamType *pType, BYTE *pStreamNum); HRESULT ParsePTS(const BYTE *pData, LONGLONG *pPTS); HRESULT GetFrameRate(BYTE frameRateCode, MFRatio *pRatio); HRESULT GetPixelAspectRatio(BYTE pixelAspectCode, MFRatio *pRatio); HRESULT GetAudioBitRate(MPEG1AudioLayer layer, BYTE index, DWORD *pdwBitRate); HRESULT GetSamplingFrequency(BYTE code, DWORD *pdwSamplesPerSec); //------------------------------------------------------------------- // Buffer class //------------------------------------------------------------------- Buffer::Buffer() : m_begin(0), m_end(0) { } //------------------------------------------------------------------- // Initalize // Sets the initial buffer size. //------------------------------------------------------------------- HRESULT Buffer::Initalize(DWORD cbSize) { HRESULT hr = SetSize(cbSize); if (SUCCEEDED(hr)) { ZeroMemory(Ptr(), cbSize); } return hr; } //------------------------------------------------------------------- // DataPtr // Returns a pointer to the start of the buffer. //------------------------------------------------------------------- BYTE* Buffer::DataPtr() { return Ptr() + m_begin; } //------------------------------------------------------------------- // DataSize // Returns the size of the buffer. // // Note: The "size" is determined by the start and end pointers. // The memory allocated for the buffer can be larger. //------------------------------------------------------------------- DWORD Buffer::DataSize() const { assert(m_end >= m_begin); return m_end - m_begin; } //------------------------------------------------------------------- // Reserve // Reserves additional bytes of memory for the buffer. // // This method does *not* increase the value returned by DataSize(). // // After this method returns, the value of DataPtr() might change, // so do not cache the old value. //------------------------------------------------------------------- HRESULT Buffer::Reserve(DWORD cb) { if (cb > MAXDWORD - DataSize()) { return E_INVALIDARG; // Overflow } HRESULT hr = S_OK; // If this would push the end position past the end of the array, // then we need to copy up the data to start of the array. We might // also need to realloc the array. if (cb > GetCount() - m_end) { // New end position would be past the end of the array. // Check if we need to grow the array. if (cb > CurrentFreeSize()) { // Array needs to grow CHECK_HR(hr = SetSize(DataSize() + cb)); } MoveMemory(Ptr(), DataPtr(), DataSize()); // Reset begin and end. m_end = DataSize(); // Update m_end first before resetting m_begin! m_begin = 0; } assert(CurrentFreeSize() >= cb); done: return hr; } //------------------------------------------------------------------- // MoveStart // Moves the start of the buffer by cb bytes. // // Call this method after consuming data from the buffer. //------------------------------------------------------------------- HRESULT Buffer::MoveStart(DWORD cb) { // Cannot advance pass the end of the buffer. if (cb > DataSize()) { return E_INVALIDARG; } m_begin += cb; return S_OK; } //------------------------------------------------------------------- // MoveEnd // Moves end position of the buffer. //------------------------------------------------------------------- HRESULT Buffer::MoveEnd(DWORD cb) { HRESULT hr = S_OK; hr = Reserve(cb); if (SUCCEEDED(hr)) { m_end += cb; } return hr; } //------------------------------------------------------------------- // CurrentFreeSize (private) // // Returns the size of the array minus the size of the data. //------------------------------------------------------------------- DWORD Buffer::CurrentFreeSize() const { assert(GetCount() >= DataSize()); return GetCount() - DataSize(); } //------------------------------------------------------------------- // Parser class //------------------------------------------------------------------- Parser::Parser() : m_SCR(0), m_muxRate(0), m_pHeader(NULL), m_bHasPacketHeader(FALSE), m_bEOS(FALSE) { ZeroMemory(&m_curPacketHeader, sizeof(m_curPacketHeader)); } Parser::~Parser() { CoTaskMemFree(m_pHeader); } //------------------------------------------------------------------- // GetSystemHeader class // // Returns a copy of the system header. // Do not call this method unless HasSystemHeader() returns TRUE. // // The caller must free the returned structure by calling // CoTaskMemFree. //------------------------------------------------------------------- HRESULT Parser::GetSystemHeader(MPEG1SystemHeader **ppHeader) { if (ppHeader == NULL) { return E_POINTER; } if (!HasSystemHeader()) { return E_FAIL; } assert(m_pHeader->cbSize > 0); MPEG1SystemHeader *pHeader = (MPEG1SystemHeader*)CoTaskMemAlloc(m_pHeader->cbSize); if (pHeader == NULL) { return E_OUTOFMEMORY; } CopyMemory(pHeader, m_pHeader, m_pHeader->cbSize); *ppHeader = pHeader; return S_OK; } //------------------------------------------------------------------- // ParseBytes // Parses as much data as possible from the pData buffer, and returns // the amount of data parsed in pAte (*pAte <= cbLen). // // Return values: // S_OK: The method consumed some data (*pAte > 0). // S_FALSE: The method did not consume any data (*pAte == 0). // [or an error code] // // If the method returns S_FALSE, the caller must allocate a larger // buffer and pass in more data. //------------------------------------------------------------------- HRESULT Parser::ParseBytes(const BYTE *pData, DWORD cbLen, DWORD *pAte) { *pAte = 0; if (cbLen < 4) { return S_FALSE; } HRESULT hr = S_OK; DWORD cbLengthToStartCode = 0; // How much we skip to reach the next start code. DWORD cbParsed = 0; // How much we parse after the start code. m_bHasPacketHeader = FALSE; hr = FindNextStartCode(pData, cbLen, &cbLengthToStartCode); if (hr == S_OK) { cbLen -= cbLengthToStartCode; pData += cbLengthToStartCode; switch (MAKE_DWORD(pData)) { case MPEG1_PACK_START_CODE: // Start of pack. hr = ParsePackHeader(pData, cbLen, &cbParsed); break; case MPEG1_SYSTEM_HEADER_CODE: // Start of system header. hr = ParseSystemHeader(pData, cbLen, &cbParsed); break; case MPEG1_STOP_CODE: // Stop code, end of stream. cbParsed = sizeof(DWORD); hr = OnEndOfStream(); break; default: // Start of packet. hr = ParsePacketHeader(pData, cbLen, &cbParsed); break; } } if (hr == S_OK) { *pAte = cbLengthToStartCode + cbParsed; } return hr; }; //------------------------------------------------------------------- // FindNextStartCode // Looks for the next start code in the buffer. // // pData: Pointer to the buffer. // cbLen: Size of the buffer. // pAte: Receives the number of bytes *before* the start code. // // If no start code is found, the method returns S_FALSE. //------------------------------------------------------------------- HRESULT Parser::FindNextStartCode(const BYTE *pData, DWORD cbLen, DWORD *pAte) { HRESULT hr = S_FALSE; DWORD cbLeft = cbLen; while (cbLeft > 4) { if ( (MAKE_DWORD_HOSTORDER(pData) & 0x00FFFFFF) == 0x00010000 ) { hr = S_OK; break; } cbLeft -= 4; pData += 4; } *pAte = (cbLen - cbLeft); return hr; } //------------------------------------------------------------------- // ParsePackHeader // Parses the start of an MPEG-1 pack. //------------------------------------------------------------------- HRESULT Parser::ParsePackHeader(const BYTE *pData, DWORD cbLen, DWORD *pAte) { assert( MAKE_DWORD(pData) == MPEG1_PACK_START_CODE ); if (cbLen < MPEG1_PACK_HEADER_SIZE) { return S_FALSE; // Not enough data yet. } // Check marker bits if ( ((pData[4] & 0xF1) != 0x21) || ((pData[6] & 0x01) != 0x01) || ((pData[8] & 0x01) != 0x01) || ((pData[9] & 0x80) != 0x80) || ((pData[11] & 0x01) != 0x01) ) { return E_FAIL; } // Calculate the SCR. LONGLONG scr = ( (pData[8] & 0xFE) >> 1) | ( (pData[7]) << 7) | ( (pData[6] & 0xFE) << 14) | ( (pData[5]) << 22) | ( (pData[4] & 0x0E) << 29); DWORD muxRate = ( (pData[11] & 0xFE) >> 1) | ( (pData[10]) << 7) | ( (pData[9] & 0x7F) << 15); m_SCR = scr; m_muxRate = muxRate; *pAte = MPEG1_PACK_HEADER_SIZE; return S_OK; } //------------------------------------------------------------------- // ParseSystemHeader. // Parses the MPEG-1 system header. // // NOTES: // The system header optionally appears after the pack header. // The first pack must contain a system header. // Subsequent packs may contain a system header. //------------------------------------------------------------------- HRESULT Parser::ParseSystemHeader(const BYTE *pData, DWORD cbLen, DWORD *pAte) { assert( MAKE_DWORD(pData) == MPEG1_SYSTEM_HEADER_CODE ); if (cbLen < MPEG1_SYSTEM_HEADER_MIN_SIZE) { return S_FALSE; // Not enough data yet. } // Find the total header length. DWORD cbHeaderLen = MPEG1_SYSTEM_HEADER_PREFIX + MAKE_WORD(pData[4], pData[5]); if (cbHeaderLen < MPEG1_SYSTEM_HEADER_MIN_SIZE - MPEG1_SYSTEM_HEADER_PREFIX) { return E_FAIL; // Invalid value. } if (cbLen < cbHeaderLen) { return S_FALSE; // Not enough data yet. } // We have enough data to parse the header. HRESULT hr = S_OK; // Did we already see a system header? if (!HasSystemHeader()) { // This is the first time we've seen the header. Parse it. // Calculate the number of stream info's in the header. DWORD cStreamInfo = (cbHeaderLen - MPEG1_SYSTEM_HEADER_MIN_SIZE) / MPEG1_SYSTEM_HEADER_STREAM; // Calculate the structure size. DWORD cbSize = sizeof(MPEG1SystemHeader); if (cStreamInfo > 1) { cbSize += sizeof(MPEG1StreamHeader) * (cStreamInfo - 1); } // Allocate room for the header. m_pHeader = (MPEG1SystemHeader*)CoTaskMemAlloc(cbSize); if (m_pHeader == NULL) { CHECK_HR(hr = E_OUTOFMEMORY); } m_pHeader->cbSize = cbSize; // Check marker bits if ( ((pData[6] & 0x80) != 0x80) || ((pData[8] & 0x01) != 0x01) || ((pData[10] & 0x20) != 0x20) || (pData[11] != 0xFF) ) { CHECK_HR(hr = E_FAIL); // Invalid bits. } m_pHeader->rateBound = ((pData[6] & 0x7F) << 16) | (pData[7] << 8) | (pData[8] >> 1); m_pHeader->cAudioBound = pData[9] >> 2; m_pHeader->bFixed = HAS_FLAG(pData[9], 0x02); m_pHeader->bCSPS = HAS_FLAG(pData[9], 0x01); m_pHeader->bAudioLock = HAS_FLAG(pData[10], 0x80); m_pHeader->bVideoLock = HAS_FLAG(pData[10], 0x40); m_pHeader->cVideoBound = pData[10] & 0x1F; m_pHeader->cStreams = cStreamInfo; // Parse the stream information. const BYTE *pStreamInfo = pData + MPEG1_SYSTEM_HEADER_MIN_SIZE; for (DWORD i = 0; i < cStreamInfo; i++) { CHECK_HR(hr = ParseStreamData(pStreamInfo, m_pHeader->streams[i])); pStreamInfo += MPEG1_SYSTEM_HEADER_STREAM; } } *pAte = cbHeaderLen; done: if (FAILED(hr)) { CoTaskMemFree(m_pHeader); m_pHeader = NULL; } return hr; } //------------------------------------------------------------------- // ParsePacketHeader // // Parses the packet header. // // If the method returns S_OK, then HasPacket() returns TRUE and the // caller can start parsing the packet. //------------------------------------------------------------------- HRESULT Parser::ParsePacketHeader(const BYTE *pData, DWORD cbLen, DWORD *pAte) { if (!HasSystemHeader()) { return E_FAIL; // We should not get a packet before the first system header. } if (cbLen < MPEG1_PACKET_HEADER_MIN_SIZE) { return S_FALSE; // Not enough data yet. } // Before we parse anything else in the packet header, look for the header length. DWORD cbPacketLen = MAKE_WORD(pData[4], pData[5]) + MPEG1_PACKET_HEADER_MIN_SIZE; // We want enough data for the maximum packet header OR the total packet size, whichever is less. if (cbLen < cbPacketLen && cbLen < MPEG1_PACKET_HEADER_MAX_SIZE) { return S_FALSE; // Not enough data yet. } // Make sure the start code is 0x000001xx if ((MAKE_DWORD(pData) & 0xFFFFFF00) != MPEG1_START_CODE_PREFIX) { return E_FAIL; } HRESULT hr = S_OK; BYTE id = 0; StreamType type = StreamType_Unknown; BYTE num = 0; BOOL bHasPTS = FALSE; ZeroMemory(&m_curPacketHeader, sizeof(m_curPacketHeader)); // Find the stream ID. id = pData[3]; CHECK_HR(hr = ParseStreamId(id, &type, &num)); DWORD cbLeft = cbPacketLen - MPEG1_PACKET_HEADER_MIN_SIZE; pData = pData + MPEG1_PACKET_HEADER_MIN_SIZE; DWORD cbPadding = 0; LONGLONG pts = 0; // Go past the stuffing bytes. while ((cbLeft > 0) && (*pData == 0xFF)) { AdvanceBufferPointer(pData, cbLeft, 1); ++cbPadding; } // Check for invalid number of stuffing bytes. if (cbPadding > MPEG1_PACKET_HEADER_MAX_STUFFING_BYTE) { CHECK_HR(hr = E_FAIL); } // The next bits are: // (optional) STD buffer size (2 bytes) // union // { // PTS (5 bytes) // PTS + DTS (10 bytes) // '0000 1111' (1 bytes) // } CHECK_HR(hr = ValidateBufferSize(cbLeft, 1)); if ((*pData & 0xC0) == 0x40) { // Skip STD buffer size. CHECK_HR(hr = AdvanceBufferPointer(pData, cbLeft, 2)); } CHECK_HR(ValidateBufferSize(cbLeft, 1)); if ((*pData & 0xF1) == 0x21) { // PTS CHECK_HR(ValidateBufferSize(cbLeft, 5)); ParsePTS(pData, &pts); bHasPTS = TRUE; CHECK_HR(hr = AdvanceBufferPointer(pData, cbLeft, 5)); } else if ((*pData & 0xF1) == 0x31) { // PTS + DTS CHECK_HR(ValidateBufferSize(cbLeft, 10)); // Parse PTS but skip DTS. ParsePTS(pData, &pts); bHasPTS = TRUE; CHECK_HR(hr = AdvanceBufferPointer(pData, cbLeft, 10)); } else if ((*pData) == 0x0F) { CHECK_HR(hr = AdvanceBufferPointer(pData, cbLeft, 1)); } else { CHECK_HR(hr = E_FAIL); // Unexpected bit field } m_curPacketHeader.stream_id = id; m_curPacketHeader.type = type; m_curPacketHeader.number = num; m_curPacketHeader.cbPacketSize = cbPacketLen; m_curPacketHeader.cbPayload = cbLeft; m_curPacketHeader.bHasPTS = bHasPTS; m_curPacketHeader.PTS = pts; // Client can read the packet now. m_bHasPacketHeader = TRUE; *pAte = cbPacketLen - cbLeft; done: return hr; } //------------------------------------------------------------------- // OnEndOfStream // Called when the parser reaches the MPEG-1 stop code. // // Note: Obviously the parser is not guaranteed to see a stop code // before the client reaches the end of the source data. The client // must be prepared to handle that case. //------------------------------------------------------------------- HRESULT Parser::OnEndOfStream() { m_bEOS = TRUE; ClearPacket(); return S_OK; } //------------------------------------------------------------------- // Static functions //------------------------------------------------------------------- //------------------------------------------------------------------- // ParsePTS // Parse the 33-bit Presentation Time Stamp (PTS) //------------------------------------------------------------------- HRESULT ParsePTS(const BYTE *pData, LONGLONG *pPTS) { BYTE byte1 = pData[0]; WORD word1 = MAKE_WORD(pData[1], pData[2]); WORD word2 = MAKE_WORD(pData[3], pData[4]); // Check marker bits. // The first byte can be '0010xxx1' or '0x11xxxx1' if (((byte1 & 0xE1) != 0x21) || ((word1 & 0x01) != 0x01) || ((word2 & 0x01) != 0x01) ) { return E_FAIL; } LARGE_INTEGER li; // The PTS is 33 bits, so bit 32 goes in the high-order DWORD li.HighPart = (byte1 & 0x08) >> 3; li.LowPart = (static_cast(byte1 & 0x06) << 29) | (static_cast(word1 & 0xFFFE) << 14) | (static_cast(word2) >> 1); *pPTS = li.QuadPart; return S_OK; } //------------------------------------------------------------------- // ParseStreamData // Parses the stream information (for one stream) in the system // header. //------------------------------------------------------------------- HRESULT ParseStreamData(const BYTE *pStreamInfo, MPEG1StreamHeader& header) { // Check marker bits. if ( (pStreamInfo[1] & 0xC0) != 0xC0 ) { return E_FAIL; // Invalid bits } HRESULT hr = S_OK; BYTE id = 0; BYTE num = 0; DWORD bound = 0; StreamType type = StreamType_Unknown; // The id is a stream code plus (for some types) a stream number, bitwise-OR'd. id = pStreamInfo[0]; hr = ParseStreamId(id, &type, &num); if (FAILED(hr)) { return hr; } // Calculate STD bound. bound = pStreamInfo[2] | ((pStreamInfo[1] & 0x1F) << 8); if (pStreamInfo[1] & 0x20) { bound *= 1024; } else { bound *= 128; } header.stream_id = id; header.type = type; header.number = num; header.sizeBound = bound; return S_OK; } //------------------------------------------------------------------- // ParseStreamId // Parses an MPEG-1 stream ID. // // Note: // The id is a stream code, plus (for some types) a stream number, // bitwise-OR'd. This function returns the type and the stream number. // // See ISO/EIC 11172-1, sec 2.4.4.2 //------------------------------------------------------------------- HRESULT ParseStreamId(BYTE id, StreamType *pType, BYTE *pStreamNum) { StreamType type = StreamType_Unknown; BYTE num = 0; switch (id) { case MPEG1_STREAMTYPE_ALL_AUDIO: type = StreamType_AllAudio; break; case MPEG1_STREAMTYPE_ALL_VIDEO: type = StreamType_AllVideo; break; case MPEG1_STREAMTYPE_RESERVED: type = StreamType_Reserved; break; case MPEG1_STREAMTYPE_PRIVATE1: type = StreamType_Private1; break; case MPEG1_STREAMTYPE_PADDING: type = StreamType_Padding; break; case MPEG1_STREAMTYPE_PRIVATE2: type = StreamType_Private2; break; default: if ((id & 0xE0) == MPEG1_STREAMTYPE_AUDIO_MASK) { type = StreamType_Audio; num = id & 0x1F; } else if ((id & 0xF0) == MPEG1_STREAMTYPE_VIDEO_MASK) { type = StreamType_Video; num = id & 0x0F; } else if ((id & 0xF0) == MPEG1_STREAMTYPE_DATA_MASK) { type = StreamType_Data; num = id & 0x0F; } else { TRACE((L"ParseStreamId: Unknown stream ID: %d\n", id)); return E_FAIL; // Unknown stream ID code. } } *pType = type; *pStreamNum = num; return S_OK; } //------------------------------------------------------------------- // ReadVideoSequenceHeader // Parses a video sequence header. // // Call Parser::HasPacket() to ensure that pData points to the start // of a payload, and call Parser::PacketHeader() to verify it is a // video packet. //------------------------------------------------------------------- HRESULT ReadVideoSequenceHeader( const BYTE *pData, DWORD cbData, MPEG1VideoSeqHeader& seqHeader, DWORD *pAte ) { DWORD cbPadding = 0; *pAte = 0; // Skip to the sequence header code. while ( ((DWORD*)pData)[0] == 0 ) { pData += 4; cbPadding += 4; if (cbData < 4) { *pAte = cbPadding; return S_FALSE; } } cbData -= cbPadding; // Validate the sequence header code. if (MAKE_DWORD(pData) != MPEG1_SEQUENCE_HEADER_CODE) { return E_FAIL; } // Check for the minimum size buffer. if (cbData < MPEG1_VIDEO_SEQ_HEADER_MIN_SIZE) { return S_FALSE; } // Calculate the actual required size. DWORD cbRequired = MPEG1_VIDEO_SEQ_HEADER_MIN_SIZE; // Look for quantization matrices. BOOL bNonIntra = FALSE; if ( HAS_FLAG(pData[11], 0x02) ) { // Intra quantization matrix is TRUE. cbRequired += 64; // Non-intra flag follows the intra matrix, but first check // if we have enough data before trying to get the non-intra flag. if (cbData >= cbRequired) { bNonIntra = HAS_FLAG( pData[11 + 64], 0x01 ); } } // Intra is FALSE, look for non-intra flag else if ( HAS_FLAG(pData[11], 0x01) ) { cbRequired += 64; } if (cbData < cbRequired) { return S_FALSE; } ZeroMemory(&seqHeader, sizeof(seqHeader)); // Check the marker bit. if ( !HAS_FLAG(pData[10], 0x20) ) { return E_FAIL; } BYTE parCode = pData[7] >> 4; BYTE frameRateCode = pData[7] & 0x0F; if (FAILED(GetPixelAspectRatio(parCode, &seqHeader.pixelAspectRatio))) { return E_FAIL; } if (FAILED(GetFrameRate(frameRateCode, &seqHeader.frameRate))) { return E_FAIL; } seqHeader.width = (pData[4] << 4) | (pData[5] >> 4) ; seqHeader.height = ((pData[5] & 0x0F) << 8) | (pData[6]); seqHeader.bitRate = (pData[8] << 10) | (pData[9] << 2) | (pData[10] >> 6); if (seqHeader.bitRate == 0) { return E_FAIL; // Not allowed. } else if (seqHeader.bitRate == 0x3FFFF) { seqHeader.bitRate = 0; // Variable bit-rate. } else { seqHeader.bitRate = seqHeader.bitRate * 400; // Units of 400 bps } seqHeader.cbVBV_Buffer = ( ((pData[10] & 0x1F) << 5) | (pData[11] >> 3) ) * 2048; seqHeader.bConstrained = HAS_FLAG(pData[11], 0x04); seqHeader.cbHeader = cbRequired; CopyMemory(seqHeader.header, pData, cbRequired); *pAte = cbRequired + cbPadding; return S_OK; } //------------------------------------------------------------------- // GetFrameRate // Returns the frame rate from the picture_rate field of the sequence // header. // // See ISO/IEC 11172-2, 2.4.3.2 "Sequence Header" //------------------------------------------------------------------- HRESULT GetFrameRate(BYTE frameRateCode, MFRatio *pRatio) { MFRatio frameRates[] = { { 0, 0 }, // invalid { 24000, 1001 }, // 23.976 fps { 24, 1 }, { 25, 1 }, { 30000, 1001 }, // 29.97 fps { 50, 1 }, { 60000, 1001 }, // 59.94 fps { 60, 1 } }; if (frameRateCode < 1 || frameRateCode >= ARRAYSIZE(frameRates)) { return MF_E_INVALIDTYPE; } pRatio->Numerator = frameRates[frameRateCode].Numerator; pRatio->Denominator = frameRates[frameRateCode].Denominator; return S_OK; } //------------------------------------------------------------------- // GetPixelAspectRatio // Returns the pixel aspect ratio (PAR) from the pel_aspect_ratio // field of the sequence header. // // See ISO/IEC 11172-2, 2.4.3.2 "Sequence Header" //------------------------------------------------------------------- HRESULT GetPixelAspectRatio(BYTE pixelAspectCode, MFRatio *pRatio) { DWORD height[] = { 0, 10000, 6735, 7031, 7615, 8055, 8437, 8935, 9157, 9815, 10255, 10695, 10950, 11575, 12015 }; const DWORD width = 10000; if (pixelAspectCode < 1 || pixelAspectCode >= ARRAYSIZE(height)) { return MF_E_INVALIDTYPE; } pRatio->Numerator = height[pixelAspectCode]; pRatio->Denominator = width; return S_OK; } //------------------------------------------------------------------- // ReadAudioFrameHeader // Parses an audio frame header. // // Call Parser::HasPacket() to ensure that pData points to the start // of a payload, and call Parser::PacketHeader() to verify it is an // audio packet. //------------------------------------------------------------------- HRESULT ReadAudioFrameHeader( const BYTE *pData, DWORD cbData, MPEG1AudioFrameHeader& audioHeader, DWORD *pAte ) { HRESULT hr = S_OK; MPEG1AudioFrameHeader header; ZeroMemory(&header, sizeof(header)); BYTE bitRateIndex = 0; BYTE samplingIndex = 0; *pAte = 0; if (cbData < MPEG1_AUDIO_FRAME_HEADER_SIZE) { return S_FALSE; } if (pData[0] != 0xFF) { return E_FAIL; } if (!HAS_FLAG(pData[1], 0xF8)) { return E_FAIL; } // Layer bits switch (pData[1] & 0x06) { case 0x00: return E_FAIL; case 0x06: header.layer = MPEG1_Audio_Layer1; break; case 0x04: header.layer = MPEG1_Audio_Layer2; break; case 0x02: header.layer = MPEG1_Audio_Layer3; break; default: return E_UNEXPECTED; // Cannot actually happen, given our bitmask above. } bitRateIndex = (pData[2] & 0xF0) >> 4; samplingIndex = (pData[2] & 0x0C) >> 2; // Bit rate. // Note: Accoring to ISO/IEC 11172-3, some combinations of bitrate and // mode are not valid. However, this is up to the decoder to validate. CHECK_HR(hr = GetAudioBitRate(header.layer, bitRateIndex, &header.dwBitRate)); // Sampling frequency. CHECK_HR(hr = GetSamplingFrequency(samplingIndex, &header.dwSamplesPerSec)); header.mode = static_cast((pData[3] & 0xC0) >> 6); header.modeExtension = (pData[3] & 0x30) >> 4; header.emphasis = (pData[3] & 0x03); // Parse the various bit flags. if (HAS_FLAG(pData[1], 0x01)) { header.wFlags |= MPEG1_AUDIO_PROTECTION_BIT; } if (HAS_FLAG(pData[2], 0x01)) { header.wFlags |= MPEG1_AUDIO_PRIVATE_BIT; } if (HAS_FLAG(pData[3], 0x08)) { header.wFlags |= MPEG1_AUDIO_COPYRIGHT_BIT; } if (HAS_FLAG(pData[3], 0x04)) { header.wFlags |= MPEG1_AUDIO_ORIGINAL_BIT; } if (header.mode == MPEG1_Audio_SingleChannel) { header.nChannels = 1; } else { header.nChannels = 2; } header.nBlockAlign = 1; CopyMemory(&audioHeader, &header, sizeof(audioHeader)); done: return hr; }; //------------------------------------------------------------------- // GetAudioBitRate // Returns the audio bit rate in KBits per second, from the // bitrate_index field of the audio frame header. // // See ISO/IEC 11172-3, 2.4.2.3, "Header" //------------------------------------------------------------------- HRESULT GetAudioBitRate(MPEG1AudioLayer layer, BYTE index, DWORD *pdwBitRate) { const DWORD MAX_BITRATE_INDEX = 14; // Table of bit rates. const DWORD bitrate[3][ (MAX_BITRATE_INDEX+1) ] = { { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448 }, // Layer I { 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384 }, // Layer II { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320 } // Layer III }; if (layer < MPEG1_Audio_Layer1 || layer > MPEG1_Audio_Layer3) { return E_INVALIDARG; } if (index > MAX_BITRATE_INDEX) { return E_INVALIDARG; } *pdwBitRate = bitrate[layer][index]; return S_OK; } //------------------------------------------------------------------- // GetSamplingFrequency // Returns the sampling frequency in samples per second, from the // sampling_frequency field of the audio frame header. // // See ISO/IEC 11172-3, 2.4.2.3, "Header" //------------------------------------------------------------------- inline HRESULT GetSamplingFrequency(BYTE code, DWORD *pdwSamplesPerSec) { switch (code) { case 0: *pdwSamplesPerSec = 44100; break; case 1: *pdwSamplesPerSec = 48000; break; case 2: *pdwSamplesPerSec = 32000; break; default: return E_FAIL; } return S_OK; }