Search code examples
windowsvisual-c++graphicsdirectxms-media-foundation

Drawing text on IMFMediaBuffer in Windows 8 Media Foundation Transform


How would you draw text on a IMFMediaBuffer object, and write it out to another IMFMediaBuffer object?

The context is that I'm building an MFT, and initially I tried using Direct2D and Direct3D11 to achieve this, but to no avail.


Solution

  • I was able to accomplish this with Windows GDI calls. The MFT I created has RGB32 input/output types, which allows me to copy to/from a Bitmap object. I copy the part of the frame I want to overlay with text into a Windows bitmap I create, draw the text, then copy back to the IMFMediaBuffer. Here's my code:

    #define BREAK_ON_FAIL(val) { if ( FAILED(hr = (val)) ) { break; } }
    
    HRESULT AddOverlay(IMFSample* pSample)
    {
        HRESULT hr = S_OK;
        IMFMediaBuffer * pBuffer;
        HDC hDC, hMemDC;
        HBITMAP hBitmap, hOldBitmap;
    
        do
        {
            BYTE * pBufferData;
            UINT32 nWidth, nHeight;
            BITMAPV5HEADER bi;
            BYTE * pBitmapData;
            UINT32 nXOffset, nYOffset;
            RECT OverlayRect;
            OverlayRect.left = 0;
            OverlayRect.top = 0;
            OverlayRect.right = 400;
            OverlayRect.bottom = 32;
            DWORD nOverlayWidth = OverlayRect.right - OverlayRect.left;
            DWORD nOverlayHeight = OverlayRect.bottom - OverlayRect.top;
            LONG lFrameStride;
    
            // Get the frame dimensions and stride
            MFGetAttributeSize(m_pInputType, MF_MT_FRAME_SIZE, &nWidth, &nHeight);
            m_pInputType->GetUINT32(MF_MT_DEFAULT_STRIDE, (UINT32*)&lFrameStride))
    
            // Setup offset for the overlay area into the video frame
            nXOffset = (nWidth - nOverlayWidth) / 2;
            nYOffset = nOverlayHeight-1;
    
            // Set up the bitmap header
            ZeroMemory(&bi, sizeof(BITMAPV5HEADER));
            bi.bV5Size          = sizeof(BITMAPV5HEADER);
            bi.bV5Width         = nOverlayWidth;
            // If the stride is negative, the bitmap is bottom-up, which is designated by a negative height
            bi.bV5Height        = (lFrameStride > 0) ? nOverlayHeight : -(LONG)nOverlayHeight;
            bi.bV5Planes        = 1;
            bi.bV5BitCount      = 32;
            bi.bV5Compression   = BI_RGB;
            // The following mask specification specifies a supported 32 BPP
            // alpha format for Windows XP.
            bi.bV5RedMask       = 0x00FF0000;
            bi.bV5GreenMask     = 0x0000FF00;
            bi.bV5BlueMask      = 0x000000FF;
            bi.bV5AlphaMask     = 0xFF000000;
    
            // Create a DIB section with an alpha channel, along with
            // a memory device context
            hDC = GetDC(NULL);
            hBitmap = CreateDIBSection(hDC, (BITMAPINFO*)&bi, DIB_RGB_COLORS, (void**)&pBitmapData, NULL, 0);
            hMemDC = CreateCompatibleDC(hDC);
            ReleaseDC(NULL, hDC);
    
            // Lock the media buffer for our use
            BREAK_ON_FAIL( pSample->GetBufferByIndex(0, &pBuffer) );
            BREAK_ON_FAIL( pBuffer->Lock(&pBufferData, NULL, NULL) );
    
            // Copy the video frame to the bitmap (to support transparency)
            MFCopyImage(pBitmapData, nOverlayWidth*sizeof(RGBQUAD),
                pBufferData + nYOffset*abs(lFrameStride) + nXOffset*sizeof(RGBQUAD), lFrameStride,
                nOverlayWidth*sizeof(RGBQUAD), nOverlayHeight);
    
            // Draw on the bitmap
            hOldBitmap = (HBITMAP)SelectObject(hMemDC, hBitmap);
            //FillRect(hMemDC, &OverlayRect, WHITE_BRUSH);
            SetTextColor(hMemDC, RGB(255,0,0));
            SetBkMode(hMemDC, TRANSPARENT);
            DrawText(hMemDC, _T("Hello World!"), 12, &OverlayRect, DT_CENTER);
            SelectObject(hMemDC, hOldBitmap);
    
            // Copy the bitmap to the buffer
            MFCopyImage(pBufferData + nYOffset*abs(lFrameStride) + nXOffset*sizeof(RGBQUAD), lFrameStride,
                pBitmapData, nOverlayWidth*sizeof(RGBQUAD),
                nOverlayWidth*sizeof(RGBQUAD), nOverlayHeight);
    
            BREAK_ON_FAIL( pBuffer->Unlock() );
    
        } while(false);
    
        DeleteDC(hMemDC);
        DeleteObject(hBitmap);
        SafeRelease(&pBuffer);
    
        return hr;
    }
    

    References: