MPEG1.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. 
// 
//--------------------------------------------------------------------------; 
 
#include <streams.h> 
#include <pullpin.h> 
#include <mmreg.h> 
#include <mpeg2typ.h> 
#include <alloc.h> 
#include <splitter.h> 
#include <mpegdef.h> 
#include <mpgutil.h> 
#include <mpeg1.h> 
#include <initguid.h> 
//  f28300a0-f0cc-11cf-93d5-0080c795857f 
DEFINE_GUID(CLSID_MPEG1Sample, 
    0xf28300a0, 0xf0cc, 0x11cf, 0x93, 0xd5, 0x00, 0x80, 0xc7, 0x95, 0x85, 0x7f); 
 
 
/*  Stuff to make this into a filter */ 
/* List of class IDs and creator functions for the class factory. This 
   provides the link between the OLE entry point in the DLL and an object 
   being created. The class factory will call the static CreateInstance 
   function when it is asked to create a CLSID_MPEG1Splitter object */ 
 
CFactoryTemplate g_Templates[1] = { 
    {L"MPEG1 Sample", &CLSID_MPEG1Sample, CMPEG1SplitterFilter::CreateInstance} 
}; 
 
int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]); 
 
 
/* This goes in the factory template table to create new instances */ 
 
CUnknown *CMPEG1SplitterFilter::CreateInstance(LPUNKNOWN pUnk, HRESULT *phr) 
{ 
    CUnknown *pUnkRet = new CMPEG1SplitterFilter(pUnk, phr); 
    return pUnkRet; 
} 
 
/*  Registration setup stuff */ 
//  Setup data 
 
AMOVIESETUP_MEDIATYPE sudMpgInputType[] = 
{ 
    { &MEDIATYPE_Stream, &MEDIASUBTYPE_MPEG1System } 
}; 
AMOVIESETUP_MEDIATYPE sudMpgAudioOutputType[] = 
{ 
    { &MEDIATYPE_Audio, &MEDIASUBTYPE_MPEG1AudioPayload } 
}; 
AMOVIESETUP_MEDIATYPE sudMpgVideoOutputType[] = 
{ 
    { &MEDIATYPE_Video, &MEDIASUBTYPE_MPEG1Payload } 
}; 
 
AMOVIESETUP_PIN sudMpgPins[3] = 
{ 
    { L"Input", 
      FALSE,                               // bRendered 
      FALSE,                               // bOutput 
      FALSE,                               // bZero 
      FALSE,                               // bMany 
      &CLSID_NULL,                         // clsConnectsToFilter 
      NULL,                                // ConnectsToPin 
      NUMELMS(sudMpgInputType),            // Number of media types 
      sudMpgInputType 
    }, 
    { L"Audio Output", 
      FALSE,                               // bRendered 
      TRUE,                                // bOutput 
      TRUE,                                // bZero 
      FALSE,                               // bMany 
      &CLSID_NULL,                         // clsConnectsToFilter 
      NULL,                                // ConnectsToPin 
      NUMELMS(sudMpgAudioOutputType),      // Number of media types 
      sudMpgAudioOutputType 
    }, 
    { L"Video Output", 
      FALSE,                               // bRendered 
      TRUE,                                // bOutput 
      TRUE,                                // bZero 
      FALSE,                               // bMany 
      &CLSID_NULL,                         // clsConnectsToFilter 
      NULL,                                // ConnectsToPin 
      NUMELMS(sudMpgVideoOutputType),      // Number of media types 
      sudMpgVideoOutputType 
    } 
}; 
 
AMOVIESETUP_FILTER sudMpgsplit = 
{ 
    &CLSID_MPEG1Sample, 
    L"MPEG1 Splitter Sample", 
    0,                                     // Don't use us for real! 
    NUMELMS(sudMpgPins),                   // 3 pins 
    sudMpgPins 
}; 
 
 
// 
// DllRegisterSever 
// 
// Handle the registration of this filter 
// 
STDAPI DllRegisterServer() 
{ 
    return AMovieDllRegisterServer(); 
 
} // DllRegisterServer 
 
//  Constructor and destructor 
CMPEG1SplitterFilter::CMPEG1SplitterFilter( 
   LPUNKNOWN pUnk, 
   HRESULT *phr) : 
   CBaseSplitterFilter( 
       TEXT("CMPEG1SplitterFilter"), 
       pUnk, 
       CLSID_MPEG1Sample, 
       phr) 
{ 
    //  Create our input pin 
    m_pInput = new CSplitterInputPin(this, phr); 
} 
//  Override type checking 
HRESULT CMPEG1SplitterFilter::CheckInputType(const CMediaType *pmt) 
{ 
    /*  We'll accept our preferred type or a wild card for the subtype */ 
 
    if (pmt->majortype != MEDIATYPE_Stream || 
        pmt->subtype != MEDIASUBTYPE_MPEG1System && 
        pmt->subtype != MEDIASUBTYPE_NULL) { 
        return S_FALSE; 
    } else { 
        return S_OK; 
    } 
} 
 
LPAMOVIESETUP_FILTER CMPEG1SplitterFilter::GetSetupData() 
{ 
    return &sudMpgsplit; 
} 
 
/*  Complete connection and instantiate parser 
    This involves: 
 
    Instatiate the parser with for the type and have it check the format 
*/ 
 
CBaseParser *CMPEG1SplitterFilter::CreateParser( 
    CParserNotify *pNotify, 
    CMediaType *pType 
) 
{ 
    HRESULT hr = S_OK; 
    return new CMPEG1Parser(pNotify, &hr); 
} 
 
/*  Cheap'n nasty parser - DON'T do yours like this! */ 
/*  Initialize a parser 
 
    pmt     - type of stream if known - can be NULL 
    pRdr    - way to read the source medium - can be NULL 
*/ 
HRESULT CMPEG1Parser::Init(CParseReader *pRdr) 
{ 
    const DWORD dwLen = 65536; 
    /*  Just read 32K and look for interesting stuff */ 
    PBYTE pbData = new BYTE[dwLen]; 
    if (pbData == NULL) { 
        return E_OUTOFMEMORY; 
    } 
    HRESULT hr = pRdr->Read(pbData, dwLen); 
    if (S_OK != hr) { 
        delete [] pbData; 
        return hr; 
    } 
 
    /*  Now just loop looking for start codes */ 
    DWORD dwLeft = dwLen; 
    PBYTE pbCurrent = pbData; 
    while (dwLeft >= 140) { 
        DWORD dwCode = *(UNALIGNED DWORD *)pbCurrent; 
 
        /*  Check if it's a valid start code */ 
        if ((dwCode & 0x00FFFFFF) == 0x00010000) { 
            dwCode = DWORD_SWAP(dwCode); 
            if (VALID_PACKET(dwCode)){ 
                MPEG_PACKET_DATA Info; 
                DWORD dwPacketLen = ParseMPEG1Packet( 
                                        pbCurrent, 
                                        dwLeft, 
                                        &Info); 
                if (dwPacketLen > 4) { 
                    if (!m_bGotFirstPts && Info.bHasPts) { 
                        m_llFirstPts = Info.llPts; 
                        m_bGotFirstPts = TRUE; 
                    } 
                    if (IsVideoStreamId((BYTE)(dwCode & 0xFF))) { 
                        m_Video.m_uStreamId = (BYTE)(dwCode & 0xFF); 
                        const BYTE * pbHeader = pbCurrent + Info.dwHeaderLen; 
                        SEQHDR_INFO seqInfo; 
 
                        /*  Create our format block */ 
                        if (DWORD_SWAP(SEQUENCE_HEADER_CODE) == 
                            *(UNALIGNED DWORD *)pbHeader && 
                            ParseSequenceHeader(pbHeader, dwLeft, &seqInfo)) { 
                            /*  Create the media type from the stream 
                                only support Payload streams for now 
                            */ 
                            CMediaType cmt; 
                            GetVideoMediaType(&cmt, TRUE, &seqInfo); 
 
                            /*  Create our video stream */ 
                            m_pNotify->CreateStream(L"Video", &m_Video.m_pNotify); 
                            m_Video.m_pNotify->AddMediaType(&cmt); 
                        } else { 
                        } 
                    } else if (IsAudioStreamId((BYTE)(dwCode & 0xFF))) { 
                        m_Audio.m_uStreamId = (BYTE)(dwCode & 0xFF); 
 
                        /*  Now let's hope the first 2 bytes are a frame 
                            start ! 
                        */ 
                        MPEG1WAVEFORMAT wf; 
                        if (ParseAudioHeader(pbCurrent + Info.dwHeaderLen, 
                                             &wf)) { 
                            CMediaType cmt(&MEDIATYPE_Audio); 
                            cmt.subtype = MEDIASUBTYPE_MPEG1AudioPayload; 
                            cmt.SetFormat((PBYTE)&wf, sizeof(wf)); 
                            cmt.SetFormatType(&FORMAT_WaveFormatEx); 
                            m_Audio.m_uStreamId = (BYTE)(dwCode & 0xFF); 
                            m_pNotify->CreateStream( 
                                L"Audio", 
                                &m_Audio.m_pNotify); 
                            m_Audio.m_pNotify->AddMediaType(&cmt); 
                        } 
                    } 
                } 
                ASSERT(dwLeft >= dwPacketLen); 
                pbCurrent += dwPacketLen; 
                dwLeft -= dwPacketLen; 
                continue; 
            } 
        } 
 
        /*  Hideously inefficient! */ 
        pbCurrent++; 
        dwLeft--; 
    } 
    delete [] pbData; 
    if (!m_bGotFirstPts || (!m_Audio.Initialized() && !m_Video.Initialized())) { 
        return VFW_E_TYPE_NOT_ACCEPTED; 
    } else { 
        return S_OK; 
    } 
} 
 
 
/*  Get the size and count of buffers preferred based on the 
    actual content 
*/ 
void CMPEG1Parser::GetSizeAndCount(LONG *plSize, LONG *plCount) 
{ 
    *plSize = 32768; 
    *plCount = 4; 
} 
 
/*  Call this to reinitialize for a new stream */ 
void CMPEG1Parser::StreamReset() 
{ 
} 
 
/*  Call this to pass new stream data : 
 
    pbData        - pointer to data 
    lData         - length of data 
    plProcessed   - Amount of data consumed 
*/ 
HRESULT CMPEG1Parser::Process( 
    const BYTE * pbData, 
    LONG lData, 
    LONG *plProcessed 
) 
{ 
    /*  Just loop processing packets until we run out of data 
        We should do a lot more to sync up than just eat a start 
        code ! 
    */ 
 
    DWORD dwLeft = lData; 
    const BYTE * pbCurrent = pbData; 
 
    while (dwLeft > 4) { 
        /*  Find a start code */ 
        DWORD dwCode = DWORD_SWAP(*(UNALIGNED DWORD *)pbCurrent); 
        MPEG_PACKET_DATA Info; 
        if (VALID_PACKET(dwCode)) { 
            DWORD dwPacketLen = ParseMPEG1Packet(pbCurrent, dwLeft, &Info); 
            if (dwPacketLen == 0) { 
                break; 
            } 
            if (dwPacketLen == 4) { 
                dwLeft -= 4; 
                pbCurrent += 4; 
                continue; 
            } 
            /*  If it's a packet we're interested in send it on */ 
            CStream *pStream = NULL; 
            if (((BYTE)(dwCode & 0xFF)) == m_Audio.m_uStreamId) { 
                pStream = &m_Audio; 
            } else 
            if (((BYTE)(dwCode & 0xFF)) == m_Video.m_uStreamId) { 
                pStream = &m_Video; 
            } 
            if (pStream != NULL) { 
                DbgLog((LOG_TRACE, 3, TEXT("Send sample for stream %2.2X"), 
                        pStream->m_uStreamId)); 
                pStream->m_pNotify->SendSample( 
                    pbCurrent + Info.dwHeaderLen, 
                    Info.dwPacketLen - Info.dwHeaderLen, 
                    Info.bHasPts ? TimeStamp(Info.llPts) : 0, 
                    Info.bHasPts); 
            } 
            dwLeft -= Info.dwPacketLen; 
            pbCurrent += Info.dwPacketLen; 
        } else { 
            dwLeft--; 
            pbCurrent++; 
        } 
    } 
    *plProcessed = lData - dwLeft; 
    return S_OK; 
}