Reputation: 53
I'm trying to get my IMFSinkWriter's Finalize() call to be asynchronous.
So I created this
class MyMFSinkWriterCallback : public IMFSinkWriterCallback
{
public:
MyMFSinkWriterCallback();
virtual ~MyMFSinkWriterCallback();
// IMFSinkWriterCallback methods
STDMETHODIMP OnFinalize(HRESULT hrStatus);
STDMETHODIMP OnMarker(DWORD dwStreamIndex, LPVOID pvContext);
// IUnknown methods
STDMETHODIMP QueryInterface(REFIID iid, void** ppv);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
private:
long m_nRefCount;
};
HRESULT MyMFSinkWriterCallback::OnFinalize(HRESULT hrStatus)
{
OutputDebugStringA("MyMFSinkWriterCallback::OnFinalize\n");
return S_OK;
}
And I installed it like this
hr = pAttributes->SetUnknown(MF_SINK_WRITER_ASYNC_CALLBACK, new MyMFSinkWriterCallback());
if (SUCCEEDED(hr))
{
hr = MFCreateSinkWriterFromURL(L"C:\test.mp4", NULL, pAttributes, &MySinkWriter);
}
QueryInterface, AddRef, and Release are all being called but when I end my capture like this
OutputDebugStringA("MySinkWriter->Finalize START\n");
HRESULT hr = MySinkWriter->Finalize();
OutputDebugStringA("MySinkWriter->Finalize END\n");
All I see in my output log is
MySinkWriter->Finalize START
MySinkWriter->Finalize END
I never see
MyMFSinkWriterCallback::OnFinalize
Upvotes: 1
Views: 344
Reputation: 1515
Perhaps you don't let time to "OnFinalize" to execute. Here is a working sample code (based on Sink Writer Tutorial) :
#include <Windows.h>
#include <mfapi.h>
#include <mfidl.h>
#include <Mfreadwrite.h>
#include <mferror.h>
#include <Shlwapi.h>
#include <new>
#pragma comment(lib, "mfreadwrite")
#pragma comment(lib, "mfplat")
#pragma comment(lib, "mfuuid")
#pragma comment(lib, "Shlwapi")
template <class T> void SafeRelease(T **ppT){
if(*ppT){
(*ppT)->Release();
*ppT = NULL;
}
}
// Format constants
const UINT32 VIDEO_WIDTH = 640;
const UINT32 VIDEO_HEIGHT = 480;
const UINT32 VIDEO_FPS = 30;
const UINT64 VIDEO_FRAME_DURATION = 10 * 1000 * 1000 / VIDEO_FPS;
const UINT32 VIDEO_BIT_RATE = 800000;
const GUID VIDEO_ENCODING_FORMAT = MFVideoFormat_WMV3;
const GUID VIDEO_INPUT_FORMAT = MFVideoFormat_RGB32;
const UINT32 VIDEO_PELS = VIDEO_WIDTH * VIDEO_HEIGHT;
const UINT32 VIDEO_FRAME_COUNT = 20 * VIDEO_FPS;
// Buffer to hold the video frame data.
DWORD videoFrameBuffer[VIDEO_PELS];
class CMFSinkWriterCallback : public IMFSinkWriterCallback{
public:
CMFSinkWriterCallback(HANDLE hFinalizeEvent) : m_nRefCount(1), m_hFinalizeEvent(hFinalizeEvent){}
virtual ~CMFSinkWriterCallback(){}
// IMFSinkWriterCallback methods
STDMETHODIMP OnFinalize(HRESULT hrStatus){
OutputDebugString(L"CMFSinkWriterCallback::OnFinalize\n");
if(m_hFinalizeEvent != NULL){
SetEvent(m_hFinalizeEvent);
}
return hrStatus;
}
STDMETHODIMP OnMarker(DWORD dwStreamIndex, LPVOID pvContext){
return S_OK;
}
// IUnknown methods
STDMETHODIMP QueryInterface(REFIID riid, void** ppv){
static const QITAB qit[] = {
QITABENT(CMFSinkWriterCallback, IMFSinkWriterCallback),
{0}
};
return QISearch(this, qit, riid, ppv);
}
STDMETHODIMP_(ULONG) AddRef(){
return InterlockedIncrement(&m_nRefCount);
}
STDMETHODIMP_(ULONG) Release(){
ULONG refCount = InterlockedDecrement(&m_nRefCount);
if(refCount == 0){
delete this;
}
return refCount;
}
private:
volatile long m_nRefCount;
HANDLE m_hFinalizeEvent;
};
HRESULT InitializeSinkWriter(IMFSinkWriter **ppWriter, DWORD *pStreamIndex, IMFSinkWriterCallback* pCallBack){
*ppWriter = NULL;
*pStreamIndex = NULL;
IMFAttributes *pAttributes = NULL;
IMFSinkWriter *pSinkWriter = NULL;
IMFMediaType *pMediaTypeOut = NULL;
IMFMediaType *pMediaTypeIn = NULL;
DWORD streamIndex = 0;
// Create the empty attribute store.
HRESULT hr = MFCreateAttributes(&pAttributes, 1);
if(SUCCEEDED(hr)){
hr = pAttributes->SetUnknown(MF_SINK_WRITER_ASYNC_CALLBACK, pCallBack);
}
if(SUCCEEDED(hr)){
hr = MFCreateSinkWriterFromURL(L"output.wmv", NULL, pAttributes, &pSinkWriter);
}
// Set the output media type.
if(SUCCEEDED(hr)){
hr = MFCreateMediaType(&pMediaTypeOut);
}
if(SUCCEEDED(hr)){
hr = pMediaTypeOut->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
}
if(SUCCEEDED(hr)){
hr = pMediaTypeOut->SetGUID(MF_MT_SUBTYPE, VIDEO_ENCODING_FORMAT);
}
if(SUCCEEDED(hr)){
hr = pMediaTypeOut->SetUINT32(MF_MT_AVG_BITRATE, VIDEO_BIT_RATE);
}
if(SUCCEEDED(hr)){
hr = pMediaTypeOut->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
}
if(SUCCEEDED(hr)){
hr = MFSetAttributeSize(pMediaTypeOut, MF_MT_FRAME_SIZE, VIDEO_WIDTH, VIDEO_HEIGHT);
}
if(SUCCEEDED(hr)){
hr = MFSetAttributeRatio(pMediaTypeOut, MF_MT_FRAME_RATE, VIDEO_FPS, 1);
}
if(SUCCEEDED(hr)){
hr = MFSetAttributeRatio(pMediaTypeOut, MF_MT_PIXEL_ASPECT_RATIO, 1, 1);
}
if(SUCCEEDED(hr)){
hr = pSinkWriter->AddStream(pMediaTypeOut, &streamIndex);
}
// Set the input media type.
if(SUCCEEDED(hr)){
hr = MFCreateMediaType(&pMediaTypeIn);
}
if(SUCCEEDED(hr)){
hr = pMediaTypeIn->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
}
if(SUCCEEDED(hr)){
hr = pMediaTypeIn->SetGUID(MF_MT_SUBTYPE, VIDEO_INPUT_FORMAT);
}
if(SUCCEEDED(hr)){
hr = pMediaTypeIn->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
}
if(SUCCEEDED(hr)){
hr = MFSetAttributeSize(pMediaTypeIn, MF_MT_FRAME_SIZE, VIDEO_WIDTH, VIDEO_HEIGHT);
}
if(SUCCEEDED(hr)){
hr = MFSetAttributeRatio(pMediaTypeIn, MF_MT_FRAME_RATE, VIDEO_FPS, 1);
}
if(SUCCEEDED(hr)){
hr = MFSetAttributeRatio(pMediaTypeIn, MF_MT_PIXEL_ASPECT_RATIO, 1, 1);
}
if(SUCCEEDED(hr)){
hr = pSinkWriter->SetInputMediaType(streamIndex, pMediaTypeIn, NULL);
}
// Tell the sink writer to start accepting data.
if(SUCCEEDED(hr)){
hr = pSinkWriter->BeginWriting();
}
// Return the pointer to the caller.
if(SUCCEEDED(hr)){
*ppWriter = pSinkWriter;
(*ppWriter)->AddRef();
*pStreamIndex = streamIndex;
}
SafeRelease(&pAttributes);
SafeRelease(&pSinkWriter);
SafeRelease(&pMediaTypeOut);
SafeRelease(&pMediaTypeIn);
return hr;
}
HRESULT WriteFrame(IMFSinkWriter *pWriter, DWORD streamIndex, const LONGLONG& rtStart){
IMFSample *pSample = NULL;
IMFMediaBuffer *pBuffer = NULL;
const LONG cbWidth = 4 * VIDEO_WIDTH;
const DWORD cbBuffer = cbWidth * VIDEO_HEIGHT;
BYTE *pData = NULL;
// Create a new memory buffer.
HRESULT hr = MFCreateMemoryBuffer(cbBuffer, &pBuffer);
// Lock the buffer and copy the video frame to the buffer.
if(SUCCEEDED(hr)){
hr = pBuffer->Lock(&pData, NULL, NULL);
}
if(SUCCEEDED(hr)){
hr = MFCopyImage(pData, cbWidth, (BYTE*)videoFrameBuffer, cbWidth, cbWidth, VIDEO_HEIGHT);
}
if(pBuffer){
pBuffer->Unlock();
}
// Set the data length of the buffer.
if(SUCCEEDED(hr)){
hr = pBuffer->SetCurrentLength(cbBuffer);
}
// Create a media sample and add the buffer to the sample.
if(SUCCEEDED(hr)){
hr = MFCreateSample(&pSample);
}
if(SUCCEEDED(hr)){
hr = pSample->AddBuffer(pBuffer);
}
// Set the time stamp and the duration.
if(SUCCEEDED(hr)){
hr = pSample->SetSampleTime(rtStart);
}
if(SUCCEEDED(hr)){
hr = pSample->SetSampleDuration(VIDEO_FRAME_DURATION);
}
// Send the sample to the Sink Writer.
if(SUCCEEDED(hr)){
hr = pWriter->WriteSample(streamIndex, pSample);
}
SafeRelease(&pSample);
SafeRelease(&pBuffer);
return hr;
}
void main(){
HANDLE hFinalizeEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if(hFinalizeEvent == NULL)
return;
// Set all pixels to green
for(DWORD i = 0; i < VIDEO_PELS; ++i){
videoFrameBuffer[i] = 0x000000FF;
}
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
if(SUCCEEDED(hr)){
hr = MFStartup(MF_VERSION);
if(SUCCEEDED(hr)){
IMFSinkWriterCallback* pCallBack = NULL;
IMFSinkWriter *pSinkWriter = NULL;
DWORD stream;
pCallBack = new (std::nothrow)CMFSinkWriterCallback(hFinalizeEvent);
hr = pCallBack ? S_OK : E_POINTER;
if(SUCCEEDED(hr)){
hr = InitializeSinkWriter(&pSinkWriter, &stream, pCallBack);
}
if(SUCCEEDED(hr)){
// Send frames to the sink writer.
LONGLONG rtStart = 0;
for(DWORD i = 0; i < VIDEO_FRAME_COUNT; ++i){
hr = WriteFrame(pSinkWriter, stream, rtStart);
if(FAILED(hr)){
break;
}
rtStart += VIDEO_FRAME_DURATION;
}
}
if(SUCCEEDED(hr)){
hr = pSinkWriter->Finalize();
WaitForSingleObject(hFinalizeEvent, INFINITE);
}
SafeRelease(&pCallBack);
SafeRelease(&pSinkWriter);
MFShutdown();
}
CoUninitialize();
}
CloseHandle(hFinalizeEvent);
}
Upvotes: 1