Example #1
0
HRESULT CMFCamCapture::stop()
{
    HRESULT hr = S_OK;
    IMFMediaEvent *pEvent = NULL;
    m_spSession->Stop();
    
    // wait for session to finish.
    while (1) {
        HRESULT hrStatus = S_OK;
        MediaEventType met;

        CHECK_HR(hr = m_spSession->GetEvent(0, &pEvent));
        CHECK_HR(hr = pEvent->GetStatus(&hrStatus));
        CHECK_HR(hr = pEvent->GetType(&met));

        if (FAILED(hrStatus)) {
            DBGMSG(L"Session error: 0x%x (event id: %d)\n", hrStatus, met);
            hr = hrStatus;
            goto done;
        }
        if (met == MESessionEnded || met == MESessionStopped) {
            break;
        }
        SafeRelease(&pEvent);
    }
    done:
    return hr;
}
HRESULT CPlayer::Invoke(IMFAsyncResult *pResult)
{
    HRESULT hr = S_OK;
    MediaEventType meType = MEUnknown;  // Event type

    IMFMediaEvent *pEvent = NULL;

    // Get the event from the event queue.
    CHECK_HR(hr = m_pSession->EndGetEvent(pResult, &pEvent));

    // Get the event type. 
    CHECK_HR(hr = pEvent->GetType(&meType));

    // If the session is closed, the application is waiting on the
    // m_hCloseEvent event handle. Also, do not get any more 
    // events from the session.

    if (meType == MESessionClosed)
    {
        SetEvent(m_hCloseEvent);
    }
    else
    {
        // For all other events, ask the media session for the
        // next event in the queue.
        CHECK_HR(hr = m_pSession->BeginGetEvent(this, NULL));
    }

    // For most events, we post the event as a private window message to the
    // application. This lets the application process the event on it's
    // main thread.

    // However, if call to IMFMediaSession::Close is pending, it means the 
    // application is waiting on the m_hCloseEvent event handle. (Blocking 
    // call.) In that case, we simply discard the event.

    // NOTE: When IMFMediaSession::Close is called, MESessionClosed is NOT
    // necessarily the next event that we will receive. We may receive
    // any number of other events before receiving MESessionClosed.
    if (m_state != Closing)
    {
        // Leave a reference count on the event.
        pEvent->AddRef();
        PostMessage(m_hwndEvent, WM_APP_PLAYER_EVENT, (WPARAM)pEvent, (LPARAM)0);
    }

done:
    SAFE_RELEASE(pEvent);
    return S_OK;
}
// Process any pending output from the decoder (until all output is
// processed).
//
// Thread context: decoder thread
bool DecoderMF::DoProcessOutput()
{
	bool						ret = false;
	HRESULT						hr;
	MFT_OUTPUT_DATA_BUFFER		mftDataBuffer;
	DWORD						mftStatus;
	bool						moreOutput;

	if (m_outputSample == NULL)
	{
		if (! CreateOutputSample())
			return false;
	}

	do
	{
		// Since we could be looping inside this method for a while,
		// if a whole stack of frames arrive at once, we want to exit
		// if the thread has been asked to die. So check on each
		// iteration of the loop.
		if (! m_decoderThreadRunning)
			return true;

		moreOutput = false;

		mftDataBuffer.dwStreamID = 0;
		mftDataBuffer.pSample = m_outputSample;
		mftDataBuffer.dwStatus = 0;
		mftDataBuffer.pEvents = NULL;
		mftStatus = 0;

		// Looks like we have to reset the sample before use:
		IMFMediaBuffer* mediaBuffer;
		hr = m_outputSample->GetBufferByIndex(0, &mediaBuffer);
		if (FAILED(hr))
			goto bail;

		hr = mediaBuffer->SetCurrentLength(0);
		if (FAILED(hr))
			goto bail;

		mediaBuffer->Release();

		hr = m_h264Decoder->ProcessOutput(0, 1, &mftDataBuffer, &mftStatus);

		// Check return code
		if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT)
			break;
		EnterCriticalSection(&m_criticalSection);
		if (hr == MF_E_TRANSFORM_STREAM_CHANGE || !m_previewConfigured)
		{
			// If the output format has changed, we need to handle
			// the stream change. This will happen after the first
			// few packets have been delivered.
			moreOutput = HandleStreamChange();
			LeaveCriticalSection(&m_criticalSection);
			if (!moreOutput)
				goto bail;
			continue;
		}
		LeaveCriticalSection(&m_criticalSection);
		if (FAILED(hr))
			goto bail;

		if (mftDataBuffer.dwStatus == MFT_OUTPUT_DATA_BUFFER_INCOMPLETE)
			moreOutput = true;

		// Process each event:
		if (mftDataBuffer.pEvents != NULL)
		{
			DWORD numElements;
			hr = mftDataBuffer.pEvents->GetElementCount(&numElements);
			if (SUCCEEDED(hr))
			{
				for (DWORD i = 0; i < numElements; i++)
				{
					IUnknown* iunk = NULL;

					hr = mftDataBuffer.pEvents->GetElement(i, &iunk);
					if (SUCCEEDED(hr))
					{
						IMFMediaEvent* mediaEvent = NULL;
						hr = iunk->QueryInterface(IID_IMFMediaEvent, (void**)&mediaEvent);

						if (SUCCEEDED(hr))
						{
							OutputDebugString(_T("FIXME: process event!\n"));

							mediaEvent->Release();
						}

						iunk->Release();
					}
				}
			}

			mftDataBuffer.pEvents = NULL;
		}

		// Process sample:
		if (mftDataBuffer.pSample != NULL)
		{
			IMFMediaBuffer* mediaBuffer;
			hr = mftDataBuffer.pSample->GetBufferByIndex(0, &mediaBuffer);
			if (FAILED(hr))
				goto bail;

			EnterCriticalSection(&m_criticalSection);

				if (m_previewWindow != NULL && m_previewConfigured)
					m_previewWindow->DrawFrame(mediaBuffer);

			LeaveCriticalSection(&m_criticalSection);

			mediaBuffer->Release();
		}
	} while(moreOutput);

	ret = true;

bail:
	if (ret == false)
		OutputDebugString(_T("ERROR: failed to process output...\n"));

	return ret;
}
HRESULT WavSource::Start(
    IMFPresentationDescriptor* pPresentationDescriptor,
    const GUID* pguidTimeFormat,
    const PROPVARIANT* pvarStartPosition
    )
{
    HRESULT hr = S_OK;
    LONGLONG llStartOffset = 0;
    BOOL bIsSeek = FALSE;    
    BOOL bIsRestartFromCurrentPosition = FALSE;     
    BOOL bQueuedStartEvent = FALSE;

    IMFMediaEvent *pEvent = NULL;

    PROPVARIANT var;
    PropVariantInit(&var);

    // Check parameters. 
    // Start position and presentation descriptor cannot be NULL.
    if (pvarStartPosition == NULL || pPresentationDescriptor == NULL)
    {
        return E_INVALIDARG;
    }

    // Check the time format. Must be "reference time" units.
    if ((pguidTimeFormat != NULL) && (*pguidTimeFormat != GUID_NULL))
    {
        // Unrecognized time format GUID.
        return MF_E_UNSUPPORTED_TIME_FORMAT;
    }

    EnterCriticalSection(&m_critSec);

    // Fail if the source is shut down.
    hr = CheckShutdown();

    if (FAILED(hr)) { goto done; }

    // Check the start position.
    if (pvarStartPosition->vt == VT_I8)
    {
        // Start position is given in pvarStartPosition in 100-ns units.
        llStartOffset = pvarStartPosition->hVal.QuadPart;

        if (m_state != STATE_STOPPED)
        {
            // Source is running or paused, so this is a seek.
            bIsSeek = TRUE;
        }
    }
    else if (pvarStartPosition->vt == VT_EMPTY)
    {
        // Start position is "current position". 
        // For stopped, that means 0. Otherwise, use the current position.
        if (m_state == STATE_STOPPED)
        {
            llStartOffset = 0;
        }
        else
        {
            llStartOffset = GetCurrentPosition();
            bIsRestartFromCurrentPosition = TRUE;
        }
    }
    else
    {
        // We don't support this time format.
        hr =  MF_E_UNSUPPORTED_TIME_FORMAT;
        goto done;
    }

    // Validate the caller's presentation descriptor.
    hr = ValidatePresentationDescriptor(pPresentationDescriptor);

    if (FAILED(hr)) { goto done; }

    // Sends the MENewStream or MEUpdatedStream event.
    hr = QueueNewStreamEvent(pPresentationDescriptor);

    if (FAILED(hr)) { goto done; }

    // Notify the stream of the new start time.
    hr = m_pStream->SetPosition(llStartOffset);

    if (FAILED(hr)) { goto done; }

    // Send Started or Seeked events. 

    var.vt = VT_I8;
    var.hVal.QuadPart = llStartOffset;

    // Send the source event.
    if (bIsSeek)
    {
        hr = QueueEvent(MESourceSeeked, GUID_NULL, hr, &var);

        if (FAILED(hr)) { goto done; }
    }
    else
    {
        // For starting, if we are RESTARTING from the current position and our 
        // previous state was running/paused, then we need to add the 
        // MF_EVENT_SOURCE_ACTUAL_START attribute to the event. This requires
        // creating the event object first.

        // Create the event.
        hr = MFCreateMediaEvent(MESourceStarted, GUID_NULL, hr, &var, &pEvent);

        if (FAILED(hr)) { goto done; }

        // For restarts, set the actual start time as an attribute.
        if (bIsRestartFromCurrentPosition)
        {
            hr = pEvent->SetUINT64(MF_EVENT_SOURCE_ACTUAL_START, llStartOffset);
            if (FAILED(hr)) { goto done; }
        }
        
        // Now  queue the event.
        hr = m_pEventQueue->QueueEvent(pEvent);

        if (FAILED(hr)) { goto done; }
    }

    bQueuedStartEvent = TRUE;

    // Send the stream event.
    if (m_pStream)
    {
        if (bIsSeek)
        {
            hr = m_pStream->QueueEvent(MEStreamSeeked, GUID_NULL, hr, &var);
        }
        else
        {
            hr = m_pStream->QueueEvent(MEStreamStarted, GUID_NULL, hr, &var);
        }
    
        if (FAILED(hr)) { goto done; }
    }

    if (bIsSeek)
    {
        // For seek requests, flush any queued samples.
        hr = m_pStream->Flush();
    }
    else
    {
        // Otherwise, deliver any queued samples.
        hr = m_pStream->DeliverQueuedSamples();
    }
    
    if (FAILED(hr)) { goto done; }

    m_state = STATE_STARTED;

done:

    // If a failure occurred and we have not sent the 
    // MESourceStarted/MESourceSeeked event yet, then it is 
    // OK just to return an error code from Start().

    // If a failure occurred and we have already sent the 
    // event (with a success code), then we need to raise an
    // MEError event.

    if (FAILED(hr) && bQueuedStartEvent)
    {
        hr = QueueEvent(MEError, GUID_NULL, hr, &var);
    }

    PropVariantClear(&var);
    SafeRelease(&pEvent);

    LeaveCriticalSection(&m_critSec);

    return hr;
}
// Handle an event from the capture engine. 
// NOTE: This method is called from the application's UI thread. 
HRESULT CaptureManager::OnCaptureEvent(WPARAM wParam, LPARAM lParam)
{
    GUID guidType;
    HRESULT hrStatus;

    IMFMediaEvent *pEvent = reinterpret_cast<IMFMediaEvent*>(wParam);

    HRESULT hr = pEvent->GetStatus(&hrStatus);
    if (FAILED(hr))
    {
        hrStatus = hr;
    }

    hr = pEvent->GetExtendedType(&guidType);
    if (SUCCEEDED(hr))
    {

#ifdef _DEBUG
        LPOLESTR str;
        if (SUCCEEDED(StringFromCLSID(guidType, &str)))
        {
            DBGMSG((L"MF_CAPTURE_ENGINE_EVENT: %s (hr = 0x%X)\n", str, hrStatus));
            CoTaskMemFree(str);
        }
#endif

        if (guidType == MF_CAPTURE_ENGINE_INITIALIZED)
        {
            OnCaptureEngineInitialized(hrStatus);
            SetErrorID(hrStatus, IDS_ERR_INITIALIZE);
        }
        else if (guidType == MF_CAPTURE_ENGINE_PREVIEW_STARTED)
        {
            OnPreviewStarted(hrStatus);
            SetErrorID(hrStatus, IDS_ERR_PREVIEW);
        }
        else if (guidType == MF_CAPTURE_ENGINE_PREVIEW_STOPPED)
        {
            OnPreviewStopped(hrStatus);
            SetErrorID(hrStatus, IDS_ERR_PREVIEW);
        }
        else if (guidType == MF_CAPTURE_ENGINE_RECORD_STARTED)
        {
            OnRecordStarted(hrStatus);
            SetErrorID(hrStatus, IDS_ERR_RECORD);
        }
        else if (guidType == MF_CAPTURE_ENGINE_RECORD_STOPPED)
        {
            OnRecordStopped(hrStatus);
            SetErrorID(hrStatus, IDS_ERR_RECORD);
        }
        else if (guidType == MF_CAPTURE_ENGINE_PHOTO_TAKEN)
        {
            m_bPhotoPending = false;
            SetErrorID(hrStatus, IDS_ERR_PHOTO);
        }
        else if (guidType == MF_CAPTURE_ENGINE_ERROR)
        {
            DestroyCaptureEngine();
            SetErrorID(hrStatus, IDS_ERR_CAPTURE);
        }
        else if (FAILED(hrStatus))
        {
            SetErrorID(hrStatus, IDS_ERR_CAPTURE);
        }
    }

    pEvent->Release();
    SetEvent(m_hEvent);
    return hrStatus;
}
HRESULT CHWMFT::ProcessOutput(
    DWORD                   dwFlags,
    DWORD                   dwOutputBufferCount,
    MFT_OUTPUT_DATA_BUFFER* pOutputSamples,
    DWORD*                  pdwStatus)
{
    /*****************************************
    ** See http://msdn.microsoft.com/en-us/library/ms704014(v=VS.85).aspx
    *****************************************/

    HRESULT     hr      = S_OK;
    IMFSample*  pSample = NULL;

    TraceString(CHMFTTracing::TRACE_INFORMATION, L"%S(): Enter",  __FUNCTION__);

    do
    {
        if(IsLocked() != FALSE)
        {
            hr = MF_E_TRANSFORM_ASYNC_LOCKED;
            break;
        }

        {
            CAutoLock lock(&m_csLock);

            TraceString(CHMFTTracing::TRACE_INFORMATION, L"%S(): HaveOutputCount: %u",  __FUNCTION__, m_dwHaveOutputCount);

            if(m_dwHaveOutputCount == 0)
            {
                // This call does not correspond to a have output call

                hr = E_UNEXPECTED;
                break;
            }
            else
            {
                m_dwHaveOutputCount--;
            }
        }

        /*****************************************
        ** Todo: If your MFT supports more than one
        ** stream, make sure you modify
        ** MFT_MAX_STREAMS and adjust this function
        ** accordingly
        *****************************************/
        if(dwOutputBufferCount < MFT_MAX_STREAMS)
        {
            hr = E_INVALIDARG;
            break;
        }

        if(IsMFTReady() == FALSE)
        {
            hr = MF_E_TRANSFORM_TYPE_NOT_SET;
            break;
        }

        /***************************************
        ** Since this in an internal function
        ** we know m_pOutputSampleQueue can never be
        ** NULL due to InitializeTransform()
        ***************************************/
        hr = m_pOutputSampleQueue->GetNextSample(&pSample);
        if(FAILED(hr))
        {
            break;
        }

        if(pSample == NULL)
        {
            hr = MF_E_TRANSFORM_NEED_MORE_INPUT;
            break;
        }

        /*******************************
        ** Todo: This MFT only has one
        ** input stream, so the output
        ** samples array and stream ID
        ** will only use the first
        ** member
        *******************************/
        pOutputSamples[0].dwStreamID    = 0;
        
        if((pOutputSamples[0].pSample) == NULL)
        {
            // The MFT is providing it's own samples
            (pOutputSamples[0].pSample)   = pSample;
            (pOutputSamples[0].pSample)->AddRef();
        }
        else
        {
            // The pipeline has allocated the samples
            IMFMediaBuffer* pBuffer = NULL;

            do
            {
                hr = pSample->ConvertToContiguousBuffer(&pBuffer);
                if(FAILED(hr))
                {
                    break;
                }

                hr = (pOutputSamples[0].pSample)->AddBuffer(pBuffer);
                if(FAILED(hr))
                {
                    break;
                }
            }while(false);

            SAFERELEASE(pBuffer);

            if(FAILED(hr))
            {
                break;
            }
        }

        /***************************************
        ** Since this in an internal function
        ** we know m_pOutputSampleQueue can never be
        ** NULL due to InitializeTransform()
        ***************************************/
        if(m_pOutputSampleQueue->IsQueueEmpty() != FALSE)
        {
            // We're out of samples in the output queue
            CAutoLock lock(&m_csLock);

            if((m_dwStatus & MYMFT_STATUS_DRAINING) != 0)
            {
                // We're done draining, time to send the event
                IMFMediaEvent*  pDrainCompleteEvent  = NULL;

                do
                {
                    hr = MFCreateMediaEvent(METransformDrainComplete , GUID_NULL, S_OK, NULL, &pDrainCompleteEvent);
                    if(FAILED(hr))
                    {
                        break;
                    }

                    /*******************************
                    ** Todo: This MFT only has one
                    ** input stream, so the drain
                    ** is always on stream zero.
                    ** Update this is your MFT
                    ** has more than one stream
                    *******************************/
                    hr = pDrainCompleteEvent->SetUINT32(MF_EVENT_MFT_INPUT_STREAM_ID, 0);
                    if(FAILED(hr))
                    {
                        break;
                    }

                    /***************************************
                    ** Since this in an internal function
                    ** we know m_pEventQueue can never be
                    ** NULL due to InitializeTransform()
                    ***************************************/
                    hr = m_pEventQueue->QueueEvent(pDrainCompleteEvent);
                    if(FAILED(hr))
                    {
                        break;
                    }
                }while(false);

                SAFERELEASE(pDrainCompleteEvent);

                if(FAILED(hr))
                {
                    break;
                }

                m_dwStatus &= (~MYMFT_STATUS_DRAINING);
            }
        }
    }while(false);

    SAFERELEASE(pSample);

    TraceString(CHMFTTracing::TRACE_INFORMATION, L"%S(): Exit (hr=0x%x)",  __FUNCTION__, hr);

    return hr;
}
HRESULT CPlayer::HandleEvent(UINT_PTR pEventPtr)
{
    HRESULT hrStatus = S_OK;
	HRESULT hr = S_OK;
    MediaEventType meType = MEUnknown;  

    IMFMediaEvent *pEvent = (IMFMediaEvent*)pEventPtr;

    if (pEvent == NULL)
    {
        return E_POINTER;
    }

    // Get the event type.
    hr = pEvent->GetType(&meType);
	CHECK_HR( hr );

    // Get the event status. If the operation that triggered the event 
    // did not succeed, the status is a failure code.
    hr = pEvent->GetStatus(&hrStatus);

    // Check if the async operation succeeded.
    if (SUCCEEDED(hr) && FAILED(hrStatus)) 
    {
        hr = hrStatus;
    }
	CHECK_HR( hr );

    switch(meType)
    {
    case MESessionTopologyStatus:
        hr = OnTopologyStatus(pEvent);
        break;

    case MEEndOfPresentation:
        hr = OnPresentationEnded(pEvent);
		CI_LOG_V("Presentation Ended");
        break;

    case MENewPresentation:
        hr = OnNewPresentation(pEvent);
		CI_LOG_V( "New Presentation" );
        break;

	case MESessionTopologySet:
		IMFTopology * topology;
		GetEventObject<IMFTopology> (pEvent,&topology);
		WORD nodeCount;
		topology->GetNodeCount(&nodeCount);
		CI_LOG_V( "Topo set and we have " << nodeCount << " nodes" );
		//cout << "Topo set and we have "  << nodeCount << " nodes" << endl;
		SafeRelease(&topology);
		break;

	case MESessionStarted:
		CI_LOG_V( "Started Session" );
		break;
	case MEBufferingStarted:
		CI_LOG_I( "Buffering..." );
		break;
	case MEBufferingStopped:
		CI_LOG_I( "Finished Buffering..." );
		break;

    default:
        hr = OnSessionEvent(pEvent, meType);
        break;
    }

done:
    SafeRelease(&pEvent);
    return hr;
}
//  Callback for the asynchronous BeginGetEvent method.
HRESULT CPlayer::Invoke(IMFAsyncResult *pResult)
{
    MediaEventType meType = MEUnknown;  // Event type
    IMFMediaEvent *pEvent = NULL;

	HRESULT hr;
	if ( !m_pSession ) {
		CI_LOG_W("Called with a null session");
		return -1; //Sometimes Invoke is called but m_pSession is closed
	}

	// Handle async-loading
	if ( m_state == OpenAsyncPending ) {
		if ( !&m_pSourceResolver ) {
			CI_LOG_E("Async request returned with NULL session");
			//ofLogError( "CPlayer::Invoke" ) << "Async request returned with NULL session";
			return -1;
		}
		MF_OBJECT_TYPE ObjectType = MF_OBJECT_INVALID;
		IUnknown	  *pSourceUnk = NULL;
		//CheckPointer(m_pSource, E_POINTER);

		hr = m_pSourceResolver->EndCreateObjectFromURL(
			pResult,					// Invoke result
			&ObjectType,                // Receives the created object type. 
			&pSourceUnk                  // Receives a pointer to the media source.
			);

		// Get the IMFMediaSource interface from the media source.
		if ( SUCCEEDED( hr ) )
		{
			hr = pSourceUnk->QueryInterface( __uuidof( IMFMediaSource ), (void**) ( &m_pSource ) );
			m_state = OpenAsyncComplete; // Session finished opening URL
		}
		SafeRelease( &pSourceUnk );
		return hr;
	}


    // Get the event from the event queue.
    hr = m_pSession->EndGetEvent(pResult, &pEvent);
	CHECK_HR( hr );

    // Get the event type. 
    hr = pEvent->GetType(&meType);
	CHECK_HR( hr );

    if (meType == MESessionClosed)
    {
        // The session was closed. 
        // The application is waiting on the m_hCloseEvent event handle. 
        SetEvent(m_hCloseEvent);
    }
    else
    {
        // For all other events, get the next event in the queue.
        hr = m_pSession->BeginGetEvent(this, NULL);
		CHECK_HR( hr );
    }

    // Check the application state. 
        
    // If a call to IMFMediaSession::Close is pending, it means the 
    // application is waiting on the m_hCloseEvent event and
    // the application's message loop is blocked. 

    // Otherwise, post a private window message to the application. 

    if (m_state != Closing)
    {
        // Leave a reference count on the event.
        pEvent->AddRef();

        PostMessage(m_hwndEvent, WM_APP_PLAYER_EVENT, 
            (WPARAM)pEvent, (LPARAM)meType);
    }

done:
    SafeRelease(&pEvent);
    return S_OK;
}
HRESULT CPlayer::HandleEvent(UINT_PTR pUnkPtr)
{
    HRESULT hr = S_OK;
    HRESULT hrStatus = S_OK;            // Event status
    MediaEventType meType = MEUnknown;  // Event type
    MF_TOPOSTATUS TopoStatus = MF_TOPOSTATUS_INVALID; // Used with MESessionTopologyStatus event.    

    IUnknown *pUnk = NULL;
    IMFMediaEvent *pEvent = NULL;

    // pUnkPtr is really an IUnknown pointer.
    pUnk = (IUnknown*)pUnkPtr;

    if (pUnk == NULL)
    {
        return E_POINTER;
    }

    CHECK_HR(hr = pUnk->QueryInterface(__uuidof(IMFMediaEvent), (void**)&pEvent));

    // Get the event type.
    CHECK_HR(hr = pEvent->GetType(&meType));

    // Get the event status. If the operation that triggered the event did
    // not succeed, the status is a failure code.
    CHECK_HR(hr = pEvent->GetStatus(&hrStatus));

    TRACE((L"Media event: %s\n", EventName(meType)));

    // Check if the async operation succeeded.
    if (SUCCEEDED(hrStatus))
    {
        // Switch on the event type. Update the internal state of the CPlayer as needed.
        switch(meType)
        {
        case MESessionTopologyStatus:
            // Get the status code.
            CHECK_HR(hr = pEvent->GetUINT32(MF_EVENT_TOPOLOGY_STATUS, (UINT32*)&TopoStatus));
            switch (TopoStatus)
            {
            case MF_TOPOSTATUS_READY: 
                hr = OnTopologyReady(pEvent);
                break;
            default: 
                // Nothing to do.
                break;  
            }
            break;

        case MEEndOfPresentation:
            CHECK_HR(hr = OnPresentationEnded(pEvent));
            break;
        }
    }
    else
    {
        hr = hrStatus;
    }

done:
    SAFE_RELEASE(pUnk);
    SAFE_RELEASE(pEvent);
    return hr;
}
HRESULT CTranscoder::Transcode()
{
    assert (m_pSession);
    
    IMFMediaEvent* pEvent = NULL;
    MediaEventType meType = MEUnknown;  // Event type

    HRESULT hr = S_OK;
    HRESULT hrStatus = S_OK;            // Event status

    //Get media session events synchronously
    while (meType != MESessionClosed)
    {
        hr = m_pSession->GetEvent(0, &pEvent);

        if (FAILED(hr)) { break; }

        // Get the event type.
        hr = pEvent->GetType(&meType);
        
        if (FAILED(hr)) { break; }

        hr = pEvent->GetStatus(&hrStatus);
        
        if (FAILED(hr)) { break; }

        if (FAILED(hrStatus))
        {
            wprintf_s(L"Failed. 0x%X error condition triggered this event.\n", hrStatus);
            hr = hrStatus;
            break;
        }

        switch (meType)
        {
        case MESessionTopologySet:
            hr = Start();
            if (SUCCEEDED(hr))
            {
                wprintf_s(L"Ready to start.\n");
            }
            break;

        case MESessionStarted:
            wprintf_s(L"Started encoding...\n");
            break;

        case MESessionEnded:
            hr = m_pSession->Close();
            if (SUCCEEDED(hr))
            {
                wprintf_s(L"Finished encoding.\n");
            }
            break;

        case MESessionClosed:
            wprintf_s(L"Output file created.\n");
            break;
        }

        if (FAILED(hr))
        {
            break;
        }

        SafeRelease(&pEvent);
    }

    SafeRelease(&pEvent);
    return hr;
}
/** Asyncronous callback */
HRESULT FImfVideoPlayer::Invoke( IMFAsyncResult* AsyncResult )
{
	IMFMediaEvent* Event = NULL;
	HRESULT HResult = MediaSession->EndGetEvent( AsyncResult, &Event );

	if( FAILED( HResult ) )
	{
		Event->Release( );
		return S_OK;
	}

	MediaEventType EventType = MEUnknown;
	HResult = Event->GetType( &EventType );
	//Moving lower because it is referenced below to GetStatus if there was a problem: Event->Release( );

	if( FAILED( HResult ) )
	{
		Event->Release( );
		return S_OK;
	}

	/* Closed */
	if( EventType == MESessionClosed )
	{
		MovieIsFinished.Set( 1 );
		CloseIsPosted.Set( 1 );
	}
	else
	{
		HResult = MediaSession->BeginGetEvent( this, NULL );
		if( FAILED( HResult ) )
		{
			Event->Release( );
			return S_OK;
		}

		if( MovieIsRunning( ) )
		{
			/* End of clip */
			if( EventType == MEEndOfPresentation )
			{
				if( Looping )
					StartPlayback( );
				else
					MovieIsFinished.Set( 1 );
			}

			/* Unknown error, dont continue */
			else if( EventType == MEError )
			{
				HRESULT HReturnCode = S_OK;
				Event->GetStatus( &HReturnCode );

				/* Log error HResult */
				UE_LOG( LogImfVideoPlayer, Log, TEXT( "ImfVideoPlayer error recieved: %i" ), HReturnCode );

				MovieIsFinished.Set( 1 );
				CloseIsPosted.Set( 1 );
			}
		}

		/* DEBUG: Displays all event ID's in log */
		//UE_LOG( LogImfVideoPlayer, Log, TEXT( "ImfVideoPlayer event id: %i" ), EventType );
	}
	
	Event->Release( );

	return S_OK;
}