0
votes

I am trying to get a directshow player to display on a panel other than the main window in c++ builder. I took the Microsoft sample player code and made this into a c++ application which created the form and played the video onto the window.

I a now want to add to a vcl application and want to display the video onto a specific panel. Anytime I try to run the graph under VCL it will only build and run if I pass handle as the application handle rather than a panel handle. I know the graph runs because i hear the audio, but i cannot get the video to render to a panel. If i pass hwnd as Handle of any VCL window the callback assignment fails for the filtergraph (SetNotifyWindow), it works if I pass Application->Handle.

    hr = m_pEvent->SetNotifyWindow((OAHWND)m_hwnd, WM_GRAPH_EVENT, NULL);
    if (FAILED(hr))
    {
        goto done;
    }

The Video render function ties to find best suited renderer as follows

HRESULT DShowPlayer::CreateVideoRenderer()
{
    HRESULT hr = E_FAIL;

    enum { Try_EVR, Try_VMR9, Try_VMR7 };

    for (DWORD i = Try_EVR; i <= Try_VMR7; i++)
    {
        switch (i)
        {
        case Try_EVR:
            m_pVideo = new (std::nothrow) CEVR();
            break;

        case Try_VMR9:
            m_pVideo = new (std::nothrow) CVMR9();
            break;

        case Try_VMR7:
            m_pVideo = new (std::nothrow) CVMR7();
            break;
        }

        if (m_pVideo == NULL)
        {
            hr = E_OUTOFMEMORY;
            break;
        }

        hr = m_pVideo->AddToGraph(m_pGraph, m_hwnd);
        if (SUCCEEDED(hr))
        {
            break;
        }

        delete m_pVideo;
        m_pVideo = NULL;
    }
    return hr;
}

Each of these initialise in windowless mode for example VMR9

HRESULT InitWindowlessVMR9( 
    IBaseFilter *pVMR,              // Pointer to the VMR
    HWND hwnd,                      // Clipping window
    IVMRWindowlessControl9** ppWC   // Receives a pointer to the VMR.
    ) 
{ 

    IVMRFilterConfig9 * pConfig = NULL; 
    IVMRWindowlessControl9 *pWC = NULL;

    // Set the rendering mode.  
    HRESULT hr = pVMR->QueryInterface(IID_PPV_ARGS(&pConfig)); 
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pConfig->SetRenderingMode(VMR9Mode_Windowless); 
    if (FAILED(hr))
    {
        goto done;
    }

    // Query for the windowless control interface.
    hr = pVMR->QueryInterface(IID_PPV_ARGS(&pWC));
    if (FAILED(hr))
    {
        goto done;
    }

    // Set the clipping window.
    hr = pWC->SetVideoClippingWindow(hwnd);
    if (FAILED(hr))
    {
        goto done;
    }

    // Preserve aspect ratio by letter-boxing
    hr = pWC->SetAspectRatioMode(VMR9ARMode_LetterBox);
    if (FAILED(hr))
    {
        goto done;
    }

    // Return the IVMRWindowlessControl pointer to the caller.
    *ppWC = pWC;
    (*ppWC)->AddRef();

done:
    SafeRelease(&pConfig);
    SafeRelease(&pWC);
    return hr; 
} 

And they add the render filter to the graph as follows:

HRESULT CVMR9::AddToGraph(IGraphBuilder *pGraph, HWND hwnd)
{
    IBaseFilter *pVMR = NULL;

    HRESULT hr = AddFilterByCLSID(pGraph, CLSID_VideoMixingRenderer9, &pVMR, L"VMR-9");
    if (SUCCEEDED(hr))
    {
        // Set windowless mode on the VMR. This must be done before the VMR 
        // is connected.
        hr = InitWindowlessVMR9(pVMR, hwnd, &m_pWindowless);
    }
    SafeRelease(&pVMR);
    return hr;
}

How can I get the player to display onto a panel?

2

2 Answers

1
votes

Note that IMediaEventEx::SetNotifyWindow you quoted in your code snippet does not define where the video is displayed. This call is intended to let filter graph manager know where you want to receive notification messages at.

You don't specify what video renderer mode you are using, so I assume it's the simplest (though referred to as "legacy") - windowed mode. IVideoWindow::put_Owner and IVideoWindow::SetWindowPosition is how you define where video is presented in windowed mode, put_Owner specifically is taking a parent window handle.

2019-06-04 Update

So you are trying to alter DShowPlayer sample & tutorial and the code is using windowless mode.

Stating with Windows Vista the sample would use EVR code path, so I am not sure why you preferred to refer to VMR-9. Nonetheless, they both have "windowless initialization" methods with HWND as an argument. This is the window handle where video is embedded. You are supposed to use a visible window handle (reference for EVR) and also provide valid clipping rectangle (reference). The sample already does this and you can alter it and use a different window such as your panel in a similar way.

0
votes

I found the issue. If you set the windowstate to maximised for the main form.

    WindowState = wsMaximized;

The handle to the forms is classed as invalid when you try to add the renderer to the graph.

        hr = m_pVideo->AddToGraph(m_pGraph, m_hwnd);
        if (SUCCEEDED(hr))
        {
            break;
        }

I had this line in the constructor for the MainForm and this caused the issue, moving the line to FormActivate fixed the issue.