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

1664 lines
42 KiB
C++

//------------------------------------------------------------------------------
// File: SampleCGB.cpp
//
// Desc: DirectShow sample code - Sample capture graph builder class
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------------------------
#include "stdafx.h"
#ifndef __STREAMS__
#define __STREAMS__
#endif
#include "ks.h"
#include "ksproxy.h"
#include "SampleCGB.h"
//
// ISampleCaptureGraphBuilder is trying to connect the video pin to the MPEG2 demux.
// The possible configurations are:
//
//
//
// 1. capture filter- > audio / video encoder & multiplexer -> MPEG2 demux
//
// ---------------------- --------------------------------------- ---------------
// | v| ---> | | | |
// | Capture Filter | | Audio / video encoder & Multiplexer | ->| MPEG2 demux |
// | a| ---> | | | |
// ---------------------- --------------------------------------- ---------------
//
//
//
//
//
// -> audio encoder ->
// 2. capture filter multiplexer -> MPEG2 demux
// -> video encoder ->
//
// ----------------------- ----------------- ---------------- ---------------
// | a| -> | audio encoder | -> | | | |
// | | ----------------- | | | |
// | capture filter | | multiplexer | -> | MPEG2 demux |
// | | ----------------- | | | |
// | v| -> | video encoder | -> | | | |
// ----------------------- ----------------- ---------------- ---------------
//
//
//
//
//
// a
// 3. capture filter - audio & video encoder -> multiplexer -> MPEG2 demux
// v
//
// ----------------------- ----------------- ---------------- ---------------
// | a| -> | audio encoder | -> | | | |
// | | | | | | | |
// | capture filter | | | | multiplexer | -> | MPEG2 demux |
// | | | | | | | |
// | v| -> | video encoder | -> | | | |
// ----------------------- ----------------- ---------------- ---------------
//
//
//
//
// 4. capture filter -> MPEG2 demux
//
// ------------------------------ ----------------
// | MPEG2 PS | ---> | |
// | Capture Filter | | MPEG2 demux |
// | | | |
// ------------------------------ ----------------
//
//
//
// How the algorithm works:
// 1. the video pin doesn't stream MPEG2
// 1. tries to connect the pin to an encoder
// 2. tries to connect the encoder directly to the MPEG2 demux
// 3. if not possible, tries to find a multiplexor that can be connected to the
// encoder and MPEG2 demux
// 4. connect audio pin to the MPEG2 demux using the same algorithm as in
// video pin case
//
// 2. if pin streams MPEG2 PS, then connect it to the MPEG2 demux
// 3. program the MPEG2 demux
// 4. render the video and the audio pin from the MPEG2 demux
//
static
BYTE
Mpeg2ProgramVideo [] = {
0x00, 0x00, 0x00, 0x00, // .hdr.rcSource.left = 0x00000000
0x00, 0x00, 0x00, 0x00, // .hdr.rcSource.top = 0x00000000
0xD0, 0x02, 0x00, 0x00, // .hdr.rcSource.right = 0x000002d0
0xE0, 0x01, 0x00, 0x00, // .hdr.rcSource.bottom = 0x000001e0
0x00, 0x00, 0x00, 0x00, // .hdr.rcTarget.left = 0x00000000
0x00, 0x00, 0x00, 0x00, // .hdr.rcTarget.top = 0x00000000
0x00, 0x00, 0x00, 0x00, // .hdr.rcTarget.right = 0x00000000
0x00, 0x00, 0x00, 0x00, // .hdr.rcTarget.bottom = 0x00000000
0x00, 0x09, 0x3D, 0x00, // .hdr.dwBitRate = 0x003d0900
0x00, 0x00, 0x00, 0x00, // .hdr.dwBitErrorRate = 0x00000000
0x63, 0x17, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, // .hdr.AvgTimePerFrame = 0x0000000000051763
0x00, 0x00, 0x00, 0x00, // .hdr.dwInterlaceFlags = 0x00000000
0x00, 0x00, 0x00, 0x00, // .hdr.dwCopyProtectFlags = 0x00000000
0x04, 0x00, 0x00, 0x00, // .hdr.dwPictAspectRatioX = 0x00000004
0x03, 0x00, 0x00, 0x00, // .hdr.dwPictAspectRatioY = 0x00000003
0x00, 0x00, 0x00, 0x00, // .hdr.dwReserved1 = 0x00000000
0x00, 0x00, 0x00, 0x00, // .hdr.dwReserved2 = 0x00000000
0x28, 0x00, 0x00, 0x00, // .hdr.bmiHeader.biSize = 0x00000028
0xD0, 0x02, 0x00, 0x00, // .hdr.bmiHeader.biWidth = 0x000002d0
0xE0, 0x01, 0x00, 0x00, // .hdr.bmiHeader.biHeight = 0x00000000
0x00, 0x00, // .hdr.bmiHeader.biPlanes = 0x0000
0x00, 0x00, // .hdr.bmiHeader.biBitCount = 0x0000
0x00, 0x00, 0x00, 0x00, // .hdr.bmiHeader.biCompression = 0x00000000
0x00, 0x00, 0x00, 0x00, // .hdr.bmiHeader.biSizeImage = 0x00000000
0xD0, 0x07, 0x00, 0x00, // .hdr.bmiHeader.biXPelsPerMeter = 0x000007d0
0x27, 0xCF, 0x00, 0x00, // .hdr.bmiHeader.biYPelsPerMeter = 0x0000cf27
0x00, 0x00, 0x00, 0x00, // .hdr.bmiHeader.biClrUsed = 0x00000000
0x00, 0x00, 0x00, 0x00, // .hdr.bmiHeader.biClrImportant = 0x00000000
0x98, 0xF4, 0x06, 0x00, // .dwStartTimeCode = 0x0006f498
0x56, 0x00, 0x00, 0x00, // .cbSequenceHeader = 0x00000056
0x02, 0x00, 0x00, 0x00, // .dwProfile = 0x00000002
0x02, 0x00, 0x00, 0x00, // .dwLevel = 0x00000002
0x00, 0x00, 0x00, 0x00, // .Flags = 0x00000000
// .dwSequenceHeader [1]
0x00, 0x00, 0x01, 0xB3, 0x2D, 0x01, 0xE0, 0x24,
0x09, 0xC4, 0x23, 0x81, 0x10, 0x11, 0x11, 0x12,
0x12, 0x12, 0x13, 0x13, 0x13, 0x13, 0x14, 0x14,
0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, 0x15,
0x15, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
0x18, 0x18, 0x18, 0x19, 0x18, 0x18, 0x18, 0x19,
0x1A, 0x1A, 0x1A, 0x1A, 0x19, 0x1B, 0x1B, 0x1B,
0x1B, 0x1B, 0x1C, 0x1C, 0x1C, 0x1C, 0x1E, 0x1E,
0x1E, 0x1F, 0x1F, 0x21, 0x00, 0x00, 0x01, 0xB5,
0x14, 0x82, 0x00, 0x01, 0x00, 0x00
} ;
static
BYTE
MPEG1AudioFormat [] = {
0x50, 0x00, 0x02, 0x00, 0x80, 0xBB, 0x00, 0x00,
0x00, 0x7D, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
0x16, 0x00, 0x02, 0x00, 0x00, 0xE8, 0x03, 0x00,
0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x1C, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
} ;
HRESULT
ISampleCaptureGraphBuilder::AllocCapFile( LPCOLESTR lpwstr, DWORDLONG dwlSize )
{
return graphBuilder2_->AllocCapFile( lpwstr, dwlSize );
}
HRESULT
ISampleCaptureGraphBuilder::ControlStream( const GUID *pCategory,
const GUID *pType,
IBaseFilter *pFilter,
REFERENCE_TIME *pstart,
REFERENCE_TIME *pstop,
WORD wStartCookie,
WORD wStopCookie )
{
return graphBuilder2_->ControlStream( pCategory, pType, pFilter,
pstart, pstop, wStartCookie, wStopCookie );
}
HRESULT
ISampleCaptureGraphBuilder::CopyCaptureFile( LPOLESTR lpwstrOld,
LPOLESTR lpwstrNew,
int fAllowEscAbort,
IAMCopyCaptureFileProgress *pCallback)
{
return graphBuilder2_->CopyCaptureFile( lpwstrOld, lpwstrNew,
fAllowEscAbort, pCallback );
}
HRESULT ISampleCaptureGraphBuilder::FindInterface(const GUID *pCategory,
const GUID *pType,
IBaseFilter *pf,
REFIID riid,
void **ppint
)
{
return graphBuilder2_->FindInterface( pCategory, pType, pf, riid, ppint );
}
HRESULT
ISampleCaptureGraphBuilder::FindPin( IUnknown *pSource,
PIN_DIRECTION pindir,
const GUID *pCategory,
const GUID *pType,
BOOL fUnconnected,
int num,
IPin **ppPin)
{
return graphBuilder2_->FindPin( pSource, pindir, pCategory, pType,
fUnconnected, num, ppPin );
}
HRESULT ISampleCaptureGraphBuilder::GetFiltergraph( IGraphBuilder **ppfg )
{
return graphBuilder2_->GetFiltergraph( ppfg );
}
HRESULT
ISampleCaptureGraphBuilder::RenderStream( const GUID *pCategory,
const GUID *pType,
IUnknown *pSource,
IBaseFilter *pIntermediate,
IBaseFilter *pSink)
{
if( !pType || !::IsEqualGUID( MEDIATYPE_Stream, *pType ) )
{
return graphBuilder2_->RenderStream( pCategory, pType, pSource,
pIntermediate, pSink );
}
HRESULT hr;
if( !graph_ )
{
hr = GetFiltergraph( &graph_ );
if( FAILED( hr ) )
{
return hr;
}
}
//
// try to build MPEG2 graph
//
SmartPtr< IBaseFilter > captureFilter;
hr = pSource->QueryInterface( & captureFilter );
if( FAILED( hr ) )
{
return E_INVALIDARG;
}
hr = BuildMPEG2Segment( captureFilter);
if( pSink || FAILED( hr ) )
{
return hr;
}
hr = ConfigureMPEG2Demux( pMPEG2Demux_ );
if( FAILED( hr ) )
{
return hr;
}
hr = RenderStream(NULL, &MEDIATYPE_Video, pMPEG2Demux_, NULL, NULL );
if( FAILED( hr ) )
{
return hr;
}
hr = RenderStream(NULL, &MEDIATYPE_Audio, pMPEG2Demux_, NULL, NULL );
if( FAILED( hr ) )
{
return hr;
}
return S_OK;
}
HRESULT
ISampleCaptureGraphBuilder::SetFiltergraph( IGraphBuilder *pfg )
{
return graphBuilder2_->SetFiltergraph( pfg );
}
HRESULT
ISampleCaptureGraphBuilder::SetOutputFileName( const GUID *pType,
LPCOLESTR lpwstrFile,
IBaseFilter **ppf,
IFileSinkFilter **pSink )
{
if( ! pType || ! lpwstrFile || !ppf || !pSink )
{
return E_INVALIDARG;
}
if( !::IsEqualGUID( *pType, MEDIASUBTYPE_Mpeg2 ) )
{
return graphBuilder2_->SetOutputFileName(pType, lpwstrFile, ppf, pSink );
}
HRESULT hr;
if( !graph_ )
{
hr = GetFiltergraph( &graph_ );
if( FAILED( hr ) )
{
return hr;
}
}
//
// Configure the dump filter
//
SmartPtr< IFileSinkFilter > pDump;
hr = CoCreateInstance(CLSID_Dump, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&pDump);
if( FAILED( hr ) )
{
return hr;
}
hr = pDump->SetFileName( lpwstrFile, NULL );
if( FAILED( hr ) )
{
return hr;
}
hr = pDump.QueryInterface( &pMPEG2Demux_ );
if( FAILED( hr ) )
{
return hr;
}
hr = graph_->AddFilter( pMPEG2Demux_, L"Dump" );
if( FAILED( hr ) )
{
pMPEG2Demux_ = NULL;
return hr;
}
*pSink = pDump;
return S_OK;
}
//
// A device can stream directly MPEG2 stream,
// or it can be linked with a video codec and multiplexer
//
//
//
// Loop through every media type supported by this pin
// to see if there is one which can be considered MPEG2
//
BOOL ISampleCaptureGraphBuilder::IsMPEG2Pin( IPin *pPin )
{
if( !pPin )
{
return FALSE; // NULL pointer
}
SmartPtr<IEnumMediaTypes> pMediaTypes;
HRESULT hr = pPin->EnumMediaTypes( &pMediaTypes );
if( FAILED( hr ) )
{
return FALSE;
}
hr = pMediaTypes->Reset();
if( FAILED( hr ) )
{
return FALSE;
}
ULONG fetched;
AM_MEDIA_TYPE *mediaType;
while( S_OK == pMediaTypes->Next( 1, &mediaType, &fetched ) )
{
if(
(
::IsEqualGUID( mediaType->majortype, MEDIATYPE_Video ) ||
::IsEqualGUID( mediaType->majortype, MEDIATYPE_Stream )
)
&&
(
::IsEqualGUID( mediaType->subtype, MEDIASUBTYPE_MPEG2_VIDEO ) ||
::IsEqualGUID( mediaType->subtype, MEDIASUBTYPE_MPEG2_PROGRAM )
)
)
{
DeleteMediaType( mediaType );
return TRUE;
}
DeleteMediaType( mediaType );
}
return FALSE;
}
BOOL ISampleCaptureGraphBuilder::IsVideoPin( IPin *pPin )
{
return HasMediaType( pPin, MEDIATYPE_Video );
}
HRESULT ISampleCaptureGraphBuilder::GetEncodersByCategory( IEnumMoniker **ppEncoders )
{
SmartPtr<ICreateDevEnum> pDeviceEnum;
HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void**)&pDeviceEnum);
if( FAILED( hr ) )
{
return hr;
}
return pDeviceEnum->CreateClassEnumerator( KSCATEGORY_ENCODER, ppEncoders, 0 );
}
HRESULT
ISampleCaptureGraphBuilder::GetEncodersByEnumerating(
IPin *pPin,
const REGPINMEDIUM& pinMedium,
IEnumMoniker **ppEncoders )
{
SmartPtr<IFilterMapper2> pFilterMapper2;
HRESULT hr = CoCreateInstance(CLSID_FilterMapper2, NULL, CLSCTX_INPROC_SERVER, IID_IFilterMapper2, (void**)&pFilterMapper2);
if( FAILED( hr ) )
{
return hr;
}
hr = pFilterMapper2->EnumMatchingFilters(
ppEncoders,
NULL,
FALSE,
0, //any merit
TRUE,
0,
NULL,
&pinMedium,
NULL,
FALSE,
TRUE,
0,
NULL,
NULL,
NULL );
return hr;
}
//
// looks for an MPEG2 pin
//
HRESULT
ISampleCaptureGraphBuilder::FindMPEG2Pin(
IBaseFilter *pFilter,
IPin **ppPin )
{
if( !pFilter )
{
return E_POINTER;
}
SmartPtr<IEnumPins> pEnumPins;
HRESULT hr = pFilter->EnumPins( &pEnumPins );
if( FAILED( hr ) )
{
return hr;
}
SmartPtr<IPin> pTempPin;
ULONG fetched;
PIN_DIRECTION dir;
hr = pEnumPins->Reset( );
while( pTempPin.Release(), S_OK == pEnumPins->Next( 1, &pTempPin, &fetched ) )
{
hr = pTempPin->QueryDirection( &dir );
if( FAILED( hr ) || PINDIR_INPUT == dir )
{
continue;
}
if( IsMPEG2Pin( pTempPin ) )
{
(*ppPin) = pTempPin.Detach();
return S_OK;
}
}
return E_FAIL;
}
//
// search the encoder that has this special medium
// video == TRUE -- look for a video pin
// video == FALSE -- look for a audio pin
//
HRESULT ISampleCaptureGraphBuilder::FindPin(
IBaseFilter *pFilter,
const REGPINMEDIUM& regPinMedium,
PIN_DIRECTION direction,
BOOL video,
IPin **ppPin)
{
if( !pFilter )
{
return E_POINTER;
}
SmartPtr<IEnumPins> pEnumPins;
HRESULT hr = pFilter->EnumPins( &pEnumPins );
if( FAILED( hr ) )
{
return hr;
}
SmartPtr<IPin> pTempPin;
ULONG fetched;
REGPINMEDIUM regPinMediumTemp;
PIN_DIRECTION dir;
hr = pEnumPins->Reset( );
while( pTempPin.Release(), S_OK == pEnumPins->Next( 1, &pTempPin, &fetched ) )
{
ASSERT( pTempPin );
hr = pTempPin->QueryDirection( &dir );
if( FAILED( hr ) || dir != direction )
{
continue;
}
hr = GetMedium( pTempPin, regPinMediumTemp );
if( FAILED( hr ) )
{
continue;
}
if( !IsVideoPin( pTempPin ) )
{
continue;
}
if( ::IsEqualGUID( regPinMediumTemp.clsMedium, regPinMedium.clsMedium ) &&
regPinMediumTemp.dw1 == regPinMedium.dw1 )
{
(*ppPin) = pTempPin.Detach();
return S_OK;
}
}
return E_FAIL;
}
//
// Get a special medium from this pin.
// If there is not one, return GUID_NULL.
// Returns the first one it finds special
//
HRESULT
ISampleCaptureGraphBuilder::GetMedium(
IPin *pPin,
REGPINMEDIUM& regPinMedium )
{
if( !pPin )
{
return E_POINTER;
}
SmartPtr<IKsPin> pKsPin;
HRESULT hr = pPin->QueryInterface(IID_IKsPin, (void**)&pKsPin );
if( FAILED( hr ) )
{
return hr;
}
PKSMULTIPLE_ITEM pmi;
hr = pKsPin->KsQueryMediums( &pmi );
if( FAILED( hr ) )
{
return hr;
}
REGPINMEDIUM *pMedium = (REGPINMEDIUM *)(pmi + 1);
for( ULONG i = 0; i < pmi->Count; i++ )
{
if( !::IsEqualGUID( pMedium->clsMedium, GUID_NULL ) &&
!::IsEqualGUID( pMedium->clsMedium, KSMEDIUMSETID_Standard )
)
{
regPinMedium.clsMedium = pMedium->clsMedium;
regPinMedium.dw1 = pMedium->dw1;
regPinMedium.dw2 = pMedium->dw2;
CoTaskMemFree( pmi );
return S_OK;
}
}
regPinMedium.clsMedium = GUID_NULL;
regPinMedium.dw1 = 0;
regPinMedium.dw2 = 0;
CoTaskMemFree( pmi );
return S_OK;
}
//
// Adds the MPEG2 demux and renders
// the audio and video pin until the end (until the renderers)
//
HRESULT ISampleCaptureGraphBuilder::AddMPEG2Demux( )
{
if( pMPEG2Demux_ )
{
//
// Instead of a MPEG2 demux there is a
// dump filter in which the file will be dumped
//
return S_OK;
}
HRESULT hr = CoCreateInstance(CLSID_MPEG2Demultiplexer, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&pMPEG2Demux_);
if( FAILED( hr ) )
{
return hr;
}
return graph_->AddFilter( static_cast<IBaseFilter *>( pMPEG2Demux_ ), L"MPEG2 Demux" );
}
HRESULT
ISampleCaptureGraphBuilder::FindVideoPin(
IBaseFilter *pFilter,
IPin **ppPin )
{
if( !pFilter )
{
return E_POINTER;
}
SmartPtr<IEnumPins> pEnumPins;
HRESULT hr = pFilter->EnumPins( &pEnumPins );
if( FAILED( hr ) )
{
return hr;
}
SmartPtr<IPin> pTempPin;
ULONG fetched;
hr = pEnumPins->Reset( );
while( pTempPin.Release(), S_OK == pEnumPins->Next( 1, &pTempPin, &fetched ) )
{
if( IsVideoPin( pTempPin ) )
{
(*ppPin) = pTempPin.Detach();
return S_OK;
}
}
return E_FAIL;
}
BOOL ISampleCaptureGraphBuilder::IsAudioPin( IPin *pPin )
{
if( !pPin )
{
return FALSE; // NULL pointer
}
return HasMediaType( pPin, MEDIATYPE_Audio) ;
}
BOOL ISampleCaptureGraphBuilder::HasMediaType(IPin *pPin, REFGUID majorType )
{
if( !pPin )
{
return FALSE;
}
SmartPtr<IEnumMediaTypes> pMediaTypes;
HRESULT hr = pPin->EnumMediaTypes( &pMediaTypes );
if( FAILED( hr ) )
{
return FALSE;
}
hr = pMediaTypes->Reset();
if( FAILED( hr ) )
{
return FALSE;
}
ULONG fetched;
AM_MEDIA_TYPE *mediaType;
while( S_OK == pMediaTypes->Next( 1, &mediaType, &fetched ) )
{
if( ::IsEqualGUID( mediaType->majortype, majorType ) )
{
DeleteMediaType( mediaType );
return TRUE;
}
DeleteMediaType( mediaType );
}
return FALSE;
}
HRESULT
ISampleCaptureGraphBuilder::FindAudioPin(
IBaseFilter *pFilter,
IPin **ppPin )
{
if( !pFilter )
{
return E_POINTER;
}
SmartPtr<IEnumPins> pEnumPins;
HRESULT hr = pFilter->EnumPins( &pEnumPins );
if( FAILED( hr ) )
{
return hr;
}
SmartPtr<IPin> pTempPin;
ULONG fetched;
hr = pEnumPins->Reset( );
while( pTempPin.Release(), S_OK == pEnumPins->Next( 1, &pTempPin, &fetched ) )
{
if( IsAudioPin( pTempPin ) )
{
(*ppPin) = pTempPin.Detach();
return S_OK;
}
}
return E_FAIL;
}
HRESULT ISampleCaptureGraphBuilder::FindEncoder(
IEnumMoniker *pEncoders,
REGPINMEDIUM pinMedium,
IBaseFilter **ppEncoder )
{
if( ! pEncoders )
{
return E_INVALIDARG;
}
if( IsEqualGUID( pinMedium.clsMedium, GUID_NULL ) ||
IsEqualGUID( pinMedium.clsMedium, KSMEDIUMSETID_Standard ) )
{
return E_INVALIDARG;
}
HRESULT hr;
SmartPtr<IBaseFilter> pFilter;
SmartPtr<IMoniker> pMoniker;
ULONG fetched;
SmartPtr<IPin> pPin;
while( pFilter.Release(), pMoniker.Release(),
S_OK == pEncoders->Next( 1, &pMoniker, &fetched ) )
{
hr = pMoniker->BindToObject(
0, 0, IID_IBaseFilter, reinterpret_cast<void **>( &pFilter ) );
if( FAILED( hr ) )
{
continue;
}
hr = FindPin( pFilter, pinMedium, PINDIR_INPUT, TRUE, &pPin );
if( SUCCEEDED( hr ) )
{
*ppEncoder = pFilter.Detach();
return hr;
}
}
return E_FAIL;
}
HRESULT
ISampleCaptureGraphBuilder::RenderToMPEG2Demux(
IPin *pPin,
IEnumMoniker *pEncoders)
{
if( !pPin || !pEncoders )
{
return E_INVALIDARG;
}
REGPINMEDIUM pinMedium ;
pinMedium.clsMedium = GUID_NULL;
pinMedium.dw1 = 0;
pinMedium.dw2 = 0;
SmartPtr<IBaseFilter> pFilter;
SmartPtr<IMoniker> pMoniker;
ULONG fetched;
HRESULT hr;
while( pFilter.Release(), pMoniker.Release(), S_OK == pEncoders->Next( 1, &pMoniker, &fetched ) )
{
hr = pMoniker->BindToObject(
0, 0, IID_IBaseFilter, reinterpret_cast<void **>( &pFilter ) );
if( FAILED( hr ) )
{
continue;
}
hr = graph_->AddFilter( pFilter, L"Encoder" );
if( FAILED( hr ) )
{
continue;
}
hr = ConnectPin( pPin, pFilter );
if( FAILED( hr ) )
{
graph_->RemoveFilter( pFilter );
continue;
}
hr = ConnectEncoderToMPEG2Demux( pFilter, pinMedium );
if( SUCCEEDED( hr ) )
{
pEncoder_ = pFilter;
return S_OK;
}
graph_->RemoveFilter( pFilter );
}
return E_FAIL;
}
//
// Loop through every encoder available on system.
// Look first after the one that has a special medium if
// there is a special one on pPin.
// Otherwise, try to render using ICaptureGrapBuilder2
// and the encoder that matches this will be the chosen one/
// If the encoder is found, then this will be rendered to the
// MPEG2 demux.
//
HRESULT
ISampleCaptureGraphBuilder::RenderToMPEG2Demux(
IPin *pPin,
const REGPINMEDIUM& pinMedium,
IEnumMoniker *pEncoders
)
{
//
// The pin has a special medium,
// there shold be an encoder with the same
// medium
//
SmartPtr< IBaseFilter > pEncoder;
HRESULT hr = FindEncoder( pEncoders, pinMedium, &pEncoder );
if( FAILED( hr ) )
{
return hr;
}
hr = graph_->AddFilter( pEncoder, L"Encoder" );
if( FAILED( hr ) )
{
return hr;
}
hr = ConnectPin( pPin, pEncoder );
if( FAILED( hr ) )
{
hr = graph_->RemoveFilter( pEncoder );
return hr;
}
//
// the video pin was rendered to the same
// ( hardware? ) encoder with the same mediu
//
hr = ConnectEncoderToMPEG2Demux( pEncoder, pinMedium );
if( FAILED( hr ) )
{
hr = graph_->RemoveFilter( pEncoder );
return hr;
}
pEncoder_ = pEncoder;
return S_OK;
}
HRESULT
ISampleCaptureGraphBuilder::RenderToMPEG2Demux( IPin *pPin )
{
if( !pPin )
{
return E_INVALIDARG;
}
REGPINMEDIUM pinMedium;
HRESULT hr = GetMedium( pPin, pinMedium );
if( FAILED( hr ) )
{
return hr;
}
SmartPtr< IEnumMoniker > pEncoders;
if( ::IsEqualGUID( pinMedium.clsMedium, GUID_NULL ) )
{
//
// Search throgh the codec category
//
hr = GetEncodersByCategory( &pEncoders );
if( FAILED( hr ) )
{
return hr;
}
hr = RenderToMPEG2Demux( pPin, pEncoders );
if( SUCCEEDED( hr ) )
{
return S_OK;
}
}
else
{
//
// search through encoders category; identify
// the encoder using the medium
//
hr = GetEncodersByCategory( &pEncoders );
if( FAILED( hr ) )
{
return hr;
}
hr = RenderToMPEG2Demux( pPin, pinMedium, pEncoders );
if( SUCCEEDED( hr ) )
{
return S_OK;
}
pEncoders = NULL;
hr = GetEncodersByEnumerating( pPin, pinMedium, &pEncoders );
if( FAILED( hr ) )
{
return hr;
}
hr = RenderToMPEG2Demux( pPin, pinMedium, pEncoders );
if( FAILED( hr ) )
{
return hr;
}
}
return S_OK;
}
//
// pEncoder - the encoder to be connected using a multiplexer
//
//
HRESULT
ISampleCaptureGraphBuilder::ConnectEncoderToMPEG2Demux(
IBaseFilter *pEncoder,
const REGPINMEDIUM& pinMedium )
{
REGPINMEDIUM regPinMedium ;
regPinMedium.clsMedium = GUID_NULL;
regPinMedium.dw1 = 0;
regPinMedium.dw2 = 0;
//
// try a direct connection between
// codec and MPEG2Demux
//
HRESULT hr = ConnectFilters(
pEncoder,
pMPEG2Demux_ );
if( SUCCEEDED( hr ) )
{
return S_OK;
}
//
// no luck
// maybe I need a multiplexer
//
SmartPtr< IEnumMoniker > pMultiplexers;
hr = GetMultiplexersByCategory( &pMultiplexers );
if( FAILED( hr ) )
{
return hr;
}
hr = ConnectMultiplexerToMPEG2Demux( pEncoder, pMultiplexers );
if( SUCCEEDED( hr ) )
{
return S_OK;
}
if( FALSE == ::IsEqualGUID( pinMedium.clsMedium, GUID_NULL ) )
{
//
// get the multiplexers using IFilterMapper2
// assuming that the encoder and the multiplexer have the same medium
//
pMultiplexers = NULL;
hr = GetMultiplexersByFilterMapper( &pMultiplexers, pinMedium );
if( FAILED( hr ) )
{
return hr;
}
hr = ConnectMultiplexerToMPEG2Demux( pEncoder, pMultiplexers );
if( SUCCEEDED( hr ) )
{
return S_OK;
}
}
return E_FAIL;
}
HRESULT
ISampleCaptureGraphBuilder::GetMultiplexersByCategory( IEnumMoniker **ppMultiplexers )
{
SmartPtr<ICreateDevEnum> pDeviceEnum;
HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void**)&pDeviceEnum);
if( FAILED( hr ) )
{
return hr;
}
return pDeviceEnum->CreateClassEnumerator( KSCATEGORY_MULTIPLEXER, ppMultiplexers, 0 );
}
HRESULT
ISampleCaptureGraphBuilder::GetMultiplexersByFilterMapper(
IEnumMoniker **ppMultiplexers,
const REGPINMEDIUM& pinMedium )
{
return E_NOTIMPL;
}
HRESULT
ISampleCaptureGraphBuilder::ConnectMultiplexerToMPEG2Demux(
IBaseFilter *pEncoder,
IEnumMoniker *pMultiplexers )
{
if( !pEncoder || !pMultiplexers )
{
return E_INVALIDARG;
}
SmartPtr<IBaseFilter> pFilter;
SmartPtr<IMoniker> pMoniker;
ULONG fetched;
HRESULT hr;
while( pFilter.Release(), pMoniker.Release(),
S_OK == pMultiplexers->Next( 1, &pMoniker, &fetched ) )
{
hr = pMoniker->BindToObject(
0, 0, IID_IBaseFilter, reinterpret_cast<void **>( &pFilter ) );
if( FAILED( hr ) )
{
continue;
}
hr = graph_->AddFilter( pFilter, NULL );
if( FAILED( hr ) )
{
continue;
}
//
// connect the encoder to the multiplexer
//
hr = ConnectFilters( pEncoder, pFilter );
if( FAILED( hr ) )
{
graph_->RemoveFilter( pFilter );
continue;
}
//
// connect the multiplexer to the encoder
//
hr = ConnectFilters( pFilter, pMPEG2Demux_ );
if( SUCCEEDED( hr ) )
{
pMultiplexer_ = pFilter;
return S_OK;
}
}
return E_FAIL;
}
HRESULT
ISampleCaptureGraphBuilder::BuildMPEG2Segment(IBaseFilter *pFilter)
{
if( ! pFilter )
{
return E_FAIL;
}
HRESULT hr = AddMPEG2Demux( );
if( FAILED( hr ) )
{
return hr;
}
//
// Search a MPEG2 pin on the
// filter
//
SmartPtr<IPin> pPin;
hr = FindMPEG2Pin( pFilter, &pPin );
if( SUCCEEDED( hr ) )
{
hr = ConnectPin( pPin, pMPEG2Demux_ );
if( FAILED( hr ) )
{
graph_->RemoveFilter( pMPEG2Demux_ );
return E_FAIL;
}
return S_OK;
}
//
// no pins that streams directly MPEG2 stream
//
hr = FindVideoPin( pFilter, &pPin );
if( FAILED( hr ) )
{
graph_->RemoveFilter( pMPEG2Demux_ );
return hr; // no video pin
}
hr = RenderToMPEG2Demux( pPin );
if( FAILED( hr ) )
{
graph_->RemoveFilter( pMPEG2Demux_ );
return hr;
}
SmartPtr<IPin> pAudioPin;
hr = FindAudioPin( pFilter, &pAudioPin );
if( FAILED( hr ) )
{
//
// don't bother with audio
//
return S_OK;
}
//
// try to connect the audio pin directly to encoder
// if this is not possible, then try to find an encoder
// and connect it to the multiplexer
//
ASSERT( pEncoder_ );
hr = ConnectPin( pAudioPin, pEncoder_ );
if( FAILED( hr ) )
{
hr = ConnectAudioPinToMultiplexer( pAudioPin, pMultiplexer_ );
}
return S_OK;
}
HRESULT
ISampleCaptureGraphBuilder::ConnectAudioPinToMultiplexer(
IPin *pPin,
IBaseFilter *pMultiplexer)
{
if( !pPin || !pMultiplexer )
{
return E_INVALIDARG;
}
REGPINMEDIUM pinMedium;
HRESULT hr = GetMedium( pPin, pinMedium );
if( FAILED( hr ) )
{
return hr;
}
SmartPtr<IBaseFilter> pEncoder;
SmartPtr<IEnumMoniker> pEncoders;
if( FALSE == ::IsEqualGUID( pinMedium.clsMedium, GUID_NULL ) )
{
//
// search through encoders category; identify
// the encoder using the medium
//
hr = GetEncodersByCategory( &pEncoders );
if( FAILED( hr ) )
{
return hr;
}
hr = FindEncoder( pEncoders, pinMedium, &pEncoder );
if( SUCCEEDED( hr ) )
{
hr = graph_->AddFilter( pEncoder, L"Audio Encoder" );
if( SUCCEEDED( hr ) &&
SUCCEEDED( ConnectPin( pPin, pEncoder ) ) &&
SUCCEEDED( ConnectFilters( pEncoder, pMultiplexer ) )
)
{
return S_OK;
}
}
pEncoders = NULL;
hr = GetEncodersByEnumerating( pPin, pinMedium, &pEncoders );
if( FAILED( hr ) )
{
return hr;
}
hr = FindEncoder( pEncoders, pinMedium, &pEncoder );
if( SUCCEEDED( hr ) )
{
hr = graph_->AddFilter( pEncoder, L"Audio Encoder" );
if( SUCCEEDED( hr ) &&
SUCCEEDED( ConnectPin( pPin, pEncoder ) ) &&
SUCCEEDED( ConnectFilters( pEncoder, pMultiplexer ) )
)
{
return S_OK;
}
}
return E_FAIL;
}
//
// Search throgh the codec category
//
hr = GetEncodersByCategory( &pEncoders );
if( FAILED( hr ) )
{
return hr;
}
SmartPtr<IBaseFilter> pFilter;
SmartPtr<IMoniker> pMoniker;
ULONG fetched;
while( pFilter.Release(), pMoniker.Release(), S_OK == pEncoders->Next( 1, &pMoniker, &fetched ) )
{
hr = pMoniker->BindToObject(
0, 0, IID_IBaseFilter, reinterpret_cast<void **>( &pFilter ) );
if( FAILED( hr ) )
{
continue;
}
hr = graph_->AddFilter( pFilter, L"Audio Encoder" );
if( FAILED( hr ) )
{
continue;
}
hr = ConnectPin( pPin, pFilter );
if( FAILED( hr ) )
{
graph_->RemoveFilter( pFilter );
continue;
}
hr = ConnectFilters( pFilter, pMultiplexer );
if( SUCCEEDED( hr ) )
{
return S_OK;
}
graph_->RemoveFilter( pFilter );
}
return E_FAIL;
}
HRESULT
ISampleCaptureGraphBuilder::CreateVideoPin(
IMpeg2Demultiplexer *pIMpeg2Demux )
{
if( !pIMpeg2Demux )
{
return E_INVALIDARG;
}
AM_MEDIA_TYPE amTypeVideo;
amTypeVideo.majortype = MEDIATYPE_Video;
amTypeVideo.subtype = MEDIASUBTYPE_MPEG2_VIDEO;
amTypeVideo.bFixedSizeSamples = TRUE;
amTypeVideo.bTemporalCompression = 0;
amTypeVideo.formattype = FORMAT_MPEG2Video;
amTypeVideo.pUnk = NULL;
amTypeVideo.cbFormat = sizeof( Mpeg2ProgramVideo );
amTypeVideo.pbFormat = Mpeg2ProgramVideo;
//
// Create video pin
//
SmartPtr<IPin> pVideoOutPin;
HRESULT hr = pIMpeg2Demux->CreateOutputPin( &amTypeVideo, L"MpegVideo", &pVideoOutPin );
if( FAILED( hr ) )
{
return hr;
}
SmartPtr<IMPEG2StreamIdMap> pIVideoPIDMap;
hr = pVideoOutPin->QueryInterface( &pIVideoPIDMap );
if( FAILED( hr ) )
{
return hr;
}
hr = pIVideoPIDMap->MapStreamId(VidPID_, MPEG2_PROGRAM_ELEMENTARY_STREAM , 0, 0);
if( FAILED( hr ) )
{
return hr;
}
#ifdef USE_VMR
//
// Get the VMR interface and add it to the graph
//
SmartPtr<IBaseFilter> pVMR;
hr = pVMR.CoCreateInstance( CLSID_VideoMixingRenderer );
if( FAILED( hr ) )
{
return hr;
}
hr = graph_->AddFilter( pVMR, L"VMR" );
if( FAILED( hr ) )
{
return hr;
}
//
//before rendering the VMR, make the number of streams 1
//
SmartPtr<IVMRFilterConfig> pConfig;
hr = pVMR.QueryInterface( &pConfig );
if( FAILED( hr ) )
{
return hr;
}
hr = pConfig->SetNumberOfStreams( 1 );
if( FAILED( hr ) )
{
return hr;
}
//
// Get the input pin from the VMR
//
SmartPtr<IPin> pInputPin;
hr = graphBuilder2_->FindPin(
static_cast<IBaseFilter *>( pVMR ),
PINDIR_INPUT,
NULL,
NULL,
TRUE,
0,
&pInputPin
);
if( FAILED( hr ) )
{
hr = pIMpeg2Demux->DeleteOutputPin(L"MpegVideo");
graph_->RemoveFilter( pVMR );
return hr;
}
return graph_->Connect( pVideoOutPin, pInputPin );
#endif
return hr;
}
HRESULT
ISampleCaptureGraphBuilder::CreateAudioPin(
IMpeg2Demultiplexer *pIMpeg2Demux
)
{
if( !pIMpeg2Demux )
{
return E_INVALIDARG;
}
//
// for audio: could be Mpeg1, Mpeg2, AC3: if Mpeg1 failed (connect failed) try Mpeg2.if failed tried AC3
// Audio struct of AC3 can be copied from dev code.
//
AM_MEDIA_TYPE amTypeAudio;
amTypeAudio.majortype = MEDIATYPE_Audio;
amTypeAudio.subtype = MEDIASUBTYPE_MPEG2_AUDIO;
amTypeAudio.bFixedSizeSamples = TRUE;
amTypeAudio.bTemporalCompression = 0;
amTypeAudio.formattype = FORMAT_WaveFormatEx;
amTypeAudio.pUnk = NULL;
amTypeAudio.cbFormat = sizeof( MPEG1AudioFormat );
amTypeAudio.pbFormat = MPEG1AudioFormat;
SmartPtr<IPin> pAudioOutPin;
HRESULT hr = pIMpeg2Demux->CreateOutputPin(&amTypeAudio, L"MpegAudio", &pAudioOutPin);
if( FAILED( hr ) )
{
return hr;
}
SmartPtr<IMPEG2StreamIdMap> pIAudioPIDMap;
hr = pAudioOutPin->QueryInterface( &pIAudioPIDMap );
if( FAILED( hr ) )
{
return hr;
}
hr = pIAudioPIDMap->MapStreamId(AudPID_, MPEG2_PROGRAM_ELEMENTARY_STREAM, 0, 0);
if( FAILED( hr ) )
{
return hr;
}
return hr;
}
HRESULT ISampleCaptureGraphBuilder::ConfigureMPEG2Demux( IBaseFilter *pFilter)
{
if( ! pFilter )
{
return E_INVALIDARG;
}
//
// Create video pin and render it
//
SmartPtr<IMpeg2Demultiplexer> pIMpeg2Demux;
HRESULT hr = pFilter->QueryInterface(IID_IMpeg2Demultiplexer, (void**)&pIMpeg2Demux );
if( FAILED( hr ) )
{
return hr;
}
hr = CreateVideoPin( pIMpeg2Demux );
if( FAILED( hr ) )
{
return hr;
}
hr = CreateAudioPin( pIMpeg2Demux );
if( FAILED( hr ) )
{
return hr;
}
return S_OK;
}
HRESULT
ISampleCaptureGraphBuilder::ConnectFilters(IBaseFilter *pUpFilter, IBaseFilter *pDownFilter)
{
if( !pUpFilter || !pDownFilter )
{
return E_INVALIDARG;
}
// All the need pin & pin enumerator pointers
SmartPtr<IEnumPins> pEnumUpFilterPins ,
pEnumDownFilterPins;
SmartPtr<IPin> pUpFilterPin ,
pDownFilterPin;
HRESULT hr = S_OK;
// Get the pin enumerators for both the filtera
hr = pUpFilter->EnumPins(&pEnumUpFilterPins);
if( FAILED( hr ) )
{
return hr;
}
hr= pDownFilter->EnumPins(&pEnumDownFilterPins);
if( FAILED( hr ) )
{
return hr;
}
// Loop on every pin on the Upstream Filter
BOOL bConnected = FALSE;
PIN_DIRECTION pinDir;
ULONG nFetched = 0;
while(pUpFilterPin.Release( ), S_OK == pEnumUpFilterPins->Next(1, &pUpFilterPin, &nFetched) )
{
// Make sure that we have the output pin of the upstream filter
hr = pUpFilterPin->QueryDirection( &pinDir );
if( FAILED( hr ) || PINDIR_INPUT == pinDir )
{
continue;
}
//
// I have an output pin; loop on every pin on the Downstream Filter
//
while(pDownFilterPin.Release( ), S_OK == pEnumDownFilterPins->Next(1, &pDownFilterPin, &nFetched) )
{
hr = pDownFilterPin->QueryDirection( &pinDir );
if( FAILED( hr ) || PINDIR_OUTPUT == pinDir )
{
continue;
}
// Try to connect them and exit if u can else loop more until you can
if(SUCCEEDED(graph_->ConnectDirect(pUpFilterPin, pDownFilterPin, NULL)))
{
bConnected = TRUE;
break;
}
}
hr = pEnumDownFilterPins->Reset();
if( FAILED( hr ) )
{
return hr;
}
}
if( !bConnected )
{
return E_FAIL;
}
return S_OK;
}
HRESULT
ISampleCaptureGraphBuilder::ConnectPin(
IPin *pPin,
IBaseFilter *pDownFilter )
{
if( !pPin || !pDownFilter )
{
return E_INVALIDARG;
}
PIN_DIRECTION pinDirection;
HRESULT hr = pPin->QueryDirection( &pinDirection );
if( FAILED( hr ) || PINDIR_INPUT == pinDirection )
{
return E_FAIL;
}
//
// Add the filter to the graph
//
BOOL bConnected = FALSE;
ULONG nFetched = 0;
SmartPtr<IPin> pDownFilterPin;
//
// Loop through every input pin from downstream filter
// and try to connect the pin
//
SmartPtr< IEnumPins > pEnumDownFilterPins;
hr= pDownFilter->EnumPins( &pEnumDownFilterPins );
if( FAILED( hr ) )
{
return hr;
}
while(pDownFilterPin.Release( ), S_OK == pEnumDownFilterPins->Next(1, &pDownFilterPin, &nFetched) )
{
hr = pDownFilterPin->QueryDirection( &pinDirection );
if( FAILED( hr ) )
{
continue;
}
if( PINDIR_OUTPUT == pinDirection )
{
continue;
}
hr = graph_->ConnectDirect(pPin, pDownFilterPin, NULL);
if(SUCCEEDED(hr))
{
bConnected = TRUE;
break;
}
}
if( !bConnected )
{
graph_->RemoveFilter( pDownFilter );
return E_FAIL;
}
return S_OK;
}