6

私はmp4ビデオストリームから画像を抽出しようとしています。記事を見て、それはC + +でメディア基盤を使用して、フレームを開く/それから物を読んでいることを行うの適切な方法のようだ。Windows 8の最新のアプリケーションでビデオストリームからフレームを取得するにはどうすればよいですか?

ドキュメンテーションとサンプルはほとんどありませんが、掘り下げた後、フレームをテクスチャに読み込み、そのテクスチャの内容をメモリが読めるテクスチャにコピーすることで成功したようですここで正しい用語を使用しているかどうかはわかりません)。私が見つけたものを試してみるとエラーが出て、多分間違ったことをやっているでしょう。

ここでは、私がそれをやろうとしている短いコードがあります(プロジェクト自体が下に添付されています)。

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

私が手にエラーがある:App1.exeで0x76814B32で

初回例外:マイクロソフトC++の例外:メモリ位置0x07AFF60Cのプラットフォーム:: InvalidArgumentException ^。 HRESULT:0x80070057

私が言ったように、それについては非常に小さなドキュメントがあるので、それをさらに追求する方法はわかりません。

Here's the modified sample私は作業しています。この質問はWinRT(Windows 8アプリ)に固有です。

+0

私はWP 8.1用のアプリケーションを開発しているので、私とコードを共有できますか?C++プログラミングには不明ですか?それはあなたから大きな助けになるでしょう。 – Xyroid

+0

こんにちは、私はwinrtアプリで作業しており、私はまたビデオからフレームを抽出する必要があります。私はあなたのコードを使用しましたが、最初の2フレームしか抽出しません。変更が必要なものはありますか?私はあなたがこの質問の編集aprtで述べたようにコードを更新しました。 –

+0

作業コードを共有できますか? –

答えて

5

UPDATE成功!!下


一部部分成功の編集を参照してください、多分十分なあなたの質問に答えるために。お読みください。

私のシステムでは、例外をデバッグすると、TransferVideoFrame()を呼び出すときにOnTimer()関数が失敗したことが示されました。エラーはInvalidArgumentExceptionでした。

グーグルの話が私の最初の発見につながりました。明らかにa bug in NVIDIA driversがあります。つまり、ビデオの再生は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()はまだ失敗し、代わりに無効な引数の(HRESULTとして)E_FAILを与えます。テクスチャを事前に作成するCreateTexture2D()を使用せずにTransferVideoFrame()の使用を示す例とした

-

もっとグーグルは私second discoveryにつながりました。私はあなたにすでに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()関数は失敗しません。ビデオフレームはテクスチャにレンダリングされ、ピクセルデータは正常にメモリバッファにコピーアウトされます。

さらに問題があるかどうかを調べる前に、私がこれまでに行っていたのと同じ進歩を遂げることができるかどうかを見ておくとよいでしょう。この回答に詳細な情報を追加すると、できるだけヘルプを追加して回答を追加します。

EDIT 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 

SUCCESS

私は現在、個々のフレームを保存することに成功しましたが、今はコピーテクスチャを使用していません。

まず、the DirectXTex Libraryの最新バージョンをダウンロードしました。これはDX11テクスチャヘルパー機能を提供します。たとえば、テクスチャから画像を抽出してファイルに保存するなどです。既存のプロジェクトとしてソリューションにDirectXTexライブラリを追加するためのThe instructionsは、Windows 8 Storeアプリケーションに必要な変更を注意深く熟読する必要があります。

一旦、上記ライブラリーは、含まれる参照され組み込まれて、最後に、FramePlayer::OnTimer()でコードの中央部分は、次のようにする必要があるFramePlayer.cpp

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

に次#include年代に追加されます。毎回同じファイル名に保存するだけですので、これを修正する必要があります。私はそれ以上にこれを取るために、今の時間を持っていないが、私は私がこれまで:-)達成したものに非常に満足している名前

// 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));    

にフレーム番号)

あなたが取ることができます新鮮な外観とあなたの結果をコメントで更新しますか?

+0

うわー。ありがとう。私は今日または明日それを見るでしょう。 –

+1

よろしくお願いします。私のテストmp4は176 * 144だったので、上記のハードコードされたサイズが600 * 400を置き換えたことに注意してください。これらのサイズは、実際のフレームサイズから実際に得られるはずです。 –

+0

ちょっと..これを見て始めましたが、始めに失敗しました...行:\t \t \t 'MEDIA :: ThrowIfFailed(m_spDX11Device-> CreateTexture2D(&texDesc、NULL、&m_spRenderTexture));'無効な引数で失敗するようです。あなたはそこに変更を加えましたか? –

0

Video Thumbnail SampleSource Readerのドキュメントをご覧ください。

あなたは、私はOpenCVのはあなたを助けるかもしれないと思うSDK Root\Samples\multimedia\mediafoundation\VideoThumbnail

+0

これはWinRTを対象としていません。 –

-2

下のサンプルコードを見つけることができます。 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; 
} 
+0

私はWinRTソリューションを探しています。 OpenCVはそれをサポートしていません。少なくともまだ。 –

関連する問題