HRESULT TffOutputQueue::ReceiveMultiple( IMediaSample **ppSamples, long nSamples, long *nSamplesProcessed) { CAutoLock lck(this); // Either call directly or queue up the samples /* We're sending to our thread */ if (m_hr != S_OK) { *nSamplesProcessed = 0; DPRINTF(_l("COutputQueue (queued) : Discarding %d samples code 0x%8.8X"), nSamples, m_hr); for (int i = 0; i < nSamples; i++) { ppSamples[i]->Release(); } return m_hr; } m_bFlushed = FALSE; for (long i = 0; i < nSamples; i++) { QueueSample(ppSamples[i]); } *nSamplesProcessed = nSamples; if (!m_bBatchExact || m_nBatched + m_List->GetCount() >= m_lBatchSize) { //DPRINTF(_l("COutputQueue queued a sample")); NotifyThread(); } return S_OK; }
// Get ready for new data - cancels sticky m_hr void COutputQueue::Reset() { if (!IsQueued()) { m_hr = S_OK; } else { CAutoLock lck(this); QueueSample(RESET_PACKET); NotifyThread(); m_evFlushComplete.Wait(); } }
void COutputQueue::NewSegment( REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate) { if (!IsQueued()) { if (S_OK == m_hr) { if (m_bBatchExact) { SendAnyway(); } m_pPin->NewSegment(tStart, tStop, dRate); } } else { if (m_hr == S_OK) { // // we need to queue the new segment to appear in order in the // data, but we need to pass parameters to it. Rather than // take the hit of wrapping every single sample so we can tell // special ones apart, we queue special pointers to indicate // special packets, and we guarantee (by holding the // critical section) that the packet immediately following a // NEW_SEGMENT value is a NewSegmentPacket containing the // parameters. NewSegmentPacket * ppack = new NewSegmentPacket; if (ppack == NULL) { return; } ppack->tStart = tStart; ppack->tStop = tStop; ppack->dRate = dRate; CAutoLock lck(this); QueueSample(NEW_SEGMENT); QueueSample( (IMediaSample*) ppack); NotifyThread(); } } }
// Send batched stuff anyway void COutputQueue::SendAnyway() { if (!IsQueued()) { // m_bSendAnyway is a private parameter checked in ReceiveMultiple m_bSendAnyway = TRUE; LONG nProcessed; ReceiveMultiple(NULL, 0, &nProcessed); m_bSendAnyway = FALSE; } else { CAutoLock lck(this); QueueSample(SEND_PACKET); NotifyThread(); } }
// // End of Stream is queued to output device // void COutputQueue::EOS() { CAutoLock lck(this); if (!IsQueued()) { if (m_bBatchExact) { SendAnyway(); } if (m_hr == S_OK) { DbgLog((LOG_TRACE, 2, TEXT("COutputQueue sending EndOfStream()"))); m_bFlushed = FALSE; HRESULT hr = m_pPin->EndOfStream(); if (FAILED(hr)) { DbgLog((LOG_ERROR, 2, TEXT("COutputQueue got code 0x%8.8X from EndOfStream()"))); } } } else { if (m_hr == S_OK) { m_bFlushed = FALSE; QueueSample(EOS_PACKET); NotifyThread(); } } }
HRESULT COutputQueue::ReceiveMultiple ( IMediaSample **ppSamples, long nSamples, long *nSamplesProcessed) { CAutoLock lck(this); // Either call directly or queue up the samples if (!IsQueued()) { // If we already had a bad return code then just return if (S_OK != m_hr) { // If we've never received anything since the last Flush() // and the sticky return code is not S_OK we must be // flushing // ((!A || B) is equivalent to A implies B) ASSERT(!m_bFlushed || m_bFlushing); // We're supposed to Release() them anyway! *nSamplesProcessed = 0; for (int i = 0; i < nSamples; i++) { DbgLog((LOG_TRACE, 3, TEXT("COutputQueue (direct) : Discarding %d samples code 0x%8.8X"), nSamples, m_hr)); ppSamples[i]->Release(); } return m_hr; } // // If we're flushing the sticky return code should be S_FALSE // ASSERT(!m_bFlushing); m_bFlushed = FALSE; ASSERT(m_nBatched < m_lBatchSize); ASSERT(m_nBatched == 0 || m_bBatchExact); // Loop processing the samples in batches LONG iLost = 0; long iDone; for (iDone = 0; iDone < nSamples || (m_nBatched != 0 && m_bSendAnyway); ) { //pragma message (REMIND("Implement threshold scheme")) ASSERT(m_nBatched < m_lBatchSize); if (iDone < nSamples) { m_ppSamples[m_nBatched++] = ppSamples[iDone++]; } if (m_nBatched == m_lBatchSize || nSamples == 0 && (m_bSendAnyway || !m_bBatchExact)) { LONG nDone; DbgLog((LOG_TRACE, 4, TEXT("Batching %d samples"), m_nBatched)); if (m_hr == S_OK) { m_hr = m_pInputPin->ReceiveMultiple(m_ppSamples, m_nBatched, &nDone); } else { nDone = 0; } iLost += m_nBatched - nDone; for (LONG i = 0; i < m_nBatched; i++) { m_ppSamples[i]->Release(); } m_nBatched = 0; } } *nSamplesProcessed = iDone - iLost; if (*nSamplesProcessed < 0) { *nSamplesProcessed = 0; } return m_hr; } else { /* We're sending to our thread */ if (m_hr != S_OK) { *nSamplesProcessed = 0; DbgLog((LOG_TRACE, 3, TEXT("COutputQueue (queued) : Discarding %d samples code 0x%8.8X"), nSamples, m_hr)); for (int i = 0; i < nSamples; i++) { ppSamples[i]->Release(); } return m_hr; } m_bFlushed = FALSE; for (long i = 0; i < nSamples; i++) { QueueSample(ppSamples[i]); } *nSamplesProcessed = nSamples; if (!m_bBatchExact || m_nBatched + m_List->GetCount() >= m_lBatchSize) { NotifyThread(); } return S_OK; } }
void CPullPin::Process(void) { // is there anything to do? if (m_tStop <= m_tStart) { EndOfStream(); return; } BOOL bDiscontinuity = TRUE; // if there is more than one sample at the allocator, // then try to queue 2 at once in order to overlap. // -- get buffer count and required alignment ALLOCATOR_PROPERTIES Actual; HRESULT hr = m_pAlloc->GetProperties(&Actual); // align the start position downwards REFERENCE_TIME tStart = AlignDown(m_tStart / UNITS, Actual.cbAlign) * UNITS; REFERENCE_TIME tCurrent = tStart; REFERENCE_TIME tStop = m_tStop; if (tStop > m_tDuration) { tStop = m_tDuration; } // align the stop position - may be past stop, but that // doesn't matter REFERENCE_TIME tAlignStop = AlignUp(tStop / UNITS, Actual.cbAlign) * UNITS; DWORD dwRequest; if (!m_bSync) { // Break out of the loop either if we get to the end or we're asked // to do something else while (tCurrent < tAlignStop) { // Break out without calling EndOfStream if we're asked to // do something different if (CheckRequest(&dwRequest)) { return; } // queue a first sample if (Actual.cBuffers > 1) { hr = QueueSample(tCurrent, tAlignStop, TRUE); bDiscontinuity = FALSE; if (FAILED(hr)) { return; } } // loop queueing second and waiting for first.. while (tCurrent < tAlignStop) { hr = QueueSample(tCurrent, tAlignStop, bDiscontinuity); bDiscontinuity = FALSE; if (FAILED(hr)) { return; } hr = CollectAndDeliver(tStart, tStop); if (S_OK != hr) { // stop if error, or if downstream filter said // to stop. return; } } if (Actual.cBuffers > 1) { hr = CollectAndDeliver(tStart, tStop); if (FAILED(hr)) { return; } } } } else { // sync version of above loop while (tCurrent < tAlignStop) { // Break out without calling EndOfStream if we're asked to // do something different if (CheckRequest(&dwRequest)) { return; } IMediaSample* pSample; hr = m_pAlloc->GetBuffer(&pSample, NULL, NULL, 0); if (FAILED(hr)) { OnError(hr); return; } LONGLONG tStopThis = tCurrent + (pSample->GetSize() * UNITS); if (tStopThis > tAlignStop) { tStopThis = tAlignStop; } pSample->SetTime(&tCurrent, &tStopThis); tCurrent = tStopThis; if (bDiscontinuity) { pSample->SetDiscontinuity(TRUE); bDiscontinuity = FALSE; } hr = m_pReader->SyncReadAligned(pSample); if (FAILED(hr)) { pSample->Release(); OnError(hr); return; } hr = DeliverSample(pSample, tStart, tStop); if (hr != S_OK) { if (FAILED(hr)) { OnError(hr); } return; } } } EndOfStream(); }
void MediaCodecDataDecoder::DecoderLoop() { bool isOutputDone = false; AutoLocalJNIFrame frame(jni::GetEnvForThread(), 1); MediaFormat::LocalRef outputFormat(frame.GetEnv()); nsresult res = NS_OK; while (WaitForInput()) { RefPtr<MediaRawData> sample = PeekNextSample(); { MonitorAutoLock lock(mMonitor); if (State() == kDrainDecoder) { MOZ_ASSERT(!sample, "Shouldn't have a sample when pushing EOF frame"); res = QueueEOS(); BREAK_ON_DECODER_ERROR(); } } if (sample) { res = QueueSample(sample); if (NS_SUCCEEDED(res)) { // We've fed this into the decoder, so remove it from the queue. MonitorAutoLock lock(mMonitor); MOZ_RELEASE_ASSERT(mQueue.size(), "Queue may not be empty"); mQueue.pop_front(); isOutputDone = false; } } if (isOutputDone) { continue; } BufferInfo::LocalRef bufferInfo; nsresult res = BufferInfo::New(&bufferInfo); BREAK_ON_DECODER_ERROR(); int32_t outputStatus = -1; res = mDecoder->DequeueOutputBuffer(bufferInfo, kDecoderTimeout, &outputStatus); BREAK_ON_DECODER_ERROR(); if (outputStatus == MediaCodec::INFO_TRY_AGAIN_LATER) { // We might want to call mCallback->InputExhausted() here, but there seems // to be some possible bad interactions here with the threading. } else if (outputStatus == MediaCodec::INFO_OUTPUT_BUFFERS_CHANGED) { res = ResetOutputBuffers(); BREAK_ON_DECODER_ERROR(); } else if (outputStatus == MediaCodec::INFO_OUTPUT_FORMAT_CHANGED) { res = mDecoder->GetOutputFormat(ReturnTo(&outputFormat)); BREAK_ON_DECODER_ERROR(); } else if (outputStatus < 0) { NS_WARNING("Unknown error from decoder!"); INVOKE_CALLBACK(Error); // Don't break here just in case it's recoverable. If it's not, other // stuff will fail later and we'll bail out. } else { // We have a valid buffer index >= 0 here. int32_t flags; nsresult res = bufferInfo->Flags(&flags); BREAK_ON_DECODER_ERROR(); if (flags & MediaCodec::BUFFER_FLAG_END_OF_STREAM) { HandleEOS(outputStatus); isOutputDone = true; // We only queue empty EOF frames, so we're done for now. continue; } res = ProcessOutput(bufferInfo, outputFormat, outputStatus); BREAK_ON_DECODER_ERROR(); } } Cleanup(); // We're done. MonitorAutoLock lock(mMonitor); State(kShutdown); mMonitor.Notify(); }