Although the BasicMixer application provides general mixing operations, there s no point in writing a compositor just to replicate functionality already provided by the VMR. The next sample shows an interesting technique that uses an alpha map to create a circular region of video.
Run the AlphaBurst sample located in the AVBook\bin directory. When the application launches, it begins playing a video. If you click the video window, a circular transparent region appears under the mouse cursor, and a second video is visible under the first video. While the mouse button is down, the circular area follows the mouse cursor. (See Plate 9.)
The AlphaBurst compositor works by applying two texture stages to the top layer of video. The first stage contains the alpha map, and the second stage contains the video surface. The diffuse color is selected from the video surface, while the alpha value is selected from the alpha map.
The alpha map texture is stored in the AlphaMap.dds file. We created the texture by drawing a radial blend in a paint program and saving it as a bitmap. We then used the DirectX Texture tool to select the bitmap into the texture s alpha channel so that the grayscale values became alpha values. The alpha map is loaded in the InitCompositionDevice method.
STDMETHODIMP CMixer::InitCompositionDevice(IUnknown* pD3DDevice) { // Load the alpha map. const TCHAR sz[] = TEXT("AlphaMap.dds"); D3DXCreateTextureFromFileEx(m_pDevice, sz, D3DX_DEFAULT, D3DX_DEFAULT, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, D3DX_DEFAULT, D3DX_DEFAULT, 0, NULL, NULL, &m_pAlphaTexture); // The rest of this method is the same as BasicMixer. }
For the SetStreamMediaType method, we have arbitrarily decided that stream 0 will be the bottom layer and stream 1 will be the top layer with the alpha. The mixer ignores any other streams, and ignores the z-order settings. That means we can always use StretchRect for stream 0, and only worry about textures for stream 1. However, we do have to check the fTexture texture flag for stream 1 and create a private video texture if fTexture is FALSE .
STDMETHODIMP CMixer::SetStreamMediaType(DWORD dwStrmID, AM_MEDIA_TYPE* pmt, BOOL fTexture ) { CLock lock(&m_CritSec); if (pmt == NULL) { // Clear the media type. MyFreeMediaType(m_mt[dwStrmID]); } else { // Save the media type. MyCopyMediaType(&m_mt[dwStrmID], *pmt); } // Create the video texture for stream 1 only. if ((dwStrmID == 1) && (pmt != NULL) && !fTexture) { CreateVideoTexture(pmt); } return S_OK; }
The CompositeImage method for mixing the video streams is similar to the code from the BasicMixer sample. We start with a minor hack to make sure the application has rendered two video streams and did not change the z-orders. That will simplify the rest of the function because then we don t have to test for the stream IDs.
if (cStreams != 2) { return E_FAIL; } if ((pVideoStreamInfo[0].dwStrmID != 0) (pVideoStreamInfo[1].dwStrmID != 1)) { return E_FAIL; }
Now, instead of looping through the stream array in z-order, we just stretch stream 0 to fill the entire render target. Alpha blending is enabled only when the user clicks the video, so the rest of the time we can skip stream 0 entirely.
if (m_fShowAlpha) { p = &pVideoStreamInfo[0]; RECT rc; ZeroMemory(&rc, sizeof(RECT)); rc.right = p->dwWidth; rc.bottom = p->dwHeight; pDevice->StretchRect(p->pddsVideoSurface, &rc, pddsRenderTarget, &rc, D3DTEXF_NONE); // Note: You could use a different filtering mode, such as D3DTEXF_LINEAR. }
For stream 1, we use the video texture and add a second set of texture coordinates for the alpha map. The alpha map is centered around a point that is determined by the position of the mouse cursor. Here is the new definition for the vertex format and the vertices.
#define VERTEX_FORMAT (D3DFVF_XYZRHW D3DFVF_TEX2) struct VERTEX { float x, y, z, rhw; float tu, tv; // Alpha map. float tu2, tv2; // Video texture. }; VERTEX vertices[] = { // x y z rhw tu tv tu2 tv2 { 0.0f, 0.0f, 0.5f, 1.0f, fAlphaU_min, fAlphaV_min, 0.0f, 0.0f }, { fWidth, 0.0f, 0.5f, 1.0f, fAlphaU_max, fAlphaV_min, fTU, 0.0f }, { fWidth, fHeight, 0.5f, 1.0f, fAlphaU_max, fAlphaV_max, fTU, fTV }, { 0.0f, 0.0f, 0.5f, 1.0f, fAlphaU_min, fAlphaV_min, 0.0f, 0.0f }, { fWidth, fHeight, 0.5f, 1.0f, fAlphaU_max, fAlphaV_max, fTU, fTV }, { 0.0f, fHeight, 0.5f, 1.0f, fAlphaU_min, fAlphaV_max, 0.0f, fTV } };
The render states depend on whether alpha blending is enabled. When alpha blending is enabled, stage 0 uses the alpha map and stage 1 uses the video texture. For the alpha map, the color operation is select current (we overwrite it in the next texture stage in any case) and the alpha operation is select from texture. For the video texture, the color operation is select from texture and the alpha operation is select current. When alpha blending is disabled, the video texture is used in stage 0 and stage 1 is disabled. Figure 11.4 discusses texture stages.
Here is the code that sets the render states.
if (m_fShowAlpha) { // Enable alpha blending. m_pDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); m_pDevice->SetRenderState(D3DRS_ALPHAREF, 0x10); m_pDevice->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATER); // Texture stage 0: Select texture alpha. m_pDevice->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0); m_pDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1); m_pDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_CURRENT); m_pDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); m_pDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE); m_pDevice->SetTexture(0, m_pAlphaTexture); // Texture stage 1: Select texture color and current alpha. m_pDevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 1); m_pDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_SELECTARG1); m_pDevice->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE); m_pDevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); m_pDevice->SetTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_CURRENT); m_pDevice->SetTexture(1, pTex); } else { // Disable alpha blending. m_pDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); m_pDevice->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE); // Texture stage 0: Select color from the texture. m_pDevice->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 1); m_pDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1); m_pDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); m_pDevice->SetTexture(0, pTex); // Disable texture stage 1. m_pDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); }