#include <mfplay.h>
#include <mfobjects.h>
#include "Strsafe.h"
+#include <wrl/client.h>
+#include <Mfreadwrite.h>
#include <new>
#include <map>
#include <vector>
#pragma comment(lib, "mf")
#pragma comment(lib, "mfuuid")
#pragma comment(lib, "Strmiids")
+#pragma comment(lib, "Mfreadwrite")
#pragma comment(lib, "MinCore_Downlevel")
struct IMFMediaType;
DWORD WINAPI MainThreadFunction( LPVOID lpParam );
typedef void(*emergensyStopEventCallback)(int, void *);
-typedef unsigned char BYTE;
class RawImage
{
SafeRelease(&pEvent);
SafeRelease(&ig_pSession);
SafeRelease(&ig_pTopology);
-
+
return hr;
}
SafeRelease(&pPD);
SafeRelease(&pSD);
SafeRelease(&pHandler);
-
+
return hr;
}
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