I am trying to basically render a preview from a capture card (720p) from a PS3 to the enhanced video render.
Ideally, I would like something like this:
I used to do this:
hr = m_pCapture->RenderStream (&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, m_pSrcFilter, NULL, NULL);
But I find that it only renders to an old default renderer, which is not adequate enough to stretch the image to 1080p (image becomes pixelated). [http://msdn.microsoft.com/en-us/library/aa930715.aspx ]
I want to use the enhanced video render as the sink but I have no idea how to. I viewed the tutorials here: http://msdn.microsoft.com/en-us/library/windows/desktop/ff625867%28v=vs.85%29.aspx
And tried to put my code in but it would not render.
Here is a snippet of the code that sets the source. Assume that setResolution will set the AM_MEDIA_TYPE format and that getVideoSourceByKeyword will get the AVermedia capture card device.
HRESULT DShowPlayer::SetPreviewDevice(PCWSTR keyname)
{
IBaseFilter *pSource = NULL;
// Create a new filter graph. (This also closes the old one, if any.)
HRESULT hr = CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL,
CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&m_pCapture));
if (FAILED(hr))
{
goto done;
}
hr = InitializeGraph();
if (FAILED(hr))
{
goto done;
}
// Add the source filter to the graph.
hr = getVideoSourceByKeyword(keyname, &pSource);
if (FAILED(hr))
{
goto done;
}
hr = m_pGraph->AddFilter(pSource, L"Source filter");
if (FAILED(hr))
{
goto done;
}
setResolution(pSource, 1280, 720);
// Try to render the streams.
hr = RenderStreams(pSource);
if (FAILED(hr))
{
goto done;
}
hr = m_pControl->Run();
done:
if (FAILED(hr))
{
TearDownGraph();
}
SafeRelease(&pSource);
return hr;
}
When the code runs RenderStreams, this is the code (from http://msdn.microsoft.com/en-us/library/windows/desktop/ff625878%28v=vs.85%29.aspx):
// Enumerate the pins on the source filter.
hr = pSource->EnumPins(&pEnum);
if (FAILED(hr))
{
goto done;
}
// Loop through all the pins
IPin *pPin;
while (S_OK == pEnum->Next(1, &pPin, NULL))
{
PIN_INFO pInfo;
pPin->QueryPinInfo(&pInfo);
// Try to render this pin.
// It's OK if we fail some pins, if at least one pin renders.
HRESULT hr2 = pGraph2->RenderEx(pPin, AM_RENDEREX_RENDERTOEXISTINGRENDERERS, NULL);
pPin->Release();
if (SUCCEEDED(hr2))
{
bRenderedAnyPin = TRUE;
}
}
In visual studio I debugged at the pin to get the source name ("Capture" pin name of the AVermedia capture card). It said it was successful to attach to the render at RenderEx however at
hr = m_pControl->Run();
It fails and there error is device is not connected.
I also tried to get the EVR renderer directly and tried to render the stream:
IBaseFilter* render;
m_pVideo->getRender(&render);
m_pGraph->AddFilter(render, L"EVR Filter");
hr = m_pCapture->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, pSource, NULL, render);
if (FAILED(hr))
{
goto done;
}
But it fails and says that VFW_E_NOT_IN_GRAPH.
What I am asking: I am still pretty new at learning Directshow and I would like to be able to preview the capture card with EVR. I found no comprehensive tutorials or source code to do this. If you need anymore information, I can add more.
Thanks in advance.
EVR can be used programmatically very much the same way as VMR-7/9. The only difference is that EVR needs "windowless" mode, while earlier renderers supported also "windowed" mode where you need minimal initialization of the renderer.
I suppose you can see video on EVR in GraphEdit? You should be able to do so, just use Preview pin, not Capture. Or, connect Capture through Smart Tee filter and its preview output.
The error codes suggest that you don't build graph correctly. In particular, VFW_E_NOT_IN_GRAPH says your filter is not in graph and hence invalid argument. You don't need to use getRender, just CoCreateInstance the EVR the usual and straightforward way. At the first moment you get an error you are interested in putting everything on hold and reviewing the filter graph topology you have at the moment.
Windows SDK samples contain \Samples\multimedia\directshow\vmr9\windowless which shows VMR-9 in windowless mode, this is supposedly the closest starting point to just switch from VMR-9 to EVR.
Related
I am doing a windows based app where I use a camera. I want to allow the user to change camera settings (VfwCaptureDialog_Source) as it is common in skype or amcap application. For now, I found out that the dialog is from vfw and it can be activated from dshow api. When I do it from AmCap, it is working. Can you help me to get this code working for given webcam (multiple camera support is required)? I use Qt and OpenCV while working with camera. In opencv just camera index is used to select proper camera. The index is from Qt where can I get nice list of camera names. The error I get from this code is 1170 : "The property set specified does not exist on the object"
//libs -lDxva2 -lstrmiids -lvfw32 -lole32 -loleaut32
#include <Windows.h>
#include "strmif.h"
#include "dshow.h"
#include "Vfw.h"
...
HRESULT hr ;
IGraphBuilder* graph= nullptr;
hr = CoCreateInstance( CLSID_FilterGraph, 0, CLSCTX_INPROC,IID_IGraphBuilder, (void **)&graph );
IMediaControl* ctrl = nullptr;
hr = graph->QueryInterface( IID_IMediaControl, (void **)&ctrl );
IMediaEventEx* mediaEvent=nullptr;
hr = graph->QueryInterface(IID_IMediaEvent, (LPVOID *) &mediaEvent);
ICreateDevEnum* devs = nullptr;
hr = CoCreateInstance (CLSID_SystemDeviceEnum, nullptr, CLSCTX_INPROC, IID_ICreateDevEnum, (void **) &devs);
IEnumMoniker* cams = nullptr;
hr = devs?devs->CreateClassEnumerator (CLSID_VideoInputDeviceCategory, &cams, 0):0;
IMoniker* mon = nullptr;
hr = cams?cams->Next (1, &mon, nullptr):0;
IBaseFilter* cam = nullptr;
hr = mon?mon->BindToObject(nullptr,nullptr,IID_IBaseFilter, (void**)&cam):0;
IEnumPins* pins = nullptr;
hr = cam?cam->EnumPins(&pins):0;
IPin* cap = nullptr;
hr = pins?pins->Next(1,&cap, nullptr):0;
IAMVfwCaptureDialogs *pVfw = nullptr;
hr = cap->QueryInterface(IID_IAMVfwCaptureDialogs, (void**)&pVfw);
if (SUCCEEDED(hr))
{
// Check if the device supports this dialog box.
if (S_OK == pVfw->HasDialog(VfwCaptureDialog_Source))
{
// Show the dialog box.
hr = pVfw->ShowDialog(VfwCaptureDialog_Source, HWND(this->winId()));
}
}
else
{
error("cap->QueryInterface");
}
First of all, you skipped a really important part. In your Qt/OpenCV application what is the API used and what exactly you have for given web camera. If it is Video for Windows, then you should look into VFW API on dialog interface. If it is DirectShow then you are basically not interested in VFW dialogs.
Presumably you interact with cameras via DirectShow (well, it does not make much sense to use VFW, esp. for multiple cameras). Then I doubt that AMCap uses exactly the code path you mentioned in the question. Note AMCap source comment:
// we use this interface to bring up the 3 dialogs
// NOTE: Only the VfW capture filter supports this. This app only brings
// up dialogs for legacy VfW capture drivers, since only those have dialogs
hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE,
&MEDIATYPE_Video, gcap.pVCap,
IID_IAMVfwCaptureDialogs, (void **)&gcap.pDlg);
With DirectShow you would typically pop up configuration interface with ISpecifyPropertyPages and OleCreatePropertyFrame. AMCap has relevant code for this as well:
else if(id - MENU_DIALOG0 == gcap.iVCapDialogPos)
{
ISpecifyPropertyPages *pSpec;
CAUUID cauuid;
hr = gcap.pVCap->QueryInterface(IID_ISpecifyPropertyPages,
(void **)&pSpec);
if(hr == S_OK)
{
hr = pSpec->GetPages(&cauuid);
hr = OleCreatePropertyFrame(ghwndApp, 30, 30, NULL, 1,
(IUnknown **)&gcap.pVCap, cauuid.cElems,
(GUID *)cauuid.pElems, 0, 0, NULL);
CoTaskMemFree(cauuid.pElems);
pSpec->Release();
}
}
Displaying a Filter's Property Pages on MSDN should be even of more help for you (you already have IBaseFilter interface pointer in your code snippet in the question).
I simply want to be able to create options for my program, so that user can pick which output device will be used to play sounds, like this one in MS Lync:
I originally created my program in Qt and I asked similar (but not identical) question here Qt5+ How to set default audio device for QMediaPlayer
I figured out that Qt is too much bugged for this and this is impossible, so I lowered my requirements and I will use native windows API as these are probably only solution here. This unfortunately requires rewrite of some parts of my program, and now I am following this guide on msdn: https://msdn.microsoft.com/en-us/library/windows/desktop/dd371455%28v=vs.85%29.aspx
I basically want to be able to do following:
List all available output devices and display them on preferences form - I already have a working code for that using IMMDeviceEnumerator
Let user pick a device they want to use for output of my program - I already have that part
Create a function, let's call it PlaySound(string path) that if called with path of .wav or .mp3 file would use the preferred IMMDevice and play a file through it - this is what I need help with
Because I was using Qt so far and I have pretty much no idea of MS windows internals, I have no idea how could one take a file stored somewhere on disk and play it using windows API's especially using that selected IMMDevice which user set in their preferences. I was googling and searching through documentation, but I could only work extremely complex and weird solutions, such as https://msdn.microsoft.com/en-us/library/windows/desktop/dd316756%28v=vs.85%29.aspx
I could even find some examples where you can play mp3 file using MCI device, but that didn't really explain how to alter preferred output device, so it isn't very useful for my use.
I understand that low-level API is probably not going to offer some simple "playmyfile" function, but it would be nice to have at least some example of super-simple solution or some tutorial that would play media files using selected output device on windows so that I could use that as a starting reference. I have a working active IMMDevice, now I just need to make it possible to play mp3 / wav files through it.
NOTE: This is not some generic "how to play a sound on windows" question. I need to be able to play that sound on selected audio output device. For my program only (just like MS Lync, VLC media player or any other advanced audio program can). I don't want to change system global preferences (default device etc).
I managed to do that but surprisingly using windows native libraries called "DirectShow" which are primarily designed for video rendering, but can handle audio as well.
How to:
Enumerate output devices
This functions iterates over all audio devices detected by OS and store them in a list.
void Options::Initialize()
{
#ifdef WIN
HRESULT hr;
ICreateDevEnum *pSysDevEnum = NULL;
hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void **)&pSysDevEnum);
if (FAILED(hr))
return;
IEnumMoniker *pEnumCat = NULL;
hr = pSysDevEnum->CreateClassEnumerator(CLSID_AudioRendererCategory, &pEnumCat, 0);
if (hr == S_OK)
{
// Enumerate the monikers.
IMoniker *pMoniker = NULL;
ULONG cFetched;
while (pEnumCat->Next(1, &pMoniker, &cFetched) == S_OK)
{
IPropertyBag *pPropBag;
hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pPropBag);
if (SUCCEEDED(hr))
{
// To retrieve the filter's friendly name, do the following:
VARIANT varName;
VariantInit(&varName);
hr = pPropBag->Read(L"FriendlyName", &varName, 0);
if (SUCCEEDED(hr))
{
OutputDevice device;
device.Name = QString((QChar*)varName.bstrVal, wcslen(varName.bstrVal));
Options::devices.append(device);
}
VariantClear(&varName);
pPropBag->Release();
}
pMoniker->Release();
}
pEnumCat->Release();
}
pSysDevEnum->Release();
#endif
}
Create a filter for device that user selected
Iterate over all devices once more and make a filter for that which was selected by user
HRESULT hr;
ICreateDevEnum *pSysDevEnum = NULL;
hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void **)&pSysDevEnum);
if (FAILED(hr))
{
Error("Failed SystemDeviceEnum");
return;
}
IEnumMoniker *pEnumCat = NULL;
QSettings s;
hr = pSysDevEnum->CreateClassEnumerator(CLSID_AudioRendererCategory, &pEnumCat, 0);
IBaseFilter *pFilter = NULL;
if (hr == S_OK)
{
// Enumerate the monikers.
IMoniker *pMoniker = NULL;
ULONG cFetched;
int i = 0;
while (pEnumCat->Next(1, &pMoniker, &cFetched) == S_OK)
{
IPropertyBag *pPropBag;
hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pPropBag);
if (SUCCEEDED(hr))
{
// retrieve the filter's friendly name now
VARIANT varName;
VariantInit(&varName);
hr = pPropBag->Read(L"FriendlyName", &varName, 0);
if (SUCCEEDED(hr))
{
QString name = QString((QChar*)varName.bstrVal, wcslen(varName.bstrVal));
if (s.value("d:" + name).toBool())
{
hr = pMoniker->BindToObject(NULL, NULL, IID_IBaseFilter, (void**)&pFilter);
// now we got the filter in pFilter so we can play sound using that filter
PlayWin(pFilter, path);
}
}
VariantClear(&varName);
pPropBag->Release();
}
pMoniker->Release();
}
pEnumCat->Release();
}
pSysDevEnum->Release();
Play the sound using the filter for our device
In this function device is pFilter from previous function
HRESULT hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, __uuidof(IGraphBuilder), (void **)&x->pGraph);
if (FAILED(hr))
{
Error("ERROR - Could not create the Filter Graph Manager.");
return;
}
hr = x->pGraph->QueryInterface(IID_IBasicAudio, (void**)&x->pOutput);
if (FAILED(hr))
{
Error("ERROR - Could not create the IBasicAudio.");
return;
}
x->pFlx = device;
if (device)
x->pGraph->AddFilter(device, L"fd");
hr = x->pGraph->QueryInterface(__uuidof(IMediaControl), (void **)&x->pControl);
hr = x->pGraph->QueryInterface(__uuidof(IMediaEvent), (void **)&x->pEvent);
// Build the graph.
hr = x->pGraph->RenderFile(path, NULL);
if (SUCCEEDED(hr))
{
// Run the graph.
hr = x->pControl->Run();
}
else
{
Error("Unable to play: " + QString::fromWCharArray(path));
}
This code on itself is of course not going to compile out of box, but it gives you a clue how to do this, in nutshell:
Retrieve list of all devices and store it somewhere, so that we can create dialog for user
Before we play a sound, we check which device user selected and create a filter for it
We apply the filter to DirectShow BasicAudio which is itself able to play any media file supported by system codecs.
Documentation on msdn: https://msdn.microsoft.com/en-us/library/windows/desktop/dd407292%28v=vs.85%29.aspx
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)?
I have created a custom allocator/presenter that works fine for playback of normal media files. However, when I use the following code to try to playback a DVD, it fails with a stack overflow exception.
vmr9_ap = new vmr9ap();
HMONITOR monitor = MonitorFromWindow(hwnd, NULL);
IGraphBuilder *graph;
IBaseFilter *filter;
IDvdGraphBuilder *builder;
CoCreateInstance(CLSID_DvdGraphBuilder, NULL, CLSCTX_INPROC_SERVER, IID_IDvdGraphBuilder, reinterpret_cast<void**>(&builder));
CoCreateInstance(::CLSID_VideoMixingRenderer9, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, reinterpret_cast<void**>(&filter));
builder->GetDvdInterface(IID_IVMRFilterConfig9, (void**)&vmr9_config);
vmr9_ap->Initialize(g_pd3dDevice, monitor, vmr9_config);
HRESULT hr = builder->RenderDvdVideoVolume(L"G:\\VIDEO_TS", AM_DVD_SWDEC_PREFER | AM_DVD_VMR9_ONLY, &status);
builder->GetFiltergraph(&graph);
IDvdControl2 *dvdControl;
builder->GetDvdInterface(::IID_IDvdControl2, (void**)&dvdControl);
graph->QueryInterface(::IID_IMediaControl, (void**)&control);
HRESULT h = control->Run();
The stack overflow happens immediately after the call to control->Run(). It's driving me nuts, as I'm sure I'm just forgetting something really really simple.
Thanks.
Your graph should look something like this. Make sure there aren't any buggy filters in your graph.
Because you are using a custom allocator, I would look there for the issue and set some breakpoints there. You code you pasted might be incomplete as I do not see you configure the VMR9 with the custom allocator, nor do I see it being added to the graph. I avoid using the DVDGraphBuilder as I had too difficult of a time getting it RenderVolume correctly with my VMR9+Allocator. I would build the graph a little more manually.
I have a custom allocator in my open source project, along w/ a DVD player. You can check that out for reference, though there is a lot of code noise in there due to me needing to hack a few things in there for WPF compatiblity. http://wpfmediakit.codeplex.com
What you are seeing should NOT be a DRM issue.
alt text http://img29.imageshack.us/img29/7798/capturelu.jpg
Could it be a form of DRM protection? Decoders in DVD graphs will typically try to prevent you building graphs that get access to the uncompressed data as you do here. Normally they do that by a cleaner method, such as refusing to connect to unauthorised renderers, but it's possible that this might be caused by something like that -- certainly there are mpeg-2 decoders which use deliberate crashes to prevent reverse engineering.
G
Thanks to the code Jeremiah Morrill pointed me to, I managed to get playback mostly working.
It works fine, as long as you don't try to resize the D3DImage that it's played in. Devil's in the details, I suppose.
Thanks to all the answers. DVD playback doesn't work with a debugger attached, which according to Google, is not DRM, but is an anti-reverse engineering measure. Could be particular to the DVD codec I'm using too.
extern "C" __declspec(dllexport) LPDIRECT3DSURFACE9 InitializeDvd(HWND hWnd)
{
CoInitialize(NULL);
IPin *dvdVideoOut;
IPin *vmr9VideoIn;
HRESULT hr = S_OK;
hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
IID_IGraphBuilder, (void **)&graph);
if(graph)
{
hr = CoCreateInstance(CLSID_DVDNavigator, NULL, CLSCTX_INPROC_SERVER,
IID_IBaseFilter, (void **)&dvdNavigator);
if(dvdNavigator)
{
hr = graph->AddFilter(dvdNavigator, L"DVD Navigator");
if(SUCCEEDED(hr))
{
hr = CoCreateInstance(CLSID_VideoMixingRenderer9, NULL, CLSCTX_INPROC_SERVER,
IID_IBaseFilter, (void **)&vmr9);
if(vmr9)
{
hr = vmr9->QueryInterface(IID_IVMRFilterConfig9, reinterpret_cast<void**>(&p_fConfig));
p_Ap = new VMR9AllocatorPresenter();
p_Dh = new DeviceHandler();
p_device = p_Dh->Initialize(hWnd);
p_fConfig->SetRenderingMode(VMR9Mode_Renderless);
p_fConfig->SetNumberOfStreams(1);
p_Ap->Initialize(hWnd, p_device, p_fConfig);
if(SUCCEEDED(hr))
{
hr = graph->AddFilter(vmr9, L"Video Mixing Renderer 9");
if(p_fConfig)
{
dvdNavigator->FindPin(L"Video", &dvdVideoOut);
if(dvdVideoOut)
{
hr = graph->Render(dvdVideoOut);
}
hr = graph->QueryInterface(IID_IMediaControl, reinterpret_cast<void**>(&control));
if(control)
{
control->Run();
}
}
}
}
}
}
}
return p_Dh->g_surface9;
}