MPGUTIL.CPP

//==========================================================================; 
//
// 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) 1996 - 1997 Microsoft Corporation. All Rights Reserved.
//
//--------------------------------------------------------------------------;
/* MPEG 1 & 2 parsing routines */

#include <streams.h>
#include <mpegdef.h>
#include <mmreg.h>
#include <mpeg2typ.h>
#include <mpgutil.h>

#ifdef DEBUG
LPCTSTR PictureTypes[8] = { TEXT("forbidden frame type"),
TEXT("I-Frame"),
TEXT("P-Frame"),
TEXT("B-Frame"),
TEXT("D-Frame"),
TEXT("Reserved frame type"),
TEXT("Reserved frame type"),
TEXT("Reserved frame type")
};
LPCTSTR PelAspectRatios[16] = { TEXT("Forbidden"),
TEXT("1.0000 - VGA etc"),
TEXT("0.6735"),
TEXT("0.7031 - 16:9, 625 line"),
TEXT("0.7615"),
TEXT("0.8055"),
TEXT("0.8437 - 16:9, 525 line"),
TEXT("0.8935"),
TEXT("0.9375 - CCIR601, 625 line"),
TEXT("0.9815"),
TEXT("1.0255"),
TEXT("1.0695"),
TEXT("1.1250 - CCIR601, 525 line"),
TEXT("1.1575"),
TEXT("1.2015"),
TEXT("Reserved") };
LPCTSTR PictureRates[16] = { TEXT("Forbidden"),
TEXT("23.976"),
TEXT("24"),
TEXT("25"),
TEXT("29.97"),
TEXT("30"),
TEXT("50"),
TEXT("59.94"),
TEXT("60"),
TEXT("Reserved"),
TEXT("Reserved"),
TEXT("Reserved"),
TEXT("Reserved"),
TEXT("Reserved"),
TEXT("Reserved"),
TEXT("Reserved") };
#endif // DBG

const LONG PictureTimes[16] = { 0,
(LONG)((double)10000000 / 23.976),
(LONG)((double)10000000 / 24),
(LONG)((double)10000000 / 25),
(LONG)((double)10000000 / 29.97),
(LONG)((double)10000000 / 30),
(LONG)((double)10000000 / 50),
(LONG)((double)10000000 / 59.94),
(LONG)((double)10000000 / 60)
};

const float fPictureRates[] =
{
(float)0,
(float)23.976,
(float)24,
(float)25,
(float)29.97,
(float)30,
(float)50,
(float)59.94,
(float)60.0
};

const LONG AspectRatios[16] = { 0,
393700,
(LONG)(393700.0 * 0.6735),
(LONG)(393700.0 * 0.7031),
(LONG)(393700.0 * 0.7615),
(LONG)(393700.0 * 0.8055),
(LONG)(393700.0 * 0.8437),
(LONG)(393700.0 * 0.8935),
(LONG)(393700.0 * 0.9375),
(LONG)(393700.0 * 0.9815),
(LONG)(393700.0 * 1.0255),
(LONG)(393700.0 * 1.0695),
(LONG)(393700.0 * 1.1250),
(LONG)(393700.0 * 1.1575),
(LONG)(393700.0 * 1.2015),
0
};

/* Bit rate tables */
const WORD BitRates[3][16] =
{{ 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0 },
{ 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0 },
{ 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0 }
};

BOOL inline TESTBIT(const BYTE * pbData, int n)
{
return 0 != (pbData[n >> 3] & (0x80 >> (n & 7)));
}

/* Read an extended clock */
BOOL GetExtendedClock(const BYTE * pbData, LONGLONG *pllClock)
{
/* Check marker bits
These occur at bit positions 5, 21, 37, 47 */
if (!TESTBIT(pbData, 5) ||
!TESTBIT(pbData, 21) ||
!TESTBIT(pbData, 37) ||
!TESTBIT(pbData, 47)) {
DbgLog((LOG_ERROR, 1, TEXT("Invalid extended clock marker bits")));
return FALSE;
}

/* Compute the clock value

* = ignore
M = Marker
x = value

0 1 2 3 4
**xxxMxx xxxxxxxx xxxxxMxx xxxxxxxx xxxxxM**
*/
LARGE_INTEGER liClock;
liClock.HighPart = (pbData[0] & 0x20) != 0;
liClock.LowPart = ((pbData[0] & 0x18) << 27) +
((pbData[0] & 0x03) << 28) +
( pbData[1] << 20) +
((pbData[2] & 0xF8) << 12) +
((pbData[2] & 0x03) << 13) +
( pbData[3] << 5) +
( pbData[4] >> 3);

/* Now compute the residual */
LONG lCRE = ((pbData[4] & 3) << 7) + (pbData[5] >> 1);
if (lCRE >= 300) {
DbgLog((LOG_ERROR, 1, TEXT("Clock reference extension >= 300")));
return FALSE;
}

*pllClock = liClock.QuadPart * 300 + lCRE;
return TRUE;
}

/* Parse pack header

Parameters :
pbData - pointer to data containing the system header
cbData - length of data

Returns :
Number of bytes processed (0 if the header doesn't fit
in the data or 4 if the header is invalid)
We will return 0 if we can't see the next start code to
check whether it's a system header start code
*/

DWORD ParseMPEG2PackHeader(const BYTE * pbData, DWORD cbData)
{
ASSERT(cbData >= 4);
ASSERT(*(UNALIGNED DWORD *)pbData == DWORD_SWAP(PACK_START_CODE));

/* Is there enough for the basic length ? */
if (cbData < 18) {
return 0;
}
DWORD dwLen = 14 + (pbData[13] & 0x07);
if (cbData < dwLen + 4) {
return 0;
}

/* Check check bits '01' */
if (pbData[4] & 0xC0 != 0x40) {
DbgLog((LOG_ERROR, 1, TEXT("Invalid bits at start of pack")));
return 4;
}
/* Get SCR */
LONGLONG llSCR;
if (!GetExtendedClock(pbData + 4, &llSCR)) {
DbgLog((LOG_ERROR, 1, TEXT("Invalid pack header SCR field")));
return 4;
}

/* Get the mux rate */
DWORD dwMuxRate = (pbData[10] << (22 - 8)) +
(pbData[11] << (22 - 16)) +
(pbData[12] >> (24 - 22));
/* Check the marker bits */
if ((pbData[12] & 0x03) != 0x03) {
DbgLog((LOG_ERROR, 1, TEXT("Invalid marker bits in pack header")));
return 4;
}

/* Check the stuffing bytes */
for (DWORD i = 14; i < dwLen; i++ ) {
if (pbData[i] != 0xFF) {
DbgLog((LOG_ERROR, 1, TEXT("Invalid pack stuffing byte 0x%2.2X"),
pbData[i]));
return 4;
}
}
DbgLog((LOG_TRACE, 4, TEXT("Decoded pack - ESCR %s, Mux Rate %d bytes/sec"),
(LPCTSTR)CDisp(llSCR, CDISP_DEC),
dwMuxRate * 50));

return dwLen;
}

/* Parse system header

Parameters :
pbData - pointer to data containing the system header
cbData - length of data

Returns :
Number of bytes processed (0 if the header doesn't fit
in the data or 4 if the header is invalid)
*/

LONG ParseSystemHeader(const BYTE * pbData, DWORD cbData)
{
BOOL bHasAudio = FALSE;
BOOL bHasVideo = FALSE;

ASSERT(cbData >= 4);
ASSERT(*(UNALIGNED DWORD *)pbData == DWORD_SWAP(SYSTEM_HEADER_START_CODE));

/* Checkt the length */
if (cbData < 6) {
return 0;
}

DWORD dwLen = 6 + pbData[5] + (pbData[4] << 8);
if (dwLen < SYSTEM_HEADER_BASIC_LENGTH) {
DbgLog((LOG_ERROR, 1, TEXT("Invalid System Header length")));
return 4;
}

if (cbData < dwLen) {
return 0;
}
/* Check the marker bits */
if (0 == (pbData[6] & 0x80) ||
0 == (pbData[8] & 0x01) ||
0 == (pbData[10] & 0x20)) {
DbgLog((LOG_ERROR, 1, TEXT("Invalid System Header Marker bits")));
return 4;
}

/* Parse the 'buffer bounds and scale' list */
const BYTE * pbNext = pbData + SYSTEM_HEADER_BASIC_LENGTH;
DWORD cbLeft = dwLen - SYSTEM_HEADER_BASIC_LENGTH;
for ( ; cbLeft >= 3; cbLeft -= 3, pbNext += 3) {
if (pbNext[0] == AUDIO_GLOBAL) {
bHasAudio == TRUE;
} else
if (pbNext[0] == VIDEO_GLOBAL) {
bHasVideo = TRUE;
} else {
if (pbNext[0] < PROGRAM_STREAM_MAP) {
DbgLog((LOG_ERROR, 1, TEXT("Invalid stream id in system header")));
return 4;
}
if (IsVideoStreamId(pbNext[0])) {
bHasVideo = TRUE;
} else
if (IsAudioStreamId(pbNext[0])) {
bHasAudio = TRUE;
}
}
}
if (cbLeft != 0) {
DbgLog((LOG_ERROR, 1, TEXT("Invalid system header length")));
return 4;
}
DbgLog((LOG_TRACE, 4, TEXT("System Header %s, %s"),
bHasAudio ? TEXT("audio") : TEXT("No audio"),
bHasVideo ? TEXT("video") : TEXT("No video")));
return dwLen;
}

/* Parse an MPEG-1 packet */
DWORD ParseMPEG1Packet(
const BYTE * pbData,
DWORD cbData,
MPEG_PACKET_DATA *pPacketData
)
{
ZeroMemory((PVOID)pPacketData, sizeof(*pPacketData));
DWORD dwStartCode = DWORD_SWAP(*(UNALIGNED DWORD *)pbData);
DbgLog((LOG_TRACE, 4, TEXT("Parse packet %d bytes"), cbData));
/* Send it to the right stream */
if (cbData < 6) {
return 0;
}

/* Find the length */
DWORD dwLen = ((LONG)pbData[4] << 8) + (LONG)pbData[5] + 6;
DbgLog((LOG_TRACE, 4, TEXT("Packet length %d bytes"), dwLen));
if (dwLen > cbData) {
return 0;
}
pPacketData->dwPacketLen = dwLen;

/* Pull out PTS if any */
DWORD dwHeaderSize = 6;

if (dwStartCode != PRIVATE_STREAM_2) {
DWORD dwPts = 6;
for (;;) {
if (dwPts >= dwLen) {
return 4;
}

if (pbData[dwPts] & 0x80) {
/* Stuffing byte */
if (pbData[dwPts] != 0xFF) {
return 4;
}
dwPts++;
continue;
}

/* Check for STD (nextbits == '01') -
we know the next bit is 0 so check the next one after that
*/
if (pbData[dwPts] & 0x40) { // STD stuff
dwPts += 2;
continue;
}

/* No PTS - normal case */
if (pbData[dwPts] == 0x0F) {
dwHeaderSize = dwPts + 1;
break;
}

if ((pbData[dwPts] & 0xF0) == 0x20 ||
(pbData[dwPts] & 0xF0) == 0x30) {


/* PTS or PTS and DTS */
dwHeaderSize = (pbData[dwPts] & 0xF0) == 0x20 ? dwPts + 5 :
dwPts + 10;
if (dwHeaderSize > dwLen) {
return 4;
}
if (!GetClock(pbData + dwPts, &pPacketData->llPts)) {
return 4;
}
pPacketData->bHasPts = TRUE;
break;
} else {
return 4;
break;
}
}
}
pPacketData->dwHeaderLen = dwHeaderSize;
return dwLen;
}

/* Parse an MPEG-2 packet */
DWORD ParseMPEG2Packet(
const BYTE * pbData,
DWORD cbData,
MPEG_PACKET_DATA *pPacketData)
{
ASSERT(cbData >= 4);
ASSERT(pbData[3] >= PROGRAM_STREAM_MAP);

ZeroMemory((PVOID)pPacketData, sizeof(*pPacketData));

if (cbData < 6) {
return 0;
}
DWORD dwLen = 6 + pbData[5] + (pbData[4] << 8);
if (cbData < dwLen + 4) {
return 0;
}

/* Check for normal */
switch (pbData[3]) {
case PROGRAM_STREAM_MAP:
case PRIVATE_STREAM_2:
case ECM_STREAM:
case EMM_STREAM:
case PROGRAM_STREAM_DIRECTORY:
case 0xF2: // DSMCC_Stream
case 0xF8: // H.222 Type E
case PADDING_STREAM:
/* Just the bytes */
pPacketData->dwHeaderLen = 6;
pPacketData->dwPacketLen = dwLen;
pPacketData->bHasPts = FALSE;
pPacketData->llPts = 0;
break;

/* PES header */
default:
{
if (!ParseMPEG2PacketHeader(pbData, dwLen, pPacketData)) {
return 4;
}
}
break;

}
return dwLen;
}

/* Parse a packet header when present */
BOOL ParseMPEG2PacketHeader(
const BYTE * pbData,
DWORD dwLen,
MPEG_PACKET_DATA *pPacketData
)
{
/* Compute the length */
if (dwLen < 6 + 3) {
return FALSE;
}

pPacketData->dwHeaderLen = 6 + 3 + pbData[8];
pPacketData->dwPacketLen = dwLen;

/* Extract the PTS */
if (MPEG2PacketHasPTS(pbData)) {
pPacketData->bHasPts = TRUE;
pPacketData->llPts = MPEG2PacketPTS(pbData);
} else {
pPacketData->bHasPts = FALSE;
pPacketData->llPts = 0;
}

return TRUE;
}

BOOL ParseSequenceHeader(const BYTE *pbData, LONG lData, SEQHDR_INFO *pInfo)
{
ASSERT(*(UNALIGNED DWORD *)pbData == DWORD_SWAP(SEQUENCE_HEADER_CODE));

/* Check random marker bit */
if (!(pbData[10] & 0x20)) {
DbgLog((LOG_ERROR, 2, TEXT("Sequence header invalid marker bit")));
return FALSE;
}

DWORD dwWidthAndHeight = ((DWORD)pbData[4] << 16) +
((DWORD)pbData[5] << 8) +
((DWORD)pbData[6]);

pInfo->lWidth = dwWidthAndHeight >> 12;
pInfo->lHeight = dwWidthAndHeight & 0xFFF;
DbgLog((LOG_TRACE, 2, TEXT("Width = %d, Height = %d"),
pInfo->lWidth,
pInfo->lHeight));

/* the '8' bit is the scramble flag used by sigma designs - ignore */
BYTE PelAspectRatioAndPictureRate = pbData[7];
if ((PelAspectRatioAndPictureRate & 0x0F) > 8) {
PelAspectRatioAndPictureRate &= 0xF7;
}
DbgLog((LOG_TRACE, 2, TEXT("Pel Aspect Ratio = %s"),
PelAspectRatios[PelAspectRatioAndPictureRate >> 4]));
DbgLog((LOG_TRACE, 2, TEXT("Picture Rate = %s"),
PictureRates[PelAspectRatioAndPictureRate & 0x0F]));

if ((PelAspectRatioAndPictureRate & 0xF0) == 0 ||
(PelAspectRatioAndPictureRate & 0x0F) == 0) {
DbgLog((LOG_ERROR, 2, TEXT("Sequence header invalid ratio/rate")));
return FALSE;
}

pInfo->tPictureTime = (LONGLONG)PictureTimes[PelAspectRatioAndPictureRate & 0x0F];
pInfo->fPictureRate = fPictureRates[PelAspectRatioAndPictureRate & 0x0F];
pInfo->lTimePerFrame = MulDiv((LONG)pInfo->tPictureTime, 9, 1000);

/* Pull out the bit rate and aspect ratio for the type */
pInfo->dwBitRate = ((((DWORD)pbData[8] << 16) +
((DWORD)pbData[9] << 8) +
(DWORD)pbData[10]) >> 6);
if (pInfo->dwBitRate == 0x3FFFF) {
DbgLog((LOG_TRACE, 2, TEXT("Variable video bit rate")));
pInfo->dwBitRate = 0;
} else {
pInfo->dwBitRate *= 400;
DbgLog((LOG_TRACE, 2, TEXT("Video bit rate is %d bits per second"),
pInfo->dwBitRate));
}

#if 0
#pragma message (REMIND("Get pel aspect ratio right don't call GDI - it will create a thread!"))
/* Get a DC */
HDC hdc = GetDC(GetDesktopWindow());

ASSERT(hdc != NULL);
/* Guess (randomly) 39.37 inches per meter */
LONG lNotionalPelsPerMeter = MulDiv((LONG)GetDeviceCaps(hdc, LOGICALPELSX),
3937, 100);
#else
LONG lNotionalPelsPerMeter = 2000;
#endif

pInfo->lXPelsPerMeter = lNotionalPelsPerMeter;

pInfo->lYPelsPerMeter = MulDiv(
lNotionalPelsPerMeter,
AspectRatios[PelAspectRatioAndPictureRate >> 4],
10000);
/* Pull out the vbv */
pInfo->lvbv = ((((LONG)pbData[10] & 0x1F) << 5) |
((LONG)pbData[11] >> 3)) * 2048;

DbgLog((LOG_TRACE, 2, TEXT("vbv size is %d bytes"), pInfo->lvbv));

/* Check constrained parameter stuff */
if (pbData[11] & 0x04) {
DbgLog((LOG_TRACE, 2, TEXT("Constrained parameter video stream")));

if (pInfo->lvbv > 40960) {
DbgLog((LOG_ERROR, 1, TEXT("Invalid vbv (%d) for Constrained stream"),
pInfo->lvbv));

/* Have to let this through too! bisp.mpg has this */
/* But constrain it since it might be random */
pInfo->lvbv = 40960;
}
} else {
DbgLog((LOG_TRACE, 2, TEXT("Non-Constrained parameter video stream")));
}

/* tp_orig has a vbv of 2048 (!) */
if (pInfo->lvbv < 20000) {
DbgLog((LOG_TRACE, 2, TEXT("Small vbv (%d) - setting to 40960"),
pInfo->lvbv));
pInfo->lvbv = 40960;
}

pInfo->lActualHeaderLen = SequenceHeaderSize(pbData);
CopyMemory((PVOID)pInfo->RawHeader, (PVOID)pbData, pInfo->lActualHeaderLen);
return TRUE;
}

HRESULT GetVideoMediaType(CMediaType *cmt, BOOL bPayload, const SEQHDR_INFO *pInfo)
{
cmt->majortype = MEDIATYPE_Video;
cmt->subtype = bPayload ? MEDIASUBTYPE_MPEG1Payload :
MEDIASUBTYPE_MPEG1Packet;
VIDEOINFO *videoInfo =
(VIDEOINFO *)cmt->AllocFormatBuffer(FIELD_OFFSET(MPEG1VIDEOINFO, bSequenceHeader[pInfo->lActualHeaderLen]));
if (videoInfo == NULL) {
return E_OUTOFMEMORY;
}
RESET_HEADER(videoInfo);

videoInfo->dwBitRate = pInfo->dwBitRate;
videoInfo->rcSource.right = pInfo->lWidth;
videoInfo->bmiHeader.biWidth = pInfo->lWidth;
videoInfo->rcSource.bottom = pInfo->lHeight;
videoInfo->bmiHeader.biHeight = pInfo->lHeight;
videoInfo->bmiHeader.biXPelsPerMeter = pInfo->lXPelsPerMeter;
videoInfo->bmiHeader.biYPelsPerMeter = pInfo->lYPelsPerMeter;
videoInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);

videoInfo->AvgTimePerFrame = pInfo->tPictureTime;
MPEG1VIDEOINFO *mpgvideoInfo = (MPEG1VIDEOINFO *)videoInfo;
mpgvideoInfo->cbSequenceHeader = pInfo->lActualHeaderLen;
CopyMemory((PVOID)mpgvideoInfo->bSequenceHeader,
(PVOID)pInfo->RawHeader,
pInfo->lActualHeaderLen);
mpgvideoInfo->dwStartTimeCode = pInfo->dwStartTimeCode;


cmt->SetFormatType(&FORMAT_MPEGVideo);
return S_OK;
}

BOOL CheckAudioHeader(const BYTE * pbData)
{
/* Just check it's valid */
#pragma message (REMIND("Check audio header"))
if ((pbData[2] & 0x0C) == 0x0C) {
DbgLog((LOG_ERROR, 2, TEXT("Invalid audio sampling frequency")));
return FALSE;
}
if ((pbData[1] & 0x08) != 0x08) {
DbgLog((LOG_ERROR, 2, TEXT("Invalid audio ID bit = 0")));
return FALSE;
}
if (((pbData[1] >> 1) & 3) == 0x00) {
DbgLog((LOG_ERROR, 2, TEXT("Invalid audio Layer")));
return FALSE;
}

if (((pbData[2] >> 2) & 3) == 3) {
DbgLog((LOG_ERROR, 2, TEXT("Invalid sample rate")));
return FALSE;
}
if ((pbData[2] >> 4) == 0x0F) {
DbgLog((LOG_ERROR, 2, TEXT("Invalid bit rate")));
return FALSE;
}

return TRUE;
}

LONG SampleRate(const BYTE * pbData)
{
switch ((pbData[2] >> 2) & 3) {
case 0:
return 44100;

case 1:
return 48000;

case 2:
return 32000;

default:
DbgBreak("Unexpected Sample Rate");
return 44100;
}
}

BOOL ParseAudioHeader(const BYTE * pbData, MPEG1WAVEFORMAT *pFormat)
{
if (!CheckAudioHeader(pbData)) {
return FALSE;
}
pFormat->wfx.wFormatTag = WAVE_FORMAT_MPEG;

/* Get number of channels from Mode */
switch (pbData[3] >> 6) {
case 0x00:
pFormat->fwHeadMode = ACM_MPEG_STEREO;
break;
case 0x01:
pFormat->fwHeadMode = ACM_MPEG_JOINTSTEREO;
break;
case 0x02:
pFormat->fwHeadMode = ACM_MPEG_DUALCHANNEL;
break;
case 0x03:
pFormat->fwHeadMode = ACM_MPEG_SINGLECHANNEL;
break;
}
pFormat->wfx.nChannels =
(WORD)(pFormat->fwHeadMode == ACM_MPEG_SINGLECHANNEL ? 1 : 2);
pFormat->fwHeadModeExt = (WORD)(1 << (pbData[3] >> 4));
pFormat->wHeadEmphasis = (WORD)((pbData[3] & 0x03) + 1);
pFormat->fwHeadFlags = (WORD)(((pbData[2] & 1) ? ACM_MPEG_PRIVATEBIT : 0) +
((pbData[3] & 8) ? ACM_MPEG_COPYRIGHT : 0) +
((pbData[3] & 4) ? ACM_MPEG_ORIGINALHOME : 0) +
((pbData[1] & 1) ? ACM_MPEG_PROTECTIONBIT : 0) +
((pbData[1] & 0x08) ? ACM_MPEG_ID_MPEG1 : 0));

int Layer;

/* Get the layer so we can work out the bit rate */
switch ((pbData[1] >> 1) & 3) {
case 3:
pFormat->fwHeadLayer = ACM_MPEG_LAYER1;
Layer = 1;
break;
case 2:
pFormat->fwHeadLayer = ACM_MPEG_LAYER2;
Layer = 2;
break;
case 1:
pFormat->fwHeadLayer = ACM_MPEG_LAYER3;
Layer = 3;
break;
case 0:
return (FALSE);
}

/* Get samples per second from sampling frequency */
pFormat->wfx.nSamplesPerSec = SampleRate(pbData);
pFormat->dwHeadBitrate =
(DWORD)BitRates[Layer - 1][pbData[2] >> 4] * 1000;
pFormat->wfx.nAvgBytesPerSec = pFormat->dwHeadBitrate / 8;

/* Deal with free format (!) */
#pragma message (REMIND("Handle variable bit rate (index 0)"))

if (pFormat->wfx.nSamplesPerSec != 44100 &&
/* Layer 3 can sometimes switch bitrates */
!(Layer == 3 && /* !m_pStreamList->AudioLock() && */
(pbData[2] >> 4) == 0)) {

if (Layer == 1) {
pFormat->wfx.nBlockAlign = (WORD)
(4 * ((pFormat->dwHeadBitrate * 12) / pFormat->wfx.nSamplesPerSec));
} else {
pFormat->wfx.nBlockAlign = (WORD)
((144 * pFormat->dwHeadBitrate) / pFormat->wfx.nSamplesPerSec);
}
} else {
pFormat->wfx.nBlockAlign = 1;
}


pFormat->wfx.wBitsPerSample = 0;
pFormat->wfx.cbSize = sizeof(MPEG1WAVEFORMAT) - sizeof(WAVEFORMATEX);

pFormat->dwPTSLow = 0;
pFormat->dwPTSHigh = 0;

return TRUE;
}

BOOL GetClock(const BYTE * pData, LONGLONG *Clock)
{
BYTE Byte1 = pData[0];
DWORD Word2 = ((DWORD)pData[1] << 8) + (DWORD)pData[2];
DWORD Word3 = ((DWORD)pData[3] << 8) + (DWORD)pData[4];

/* Do checks */
if ((Byte1 & 0xE0) != 0x20 ||
(Word2 & 1) != 1 ||
(Word3 & 1) != 1) {
DbgLog((LOG_TRACE, 2, TEXT("Invalid clock field - 0x%2.2X 0x%4.4X 0x%4.4X"),
Byte1, Word2, Word3));
return FALSE;
}

LARGE_INTEGER liClock;
liClock.HighPart = (Byte1 & 8) != 0;
liClock.LowPart = (DWORD)((((DWORD)Byte1 & 0x6) << 29) +
(((DWORD)Word2 & 0xFFFE) << 14) +
((DWORD)Word3 >> 1));

*Clock = liClock.QuadPart;

return TRUE;
}

/* Find the next start code */
BOOL NextStartCode(const BYTE * *ppbData, DWORD *pdwLeft)
{
const BYTE * pbData = *ppbData;
DWORD dwLeft = *pdwLeft;

while (dwLeft > 4 &&
(*(UNALIGNED DWORD *)pbData & 0x00FFFFFF) != 0x00010000) {
dwLeft--;
pbData++;
}
*ppbData = pbData;
*pdwLeft = dwLeft;
return dwLeft >= 4;
}

/* Parse AC3 header */
BOOL ParseAC3Header(const BYTE * pbData, DOLBYAC3WAVEFORMAT *pwf)
{
ZeroMemory((PVOID)pwf, sizeof(*pwf));
pwf->wfx.cbSize = sizeof(DOLBYAC3WAVEFORMAT) - sizeof(WAVEFORMATEX);

/* First check it IS AC3 */
if (pbData[0] == 0x77 && pbData[1] == 0x0B) {
} else
if (pbData[0] == 0x0B && pbData[1] == 0x77) {
pwf->bBigEndian = TRUE;
} else {
return FALSE;
}

/* Get the sampling rate */
BYTE bData = pwf->bBigEndian ? pbData[4] : pbData[5];
int SampleRateCode = bData >> 6;
int BitRateCode = bData & 0x3F;
switch (SampleRateCode) {
case 1:
pwf->wfx.nSamplesPerSec = 44100;
break;
case 2:
pwf->wfx.nSamplesPerSec = 32000;
break;

default: // Isn't one illegal?
pwf->wfx.nSamplesPerSec = 48000;
break;
}

if (BitRateCode >= 38) {
return FALSE;
}

/* Get the bit rate */
int BitRates[] =
{
32, 32, 40, 40, 48, 48, 56, 56, 64, 64,
80, 80, 96, 96, 112, 112, 128, 128, 160, 160,
192, 192, 224, 224, 256, 256, 320, 320, 384, 384,
448, 448, 512, 512, 576, 576, 640, 640
};
pwf->wfx.nAvgBytesPerSec = BitRates[BitRateCode] * (1000 / 8);

/* Get the frame size
Since every frame contains 1536 samples we can compute it as

Bypes per frame = Samples per frame (1536) * Bytes per second
--------------------------------------------
Samples per second
*/
pwf->wfx.nBlockAlign = MulDiv(1536,
pwf->wfx.nAvgBytesPerSec,
pwf->wfx.nSamplesPerSec);

pwf->wfx.nChannels = 6; // Hack!
pwf->wfx.wFormatTag = WAVE_FORMAT_DOLBY_AC3;

DbgLog((LOG_TRACE, 3, TEXT("Wave Format Dolby AC3")));
DbgLog((LOG_TRACE, 3, TEXT("%d bytes per sec, %d samples per sec, align %d"),
pwf->wfx.nAvgBytesPerSec,
pwf->wfx.nSamplesPerSec,
pwf->wfx.nBlockAlign));

return TRUE;
}

/* Return the PTS clock value as a 33 bit number
Returns LONGLONG(-1) if the clock is invalid
*/
LONGLONG MPEG2PacketPTS(const BYTE * pbPacket)
{
ASSERT(MPEG2PacketHasPTS(pbPacket));
LONGLONG llClock;
if (!GetClock(pbPacket + 9, &llClock)) {
return (LONGLONG)-1;
}
return llClock;
}