//----------------------------------------------------------------------------- // File: DShowTextures.cpp // // Desc: DirectShow sample code - adds support for DirectShow videos playing // on a DirectX 8.0 texture surface. Turns the D3D texture tutorial into // a recreation of the VideoTex sample from previous versions of DirectX. // // Copyright (c) 2000-2001 Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- #include "Textures.h" #include "DShowTextures.h" #include "DXUtil.h" //----------------------------------------------------------------------------- // Global Constants //----------------------------------------------------------------------------- #define SOURCE_FILE TEXT("skiing.avi") // 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_FILTERGRAPH //----------------------------------------------------------------------------- // Global DirectShow pointers //----------------------------------------------------------------------------- CComPtr g_pGB; // GraphBuilder CComPtr g_pMC; // Media Control CComPtr g_pMP; // Media Postion CComPtr g_pME; // Media Event D3DFORMAT g_TextureFormat; // Texture format //----------------------------------------------------------------------------- // InitDShowTextureRenderer : Create DirectShow filter graph and run the graph //----------------------------------------------------------------------------- HRESULT InitDShowTextureRenderer(LPDIRECT3DTEXTURE8 pTexture) { HRESULT hr = S_OK; CComPtr pFTR; // Texture Renderer Filter CComPtr pFTRPinIn; // Texture Renderer Input Pin CComPtr pFSrc; // Source Filter CComPtr pFSrcPinOut; // Source Filter Output Pin CTextureRenderer *pCTR; // DShow Texture renderer // Create the filter graph if (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 object pCTR = new CTextureRenderer(NULL, &hr); if (FAILED(hr)) { 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 graph pFTR = pCTR; if (FAILED(hr = g_pGB->AddFilter(pFTR, L"TEXTURERENDERER"))) { Msg(TEXT("Could not add renderer filter to graph! hr=0x%x"), hr); return hr; } // Determine the file to load based on DirectX Media path (from SDK) // Use a helper function included in DXUtils.cpp TCHAR strFileName[MAX_PATH]; WCHAR wFileName[MAX_PATH]; lstrcpy( strFileName, DXUtil_GetDXSDKMediaPath() ); lstrcat( strFileName, SOURCE_FILE ); #ifndef UNICODE MultiByteToWideChar(CP_ACP, 0, strFileName, -1, wFileName, MAX_PATH); #else lstrcpy(wFileName, strFileName); #endif // Add the source filter if (FAILED(hr = g_pGB->AddSourceFilter (wFileName, L"SOURCE", &pFSrc))) { Msg(TEXT("Could not create source filter to graph! hr=0x%x"), hr); return hr; } // Find the source's output pin and the renderer's input pin if (FAILED(hr = pFTR->FindPin(L"In", &pFTRPinIn))) { Msg(TEXT("Could not find input pin! 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; } // Connect these two filters if (FAILED(hr = g_pGB->Connect(pFSrcPinOut, pFTRPinIn))) { Msg(TEXT("Could not connect pins! hr=0x%x"), hr); return hr; } // Get the graph's media control, event & position interfaces g_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 lParam1; long lParam2; HRESULT hr; // Check for completion events hr = g_pME->GetEvent(&lEventCode, (LONG_PTR *) &lParam1, (LONG_PTR *) &lParam2, 0); if (SUCCEEDED(hr)) { if (EC_COMPLETE == lEventCode) { hr = g_pMP->put_CurrentPosition(0); } // Free any memory associated with this event hr = 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 graph if (!(!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(); } //----------------------------------------------------------------------------- // CTextureRenderer constructor //----------------------------------------------------------------------------- CTextureRenderer::CTextureRenderer( LPUNKNOWN pUnk, HRESULT *phr ) : CBaseVideoRenderer(__uuidof(CLSID_TextureRenderer), NAME("Texture Renderer"), pUnk, phr) { // Store and AddRef the texture for our use. *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; // Reject the connection if this is not a video type if( *pmt->FormatType() != FORMAT_VideoInfo ) { return E_INVALIDARG; } // Only accept RGB24 pvi = (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; // Retrive the size of this media type VIDEOINFO *pviBmp; // Bitmap info header pviBmp = (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 // Create the texture that maps to this media type if( FAILED( hr = D3DXCreateTexture(g_pd3dDevice, m_lVidWidth, m_lVidHeight, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &g_pTexture ) ) ) { Msg(TEXT("Could not create the D3DX texture! hr=0x%x"), hr); return hr; } // D3DXCreateTexture can silently change the parameters on us D3DSURFACE_DESC 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; } g_TextureFormat = ddsd.Format; if (g_TextureFormat != D3DFMT_A8R8G8B8 && 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 buffer LONG lTxtPitch; // Pitch of bitmap, texture // Get the video bitmap buffer pSample->GetPointer( &pBmpBuffer ); // Lock the Texture D3DLOCKED_RECT d3dlr; if (FAILED(g_pTexture->LockRect(0, &d3dlr, 0, 0))) return E_FAIL; // Get the texture buffer & pitch pTxtBuffer = static_cast(d3dlr.pBits); lTxtPitch = d3dlr.Pitch; // Copy the bits // OPTIMIZATION OPPORTUNITY: Use a video and texture // format that allows a simpler copy than this one. if (g_TextureFormat == D3DFMT_A8R8G8B8) { for(int y = 0; y < m_lVidHeight; y++ ) { BYTE *pBmpBufferOld = pBmpBuffer; BYTE *pTxtBufferOld = pTxtBuffer; for (int x = 0; x < m_lVidWidth; x++) { pTxtBuffer[0] = pBmpBuffer[0]; pTxtBuffer[1] = pBmpBuffer[1]; pTxtBuffer[2] = pBmpBuffer[2]; pTxtBuffer[3] = 0xff; pTxtBuffer += 4; pBmpBuffer += 3; } pBmpBuffer = pBmpBufferOld + m_lVidPitch; pTxtBuffer = pTxtBufferOld + lTxtPitch; } } 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 Texture if (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 *pirot; if (FAILED(GetRunningObjectTable(0, &pirot))) { return E_FAIL; } WCHAR wsz[256]; wsprintfW(wsz, L"FilterGraph %08x pid %08x", (DWORD_PTR) 0, GetCurrentProcessId()); HRESULT hr = CreateItemMoniker(L"!", wsz, &pmk); if (SUCCEEDED(hr)) { hr = pirot->Register(0, pUnkGraph, pmk, &dwROTReg); pmk->Release(); } pirot->Release(); return hr; } void RemoveFromROT(void) { IRunningObjectTable *pirot; 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[512]; va_list pArgs; va_start(pArgs, szFormat); _vstprintf(szBuffer, szFormat, pArgs); va_end(pArgs); MessageBox(NULL, szBuffer, TEXT("DirectShow Texture3D Sample"), MB_OK | MB_ICONERROR); }