如何抓住从视频流帧在Windows 8上的现代应用?(How do I grab frames fr

2019-08-18 06:18发布

我试图提取图像了MP4的视频流。 寻找的东西后,它似乎是这样做的正确方法是使用媒体基金会在C ++中,打开帧/读的东西出来。

还有通过文档和示例的方式是非常小的,但一些挖掘之后,通过读取帧到纹理和复制纹理于存储可读纹理的内容(我甚至不是好像有些人有过这样的成功知道我在这里使用的是正确的术语)。 尝试我发现,虽然给我的错误,我可能做了一堆东西是错误的。

下面是从那里我尝试这样做,(项目本身附着在底部)一小段代码。

    ComPtr<ID3D11Texture2D> spTextureDst;
    MEDIA::ThrowIfFailed(
        m_spDX11SwapChain->GetBuffer(0, IID_PPV_ARGS(&spTextureDst))
        );

    auto rcNormalized = MFVideoNormalizedRect();
    rcNormalized.left = 0;
    rcNormalized.right = 1;
    rcNormalized.top = 0;
    rcNormalized.bottom = 1;
    MEDIA::ThrowIfFailed(
        m_spMediaEngine->TransferVideoFrame(m_spRenderTexture.Get(), &rcNormalized, &m_rcTarget, &m_bkgColor)
        );

    //copy the render target texture to the readable texture.
    m_spDX11DeviceContext->CopySubresourceRegion(m_spCopyTexture.Get(),0,0,0,0,m_spRenderTexture.Get(),0,NULL);
    m_spDX11DeviceContext->Flush();

    //Map the readable texture;                 
    D3D11_MAPPED_SUBRESOURCE mapped = {0};
    m_spDX11DeviceContext->Map(m_spCopyTexture.Get(),0,D3D11_MAP_READ,0,&mapped);
    void* buffer = ::CoTaskMemAlloc(600 * 400 * 3);
    memcpy(buffer, mapped.pData,600 * 400 * 3);
    //unmap so we can copy during next update.
    m_spDX11DeviceContext->Unmap(m_spCopyTexture.Get(),0);


    // and the present it to the screen
    MEDIA::ThrowIfFailed(
        m_spDX11SwapChain->Present(1, 0)
        );            
}

我得到的错误是:

微软C ++异常:平台:: InvalidArgumentException ^内存位置0x07AFF60C在0x76814B32在App1.exe第一次机会异常。 HRESULT:80070057

我真的不知道怎么样,因为进一步它追求它,就像我说的,有关于它的很少文档。

下面是修改后的样品我工作过的。 这个问题是具体的的WinRT(Windows 8的应用程序)。

Answer 1:

UPDATE成功! 看到底部编辑


有些部分成功,但也许足以回答你的问题。 请往下看。

在我的系统,调试异常表明OnTimer()函数试图调用失败时TransferVideoFrame() 它给错误是InvalidArgumentException

所以,有点谷歌搜索导致我第一次发现-显然有在NVIDIA驱动程序中的错误 -这意味着视频播放,似乎失败,11级10的功能水平。

所以我的第一个变化是在功能CreateDX11Device()如下:

static const D3D_FEATURE_LEVEL levels[] = {
/*
        D3D_FEATURE_LEVEL_11_1,
        D3D_FEATURE_LEVEL_11_0,  
        D3D_FEATURE_LEVEL_10_1,
        D3D_FEATURE_LEVEL_10_0,
*/
        D3D_FEATURE_LEVEL_9_3,
        D3D_FEATURE_LEVEL_9_2,
        D3D_FEATURE_LEVEL_9_1
    };

现在TransferVideoFrame()仍然失败,但给E_FAIL (作为HRESULT),而不是无效参数。

更多的谷歌搜索导致我的第二个发现 -

这是示出了使用的一个例子TransferVideoFrame()而不使用CreateTexture2D()来预先创建的质感。 我看你已经有了一些代码OnTimer()类似,只是不加这个,但是,所以我想你会发现同样的链接。

反正我现在用这个代码来获取视频帧:

ComPtr <ID3D11Texture2D> spTextureDst;
m_spDX11SwapChain->GetBuffer (0, IID_PPV_ARGS (&spTextureDst));
m_spMediaEngine->TransferVideoFrame (spTextureDst.Get (), nullptr, &m_rcTarget, &m_bkgColor);

这样做后,我看到TransferVideoFrame()成功(好!),但调用Map()上复制的纹理- m_spCopyTexture -因为CPU读取访问纹理未创建失败。

所以,我只是用你的读/写m_spRenderTexture的,而不是因为有正确的标志副本的目标,而且由于之前的变化,我不再使用它。

        //copy the render target texture to the readable texture.
        m_spDX11DeviceContext->CopySubresourceRegion(m_spRenderTexture.Get(),0,0,0,0,spTextureDst.Get(),0,NULL);
        m_spDX11DeviceContext->Flush();

        //Map the readable texture;                 
        D3D11_MAPPED_SUBRESOURCE mapped = {0};
        HRESULT hr = m_spDX11DeviceContext->Map(m_spRenderTexture.Get(),0,D3D11_MAP_READ,0,&mapped);
        void* buffer = ::CoTaskMemAlloc(176 * 144 * 3);
        memcpy(buffer, mapped.pData,176 * 144 * 3);
        //unmap so we can copy during next update.
        m_spDX11DeviceContext->Unmap(m_spRenderTexture.Get(),0);

现在,我的系统中, OnTimer()函数不会失败。 视频帧被呈现给纹理和像素数据被成功地复制出到存储器缓冲器。

想看看是否还有其它问题之前,也许这就是看你是否能做出同样的进步我到目前为止的好时机。 如果你对这个答案与更多的信息发表评论,我会编辑的答案,如果有可能增加任何更多的帮助。

编辑

在纹理描述所做的更改FramePlayer::CreateBackBuffers()

        //make first texture cpu readable
        D3D11_TEXTURE2D_DESC texDesc = {0};
        texDesc.Width = 176;
        texDesc.Height = 144;
        texDesc.MipLevels = 1;
        texDesc.ArraySize = 1;
        texDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
        texDesc.SampleDesc.Count = 1;
        texDesc.SampleDesc.Quality =  0;
        texDesc.Usage = D3D11_USAGE_STAGING;
        texDesc.BindFlags = 0;
        texDesc.CPUAccessFlags =  D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
        texDesc.MiscFlags = 0;

        MEDIA::ThrowIfFailed(m_spDX11Device->CreateTexture2D(&texDesc,NULL,&m_spRenderTexture));

还需要注意的是有内存泄漏,需要某个时候清理了(我相信你知道) - 在下面的行分配的内存永远不会被释放:

void* buffer = ::CoTaskMemAlloc(176 * 144 * 3); // sizes changed for my test

成功

现在我已经成功地拯救了一个单独的帧,但是现在不使用拷贝质感。

首先,我下载了最新版本的DirectXTex库 ,它提供了DX11质地辅助功能,例如从纹理提取图像并保存到文件。 该指令添加了DirectXTex库解决方案为现有的项目需要遵循审慎,注意到需要为Windows 8商店应用程序的变化。

有一次,上述库收录,引用而建,添加下面#include “s到FramePlayer.cpp

#include "..\DirectXTex\DirectXTex.h"  // nb - use the relative path you copied to
#include <wincodec.h>

最后,代码在中央部FramePlayer::OnTimer()需要是类似于以下。 你会看到我只是保存到相同的文件名,每次所以这将需要修改的帧号码添加到例如名字

// new frame available at the media engine so get it 
ComPtr<ID3D11Texture2D> spTextureDst;
MEDIA::ThrowIfFailed(m_spDX11SwapChain->GetBuffer(0, IID_PPV_ARGS(&spTextureDst)));

auto rcNormalized = MFVideoNormalizedRect();
rcNormalized.left = 0;
rcNormalized.right = 1;
rcNormalized.top = 0;
rcNormalized.bottom = 1;
MEDIA::ThrowIfFailed(m_spMediaEngine->TransferVideoFrame(spTextureDst.Get(), &rcNormalized, &m_rcTarget, &m_bkgColor));

// capture an image from the DX11 texture
DirectX::ScratchImage pImage;
HRESULT hr = DirectX::CaptureTexture(m_spDX11Device.Get(), m_spDX11DeviceContext.Get(), spTextureDst.Get(), pImage);
if (SUCCEEDED(hr))
{
    // get the image object from the wrapper
    const DirectX::Image *pRealImage = pImage.GetImage(0, 0, 0);

    // set some place to save the image frame
    StorageFolder ^dataFolder = ApplicationData::Current->LocalFolder;
    Platform::String ^szPath = dataFolder->Path + "\\frame.png";

    // save the image to file
    hr = DirectX::SaveToWICFile(*pRealImage, DirectX::WIC_FLAGS_NONE, GUID_ContainerFormatPng, szPath->Data());
}

// and the present it to the screen
MEDIA::ThrowIfFailed(m_spDX11SwapChain->Present(1, 0));            

我没有时间,现在任何进一步采取这一点,但我很高兴,我所取得的成就到目前为止:-))

你能以新的眼光和评论更新您的结果吗?



Answer 2:

看视频缩略图样品和源读取文档。

您可以在找到示例代码SDK Root\Samples\multimedia\mediafoundation\VideoThumbnail



Answer 3:

我认为OpenCV的可以帮助你。 OpenCV中提供的API来捕捉摄像机或视频文件的帧。 您可以在这里下载http://opencv.org/downloads.html 。 下面是一个演示中,我用“的OpenCV 2.3.1”所著。

#include "opencv.hpp"

using namespace cv;
int main()
{
    VideoCapture cap("demo.avi");   // open a video to capture
    if (!cap.isOpened())        // check if succeeded
        return -1;

    Mat frame;
    namedWindow("Demo", CV_WINDOW_NORMAL);
    // Loop to capture frame and show on the window
    while (1) {
        cap >> frame;
        if (frame.empty())
            break;

        imshow("Demo", frame);
        if (waitKey(33) >= 0)  // pause 33ms every frame
            break;
    }

    return 0;
}


文章来源: How do I grab frames from a video stream on Windows 8 modern apps?