2025-11-28 00:35:46 +09:00

1218 lines
31 KiB
C++

//////////////////////////////////////////////////////////////////////////
//
// 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<DWORD>(byte1 & 0x06) << 29) |
(static_cast<DWORD>(word1 & 0xFFFE) << 14) |
(static_cast<DWORD>(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<MPEG1AudioMode>((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;
}