Game client codebase including: - CharacterActionControl: Character and creature management - GlobalScript: Network, items, skills, quests, utilities - RYLClient: Main client application with GUI and event handlers - Engine: 3D rendering engine (RYLGL) - MemoryManager: Custom memory allocation - Library: Third-party dependencies (DirectX, boost, etc.) - Tools: Development utilities 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1419 lines
37 KiB
C++
1419 lines
37 KiB
C++
//------------------------------------------------------------------------------
|
|
// File: XMLTlTst.cpp
|
|
//
|
|
// Desc: DirectShow sample code - test utility for building timelines from
|
|
// .XTL files.
|
|
//
|
|
// Copyright (c) 1999-2001 Microsoft Corporation. All rights reserved.
|
|
//------------------------------------------------------------------------------
|
|
|
|
//
|
|
// This sample demonstrates the following:
|
|
//
|
|
// - Using IXml2Dex to load timelines from and save to .XTL files
|
|
// - Adding Windows Media (ASF) support to Editing applications
|
|
// - Using IAMErrorLog to display error messages
|
|
// - Preview of DES timelines (IAMTimeLine, IRenderEngine)
|
|
// - Traversing the timeline nodes
|
|
//
|
|
|
|
#include <windows.h>
|
|
#include <streams.h>
|
|
#include <stdio.h>
|
|
#include <atlbase.h>
|
|
#include <qedit.h>
|
|
|
|
extern CComModule _Module;
|
|
#include <atlimpl.cpp>
|
|
|
|
//
|
|
// NOTE:
|
|
//
|
|
// In order to write ASF files using this program, you need to obtain
|
|
// a Windows Media Format SDK Certificate (WMStub.lib), link to it,
|
|
// and define USE_WMF_CERT below.
|
|
//
|
|
// See the Windows Media Format SDK documentation for more information.
|
|
//
|
|
// The SDK download page is located at
|
|
// http://msdn.microsoft.com/workshop/imedia/windowsmedia/sdk/wmsdk.asp,
|
|
// with links to the SDK itself and information for obtaining a certificate.
|
|
//
|
|
// #define USE_WMF_CERT
|
|
//
|
|
|
|
#ifdef USE_WMF_CERT
|
|
|
|
#include <wmsdk.h>
|
|
#include <dshowasf.h>
|
|
|
|
|
|
// Note: this object is a SEMI-COM object, and can only be created statically.
|
|
class CKeyProvider : public IServiceProvider {
|
|
public:
|
|
STDMETHODIMP QueryInterface(REFIID riid, void ** ppv);
|
|
STDMETHODIMP_(ULONG) AddRef() { return 2; }
|
|
STDMETHODIMP_(ULONG) Release() { return 1; }
|
|
|
|
// IServiceProvider
|
|
STDMETHODIMP QueryService(REFIID siid, REFIID riid, void **ppv);
|
|
|
|
};
|
|
|
|
CKeyProvider g_prov; // note, must stay in scope while graph is alive
|
|
|
|
#endif // USE_WMF_CERT
|
|
|
|
|
|
class CErrorReporter : public IAMErrorLog
|
|
{
|
|
// IAMErrorLog
|
|
STDMETHODIMP LogError( long Severity, BSTR ErrorString, LONG ErrorCode,
|
|
HRESULT hresult, VARIANT * pExtraInfo );
|
|
|
|
public:
|
|
|
|
// SEMI COM
|
|
STDMETHODIMP QueryInterface(REFIID riid, void ** ppv)
|
|
{
|
|
if (riid == IID_IAMErrorLog || riid == IID_IUnknown) {
|
|
*ppv = (void *) static_cast<IAMErrorLog *>(this);
|
|
return NOERROR;
|
|
}
|
|
return E_NOINTERFACE;
|
|
}
|
|
STDMETHODIMP_(ULONG) AddRef() { return 2; }
|
|
STDMETHODIMP_(ULONG) Release() { return 1; }
|
|
};
|
|
|
|
|
|
//
|
|
// Declarations for helper functions defined below
|
|
//
|
|
IPin * GetPin( IBaseFilter * pFilter, int PinNum, PIN_DIRECTION pd );
|
|
|
|
void TurnOffPreviewMode( IAMTimeline * pTimeline );
|
|
|
|
HRESULT GetFirstSourceOnTimeline(
|
|
IAMTimeline *pTimeline, GUID MajorType,
|
|
IAMTimelineGroup ** ppGroup, IAMTimelineSrc ** ppSource );
|
|
|
|
HRESULT GetSourceVideoType(WCHAR *wszFilename, AM_MEDIA_TYPE *pmt);
|
|
|
|
HRESULT GetDestinationASFFormat(
|
|
AM_MEDIA_TYPE **ppmt,
|
|
int iProfile );
|
|
|
|
#ifdef USE_WMF_CERT
|
|
HRESULT MapProfileIdToProfile(
|
|
int iProfile, IWMProfile **ppProfile);
|
|
#endif
|
|
|
|
void ListWMSDKProfiles();
|
|
BOOL IsAsfExtension( WCHAR * Filename );
|
|
|
|
HRESULT ConnectOutputFile(
|
|
IRenderEngine * pEngine, WCHAR * Filename
|
|
#ifdef USE_WMF_CERT
|
|
, int iProfile
|
|
#endif
|
|
);
|
|
|
|
|
|
//
|
|
// Main program code
|
|
//
|
|
int __cdecl main(int argc, char *argv[])
|
|
{
|
|
CErrorReporter pLog; // note, must stay in scope while graph is alive
|
|
HRESULT hr = 0;
|
|
|
|
++argv; // skip the app name
|
|
--argc;
|
|
|
|
int Ret = 0;
|
|
|
|
char **&ppArg = argv;
|
|
|
|
BOOL fNoRender = FALSE;
|
|
BOOL fDynamic = FALSE;
|
|
BOOL fRecomp = FALSE;
|
|
BOOL fWriteIt = FALSE;
|
|
int nWriteArg = -1;
|
|
BOOL fWriteGrf = FALSE;
|
|
int nWriteGrfArg = -1;
|
|
int nInputFileArg = -1;
|
|
BOOL fWriteXtl = FALSE;
|
|
int nWriteXtlArg = -1;
|
|
double RenderStart = -1.0;
|
|
double RenderStop = -1.0;
|
|
BOOL fNoClock = FALSE;
|
|
#ifdef USE_WMF_CERT
|
|
int iASFProfile = -1;
|
|
#endif
|
|
|
|
// find which switches we've set. Every time we process a switch,
|
|
// we pull it off the stack
|
|
//
|
|
for( int arg = 0 ; arg < argc ; arg++ )
|
|
{
|
|
if( !ppArg[arg] ) continue;
|
|
|
|
// found the input .xtl file specified
|
|
//
|
|
if( ppArg[arg][0] != '/' )
|
|
{
|
|
nInputFileArg = arg;
|
|
continue;
|
|
}
|
|
|
|
if (ppArg[arg][1] == 'N' || ppArg[arg][1] == 'n') {
|
|
fNoRender = TRUE;
|
|
continue;
|
|
}
|
|
|
|
if (ppArg[arg][1] == 'D' || ppArg[arg][1] == 'd') {
|
|
fDynamic = TRUE;
|
|
continue;
|
|
}
|
|
|
|
if (ppArg[arg][1] == 'C' || ppArg[arg][1] == 'c') {
|
|
fNoClock = TRUE;
|
|
continue;
|
|
}
|
|
|
|
if (ppArg[arg][1] == 'R' || ppArg[arg][1] == 'r') {
|
|
fRecomp = TRUE;
|
|
continue;
|
|
}
|
|
if (ppArg[arg][1] == 'W' || ppArg[arg][1] == 'w') {
|
|
fWriteIt = TRUE;
|
|
nWriteArg = arg + 1;
|
|
arg++; // advance beyond the extra arg
|
|
continue;
|
|
}
|
|
if (ppArg[arg][1] == 'G' || ppArg[arg][1] == 'g') {
|
|
fWriteGrf = TRUE;
|
|
nWriteGrfArg = arg + 1;
|
|
arg++; // advance beyond the extra arg
|
|
continue;
|
|
}
|
|
if (ppArg[arg][1] == 'X' || ppArg[arg][1] == 'x') {
|
|
fWriteXtl = TRUE;
|
|
nWriteXtlArg = arg + 1;
|
|
arg++; // advance beyond the extra arg
|
|
continue;
|
|
}
|
|
|
|
#ifdef USE_WMF_CERT
|
|
if (ppArg[arg][1] == 'P' || ppArg[arg][1] == 'p') {
|
|
arg++;
|
|
if (arg >= argc || (ppArg[arg][0] < '0' || ppArg[arg][0] > '9')) {
|
|
ListWMSDKProfiles();
|
|
return -1;
|
|
}
|
|
|
|
iASFProfile = atoiA(ppArg[arg]);
|
|
printf("Using WMSDK profile #%d\r\n", iASFProfile);
|
|
continue;
|
|
}
|
|
#endif // USE_WMF_CERT
|
|
|
|
if( ppArg[arg][1] == '[' ) {
|
|
// need to pull doubles out of the string
|
|
//
|
|
char * p = &ppArg[arg][2];
|
|
for( unsigned long k = 0 ; k < strlen( p ) ; k++ )
|
|
{
|
|
if( p[k] == '-' )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if( p[k] != '-' )
|
|
{
|
|
printf( "forgot the '-' between doubles\r\n" );
|
|
return -1;
|
|
}
|
|
for( unsigned long j = 0 ; j < strlen( p ) ; j++ )
|
|
{
|
|
if( p[j] == ']' )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if( p[j] != ']' )
|
|
{
|
|
printf( "forgot the ']' \r\n" );
|
|
return -1;
|
|
}
|
|
|
|
// set it to zero so atof will work
|
|
//
|
|
p[k] = 0;
|
|
p[j] = 0;
|
|
|
|
// get the float
|
|
//
|
|
RenderStart = atof( p );
|
|
|
|
// get the next float
|
|
//
|
|
p = &p[k+1];
|
|
|
|
RenderStop = atof( p );
|
|
continue;
|
|
}
|
|
|
|
|
|
printf("unrecognized switch: %s\n\n", ppArg[arg]);
|
|
nInputFileArg = -1;
|
|
break;
|
|
}
|
|
|
|
if( argc < 1 || nInputFileArg == -1 ) {
|
|
printf("Usage: [various switches] input.xtl\r\n");
|
|
printf(" /N - No preview, just connect it up\r\n");
|
|
printf(" /G output.grf - Output a GRF file\r\n");
|
|
printf(" /X output.xtl - Output an XTL file\r\n");
|
|
printf(" /R - Connect the graph with smart recompression turned on\r\n");
|
|
printf(" /W <filename> - Render the clip to file. (It may take a while)\r\n" );
|
|
printf(" /D - Dynamic connections on\r\n");
|
|
printf(" /[double-double] - set the render range start/stop times\r\n" );
|
|
printf(" /C - No clock. Run as fast as you can\r\n" );
|
|
#ifdef USE_WMF_CERT
|
|
printf(" /P [number] - Choose an ASF compression profile\r\n" );
|
|
printf(" /P without a number - list available profiles\r\n" );
|
|
#endif // USE_WMF_CERT
|
|
return -1;
|
|
}
|
|
|
|
USES_CONVERSION;
|
|
WCHAR *wszFileInput = A2W(ppArg[nInputFileArg]);
|
|
|
|
CComPtr< IRenderEngine > pRenderEngine;
|
|
CComPtr< IAMTimeline > pTimeline;
|
|
CComPtr< IXml2Dex > pXml;
|
|
|
|
|
|
do
|
|
{
|
|
hr = CoInitialize( NULL );
|
|
|
|
hr = CoCreateInstance(
|
|
__uuidof(AMTimeline),
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
__uuidof(IAMTimeline),
|
|
(void**) &pTimeline
|
|
);
|
|
|
|
if (FAILED(hr)) {
|
|
printf("Failed to create timeline, error = %x\r\n", hr);
|
|
Ret = -1;
|
|
break;
|
|
}
|
|
|
|
CComQIPtr< IAMSetErrorLog, &IID_IAMSetErrorLog > pTimelineLog( pTimeline );
|
|
if( pTimelineLog )
|
|
{
|
|
pTimelineLog->put_ErrorLog( &pLog );
|
|
}
|
|
|
|
hr = CoCreateInstance(
|
|
__uuidof(Xml2Dex),
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
__uuidof(IXml2Dex),
|
|
(void**) &pXml
|
|
);
|
|
|
|
if (FAILED(hr)) {
|
|
printf("QEDIT not registered properly\r\n");
|
|
Ret = -1;
|
|
break;
|
|
}
|
|
|
|
|
|
hr = pXml->ReadXMLFile(pTimeline, wszFileInput);
|
|
printf("ReadXMLFile('%ls') returned %x\r\n", wszFileInput, hr);
|
|
if (FAILED(hr)) {
|
|
Ret = -1;
|
|
break;
|
|
}
|
|
|
|
|
|
// validate sources
|
|
//
|
|
HANDLE WaitEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
|
|
|
|
long Flags =
|
|
SFN_VALIDATEF_CHECK |
|
|
SFN_VALIDATEF_POPUP |
|
|
// SFN_VALIDATEF_TELLME |
|
|
SFN_VALIDATEF_REPLACE |
|
|
SFN_VALIDATEF_USELOCAL |
|
|
0;
|
|
|
|
hr = pTimeline->ValidateSourceNames( Flags, NULL, (LONG_PTR) WaitEvent );
|
|
if( hr == NOERROR )
|
|
{
|
|
WaitForSingleObject( WaitEvent, INFINITE );
|
|
}
|
|
CloseHandle( WaitEvent );
|
|
|
|
// create a render engine
|
|
//
|
|
if( fRecomp )
|
|
{
|
|
hr = CoCreateInstance(
|
|
__uuidof(SmartRenderEngine),
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
__uuidof(IRenderEngine),
|
|
(void**) &pRenderEngine );
|
|
|
|
TurnOffPreviewMode( pTimeline );
|
|
|
|
#ifdef USE_WMF_CERT
|
|
// if we're doing smart recompression then the AMTimeline object will
|
|
// need a way to get our key, so we SetSite on the AMTimeline object here.
|
|
// Note that the pRenderEngine gets the key a little later than this.
|
|
if( SUCCEEDED( hr ) )
|
|
{
|
|
CComQIPtr< IObjectWithSite, &IID_IObjectWithSite > pOWS( pTimeline );
|
|
ASSERT( pOWS );
|
|
if( pOWS )
|
|
{
|
|
pOWS->SetSite((IUnknown *) (IServiceProvider *) &g_prov);
|
|
}
|
|
}
|
|
|
|
#endif // USE_WMF_CERT
|
|
|
|
SCompFmt0 scompFmt;
|
|
ZeroMemory(&scompFmt, sizeof(scompFmt));
|
|
// scompFmt.nFormatId initialized to 0;
|
|
|
|
// use the compression type of the first input video in
|
|
// the project (note this can change the height/width of
|
|
// the group)
|
|
|
|
CComPtr<IAMTimelineGroup> pGroup;
|
|
CComPtr<IAMTimelineSrc> pSource;
|
|
hr = GetFirstSourceOnTimeline( pTimeline, MEDIATYPE_Video, &pGroup, &pSource);
|
|
|
|
|
|
#ifdef USE_WMF_CERT
|
|
|
|
// smart recompression works differently for ASF than it
|
|
// does for AVI. ASF compression needs to be configured by
|
|
// profile id rather than compressor and format block. We
|
|
// cannot recover the profile used to author an ASF file,
|
|
// so the caller is required to provide us the right
|
|
// profile id. We ask the WMSDK for the media type
|
|
// corresponding to the profile.
|
|
|
|
WCHAR *wszFileTmp = A2W(ppArg[nWriteArg]);
|
|
if( IsAsfExtension( wszFileTmp ) )
|
|
{
|
|
AM_MEDIA_TYPE *pmt;
|
|
hr = GetDestinationASFFormat(
|
|
&pmt,
|
|
iASFProfile);
|
|
if(SUCCEEDED(hr)) {
|
|
CopyMediaType(&scompFmt.MediaType, pmt);
|
|
DeleteMediaType(pmt);
|
|
}
|
|
}
|
|
else
|
|
#endif // USE_WMF_CERT
|
|
{
|
|
CComBSTR Filename;
|
|
if(SUCCEEDED(hr)) {
|
|
hr = pSource->GetMediaName(&Filename);
|
|
}
|
|
if(SUCCEEDED(hr)) {
|
|
hr = GetSourceVideoType(Filename, &scompFmt.MediaType);
|
|
}
|
|
}
|
|
|
|
if(SUCCEEDED(hr)) {
|
|
hr = pGroup->SetSmartRecompressFormat( (long*) &scompFmt );
|
|
FreeMediaType(scompFmt.MediaType);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = CoCreateInstance(
|
|
__uuidof(RenderEngine),
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
__uuidof(IRenderEngine),
|
|
(void**) &pRenderEngine );
|
|
|
|
}
|
|
if (pRenderEngine == NULL) {
|
|
printf("Failed to create render engine, error = %x\r\n", hr);
|
|
Ret = -1;
|
|
break;
|
|
}
|
|
|
|
#ifdef USE_WMF_CERT
|
|
|
|
//
|
|
// give the RenderEngine a key to support ASF
|
|
//
|
|
{
|
|
CComQIPtr< IObjectWithSite, &IID_IObjectWithSite > pOWS( pRenderEngine );
|
|
ASSERT( pOWS );
|
|
if( pOWS )
|
|
{
|
|
pOWS->SetSite((IUnknown *) (IServiceProvider *) &g_prov);
|
|
}
|
|
}
|
|
#endif // USE_WMF_CERT
|
|
|
|
long gRenderMedLocFlags = 0 |
|
|
// SFN_VALIDATEF_POPUP |
|
|
// SFN_VALIDATEF_TELLME |
|
|
// SFN_VALIDATEF_VERBOSE |
|
|
// SFN_VALIDATEF_USELOCAL |
|
|
0;
|
|
|
|
pRenderEngine->SetSourceNameValidation( NULL, NULL, gRenderMedLocFlags );
|
|
|
|
// set it's timeline
|
|
//
|
|
hr = pRenderEngine->SetTimelineObject( pTimeline );
|
|
if (FAILED(hr)) {
|
|
printf("Failed to set timeline object, error = %x\r\n", hr);
|
|
Ret = -1;
|
|
break;
|
|
}
|
|
|
|
// set dynamicness
|
|
//
|
|
if (fDynamic) {
|
|
pRenderEngine->SetDynamicReconnectLevel(CONNECTF_DYNAMIC_SOURCES);
|
|
printf("DYNAMIC ON\r\n");
|
|
} else {
|
|
pRenderEngine->SetDynamicReconnectLevel(CONNECTF_DYNAMIC_NONE);
|
|
printf("DYNAMIC OFF\r\n");
|
|
}
|
|
|
|
// set the renderrange, if there is one
|
|
//
|
|
if( RenderStart != -1.0 && RenderStop != -1.0 )
|
|
{
|
|
pRenderEngine->SetRenderRange2( RenderStart, RenderStop );
|
|
}
|
|
|
|
|
|
|
|
// connect up first half
|
|
//
|
|
hr = pRenderEngine->ConnectFrontEnd( );
|
|
|
|
if( FAILED( hr ) )
|
|
{
|
|
printf("ConnectFrontEnd returned bomb code of %x\r\n", hr);
|
|
Ret = -1;
|
|
break;
|
|
}
|
|
|
|
|
|
|
|
// get this now, because other calls will need it
|
|
//
|
|
CComPtr< IGraphBuilder > pGraph;
|
|
hr = pRenderEngine->GetFilterGraph( &pGraph );
|
|
|
|
if (FAILED(hr)) {
|
|
printf("Couldn't get graph! hr = %x", hr);
|
|
break;
|
|
}
|
|
|
|
// if we didn't want to write the file to disk, may as well render it
|
|
//
|
|
if( !fWriteIt )
|
|
{
|
|
hr = pRenderEngine->RenderOutputPins( );
|
|
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
printf("RenderOutputPins returned bomb code of %x\r\n", hr);
|
|
Ret = -1;
|
|
break;
|
|
}
|
|
|
|
if( fNoClock )
|
|
{
|
|
CComQIPtr< IMediaFilter, &IID_IMediaFilter > pMFGraph( pGraph );
|
|
if( pMFGraph )
|
|
{
|
|
pMFGraph->SetSyncSource( NULL );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TurnOffPreviewMode( pTimeline );
|
|
WCHAR *wszFileTmp = A2W(ppArg[nWriteArg]);
|
|
|
|
hr = ConnectOutputFile(pRenderEngine, wszFileTmp
|
|
#ifdef USE_WMF_CERT
|
|
, iASFProfile
|
|
#endif
|
|
);
|
|
|
|
printf( "ConnectOutputFile returned %x\r\n", hr );
|
|
if( FAILED( hr ) )
|
|
{
|
|
fNoRender = TRUE;
|
|
}
|
|
}
|
|
|
|
// write out GRF files
|
|
//
|
|
if( fWriteGrf )
|
|
{
|
|
WCHAR *wszFileTmp = A2W(ppArg[nWriteGrfArg]);
|
|
hr = pXml->WriteGrfFile(pGraph, wszFileTmp);
|
|
printf("WriteGrfFile('%ls') returned %x\r\n", wszFileTmp, hr);
|
|
}
|
|
if( fWriteXtl )
|
|
{
|
|
WCHAR *wszFileTmp = A2W(ppArg[nWriteXtlArg]);
|
|
hr = pXml->WriteXMLFile(pTimeline, wszFileTmp);
|
|
printf("WriteXtlFile('%ls') returned %x\r\n", wszFileTmp, hr);
|
|
}
|
|
|
|
if (!fNoRender) {
|
|
CComQIPtr< IMediaEvent, &IID_IMediaEvent > pEvent( pGraph );
|
|
CComQIPtr< IMediaControl, &IID_IMediaControl > pControl( pGraph );
|
|
hr = pControl->Run( );
|
|
if (FAILED(hr)) {
|
|
printf("Failed to run the graph, error = %x\r\n", hr);
|
|
Ret = -1;
|
|
break;
|
|
}
|
|
|
|
|
|
HANDLE hEvent;
|
|
|
|
if( !pEvent ) {
|
|
// unexpected error
|
|
Ret = -1;
|
|
}
|
|
|
|
hr = pEvent->GetEventHandle((OAEVENT *)&hEvent);
|
|
if(FAILED(hr)) {
|
|
// unexpected error
|
|
Ret = -1;
|
|
}
|
|
|
|
// wait for completion and dispatch messages for any windows
|
|
// created on our thread.
|
|
for(;;)
|
|
{
|
|
while(MsgWaitForMultipleObjects(
|
|
1,
|
|
&hEvent,
|
|
FALSE,
|
|
INFINITE,
|
|
QS_ALLINPUT) != WAIT_OBJECT_0)
|
|
{
|
|
MSG Message;
|
|
|
|
while (PeekMessage(&Message, NULL, 0, 0, TRUE))
|
|
{
|
|
TranslateMessage(&Message);
|
|
DispatchMessage(&Message);
|
|
}
|
|
}
|
|
|
|
// event signaled. see if we're done.
|
|
|
|
LONG levCode;
|
|
LONG_PTR lp1, lp2;
|
|
|
|
if(pEvent->GetEvent(&levCode, &lp1, &lp2, 0) == S_OK)
|
|
{
|
|
EXECUTE_ASSERT(SUCCEEDED(
|
|
pEvent->FreeEventParams(levCode, lp1, lp2)));
|
|
|
|
if(levCode == EC_COMPLETE || levCode == EC_ERRORABORT ||
|
|
levCode == EC_USERABORT)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
hr = pControl->Stop( );
|
|
|
|
|
|
}
|
|
} while(0);
|
|
|
|
pRenderEngine.Release( );
|
|
pXml.Release( );
|
|
if (pTimeline)
|
|
{
|
|
hr = pTimeline->ClearAllGroups();
|
|
pTimeline.Release( );
|
|
}
|
|
|
|
CoUninitialize( );
|
|
|
|
|
|
return Ret;
|
|
}
|
|
|
|
//
|
|
// helper function to traverse a timline and return the first source
|
|
//
|
|
|
|
HRESULT GetFirstSourceOnTimeline(
|
|
IAMTimeline *pTimeline, GUID MajorType,
|
|
IAMTimelineGroup ** ppGroup, IAMTimelineSrc ** ppSource )
|
|
{
|
|
*ppGroup = 0;
|
|
*ppSource = 0;
|
|
|
|
for( long g = 0 ;; g++ )
|
|
{
|
|
// locate group of right media type (audio / video)
|
|
CComPtr< IAMTimelineObj > pGroupObj;
|
|
HRESULT hr = pTimeline->GetGroup( &pGroupObj, g );
|
|
if(FAILED(hr)) {
|
|
break;
|
|
}
|
|
CComQIPtr< IAMTimelineGroup, &IID_IAMTimelineGroup > pGroup( pGroupObj );
|
|
|
|
CMediaType GroupType;
|
|
pGroup->GetMediaType( &GroupType );
|
|
if( GroupType.majortype != MajorType ) {
|
|
continue;
|
|
}
|
|
|
|
// found the right group, go through each track in it
|
|
|
|
long TrackCount = 0;
|
|
long Layers = 0;
|
|
pTimeline->GetCountOfType( g, &TrackCount, &Layers, TIMELINE_MAJOR_TYPE_TRACK );
|
|
|
|
if( TrackCount < 1 )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
CComQIPtr< IAMTimelineComp, &IID_IAMTimelineComp > pGroupComp( pGroupObj );
|
|
|
|
for( int CurrentLayer = 0 ; CurrentLayer < Layers ; CurrentLayer++ )
|
|
{
|
|
// get the layer itself
|
|
//
|
|
CComPtr< IAMTimelineObj > pLayer;
|
|
hr = pGroupComp->GetRecursiveLayerOfType( &pLayer, CurrentLayer, TIMELINE_MAJOR_TYPE_TRACK );
|
|
|
|
// ask if the layer is muted
|
|
//
|
|
BOOL LayerMuted = FALSE;
|
|
pLayer->GetMuted( &LayerMuted );
|
|
if( LayerMuted )
|
|
{
|
|
// don't look at this layer
|
|
//
|
|
continue; // skip this layer, don't worry about grid
|
|
}
|
|
|
|
CComQIPtr< IAMTimelineTrack, &IID_IAMTimelineTrack > pTrack( pLayer );
|
|
if( !pTrack )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
REFERENCE_TIME InOut = 0;
|
|
while( 1 )
|
|
{
|
|
// get the next source on this layer, given a time.
|
|
//
|
|
CComPtr< IAMTimelineObj > pSourceObj;
|
|
hr = pTrack->GetNextSrc( &pSourceObj, &InOut );
|
|
ASSERT( !FAILED( hr ) );
|
|
if( hr != NOERROR )
|
|
{
|
|
// all done with sources
|
|
//
|
|
break;
|
|
}
|
|
|
|
CComQIPtr< IAMTimelineSrc, &IID_IAMTimelineSrc > pSource( pSourceObj );
|
|
ASSERT( pSource );
|
|
if( !pSource )
|
|
{
|
|
// this one failed, so look at the next
|
|
//
|
|
continue; // sources
|
|
}
|
|
|
|
// ask if the source is muted
|
|
//
|
|
BOOL SourceMuted = FALSE;
|
|
pSourceObj->GetMuted( &SourceMuted );
|
|
if( SourceMuted )
|
|
{
|
|
// don't look at this source
|
|
//
|
|
continue; // sources
|
|
}
|
|
|
|
*ppSource = pSource;
|
|
pSource.p->AddRef();
|
|
*ppGroup = pGroup;
|
|
pGroup.p->AddRef();
|
|
|
|
return S_OK;
|
|
|
|
} // while sources
|
|
|
|
|
|
} // while currentlayer
|
|
|
|
} // for all groups
|
|
|
|
return VFW_E_NOT_FOUND;
|
|
}
|
|
|
|
void TurnOffPreviewMode( IAMTimeline * pTimeline )
|
|
{
|
|
long Groups = 0;
|
|
pTimeline->GetGroupCount( &Groups );
|
|
for( int i = 0 ; i < Groups ; i++ )
|
|
{
|
|
CComPtr< IAMTimelineObj > pGroupObj;
|
|
pTimeline->GetGroup( &pGroupObj, i );
|
|
CComQIPtr< IAMTimelineGroup, &IID_IAMTimelineGroup > pGroup( pGroupObj );
|
|
pGroup->SetPreviewMode( FALSE );
|
|
}
|
|
}
|
|
|
|
//
|
|
// uses the media detector to retrieve mediatype of a clip
|
|
//
|
|
HRESULT GetSourceVideoType(WCHAR *wszFilename, AM_MEDIA_TYPE *pmt)
|
|
{
|
|
CComPtr< IMediaDet > pDet;
|
|
HRESULT hr = CoCreateInstance( CLSID_MediaDet,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_IMediaDet,
|
|
(void**) &pDet );
|
|
if( FAILED( hr ) ) {
|
|
return hr;
|
|
}
|
|
|
|
#ifdef USE_WMF_CERT
|
|
|
|
//
|
|
// set the site provider on the MediaDet object to allowed keyed apps
|
|
// to use ASF decoder
|
|
//
|
|
CComQIPtr< IObjectWithSite, &IID_IObjectWithSite > pOWS( pDet );
|
|
if( pOWS )
|
|
{
|
|
pOWS->SetSite( (IServiceProvider *) &g_prov );
|
|
}
|
|
|
|
#endif // USE_WMF_CERT
|
|
|
|
hr = pDet->put_Filename( wszFilename );
|
|
if( FAILED( hr ) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
// go through and find the video stream type
|
|
//
|
|
long Streams = 0;
|
|
long VideoStream = -1;
|
|
hr = pDet->get_OutputStreams( &Streams );
|
|
for( int i = 0 ; i < Streams ; i++ )
|
|
{
|
|
pDet->put_CurrentStream( i );
|
|
GUID Major = GUID_NULL;
|
|
pDet->get_StreamType( &Major );
|
|
if( Major == MEDIATYPE_Video )
|
|
{
|
|
VideoStream = i;
|
|
break;
|
|
}
|
|
}
|
|
if( VideoStream == -1 )
|
|
{
|
|
return VFW_E_INVALIDMEDIATYPE;
|
|
}
|
|
|
|
hr = pDet->get_StreamMediaType( pmt );
|
|
return hr;
|
|
}
|
|
|
|
|
|
#ifdef USE_WMF_CERT
|
|
|
|
// ------------------------------------------------------------------------
|
|
// CKeyProvider methods
|
|
//
|
|
|
|
HRESULT CKeyProvider::QueryInterface(REFIID riid, void ** ppv)
|
|
{
|
|
if (riid == IID_IServiceProvider || riid == IID_IUnknown) {
|
|
*ppv = (void *) static_cast<IServiceProvider *>(this);
|
|
return NOERROR;
|
|
}
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CKeyProvider::QueryService(REFIID siid, REFIID riid, void **ppv)
|
|
{
|
|
if (siid == __uuidof(IWMReader) && riid == IID_IUnknown) {
|
|
|
|
IUnknown *punkCert;
|
|
|
|
HRESULT hr = WMCreateCertificate( &punkCert );
|
|
if (SUCCEEDED(hr)) {
|
|
*ppv = (void *) punkCert;
|
|
}
|
|
return hr;
|
|
}
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
#endif // USE_WMF_CERT
|
|
|
|
// ------------------------------------------------------------------------
|
|
// CErrorReporter methods
|
|
//
|
|
|
|
STDMETHODIMP CErrorReporter::LogError( long Severity, BSTR ErrorString, LONG ErrorCode, HRESULT hresult, VARIANT * pExtraInfo )
|
|
{
|
|
USES_CONVERSION;
|
|
printf( "Error %d (HRESULT %x)\r\n", ErrorCode, hresult);
|
|
char * t = W2A( ErrorString );
|
|
printf( t );
|
|
printf( "\r\n" );
|
|
|
|
#ifdef DEBUG
|
|
OutputDebugStringA( t );
|
|
OutputDebugString( TEXT("\r\n") );
|
|
#endif // DEBUG
|
|
|
|
// look at variant
|
|
//
|
|
if( pExtraInfo )
|
|
{
|
|
if( pExtraInfo->vt == VT_BSTR )
|
|
{
|
|
printf( "Extra Info:%ls\r\n", pExtraInfo->bstrVal);
|
|
} else if( pExtraInfo->vt == VT_I4 ) {
|
|
printf( "Extra Info: %d\r\n", (int)pExtraInfo->lVal );
|
|
} else if( pExtraInfo->vt == VT_R8 ) {
|
|
printf( "Extra Info: %d/100\r\n",
|
|
(int)(V_R8(pExtraInfo) * 100));
|
|
}
|
|
}
|
|
return hresult;
|
|
}
|
|
|
|
#ifdef USE_WMF_CERT
|
|
|
|
// list formats Windows Media Format SDK supports.
|
|
//
|
|
void ListWMSDKProfiles()
|
|
{
|
|
USES_CONVERSION;
|
|
|
|
int wextent = 0 ;
|
|
int Loop = 0 ;
|
|
SIZE extent ;
|
|
DWORD cProfiles = 0 ;
|
|
|
|
printf("Standard system profiles:\n");
|
|
|
|
CComPtr <IWMProfileManager> pIWMProfileManager;
|
|
|
|
HRESULT hr = WMCreateProfileManager( &pIWMProfileManager );
|
|
if( FAILED( hr ) ) {
|
|
return; // error
|
|
}
|
|
|
|
IWMProfileManager2* pIPM2;
|
|
hr = pIWMProfileManager->QueryInterface( IID_IWMProfileManager2,
|
|
( void ** )&pIPM2 );
|
|
if(FAILED(hr)) {
|
|
return;
|
|
}
|
|
// we only use 7_0 profiles
|
|
hr = pIPM2->SetSystemProfileVersion( WMT_VER_7_0 );
|
|
pIPM2->Release();
|
|
|
|
if(FAILED(hr)) {
|
|
return;
|
|
}
|
|
|
|
hr = pIWMProfileManager->GetSystemProfileCount( &cProfiles );
|
|
if( FAILED( hr ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// now load the profile strings
|
|
//
|
|
LRESULT ix;
|
|
DWORD cchName, cchDescription;
|
|
for (int i = 0; i < (int)cProfiles; ++i) {
|
|
CComPtr <IWMProfile> pIWMProfile;
|
|
|
|
hr = pIWMProfileManager->LoadSystemProfile( i, &pIWMProfile );
|
|
if( FAILED( hr ) )
|
|
return;
|
|
|
|
hr = pIWMProfile->GetName( NULL, &cchName );
|
|
if( FAILED( hr ) )
|
|
return;
|
|
|
|
WCHAR *wszProfile = new WCHAR[ cchName ];
|
|
if( NULL == wszProfile )
|
|
return;
|
|
|
|
hr = pIWMProfile->GetName( wszProfile, &cchName );
|
|
if( FAILED( hr ) )
|
|
return;
|
|
|
|
hr = pIWMProfile->GetDescription( NULL, &cchDescription );
|
|
if( FAILED( hr ) )
|
|
return;
|
|
|
|
WCHAR *wszDescription = new WCHAR[ cchDescription ];
|
|
if( NULL == wszDescription )
|
|
return;
|
|
|
|
|
|
hr = pIWMProfile->GetDescription( wszDescription, &cchDescription );
|
|
if( FAILED( hr ) )
|
|
return;
|
|
|
|
|
|
printf(" %3d: %ls\n", i, wszProfile);
|
|
|
|
delete[] wszProfile;
|
|
delete[] wszDescription;
|
|
}
|
|
|
|
printf("\r\n Use /p [#] to choose the profile you want\r\n");
|
|
}
|
|
#endif // USE_WMF_CERT
|
|
|
|
//
|
|
// helper function to return Nth input pin or output pin on a
|
|
// filter. pin is addref'd
|
|
//
|
|
IPin * GetPin( IBaseFilter * pFilter, int PinNum, PIN_DIRECTION pd )
|
|
{
|
|
CComPtr<IEnumPins> pEnum;
|
|
HRESULT hr = pFilter->EnumPins( &pEnum );
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
ULONG cFetched;
|
|
IPin * pPin;
|
|
while(pEnum->Next( 1, &pPin, &cFetched) == S_OK)
|
|
{
|
|
PIN_DIRECTION pd2;
|
|
pPin->QueryDirection( &pd2 );
|
|
if( pd2 == pd )
|
|
{
|
|
if( PinNum == 0 )
|
|
{
|
|
// return addref'd pin
|
|
return pPin;
|
|
}
|
|
PinNum--;
|
|
}
|
|
|
|
pPin->Release();
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Configure the graph to write to an AVI file (or ASF or .WAV)
|
|
//
|
|
|
|
HRESULT ConnectOutputFile(
|
|
IRenderEngine * pEngine, WCHAR * Filename
|
|
#ifdef USE_WMF_CERT
|
|
, int iProfile
|
|
#endif
|
|
)
|
|
{
|
|
CComPtr< ICaptureGraphBuilder2 > pBuilder;
|
|
HRESULT hr = CoCreateInstance(
|
|
CLSID_CaptureGraphBuilder2,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_ICaptureGraphBuilder2,
|
|
(void**) &pBuilder );
|
|
if( FAILED( hr ) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
CComPtr< IAMTimeline > pTimeline;
|
|
hr = pEngine->GetTimelineObject( &pTimeline );
|
|
if( FAILED( hr ) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
CComPtr< IGraphBuilder > pGraph;
|
|
hr = pEngine->GetFilterGraph( &pGraph );
|
|
if( FAILED( hr ) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
long Groups = 0;
|
|
pTimeline->GetGroupCount( &Groups );
|
|
if( !Groups )
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
CComPtr< IBaseFilter > pMux;
|
|
CComPtr< IFileSinkFilter > pWriter;
|
|
|
|
hr = pBuilder->SetFiltergraph( pGraph );
|
|
if( FAILED( hr ) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
|
|
bool fConnectManually = false;
|
|
GUID guid = MEDIASUBTYPE_Avi;
|
|
|
|
// determine which writer to use based on file extension
|
|
if (lstrlenW(Filename) > 4)
|
|
{
|
|
WCHAR *pExt = Filename + lstrlenW(Filename) - 3;
|
|
|
|
#ifdef USE_WMF_CERT
|
|
if(IsAsfExtension(pExt))
|
|
{
|
|
guid = CLSID_WMAsfWriter;
|
|
}
|
|
#endif // USE_WMF_CERT
|
|
if (lstrcmpiW(pExt, L"wav") == 0)
|
|
{
|
|
fConnectManually = true;
|
|
|
|
// creation of the wav writer filter SDK sample
|
|
//
|
|
// .wav files need to be special-cased here because the
|
|
// capture graph builder doesn't know about the wav writer
|
|
// filter.
|
|
//
|
|
// {3C78B8E2-6C4D-11d1-ADE2-0000F8754B99}
|
|
const CLSID CLSID_WavDest = {
|
|
0x3C78B8E2,0x6C4D,0x11D1,{0xAD,0xE2,0x00,0x00,0xF8,0x75,0x4B,0x99}
|
|
};
|
|
|
|
hr = CoCreateInstance(
|
|
CLSID_WavDest,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_IBaseFilter,
|
|
(void**) &pMux );
|
|
if( SUCCEEDED( hr ) )
|
|
{
|
|
hr = pGraph->AddFilter( pMux, L"Wave Mux" );
|
|
}
|
|
|
|
if( SUCCEEDED( hr ) )
|
|
{
|
|
hr = CoCreateInstance(
|
|
CLSID_FileWriter,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_IFileSinkFilter,
|
|
(void**) &pWriter );
|
|
}
|
|
if( SUCCEEDED( hr ) )
|
|
{
|
|
hr = pWriter->SetFileName( Filename, NULL );
|
|
}
|
|
if( SUCCEEDED( hr ) )
|
|
{
|
|
CComQIPtr< IBaseFilter, &IID_IBaseFilter > pWriterBase( pWriter );
|
|
hr = pGraph->AddFilter( pWriterBase, L"Writer" );
|
|
}
|
|
}
|
|
}
|
|
|
|
if(FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
|
|
if(!fConnectManually)
|
|
{
|
|
hr = pBuilder->SetOutputFileName(
|
|
&guid, Filename, &pMux, &pWriter );
|
|
}
|
|
if( FAILED( hr ) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
CComQIPtr<IConfigInterleaving, &IID_IConfigInterleaving> pConfigInterleaving(pMux);
|
|
if(pConfigInterleaving) {
|
|
pConfigInterleaving->put_Mode(INTERLEAVE_FULL);
|
|
}
|
|
CComQIPtr<IFileSinkFilter2, &IID_IFileSinkFilter2> pCfgFw(pWriter);
|
|
if(pCfgFw) {
|
|
pCfgFw->SetMode(AM_FILE_OVERWRITE);
|
|
}
|
|
|
|
#ifdef USE_WMF_CERT
|
|
CComQIPtr<IConfigAsfWriter, &IID_IConfigAsfWriter> pConfigAsfWriter(pMux);
|
|
if(pConfigAsfWriter && iProfile >= 0)
|
|
{
|
|
CComPtr<IWMProfile> pProfile;
|
|
hr = MapProfileIdToProfile(iProfile, &pProfile);
|
|
if(FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
|
|
// note that the ASF writer will not run if the number of streams
|
|
// does not match the profile.
|
|
hr = pConfigAsfWriter->ConfigureFilterUsingProfile(pProfile);
|
|
if(FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
for( int g = 0 ; g < Groups ; g++ )
|
|
{
|
|
CComPtr< IPin > pPin;
|
|
hr = pEngine->GetGroupOutputPin( g, &pPin );
|
|
if( FAILED( hr ) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
// connect pin to the mux
|
|
//
|
|
hr = pBuilder->RenderStream( NULL, NULL, pPin, NULL, pMux );
|
|
if( FAILED( hr ) )
|
|
{
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
if( fConnectManually )
|
|
{
|
|
// connect the mux to the writer. the wav mux refuses to
|
|
// connect its output pin until its input pin is
|
|
// connected. Otherwise this could be done earlier.
|
|
//
|
|
CComQIPtr< IBaseFilter, &IID_IBaseFilter > pWriterBase( pWriter );
|
|
CComPtr<IPin> pMuxOut, pWriterIn;
|
|
pMuxOut.p = GetPin( pMux, 0, PINDIR_OUTPUT );
|
|
pWriterIn.p = GetPin( pWriterBase, 0, PINDIR_INPUT );
|
|
if(!pMuxOut || !pWriterIn) {
|
|
return E_UNEXPECTED;
|
|
}
|
|
hr = pGraph->Connect( pMuxOut, pWriterIn );
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
BOOL IsAsfExtension( WCHAR *Filename )
|
|
{
|
|
if (lstrlenW(Filename) >= 3)
|
|
{
|
|
WCHAR *pExt = Filename + lstrlenW(Filename) - 3;
|
|
|
|
if(lstrcmpiW(pExt, L"asf") == 0 ||
|
|
lstrcmpiW(pExt, L"wma") == 0 ||
|
|
lstrcmpiW(pExt, L"wmv") == 0)
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Get the mediatype for a particular ASF profile. it does this using
|
|
// the DirectShow components that use the WMSDK rather than through
|
|
// the WMSDK directly.
|
|
//
|
|
|
|
#ifdef USE_WMF_CERT
|
|
|
|
HRESULT GetDestinationASFFormat(
|
|
AM_MEDIA_TYPE **ppmt,
|
|
int iProfile
|
|
)
|
|
{
|
|
*ppmt = 0;
|
|
|
|
CComPtr< IConfigAsfWriter > pWriter;
|
|
HRESULT hr = CoCreateInstance(
|
|
CLSID_WMAsfWriter,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_IConfigAsfWriter,
|
|
(void **)&pWriter);
|
|
if(FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
|
|
CComQIPtr<IBaseFilter,&IID_IBaseFilter> pwf(pWriter);
|
|
if(!pWriter) {
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
// we have to add the ASF writer to a dummy graph in order for the
|
|
// calls to work.
|
|
CComPtr< IGraphBuilder > pBuilder;
|
|
hr = CoCreateInstance(
|
|
CLSID_FilterGraph,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_IGraphBuilder,
|
|
(void**) &pBuilder );
|
|
if( FAILED( hr ) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// give the graph a key to support ASF
|
|
//
|
|
{
|
|
CComQIPtr< IObjectWithSite, &IID_IObjectWithSite > pOWS( pBuilder );
|
|
ASSERT( pOWS );
|
|
if( pOWS )
|
|
{
|
|
pOWS->SetSite((IUnknown *) (IServiceProvider *) &g_prov);
|
|
}
|
|
}
|
|
|
|
hr = pBuilder->AddFilter(pwf, 0);
|
|
if(FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
|
|
// we use profile id -1 to indicate no profile was set.
|
|
if (iProfile >= 0)
|
|
{
|
|
CComPtr<IWMProfile> pProfile;
|
|
hr = MapProfileIdToProfile(iProfile, &pProfile);
|
|
if(FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
hr = pWriter->ConfigureFilterUsingProfile(pProfile);
|
|
if(FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
CComPtr<IEnumPins> pEnumPins;
|
|
hr = pwf->EnumPins( &pEnumPins );
|
|
if(FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
|
|
// get destination video format from ASF writer using IAMStreamConfig::GetFormat on
|
|
// its input pins
|
|
int iPins = 0;
|
|
ULONG ul = 0;
|
|
CComPtr < IPin > pWriterInputPin; // we only expect to find input pins
|
|
while( S_OK == pEnumPins->Next( 1, &pWriterInputPin, &ul ) )
|
|
{
|
|
#ifdef DEBUG
|
|
{
|
|
PIN_DIRECTION pd;
|
|
pWriterInputPin->QueryDirection( &pd );
|
|
ASSERT( PINDIR_OUTPUT != pd );
|
|
}
|
|
#endif
|
|
CComQIPtr <IAMStreamConfig, &IID_IAMStreamConfig> pStreamConfig( pWriterInputPin);
|
|
if( !pStreamConfig ) {
|
|
return E_UNEXPECTED;
|
|
}
|
|
pWriterInputPin.Release();
|
|
|
|
AM_MEDIA_TYPE *pmt2;
|
|
hr = pStreamConfig->GetFormat( &pmt2 );
|
|
if( SUCCEEDED( hr ) )
|
|
{
|
|
if(pmt2->majortype != MEDIATYPE_Video) {
|
|
DeleteMediaType(pmt2);
|
|
continue;
|
|
}
|
|
*ppmt = pmt2; // caller needs to delete
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
return E_FAIL;
|
|
}
|
|
|
|
|
|
HRESULT MapProfileIdToProfile(
|
|
int iProfile, IWMProfile **ppProfile)
|
|
{
|
|
*ppProfile = 0;
|
|
|
|
CComPtr <IWMProfileManager> pIWMProfileManager;
|
|
HRESULT hr = WMCreateProfileManager( &pIWMProfileManager );
|
|
if(FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
|
|
// we only use 7_0 profiles
|
|
CComQIPtr<IWMProfileManager2, &IID_IWMProfileManager2> pIPM2(pIWMProfileManager);
|
|
if(!pIWMProfileManager) {
|
|
return E_UNEXPECTED;
|
|
}
|
|
hr = pIPM2->SetSystemProfileVersion( WMT_VER_7_0 );
|
|
if(FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
|
|
DWORD cProfiles;
|
|
hr = pIWMProfileManager->GetSystemProfileCount( &cProfiles );
|
|
if(FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
if( (DWORD)iProfile >= cProfiles ) {
|
|
printf("Invalid profile: %d\n", iProfile);
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
return pIWMProfileManager->LoadSystemProfile( iProfile, ppProfile );
|
|
}
|
|
|
|
#endif // USE_WMF_CERT
|