//------------------------------------------------------------------------------ // File: ASFCopy.cpp // // Desc: DirectShow sample code - ASF copy. // // Copyright (c) 1999-2001 Microsoft Corporation. All rights reserved. //------------------------------------------------------------------------------ #include // Disable warning C4268, which is generated within #pragma warning(disable:4268) #include #pragma warning(default:4268) #include #include #include #include // // Build warning to remind developers of the dependency on the // Windows Media Format SDK libraries, which do not ship with // the DirectX SDK. // #pragma message("NOTE: To link and run this sample, you must install the Windows Media Format SDK.") #pragma message("After signing a license agreement with Microsoft, you will receive a") #pragma message("unique version of WMStub.LIB, which should be added to this VC++ project.") #pragma message("Without this library, you will receive linker errors for the following:") #pragma message(" WMCreateCertificate") #pragma message("You must also add WMVCore.LIB to the linker settings to resolve the following:") #pragma message(" WMCreateProfileManager") // Global data BOOL fVerbose = FALSE; // Function prototypes HRESULT MapProfileIdToProfile(int iProfile, IWMProfile **ppProfile); class CKeyProvider : public IServiceProvider { public: // // IUnknown interface // STDMETHODIMP QueryInterface(REFIID riid, void ** ppv); STDMETHODIMP_(ULONG) AddRef(); STDMETHODIMP_(ULONG) Release(); CKeyProvider(); // IServiceProvider STDMETHODIMP QueryService(REFIID siid, REFIID riid, void **ppv); private: ULONG m_cRef; }; CKeyProvider::CKeyProvider() : m_cRef(0) { } ////////////////////////////////////////////////////////////////////////// // // IUnknown methods // ////////////////////////////////////////////////////////////////////////// ULONG CKeyProvider::AddRef() { return ++m_cRef; } ULONG CKeyProvider::Release() { ASSERT(m_cRef > 0); m_cRef--; if(m_cRef == 0) { delete this; // don't return m_cRef, because the object doesn't exist anymore return((ULONG) 0); } return(m_cRef); } // // QueryInterface // // We only support IUnknown and IServiceProvider // HRESULT CKeyProvider::QueryInterface(REFIID riid, void ** ppv) { if(riid == IID_IServiceProvider || riid == IID_IUnknown) { *ppv = (void *) static_cast(this); AddRef(); 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; else printf("CKeyProvider::QueryService failed to create certificate! hr=0x%x\n", hr); return hr; } return E_NOINTERFACE; } HRESULT FindPinOnFilter( IBaseFilter * pFilter, PIN_DIRECTION PinDir, DWORD dwPin, BOOL fConnected, IPin ** ppPin ) { HRESULT hr = S_OK; IEnumPins * pEnumPin = NULL; IPin * pConnectedPin = NULL; PIN_DIRECTION PinDirection; ULONG ulFetched; DWORD nFound = 0; ASSERT( pFilter != NULL ); *ppPin = NULL; hr = pFilter->EnumPins( &pEnumPin ); if(SUCCEEDED(hr)) { while ( S_OK == ( hr = pEnumPin->Next( 1L, ppPin, &ulFetched ) ) ) { hr = (*ppPin)->ConnectedTo( &pConnectedPin ); if (pConnectedPin) { pConnectedPin->Release(); pConnectedPin = NULL; } if ( ( ( VFW_E_NOT_CONNECTED == hr ) && !fConnected ) || ( ( S_OK == hr ) && fConnected ) ) { hr = (*ppPin)->QueryDirection( &PinDirection ); if ( ( S_OK == hr ) && ( PinDirection == PinDir ) ) { if ( nFound == dwPin ) break; nFound++; } } (*ppPin)->Release(); } } pEnumPin->Release(); return hr; } // FindPinOnFilter HRESULT GetPin(IBaseFilter *pFilter, DWORD dwPin, IPin **ppPin) { IEnumPins *pins; *ppPin = NULL; HRESULT hr = pFilter->EnumPins(&pins); if(FAILED(hr)) { DbgLog((LOG_ERROR,1,TEXT("EnumPins failed! (%x)\n"), hr)); return hr; } if(dwPin > 0) { hr = pins->Skip(dwPin); if(FAILED(hr)) { DbgLog((LOG_ERROR,1,TEXT("Skip(%d) failed! (%x)\n"), dwPin, hr)); pins->Release(); return hr; } if(hr == S_FALSE) { DbgLog((LOG_ERROR,1,TEXT("Skip(%d) ran out of pins!\n"), dwPin)); pins->Release(); return hr; } } DWORD n; hr = pins->Next(1, ppPin, &n); if(FAILED(hr)) { DbgLog((LOG_ERROR,1,TEXT("Next() failed! (%x)\n"), hr)); } if(hr == S_FALSE) { DbgLog((LOG_ERROR,1,TEXT("Next() ran out of pins! \n"))); pins->Release(); return hr; } pins->Release(); return hr; } void ListProfiles() { USES_CONVERSION; int wextent = 0, Loop = 0; DWORD cProfiles = 0; DWORD cchName, cchDescription; CComPtr pIWMProfileManager; printf("Standard system profiles:\n"); HRESULT hr = WMCreateProfileManager(&pIWMProfileManager); if(FAILED(hr)) { printf("ListProfiles: Failed to create profile manager! hr=0x%x\n", hr); return; // error } CComQIPtr pIPM2(pIWMProfileManager); if(!pIPM2) { printf("ListProfiles: Failed to QI IWMProfileManager2! hr=0x%x\n", hr); return; } // we only use 7_0 profiles hr = pIPM2->SetSystemProfileVersion( WMT_VER_7_0 ); if(FAILED(hr)) { printf("ListProfiles: Failed to set system profile version! hr=0x%x\n", hr); return; } hr = pIWMProfileManager->GetSystemProfileCount(&cProfiles); if(FAILED(hr)) { printf("ListProfiles: Failed to get system profile count! hr=0x%x\n", hr); return; } // Load the profile strings for(int i = 0; i < (int)cProfiles; ++i) { CComPtr pIWMProfile; hr = pIWMProfileManager->LoadSystemProfile(i, &pIWMProfile); if(FAILED(hr)) { printf("ListProfiles: Failed to load system profile! hr=0x%x\n", hr); return; } // How large is the profile name? hr = pIWMProfile->GetName(NULL, &cchName); if(FAILED(hr)) { printf("ListProfiles: Failed to read profile name size! hr=0x%x\n", hr); return; } WCHAR *wszProfile = new WCHAR[ cchName + 1 ]; if(NULL == wszProfile) return; hr = pIWMProfile->GetName(wszProfile, &cchName); if(FAILED(hr)) { printf("ListProfiles: Failed to read profile name! hr=0x%x\n", hr); return; } // How large is the description? hr = pIWMProfile->GetDescription(NULL, &cchDescription); if(FAILED(hr)) { printf("ListProfiles: Failed to read profile description size! hr=0x%x\n", hr); return; } WCHAR *wszDescription = new WCHAR[ cchDescription + 1 ]; if(NULL == wszDescription) return; hr = pIWMProfile->GetDescription(wszDescription, &cchDescription); if(FAILED(hr)) { printf("ListProfiles: Failed to read profile description! hr=0x%x\n", hr); return; } // Display the profile name and description if (fVerbose) printf(" %3d: %ls \n[%ls]\n\n", i, wszProfile, wszDescription); else printf(" %3d: %ls\n", i, wszProfile); delete[] wszProfile; delete[] wszDescription; } } //======================= // CreateFilterGraph //======================= HRESULT CreateFilterGraph(IGraphBuilder **pGraph) { HRESULT hr; hr = CoCreateInstance(CLSID_FilterGraph, // get the graph object NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **) pGraph); if(FAILED(hr)) { printf("CreateFilterGraph: Failed to create graph! hr=0x%x\n", hr); *pGraph = NULL; return hr; } return S_OK; } HRESULT CreateFilter(REFCLSID clsid, IBaseFilter **ppFilter) { HRESULT hr; hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void **) ppFilter); if(FAILED(hr)) { printf("CreateFilter: Failed to create filter! hr=0x%x\n", hr); *ppFilter = NULL; return hr; } return S_OK; } HRESULT SetNoClock(IFilterGraph *graph) { // Keep a useless clock from being instantiated.... IMediaFilter *graphF; HRESULT hr = graph->QueryInterface(IID_IMediaFilter, (void **) &graphF); if(SUCCEEDED(hr)) { hr = graphF->SetSyncSource(NULL); if (FAILED(hr)) printf("SetNoClock: Failed to set sync source! hr=0x%x\n", hr); graphF->Release(); } else { printf("SetNoClock: Failed to QI for media filter! hr=0x%x\n", hr); } return hr; } HRESULT MapProfileIdToProfile(int iProfile, IWMProfile **ppProfile) { DWORD cProfiles; *ppProfile = 0; CComPtr pIWMProfileManager; HRESULT hr = WMCreateProfileManager( &pIWMProfileManager ); if(FAILED(hr)) { printf("MapProfile: Failed to create profile manager! hr=0x%x\n", hr); return hr; } // We only use 7_0 profiles CComQIPtr pIPM2(pIWMProfileManager); if(!pIPM2) { printf("MapProfile: Failed to QI IWMProfileManager2!\n"); return E_UNEXPECTED; } hr = pIPM2->SetSystemProfileVersion( WMT_VER_7_0 ); if(FAILED(hr)) { printf("MapProfile: Failed to set system profile version! hr=0x%x\n", hr); return hr; } hr = pIWMProfileManager->GetSystemProfileCount( &cProfiles ); if(FAILED(hr)) { printf("MapProfile: Failed to get system profile count! hr=0x%x\n", hr); return hr; } // Invalid profile requested? if( (DWORD)iProfile >= cProfiles ) { printf("Invalid profile: %d\n", iProfile); return E_INVALIDARG; } return (pIWMProfileManager->LoadSystemProfile( iProfile, ppProfile )); } void WaitForCompletion( IGraphBuilder *pGraph ) { HRESULT hr; LONG lEvCode = 0; IMediaEvent *pEvent; pGraph->QueryInterface(IID_IMediaEvent, (void **) &pEvent); printf("Waiting for completion...\n This could take several minutes, " "depending on file size and selected profile.\n"); do { MSG Message; while(PeekMessage(&Message, NULL, 0, 0, TRUE)) { TranslateMessage(&Message); DispatchMessage(&Message); } hr = pEvent->WaitForCompletion(10, &lEvCode); } while(lEvCode == 0); pEvent->Release(); } HRESULT CopyASF(int argc, char *argv[]) { HRESULT hr; WCHAR SourceFile[256], TargetFile[256]; BOOL fListProfiles = TRUE; DWORD dwProfile=0; int i = 1; // Parse command line options while(i < argc && (argv[i][0] == '-' || argv[i][0] == '/')) { // options if(lstrcmpiA(argv[i] + 1, "v") == 0) { fVerbose = TRUE; printf("Verbose mode enabled.\n"); } else if((i+1 < argc) && lstrcmpiA(argv[i] + 1, "p") == 0) { fListProfiles = FALSE; dwProfile = atoiA(argv[i+1]); i++; // skip two args here } i++; } // List profiles only? if(fListProfiles) { printf("Usage: asfcopy [/v] /p profnum file1 [ file2 ...] target\n\n"); HRESULT hr = CoInitialize(NULL); ListProfiles(); CoUninitialize(); return -1; } // Fail with usage information if improper number of arguments if(argc < i+2) { printf("Usage: asfcopy [/v] /p profnum file1 [ file2 ...] target\n"); return -1; } CComPtr pGraph; CComPtr pObjectWithSite; CComPtr pMux; CComPtr pWriter; CComPtr pFS; CComPtr pConfigInterleaving; CComPtr pConfigAsfWriter; CComPtr pGraphC; // Convert target filename MultiByteToWideChar(CP_ACP, 0, argv[argc - 1], -1, TargetFile, 256); hr = CreateFilterGraph(&pGraph); if(FAILED(hr)) { printf("Couldn't create filter graph! hr=0x%x", hr); return hr; } CKeyProvider prov; prov.AddRef(); // Don't let COM try to free our static object // Give the graph a pointer to us for callbacks & QueryService hr = pGraph->QueryInterface(IID_IObjectWithSite, (void**)&pObjectWithSite); if(SUCCEEDED(hr)) { hr = pObjectWithSite->SetSite((IUnknown *) (IServiceProvider *) &prov); if(FAILED(hr)) { printf("Failed to set service provider! hr=0x%x\n", hr); return hr; } } hr = CreateFilter(CLSID_WMAsfWriter, &pMux); if(FAILED(hr)) { printf("Failed to create WMAsfWriter filter! hr=0x%x\n", hr); return hr; } hr = pMux->QueryInterface(IID_IFileSinkFilter, (void **) &pFS); if(FAILED(hr)) { // We need a writer also hr = CreateFilter(CLSID_FileWriter, &pWriter); if(FAILED(hr)) { printf("Failed to create FileWriter filter! hr=0x%x\n", hr); return hr; } else { hr = pWriter->QueryInterface(IID_IFileSinkFilter, (void **) &pFS); if(FAILED(hr)) { printf("Failed to create QI IFileSinkFilter! hr=0x%x\n", hr); return hr; } } } hr = pFS->SetFileName(TargetFile, NULL); if(FAILED(hr)) { printf("Failed to set target filename! hr=0x%x\n", hr); return hr; } hr = pGraph->AddFilter(pMux, L"Mux"); if(FAILED(hr)) { printf("Failed to add Mux filter to graph! hr=0x%x\n", hr); return hr; } // Set interleaving mode to FULL // !!! ASF won't support this, but that's okay hr = pMux->QueryInterface(IID_IConfigInterleaving, (void **) &pConfigInterleaving); if(SUCCEEDED(hr)) { printf("Setting interleaving mode to INTERLEAVE_FULL\r\n"); hr = pConfigInterleaving->put_Mode(INTERLEAVE_FULL); } // !!! We should only require a profile if we're using a filter which needs it hr = pMux->QueryInterface(IID_IConfigAsfWriter, (void **) &pConfigAsfWriter); if(SUCCEEDED(hr)) { if (fVerbose) printf("Setting profile to %d\r\n", dwProfile); CComPtr pProfile; hr = MapProfileIdToProfile(dwProfile, &pProfile); if(FAILED(hr)) { printf("Failed to map profile ID! hr=0x%x\n", 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)) { printf("Failed to configure filter to use profile! hr=0x%x\n", hr); return hr; } } else { printf("Failed to QI for IConfigAsfWriter! hr=0x%x\n", hr); return hr; } // Connect writer filter if needed if(pWriter) { IPin *pMuxOut, *pWriterIn; hr = pGraph->AddFilter(pWriter, L"Writer"); if(FAILED(hr)) { printf("Failed to add FileWriter filter to graph! hr=0x%x\n", hr); return hr; } // Look for the first unconnected output pin hr = FindPinOnFilter(pMux, PINDIR_OUTPUT, 0, FALSE, &pMuxOut); if(FAILED(hr)) { printf("Failed to find output pin on Mux! hr=0x%x\n", hr); return hr; } // Find the first connected pin hr = FindPinOnFilter(pWriter, PINDIR_INPUT, 0, FALSE, &pWriterIn); if(FAILED(hr)) { printf("Failed to find input pin on FileWriter! hr=0x%x\n", hr); pMuxOut->Release(); return hr; } hr = pGraph->ConnectDirect(pMuxOut, pWriterIn, NULL); pMuxOut->Release(); pWriterIn->Release(); if(FAILED(hr)) { printf("Failed to connect Mux to FileWriter! hr=0x%x\n", hr); return hr; } if(fVerbose) printf("Connected Mux and writer, hr = 0x%x\n", hr); } // Set sync source to NULL to speed processing SetNoClock(pGraph); // Render all source files listed on the command line while(i < argc - 1) { MultiByteToWideChar(CP_ACP, 0, argv[i], -1, SourceFile, 256); printf("Copying %ls to %ls\n", SourceFile, TargetFile); hr = pGraph->RenderFile(SourceFile, NULL); if(FAILED(hr)) printf("Failed to render source file %s! hr=0x%x\n", argv[i], hr); else if (fVerbose) printf("RenderFile('%ls') returned hr=0x%x\n", SourceFile, hr); ++i; } // Run the graph hr = pGraph->QueryInterface(IID_IMediaControl, (void **) &pGraphC); if(FAILED(hr)) { printf("Failed to QI for IMediaControl! hr=0x%x\n", hr); return hr; } hr = pGraphC->Run(); if(FAILED(hr)) { printf("Failed to run the graph! hr=0x%x\nCopy aborted.\n\n", hr); printf("Please check that you have selected the correct profile for copying.\n" "Note that if your source ASF file is audio-only, then selecting a\n" "video profile will cause a failure when running the graph.\n\n"); ListProfiles(); } else { WaitForCompletion(pGraph); printf("Copy complete.\n"); // Stop the graph hr = pGraphC->Stop(); } return hr; } int __cdecl main( int argc, char *argv[] ) { // Initialize COM HRESULT hr = CoInitialize(NULL); // Since COM smart pointers are used, the main functionality is wrapped // in CopyASF(). When the function returns, the smart pointers will clean // up properly, and then we'll uninitialize COM. hr = CopyASF(argc, argv); CoUninitialize(); return hr; }