Search code examples
c++directshow

keep aspect ratio in DirectShow? (windowed) C++


EDIT: Updated the code and the application calling the DLL no longer crashes.

I want the video being played by DirectShow to keep its aspect ratio when the fourth argument of show_video() is set to true. Here's my DLL's source:

#include <windows.h>
#include <dshow.h>

#pragma comment (lib, "strmiids.lib")
#define DLL extern "C" _declspec(dllexport)

wchar_t *convertCharArrayToLPCWSTR(const char* charArray) {
    wchar_t* wString = new wchar_t[4096];

    MultiByteToWideChar(CP_ACP, 0, charArray, -1, wString, 4096);

    return wString;
}

DLL void show_video(double window1, HWND window2, char *fname, double keep_aspect_ratio) {
    CoInitialize(NULL);

    HRESULT hr = S_OK;

    IGraphBuilder *pGraph = NULL;
    hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **)&pGraph);
    hr = pGraph->RenderFile(convertCharArrayToLPCWSTR(fname), NULL);

    IBaseFilter *pVideoRenderer = NULL;
    hr = CoCreateInstance(CLSID_VideoMixingRenderer, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void **)&pVideoRenderer);

    IVMRAspectRatioControl *pAspectRatio = NULL;
    hr = pVideoRenderer->QueryInterface(IID_IVMRAspectRatioControl, (void**)&pAspectRatio);

    if ((bool)keep_aspect_ratio == true) {
        hr = pAspectRatio->SetAspectRatioMode(VMR_ARMODE_LETTER_BOX);
    }
    else {
        hr = pAspectRatio->SetAspectRatioMode(VMR_ARMODE_NONE);
    }

    IVideoWindow *pVidWin = NULL;
    hr = pGraph->QueryInterface(IID_IVideoWindow, (void **)&pVidWin);

    RECT rect;
    if ((HWND)(DWORD)window1 != NULL) {
        SetWindowLong((HWND)(DWORD)window1, GWL_STYLE, GetWindowLong((HWND)(DWORD)window1, GWL_STYLE) | WS_CLIPCHILDREN | WS_CLIPSIBLINGS);
        hr = pVidWin->put_Owner((OAHWND)(HWND)(DWORD)window1);
        GetClientRect((HWND)(DWORD)window1, &rect);
    }
    else {
        SetWindowLong(window2, GWL_STYLE, GetWindowLong(window2, GWL_STYLE) | WS_CLIPCHILDREN | WS_CLIPSIBLINGS);
        hr = pVidWin->put_Owner((OAHWND)window2);
        GetClientRect(window2, &rect);
    }

    hr = pVidWin->SetWindowPosition(0, 0, rect.right - rect.left, rect.bottom - rect.top);
    hr = pVidWin->put_WindowStyle(WS_CHILD | WS_CLIPSIBLINGS);
    hr = pVidWin->SetWindowForeground(OATRUE);
    hr = pVidWin->HideCursor(OATRUE);

    IMediaControl *pControl = NULL;
    hr = pGraph->QueryInterface(IID_IMediaControl, (void**)&pControl);
    hr = pControl->Run();

    IMediaEvent *pEvent = NULL;
    hr = pGraph->QueryInterface(IID_IMediaEvent, (void **)&pEvent);
    long evCode;

    hr = pEvent->WaitForCompletion(INFINITE, &evCode);

    hr = pControl->Stop();
    hr = pVidWin->put_Visible(OAFALSE);
    hr = pVidWin->put_Owner(NULL);

    pEvent->Release();
    pControl->Release();
    pVidWin->Release();

    pAspectRatio->Release();
    pVideoRenderer->Release();
    pGraph->Release();

    CoUninitialize();
}

As it stands, calling the DLL from my application, the video I selected for the third argument plays fine, but the video does not keep its original aspect ratio. Does anyone know what I'm doing wrong?


Solution

  • Found the solution. After initializing pVideoRenderer, I needed to add the following line:

    pGraph->FindFilterByName(L"Video Renderer", &pVideoRenderer);
    

    So the resulting code looks like this:

    #include <windows.h>
    #include <dshow.h>
    
    #pragma comment (lib, "strmiids.lib")
    #define DLL extern "C" _declspec(dllexport)
    
    wchar_t *convertCharArrayToLPCWSTR(const char* charArray) {
        wchar_t* wString = new wchar_t[4096];
    
        MultiByteToWideChar(CP_ACP, 0, charArray, -1, wString, 4096);
    
        return wString;
    }
    
    DLL void show_video(double window1, HWND window2, char *fname, double keep_aspect_ratio) {
        CoInitialize(NULL);
    
        HRESULT hr = S_OK;
    
        IGraphBuilder *pGraph = NULL;
        hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **)&pGraph);
        hr = pGraph->RenderFile(convertCharArrayToLPCWSTR(fname), NULL);
    
        IBaseFilter *pVideoRenderer = NULL;
        hr = CoCreateInstance(CLSID_VideoMixingRenderer, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void **)&pVideoRenderer);
        pGraph->FindFilterByName(L"Video Renderer", &pVideoRenderer);
    
        IVMRAspectRatioControl *pAspectRatio = NULL;
        hr = pVideoRenderer->QueryInterface(IID_IVMRAspectRatioControl, (void**)&pAspectRatio);
    
        if ((bool)keep_aspect_ratio == true) {
            hr = pAspectRatio->SetAspectRatioMode(VMR_ARMODE_LETTER_BOX);
        }
        else {
            hr = pAspectRatio->SetAspectRatioMode(VMR_ARMODE_NONE);
        }
    
        IVideoWindow *pVidWin = NULL;
        hr = pGraph->QueryInterface(IID_IVideoWindow, (void **)&pVidWin);
    
        RECT rect;
        if ((HWND)(DWORD)window1 != NULL) {
            SetWindowLong((HWND)(DWORD)window1, GWL_STYLE, GetWindowLong((HWND)(DWORD)window1, GWL_STYLE) | WS_CLIPCHILDREN | WS_CLIPSIBLINGS);
            hr = pVidWin->put_Owner((OAHWND)(HWND)(DWORD)window1);
            GetClientRect((HWND)(DWORD)window1, &rect);
        }
        else {
            SetWindowLong(window2, GWL_STYLE, GetWindowLong(window2, GWL_STYLE) | WS_CLIPCHILDREN | WS_CLIPSIBLINGS);
            hr = pVidWin->put_Owner((OAHWND)window2);
            GetClientRect(window2, &rect);
        }
    
        hr = pVidWin->SetWindowPosition(0, 0, rect.right - rect.left, rect.bottom - rect.top);
        hr = pVidWin->put_WindowStyle(WS_CHILD | WS_CLIPSIBLINGS);
        hr = pVidWin->SetWindowForeground(OATRUE);
        hr = pVidWin->HideCursor(OATRUE);
    
        IMediaControl *pControl = NULL;
        hr = pGraph->QueryInterface(IID_IMediaControl, (void**)&pControl);
        hr = pControl->Run();
    
        IMediaEvent *pEvent = NULL;
        hr = pGraph->QueryInterface(IID_IMediaEvent, (void **)&pEvent);
        long evCode;
    
        hr = pEvent->WaitForCompletion(INFINITE, &evCode);
    
        hr = pControl->Stop();
        hr = pVidWin->put_Visible(OAFALSE);
        hr = pVidWin->put_Owner(NULL);
    
        pEvent->Release();
        pControl->Release();
        pVidWin->Release();
    
        pAspectRatio->Release();
        pVideoRenderer->Release();
        pGraph->Release();
    
        CoUninitialize();
    }
    

    Problem solved! :D