【问题标题】:keep aspect ratio in DirectShow? (windowed) C++在 DirectShow 中保持纵横比? (窗口)C++
【发布时间】:2017-08-02 15:20:03
【问题描述】:

编辑:更新了代码,调用 DLL 的应用程序不再崩溃。

我希望 DirectShow 正在播放的视频在 show_video() 的第四个参数设置为 true 时保持其纵横比。这是我的 DLL 的源代码:

#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();
}

就目前而言,从我的应用程序调用 DLL,我为第三个参数选择的视频播放正常,但视频没有保持其原始纵横比。有谁知道我做错了什么?

【问题讨论】:

  • 你查看QueryInterface的hr结果了吗?也许 pAspectRatio 为 NULL。您应该直接在渲染器上查询界面。例如对于 EVR,您应该查询 IMFVideoDisplayControl 并使用 SetAspectRatioMode。
  • @VuVirt 我更新了代码,以便它直接在渲染器上查询其接口,(或者,至少,我认为我是这样做的),虽然它不再崩溃,但它仍然不是t 当第四个参数设置为 true 时保持其纵横比。

标签: c++ directshow


【解决方案1】:

找到了解决办法。初始化 pVideoRenderer 后,我需要添加以下行:

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

所以生成的代码如下所示:

#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();
}

问题解决了! :D

【讨论】:

    【解决方案2】:

    ...调用 DLL 的进程现在崩溃了。

    进程崩溃假设您可以提供其他详细信息,例如崩溃点、异常详细信息等。

    可能的原因是前面的QueryInterface 调用上的E_NOINTERFACE 失败并且缺少IVMRAspectRatioControl 指针。

    Filter Graph Manager 不应该实现IVMRAspectRatioControl。如果您的过滤器图中有视频混合渲染器过滤器,请直接从它QueryInterface

    UPD。另一个答案中的解决方案是不应该如何做的一个例子。如果你为 VR 做CoCreateInstance,你应该将它添加到图表中,并将其包含到渲染过程中。否则,您根本不需要CoCreateInstance,并且您不应该按名称定位过滤器 - 而是枚举过滤器并通过找到实现相关接口的过滤器来识别您的 VR。

    【讨论】:

    • 我在 Windows XP 及更高版本 (VMR7) 上使用 DirectShow 的默认视频混合渲染器。你有机会提供一个代码 sn-p 吗?
    • 我更新了代码并设法解决了崩溃问题。但是视频仍然没有保持原来的宽高比。
    猜你喜欢
    • 2013-09-01
    • 2017-11-11
    • 2021-10-05
    • 1970-01-01
    • 2012-12-16
    • 2012-08-25
    • 2021-09-22
    • 1970-01-01
    • 2023-03-18
    相关资源
    最近更新 更多