I'm experiencing an important memory leak in a camera application that uses an EVR (Enhanced Video Renderer). The leak happens when I prepare, run, stop and unprepare a graph several times (several MB every cycle, depending on the video resolution).
I simplified the code as much as I could; you can download it from here:
https://daptech.box.com/s/6csm91vhcawiw18u42kq
The graph has a video capture filter, a SampleGrabber filter and a renderer. When connecting, it creates an AVI Decompressor filter to complete the graph.
Here are some excerpts...
Declarations:
CComPtr<IGraphBuilder> pGraphBuilder;
CComPtr<IBaseFilter> pSource; // The capture device filter
CComPtr<IBaseFilter> pSampleGrabberFilter; // Filter to capture data that flows through the graph
CComPtr<IBaseFilter> pRenderer; // EVR
Graph preparation (validation removed):
hr = m_p->pGraphBuilder.CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC);
hr = m_p->pSampleGrabberFilter.CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC);
CComQIPtr<ISampleGrabber, &IID_ISampleGrabber> pSampleGrabber(m_p->pSampleGrabberFilter);
hr = pSampleGrabber->SetBufferSamples(FALSE); // Don't copy image data for better performances
// Setup the callback
hr = pSampleGrabber->SetCallback(&m_p->cb, 1); // 0: SampleCB() is called, so that we directly access the buffer. 1: BufferCB() is called. The doc states that the function always makes a copy of the data.
CComPtr<IPin> pSampleGrabberInputPin( FindPin(m_p->pSampleGrabberFilter, PINDIR_INPUT, NULL) );
CComPtr<IPin> pSampleGrabberOutputPin( FindPin(m_p->pSampleGrabberFilter, PINDIR_OUTPUT, NULL) );
hr = m_p->pGraphBuilder->AddFilter(m_p->pSampleGrabberFilter, L"SampleGrabber");
hr = FindVideoCaptureDevice(m_p->pSource, (unsigned)iCameraIndex);
CComPtr<IPin> pSourceOutputPin( FindPin(m_p->pSource, PINDIR_OUTPUT, L"CAPTURE") );
hr = m_p->pGraphBuilder->AddFilter(m_p->pSource, L"Camera");
hr = m_p->pRenderer.CoCreateInstance(CLSID_EnhancedVideoRenderer, NULL, CLSCTX_INPROC);
hr = m_p->pGraphBuilder->AddFilter(m_p->pRenderer, L"Renderer");
hr = ConfigEVR(m_p->pRenderer, m_staticPreview.m_hWnd, uWidth, uHeight);
CComPtr<IPin> pRendererInputPin( FindPin(m_p->pRenderer, PINDIR_INPUT, NULL) );
// Now select the format:
if (!SelectFormat(pSourceOutputPin, uWidth, uHeight, bCompression))
{ MessageBox(L"No appropriate format found.\n"); return; }
// Connection:
hr = m_p->pGraphBuilder->Connect(pSourceOutputPin, pSampleGrabberInputPin);
hr = m_p->pGraphBuilder->Connect(pSampleGrabberOutputPin, pRendererInputPin);
This function is called to "unprepare" the graph:
void FreeDirectShowGraph()
{
if (pSource) DisconnectDownstream(pSource);
RemoveAndRelease(pRenderer);
RemoveAndRelease(pSampleGrabberFilter);
RemoveAndRelease(pSource);
if (pGraphBuilder) pGraphBuilder.Release();
}
Any clue would be appreciated!
Related
I am writing directshow application. Below is the code that works fine but crashes with the error message "App.exe has stopped working". The entire code is written below. Please note that I am using Windows SDK 7.0 which does not have atlbase.h and hence I cannot use CComPtr<IBaseFilter> myFilter; type of pointer declaration which is supposed to clear memory at exit.
EDIT: The application crashes only if I connect all filters explicitly. In this case, the destructor of my filter is not called. If I just connect source filter to renderer (which will internally connect my filter and a demux filter), the destructor of my filter is called and there is no crash. I have put the macro MANUAL_CONNECT over the code which causes the crash. I have removed RemoveFilter call and replaced it with Release call.
I am writing the application code here:
void main(WORD32 argc, CHAR *argv[])
{
// Initialize the COM library.
HRESULT hr = CoInitialize(NULL);
if (FAILED(hr))
{
printf("ERROR - Could not initialize COM library");
return;
}
{
IGraphBuilder *pGraph = NULL;
IFileSourceFilter *pFileSourceFilter = NULL;
IMediaControl *pControl = NULL;
IMediaEvent *pEvent = NULL;
IBaseFilter *pSource = NULL;
IBaseFilter *pVideoDecode = NULL;
IBaseFilter *pVideoRenderer = NULL;
IEnumPins *pEnumPins = NULL;
IPin *pPinIn = NULL;
IPin *pPinOut = NULL;
ULONG fetched;
PIN_INFO PinInfo;
IEnumFilters *pEnum = NULL;
BOOL stop = FALSE;
int i;
// Create the filter graph manager and query for interfaces.
hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **)&pGraph);
// Create the filter graph manager and query for interfaces.
hr = CoCreateInstance(CLSID_AsyncReader, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void **)&pSource);
hr = pGraph->AddFilter(pSource, NULL);
hr = pSource->QueryInterface(IID_IFileSourceFilter, (void**)&pFileSourceFilter);
hr = pFileSourceFilter->Load(L"input.mp4", NULL);
// Create Ittiam HEVC Decoder instance
hr = CoCreateInstance(CLSID_ivdec, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void **)&pVideoDecode);
// Create Video Renderer instance. We have used default video renderer
hr = CoCreateInstance(CLSID_VideoRendererDefault, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void **)&pVideoRenderer);
// Add decoder filter to the filter graph
hr = pGraph->AddFilter(pVideoDecode, NULL);
// Add renderer filter to the filter graph
hr = pGraph->AddFilter(pVideoRenderer, NULL);
/**************************************************************/
/* -- Connecting source filter to demux filter starts here -- */
/**************************************************************/
// Enumerate pins of the source filter
hr = pSource->EnumPins(&pEnumPins);
hr = pEnumPins->Reset();
// Get pin of source filter. Source filter has only output pin, so no check required
hr = pEnumPins->Next(1, &pPinOut, &fetched);
hr = pEnumPins->Release();
#if MANUAL_CONNECT
// Enumerate pins of the decoder filter
hr = pVideoDecode->EnumPins(&pEnumPins);
hr = pEnumPins->Reset();
// Get pin of decoder filter. Decoder filter has 2 pins, so ensure the selected pin is input pin.
// If not, get another pin
hr = pEnumPins->Next(1, &pPinIn, &fetched);
hr = pPinIn->QueryPinInfo(&PinInfo);
if(PINDIR_OUTPUT == PinInfo.dir)
{
hr = pPinIn->Release();
hr = pEnumPins->Next(1, &pPinIn, &fetched);
}
// Connect output pin of demux filter to input pin of decoder filter
hr = pGraph->Connect(pPinOut, pPinIn);
/*************************************************************/
/* -- Connecting demux filter to decoder filter ends here -- */
/*************************************************************/
/******************************************************************/
/* -- Connecting decoder filter to renderer filter starts here -- */
/******************************************************************/
// Enumerate pins of the decoder filter
hr = pVideoDecode->EnumPins(&pEnumPins);
hr = pEnumPins->Reset();
// Get pin of decoder filter. Decoder filter has 2 pins, so ensure the selected pin is output pin.
// If not, get another pin
hr = pEnumPins->Next(1, &pPinOut, &fetched);
hr = pPinOut->QueryPinInfo(&PinInfo);
if(PINDIR_INPUT == PinInfo.dir)
{
hr = pPinOut->Release();
hr = pEnumPins->Next(1, &pPinOut, &fetched);
}
hr = pEnumPins->Release();
#endif
// Enumerate pins of the renderer filter
hr = pVideoRenderer->EnumPins(&pEnumPins);
hr = pEnumPins->Reset();
// Get pin of renderer filter. Renderer filter has only input pin, so no check required
hr = pEnumPins->Next(1, &pPinIn, &fetched);
hr = pPinIn->QueryPinInfo(&PinInfo);
// Connect output pin of decoder filter to input pin of renderer filter
hr = pGraph->Connect(pPinOut, pPinIn);
/****************************************************************/
/* -- Connecting decoder filter to renderer filter ends here -- */
/****************************************************************/
hr = pGraph->QueryInterface(IID_IMediaControl, (void **)&pControl);
hr = pGraph->QueryInterface(IID_IMediaEvent, (void **)&pEvent);
// Run the graph.
hr = pControl->Run();
if (SUCCEEDED(hr))
{
// Wait for completion.
long evCode;
pEvent->WaitForCompletion(INFINITE, &evCode);
// Note: Do not use INFINITE in a real application, because it
// can block indefinitely.
}
hr = pControl->Stop();
hr = pSource->Release();
hr = pVideoDecode->Release();
hr = pControl->Release();
hr = pEvent->Release();
hr = pGraph->Release();
}
CoUninitialize();
printf("Exiting main!!\n");
}
I have removed error checks from the post but I have all error checks in my code. I can see the Exiting main!! print but than the application crashes. Any suggestions on how to debug this? Please let me know if any information is missing. I am using Microsoft Visual C++ 2010 Express for my development.
You must terminate all COM activity (specifically: release all COM interface pointers) on the thread before calling CoUninitialize, you don't do it.
See, for example, this code and its Release calls at the _tmain bottom.
It makes sense to use more recent version of Visual Studio (2013, 2015), where free community edition already includes ATL and you can enjoy automatic COM interface reference management using CComPtr and friends. This is the first advise to those who uses raw COM interface pointers and experience issues managing them incorrectly.
See also:
Not releasing filter com object causing a crash
Unreleased DirectShow CSource filter makes program crash at process shutdown
Why does CoUninitialize cause an error on exit?
UPDATE: Using raw pointers inaccurately, you keep having leaks:
hr = pGraph->Connect(pPinOut, pPinIn);
// ...
hr = pEnumPins->Next(1, &pPinOut, &fetched);
IEnumPin::Next call overwrites pPinOut pointer and leaks a reference.
I'm trying to open an exclusive stream with an output device using WASAPI. I'm having trouble choosing an acceptable format, since there appear to be no hints as to what formats are accepted by a given device.
In my case, IAudioClient::GetMixFormat(), which would otherwise return a sort of default format for the device, returns a format that can't be used in exclusive mode (IAudioClient::IsFormatSupported() returns AUDCLNT_E_UNSUPPORTED_FORMAT). I don't know where to go from there. There's a ridiculous number of combinations of wave format parameters - do I literally have to iterate through every one of them until something works?
Well, I asked the MSDN forums and they came up with a good answer.
You need to check the device's default device format via IMMDevice::OpenPropertyStore(), and subsequently IPropertyStore::GetValue(), not IAudioClient::GetMixFormat(). Here is the code that retrieved an acceptable WAVEFORMATEX structure:
//CoInitialize/Enumerate devices
IPropertyStore* store = nullptr;
hr = device->OpenPropertyStore(STGM_READ, &store);
if (FAILED(hr)) {
ExitProcess(1);
}
PROPVARIANT prop;
hr = store->GetValue(PKEY_AudioEngine_DeviceFormat, &prop);
if (FAILED(hr)) {
ExitProcess(2);
}
hr = device->Activate (
__uuidof(IAudioClient),
CLSCTX_ALL,
NULL,
(void**)&audioClient
);
device->Release();
device = nullptr;
if (FAILED(hr)) {
ExitProcess(3);
}
hr = audioClient->IsFormatSupported (
AUDCLNT_SHAREMODE_EXCLUSIVE,
(PWAVEFORMATEX)prop.blob.pBlobData,
NULL
);
if (FAILED(hr)) {
ExitProcess(4);
}
The final value of hr is S_OK.
I'm using DirectShow in order to capture video. I'm attaching the camera to sample grabber and saving the images that I get.
In the following code I have IID_ICaptureGraphBuilder2 connected to the capture device (CLSID_VideoCaptureSources) that is connected to the sample grabber (ISampleGrabber).
The graph is connected to IID_IMediaControl.
I use IID_IMediaControl Run and Stop. Most of the times the stop gets stuck.
There's some kind of deadlock. I tried to add IID_IMediaEvent and m_pEvent->WaitForCompletion(INFINITE, &evCode); but it still doesn't work.
Pausing works with no problesm, however upon trying to stop the software is stuck
Building the graph
ICaptureGraphBuilder2 *pBuilder;
IBaseFilter *pCamera;
IPin *pOutPin;
IPin *pInPin;
IBaseFilter *pSampleGrabberFilter;
IBaseFilter *pNullRendererFilter;
ISampleGrabber *pSampleGrabber;
HRESULT hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **)&m_pGraph);
hr = CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL, CLSCTX_INPROC_SERVER, IID_ICaptureGraphBuilder2, (LPVOID*)&pBuilder);
CHECK_HR(hr, L"Can't create Capture Graph Builder");
hr = pBuilder->SetFiltergraph(m_pGraph);
CHECK_HR(hr, L"Can't SetFiltergraph");
pCamera = CreateFilterByName(pCaptureDeviceName, CLSID_VideoCaptureSources);
WCHAR err[256];
wsprintf(err, L"Can't add Camera '%s' to graph", pCaptureDeviceName);
hr = m_pGraph->AddFilter(pCamera , pCaptureDeviceName);
CHECK_HR(hr, err);
WCHAR filterName[256];
pOutPin = GetPinCapture(pCamera, L"Capture", i);
if (!pOutPin)
continue;
IAMStreamConfig *pConfig = NULL;
hr = pOutPin->QueryInterface(IID_IAMStreamConfig, (void**)&pConfig);
CHECK_HR(hr, L"Can't get configuration");
AM_MEDIA_TYPE *pmt = NULL;
pConfig->GetFormat(&pmt);
VIDEOINFOHEADER *pFrmt = (VIDEOINFOHEADER *)pmt->pbFormat;
pFrmt->bmiHeader.biWidth = 1920;
pFrmt->bmiHeader.biHeight = 1080;
pConfig->SetFormat(pmt);
SAFE_RELEASE(pConfig);
SAFE_RELEASE(pOutPin);
// Create a sample grabber
wsprintf(filterName, L"Sample Grabber %d", i);
// Create a sample grabber
hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void **)&pSampleGrabberFilter);
CHECK_HR(hr, L"Unable to create sample grabber filter");
// Initialize sample grabber
hr = pSampleGrabberFilter->QueryInterface(IID_ISampleGrabber, (void **)&pSampleGrabber);
CHECK_HR(hr, L"Unable to get sample grabber");
hr = pSampleGrabber->SetMediaType(pmt);
CHECK_HR(hr, L"Unable to set media type");
hr = pSampleGrabber->SetBufferSamples(false);
CHECK_HR(hr, L"Unable to set buffer samples!");
hr = pSampleGrabber->SetOneShot(false);
CHECK_HR(hr, L"Unable to set one shot!");
// Add the sample grabber to the graph
hr = m_pGraph->AddFilter(pSampleGrabberFilter, filterName);
CHECK_HR(hr, L"Unable to add sample grabber to graph");
pOutPin = GetPinCapture(pCamera, L"Capture", i);
pInPin = GetPin(pSampleGrabberFilter, PINDIR_INPUT);
hr = m_pGraph->ConnectDirect(pOutPin, pInPin, 0);
CHECK_HR(hr, L"Unable to connect Camera to pSampleGrabberFilter");
SAFE_RELEASE(pOutPin);
SAFE_RELEASE(pInPin);
wsprintf(filterName, L"Null Renderer %d", i);
// Create a null renderer
hr = CoCreateInstance(CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter,(void **)&pNullRendererFilter);
CHECK_HR(hr, L"Unable to create null renderer filter");
hr = m_pGraph->AddFilter(pNullRendererFilter, filterName);
CHECK_HR(hr, L"Unable to add null renderer to graph");
pOutPin = GetPin(pSampleGrabberFilter, PINDIR_OUTPUT);
pInPin = GetPin(pNullRendererFilter, PINDIR_INPUT);
hr = m_pGraph->ConnectDirect(pOutPin, pInPin, 0);
SAFE_RELEASE(pOutPin);
SAFE_RELEASE(pInPin);
pFrmt = ((VIDEOINFOHEADER *)pmt->pbFormat);
// Initialize the capture grabber
m_pCapGrabber[i] = new CapGrabber(i);
m_pCapGrabber[i]->SetVideoInfoHeader(pFrmt);
m_pCapGrabber[i]->SetAttachGrabberCB(m_funcAttachGrabber);
m_pCapGrabber[i]->SetWidth((int)pFrmt->bmiHeader.biWidth);
m_pCapGrabber[i]->SetHeight((int)pFrmt->bmiHeader.biHeight);
// Set the capture callback
hr = pSampleGrabber->SetCallback(m_pCapGrabber[i], 1);
SAFE_RELEASE(pSampleGrabberFilter);
SAFE_RELEASE(pSampleGrabber);
SAFE_RELEASE(pNullRendererFilter);
The problem is typical, but it is a guesswork without additional details you need to find out. It is easy to start multithreaded operation, but when it comes to stopping it, one needs to synchronize threads and inaccurate doing this is a typical cause of a deadlock - what you are seeing.
To address this problem you typically attach debugger to the process in question and inspect its call stacks. You will see one thread calling stop and sleeping somewhere deep within the call waiting for something else to happen. It is likely that there is another thread is doing something suspicious as well.
The threads, call stacks and modules on them are suggesting what's wrong and isolating the problem to specific filter or library. More to that, you might want to start reducing your graph temporarily removing filters until you see that the freeze is gone, and you identify the suspicious filter.
Sometime it is better to pause graph and wait for state change before stopping the graph.
Also note that as per msdn stop will not reset the graph position to the beginning.
http://msdn.microsoft.com/en-us/library/windows/desktop/dd390178(v=vs.85).aspx
So to make sure that graph goes to the beginning after stop. I would recommend use SetPosition interface. Below is the small snippet of code I've used for stopping the graph. I assumed that you have valid media control and media seeking interface.
IMediaControl *m_pControl = NULL;
IMediaSeeking *m_pMediaSeek = NULL;
//Assuming that you have valid media control interface and media seeking interface using QueryInterface
long long m_pStart = 0;
m_pControl->Pause();
m_pControl->GetState(1000, NULL);
m_pMediaSeek->SetPositions(&m_pStart, AM_SEEKING_AbsolutePositioning, NULL, AM_SEEKING_NoPositioning);
m_pControl->Run();
m_pControl->GetState(1000,NULL);
m_pControl->Stop();
m_pControl->GetState(1000,NULL);
I'm trying to write a C++ application with directshow that saves video capture to file.
The steps in the code are:
1. Create the Capture Graph Builder
2. Create the System Device Enumerator
3. Create the System Device Enumerator - in order to get capture filter
4. Create an enumerator for the video capture category
5. Create query to capture the video
Attaching the code
// gets the device filter
HRESULT getDeviceFilter(REFCLSID clsid, int order, IBaseFilter **pCap)
{
ICreateDevEnum *pDevEnum = NULL;
IEnumMoniker *pEnum = NULL;
// Create the System Device Enumerator.
HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL,
CLSCTX_INPROC_SERVER, IID_ICreateDevEnum,
reinterpret_cast<void**>(&pDevEnum));
if (SUCCEEDED(hr))
{
// Create an enumerator for the video capture category.
hr = pDevEnum->CreateClassEnumerator( clsid, &pEnum, 0);
}
IMoniker *pMoniker = NULL;
if (pEnum->Next(1, &pMoniker, NULL) == S_OK)
hr = pMoniker->BindToObject(0, 0, IID_IBaseFilter, (void**)pCap);
return hr;
}
int main()
{
IGraphBuilder *pGraph = 0;
ICaptureGraphBuilder2 *pBuild = 0;
IBaseFilter *pCap = 0;
HRESULT hr = CoInitialize(NULL);
// Create the Capture Graph Builder.
hr = CoCreateInstance(CLSID_CaptureGraphBuilder2,
NULL,
CLSCTX_INPROC_SERVER,
IID_ICaptureGraphBuilder2,
(void**)&pBuild );
ICreateDevEnum *pDevEnum = NULL;
IEnumMoniker *pEnum = NULL;
// Create the System Device Enumerator.
hr = CoCreateInstance(CLSID_SystemDeviceEnum,
NULL,
CLSCTX_INPROC_SERVER,
IID_ICreateDevEnum,
reinterpret_cast<void**>(&pDevEnum));
IBaseFilter *pMux = 0;
hr = pBuild->SetOutputFileName(&MEDIASUBTYPE_Avi, // Specifies AVI for the target file.
L"C:\\Example.avi", // File name.
&pMux, // Receives a pointer to the mux.
NULL); // (Optional) Receives a pointer to the file sink.
// gets the first device, VDM tv card
hr = getDeviceFilter(CLSID_VideoInputDeviceCategory, 0, &pCap);
hr = pBuild->RenderStream(&PIN_CATEGORY_CAPTURE, // Pin category.
&MEDIATYPE_Video, // Media type.
pCap, // Capture filter.
NULL, // Intermediate filter (optional).
pMux); // Mux or file sink filter.
// Release the mux filter.
pMux->Release();
IConfigAviMux *pConfigMux = NULL;
hr = pMux->QueryInterface(IID_IConfigAviMux, (void**)&pConfigMux);
if (SUCCEEDED(hr))
{
pConfigMux->SetMasterStream(1);
pConfigMux->Release();
}
return 0;
}
However, upon calling RenderStream I get an E_INVALIDARG error
Any suggestions?
Thanks
Take a look at this topic. It seems you have missed some steps.
First of all, you are not using pGraph anywhere. You should create a graph manager, and then initialize the graph builder by providing it with a pointer to the graph manager using SetFilterGraph.
// Create the Filter Graph Manager.
hr = CoCreateInstance(CLSID_FilterGraph, 0, CLSCTX_INPROC_SERVER,
IID_IGraphBuilder, (void**)&pGraph);
if (SUCCEEDED(hr))
{
// Initialize the Capture Graph Builder.
pBuild->SetFiltergraph(pGraph);
// ...
}
Second, you are using filters that are not managed by a graph manager. Quoting from here:
All of the filters specified by pSource, pIntermediate, and pSink must be added to the graph prior to calling the method.
You will have to add the filters pCap and pMux to the graph manager you created earlier, using AddFilter. You should do this before calling RenderStream. This is so because RenderStream eventually calls connection methods on the manager.
If the above steps do not solve your problem, there are several other things you can try.
Device Filter. You are using the first device of CLSID_VideoInputDeviceCategory, but are you sure this is the correct device? Webcams and such are also included in this category. Make sure that there are no other devices of the same category connected, and try again.
Connection. Every device is different. It could be that your device can't be directly connected to the mux. In this case, we will have to figure out why, and determine whether you need to connect additional filters (like decoders). GraphEdit is a very fast way to find these filters.
Pin Category/Media Type. In my experience, E_INVALIDARG is 90% of the time caused by the first 2 parameters of RenderStream. Try setting the pin category or the media type to NULL.
System Device Enumerator: As you described yourself, you are creating a system device enumerator twice. This seems odd to me, why not use one, for both purposes?
If your code still does not work, you should provide me with more information. Have you achieved your goals when using GraphEdit? How does your VDM TV card filter look like (pins, media types)?
Basiclly, I want to capture audio/video. Run it through a mp4 muxer and save it to a file on disk. Before I used ICaptureGraphBuilder2, but it seems unusuable when saving to custom formats.
My code so far,
I enumerate video/audio devices. In this sample I only try to capture audio. I get the correct device and use GetPin to enumerate the filters pins to get its output pin.
hr = pMoniker2->BindToObject(0, 0, IID_IBaseFilter, (void**)&pSrc2);
hr = pGraph->AddFilter(pSrc2, L"AudioCap");
hr = GetPin(pSrc2, PINDIR_OUTPUT, &outPin);
This is the custom filter, a MP4 muxer. It properly loads and I can get the input pin and connect it with my output pin. So far so good.
HRESULT hr = CreateObjectFromPath(TEXT("c:\\filters\\mp4mux.dll"), clsid, &pUnk);
if (SUCCEEDED(hr))
{
IBaseFilterPtr pFilter = pUnk;
HRESULT hr = pGraph->AddFilter(pFilter, L"Private Filter");
hr = GetPin(pFilter, PINDIR_INPUT, &inPin);
}
hr = pGraph->Connect(outPin, inPin);
This is where I get lost, I can't find out how to take the next steps to Render and save the output to a file on disk. So any help with the next steps would be greatfully appreciated, thanks in advance!
EDIT: Filesink code
AM_MEDIA_TYPE mType;
mType.majortype = MEDIATYPE_Video;
mType.subtype = MEDIASUBTYPE_H264;
mType.bFixedSizeSamples = FALSE;
mType.bTemporalCompression = TRUE;
mType.lSampleSize = 0;
mType.formattype = FORMAT_None;
mType.pUnk = NULL;
mType.cbFormat = 0;
mType.pbFormat = NULL;
//Not 100% sure about the setup of the media format.
IBaseFilter * iFiltera = NULL;
IFileSinkFilter* iFilter = NULL;
IGraphBuilder *pGraph;
hr = pMoniker2->BindToObject(0, 0, IID_IBaseFilter, (void**)&pSrc2); //audio capture
hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void**)&pGraph);
hr = CoCreateInstance(CLSID_FileWriter, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&iFiltera);
hr = pBuild->SetFiltergraph(pGraph);
hr = pGraph->AddFilter(pSrc2, L"AudioCap");
hr = GetPin(pSrc2, PINDIR_OUTPUT, &outPin); //ADDED
hr = pGraph->AddFilter(iFiltera, L"FileWriter");
hr = iFiltera->QueryInterface(IID_IFileSinkFilter, (void**)&iFilter);
iFilter->SetFileName((LPCOLESTR)"c:\\wav\\tester.mp4", NULL); //UPDATED mType set to NULL
HRESULT hr = CreateObjectFromPath(TEXT("c:\\filters\\mp4mux.dll"), clsid, &pUnk);
IBaseFilterPtr pFilter = pUnk;
if (SUCCEEDED(hr))
{
HRESULT hr = pGraph->AddFilter(pFilter, L"Private Filter");
hr = GetPin(pFilter, PINDIR_INPUT, &inPin); //mux in
hr = GetPin(pFilter, PINDIR_OUTPUT, &mOutPin); //mux out
hr = GetPin(iFiltera, PINDIR_INPUT, &filePin); // filewriter in
}
hr = pGraph->Connect(outPin, inPin); //connect audio out and mux in
hr = pGraph->Connect(mOutPin, filePin); //connect mux out and file in; Error 0x80040217(VFW_E_CANNOT_CONNECT?) //works now
//ADDED code
IMediaControl *pMC = NULL;
hr = pGraph->QueryInterface(IID_IMediaControl, (void **)&pMC);
hr = pMC->Run();
Sleep(4000);
hr = pMC->Stop();
You need to have an idea what filter graph topology you need for specific task. You are doing capture, here - fine. So you have audio capture filter you provided code snippet for. Then you either compress audio (where your preferred choice should be AAC AKA MPEG-4 Part 3, provided that you are to produce MP4 file), or keep audio uncompressed PCM. Then you connect MPEG-4 multiplexer as you do. The multiplexer produces output stream and you are expected to complete the pipeline with File Writer Filter.
You can manually build the chain in GraphEdit SDK application (or there are alternate richer tools). Your filter graph looks like this:
Note that you can expose filter graph in your application, and connect to it remotely and inspect the topology. This makes debugging much easier. Starting/stopping the filter graph (IMediaControl::Run, ::Stop from code) creates you the file.
My understanding is that you get lost immediately after adding multiplexer. Now you need to find its output pin, add File Writer, query its IFileSinkFilter, set the destination file name using it, find its input pin, connect the two unconnected pins (mux output, writer input). Your pipeline is ready to run.