I've been trying in vain to come up with a no frills example of displaying video using Microsoft's Media Foundation Enhanced Video Renderer (EVR). I'm testing on Windows 7 with Visual Studio 2013.
I'm pretty sure I've got the media types configured correctly as I can export and save the buffer from the IMFSample in my read loop to a bitmap. I can also get the video to render IF I get MF to automatically generate the topology but in this case I need to wire up the source reader and sink writer manually so I can get access to the different parts of the pipeline.
I have used mftrace to see if I can spot anything different between the automatically generated topology and the manually wired up example but nothing obvious jumps out.
The code is below (full sample project at https://github.com/sipsorcery/mediafoundationsamples/tree/master/MFVideoEVR).
Is there a step I've missed to get the IMFSample from the SinkWriter to display on the video window? I've been looking at a few examples that go deeper into the DirectX pipeline but should that be necessary or is the EVR meant to abstract those mechanics aways?
#include <stdio.h>
#include <tchar.h>
#include <evr.h>
#include <mfapi.h>
#include <mfplay.h>
#include <mfreadwrite.h>
#include <mferror.h>
#include "..\Common\MFUtility.h"
#include <windows.h>
#include <windowsx.h>
#pragma comment(lib, "mf.lib")
#pragma comment(lib, "evr.lib")
#pragma comment(lib, "mfplat.lib")
#pragma comment(lib, "mfplay.lib")
#pragma comment(lib, "mfreadwrite.lib")
#pragma comment(lib, "mfuuid.lib")
#pragma comment(lib, "Strmiids")
#pragma comment(lib, "wmcodecdspuuid.lib")
#define CHECK_HR(hr, msg) if (hr != S_OK) { printf(msg); printf("Error: %.2X.\n", hr); goto done; }
void InitializeWindow();
// Constants
const WCHAR CLASS_NAME[] = L"MFVideoEVR Window Class";
const WCHAR WINDOW_NAME[] = L"MFVideoEVR";
// Globals.
HWND _hwnd;
using namespace System::Threading::Tasks;
int main()
{
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
MFStartup(MF_VERSION);
IMFMediaSource *videoSource = NULL;
UINT32 videoDeviceCount = 0;
IMFAttributes *videoConfig = NULL;
IMFActivate **videoDevices = NULL;
IMFSourceReader *videoReader = NULL;
WCHAR *webcamFriendlyName;
IMFMediaType *videoSourceOutputType = NULL, *pvideoSourceModType = NULL, *pSrcOutMediaType = NULL;
IMFSourceResolver *pSourceResolver = NULL;
IUnknown* uSource = NULL;
IMFMediaSource *mediaFileSource = NULL;
IMFAttributes *pVideoReaderAttributes = NULL;
IMFMediaType *pVideoOutType = NULL;
MF_OBJECT_TYPE ObjectType = MF_OBJECT_INVALID;
IMFMediaSink *pVideoSink = NULL;
IMFStreamSink *pStreamSink = NULL;
IMFMediaTypeHandler *pMediaTypeHandler = NULL;
IMFMediaType *pMediaType = NULL;
IMFMediaType *pSinkMediaType = NULL;
IMFSinkWriter *pSinkWriter = NULL;
IMFVideoRenderer *pVideoRenderer = NULL;
IMFVideoPresenter *pVideoPresenter = nullptr;
IMFVideoDisplayControl *pVideoDisplayControl = nullptr;
IMFGetService *pService = nullptr;
IMFActivate* pActive = NULL;
MFVideoNormalizedRect nrcDest = { 0.5f, 0.5f, 1.0f, 1.0f };
IMFPresentationTimeSource *pSystemTimeSource = nullptr;
IMFMediaType *sinkPreferredType = nullptr;
IMFPresentationClock *pClock = NULL;
IMFPresentationTimeSource *pTimeSource = NULL;
CHECK_HR(MFTRegisterLocalByCLSID(
__uuidof(CColorConvertDMO),
MFT_CATEGORY_VIDEO_PROCESSOR,
L"",
MFT_ENUM_FLAG_SYNCMFT,
0,
NULL,
0,
NULL
), "Error registering colour converter DSP.\n");
Task::Factory->StartNew(gcnew Action(InitializeWindow));
Sleep(1000);
if (_hwnd == nullptr)
{
printf("Failed to initialise video window.\n");
goto done;
}
// Set up the reader for the file.
CHECK_HR(MFCreateSourceResolver(&pSourceResolver), "MFCreateSourceResolver failed.\n");
CHECK_HR(pSourceResolver->CreateObjectFromURL(
L"..\\..\\MediaFiles\\big_buck_bunny.mp4", // URL of the source.
MF_RESOLUTION_MEDIASOURCE, // Create a source object.
NULL, // Optional property store.
&ObjectType, // Receives the created object type.
&uSource // Receives a pointer to the media source.
), "Failed to create media source resolver for file.\n");
CHECK_HR(uSource->QueryInterface(IID_PPV_ARGS(&mediaFileSource)), "Failed to create media file source.\n");
CHECK_HR(MFCreateAttributes(&pVideoReaderAttributes, 2), "Failed to create attributes object for video reader.\n");
CHECK_HR(pVideoReaderAttributes->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID), "Failed to set dev source attribute type for reader config.\n");
CHECK_HR(pVideoReaderAttributes->SetUINT32(MF_SOURCE_READER_ENABLE_VIDEO_PROCESSING, 1), "Failed to set enable video processing attribute type for reader config.\n");
CHECK_HR(MFCreateSourceReaderFromMediaSource(mediaFileSource, pVideoReaderAttributes, &videoReader),
"Error creating media source reader.\n");
CHECK_HR(videoReader->GetCurrentMediaType((DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, &videoSourceOutputType),
"Error retrieving current media type from first video stream.\n");
Console::WriteLine("Default output media type for source reader:");
Console::WriteLine(GetMediaTypeDescription(videoSourceOutputType));
Console::WriteLine();
// Set the video output type on the source reader.
CHECK_HR(MFCreateMediaType(&pvideoSourceModType), "Failed to create video output media type.\n");
CHECK_HR(pvideoSourceModType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), "Failed to set video output media major type.\n");
CHECK_HR(pvideoSourceModType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32), "Failed to set video sub-type attribute on EVR input media type.\n");
CHECK_HR(pvideoSourceModType->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive), "Failed to set interlace mode attribute on EVR input media type.\n");
CHECK_HR(pvideoSourceModType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE), "Failed to set independent samples attribute on EVR input media type.\n");
CHECK_HR(MFSetAttributeRatio(pvideoSourceModType, MF_MT_PIXEL_ASPECT_RATIO, 1, 1), "Failed to set pixel aspect ratio attribute on EVR input media type.\n");
CHECK_HR(CopyAttribute(videoSourceOutputType, pvideoSourceModType, MF_MT_FRAME_SIZE), "Failed to copy video frame size attribute from input file to output sink.\n");
CHECK_HR(CopyAttribute(videoSourceOutputType, pvideoSourceModType, MF_MT_FRAME_RATE), "Failed to copy video frame rate attribute from input file to output sink.\n");
CHECK_HR(videoReader->SetCurrentMediaType((DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, NULL, pvideoSourceModType), "Failed to set media type on source reader.\n");
Console::WriteLine("Output media type set on source reader:");
Console::WriteLine(GetMediaTypeDescription(pvideoSourceModType));
Console::WriteLine();
// Create EVR sink .
//CHECK_HR(MFCreateVideoRenderer(__uuidof(IMFMediaSink), (void**)&pVideoSink), "Failed to create video sink.\n");
CHECK_HR(MFCreateVideoRendererActivate(_hwnd, &pActive), "Failed to created video rendered activation context.\n");
CHECK_HR(pActive->ActivateObject(IID_IMFMediaSink, (void**)&pVideoSink), "Failed to activate IMFMediaSink interface on video sink.\n");
// Initialize the renderer before doing anything else including querying for other interfaces (https://msdn.microsoft.com/en-us/library/windows/desktop/ms704667(v=vs.85).aspx).
CHECK_HR(pVideoSink->QueryInterface(__uuidof(IMFVideoRenderer), (void**)&pVideoRenderer), "Failed to get video Renderer interface from EVR media sink.\n");
CHECK_HR(pVideoRenderer->InitializeRenderer(NULL, NULL), "Failed to initialise the video renderer.\n");
CHECK_HR(pVideoSink->QueryInterface(__uuidof(IMFGetService), (void**)&pService), "Failed to get service interface from EVR media sink.\n");
CHECK_HR(pService->GetService(MR_VIDEO_RENDER_SERVICE, __uuidof(IMFVideoDisplayControl), (void**)&pVideoDisplayControl), "Failed to get video display control interface from service interface.\n");
CHECK_HR(pVideoSink->GetStreamSinkByIndex(0, &pStreamSink), "Failed to get video renderer stream by index.\n");
CHECK_HR(pStreamSink->GetMediaTypeHandler(&pMediaTypeHandler), "Failed to get media type handler.\n");
// Set the video output type on the source reader.
CHECK_HR(MFCreateMediaType(&pVideoOutType), "Failed to create video output media type.\n");
CHECK_HR(pVideoOutType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), "Failed to set video output media major type.\n");
CHECK_HR(pVideoOutType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32), "Failed to set video sub-type attribute on EVR input media type.\n");
CHECK_HR(pVideoOutType->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive), "Failed to set interlace mode attribute on EVR input media type.\n");
CHECK_HR(pVideoOutType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE), "Failed to set independent samples attribute on EVR input media type.\n");
CHECK_HR(MFSetAttributeRatio(pVideoOutType, MF_MT_PIXEL_ASPECT_RATIO, 1, 1), "Failed to set pixel aspect ratio attribute on EVR input media type.\n");
CHECK_HR(CopyAttribute(videoSourceOutputType, pVideoOutType, MF_MT_FRAME_SIZE), "Failed to copy video frame size attribute from input file to output sink.\n");
CHECK_HR(CopyAttribute(videoSourceOutputType, pVideoOutType, MF_MT_FRAME_RATE), "Failed to copy video frame rate attribute from input file to output sink.\n");
//CHECK_HR(pMediaTypeHandler->GetMediaTypeByIndex(0, &pSinkMediaType), "Failed to get sink media type.\n");
CHECK_HR(pMediaTypeHandler->SetCurrentMediaType(pVideoOutType), "Failed to set current media type.\n");
Console::WriteLine("Input media type set on EVR:");
Console::WriteLine(GetMediaTypeDescription(pVideoOutType));
Console::WriteLine();
CHECK_HR(MFCreatePresentationClock(&pClock), "Failed to create presentation clock.\n");
CHECK_HR(MFCreateSystemTimeSource(&pTimeSource), "Failed to create system time source.\n");
CHECK_HR(pClock->SetTimeSource(pTimeSource), "Failed to set time source.\n");
//CHECK_HR(pClock->Start(0), "Error starting presentation clock.\n");
CHECK_HR(pVideoSink->SetPresentationClock(pClock), "Failed to set presentation clock on video sink.\n");
Console::WriteLine("Press any key to start video sampling...");
Console::ReadLine();
IMFSample *videoSample = NULL;
DWORD streamIndex, flags;
LONGLONG llTimeStamp;
while (true)
{
CHECK_HR(videoReader->ReadSample(
MF_SOURCE_READER_FIRST_VIDEO_STREAM,
0, // Flags.
&streamIndex, // Receives the actual stream index.
&flags, // Receives status flags.
&llTimeStamp, // Receives the time stamp.
&videoSample // Receives the sample or NULL.
), "Error reading video sample.");
if (flags & MF_SOURCE_READERF_ENDOFSTREAM)
{
printf("End of stream.\n");
break;
}
if (flags & MF_SOURCE_READERF_STREAMTICK)
{
printf("Stream tick.\n");
}
if (!videoSample)
{
printf("Null video sample.\n");
}
else
{
printf("Attempting to write sample to stream sink.\n");
CHECK_HR(videoSample->SetSampleTime(llTimeStamp), "Error setting the video sample time.\n");
//CHECK_HR(videoSample->SetSampleDuration(41000000), "Error setting the video sample duration.\n");
CHECK_HR(pStreamSink->ProcessSample(videoSample), "Streamsink process sample failed.\n");
}
SafeRelease(&videoSample);
}
done:
printf("finished.\n");
getchar();
return 0;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
void InitializeWindow()
{
WNDCLASS wc = { 0 };
wc.lpfnWndProc = WindowProc;
wc.hInstance = GetModuleHandle(NULL);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.lpszClassName = CLASS_NAME;
if (RegisterClass(&wc))
{
_hwnd = CreateWindow(
CLASS_NAME,
WINDOW_NAME,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
640,
480,
NULL,
NULL,
GetModuleHandle(NULL),
NULL
);
if (_hwnd)
{
ShowWindow(_hwnd, SW_SHOWDEFAULT);
MSG msg = { 0 };
while (true)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
Sleep(1);
}
}
}
}
}
Yes it seems that EVR needs Video Samples
The video sample object is a specialized implementation of the IMFSample interface for use with the Enhanced Video Renderer (EVR).
But it seems not to be needed to manually provide Video Samples if the decoder supports DXVA IMFVideoSampleAllocator
The Media Session uses this interface to allocate samples for the EVR, unless the upstream decoder supports DirectX Video Acceleration (DXVA).
I think you should investigate under IMFVideoSampleAllocator, because your software acts like a mediasession.
You also need to call clock start, just before "while loop video processing".
You also need to release all interfaces, stop clock and call some shutdown method.
Like David Wohlferd said, you should handle Media Sink Events too, for better improvment of your program.
EDIT :
#include <stdio.h>
#include <tchar.h>
#include <evr.h>
#include <mfapi.h>
#include <mfplay.h>
#include <mfreadwrite.h>
#include <mferror.h>
#include "MFUtility.h"
#include <windows.h>
#include <windowsx.h>
#include <d3d9.h>
#include <mfobjects.h>
#include <Dxva2api.h>
#pragma comment(lib, "mf.lib")
#pragma comment(lib, "evr.lib")
#pragma comment(lib, "mfplat.lib")
#pragma comment(lib, "mfplay.lib")
#pragma comment(lib, "mfreadwrite.lib")
#pragma comment(lib, "mfuuid.lib")
#pragma comment(lib, "Strmiids")
#pragma comment(lib, "wmcodecdspuuid.lib")
#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "Dxva2.lib")
#define CHECK_HR(hr, msg) if (hr != S_OK) { printf(msg); printf("Error: %.2X.\n", hr); goto done; }
void InitializeWindow();
HRESULT CreateD3DSample(IDirect3DSwapChain9 *pSwapChain, IMFSample **ppVideoSample);
// Constants
const WCHAR CLASS_NAME[] = L"MFVideoEVR Window Class";
const WCHAR WINDOW_NAME[] = L"MFVideoEVR";
// Globals.
HWND _hwnd;
LPDIRECT3D9 _d3d; // the pointer to our Direct3D interface
LPDIRECT3DDEVICE9 _d3ddev; // the pointer to the device class
IDirect3DSwapChain9 * _pSwapChain;
IDirect3DTexture9 *_pd3dTexture;
#define VIDEO_WIDTH 320
#define VIDEO_HEIGHT 240
using namespace System::Threading::Tasks;
int main()
{
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
MFStartup(MF_VERSION);
IMFMediaSource *videoSource = NULL;
UINT32 videoDeviceCount = 0;
IMFAttributes *videoConfig = NULL;
IMFActivate **videoDevices = NULL;
IMFSourceReader *videoReader = NULL;
WCHAR *webcamFriendlyName;
IMFMediaType *videoSourceOutputType = NULL, *pvideoSourceModType = NULL, *pSrcOutMediaType = NULL;
IMFSourceResolver *pSourceResolver = NULL;
IUnknown* uSource = NULL;
IMFMediaSource *mediaFileSource = NULL;
IMFAttributes *pVideoReaderAttributes = NULL;
IMFMediaType *pVideoOutType = NULL;
MF_OBJECT_TYPE ObjectType = MF_OBJECT_INVALID;
IMFMediaSink *pVideoSink = NULL;
IMFStreamSink *pStreamSink = NULL;
IMFMediaTypeHandler *pMediaTypeHandler = NULL;
IMFMediaType *pMediaType = NULL;
IMFMediaType *pSinkMediaType = NULL;
IMFSinkWriter *pSinkWriter = NULL;
IMFVideoRenderer *pVideoRenderer = NULL;
IMFVideoPresenter *pVideoPresenter = nullptr;
IMFVideoDisplayControl *pVideoDisplayControl = nullptr;
IMFGetService *pService = nullptr;
IMFActivate* pActive = NULL;
MFVideoNormalizedRect nrcDest = { 0.5f, 0.5f, 1.0f, 1.0f };
IMFPresentationTimeSource *pSystemTimeSource = nullptr;
IMFMediaType *sinkPreferredType = nullptr;
IMFPresentationClock *pClock = NULL;
IMFPresentationTimeSource *pTimeSource = NULL;
IDirect3DDeviceManager9 * pD3DManager = NULL;
IMFVideoSampleAllocator* pEvrSampleAllocator = nullptr;
// Add
IMFVideoSampleAllocator* pVideoSampleAllocator = NULL;
IMFSample* pD3DVideoSample = NULL;
RECT rc = { 0, 0, VIDEO_WIDTH, VIDEO_HEIGHT };
CHECK_HR(MFTRegisterLocalByCLSID(
__uuidof(CColorConvertDMO),
MFT_CATEGORY_VIDEO_PROCESSOR,
L"",
MFT_ENUM_FLAG_SYNCMFT,
0,
NULL,
0,
NULL
), "Error registering colour converter DSP.\n");
Task::Factory->StartNew(gcnew Action(InitializeWindow));
Sleep(1000);
if (_hwnd == nullptr)
{
printf("Failed to initialise video window.\n");
goto done;
}
// Create EVR sink .
//CHECK_HR(MFCreateVideoRenderer(__uuidof(IMFMediaSink), (void**)&pVideoSink), "Failed to create video sink.\n");
CHECK_HR(MFCreateVideoRendererActivate(_hwnd, &pActive), "Failed to created video rendered activation context.\n");
CHECK_HR(pActive->ActivateObject(IID_IMFMediaSink, (void**)&pVideoSink), "Failed to activate IMFMediaSink interface on video sink.\n");
// Initialize the renderer before doing anything else including querying for other interfaces (https://msdn.microsoft.com/en-us/library/windows/desktop/ms704667(v=vs.85).aspx).
CHECK_HR(pVideoSink->QueryInterface(__uuidof(IMFVideoRenderer), (void**)&pVideoRenderer), "Failed to get video Renderer interface from EVR media sink.\n");
CHECK_HR(pVideoRenderer->InitializeRenderer(NULL, NULL), "Failed to initialise the video renderer.\n");
CHECK_HR(pVideoSink->QueryInterface(__uuidof(IMFGetService), (void**)&pService), "Failed to get service interface from EVR media sink.\n");
CHECK_HR(pService->GetService(MR_VIDEO_RENDER_SERVICE, __uuidof(IMFVideoDisplayControl), (void**)&pVideoDisplayControl), "Failed to get video display control interface from service interface.\n");
CHECK_HR(pVideoDisplayControl->SetVideoWindow(_hwnd), "Failed to SetVideoWindow.\n");
CHECK_HR(pVideoDisplayControl->SetVideoPosition(NULL, &rc), "Failed to SetVideoPosition.\n");
CHECK_HR(MFGetService(pVideoSink, MR_VIDEO_ACCELERATION_SERVICE, IID_PPV_ARGS(&pD3DManager)), "Failed to get Direct3D manager from EVR media sink.\n");
//CHECK_HR(MFGetService(pVideoSink, MR_VIDEO_ACCELERATION_SERVICE, IID_PPV_ARGS(&pEvrSampleAllocator)), "Failed to get sample allocator from EVR media sink.\n");
//CHECK_HR(pService->GetService(MR_VIDEO_ACCELERATION_SERVICE, __uuidof(IMFVideoSampleAllocator), (void**)pEvrSampleAllocator), "Failed to get sample allocator from EVR media sink.\n");
// Set up the reader for the file.
CHECK_HR(MFCreateSourceResolver(&pSourceResolver), "MFCreateSourceResolver failed.\n");
CHECK_HR(pSourceResolver->CreateObjectFromURL(
L"big_buck_bunny_240p_5mb.mp4", // URL of the source.
MF_RESOLUTION_MEDIASOURCE, // Create a source object.
NULL, // Optional property store.
&ObjectType, // Receives the created object type.
&uSource // Receives a pointer to the media source.
), "Failed to create media source resolver for file.\n");
CHECK_HR(uSource->QueryInterface(IID_PPV_ARGS(&mediaFileSource)), "Failed to create media file source.\n");
CHECK_HR(MFCreateAttributes(&pVideoReaderAttributes, 2), "Failed to create attributes object for video reader.\n");
//CHECK_HR(pVideoReaderAttributes->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID), "Failed to set dev source attribute type for reader config.\n");
CHECK_HR(pVideoReaderAttributes->SetUINT32(MF_SOURCE_READER_ENABLE_VIDEO_PROCESSING, 1), "Failed to set enable video processing attribute type for reader config.\n");
//CHECK_HR(pVideoReaderAttributes->SetUINT32(MF_SOURCE_READER_ENABLE_ADVANCED_VIDEO_PROCESSING, 1), "Failed to set enable advanced video processing attribute type for reader config.\n");
//CHECK_HR(pVideoReaderAttributes->SetUnknown(MF_SOURCE_READER_D3D_MANAGER, pD3DManager), "Failed to set D3D manager attribute type for reader config.\n");
CHECK_HR(MFCreateSourceReaderFromMediaSource(mediaFileSource, pVideoReaderAttributes, &videoReader),
"Error creating media source reader.\n");
CHECK_HR(videoReader->GetCurrentMediaType((DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, &videoSourceOutputType),
"Error retrieving current media type from first video stream.\n");
Console::WriteLine("Default output media type for source reader:");
Console::WriteLine(GetMediaTypeDescription(videoSourceOutputType));
Console::WriteLine();
// Set the video output type on the source reader.
CHECK_HR(MFCreateMediaType(&pvideoSourceModType), "Failed to create video output media type.\n");
CHECK_HR(pvideoSourceModType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), "Failed to set video output media major type.\n");
CHECK_HR(pvideoSourceModType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32), "Failed to set video sub-type attribute on EVR input media type.\n");
CHECK_HR(pvideoSourceModType->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive), "Failed to set interlace mode attribute on EVR input media type.\n");
CHECK_HR(pvideoSourceModType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE), "Failed to set independent samples attribute on EVR input media type.\n");
CHECK_HR(MFSetAttributeRatio(pvideoSourceModType, MF_MT_PIXEL_ASPECT_RATIO, 1, 1), "Failed to set pixel aspect ratio attribute on EVR input media type.\n");
CHECK_HR(CopyAttribute(videoSourceOutputType, pvideoSourceModType, MF_MT_FRAME_SIZE), "Failed to copy video frame size attribute from input file to output sink.\n");
CHECK_HR(CopyAttribute(videoSourceOutputType, pvideoSourceModType, MF_MT_FRAME_RATE), "Failed to copy video frame rate attribute from input file to output sink.\n");
//CHECK_HR(pvideoSourceModType->SetUnknown(MF_SOURCE_READER_D3D_MANAGER, pD3DManager), "Failed to set D3D manager attribute type on EVR input media type.\n");
CHECK_HR(videoReader->SetCurrentMediaType((DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, NULL, pvideoSourceModType), "Failed to set media type on source reader.\n");
Console::WriteLine("Output media type set on source reader:");
Console::WriteLine(GetMediaTypeDescription(pvideoSourceModType));
Console::WriteLine();
CHECK_HR(pVideoSink->GetStreamSinkByIndex(0, &pStreamSink), "Failed to get video renderer stream by index.\n");
CHECK_HR(pStreamSink->GetMediaTypeHandler(&pMediaTypeHandler), "Failed to get media type handler.\n");
// Set the video output type on the source reader.
CHECK_HR(MFCreateMediaType(&pVideoOutType), "Failed to create video output media type.\n");
CHECK_HR(pVideoOutType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), "Failed to set video output media major type.\n");
CHECK_HR(pVideoOutType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32), "Failed to set video sub-type attribute on EVR input media type.\n");
CHECK_HR(pVideoOutType->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive), "Failed to set interlace mode attribute on EVR input media type.\n");
CHECK_HR(pVideoOutType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE), "Failed to set independent samples attribute on EVR input media type.\n");
CHECK_HR(MFSetAttributeRatio(pVideoOutType, MF_MT_PIXEL_ASPECT_RATIO, 1, 1), "Failed to set pixel aspect ratio attribute on EVR input media type.\n");
CHECK_HR(CopyAttribute(videoSourceOutputType, pVideoOutType, MF_MT_FRAME_SIZE), "Failed to copy video frame size attribute from input file to output sink.\n");
CHECK_HR(CopyAttribute(videoSourceOutputType, pVideoOutType, MF_MT_FRAME_RATE), "Failed to copy video frame rate attribute from input file to output sink.\n");
//CHECK_HR(pVideoOutType->SetUnknown(MF_SOURCE_READER_D3D_MANAGER, pD3DManager), "Failed to set D3D manager attribute on EVR input media type.\n");
//CHECK_HR(pMediaTypeHandler->GetMediaTypeByIndex(0, &pSinkMediaType), "Failed to get sink media type.\n");
CHECK_HR(pMediaTypeHandler->SetCurrentMediaType(pVideoOutType), "Failed to set current media type.\n");
Console::WriteLine("Input media type set on EVR:");
Console::WriteLine(GetMediaTypeDescription(pVideoOutType));
Console::WriteLine();
// https://msdn.microsoft.com/fr-fr/library/windows/desktop/aa473823(v=vs.85).aspx
CHECK_HR(MFGetService(pStreamSink, MR_VIDEO_ACCELERATION_SERVICE, IID_PPV_ARGS(&pVideoSampleAllocator)), "Failed to get IMFVideoSampleAllocator.\n");
CHECK_HR(MFGetService(pVideoSink, MR_VIDEO_ACCELERATION_SERVICE, IID_PPV_ARGS(&pD3DManager)), "Failed to get D3DManager.\n");
CHECK_HR(pVideoSampleAllocator->SetDirectXManager(pD3DManager), "Failed to set D3DManager.\n");
CHECK_HR(pVideoSampleAllocator->InitializeSampleAllocator(1, pVideoOutType), "Failed to InitializeSampleAllocator.\n");
CHECK_HR(pVideoSampleAllocator->AllocateSample(&pD3DVideoSample), "Failed to AllocateSample.\n");
CHECK_HR(MFCreatePresentationClock(&pClock), "Failed to create presentation clock.\n");
CHECK_HR(MFCreateSystemTimeSource(&pTimeSource), "Failed to create system time source.\n");
CHECK_HR(pClock->SetTimeSource(pTimeSource), "Failed to set time source.\n");
CHECK_HR(pVideoSink->SetPresentationClock(pClock), "Failed to set presentation clock on video sink.\n");
CHECK_HR(pClock->Start(0), "Error starting presentation clock.\n");
// Wait for the Sink to start
Sleep(1000);
//_d3d = Direct3DCreate9(D3D_SDK_VERSION); // create the Direct3D interface
//D3DPRESENT_PARAMETERS d3dpp; // create a struct to hold various device information
//ZeroMemory(&d3dpp, sizeof(d3dpp)); // clear out the struct for use
//d3dpp.Windowed = TRUE; // program windowed, not fullscreen
//d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; // discard old frames
//d3dpp.hDeviceWindow = _hwnd; // set the window to be used by Direct3D
//d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8; // set the back buffer format to 32-bit
//d3dpp.BackBufferWidth = 640; // set the width of the buffer
//d3dpp.BackBufferHeight = 360; // set the height of the buffer
//// create a device class using this information and information from the d3dpp stuct
//_d3d->CreateDevice(D3DADAPTER_DEFAULT,
// D3DDEVTYPE_HAL,
// _hwnd,
// D3DCREATE_SOFTWARE_VERTEXPROCESSING,
// &d3dpp,
// &_d3ddev);
//CHECK_HR(_d3ddev->GetSwapChain(0, &_pSwapChain), "Failed to get swap chain from D3D device.\n");
////_d3ddev->CreateTexture(640, 360, 0, 0, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &_pd3dTexture, NULL);
//// clear the window to a deep blue
//_d3ddev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 40, 100), 1.0f, 0);
//_d3ddev->BeginScene(); // begins the 3D scene
//_d3ddev->EndScene(); // ends the 3D scene
//_d3ddev->Present(NULL, NULL, NULL, NULL); // displays the created frame
Console::WriteLine("Press any key to start video sampling...");
//Console::ReadLine();
IMFSample *videoSample = NULL;
DWORD streamIndex, flags;
LONGLONG llTimeStamp;
bool clockStarted = false;
IMFSample *d3dSample = nullptr;
LONGLONG llVideoTimeStamp = 0;
UINT32 uiAttribute = 0;
IMFMediaBuffer* pSrcBuffer = NULL;
IMFMediaBuffer* pDstBuffer = NULL;
IMF2DBuffer* p2DBuffer = NULL;
BYTE* pbBuffer = NULL;
DWORD dwBuffer = 0;
while (true)
{
CHECK_HR(videoReader->ReadSample(
MF_SOURCE_READER_FIRST_VIDEO_STREAM,
0, // Flags.
&streamIndex, // Receives the actual stream index.
&flags, // Receives status flags.
&llTimeStamp, // Receives the time stamp.
&videoSample // Receives the sample or NULL.
), "Error reading video sample.");
if (flags & MF_SOURCE_READERF_ENDOFSTREAM)
{
printf("End of stream.\n");
break;
}
if (flags & MF_SOURCE_READERF_STREAMTICK)
{
printf("Stream tick.\n");
}
if (!videoSample)
{
printf("Null video sample.\n");
}
else
{
/*if (!clockStarted)
{
clockStarted = true;
CHECK_HR(pClock->Start(llTimeStamp), "Error starting the presentation clock.\n");
}*/
printf("Attempting to write sample to stream sink.\n");
//CHECK_HR(videoSample->SetSampleTime(llTimeStamp), "Error setting the video sample time.\n");
//CHECK_HR(videoSample->SetSampleDuration(41000000), "Error setting the video sample duration.\n");
/*CHECK_HR(CreateD3DSample(_pSwapChain, &d3dSample), "Failed to create 3D sample.\n");
CHECK_HR(d3dSample->SetSampleTime(llTimeStamp), "Error setting the 3D sample time.\n");*/
//CHECK_HR(pStreamSink->ProcessSample(videoSample), "Streamsink process sample failed.\n");
//CHECK_HR(pStreamSink->ProcessSample(d3dSample), "Streamsink process sample failed.\n");
CHECK_HR(videoSample->GetSampleTime(&llVideoTimeStamp), ".\n");
CHECK_HR(pD3DVideoSample->SetSampleTime(llVideoTimeStamp), ".\n");
CHECK_HR(videoSample->GetSampleDuration(&llVideoTimeStamp), ".\n");
CHECK_HR(pD3DVideoSample->SetSampleDuration(llVideoTimeStamp), ".\n");
CHECK_HR(videoSample->ConvertToContiguousBuffer(&pSrcBuffer), ".\n");
CHECK_HR(pSrcBuffer->Lock(&pbBuffer, NULL, &dwBuffer), ".\n");
CHECK_HR(pD3DVideoSample->GetBufferByIndex(0, &pDstBuffer), ".\n");
CHECK_HR(pDstBuffer->QueryInterface(IID_PPV_ARGS(&p2DBuffer)), ".\n");
CHECK_HR(p2DBuffer->ContiguousCopyFrom(pbBuffer, dwBuffer), ".\n");
CHECK_HR(pSrcBuffer->Unlock(), ".\n");
CHECK_HR(videoSample->GetUINT32(MFSampleExtension_FrameCorruption, &uiAttribute), ".\n");
CHECK_HR(pD3DVideoSample->SetUINT32(MFSampleExtension_FrameCorruption, uiAttribute), ".\n");
CHECK_HR(videoSample->GetUINT32(MFSampleExtension_Discontinuity, &uiAttribute), ".\n");
CHECK_HR(pD3DVideoSample->SetUINT32(MFSampleExtension_Discontinuity, uiAttribute), ".\n");
CHECK_HR(videoSample->GetUINT32(MFSampleExtension_CleanPoint, &uiAttribute), ".\n");
CHECK_HR(pD3DVideoSample->SetUINT32(MFSampleExtension_CleanPoint, uiAttribute), ".\n");
CHECK_HR(videoSample->GetUINT32(MFSampleExtension_CleanPoint, &uiAttribute), ".\n");
CHECK_HR(pD3DVideoSample->SetUINT32(MFSampleExtension_CleanPoint, uiAttribute), ".\n");
CHECK_HR(pStreamSink->ProcessSample(pD3DVideoSample), "Streamsink process sample failed.\n");
Sleep(75);
}
SafeRelease(&p2DBuffer);
SafeRelease(&pDstBuffer);
SafeRelease(&pSrcBuffer);
SafeRelease(&videoSample);
}
done:
// Todo : stop clock - shutdown object - release object
SafeRelease(_d3ddev); // close and release the 3D device
SafeRelease(_d3d); // close and release Direct3D
printf("finished.\n");
getchar();
return 0;
}
HRESULT CreateD3DSample(
IDirect3DSwapChain9 *pSwapChain,
IMFSample **ppVideoSample
)
{
// Caller holds the object lock.
D3DCOLOR clrBlack = D3DCOLOR_ARGB(0xFF, 0x00, 0x00, 0x00);
IDirect3DSurface9* pSurface = NULL;
IMFSample* pSample = NULL;
// Get the back buffer surface.
HRESULT hr = pSwapChain->GetBackBuffer(
0, D3DBACKBUFFER_TYPE_MONO, &pSurface);
if (FAILED(hr))
{
goto done;
}
// Fill it with black.
hr = _d3ddev->ColorFill(pSurface, NULL, clrBlack);
if (FAILED(hr))
{
goto done;
}
// Create the sample.
hr = MFCreateVideoSampleFromSurface(pSurface, &pSample);
if (FAILED(hr))
{
goto done;
}
// Return the pointer to the caller.
*ppVideoSample = pSample;
(*ppVideoSample)->AddRef();
done:
SafeRelease(&pSurface);
SafeRelease(&pSample);
return hr;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
void InitializeWindow()
{
WNDCLASS wc = { 0 };
wc.lpfnWndProc = WindowProc;
wc.hInstance = GetModuleHandle(NULL);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.lpszClassName = CLASS_NAME;
if (RegisterClass(&wc))
{
_hwnd = CreateWindow(
CLASS_NAME,
WINDOW_NAME,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
VIDEO_WIDTH,
VIDEO_HEIGHT,
NULL,
NULL,
GetModuleHandle(NULL),
NULL
);
if (_hwnd)
{
ShowWindow(_hwnd, SW_SHOWDEFAULT);
MSG msg = { 0 };
while (true)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
Sleep(1);
}
}
}
}
}
Happened across a post on the MSDN forums which makes mention of the fact that the EVR requires a D3D surface sample rather than a standard IMFSample. I did suspect the problem might be something like that. I don't know exactly how to supply the D3D sample to the EVR and I have already had a few goes at doing so but at least I now know which direction to follow.
I'm using the Quadro SDI SDK along with my Quadro K6000+ SDI Input & output cards and have converted the included cudaDVP SDK sample to send raw image buffers directly to the GPU from the SDI input card.
In the next step I display the data via opengl bindings. Finally I want to output the same identical data to my output card, and this is where I'm having troubles.
I am getting correct input data and I do manage to output to the screen but there appears to be some data modifications happening in the SDI output pipeline as the outgoing image is not quite correct (wrong colors etc). I'm passing the raw input buffer as can be seen below.
Which output card configuration should I use to match my input settings (see below)?
Which if any modifications to the OpenGL output texture configuration are required (see below)?
Input/output Capture/receive options & GL bindings in order of being called in the application:
........
Display *dpy = XOpenDisplay(NULL);
//scan the systems for GPUs
int num_gpus = ScanHW(dpy,gpuList);
if(num_gpus < 1)
exit(1);
//grab the first GPU for now for DVP
HGPUNV *g = &gpuList[0];
////////////////////////////////////////////////////////////////////////////////////////////////
/// Input related SDI settings and OpenGL settings
// Query input , in our case Video format: NV_CTRL_GVIO_VIDEO_FORMAT_576I_50_00_SMPTE259_PAL
XNVCTRLQueryTargetAttribute(dpy, NV_CTRL_TARGET_TYPE_GVI, 0, 0, NV_CTRL_GVIO_DETECTED_VIDEO_FORMAT, &m_videoFormat);
//
// Set desired parameters
//
// Signal format.
XNVCTRLSetTargetAttribute(dpy, NV_CTRL_TARGET_TYPE_GVI, 0, 0, NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT, m_videoFormat);
//Bits per component - 10 bits
XNVCTRLSetTargetAttribute(dpy, NV_CTRL_TARGET_TYPE_GVI, 0, 0, NV_CTRL_GVI_REQUESTED_STREAM_BITS_PER_COMPONENT, NV_CTRL_GVI_BITS_PER_COMPONENT_10);
// Component sampling -422
XNVCTRLSetTargetAttribute(dpy, NV_CTRL_TARGET_TYPE_GVI, 0, 0, NV_CTRL_GVI_REQUESTED_STREAM_COMPONENT_SAMPLING, NV_CTRL_GVI_COMPONENT_SAMPLING_422);
// Chroma expansion OFF
XNVCTRLSetTargetAttribute(dpy, NV_CTRL_TARGET_TYPE_GVI, 0, 0, NV_CTRL_GVI_REQUESTED_STREAM_CHROMA_EXPAND, NV_CTRL_GVI_CHROMA_EXPAND_FALSE);
// Query the width and height of the input signal format
XNVCTRLQueryAttribute(dpy, g->deviceXScreen, value, NV_CTRL_GVIO_VIDEO_FORMAT_WIDTH, &m_videoWidth);
XNVCTRLQueryAttribute(dpy, g->deviceXScreen, value, NV_CTRL_GVIO_VIDEO_FORMAT_HEIGHT, &m_videoHeight);
....
GLuint m_videoSlot; // Video slot number
GLuint m_vidBuf; // Video capture buffers
GLint m_bufPitch; // Buffer pitch
GLuint m_vidTex; // Video capture textures
m_videoSlot = 1;
//////////////////////////////////////
////////// OpenGL related settings
// No video color conversion desired ( we want the raw data, NULL )
glVideoCaptureStreamParameterfvNV(m_videoSlot, 0, GL_VIDEO_COLOR_CONVERSION_MATRIX_NV, NULL);
glVideoCaptureStreamParameterfvNV(m_videoSlot, 0,GL_VIDEO_COLOR_CONVERSION_MAX_NV, NULL);
glVideoCaptureStreamParameterfvNV(m_videoSlot, 0,GL_VIDEO_COLOR_CONVERSION_MIN_NV, NULL);
glVideoCaptureStreamParameterfvNV(m_videoSlot, 0,GL_VIDEO_COLOR_CONVERSION_OFFSET_NV, NULL);
// Set the buffer object capture data format. - IE number of components in a pixel
glVideoCaptureStreamParameterivNV(m_videoSlot, 0, GL_VIDEO_BUFFER_INTERNAL_FORMAT_NV, &GL_Z6Y10Z6CB10Z6Y10Z6CR10_422_NV);
// Get the video buffer pitch
glGetVideoCaptureStreamivNV(m_videoSlot, 0, GL_VIDEO_BUFFER_PITCH_NV, &m_bufPitch);
// Bind the buffer
glBindBufferARB(GL_VIDEO_BUFFER_NV, m_vidBuf);
// Allocate required space in video capture buffer
glBufferDataARB(GL_VIDEO_BUFFER_NV, m_bufPitch * m_videoHeight, NULL, GL_STREAM_READ_ARB);
// Bind the buffer to the video capture device.
glBindVideoCaptureStreamBufferNV(m_videoSlot, 0, GL_FRAME_NV, 0);
////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
/// SDI Output card settings
GLuint m_CudaOutTexture; // Texture to send to the output device
GLuint m_CudaOutBuffer; // Texture to send to the output device
GLuint m_OutTexture;
// Set video format - same as input - NV_CTRL_GVIO_VIDEO_FORMAT_576I_50_00_SMPTE259_PAL
XNVCTRLSetAttribute(dpy, m_outputOptions.xscreen, 0, NV_CTRL_GVO_OUTPUT_VIDEO_FORMAT, m_videoFormat);
// Set data format format.
// Attempted several different settings here....
data_format = NV_CTRL_GVO_DATA_FORMAT_R8G8B8_TO_YCRCB422;
//data_format = NV_CTRL_GVO_DATA_FORMAT_X10X10X10_422_PASSTHRU;
//data_format = NV_CTRL_GVO_DATA_FORMAT_X8X8X8_422_PASSTHRU;
//data_format = NV_CTRL_GVO_DATA_FORMAT_R10G10B10_TO_YCRCB422;
//data_format = NV_CTRL_GVO_DATA_FORMAT_X12X12X12_422_PASSTHRU;
//data_format = NV_CTRL_GVO_DATA_FORMAT_Y10CR10CB10_TO_YCRCB444;
//data_format = NV_CTRL_GVO_DATA_FORMAT_X10X8X8_422_PASSTHRU;
//data_format = NV_CTRL_GVO_ENABLE_RGB_DATA_DISABLE
XNVCTRLSetAttribute(dpy, m_outputOptions.xscreen, 0, NV_CTRL_GVO_DATA_FORMAT, data_format);
// Set sync mode
XNVCTRLSetAttribute(dpy, m_outputOptions.xscreen, 0, NV_CTRL_GVO_SYNC_MODE, NV_CTRL_GVO_SYNC_MODE_FREE_RUNNING);
// Set sync source
XNVCTRLSetAttribute(dpy, m_outputOptions.xscreen, 0, NV_CTRL_GVO_SYNC_SOURCE, NV_CTRL_GVO_SYNC_SOURCE_SDI);
// Set flip queue length
XNVCTRLSetAttribute(dpy, m_outputOptions.xscreen, 0, NV_CTRL_GVO_FLIP_QUEUE_SIZE, 5);
.....
///////////////////////////////////////////////////////////////////
// OpenGL related settings for output
//Setup the output after the capture is configured.
glGenTextures(1, &m_OutTexture);
glBindTexture(GL_TEXTURE_RECTANGLE_NV, m_OutTexture);
glTexParameterf(GL_TEXTURE_RECTANGLE_NV, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_RECTANGLE_NV, 0, GL_RGBA8, WIDTH, HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0 );
////////////////////////////////////////////////////////
//Setup GL output from cuda
// Create and initialize a texture objects.
glGenBuffersARB(1, &m_CudaOutBuffer);
assert(glGetError() == GL_NO_ERROR);
glBindBufferARB(GL_VIDEO_BUFFER_NV, m_CudaOutBuffer);
assert(glGetError() == GL_NO_ERROR);
// Allocate required space in video capture buffer
glBufferDataARB(GL_VIDEO_BUFFER_NV, pitch * height, NULL, GL_STREAM_COPY);
glGenTextures(1, &m_CudaOutTexture);
glBindTexture(GL_TEXTURE_RECTANGLE_NV, m_CudaOutTexture);
glTexParameterf(GL_TEXTURE_RECTANGLE_NV, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
assert(glGetError() == GL_NO_ERROR);
glTexImage2D(GL_TEXTURE_RECTANGLE_NV, 0, GL_RGBA8, width,height,
glBindTexture(GL_TEXTURE_RECTANGLE_NV, 0);
//register the buffer objects
cudaRegisterBuffers();
....
////////////////////////////////////////////////////////////////
/////////// Data transfer from GPU to output device buffer (to be outputted to the SDI output card)
GLint inBuf = m_vidBuf;
// Map buffer(s) into CUDA
cudaError_t cerr;
unsigned char *inDevicePtr;
cerr = cudaGLMapBufferObject((void**)&inDevicePtr, inBuf);
cudaCheckErrors("map");
unsigned char *outDevicePtr;
cerr = cudaGLMapBufferObject((void**)&outDevicePtr, m_CudaOutBuffer);
cudaCheckErrors("map");
//
// Dummy CUDA operation:
// Perform CUDA Operations here such as a kernel call with outDevicePtr and inDevicePtr.
//
unsigned int pitch = m_SDIin.getBufferObjectPitch(0);
unsigned int height = m_SDIin.getHeight();
cudaMemcpy(outDevicePtr,inDevicePtr,pitch*height,cudaMemcpyDeviceToDevice);
////////////////////////////////////////////////////////
/////// output
GLboolean C_cudaDVP::OutputVideo()
{
if(!m_SDIoutEnabled)
return GL_FALSE;
//send the texture to SDI out.
glBindTexture(GL_TEXTURE_RECTANGLE_NV, m_OutTexture);
glEnable(GL_TEXTURE_RECTANGLE_NV);
glPresentFrameKeyedNV(1, 0,
0, 0,
GL_FRAME_NV,
GL_TEXTURE_RECTANGLE_NV, m_OutTexture, 0,
GL_NONE, 0, 0);
GLenum l_eVal = glGetError();
glBindTexture(GL_TEXTURE_RECTANGLE_NV, 0);
if (l_eVal != GL_NO_ERROR) {
fprintf(stderr, "glPresentFameKeyedNV returned error: %s\n", gluErrorString(l_eVal));
return FALSE;
}
return GL_TRUE;
}
.....
// After which we call:
// To skip a costly data copy from video buffer to texture we
// can just bind a video buffer to GL_PIXEL_UNPACK_BUFFER_ARB and call
// glTexSubImage2D referencing into the buffer with the PixelData pointer
// set to 0.
glBindTexture(GL_TEXTURE_RECTANGLE_NV, m_CudaOutTexture);
//glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, m_SDIin.getBufferObjectHandle(0));
glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, m_CudaOutBuffer);
glPixelStorei(GL_UNPACK_ROW_LENGTH,pitch/4);
glTexSubImage2D(GL_TEXTURE_RECTANGLE_NV, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, 0);
glBindTexture(GL_TEXTURE_RECTANGLE_NV, 0);
glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0);
//////////////////////////////////////////////////////