protected int ProcessOutput() { Debug.Assert(m_bSampleNotify || m_bRepaint); // See note above. int hr = S_Ok; ProcessOutputStatus dwStatus = 0; long mixerStartTime = 0, mixerEndTime = 0; long systemTime = 0; bool bRepaint = m_bRepaint; // Temporarily store this state flag. MFTOutputDataBuffer[] dataBuffer = new MFTOutputDataBuffer[1]; IMFSample pSample = null; // If the clock is not running, we present the first sample, // and then don't present any more until the clock starts. if ((m_RenderState != RenderState.Started) && // Not running. !m_bRepaint && // Not a repaint request. m_bPrerolled // At least one sample has been presented. ) { return S_False; } // Make sure we have a pointer to the mixer. if (m_pMixer == null) { return MFError.MF_E_INVALIDREQUEST; } if (!m_bRepaint) { MFTOutputStatusFlags osf; hr = m_pMixer.GetOutputStatus(out osf); MFError.ThrowExceptionForHR(hr); if ((osf & MFTOutputStatusFlags.SampleReady) == 0) { m_bSampleNotify = false; return S_Ok; } } try { // Try to get a free sample from the video sample pool. m_SamplePool.GetSample(out pSample); if (pSample == null) { return S_False; // No free samples. We'll try again when a sample is released. } // (If the following assertion fires, it means we are not managing the sample pool correctly.) //Debug.Assert(MFGetAttributeUINT32(pSample, MFSamplePresenter_SampleCounter, -1) == m_TokenCounter); if (m_bRepaint) { // Repaint request. Ask the mixer for the most recent sample. SetDesiredSampleTime(pSample, m_scheduler.LastSampleTime(), m_scheduler.FrameDuration()); m_bRepaint = false; // OK to clear this flag now. } else { // Not a repaint request. Clear the desired sample time; the mixer will // give us the next frame in the stream. ClearDesiredSampleTime(pSample); if (m_pClock != null) { // Latency: Record the starting time for the ProcessOutput operation. hr = m_pClock.GetCorrelatedTime(0, out mixerStartTime, out systemTime); MFError.ThrowExceptionForHR(hr); } } // Now we are ready to get an output sample from the mixer. dataBuffer[0].dwStreamID = 0; dataBuffer[0].dwStatus = 0; dataBuffer[0].pEvents = null; dataBuffer[0].pSample = Marshal.GetIUnknownForObject(pSample); try { hr = m_pMixer.ProcessOutput(0, 1, dataBuffer, out dwStatus); MFError.ThrowExceptionForHR(hr); // Release any events that were returned from the ProcessOutput method. // (We don't expect any events from the mixer, but this is a good practice.) ReleaseEventCollection(dataBuffer.Length, dataBuffer); } catch (Exception e) { hr = Marshal.GetHRForException(e); } finally { Marshal.Release(dataBuffer[0].pSample); //SafeRelease(dataBuffer[0].pSample); } if (Failed(hr)) { // Return the sample to the pool. m_SamplePool.ReturnSample(pSample); // Handle some known error codes from ProcessOutput. if (hr == MFError.MF_E_TRANSFORM_TYPE_NOT_SET) { // The mixer's format is not set. Negotiate a new format. RenegotiateMediaType(); } else if (hr == MFError.MF_E_TRANSFORM_STREAM_CHANGE) { // There was a dynamic media type change. Clear our media type. SetMediaType(null); } else if (hr == MFError.MF_E_TRANSFORM_NEED_MORE_INPUT) { // The mixer needs more input. // We have to wait for the mixer to get more input. m_bSampleNotify = false; } else { MFError.ThrowExceptionForHR(hr); } } else { // We got an output sample from the mixer. if (m_pClock != null && !bRepaint) { // Latency: Record the ending time for the ProcessOutput operation, // and notify the EVR of the latency. hr = m_pClock.GetCorrelatedTime(0, out mixerEndTime, out systemTime); MFError.ThrowExceptionForHR(hr); long latencyTime = mixerEndTime - mixerStartTime; GCHandle gh = GCHandle.Alloc(latencyTime, GCHandleType.Pinned); try { // This event (EventCode.ProcessingLatency) isn't defined until DirectShowNet v2.1 NotifyEvent((EventCode)0x21, gh.AddrOfPinnedObject(), IntPtr.Zero); } finally { gh.Free(); } } // Set up notification for when the sample is released. TrackSample(pSample); // Schedule the sample. if ((m_FrameStep.state == FrameStepRate.None) || bRepaint) { DeliverSample(pSample, bRepaint); } else { // We are frame-stepping (and this is not a repaint request). DeliverFrameStepSample(pSample); } m_bPrerolled = true; // We have presented at least one sample now. } } finally { //SafeRelease(pSample); } return S_Ok; }
protected void ReleaseEventCollection(int cOutputBuffers, MFTOutputDataBuffer[] pBuffers) { for (int i = 0; i < cOutputBuffers; i++) { SafeRelease(pBuffers[i].pEvents); pBuffers[i].pEvents = null; } }
/// <summary> /// The routine that usually performs the transform. /// </summary> /// <param name="pOutputSamples">The structure to populate with output values.</param> /// <returns>S_Ok unless error.</returns> /// <remarks> /// The input sample is in InputSample. Process it into the pOutputSamples struct. /// Depending on what you set in On*StreamInfo, you can either perform /// in-place processing by modifying the input sample (which still must /// set inout the struct), or create a new IMFSample and FULLY populate /// it from the input. If the input sample has been fully processed, /// set InputSample to null. /// </remarks> abstract protected HResult OnProcessOutput(ref MFTOutputDataBuffer pOutputSamples);