HRESULT WMFAACDecoder::GetOutputSample(IMFSample** aOutSample) { HRESULT hr; // We allocate samples for MFT output. MFT_OUTPUT_DATA_BUFFER output = {0}; CComPtr<IMFSample> sample = nullptr; hr = CreateOutputSample(&sample); ENSURE(SUCCEEDED(hr), hr); output.pSample = sample; DWORD status = 0; hr = mDecoder->ProcessOutput(0, 1, &output, &status); CComPtr<IMFCollection> events = output.pEvents; // Ensure this is released. if (hr == MF_E_TRANSFORM_STREAM_CHANGE) { // Type change. Probably geometric apperature change. hr = SetDecoderOutputType(); ENSURE(SUCCEEDED(hr), hr); return GetOutputSample(aOutSample); } else if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT || !sample) { return MF_E_TRANSFORM_NEED_MORE_INPUT; } // Treat other errors as fatal. ENSURE(SUCCEEDED(hr), hr); assert(sample); *aOutSample = sample.Detach(); return S_OK; }
// This is called from the DoProcessOutput method if the stream format // has changed. // // Thread context: decoder thread bool DecoderMF::HandleStreamChange() { bool ret = false; HRESULT hr; DWORD numOutputStreams; IMFMediaType* mediaType = NULL; AM_MEDIA_TYPE* mformt = NULL; hr = m_h264Decoder->GetStreamCount(NULL, &numOutputStreams); if (FAILED(hr)) goto bail; if (numOutputStreams != 1) goto bail; hr = S_OK; int idx = 0; while (hr == S_OK) { hr = m_h264Decoder->GetOutputAvailableType(0, idx, &mediaType); if (FAILED(hr)) goto bail; mediaType->GetRepresentation(FORMAT_MFVideoFormat , (LPVOID*)&mformt); MFVIDEOFORMAT* z = (MFVIDEOFORMAT*)mformt->pbFormat; unsigned int format = z->surfaceInfo.Format; mediaType->FreeRepresentation(FORMAT_MFVideoFormat ,(LPVOID)mformt); if (format == '2YUY') break; ++idx; } hr = m_h264Decoder->SetOutputType(0, mediaType, 0); if (FAILED(hr)) goto bail; if (! CreateOutputSample()) goto bail; if (m_previewWindow != NULL) { if (! m_previewWindow->SetMediaType(mediaType)) goto bail; m_previewConfigured = true; } ret = true; bail: if (mediaType != NULL) mediaType->Release(); return ret; }
// 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 MFTDecoder::Output(RefPtr<IMFSample>* aOutput) { NS_ENSURE_TRUE(mDecoder != nullptr, E_POINTER); HRESULT hr; MFT_OUTPUT_DATA_BUFFER output = {0}; RefPtr<IMFSample> sample; if (!mMFTProvidesOutputSamples) { hr = CreateOutputSample(&sample); NS_ENSURE_TRUE(SUCCEEDED(hr), hr); output.pSample = sample; } DWORD status = 0; hr = mDecoder->ProcessOutput(0, 1, &output, &status); if (output.pEvents) { // We must release this, as per the IMFTransform::ProcessOutput() // MSDN documentation. output.pEvents->Release(); output.pEvents = nullptr; } if (hr == MF_E_TRANSFORM_STREAM_CHANGE) { // Type change, probably geometric aperature change. // Reconfigure decoder output type, so that GetOutputMediaType() // returns the new type, and return the error code to caller. // This is an expected failure, so don't warn on encountering it. hr = SetDecoderOutputType(); NS_ENSURE_TRUE(SUCCEEDED(hr), hr); // Return the error, so that the caller knows to retry. return MF_E_TRANSFORM_STREAM_CHANGE; } if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) { // Not enough input to produce output. This is an expected failure, // so don't warn on encountering it. return hr; } // Treat other errors as unexpected, and warn. NS_ENSURE_TRUE(SUCCEEDED(hr), hr); MOZ_ASSERT(output.pSample); if (mDiscontinuity) { output.pSample->SetUINT32(MFSampleExtension_Discontinuity, TRUE); mDiscontinuity = false; } *aOutput = output.pSample; // AddRefs if (mMFTProvidesOutputSamples) { // If the MFT is providing samples, we must release the sample here. // Typically only the H.264 MFT provides samples when using DXVA, // and it always re-uses the same sample, so if we don't release it // MFT::ProcessOutput() deadlocks waiting for the sample to be released. output.pSample->Release(); output.pSample = nullptr; } return S_OK; }