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