//==========================================================================; // // 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 - 1998 Microsoft Corporation. All Rights Reserved. // //--------------------------------------------------------------------------; /* MPEG 1 & 2 parsing routines */ #include #include #include #include #include #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; }