Media Foundation-based VideoWriter added
authorAlexander Smorkalov <alexander.smorkalov@itseez.com>
Mon, 6 May 2013 10:36:51 +0000 (03:36 -0700)
committerAlexander Smorkalov <alexander.smorkalov@itseez.com>
Mon, 24 Jun 2013 09:45:22 +0000 (02:45 -0700)
modules/highgui/src/cap.cpp
modules/highgui/src/cap_ffmpeg.cpp
modules/highgui/src/cap_msmf.cpp
modules/highgui/src/precomp.hpp

index 3750d1d..afab8d4 100644 (file)
@@ -408,8 +408,20 @@ CV_IMPL CvVideoWriter* cvCreateVideoWriter( const char* filename, int fourcc,
     if(!fourcc || !fps)
         result = cvCreateVideoWriter_Images(filename);
 
+#ifdef HAVE_FFMPEG
     if(!result)
         result = cvCreateVideoWriter_FFMPEG_proxy (filename, fourcc, fps, frameSize, is_color);
+#endif
+
+#ifdef HAVE_VFW
+    if(!result)
+     return cvCreateVideoWriter_VFW(filename, fourcc, fps, frameSize, isColor);
+#endif
+
+#ifdef HAVE_MSMF
+    if (!result)
+        result = cvCreateVideoWriter_MSMF(filename, fourcc, fps, frameSize, is_color);
+#endif
 
 /*  #ifdef HAVE_XINE
     if(!result)
index 669ebda..7d4d6af 100644 (file)
@@ -263,9 +263,5 @@ CvVideoWriter* cvCreateVideoWriter_FFMPEG_proxy( const char* filename, int fourc
     if( result->open( filename, fourcc, fps, frameSize, isColor != 0 ))
         return result;
     delete result;
-#ifdef HAVE_VFW
-     return cvCreateVideoWriter_VFW(filename, fourcc, fps, frameSize, isColor);
- #else
     return 0;
-#endif
 }
index 28d92c3..1d6bb59 100644 (file)
@@ -54,6 +54,8 @@
 #include <mfplay.h>
 #include <mfobjects.h>
 #include "Strsafe.h"
+#include <wrl/client.h>
+#include <Mfreadwrite.h>
 #include <new>
 #include <map>
 #include <vector>
@@ -67,6 +69,7 @@
 #pragma comment(lib, "mf")
 #pragma comment(lib, "mfuuid")
 #pragma comment(lib, "Strmiids")
+#pragma comment(lib, "Mfreadwrite")
 #pragma comment(lib, "MinCore_Downlevel")
 
 struct IMFMediaType;
@@ -146,7 +149,6 @@ private:
 
 DWORD WINAPI MainThreadFunction( LPVOID lpParam );
 typedef void(*emergensyStopEventCallback)(int, void *);
-typedef unsigned char BYTE;
 
 class RawImage
 {
@@ -1031,7 +1033,7 @@ done:
     SafeRelease(&pEvent);
     SafeRelease(&ig_pSession);
     SafeRelease(&ig_pTopology);
-    
+
     return hr;
 }
 
@@ -1080,7 +1082,7 @@ done:
     SafeRelease(&pPD);
     SafeRelease(&pSD);
     SafeRelease(&pHandler);
-    
+
     return hr;
 }
 
@@ -2939,4 +2941,352 @@ CvCapture* cvCreateCameraCapture_MSMF( int index )
     delete capture;
     return 0;
 }
+
+
+//
+//
+// Media Foundation-based Video Writer
+//
+//
+
+using namespace Microsoft::WRL;
+
+class CvVideoWriter_MSMF : public CvVideoWriter
+{
+public:
+    CvVideoWriter_MSMF();
+    virtual ~CvVideoWriter_MSMF();
+    virtual bool open( const char* filename, int fourcc,
+                       double fps, CvSize frameSize, bool isColor );
+    virtual void close();
+    virtual bool writeFrame( const IplImage* img);
+
+private:
+    UINT32 videoWidth;
+    UINT32 videoHeight;
+    double fps;
+    UINT32 bitRate;
+    UINT32 frameSize;
+    GUID   encodingFormat;
+    GUID   inputFormat;
+
+    DWORD  streamIndex;
+    ComPtr<IMFSinkWriter> sinkWriter;
+
+    bool   initiated;
+
+    LONGLONG rtStart;
+    UINT64 rtDuration;
+
+    HRESULT InitializeSinkWriter(const char* filename);
+    HRESULT WriteFrame(DWORD *videoFrameBuffer, const LONGLONG& rtStart, const LONGLONG& rtDuration);
+};
+
+CvVideoWriter_MSMF::CvVideoWriter_MSMF()
+{
+}
+
+CvVideoWriter_MSMF::~CvVideoWriter_MSMF()
+{
+    close();
+}
+
+bool CvVideoWriter_MSMF::open( const char* filename, int fourcc,
+                       double _fps, CvSize frameSize, bool isColor )
+{
+    videoWidth = frameSize.width;
+    videoHeight = frameSize.height;
+    fps = _fps;
+    bitRate = 4*800000;
+    encodingFormat = MFVideoFormat_WMV3;
+    inputFormat = MFVideoFormat_RGB32;
+
+    HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
+    if (SUCCEEDED(hr))
+    {
+        printf("CoInitializeEx is successfull\n");
+        hr = MFStartup(MF_VERSION);
+        if (SUCCEEDED(hr))
+        {
+            printf("MFStartup is successfull\n");
+            hr = InitializeSinkWriter(filename);
+            if (SUCCEEDED(hr))
+            {
+                printf("InitializeSinkWriter is successfull\n");
+                initiated = true;
+                rtStart = 0;
+                MFFrameRateToAverageTimePerFrame(fps, 1, &rtDuration);
+                printf("duration: %d\n", rtDuration);
+            }
+        }
+    }
+
+    return SUCCEEDED(hr);
+}
+
+void CvVideoWriter_MSMF::close()
+{
+    printf("VideoWriter::close()\n");
+    if (!initiated)
+    {
+        printf("VideoWriter was not Initialized\n");
+        return;
+    }
+
+    initiated = false;
+    HRESULT hr = sinkWriter->Finalize();
+    printf("sinkWriter Finalize status %u\n", hr);
+    MFShutdown();
+}
+
+bool CvVideoWriter_MSMF::writeFrame(const IplImage* img)
+{
+    if (!img)
+        return false;
+
+    printf("Writing not empty IplImage\n");
+
+    auto length = img->width * img->height * 4;
+    printf("Image: %dx%d, %d\n", img->width, img->height, length);
+    DWORD* target = new DWORD[length];
+
+    printf("Before for loop\n");
+    for (int rowIdx = 0; rowIdx < img->height; rowIdx++)
+    {
+        char* rowStart = img->imageData + rowIdx*img->widthStep;
+        for (int colIdx = 0; colIdx < img->width; colIdx++)
+        {
+            BYTE b = rowStart[colIdx * img->nChannels + 0];
+            BYTE g = rowStart[colIdx * img->nChannels + 1];
+            BYTE r = rowStart[colIdx * img->nChannels + 2];
+
+                    // On ARM devices data is stored starting from the last line
+        // (and not the first line) so you have to revert them on the Y axis
+#if _M_ARM
+            auto row = index / videoWidth;
+            auto targetRow = videoHeight - row - 1;
+            auto column = index - (row * videoWidth);
+            target[(targetRow * videoWidth) + column] = (r << 16) + (g << 8) + b;
+#else
+            target[rowIdx*img->width+colIdx] = (r << 16) + (g << 8) + b;
+#endif
+        }
+    }
+
+    // Send frame to the sink writer.
+    printf("Before private WriteFrame call\n");
+    HRESULT hr = WriteFrame(target, rtStart, rtDuration);
+    printf("After private WriteFrame call\n");
+    if (FAILED(hr))
+    {
+        printf("Private WriteFrame failed\n");
+        delete[] target;
+        return false;
+    }
+    rtStart += rtDuration;
+
+    printf("End of writing IplImage\n");
+
+    delete[] target;
+
+    return true;
+}
+
+HRESULT CvVideoWriter_MSMF::InitializeSinkWriter(const char* filename)
+{
+    ComPtr<IMFAttributes> spAttr;
+    ComPtr<IMFMediaType>  mediaTypeOut;
+    ComPtr<IMFMediaType>  mediaTypeIn;
+    ComPtr<IMFByteStream> spByteStream;
+
+    MFCreateAttributes(&spAttr, 10);
+    spAttr->SetUINT32(MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, true);
+
+    wchar_t* unicodeFileName = new wchar_t[strlen(filename)+1];
+    MultiByteToWideChar(CP_ACP, 0, filename, -1, unicodeFileName, strlen(filename)+1);
+
+    HRESULT hr = MFCreateSinkWriterFromURL(unicodeFileName, NULL, spAttr.Get(), &sinkWriter);
+
+    delete[] unicodeFileName;
+
+    // Set the output media type.
+    if (SUCCEEDED(hr))
+    {
+        printf("MFCreateSinkWriterFromURL is successfull\n");
+        hr = MFCreateMediaType(&mediaTypeOut);
+    }
+    if (SUCCEEDED(hr))
+    {
+        hr = mediaTypeOut->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
+    }
+    if (SUCCEEDED(hr))
+    {
+        hr = mediaTypeOut->SetGUID(MF_MT_SUBTYPE, encodingFormat);
+    }
+    if (SUCCEEDED(hr))
+    {
+        hr = mediaTypeOut->SetUINT32(MF_MT_AVG_BITRATE, bitRate);
+    }
+    if (SUCCEEDED(hr))
+    {
+        hr = mediaTypeOut->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
+    }
+    if (SUCCEEDED(hr))
+    {
+        hr = MFSetAttributeSize(mediaTypeOut.Get(), MF_MT_FRAME_SIZE, videoWidth, videoHeight);
+    }
+    if (SUCCEEDED(hr))
+    {
+        hr = MFSetAttributeRatio(mediaTypeOut.Get(), MF_MT_FRAME_RATE, fps, 1);
+    }
+    if (SUCCEEDED(hr))
+    {
+        hr = MFSetAttributeRatio(mediaTypeOut.Get(), MF_MT_PIXEL_ASPECT_RATIO, 1, 1);
+    }
+    if (SUCCEEDED(hr))
+    {
+        hr = sinkWriter->AddStream(mediaTypeOut.Get(), &streamIndex);
+    }
+
+    // Set the input media type.
+    if (SUCCEEDED(hr))
+    {
+        hr = MFCreateMediaType(&mediaTypeIn);
+    }
+    if (SUCCEEDED(hr))
+    {
+        hr = mediaTypeIn->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
+    }
+    if (SUCCEEDED(hr))
+    {
+        hr = mediaTypeIn->SetGUID(MF_MT_SUBTYPE, inputFormat);
+    }
+    if (SUCCEEDED(hr))
+    {
+        hr = mediaTypeIn->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
+    }
+    if (SUCCEEDED(hr))
+    {
+        hr = MFSetAttributeSize(mediaTypeIn.Get(), MF_MT_FRAME_SIZE, videoWidth, videoHeight);
+    }
+    if (SUCCEEDED(hr))
+    {
+        hr = MFSetAttributeRatio(mediaTypeIn.Get(), MF_MT_FRAME_RATE, fps, 1);
+    }
+    if (SUCCEEDED(hr))
+    {
+        hr = MFSetAttributeRatio(mediaTypeIn.Get(), MF_MT_PIXEL_ASPECT_RATIO, 1, 1);
+    }
+    if (SUCCEEDED(hr))
+    {
+        hr = sinkWriter->SetInputMediaType(streamIndex, mediaTypeIn.Get(), NULL);
+    }
+
+    // Tell the sink writer to start accepting data.
+    if (SUCCEEDED(hr))
+    {
+        hr = sinkWriter->BeginWriting();
+    }
+
+    return hr;
+}
+
+HRESULT CvVideoWriter_MSMF::WriteFrame(DWORD *videoFrameBuffer, const LONGLONG& Start, const LONGLONG& Duration)
+{
+    printf("Private WriteFrame(%p, %llu, %llu)\n", videoFrameBuffer, Start, Duration);
+    IMFSample* sample;
+    IMFMediaBuffer* buffer;
+
+    const LONG cbWidth = 4 * videoWidth;
+    const DWORD cbBuffer = cbWidth * videoHeight;
+
+    BYTE *pData = NULL;
+
+    // Create a new memory buffer.
+    HRESULT hr = MFCreateMemoryBuffer(cbBuffer, &buffer);
+
+    // Lock the buffer and copy the video frame to the buffer.
+    if (SUCCEEDED(hr))
+    {
+        printf("MFCreateMemoryBuffer successfull\n");
+        hr = buffer->Lock(&pData, NULL, NULL);
+    }
+
+    if (SUCCEEDED(hr))
+    {
+        printf("Before MFCopyImage(%p, %d, %p, %d, %d %d)\n", pData, cbWidth, videoFrameBuffer, cbWidth, cbWidth, videoHeight);
+        hr = MFCopyImage(
+            pData,                      // Destination buffer.
+            cbWidth,                    // Destination stride.
+            (BYTE*)videoFrameBuffer,    // First row in source image.
+            cbWidth,                    // Source stride.
+            cbWidth,                    // Image width in bytes.
+            videoHeight                 // Image height in pixels.
+            );
+        printf("After MFCopyImage()\n");
+    }
+
+    printf("Before buffer.Get()\n");
+    if (buffer)
+    {
+        printf("Before buffer->Unlock\n");
+        buffer->Unlock();
+        printf("After buffer->Unlock\n");
+    }
+
+    // Set the data length of the buffer.
+    if (SUCCEEDED(hr))
+    {
+        printf("MFCopyImage successfull\n");
+        hr = buffer->SetCurrentLength(cbBuffer);
+    }
+
+    // Create a media sample and add the buffer to the sample.
+    if (SUCCEEDED(hr))
+    {
+        hr = MFCreateSample(&sample);
+    }
+    if (SUCCEEDED(hr))
+    {
+        hr = sample->AddBuffer(buffer);
+    }
+
+    // Set the time stamp and the duration.
+    if (SUCCEEDED(hr))
+    {
+        printf("Sample time: %d\n", Start);
+        hr = sample->SetSampleTime(Start);
+    }
+    if (SUCCEEDED(hr))
+    {
+        printf("Duration: %d\n", Duration);
+        hr = sample->SetSampleDuration(Duration);
+    }
+
+    // Send the sample to the Sink Writer.
+    if (SUCCEEDED(hr))
+    {
+        printf("Setting writer params successfull\n");
+        hr = sinkWriter->WriteSample(streamIndex, sample);
+    }
+
+    printf("Private WriteFrame(%d, %p) end with status %u\n", streamIndex, sample, hr);
+
+    SafeRelease(&sample);
+    SafeRelease(&buffer);
+
+    return hr;
+}
+
+CvVideoWriter* cvCreateVideoWriter_MSMF( const char* filename, int fourcc,
+                                        double fps, CvSize frameSize, int isColor )
+{
+    printf("Creating Media Foundation VideoWriter\n");
+    CvVideoWriter_MSMF* writer = new CvVideoWriter_MSMF;
+    if( writer->open( filename, fourcc, fps, frameSize, isColor != 0 ))
+        return writer;
+    delete writer;
+    return NULL;
+}
+
 #endif
\ No newline at end of file
index aa327d6..b989695 100644 (file)
@@ -119,6 +119,8 @@ CvVideoWriter* cvCreateVideoWriter_VFW( const char* filename, int fourcc,
                                         double fps, CvSize frameSize, int is_color );
 CvCapture* cvCreateCameraCapture_DShow( int index );
 CvCapture* cvCreateCameraCapture_MSMF( int index );
+CvVideoWriter* cvCreateVideoWriter_MSMF( const char* filename, int fourcc,
+                                        double fps, CvSize frameSize, int is_color );
 CvCapture* cvCreateCameraCapture_OpenNI( int index );
 CvCapture* cvCreateFileCapture_OpenNI( const char* filename );
 CvCapture* cvCreateCameraCapture_Android( int index );