Click here to Skip to main content
15,890,557 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
Hello, I am rather new to DirectShow (or more specifically VMR-9). Right now I am using Renderless mode to grab the texture generated from my custom presenter-allocator, and store it later for my deferred renderer to use.

Currently, everything works perfectly for my machine (specs below), which is a Windows 7 machine. However, the target machine (Windows XP) for the game that I am making has issues. I hear the audio playing for the video just fine, but I am not getting a texture to use, so no video.

Here are the specs for the two machines:

My Machine:
Windows 7 Professional
Service Pack 1
Intel Core i5 CPU
M 520 @ 2.4 GHz
Graphics Card:
NVIDIA Quadro FX 2800M
3750 MB Approx. Total Memory
Version 8.17.12.7621

Target Machine:
Windows XP Professional 2002
Service Pack 2
Intel Core 2 Duo CPU
E8400 @ 3.0 GHz
Graphics Card:
NVIDIA GeForce 9800 GT
512.0 MB RAM
Version 6.14.0012.8026

Is there anything obvious that I'm missing, like does the VMR-9 simply not work on this machine, or some flag or extra call I need to make? Or is there something deeper?

To top it all off, I do not always have access to the machine (in fact I rarely do), so this makes testing difficult, and I need to make every test count.

I have my code listed below. Please help if you can.

C++
VideoAllocator::VideoAllocator (void)
{
	m_nRefCount = 0;
	m_pConfig = NULL;
	m_pNotify = NULL;
	m_pDevice = NULL;
	m_pSurfaces = NULL;
	m_dwSurfaces = 0;
	m_pTestMesh = NULL;
	InitializeCriticalSection(&m_CriticalSection);
}

VideoAllocator::~VideoAllocator (void)
{
	TerminateDevice(0);
	m_nRefCount = 0;
	m_pConfig = NULL;
	m_pNotify = NULL;
	m_pDevice = NULL;
	m_pSurfaces = NULL;
	m_dwSurfaces = 0;

	if (m_pTestMesh)
	{
		m_pTestMesh->ReleaseTexture();
		m_pTestMesh = NULL;
	}

	DeleteCriticalSection(&m_CriticalSection);
}

ULONG VideoAllocator::AddRef (void)
{
	return InterlockedIncrement(&m_nRefCount);
}

HRESULT VideoAllocator::QueryInterface (REFIID riid, void** ppvObject)
{
	if (ppvObject == NULL)
	{
#ifndef _RELEASEBUILD
		printf("The Allocator-Presenter's QueryInterface was passed an invalid pointer.\n");
#endif
		return E_POINTER;
	}

	// NOTE: We use a static_cast<> for a reason.  If we did something like
	// *ppvObject = (IVMRSurfaceAllocator9*)(this);  it would blow up the
	// Virtual Function Table.  Trust me, I found out the hard way :p.

	if (riid == IID_IVMRSurfaceAllocator9)
	{
		*ppvObject = static_cast<IVMRSurfaceAllocator9*>(this);
		AddRef();
		return S_OK;
	}

	if (riid == IID_IVMRImagePresenter9)
	{
		*ppvObject = static_cast<IVMRImagePresenter9*>(this);
		AddRef();
		return S_OK;
	}

	if (riid == IID_IUnknown)
	{
		// Since this class technically inherits from IUnknown twice,
		// we have to cast this class as one of the interfaces it
		// inherits off of, then cast it as an IUnknown (it does not
		// matter if we cast it as a IVMRSurfaceAllocator9* or as a
		// IVMRImagePresenter9* first).
		*ppvObject = static_cast<IUnknown*>(static_cast<IVMRSurfaceAllocator9*>(this));
		AddRef();
		return S_OK;
	}

#ifndef _RELEASEBUILD
	if (riid != IID_IVMRImagePresenterConfig9 && riid != IID_IVMRSurfaceAllocatorEx9 &&
		riid != IID_IVMRWindowlessControl9 && riid != IID_IVMRWindowlessControl9)
		printf("The Allocator-Presenter's QueryInterface was passed an invalid ID.\n");
#endif
	return E_NOINTERFACE;
}

ULONG VideoAllocator::Release (void)
{
	ULONG uCount = InterlockedDecrement(&m_nRefCount);
	if (uCount <= 0)
	{
		delete this;
	}

	return uCount;
}

HRESULT VideoAllocator::AdviseNotify (IVMRSurfaceAllocatorNotify9* lpIVMRSurfAllocNotify)
{
	EnterCriticalSection(&m_CriticalSection);

	if (lpIVMRSurfAllocNotify == NULL)
	{
		LeaveCriticalSection(&m_CriticalSection);
		return E_POINTER;
	}

	m_pNotify = lpIVMRSurfAllocNotify;

	LeaveCriticalSection(&m_CriticalSection);
	return S_OK;
}

HRESULT VideoAllocator::GetSurface (DWORD_PTR dwUserID, DWORD SurfaceIndex,
		DWORD SurfaceFlags, IDirect3DSurface9** lplpSurface)
{
	EnterCriticalSection(&m_CriticalSection);

	if (lplpSurface == NULL)
	{
#ifndef _RELEASEBUILD
		printf("Invalid surface pointer in GetSurface().\n");
#endif
		LeaveCriticalSection(&m_CriticalSection);
		return E_POINTER;
	}

	if (SurfaceIndex >= m_dwSurfaces)
	{
#ifndef _RELEASEBUILD
		printf("Invalid surface index in GetSurface().\n");
#endif
		LeaveCriticalSection(&m_CriticalSection);
		return E_INVALIDARG;
	}

	*lplpSurface = m_pSurfaces[SurfaceIndex];
	(*lplpSurface)->AddRef();

	LeaveCriticalSection(&m_CriticalSection);
	return S_OK;
}

HRESULT VideoAllocator::InitializeDevice (DWORD_PTR dwUserID, VMR9AllocationInfo* lpAllocInfo, DWORD* lpNumBuffers)
{
	EnterCriticalSection(&m_CriticalSection);

	if (lpAllocInfo == NULL)
	{
#ifndef _RELEASEBUILD
		printf("Invalid Allocation pointer in InitializeDevice().\n");
#endif
		LeaveCriticalSection(&m_CriticalSection);
		return E_POINTER;
	}

	if (lpNumBuffers == NULL)
	{
#ifndef _RELEASEBUILD
		printf("Invalid Number of Buffers pointer in InitializeDevice().\n");
#endif
		LeaveCriticalSection(&m_CriticalSection);
		return E_POINTER;
	}

	// Specify that we want a texture surface.
	lpAllocInfo->dwFlags = VMR9AllocFlag_TextureSurface/* | VMR9AllocFlag_3DRenderTarget*/;
	lpAllocInfo->Format = D3DFMT_X8R8G8B8;

	// Release the old surfaces and create new ones
	ReleaseSurfaces();
	m_pSurfaces = new IDirect3DSurface9* [*lpNumBuffers];
	if (m_pSurfaces == NULL)
	{
		LeaveCriticalSection(&m_CriticalSection);
		return E_OUTOFMEMORY;
	}

	// Allocate the Surfaces
	HRESULT hr = m_pNotify->AllocateSurfaceHelper(lpAllocInfo, lpNumBuffers,
		m_pSurfaces);

	m_dwSurfaces = *lpNumBuffers;
	if (FAILED(hr))
	{
#ifndef _RELEASEBUILD
		printf("Failed to Allocate the Surfaces in InitializeDevice().\n");
#endif
		ReleaseSurfaces();
		*lpNumBuffers = 0;
	}

	LeaveCriticalSection(&m_CriticalSection);
	return hr;
}

HRESULT VideoAllocator::TerminateDevice (DWORD_PTR dwID)
{
	EnterCriticalSection(&m_CriticalSection);
	ReleaseSurfaces();
	LeaveCriticalSection(&m_CriticalSection);
	return S_OK;
}

HRESULT VideoAllocator::PresentImage (DWORD_PTR dwUserID, VMR9PresentationInfo* lpPresInfo)
{
	EnterCriticalSection(&m_CriticalSection);

	if (m_pNotify == NULL)
	{
#ifndef _RELEASEBUILD
		printf("m_pNotify has not been created before PresentImage() was called.\n");
#endif
		LeaveCriticalSection(&m_CriticalSection);
		return E_FAIL;
	}

	if (lpPresInfo == NULL)
	{
#ifndef _RELEASEBUILD
		printf("No presentation info was passed into PresentImage().\n");
#endif
		LeaveCriticalSection(&m_CriticalSection);
		return E_FAIL;
	}

	if (lpPresInfo->lpSurf == NULL)
	{
#ifndef _RELEASEBUILD
		printf("No surface was passed into PresentImage().\n");
#endif
		LeaveCriticalSection(&m_CriticalSection);
		return E_FAIL;
	}

	IDirect3DTexture9* texture;
	lpPresInfo->lpSurf->GetContainer(IID_IDirect3DTexture9,
		(void**)&texture);

	if (texture && m_pTestMesh)
	{
		m_pTestMesh->SetTexture(texture);
	}

	LeaveCriticalSection(&m_CriticalSection);
	return S_OK;
}

HRESULT VideoAllocator::StartPresenting (DWORD_PTR dwUserID)
{
	return S_OK;
}

HRESULT VideoAllocator::StopPresenting (DWORD_PTR dwUserID)
{
	return S_OK;
}

void VideoAllocator::ReleaseSurfaces (void)
{
	for (DWORD i = 0; i < m_dwSurfaces; ++i)
	{
		if (m_pSurfaces[i])
		{
			m_pSurfaces[i]->Release();
			m_pSurfaces[i] = NULL;
		}
	}

	if (m_pSurfaces)
	{
		delete m_pSurfaces;
		m_pSurfaces = NULL;
	}

	m_dwSurfaces = 0;
	m_pTestMesh = NULL;
}

VideoManager::VideoManager (void)
{
	m_pGraph = NULL;
	m_pControl = NULL;
	m_pEvent = NULL;
	m_pVMR9 = NULL;
	m_pDSound = NULL;
	m_pNotify = NULL;
	m_pAllocator = NULL;
}

VideoManager::~VideoManager (void)
{

}

void VideoManager::Initialize (HWND hWnd, IDirect3DDevice9* device)
{
	// Initialize the COM Library
	HRESULT hr = CoInitialize(NULL);
	if (FAILED(hr))
	{
#ifndef _RELEASEBUILD
		printf("Could not initialize the COM Library.\n");
#endif
		return;
	}

	// Create the Filter Graph Manager
	hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
		IID_IGraphBuilder, (void**)&m_pGraph);
	if (FAILED(hr))
	{
#ifndef _RELEASEBUILD
		printf("Could not create the Filter Graph Manager.\n");
#endif
		return;
	}

	hr = m_pGraph->QueryInterface(IID_IMediaControl, (void**)&m_pControl);
	if (FAILED(hr))
	{
#ifndef _RELEASEBUILD
		printf("Could not create the Media Control Interface.\n");
#endif
		return;
	}

	hr = m_pGraph->QueryInterface(IID_IMediaEventEx, (void**)&m_pEvent);
	if (FAILED(hr))
	{
#ifndef _RELEASEBUILD
		printf("Could not create the Media Event Interface.\n");
#endif
		return;
	}

	// Set up the windows message
	m_pEvent->SetNotifyWindow((OAHWND)hWnd, WM_GRAPH_EVENT, 0);

	// Create the VMR-9 filter
	hr = CoCreateInstance(CLSID_VideoMixingRenderer9, NULL, CLSCTX_INPROC_SERVER,
		IID_PPV_ARGS(&m_pVMR9));
	if (FAILED(hr))
	{
#ifndef _RELEASEBUILD
		printf("Could not create the VMR-9 Filter.\n");
#endif
		return;
	}

	// Add the VMR-9 Filter to the graph
	hr = m_pGraph->AddFilter(m_pVMR9, L"VMR9");
	if (FAILED(hr))
	{
#ifndef _RELEASEBUILD
		printf("Could not add the VMR-9 Filter to the graph.\n");
#endif
		return;
	}

	// Configure the VMR for Renderless mode before adding video streams to it.
	IVMRFilterConfig9* pConfig  = NULL;
	m_pVMR9->QueryInterface(IID_IVMRFilterConfig9, (void**)&pConfig);
	hr = pConfig->SetRenderingMode(VMR9Mode_Renderless);
	hr = pConfig->SetNumberOfStreams(1);
	if (FAILED(hr))
	{
#ifndef _RELEASEBUILD
		printf("Could not properly configure the VMR-9 Filter.\n");
#endif
		return;
	}
	pConfig->Release();

	hr = m_pVMR9->QueryInterface(IID_IVMRSurfaceAllocatorNotify9,
		(void**)&m_pNotify);
	if (FAILED(hr))
	{
#ifndef _RELEASEBUILD
		printf("Could not create the Surface Allocator Notify.\n");
#endif
		return;
	}

	// Create the DirectSound Filter
	hr = CoCreateInstance(CLSID_DSoundRender, NULL, CLSCTX_INPROC_SERVER,
		IID_PPV_ARGS(&m_pDSound));
	if (FAILED(hr))
	{
#ifndef _RELEASEBUILD
		printf("Could not create the DirectSound Filter.\n");
#endif
		return;
	}

	// Add the DirectSound Filter to the graph
	hr = m_pGraph->AddFilter(m_pDSound, L"DirectSound");
	if (FAILED(hr))
	{
#ifndef _RELEASEBUILD
		printf("Could not add the DirectSound Filter to the graph.\n");
#endif
		return;
	}

	// Add a source filter
	IBaseFilter* pSource = NULL;
	hr = m_pGraph->AddSourceFilter(L"Resource/Videos/My Little Pony Friendship is Magic Opening.mp4",
		L"Source1", &pSource);
	if (FAILED(hr))
	{
#ifndef _RELEASEBUILD
		printf("Could not add the source filter to the graph.\n");
#endif
		return;
	}

	// Hook up the VMR to the D3D Device
	IDirect3D9* pD3D = NULL;
	device->GetDirect3D(&pD3D);
	HMONITOR hMonitor = pD3D->GetAdapterMonitor(D3DADAPTER_DEFAULT);
	hr = m_pNotify->SetD3DDevice(device, hMonitor);
	if (FAILED(hr))
	{
#ifndef _RELEASEBUILD
		printf("Failed to hook up the VMR to the D3D Device.\n");
#endif
		return;
	}

	m_pAllocator = new VideoAllocator();
	m_pAllocator->AddRef();

	m_pAllocator->SetD3DDevice(device);

	// Connect the Allocator to the Notify
	hr = m_pNotify->AdviseSurfaceAllocator(0, m_pAllocator);
	if (FAILED(hr))
	{
#ifndef _RELEASEBUILD
		printf("Failed to connect the Allocator to the Notify.\n");
#endif
		return;
	}

	// Connect the Notify to the Allocator
	hr = m_pAllocator->AdviseNotify(m_pNotify);
	if (FAILED(hr))
	{
#ifndef _RELEASEBUILD
		printf("Failed to connect the Notify to the Allocator.\n");
#endif
		return;
	}

	// Prevent VMR-9 from changing the Render Target.
	IVMRMixerControl9* pMix = NULL;
	hr = m_pVMR9->QueryInterface(IID_IVMRMixerControl9, (void**)&pMix);
	if (SUCCEEDED(hr))
	{
		DWORD dwPrefs = 0;
		hr = pMix->GetMixingPrefs(&dwPrefs);
		if (SUCCEEDED(hr))
		{
			dwPrefs &= ~MixerPref9_RenderTargetMask;
			dwPrefs |= MixerPref9_RenderTargetYUV;

			hr = pMix->SetMixingPrefs(dwPrefs);
		}

		pMix->Release();
	}
	if (FAILED(hr))
	{
#ifndef _RELEASEBUILD
		printf("Could not prevent the VMR-9 from not changing Targets.\n");
#endif
		return;
	}

	// Collect all of the output pins for the source
	PinList pins;
	hr = GetPinList(pSource, PINDIR_OUTPUT, pins);
	if (FAILED(hr))
	{
#ifndef _RELEASEBUILD
		printf("Error retrieving the source filter's pins.\n");
#endif
		return;
	}

	// Try to render each pin. If one pin succeeds, then we are okay.
	// The other pins may be not video related.
	IFilterGraph2* pGraph2 = NULL;
	m_pGraph->QueryInterface(IID_IFilterGraph2, (void**)&pGraph2);
	hr = E_FAIL;
	for (PinIter iter (pins); !iter.end(); ++iter)
	{
		HRESULT hr2 = pGraph2->RenderEx(iter.current(),
			AM_RENDEREX_RENDERTOEXISTINGRENDERERS, 0);
		if (hr == E_FAIL && SUCCEEDED(hr2))
			hr = S_OK;
	}
	if (FAILED(hr))
	{
#ifndef _RELEASEBUILD
		printf("Failed to render one of the source filter's pins.\n");
#endif
		return;
	}

	pGraph2->Release();
	pSource->Release();
}

void VideoManager::Test (void)
{
	if (FAILED(m_pControl->Run()))
	{
#ifndef _RELEASEBUILD
		printf("Could not Run the Media Control.\n");
#endif
	}

	long evCode = 0;
	m_pEvent->WaitForCompletion(0, &evCode);
}

void VideoManager::Exit (void)
{
	if (m_pAllocator)
	{
		m_pAllocator->ReleaseSurfaces();
		delete m_pAllocator;
		m_pAllocator = NULL;
	}

	if (m_pGraph)
	{
		m_pGraph->Release();
		m_pGraph = NULL;
	}

	CoUninitialize();
}

void VideoManager::ClearVideos (void)
{
	EnterCriticalSection(&m_pAllocator->m_CriticalSection);
	m_pAllocator->m_pTestMesh = NULL;
	LeaveCriticalSection(&m_pAllocator->m_CriticalSection);
}

void VideoManager::HandleWindowsMsg (void)
{
	long EventCode, param1, param2;
	
	// Loop until no messages are left in the queue
	while (SUCCEEDED(m_pEvent->GetEvent(&EventCode, ¶m1, ¶m2, 0)))
	{
		switch (EventCode)
		{
		case EC_COMPLETE:
			break;
		};
	}

	m_pEvent->FreeEventParams(EventCode, param1, param2);
}

HRESULT VideoManager::GetPinList (IBaseFilter* pFilter, PIN_DIRECTION pinDir, PinList& pins)
{
	if (pFilter == NULL)
		return E_POINTER;

	CComPtr<IEnumPins> pEnum;
	HRESULT hr = pFilter->EnumPins(&pEnum);
	if (FAILED(hr))
		return hr;

	while (hr == S_OK)
	{
		CComPtr<IPin> pPin;
		hr = pEnum->Next(1, &pPin, NULL);

		if (hr != S_OK)
			break;

		PIN_DIRECTION curDir;
		hr = pPin->QueryDirection(&curDir);
		if (FAILED(hr))
			return hr;

		if (curDir == pinDir)
			pins.addTail(pPin);
	}

	return S_OK;
}
Posted
Updated 5-Sep-12 18:43pm
v3

1 solution

Your issue is that you allocate fixed surface formats no matter what format is queried by the filter:
C#
lpAllocInfo->Format = D3DFMT_X8R8G8B8;
In that case VMR9 will accepts only RGB32 inputs, and on your test PC you should have color space converter in case if your decoder or downstream filter can't make RGB32 output, most of decoders makes an output in YV12,IYUV or NV12, so you should have colorspace converter which support conversion of those formats into RGB32. Or you should have same decoder as you use on Windows 7 Machine for playback. Or you should have to support other formats at least YUY2 or other YUV packets formats as in that case will be used AVI Decompressor intermediate filter which solve convestion color spaces from decoder. Try to look check SDK sample of VMR9 allocator in there is shown how to handle other color spaces.

Regards,
Maxim.
 
Share this answer
 
Comments
GrizzlyJames 8-Sep-12 12:54pm    
Thank you very much for taking a look at this. I have implemented the solution on my machine (the Windows 7 one), and it works. Unfortunately, I have to wait until Monday before I can test to see if this works on the target machine (the Windows XP one).

I will inform you of the results, and if it works, I will be hitting that green button above, and a very big special thanks in the game's credits!
GrizzlyJames 10-Sep-12 13:07pm    
Unfortunately, it still does not work on the test machine. For now I'm going to just use AVIFile to get videos to work.

Here is my new code for InitializeDevice if you or anyone else would like to take a look at it:

HRESULT VideoAllocator::InitializeDevice (DWORD_PTR dwUserID, VMR9AllocationInfo* lpAllocInfo, DWORD* lpNumBuffers)
{
EnterCriticalSection(&m_CriticalSection);

if (lpAllocInfo == NULL)
{
#ifndef _RELEASEBUILD
printf("Invalid Allocation pointer in InitializeDevice().\n");
#endif
LeaveCriticalSection(&m_CriticalSection);
return E_POINTER;
}

if (lpNumBuffers == NULL)
{
#ifndef _RELEASEBUILD
printf("Invalid Number of Buffers pointer in InitializeDevice().\n");
#endif
LeaveCriticalSection(&m_CriticalSection);
return E_POINTER;
}

D3DCAPS9 d3dcaps;
DWORD dwWidth = 1;
DWORD dwHeight = 1;

m_pDevice->GetDeviceCaps(&d3dcaps);
if (d3dcaps.TextureCaps & D3DPTEXTURECAPS_POW2)
{
while (dwWidth < lpAllocInfo->dwWidth)
dwWidth <<= 1;
while (dwHeight < lpAllocInfo->dwHeight)
dwHeight <<= 1;
lpAllocInfo->dwWidth = dwWidth;
lpAllocInfo->dwHeight = dwHeight;
}

// Try creating textures first, if the machine can handle it.
lpAllocInfo->dwFlags |= VMR9AllocFlag_TextureSurface;

ReleaseSurfaces();
m_dwSurfaces = *lpNumBuffers;
m_pSurfaces = new IDirect3DSurface9* [m_dwSurfaces];
HRESULT hr = m_pNotify->AllocateSurfaceHelper(lpAllocInfo, lpNumBuffers, m_pSurfaces);

// If we failed, and the format is not an alpha format, then we need to render
// to an off-screen surface, and copy decode the images onto a private texture.
if (FAILED(hr) && !(lpAllocInfo->dwFlags & VMR9AllocFlag_3DRenderTarget))
{
// Is the surface a YUV?
if (lpAllocInfo->Format > '0000')
{
D3DDISPLAYMODE dm;
hr = m_pDevice->GetDisplayMode(NULL, &dm);
if (FAILED(hr))
{
#ifndef _RELEASEBUILD
printf("Failed to get the Display Mode in InitializeDevice().\n");
#endif
LeaveCriticalSection(&m_CriticalSection);
return hr;
}

hr = m_pDevice->CreateTexture(lpAllocInfo->dwWidth, lpAllocInfo->dwHeight,
1, D3DUSAGE_RENDERTARGET, dm.Format, D3DPOOL_DEFAULT, &m_pPrivateTexture, NULL);
if (FAILED(hr))
{
#ifndef _RELEASEBUILD
printf("Failed to create a private texture in InitializeDevice().\n");
#endif
LeaveCriticalSection(&m_CriticalSection);
return hr;
}
}

lpAllocInfo->dwFlags &= ~VMR9AllocFlag_TextureSurface;
lpAllocInfo->dwFlags |= VMR9AllocFlag_OffscreenSurface;

hr = m_pNotify->AllocateSurfaceHelper(lpAllocInfo, lpNumBuffers, m_pSurfaces);
if (FAILED(hr))
{
#ifndef _RELEASEBUILD
printf("Failed to work around this machine, VMR-9 ain't gonna happen.\n");
#endif
LeaveCriticalSection(&m_CriticalSection);
return hr;
}
}

LeaveCriticalSection(&m_CriticalSection);
return hr;
}
Maxim Kartavenkov 10-Sep-12 13:30pm    
Check in GraphEdit graphs on both machines. Try with the same decoders. NOTE on XP you can add graph to the ROT and connect to it remotely from GraphEdit
GrizzlyJames 11-Sep-12 0:31am    
Holy crap! I did not know this thing existed! I will definitely try running this tool tomorrow and see what's up.
GrizzlyJames 12-Sep-12 2:17am    
Using GraphEdit I found out that the AVI file that I was using had a format that was unsupported by the machine. I did some conversions, and it now runs on the test machine as well!

Thank you very much Maxim Kartavenkov! You will be listed in our games credits for your help. I'll be sure to send you a link to our game once we finish it next month.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900