#include #include #include #include /** straight from example code; add to ROT for use in graph edit. */ static HRESULT AddToRot(IUnknown *pUnkGraph, DWORD *pdwRegister) { IMoniker * pMoniker; IRunningObjectTable *pROT; if (FAILED(GetRunningObjectTable(0, &pROT))) { return E_FAIL; } WCHAR wsz[256]; wsprintfW(wsz, L"FilterGraph %08x pid %08x", (DWORD*)pUnkGraph, GetCurrentProcessId()); HRESULT hr = CreateItemMoniker(L"!", wsz, &pMoniker); if (SUCCEEDED(hr)) { hr = pROT->Register(ROTFLAGS_REGISTRATIONKEEPSALIVE, pUnkGraph, pMoniker, pdwRegister); pMoniker->Release(); } pROT->Release(); return hr; } /** called on exit. */ static void _cleanup (void) { CoUninitialize(); } /** convert c string to lpwstr; returns null on error, caller must free() memory. */ static LPWSTR _towstr (const char *str) { LPWSTR wstr; int len; if ((len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0)) <= 0) return NULL; if (!(wstr = (WCHAR *)malloc(sizeof(WCHAR) * len))) return NULL; if (!MultiByteToWideChar(CP_ACP, 0, str, -1, wstr, sizeof(WCHAR) * len)) { free(wstr); return NULL; } return wstr; } /** returns IPin interface to index'th dir pin in base. or returns NULL. */ static IPin * _getpin (IBaseFilter *base, PIN_DIRECTION dir, int index) { HRESULT hr; PIN_DIRECTION pindir; CComPtr ep; IPin *pin, *found = NULL; hr = base->EnumPins(&ep); if (FAILED(hr)) return NULL; while (ep->Next(1, &pin, NULL) == S_OK) { pin->QueryDirection(&pindir); if (pindir == dir) { if (!index) { found = pin; break; } else -- index; } pin->Release(); } return found; } /** display message and exit */ #define EXITMSG(s) { \ MessageBox(NULL, (s), "Error", MB_ICONERROR); \ exit(1); \ } /** display error message and exit if FAILED(hr) */ static void _checkhr (HRESULT hr, const char *msg) { char buf[512], buf2[1024]; if (FAILED(hr)) { AMGetErrorText(hr, buf, sizeof(buf) - 1); _snprintf(buf2, sizeof(buf2), "%s:\n%s", msg, buf); EXITMSG(buf2); } } /** using graph builder 'gb' connect findex'th output pin of "from" to * tindex'th input pin of "to". it's assumed both filters have been * added to the graph already. if "to" is NULL then render the "from" * pin instead. display message and exit on error. */ static void _connectpins (IGraphBuilder *gb, IBaseFilter *from, int findex, IBaseFilter *to, int tindex, const char *what) { HRESULT hr; CComPtr fpin, tpin; char errstr[512]; _snprintf(errstr, sizeof(errstr), "Error connecting %s", what); if (!(fpin = _getpin(from, PINDIR_OUTPUT, findex))) EXITMSG("Error connecting pins (source pin index out of range)"); if (to) { if (!(tpin = _getpin(to, PINDIR_INPUT, tindex))) EXITMSG("Error connecting pins (destination pin index out of range)"); hr = gb->Connect(fpin, tpin); _checkhr(hr, errstr); } else { hr = gb->Render(fpin); _checkhr(hr, errstr); } } int main (int argc, char **argv) { LPCWSTR infn, outfn; HRESULT hr; CComPtr gb; CComPtr mc; CComPtr me; CComPtr input_base, tee, file_base, mux_base; CComPtr file_output; int index = -1; long evcode; // -------- COMMAND LINE if (argc != 3) EXITMSG("No input/output filename specified"); if (!(infn = _towstr(argv[1]))) EXITMSG("Out of memory or something"); if (!(outfn = _towstr(argv[2]))) EXITMSG("Out of memory or something"); //MessageBoxW(NULL, infn, L"Filename", MB_ICONINFORMATION); // -------- COM / FILTER GRAPH INITIALIZE hr = CoInitialize(NULL); _checkhr(hr, "CoInitialize failed"); atexit(&_cleanup); hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **)&gb); _checkhr(hr, "Could not create filter graph"); DWORD reg; AddToRot(gb, ®); hr = gb->QueryInterface(IID_IMediaControl, (void **)&mc); _checkhr(hr, "Could not get IMediaControl interface"); hr = gb->QueryInterface(IID_IMediaEvent, (void **)&me); _checkhr(hr, "Could not get IMediaEvent interface"); // -------- FILE SOURCE FILTER hr = gb->AddSourceFilter(infn, NULL, (IBaseFilter **)&input_base); _checkhr(hr, "Could not read input file"); // -------- FILE RUNS TO INFINITE PIN TEE FILTER hr = CoCreateInstance(CLSID_InfTee, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void **)&tee); _checkhr(hr, "CoCreateInstance(CLSID_InfTee) failed"); hr = gb->AddFilter(tee, NULL); _checkhr(hr, "Failed to add tee filter to graph"); // -------- CREATE AVI STREAM MULTIPLEXER + FILE WRITER hr = CoCreateInstance(CLSID_AviDest, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void **)&mux_base); _checkhr(hr, "Couldn't create AVI multiplexer"); // commenting this out to not add muxer to graph hr = gb->AddFilter(mux_base, NULL); _checkhr(hr, "Couldn't add multiplexer to graph"); hr = CoCreateInstance(CLSID_FileWriter, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void **)&file_base); _checkhr(hr, "Couldn't create file writer filter"); hr = file_base->QueryInterface(IID_IFileSinkFilter2, (void **)&file_output); _checkhr(hr, "Couldn't get file writer sink interface"); hr = gb->AddFilter(file_base, NULL); _checkhr(hr, "Couldn't add file writer to graph"); hr = file_output->SetFileName(W2COLE(outfn), NULL); _checkhr(hr, "Couldn't set output file name"); hr = file_output->SetMode(AM_FILE_OVERWRITE); _checkhr(hr, "Couldn't set output file overwrite mode"); // -------- CONNECT (TRY MOVING SOME OF THESE BEFORE MUXER IS ADDED ABOVE // file input -> tee _connectpins(gb, input_base, 0, tee, 0, "input to tee"); // tee 0 -> video renderer _connectpins(gb, tee, 0, NULL, 0, "tee 0 to renderer"); #if 1 // tee 1 -> avi muxer _connectpins(gb, tee, 1, mux_base, 0, "tee 0 to multiplexer"); // avi muxer -> file writer _connectpins(gb, mux_base, 0, file_base, 0, "multiplexer to file writer"); #else // tee 1 -> video renderer _connectpins(gb, tee, 1, NULL, 0, "tee 1 to renderer"); #endif // -------- GO printf("press enter\n"); getchar(); // pause so you can check it out in graph edit printf("running...\n"); hr = mc->Run(); _checkhr(hr, "Couldn't run filter graph"); hr = me->WaitForCompletion(INFINITE, &evcode); _checkhr(hr, "Couldn't wait for filter graph to finish"); if (evcode != EC_COMPLETE) EXITMSG("Unexpected end of playback"); // -------- return 0; }