Blame | Last modification | View Log | RSS feed
//-----------------------------------------------------------------------------// File: DShowTextures.cpp//// Desc: DirectShow sample code - adds support for DirectShow videos playing// on a DirectX 9.0 texture surface. Turns the D3D texture tutorial into// a recreation of the VideoTex sample from previous versions of DirectX.//// Copyright (c) Microsoft Corporation. All rights reserved.//-----------------------------------------------------------------------------#include "Textures.h"#include "DShowTextures.h"#include "DXUtil.h"//-----------------------------------------------------------------------------// Global Constants//-----------------------------------------------------------------------------// Define this if you want to render only the video component with no audio//// #define NO_AUDIO_RENDERER// An application can advertise the existence of its filter graph// by registering the graph with a global Running Object Table (ROT).// The GraphEdit application can detect and remotely view the running// filter graph, allowing you to 'spy' on the graph with GraphEdit.//// To enable registration in this sample, define REGISTER_FILTERGRAPH.//#define REGISTER_FILTERGRAPHLPDIRECT3DDEVICE9 g_pd3dDevice; // Our rendering deviceLPDIRECT3DTEXTURE9 g_pTexture; // Our texture// File filter for OpenFile dialog#define FILE_FILTER_TEXT \TEXT("Video Files (*.avi; *.qt; *.mov; *.mpg; *.mpeg; *.m1v)\0*.avi; *.qt; *.mov; *.mpg; *.mpeg; *.m1v\0")\TEXT("Audio files (*.wav; *.mpa; *.mp2; *.mp3; *.au; *.aif; *.aiff; *.snd)\0*.wav; *.mpa; *.mp2; *.mp3; *.au; *.aif; *.aiff; *.snd\0")\TEXT("MIDI Files (*.mid, *.midi, *.rmi)\0*.mid; *.midi; *.rmi\0") \TEXT("Image Files (*.jpg, *.bmp, *.gif, *.tga)\0*.jpg; *.bmp; *.gif; *.tga\0") \TEXT("All Files (*.*)\0*.*;\0\0")//-----------------------------------------------------------------------------// Global DirectShow pointers//-----------------------------------------------------------------------------CComPtr<IGraphBuilder> g_pGB; // GraphBuilderCComPtr<IMediaControl> g_pMC; // Media ControlCComPtr<IMediaPosition> g_pMP; // Media PositionCComPtr<IMediaEvent> g_pME; // Media EventCComPtr<IBaseFilter> g_pRenderer; // our custom rendererD3DFORMAT g_TextureFormat; // Texture format//extern HWND g_hWnd;TCHAR g_achCopy[] = TEXT("Bitwise copy of the sample");TCHAR g_achOffscr[] = TEXT("Using offscreen surfaces and StretchCopy()");TCHAR g_achDynTextr[] = TEXT("Using Dynamic Textures");TCHAR* g_pachRenderMethod = NULL;//-----------------------------------------------------------------------------// InitDShowTextureRenderer : Create DirectShow filter graph and run the graph//-----------------------------------------------------------------------------HRESULT InitDShowTextureRenderer( const char *szVideoFilename, LPDIRECT3DTEXTURE9 texture, LPDIRECT3DDEVICE9 device ){g_pTexture = texture;g_pd3dDevice = device;HRESULT hr = S_OK;CComPtr<IBaseFilter> pFSrc; // Source FilterCComPtr<IPin> pFSrcPinOut; // Source Filter Output PinCTextureRenderer *pCTR=0; // DirectShow Texture renderer// Create the filter graphif (FAILED(g_pGB.CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC)))return E_FAIL;#ifdef REGISTER_FILTERGRAPH// Register the graph in the Running Object Table (for debug purposes)AddToROT(g_pGB);#endif// Create the Texture Renderer objectpCTR = new CTextureRenderer(NULL, &hr);if (FAILED(hr) || !pCTR){delete pCTR;Msg(TEXT("Could not create texture renderer object! hr=0x%x"), hr);return E_FAIL;}// Get a pointer to the IBaseFilter on the TextureRenderer, add it to graphg_pRenderer = pCTR;if (FAILED(hr = g_pGB->AddFilter(g_pRenderer, L"TEXTURERENDERER"))){Msg(TEXT("Could not add renderer filter to graph! hr=0x%x"), hr);return hr;}// Determine the file to load based on windows directory// Use the standard win32 API to do this.TCHAR strFileName[MAX_PATH] = {0};WCHAR wFileName[MAX_PATH];lstrcpyn( strFileName, szVideoFilename, MAX_PATH - 1 );/*if (! GetClipFileName(strFileName)){DWORD dwDlgErr = CommDlgExtendedError();// Don't show output if user cancelled the selection (no dlg error)if (dwDlgErr){Msg(TEXT("GetClipFileName Failed! Error=0x%x\r\n"), GetLastError());}return E_FAIL;}*/strFileName[MAX_PATH-1] = 0; // NULL-terminatewFileName[MAX_PATH-1] = 0; // NULL-terminateUSES_CONVERSION;(void)StringCchCopyW(wFileName, NUMELMS(wFileName), T2W(strFileName));return hr;// Add the source filter to the graph.hr = g_pGB->AddSourceFilter (wFileName, L"SOURCE", &pFSrc);// If the media file was not found, inform the user.if (hr == VFW_E_NOT_FOUND){Msg(TEXT("Could not add source filter to graph! (hr==VFW_E_NOT_FOUND)\r\n\r\n")TEXT("This sample reads a media file from your windows directory.\r\n")TEXT("This file is missing from this machine."));return hr;}else if(FAILED(hr)){Msg(TEXT("Could not add source filter to graph! hr=0x%x"), hr);return hr;}if (FAILED(hr = pFSrc->FindPin(L"Output", &pFSrcPinOut))){Msg(TEXT("Could not find output pin! hr=0x%x"), hr);return hr;}// Render the source filter's output pin. The Filter Graph Manager// will connect the video stream to the loaded CTextureRenderer// and will load and connect an audio renderer (if needed)./*if (FAILED(hr = g_pGB->Render(pFSrcPinOut))){Msg(TEXT("Could not render source output pin! hr=0x%x"), hr);return hr;}*/// Get the graph's media control, event & position interfacesg_pGB.QueryInterface(&g_pMC);g_pGB.QueryInterface(&g_pMP);g_pGB.QueryInterface(&g_pME);/*// Start the graph running;if (FAILED(hr = g_pMC->Run())){Msg(TEXT("Could not run the DirectShow graph! hr=0x%x"), hr);return hr;}*/return S_OK;}//-----------------------------------------------------------------------------// CheckMovieStatus: If the movie has ended, rewind to beginning//-----------------------------------------------------------------------------void CheckMovieStatus(void){long lEventCode;LONG_PTR lParam1, lParam2;HRESULT hr;if (!g_pME)return;// Check for completion eventshr = g_pME->GetEvent(&lEventCode, &lParam1, &lParam2, 0);if (SUCCEEDED(hr)){// If we have reached the end of the media file, reset to beginningif (EC_COMPLETE == lEventCode){hr = g_pMP->put_CurrentPosition(0);}// Free any memory associated with this eventhr = g_pME->FreeEventParams(lEventCode, lParam1, lParam2);}}//-----------------------------------------------------------------------------// CleanupDShow//-----------------------------------------------------------------------------void CleanupDShow(void){#ifdef REGISTER_FILTERGRAPH// Pull graph from Running Object Table (Debug)RemoveFromROT();#endif// Shut down the graphif (!(!g_pMC)) g_pMC->Stop();if (!(!g_pMC)) g_pMC.Release();if (!(!g_pME)) g_pME.Release();if (!(!g_pMP)) g_pMP.Release();if (!(!g_pGB)) g_pGB.Release();if (!(!g_pRenderer)) g_pRenderer.Release();}//-----------------------------------------------------------------------------// CTextureRenderer constructor//-----------------------------------------------------------------------------CTextureRenderer::CTextureRenderer( LPUNKNOWN pUnk, HRESULT *phr ): CBaseVideoRenderer(__uuidof(CLSID_TextureRenderer),NAME("Texture Renderer"), pUnk, phr),m_bUseDynamicTextures(FALSE){// Store and AddRef the texture for our use.ASSERT(phr);if (phr)*phr = S_OK;}//-----------------------------------------------------------------------------// CTextureRenderer destructor//-----------------------------------------------------------------------------CTextureRenderer::~CTextureRenderer(){// Do nothing}//-----------------------------------------------------------------------------// CheckMediaType: This method forces the graph to give us an R8G8B8 video// type, making our copy to texture memory trivial.//-----------------------------------------------------------------------------HRESULT CTextureRenderer::CheckMediaType(const CMediaType *pmt){HRESULT hr = E_FAIL;VIDEOINFO *pvi=0;CheckPointer(pmt,E_POINTER);// Reject the connection if this is not a video typeif( *pmt->FormatType() != FORMAT_VideoInfo ) {return E_INVALIDARG;}// Only accept RGB24 videopvi = (VIDEOINFO *)pmt->Format();if(IsEqualGUID( *pmt->Type(), MEDIATYPE_Video) &&IsEqualGUID( *pmt->Subtype(), MEDIASUBTYPE_RGB24)){hr = S_OK;}return hr;}//-----------------------------------------------------------------------------// SetMediaType: Graph connection has been made.//-----------------------------------------------------------------------------HRESULT CTextureRenderer::SetMediaType(const CMediaType *pmt){HRESULT hr;UINT uintWidth = 2;UINT uintHeight = 2;// Retrive the size of this media typeD3DCAPS9 caps;VIDEOINFO *pviBmp; // Bitmap info headerpviBmp = (VIDEOINFO *)pmt->Format();m_lVidWidth = pviBmp->bmiHeader.biWidth;m_lVidHeight = abs(pviBmp->bmiHeader.biHeight);m_lVidPitch = (m_lVidWidth * 3 + 3) & ~(3); // We are forcing RGB24// here let's check if we can use dynamic texturesZeroMemory( &caps, sizeof(D3DCAPS9));hr = g_pd3dDevice->GetDeviceCaps( &caps );if( caps.Caps2 & D3DCAPS2_DYNAMICTEXTURES ){m_bUseDynamicTextures = TRUE;}if( caps.TextureCaps & D3DPTEXTURECAPS_POW2 ){while( (LONG)uintWidth < m_lVidWidth ){uintWidth = uintWidth << 1;}while( (LONG)uintHeight < m_lVidHeight ){uintHeight = uintHeight << 1;}}else{uintWidth = m_lVidWidth;uintHeight = m_lVidHeight;}// Create the texture that maps to this media typehr = E_UNEXPECTED;if( m_bUseDynamicTextures ){hr = g_pd3dDevice->CreateTexture(uintWidth, uintHeight, 1, D3DUSAGE_DYNAMIC,D3DFMT_X8R8G8B8,D3DPOOL_DEFAULT,&g_pTexture, NULL);g_pachRenderMethod = g_achDynTextr;if( FAILED(hr)){m_bUseDynamicTextures = FALSE;}}if( FALSE == m_bUseDynamicTextures ){hr = g_pd3dDevice->CreateTexture(uintWidth, uintHeight, 1, 0,D3DFMT_X8R8G8B8,D3DPOOL_MANAGED,&g_pTexture, NULL);g_pachRenderMethod = g_achCopy;}if( FAILED(hr)){Msg(TEXT("Could not create the D3DX texture! hr=0x%x"), hr);return hr;}// CreateTexture can silently change the parameters on usD3DSURFACE_DESC ddsd;ZeroMemory(&ddsd, sizeof(ddsd));if ( FAILED( hr = g_pTexture->GetLevelDesc( 0, &ddsd ) ) ) {Msg(TEXT("Could not get level Description of D3DX texture! hr = 0x%x"), hr);return hr;}CComPtr<IDirect3DSurface9> pSurf;if (SUCCEEDED(hr = g_pTexture->GetSurfaceLevel(0, &pSurf)))pSurf->GetDesc(&ddsd);// Save format infog_TextureFormat = ddsd.Format;if (g_TextureFormat != D3DFMT_X8R8G8B8 &&g_TextureFormat != D3DFMT_A1R5G5B5) {Msg(TEXT("Texture is format we can't handle! Format = 0x%x"), g_TextureFormat);return VFW_E_TYPE_NOT_ACCEPTED;}return S_OK;}//-----------------------------------------------------------------------------// DoRenderSample: A sample has been delivered. Copy it to the texture.//-----------------------------------------------------------------------------HRESULT CTextureRenderer::DoRenderSample( IMediaSample * pSample ){BYTE *pBmpBuffer, *pTxtBuffer; // Bitmap buffer, texture bufferLONG lTxtPitch; // Pitch of bitmap, textureBYTE * pbS = NULL;DWORD * pdwS = NULL;DWORD * pdwD = NULL;UINT row, col, dwordWidth;CheckPointer(pSample,E_POINTER);CheckPointer(g_pTexture,E_UNEXPECTED);// Get the video bitmap bufferpSample->GetPointer( &pBmpBuffer );// Lock the TextureD3DLOCKED_RECT d3dlr;if( m_bUseDynamicTextures ){if( FAILED(g_pTexture->LockRect(0, &d3dlr, 0, D3DLOCK_DISCARD)))return E_FAIL;}else{if (FAILED(g_pTexture->LockRect(0, &d3dlr, 0, 0)))return E_FAIL;}// Get the texture buffer & pitchpTxtBuffer = static_cast<byte *>(d3dlr.pBits);lTxtPitch = d3dlr.Pitch;// Copy the bitsif (g_TextureFormat == D3DFMT_X8R8G8B8){// Instead of copying data bytewise, we use DWORD alignment here.// We also unroll loop by copying 4 pixels at once.//// original BYTE array is [b0][g0][r0][b1][g1][r1][b2][g2][r2][b3][g3][r3]//// aligned DWORD array is [b1 r0 g0 b0][g2 b2 r1 g1][r3 g3 b3 r2]//// We want to transform it to [ff r0 g0 b0][ff r1 g1 b1][ff r2 g2 b2][ff r3 b3 g3]// below, bitwise operations do exactly this.dwordWidth = m_lVidWidth / 4; // aligned width of the row, in DWORDS// (pixel by 3 bytes over sizeof(DWORD))for( row = 0; row< (UINT)m_lVidHeight; row++){pdwS = ( DWORD*)pBmpBuffer;pdwD = ( DWORD*)pTxtBuffer;for( col = 0; col < dwordWidth; col ++ ){pdwD[0] = pdwS[0] | 0xFF000000;pdwD[1] = ((pdwS[1]<<8) | 0xFF000000) | (pdwS[0]>>24);pdwD[2] = ((pdwS[2]<<16) | 0xFF000000) | (pdwS[1]>>16);pdwD[3] = 0xFF000000 | (pdwS[2]>>8);pdwD +=4;pdwS +=3;}// we might have remaining (misaligned) bytes herepbS = (BYTE*) pdwS;for( col = 0; col < (UINT)m_lVidWidth % 4; col++){*pdwD = 0xFF000000 |(pbS[2] << 16) |(pbS[1] << 8) |(pbS[0]);pdwD++;pbS += 3;}pBmpBuffer += m_lVidPitch;pTxtBuffer += lTxtPitch;}// for rows}if (g_TextureFormat == D3DFMT_A1R5G5B5){for(int y = 0; y < m_lVidHeight; y++ ){BYTE *pBmpBufferOld = pBmpBuffer;BYTE *pTxtBufferOld = pTxtBuffer;for (int x = 0; x < m_lVidWidth; x++){*(WORD *)pTxtBuffer = (WORD)(0x8000 +((pBmpBuffer[2] & 0xF8) << 7) +((pBmpBuffer[1] & 0xF8) << 2) +(pBmpBuffer[0] >> 3));pTxtBuffer += 2;pBmpBuffer += 3;}pBmpBuffer = pBmpBufferOld + m_lVidPitch;pTxtBuffer = pTxtBufferOld + lTxtPitch;}}// Unlock the Textureif (FAILED(g_pTexture->UnlockRect(0)))return E_FAIL;return S_OK;}#ifdef REGISTER_FILTERGRAPH//-----------------------------------------------------------------------------// Running Object Table functions: Used to debug. By registering the graph// in the running object table, GraphEdit is able to connect to the running// graph. This code should be removed before the application is shipped in// order to avoid third parties from spying on your graph.//-----------------------------------------------------------------------------DWORD dwROTReg = 0xfedcba98;HRESULT AddToROT(IUnknown *pUnkGraph){IMoniker * pmk;IRunningObjectTable *pROT;if (FAILED(GetRunningObjectTable(0, &pROT))) {return E_FAIL;}WCHAR wsz[256];(void)StringCchPrintfW(wsz, NUMELMS(wsz),L"FilterGraph %08x pid %08x\0", (DWORD_PTR) 0, GetCurrentProcessId());HRESULT hr = CreateItemMoniker(L"!", wsz, &pmk);if (SUCCEEDED(hr)){// Use the ROTFLAGS_REGISTRATIONKEEPSALIVE to ensure a strong reference// to the object. Using this flag will cause the object to remain// registered until it is explicitly revoked with the Revoke() method.//// Not using this flag means that if GraphEdit remotely connects// to this graph and then GraphEdit exits, this object registration// will be deleted, causing future attempts by GraphEdit to fail until// this application is restarted or until the graph is registered again.hr = pROT->Register(ROTFLAGS_REGISTRATIONKEEPSALIVE, pUnkGraph,pmk, &dwROTReg);pmk->Release();}pROT->Release();return hr;}void RemoveFromROT(void){IRunningObjectTable *pirot=0;if (SUCCEEDED(GetRunningObjectTable(0, &pirot))){pirot->Revoke(dwROTReg);pirot->Release();}}#endif//-----------------------------------------------------------------------------// Msg: Display an error message box if needed//-----------------------------------------------------------------------------void Msg(TCHAR *szFormat, ...){TCHAR szBuffer[1024]; // Large buffer for long filenames or URLsconst size_t NUMCHARS = sizeof(szBuffer) / sizeof(szBuffer[0]);const int LASTCHAR = NUMCHARS - 1;// Format the input stringva_list pArgs;va_start(pArgs, szFormat);// Use a bounded buffer size to prevent buffer overruns. Limit count to// character size minus one to allow for a NULL terminating character.(void)StringCchVPrintf(szBuffer, NUMCHARS - 1, szFormat, pArgs);va_end(pArgs);// Ensure that the formatted string is NULL-terminatedszBuffer[LASTCHAR] = TEXT('\0');MessageBox(NULL, szBuffer, TEXT("DirectShow Texture3D9 Sample"),MB_OK | MB_ICONERROR);}/*BOOL GetClipFileName(LPTSTR szName){OPENFILENAME ofn;ZeroMemory(&ofn, sizeof(ofn));// Reset filename*szName = 0;// Fill in standard structure fieldsofn.lStructSize = sizeof(OPENFILENAME);// ofn.hwndOwner = g_hWnd;ofn.lpstrFilter = FILE_FILTER_TEXT;ofn.lpstrCustomFilter = NULL;ofn.nFilterIndex = 1;ofn.lpstrFile = szName;ofn.nMaxFile = MAX_PATH;ofn.lpstrTitle = TEXT("Open Media File...\0");ofn.lpstrFileTitle = NULL;ofn.lpstrDefExt = TEXT("*\0");ofn.Flags = OFN_FILEMUSTEXIST | OFN_READONLY | OFN_PATHMUSTEXIST;// Create the standard file open dialog and return its resultreturn GetOpenFileName((LPOPENFILENAME)&ofn);}*/