Bug 882543 - Use a linked list for ordering stream instead of an array. r=ehsan
[gecko.git] / content / media / MediaStreamGraph.cpp
blobd323f5758cb289b07b62ca35da27407754cd3d02
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
4 * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "MediaStreamGraphImpl.h"
7 #include "mozilla/LinkedList.h"
9 #include "AudioSegment.h"
10 #include "VideoSegment.h"
11 #include "nsContentUtils.h"
12 #include "nsIAppShell.h"
13 #include "nsIObserver.h"
14 #include "nsServiceManagerUtils.h"
15 #include "nsWidgetsCID.h"
16 #include "nsXPCOMCIDInternal.h"
17 #include "prlog.h"
18 #include "VideoUtils.h"
19 #include "mozilla/Attributes.h"
20 #include "TrackUnionStream.h"
21 #include "ImageContainer.h"
22 #include "AudioChannelCommon.h"
23 #include "AudioNodeEngine.h"
24 #include "AudioNodeStream.h"
25 #include <algorithm>
26 #include "DOMMediaStream.h"
27 #include "GeckoProfiler.h"
29 using namespace mozilla::layers;
30 using namespace mozilla::dom;
32 namespace mozilla {
34 #ifdef PR_LOGGING
35 PRLogModuleInfo* gMediaStreamGraphLog;
36 #endif
38 /**
39 * The singleton graph instance.
41 static MediaStreamGraphImpl* gGraph;
43 StreamTime
44 MediaStreamGraphImpl::GetDesiredBufferEnd(MediaStream* aStream)
46 StreamTime current = mCurrentTime - aStream->mBufferStartTime;
47 return current +
48 MillisecondsToMediaTime(std::max(AUDIO_TARGET_MS, VIDEO_TARGET_MS));
51 void
52 MediaStreamGraphImpl::FinishStream(MediaStream* aStream)
54 if (aStream->mFinished)
55 return;
56 LOG(PR_LOG_DEBUG, ("MediaStream %p will finish", aStream));
57 aStream->mFinished = true;
58 // Force at least one more iteration of the control loop, since we rely
59 // on UpdateCurrentTime to notify our listeners once the stream end
60 // has been reached.
61 EnsureNextIteration();
64 void
65 MediaStreamGraphImpl::AddStream(MediaStream* aStream)
67 aStream->mBufferStartTime = mCurrentTime;
68 *mStreams.AppendElement() = already_AddRefed<MediaStream>(aStream);
69 LOG(PR_LOG_DEBUG, ("Adding media stream %p to the graph", aStream));
72 void
73 MediaStreamGraphImpl::RemoveStream(MediaStream* aStream)
75 // Remove references in mStreamUpdates before we allow aStream to die.
76 // Pending updates are not needed (since the main thread has already given
77 // up the stream) so we will just drop them.
79 MonitorAutoLock lock(mMonitor);
80 for (uint32_t i = 0; i < mStreamUpdates.Length(); ++i) {
81 if (mStreamUpdates[i].mStream == aStream) {
82 mStreamUpdates[i].mStream = nullptr;
87 // This unrefs the stream, probably destroying it
88 mStreams.RemoveElement(aStream);
90 LOG(PR_LOG_DEBUG, ("Removing media stream %p from the graph", aStream));
93 void
94 MediaStreamGraphImpl::UpdateConsumptionState(SourceMediaStream* aStream)
96 MediaStreamListener::Consumption state =
97 aStream->mIsConsumed ? MediaStreamListener::CONSUMED
98 : MediaStreamListener::NOT_CONSUMED;
99 if (state != aStream->mLastConsumptionState) {
100 aStream->mLastConsumptionState = state;
101 for (uint32_t j = 0; j < aStream->mListeners.Length(); ++j) {
102 MediaStreamListener* l = aStream->mListeners[j];
103 l->NotifyConsumptionChanged(this, state);
108 void
109 MediaStreamGraphImpl::ExtractPendingInput(SourceMediaStream* aStream,
110 GraphTime aDesiredUpToTime,
111 bool* aEnsureNextIteration)
113 bool finished;
115 MutexAutoLock lock(aStream->mMutex);
116 if (aStream->mPullEnabled && !aStream->mFinished &&
117 !aStream->mListeners.IsEmpty()) {
118 // Compute how much stream time we'll need assuming we don't block
119 // the stream at all between mBlockingDecisionsMadeUntilTime and
120 // aDesiredUpToTime.
121 StreamTime t =
122 GraphTimeToStreamTime(aStream, mStateComputedTime) +
123 (aDesiredUpToTime - mStateComputedTime);
124 LOG(PR_LOG_DEBUG+1, ("Calling NotifyPull aStream=%p t=%f current end=%f", aStream,
125 MediaTimeToSeconds(t),
126 MediaTimeToSeconds(aStream->mBuffer.GetEnd())));
127 if (t > aStream->mBuffer.GetEnd()) {
128 *aEnsureNextIteration = true;
129 #ifdef DEBUG
130 if (aStream->mListeners.Length() == 0) {
131 LOG(PR_LOG_ERROR, ("No listeners in NotifyPull aStream=%p desired=%f current end=%f",
132 aStream, MediaTimeToSeconds(t),
133 MediaTimeToSeconds(aStream->mBuffer.GetEnd())));
134 aStream->DumpTrackInfo();
136 #endif
137 for (uint32_t j = 0; j < aStream->mListeners.Length(); ++j) {
138 MediaStreamListener* l = aStream->mListeners[j];
140 MutexAutoUnlock unlock(aStream->mMutex);
141 l->NotifyPull(this, t);
146 finished = aStream->mUpdateFinished;
147 for (int32_t i = aStream->mUpdateTracks.Length() - 1; i >= 0; --i) {
148 SourceMediaStream::TrackData* data = &aStream->mUpdateTracks[i];
149 aStream->ApplyTrackDisabling(data->mID, data->mData);
150 for (uint32_t j = 0; j < aStream->mListeners.Length(); ++j) {
151 MediaStreamListener* l = aStream->mListeners[j];
152 TrackTicks offset = (data->mCommands & SourceMediaStream::TRACK_CREATE)
153 ? data->mStart : aStream->mBuffer.FindTrack(data->mID)->GetSegment()->GetDuration();
154 l->NotifyQueuedTrackChanges(this, data->mID, data->mRate,
155 offset, data->mCommands, *data->mData);
157 if (data->mCommands & SourceMediaStream::TRACK_CREATE) {
158 MediaSegment* segment = data->mData.forget();
159 LOG(PR_LOG_DEBUG, ("SourceMediaStream %p creating track %d, rate %d, start %lld, initial end %lld",
160 aStream, data->mID, data->mRate, int64_t(data->mStart),
161 int64_t(segment->GetDuration())));
162 aStream->mBuffer.AddTrack(data->mID, data->mRate, data->mStart, segment);
163 // The track has taken ownership of data->mData, so let's replace
164 // data->mData with an empty clone.
165 data->mData = segment->CreateEmptyClone();
166 data->mCommands &= ~SourceMediaStream::TRACK_CREATE;
167 } else if (data->mData->GetDuration() > 0) {
168 MediaSegment* dest = aStream->mBuffer.FindTrack(data->mID)->GetSegment();
169 LOG(PR_LOG_DEBUG+1, ("SourceMediaStream %p track %d, advancing end from %lld to %lld",
170 aStream, data->mID,
171 int64_t(dest->GetDuration()),
172 int64_t(dest->GetDuration() + data->mData->GetDuration())));
173 dest->AppendFrom(data->mData);
175 if (data->mCommands & SourceMediaStream::TRACK_END) {
176 aStream->mBuffer.FindTrack(data->mID)->SetEnded();
177 aStream->mUpdateTracks.RemoveElementAt(i);
180 aStream->mBuffer.AdvanceKnownTracksTime(aStream->mUpdateKnownTracksTime);
182 if (aStream->mBuffer.GetEnd() > 0) {
183 aStream->mHasCurrentData = true;
185 if (finished) {
186 FinishStream(aStream);
190 void
191 MediaStreamGraphImpl::UpdateBufferSufficiencyState(SourceMediaStream* aStream)
193 StreamTime desiredEnd = GetDesiredBufferEnd(aStream);
194 nsTArray<SourceMediaStream::ThreadAndRunnable> runnables;
197 MutexAutoLock lock(aStream->mMutex);
198 for (uint32_t i = 0; i < aStream->mUpdateTracks.Length(); ++i) {
199 SourceMediaStream::TrackData* data = &aStream->mUpdateTracks[i];
200 if (data->mCommands & SourceMediaStream::TRACK_CREATE) {
201 // This track hasn't been created yet, so we have no sufficiency
202 // data. The track will be created in the next iteration of the
203 // control loop and then we'll fire insufficiency notifications
204 // if necessary.
205 continue;
207 if (data->mCommands & SourceMediaStream::TRACK_END) {
208 // This track will end, so no point in firing not-enough-data
209 // callbacks.
210 continue;
212 StreamBuffer::Track* track = aStream->mBuffer.FindTrack(data->mID);
213 // Note that track->IsEnded() must be false, otherwise we would have
214 // removed the track from mUpdateTracks already.
215 NS_ASSERTION(!track->IsEnded(), "What is this track doing here?");
216 data->mHaveEnough = track->GetEndTimeRoundDown() >= desiredEnd;
217 if (!data->mHaveEnough) {
218 runnables.MoveElementsFrom(data->mDispatchWhenNotEnough);
223 for (uint32_t i = 0; i < runnables.Length(); ++i) {
224 runnables[i].mThread->Dispatch(runnables[i].mRunnable, 0);
228 StreamTime
229 MediaStreamGraphImpl::GraphTimeToStreamTime(MediaStream* aStream,
230 GraphTime aTime)
232 NS_ASSERTION(aTime <= mStateComputedTime,
233 "Don't ask about times where we haven't made blocking decisions yet");
234 if (aTime <= mCurrentTime) {
235 return std::max<StreamTime>(0, aTime - aStream->mBufferStartTime);
237 GraphTime t = mCurrentTime;
238 StreamTime s = t - aStream->mBufferStartTime;
239 while (t < aTime) {
240 GraphTime end;
241 if (!aStream->mBlocked.GetAt(t, &end)) {
242 s += std::min(aTime, end) - t;
244 t = end;
246 return std::max<StreamTime>(0, s);
249 StreamTime
250 MediaStreamGraphImpl::GraphTimeToStreamTimeOptimistic(MediaStream* aStream,
251 GraphTime aTime)
253 GraphTime computedUpToTime = std::min(mStateComputedTime, aTime);
254 StreamTime s = GraphTimeToStreamTime(aStream, computedUpToTime);
255 return s + (aTime - computedUpToTime);
258 GraphTime
259 MediaStreamGraphImpl::StreamTimeToGraphTime(MediaStream* aStream,
260 StreamTime aTime, uint32_t aFlags)
262 if (aTime >= STREAM_TIME_MAX) {
263 return GRAPH_TIME_MAX;
265 MediaTime bufferElapsedToCurrentTime = mCurrentTime - aStream->mBufferStartTime;
266 if (aTime < bufferElapsedToCurrentTime ||
267 (aTime == bufferElapsedToCurrentTime && !(aFlags & INCLUDE_TRAILING_BLOCKED_INTERVAL))) {
268 return aTime + aStream->mBufferStartTime;
271 MediaTime streamAmount = aTime - bufferElapsedToCurrentTime;
272 NS_ASSERTION(streamAmount >= 0, "Can't answer queries before current time");
274 GraphTime t = mCurrentTime;
275 while (t < GRAPH_TIME_MAX) {
276 bool blocked;
277 GraphTime end;
278 if (t < mStateComputedTime) {
279 blocked = aStream->mBlocked.GetAt(t, &end);
280 end = std::min(end, mStateComputedTime);
281 } else {
282 blocked = false;
283 end = GRAPH_TIME_MAX;
285 if (blocked) {
286 t = end;
287 } else {
288 if (streamAmount == 0) {
289 // No more stream time to consume at time t, so we're done.
290 break;
292 MediaTime consume = std::min(end - t, streamAmount);
293 streamAmount -= consume;
294 t += consume;
297 return t;
300 GraphTime
301 MediaStreamGraphImpl::GetAudioPosition(MediaStream* aStream)
303 if (aStream->mAudioOutputStreams.IsEmpty()) {
304 return mCurrentTime;
306 int64_t positionInFrames = aStream->mAudioOutputStreams[0].mStream->GetPositionInFrames();
307 if (positionInFrames < 0) {
308 return mCurrentTime;
310 return aStream->mAudioOutputStreams[0].mAudioPlaybackStartTime +
311 TicksToTimeRoundDown(aStream->mAudioOutputStreams[0].mStream->GetRate(),
312 positionInFrames);
315 void
316 MediaStreamGraphImpl::UpdateCurrentTime()
318 GraphTime prevCurrentTime, nextCurrentTime;
319 if (mRealtime) {
320 TimeStamp now = TimeStamp::Now();
321 prevCurrentTime = mCurrentTime;
322 nextCurrentTime =
323 SecondsToMediaTime((now - mCurrentTimeStamp).ToSeconds()) + mCurrentTime;
325 mCurrentTimeStamp = now;
326 LOG(PR_LOG_DEBUG+1, ("Updating current time to %f (real %f, mStateComputedTime %f)",
327 MediaTimeToSeconds(nextCurrentTime),
328 (now - mInitialTimeStamp).ToSeconds(),
329 MediaTimeToSeconds(mStateComputedTime)));
330 } else {
331 prevCurrentTime = mCurrentTime;
332 nextCurrentTime = mCurrentTime + MEDIA_GRAPH_TARGET_PERIOD_MS;
333 LOG(PR_LOG_DEBUG+1, ("Updating offline current time to %f (mStateComputedTime %f)",
334 MediaTimeToSeconds(nextCurrentTime),
335 MediaTimeToSeconds(mStateComputedTime)));
338 if (mStateComputedTime < nextCurrentTime) {
339 LOG(PR_LOG_WARNING, ("Media graph global underrun detected"));
340 nextCurrentTime = mStateComputedTime;
343 if (prevCurrentTime >= nextCurrentTime) {
344 NS_ASSERTION(prevCurrentTime == nextCurrentTime, "Time can't go backwards!");
345 // This could happen due to low clock resolution, maybe?
346 LOG(PR_LOG_DEBUG, ("Time did not advance"));
347 // There's not much left to do here, but the code below that notifies
348 // listeners that streams have ended still needs to run.
351 for (uint32_t i = 0; i < mStreams.Length(); ++i) {
352 MediaStream* stream = mStreams[i];
354 // Calculate blocked time and fire Blocked/Unblocked events
355 GraphTime blockedTime = 0;
356 GraphTime t = prevCurrentTime;
357 while (t < nextCurrentTime) {
358 GraphTime end;
359 bool blocked = stream->mBlocked.GetAt(t, &end);
360 if (blocked) {
361 blockedTime += std::min(end, nextCurrentTime) - t;
363 if (blocked != stream->mNotifiedBlocked) {
364 for (uint32_t j = 0; j < stream->mListeners.Length(); ++j) {
365 MediaStreamListener* l = stream->mListeners[j];
366 l->NotifyBlockingChanged(this,
367 blocked ? MediaStreamListener::BLOCKED : MediaStreamListener::UNBLOCKED);
369 stream->mNotifiedBlocked = blocked;
371 t = end;
374 stream->AdvanceTimeVaryingValuesToCurrentTime(nextCurrentTime, blockedTime);
375 // Advance mBlocked last so that implementations of
376 // AdvanceTimeVaryingValuesToCurrentTime can rely on the value of mBlocked.
377 stream->mBlocked.AdvanceCurrentTime(nextCurrentTime);
379 if (blockedTime < nextCurrentTime - prevCurrentTime) {
380 for (uint32_t i = 0; i < stream->mListeners.Length(); ++i) {
381 MediaStreamListener* l = stream->mListeners[i];
382 l->NotifyOutput(this);
386 if (stream->mFinished && !stream->mNotifiedFinished &&
387 stream->mBufferStartTime + stream->GetBufferEnd() <= nextCurrentTime) {
388 stream->mNotifiedFinished = true;
389 stream->mLastPlayedVideoFrame.SetNull();
390 for (uint32_t j = 0; j < stream->mListeners.Length(); ++j) {
391 MediaStreamListener* l = stream->mListeners[j];
392 l->NotifyFinished(this);
396 LOG(PR_LOG_DEBUG+1, ("MediaStream %p bufferStartTime=%f blockedTime=%f",
397 stream, MediaTimeToSeconds(stream->mBufferStartTime),
398 MediaTimeToSeconds(blockedTime)));
401 mCurrentTime = nextCurrentTime;
404 bool
405 MediaStreamGraphImpl::WillUnderrun(MediaStream* aStream, GraphTime aTime,
406 GraphTime aEndBlockingDecisions, GraphTime* aEnd)
408 // Finished streams can't underrun. ProcessedMediaStreams also can't cause
409 // underrun currently, since we'll always be able to produce data for them
410 // unless they block on some other stream.
411 if (aStream->mFinished || aStream->AsProcessedStream()) {
412 return false;
414 GraphTime bufferEnd =
415 StreamTimeToGraphTime(aStream, aStream->GetBufferEnd(),
416 INCLUDE_TRAILING_BLOCKED_INTERVAL);
417 #ifdef DEBUG
418 if (bufferEnd < mCurrentTime) {
419 LOG(PR_LOG_ERROR, ("MediaStream %p underrun, "
420 "bufferEnd %f < mCurrentTime %f (%lld < %lld), Streamtime %lld",
421 aStream, MediaTimeToSeconds(bufferEnd), MediaTimeToSeconds(mCurrentTime),
422 bufferEnd, mCurrentTime, aStream->GetBufferEnd()));
423 aStream->DumpTrackInfo();
424 NS_ASSERTION(bufferEnd >= mCurrentTime, "Buffer underran");
426 #endif
427 // We should block after bufferEnd.
428 if (bufferEnd <= aTime) {
429 LOG(PR_LOG_DEBUG+1, ("MediaStream %p will block due to data underrun, "
430 "bufferEnd %f",
431 aStream, MediaTimeToSeconds(bufferEnd)));
432 return true;
434 // We should keep blocking if we're currently blocked and we don't have
435 // data all the way through to aEndBlockingDecisions. If we don't have
436 // data all the way through to aEndBlockingDecisions, we'll block soon,
437 // but we might as well remain unblocked and play the data we've got while
438 // we can.
439 if (bufferEnd <= aEndBlockingDecisions && aStream->mBlocked.GetBefore(aTime)) {
440 LOG(PR_LOG_DEBUG+1, ("MediaStream %p will block due to speculative data underrun, "
441 "bufferEnd %f",
442 aStream, MediaTimeToSeconds(bufferEnd)));
443 return true;
445 // Reconsider decisions at bufferEnd
446 *aEnd = std::min(*aEnd, bufferEnd);
447 return false;
450 void
451 MediaStreamGraphImpl::MarkConsumed(MediaStream* aStream)
453 if (aStream->mIsConsumed) {
454 return;
456 aStream->mIsConsumed = true;
458 ProcessedMediaStream* ps = aStream->AsProcessedStream();
459 if (!ps) {
460 return;
462 // Mark all the inputs to this stream as consumed
463 for (uint32_t i = 0; i < ps->mInputs.Length(); ++i) {
464 MarkConsumed(ps->mInputs[i]->mSource);
468 void
469 MediaStreamGraphImpl::UpdateStreamOrderForStream(mozilla::LinkedList<MediaStream>* aStack,
470 already_AddRefed<MediaStream> aStream)
472 nsRefPtr<MediaStream> stream = aStream;
473 NS_ASSERTION(!stream->mHasBeenOrdered, "stream should not have already been ordered");
474 if (stream->mIsOnOrderingStack) {
475 MediaStream* iter = aStack->getLast();
476 do {
477 iter->AsProcessedStream()->mInCycle = true;
478 iter = iter->getPrevious();
479 } while (iter != stream);
480 return;
482 ProcessedMediaStream* ps = stream->AsProcessedStream();
483 if (ps) {
484 aStack->insertBack(stream);
485 stream->mIsOnOrderingStack = true;
486 for (uint32_t i = 0; i < ps->mInputs.Length(); ++i) {
487 MediaStream* source = ps->mInputs[i]->mSource;
488 if (!source->mHasBeenOrdered) {
489 nsRefPtr<MediaStream> s = source;
490 UpdateStreamOrderForStream(aStack, s.forget());
493 aStack->popLast();
494 stream->mIsOnOrderingStack = false;
497 stream->mHasBeenOrdered = true;
498 *mStreams.AppendElement() = stream.forget();
501 void
502 MediaStreamGraphImpl::UpdateStreamOrder()
504 mOldStreams.SwapElements(mStreams);
505 mStreams.ClearAndRetainStorage();
506 for (uint32_t i = 0; i < mOldStreams.Length(); ++i) {
507 MediaStream* stream = mOldStreams[i];
508 stream->mHasBeenOrdered = false;
509 stream->mIsConsumed = false;
510 stream->mIsOnOrderingStack = false;
511 stream->mInBlockingSet = false;
512 ProcessedMediaStream* ps = stream->AsProcessedStream();
513 if (ps) {
514 ps->mInCycle = false;
518 mozilla::LinkedList<MediaStream> stack;
519 for (uint32_t i = 0; i < mOldStreams.Length(); ++i) {
520 nsRefPtr<MediaStream>& s = mOldStreams[i];
521 if (!s->mAudioOutputs.IsEmpty() || !s->mVideoOutputs.IsEmpty()) {
522 MarkConsumed(s);
524 if (!s->mHasBeenOrdered) {
525 UpdateStreamOrderForStream(&stack, s.forget());
530 void
531 MediaStreamGraphImpl::RecomputeBlocking(GraphTime aEndBlockingDecisions)
533 bool blockingDecisionsWillChange = false;
535 LOG(PR_LOG_DEBUG+1, ("Media graph %p computing blocking for time %f",
536 this, MediaTimeToSeconds(mStateComputedTime)));
537 for (uint32_t i = 0; i < mStreams.Length(); ++i) {
538 MediaStream* stream = mStreams[i];
539 if (!stream->mInBlockingSet) {
540 // Compute a partition of the streams containing 'stream' such that we can
541 // compute the blocking status of each subset independently.
542 nsAutoTArray<MediaStream*,10> streamSet;
543 AddBlockingRelatedStreamsToSet(&streamSet, stream);
545 GraphTime end;
546 for (GraphTime t = mStateComputedTime;
547 t < aEndBlockingDecisions; t = end) {
548 end = GRAPH_TIME_MAX;
549 RecomputeBlockingAt(streamSet, t, aEndBlockingDecisions, &end);
550 if (end < GRAPH_TIME_MAX) {
551 blockingDecisionsWillChange = true;
556 GraphTime end;
557 stream->mBlocked.GetAt(mCurrentTime, &end);
558 if (end < GRAPH_TIME_MAX) {
559 blockingDecisionsWillChange = true;
562 LOG(PR_LOG_DEBUG+1, ("Media graph %p computed blocking for interval %f to %f",
563 this, MediaTimeToSeconds(mStateComputedTime),
564 MediaTimeToSeconds(aEndBlockingDecisions)));
565 mStateComputedTime = aEndBlockingDecisions;
567 if (blockingDecisionsWillChange) {
568 // Make sure we wake up to notify listeners about these changes.
569 EnsureNextIteration();
573 void
574 MediaStreamGraphImpl::AddBlockingRelatedStreamsToSet(nsTArray<MediaStream*>* aStreams,
575 MediaStream* aStream)
577 if (aStream->mInBlockingSet)
578 return;
579 aStream->mInBlockingSet = true;
580 aStreams->AppendElement(aStream);
581 for (uint32_t i = 0; i < aStream->mConsumers.Length(); ++i) {
582 MediaInputPort* port = aStream->mConsumers[i];
583 if (port->mFlags & (MediaInputPort::FLAG_BLOCK_INPUT | MediaInputPort::FLAG_BLOCK_OUTPUT)) {
584 AddBlockingRelatedStreamsToSet(aStreams, port->mDest);
587 ProcessedMediaStream* ps = aStream->AsProcessedStream();
588 if (ps) {
589 for (uint32_t i = 0; i < ps->mInputs.Length(); ++i) {
590 MediaInputPort* port = ps->mInputs[i];
591 if (port->mFlags & (MediaInputPort::FLAG_BLOCK_INPUT | MediaInputPort::FLAG_BLOCK_OUTPUT)) {
592 AddBlockingRelatedStreamsToSet(aStreams, port->mSource);
598 void
599 MediaStreamGraphImpl::MarkStreamBlocking(MediaStream* aStream)
601 if (aStream->mBlockInThisPhase)
602 return;
603 aStream->mBlockInThisPhase = true;
604 for (uint32_t i = 0; i < aStream->mConsumers.Length(); ++i) {
605 MediaInputPort* port = aStream->mConsumers[i];
606 if (port->mFlags & MediaInputPort::FLAG_BLOCK_OUTPUT) {
607 MarkStreamBlocking(port->mDest);
610 ProcessedMediaStream* ps = aStream->AsProcessedStream();
611 if (ps) {
612 for (uint32_t i = 0; i < ps->mInputs.Length(); ++i) {
613 MediaInputPort* port = ps->mInputs[i];
614 if (port->mFlags & MediaInputPort::FLAG_BLOCK_INPUT) {
615 MarkStreamBlocking(port->mSource);
621 void
622 MediaStreamGraphImpl::RecomputeBlockingAt(const nsTArray<MediaStream*>& aStreams,
623 GraphTime aTime,
624 GraphTime aEndBlockingDecisions,
625 GraphTime* aEnd)
627 for (uint32_t i = 0; i < aStreams.Length(); ++i) {
628 MediaStream* stream = aStreams[i];
629 stream->mBlockInThisPhase = false;
632 for (uint32_t i = 0; i < aStreams.Length(); ++i) {
633 MediaStream* stream = aStreams[i];
635 if (stream->mFinished) {
636 GraphTime endTime = StreamTimeToGraphTime(stream, stream->GetBufferEnd());
637 if (endTime <= aTime) {
638 LOG(PR_LOG_DEBUG+1, ("MediaStream %p is blocked due to being finished", stream));
639 // We'll block indefinitely
640 MarkStreamBlocking(stream);
641 *aEnd = aEndBlockingDecisions;
642 continue;
643 } else {
644 LOG(PR_LOG_DEBUG+1, ("MediaStream %p is finished, but not blocked yet (end at %f, with blocking at %f)",
645 stream, MediaTimeToSeconds(stream->GetBufferEnd()),
646 MediaTimeToSeconds(endTime)));
647 *aEnd = std::min(*aEnd, endTime);
651 GraphTime end;
652 bool explicitBlock = stream->mExplicitBlockerCount.GetAt(aTime, &end) > 0;
653 *aEnd = std::min(*aEnd, end);
654 if (explicitBlock) {
655 LOG(PR_LOG_DEBUG+1, ("MediaStream %p is blocked due to explicit blocker", stream));
656 MarkStreamBlocking(stream);
657 continue;
660 bool underrun = WillUnderrun(stream, aTime, aEndBlockingDecisions, aEnd);
661 if (underrun) {
662 // We'll block indefinitely
663 MarkStreamBlocking(stream);
664 *aEnd = aEndBlockingDecisions;
665 continue;
668 NS_ASSERTION(*aEnd > aTime, "Failed to advance!");
670 for (uint32_t i = 0; i < aStreams.Length(); ++i) {
671 MediaStream* stream = aStreams[i];
672 stream->mBlocked.SetAtAndAfter(aTime, stream->mBlockInThisPhase);
676 void
677 MediaStreamGraphImpl::NotifyHasCurrentData(MediaStream* aStream)
679 if (!aStream->mNotifiedHasCurrentData && aStream->mHasCurrentData) {
680 for (uint32_t j = 0; j < aStream->mListeners.Length(); ++j) {
681 MediaStreamListener* l = aStream->mListeners[j];
682 l->NotifyHasCurrentData(this);
684 aStream->mNotifiedHasCurrentData = true;
688 void
689 MediaStreamGraphImpl::CreateOrDestroyAudioStreams(GraphTime aAudioOutputStartTime,
690 MediaStream* aStream)
692 MOZ_ASSERT(mRealtime, "Should only attempt to create audio streams in real-time mode");
694 nsAutoTArray<bool,2> audioOutputStreamsFound;
695 for (uint32_t i = 0; i < aStream->mAudioOutputStreams.Length(); ++i) {
696 audioOutputStreamsFound.AppendElement(false);
699 if (!aStream->mAudioOutputs.IsEmpty()) {
700 for (StreamBuffer::TrackIter tracks(aStream->GetStreamBuffer(), MediaSegment::AUDIO);
701 !tracks.IsEnded(); tracks.Next()) {
702 uint32_t i;
703 for (i = 0; i < audioOutputStreamsFound.Length(); ++i) {
704 if (aStream->mAudioOutputStreams[i].mTrackID == tracks->GetID()) {
705 break;
708 if (i < audioOutputStreamsFound.Length()) {
709 audioOutputStreamsFound[i] = true;
710 } else {
711 // No output stream created for this track yet. Check if it's time to
712 // create one.
713 GraphTime startTime =
714 StreamTimeToGraphTime(aStream, tracks->GetStartTimeRoundDown(),
715 INCLUDE_TRAILING_BLOCKED_INTERVAL);
716 if (startTime >= mStateComputedTime) {
717 // The stream wants to play audio, but nothing will play for the forseeable
718 // future, so don't create the stream.
719 continue;
722 // XXX allocating a AudioStream could be slow so we're going to have to do
723 // something here ... preallocation, async allocation, multiplexing onto a single
724 // stream ...
725 MediaStream::AudioOutputStream* audioOutputStream =
726 aStream->mAudioOutputStreams.AppendElement();
727 audioOutputStream->mAudioPlaybackStartTime = aAudioOutputStartTime;
728 audioOutputStream->mBlockedAudioTime = 0;
729 audioOutputStream->mStream = AudioStream::AllocateStream();
730 // XXX for now, allocate stereo output. But we need to fix this to
731 // match the system's ideal channel configuration.
732 audioOutputStream->mStream->Init(2, tracks->GetRate(), AUDIO_CHANNEL_NORMAL);
733 audioOutputStream->mTrackID = tracks->GetID();
738 for (int32_t i = audioOutputStreamsFound.Length() - 1; i >= 0; --i) {
739 if (!audioOutputStreamsFound[i]) {
740 aStream->mAudioOutputStreams[i].mStream->Shutdown();
741 aStream->mAudioOutputStreams.RemoveElementAt(i);
746 void
747 MediaStreamGraphImpl::PlayAudio(MediaStream* aStream,
748 GraphTime aFrom, GraphTime aTo)
750 MOZ_ASSERT(mRealtime, "Should only attempt to play audio in realtime mode");
752 if (aStream->mAudioOutputStreams.IsEmpty()) {
753 return;
756 // When we're playing multiple copies of this stream at the same time, they're
757 // perfectly correlated so adding volumes is the right thing to do.
758 float volume = 0.0f;
759 for (uint32_t i = 0; i < aStream->mAudioOutputs.Length(); ++i) {
760 volume += aStream->mAudioOutputs[i].mVolume;
763 for (uint32_t i = 0; i < aStream->mAudioOutputStreams.Length(); ++i) {
764 MediaStream::AudioOutputStream& audioOutput = aStream->mAudioOutputStreams[i];
765 StreamBuffer::Track* track = aStream->mBuffer.FindTrack(audioOutput.mTrackID);
766 AudioSegment* audio = track->Get<AudioSegment>();
768 // We don't update aStream->mBufferStartTime here to account for
769 // time spent blocked. Instead, we'll update it in UpdateCurrentTime after the
770 // blocked period has completed. But we do need to make sure we play from the
771 // right offsets in the stream buffer, even if we've already written silence for
772 // some amount of blocked time after the current time.
773 GraphTime t = aFrom;
774 while (t < aTo) {
775 GraphTime end;
776 bool blocked = aStream->mBlocked.GetAt(t, &end);
777 end = std::min(end, aTo);
779 AudioSegment output;
780 if (blocked) {
781 // Track total blocked time in aStream->mBlockedAudioTime so that
782 // the amount of silent samples we've inserted for blocking never gets
783 // more than one sample away from the ideal amount.
784 TrackTicks startTicks =
785 TimeToTicksRoundDown(track->GetRate(), audioOutput.mBlockedAudioTime);
786 audioOutput.mBlockedAudioTime += end - t;
787 TrackTicks endTicks =
788 TimeToTicksRoundDown(track->GetRate(), audioOutput.mBlockedAudioTime);
790 output.InsertNullDataAtStart(endTicks - startTicks);
791 LOG(PR_LOG_DEBUG+1, ("MediaStream %p writing blocking-silence samples for %f to %f",
792 aStream, MediaTimeToSeconds(t), MediaTimeToSeconds(end)));
793 } else {
794 TrackTicks startTicks =
795 track->TimeToTicksRoundDown(GraphTimeToStreamTime(aStream, t));
796 TrackTicks endTicks =
797 track->TimeToTicksRoundDown(GraphTimeToStreamTime(aStream, end));
799 // If startTicks is before the track start, then that part of 'audio'
800 // will just be silence, which is fine here. But if endTicks is after
801 // the track end, then 'audio' won't be long enough, so we'll need
802 // to explicitly play silence.
803 TrackTicks sliceEnd = std::min(endTicks, audio->GetDuration());
804 if (sliceEnd > startTicks) {
805 output.AppendSlice(*audio, startTicks, sliceEnd);
807 // Play silence where the track has ended
808 output.AppendNullData(endTicks - sliceEnd);
809 NS_ASSERTION(endTicks == sliceEnd || track->IsEnded(),
810 "Ran out of data but track not ended?");
811 output.ApplyVolume(volume);
812 LOG(PR_LOG_DEBUG+1, ("MediaStream %p writing samples for %f to %f (samples %lld to %lld)",
813 aStream, MediaTimeToSeconds(t), MediaTimeToSeconds(end),
814 startTicks, endTicks));
816 output.WriteTo(audioOutput.mStream);
817 t = end;
822 static void
823 SetImageToBlackPixel(PlanarYCbCrImage* aImage)
825 uint8_t blackPixel[] = { 0x10, 0x80, 0x80 };
827 PlanarYCbCrImage::Data data;
828 data.mYChannel = blackPixel;
829 data.mCbChannel = blackPixel + 1;
830 data.mCrChannel = blackPixel + 2;
831 data.mYStride = data.mCbCrStride = 1;
832 data.mPicSize = data.mYSize = data.mCbCrSize = gfxIntSize(1, 1);
833 aImage->SetData(data);
836 void
837 MediaStreamGraphImpl::PlayVideo(MediaStream* aStream)
839 MOZ_ASSERT(mRealtime, "Should only attempt to play video in realtime mode");
841 if (aStream->mVideoOutputs.IsEmpty())
842 return;
844 // Display the next frame a bit early. This is better than letting the current
845 // frame be displayed for too long.
846 GraphTime framePosition = mCurrentTime + MEDIA_GRAPH_TARGET_PERIOD_MS;
847 NS_ASSERTION(framePosition >= aStream->mBufferStartTime, "frame position before buffer?");
848 StreamTime frameBufferTime = GraphTimeToStreamTime(aStream, framePosition);
850 TrackTicks start;
851 const VideoFrame* frame = nullptr;
852 StreamBuffer::Track* track;
853 for (StreamBuffer::TrackIter tracks(aStream->GetStreamBuffer(), MediaSegment::VIDEO);
854 !tracks.IsEnded(); tracks.Next()) {
855 VideoSegment* segment = tracks->Get<VideoSegment>();
856 TrackTicks thisStart;
857 const VideoFrame* thisFrame =
858 segment->GetFrameAt(tracks->TimeToTicksRoundDown(frameBufferTime), &thisStart);
859 if (thisFrame && thisFrame->GetImage()) {
860 start = thisStart;
861 frame = thisFrame;
862 track = tracks.get();
865 if (!frame || *frame == aStream->mLastPlayedVideoFrame)
866 return;
868 LOG(PR_LOG_DEBUG+1, ("MediaStream %p writing video frame %p (%dx%d)",
869 aStream, frame->GetImage(), frame->GetIntrinsicSize().width,
870 frame->GetIntrinsicSize().height));
871 GraphTime startTime = StreamTimeToGraphTime(aStream,
872 track->TicksToTimeRoundDown(start), INCLUDE_TRAILING_BLOCKED_INTERVAL);
873 TimeStamp targetTime = mCurrentTimeStamp +
874 TimeDuration::FromMilliseconds(double(startTime - mCurrentTime));
875 for (uint32_t i = 0; i < aStream->mVideoOutputs.Length(); ++i) {
876 VideoFrameContainer* output = aStream->mVideoOutputs[i];
878 if (frame->GetForceBlack()) {
879 static const ImageFormat formats[1] = { PLANAR_YCBCR };
880 nsRefPtr<Image> image =
881 output->GetImageContainer()->CreateImage(formats, 1);
882 if (image) {
883 // Sets the image to a single black pixel, which will be scaled to fill
884 // the rendered size.
885 SetImageToBlackPixel(static_cast<PlanarYCbCrImage*>(image.get()));
887 output->SetCurrentFrame(frame->GetIntrinsicSize(), image,
888 targetTime);
889 } else {
890 output->SetCurrentFrame(frame->GetIntrinsicSize(), frame->GetImage(),
891 targetTime);
894 nsCOMPtr<nsIRunnable> event =
895 NS_NewRunnableMethod(output, &VideoFrameContainer::Invalidate);
896 NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
898 if (!aStream->mNotifiedFinished) {
899 aStream->mLastPlayedVideoFrame = *frame;
903 bool
904 MediaStreamGraphImpl::ShouldUpdateMainThread()
906 if (mRealtime) {
907 return true;
910 TimeStamp now = TimeStamp::Now();
911 if ((now - mLastMainThreadUpdate).ToMilliseconds() > MEDIA_GRAPH_TARGET_PERIOD_MS) {
912 mLastMainThreadUpdate = now;
913 return true;
915 return false;
918 void
919 MediaStreamGraphImpl::PrepareUpdatesToMainThreadState(bool aFinalUpdate)
921 mMonitor.AssertCurrentThreadOwns();
923 // We don't want to update the main thread about timing update when we are not
924 // running in realtime.
925 if (ShouldUpdateMainThread()) {
926 mStreamUpdates.SetCapacity(mStreamUpdates.Length() + mStreams.Length());
927 for (uint32_t i = 0; i < mStreams.Length(); ++i) {
928 MediaStream* stream = mStreams[i];
929 if (!stream->MainThreadNeedsUpdates()) {
930 continue;
932 StreamUpdate* update = mStreamUpdates.AppendElement();
933 update->mGraphUpdateIndex = stream->mGraphUpdateIndices.GetAt(mCurrentTime);
934 update->mStream = stream;
935 update->mNextMainThreadCurrentTime =
936 GraphTimeToStreamTime(stream, mCurrentTime);
937 update->mNextMainThreadFinished =
938 stream->mFinished &&
939 StreamTimeToGraphTime(stream, stream->GetBufferEnd()) <= mCurrentTime;
941 if (!mPendingUpdateRunnables.IsEmpty()) {
942 mUpdateRunnables.MoveElementsFrom(mPendingUpdateRunnables);
946 // Don't send the message to the main thread if it's not going to have
947 // any work to do.
948 if (aFinalUpdate ||
949 !mUpdateRunnables.IsEmpty() ||
950 !mStreamUpdates.IsEmpty()) {
951 EnsureStableStateEventPosted();
955 void
956 MediaStreamGraphImpl::EnsureImmediateWakeUpLocked(MonitorAutoLock& aLock)
958 if (mWaitState == WAITSTATE_WAITING_FOR_NEXT_ITERATION ||
959 mWaitState == WAITSTATE_WAITING_INDEFINITELY) {
960 mWaitState = WAITSTATE_WAKING_UP;
961 aLock.Notify();
965 void
966 MediaStreamGraphImpl::EnsureNextIteration()
968 MonitorAutoLock lock(mMonitor);
969 EnsureNextIterationLocked(lock);
972 void
973 MediaStreamGraphImpl::EnsureNextIterationLocked(MonitorAutoLock& aLock)
975 if (mNeedAnotherIteration)
976 return;
977 mNeedAnotherIteration = true;
978 if (mWaitState == WAITSTATE_WAITING_INDEFINITELY) {
979 mWaitState = WAITSTATE_WAKING_UP;
980 aLock.Notify();
984 static GraphTime
985 RoundUpToAudioBlock(TrackRate aSampleRate, GraphTime aTime)
987 int64_t ticksAtIdealaSampleRate = (aTime*aSampleRate) >> MEDIA_TIME_FRAC_BITS;
988 // Round up to nearest block boundary
989 int64_t blocksAtIdealaSampleRate =
990 (ticksAtIdealaSampleRate + (WEBAUDIO_BLOCK_SIZE - 1)) >>
991 WEBAUDIO_BLOCK_SIZE_BITS;
992 // Round up to nearest MediaTime unit
993 return
994 ((((blocksAtIdealaSampleRate + 1)*WEBAUDIO_BLOCK_SIZE) << MEDIA_TIME_FRAC_BITS)
995 + aSampleRate - 1)/aSampleRate;
998 void
999 MediaStreamGraphImpl::ProduceDataForStreamsBlockByBlock(uint32_t aStreamIndex,
1000 TrackRate aSampleRate,
1001 GraphTime aFrom,
1002 GraphTime aTo)
1004 GraphTime t = aFrom;
1005 while (t < aTo) {
1006 GraphTime next = RoundUpToAudioBlock(aSampleRate, t + 1);
1007 for (uint32_t i = aStreamIndex; i < mStreams.Length(); ++i) {
1008 nsRefPtr<ProcessedMediaStream> ps = mStreams[i]->AsProcessedStream();
1009 if (ps) {
1010 ps->ProduceOutput(t, next);
1013 t = next;
1015 NS_ASSERTION(t == aTo, "Something went wrong with rounding to block boundaries");
1018 void
1019 MediaStreamGraphImpl::RunThread()
1021 nsTArray<MessageBlock> messageQueue;
1023 MonitorAutoLock lock(mMonitor);
1024 messageQueue.SwapElements(mMessageQueue);
1026 NS_ASSERTION(!messageQueue.IsEmpty(),
1027 "Shouldn't have started a graph with empty message queue!");
1029 uint32_t ticksProcessed = 0;
1030 if (!mRealtime) {
1031 NS_ASSERTION(!mNonRealtimeIsRunning,
1032 "We should not be running in non-realtime mode already");
1033 mNonRealtimeIsRunning = true;
1036 for (;;) {
1037 // Update mCurrentTime to the min of the playing audio times, or using the
1038 // wall-clock time change if no audio is playing.
1039 UpdateCurrentTime();
1041 // Calculate independent action times for each batch of messages (each
1042 // batch corresponding to an event loop task). This isolates the performance
1043 // of different scripts to some extent.
1044 for (uint32_t i = 0; i < messageQueue.Length(); ++i) {
1045 mProcessingGraphUpdateIndex = messageQueue[i].mGraphUpdateIndex;
1046 nsTArray<nsAutoPtr<ControlMessage> >& messages = messageQueue[i].mMessages;
1048 for (uint32_t j = 0; j < messages.Length(); ++j) {
1049 messages[j]->Run();
1052 messageQueue.Clear();
1054 UpdateStreamOrder();
1056 // Find the sampling rate that we need to use for non-realtime graphs.
1057 TrackRate sampleRate = IdealAudioRate();
1058 if (!mRealtime) {
1059 for (uint32_t i = 0; i < mStreams.Length(); ++i) {
1060 AudioNodeStream* n = mStreams[i]->AsAudioNodeStream();
1061 if (n) {
1062 // We know that the rest of the streams will run at the same rate.
1063 sampleRate = n->SampleRate();
1064 break;
1069 GraphTime endBlockingDecisions =
1070 RoundUpToAudioBlock(sampleRate, mCurrentTime + MillisecondsToMediaTime(AUDIO_TARGET_MS));
1071 bool ensureNextIteration = false;
1073 // Grab pending stream input.
1074 for (uint32_t i = 0; i < mStreams.Length(); ++i) {
1075 SourceMediaStream* is = mStreams[i]->AsSourceStream();
1076 if (is) {
1077 UpdateConsumptionState(is);
1078 ExtractPendingInput(is, endBlockingDecisions, &ensureNextIteration);
1082 // Figure out which streams are blocked and when.
1083 GraphTime prevComputedTime = mStateComputedTime;
1084 RecomputeBlocking(endBlockingDecisions);
1086 // Play stream contents.
1087 uint32_t audioStreamsActive = 0;
1088 bool allBlockedForever = true;
1089 // True when we've done ProduceOutput for all processed streams.
1090 bool doneAllProducing = false;
1091 // Figure out what each stream wants to do
1092 for (uint32_t i = 0; i < mStreams.Length(); ++i) {
1093 MediaStream* stream = mStreams[i];
1094 if (!doneAllProducing && !stream->IsFinishedOnGraphThread()) {
1095 ProcessedMediaStream* ps = stream->AsProcessedStream();
1096 if (ps) {
1097 AudioNodeStream* n = stream->AsAudioNodeStream();
1098 if (n) {
1099 #ifdef DEBUG
1100 // Verify that the sampling rate for all of the following streams is the same
1101 for (uint32_t j = i + 1; j < mStreams.Length(); ++j) {
1102 AudioNodeStream* nextStream = mStreams[j]->AsAudioNodeStream();
1103 if (nextStream) {
1104 MOZ_ASSERT(n->SampleRate() == nextStream->SampleRate(),
1105 "All AudioNodeStreams in the graph must have the same sampling rate");
1108 #endif
1109 // Since an AudioNodeStream is present, go ahead and
1110 // produce audio block by block for all the rest of the streams.
1111 ProduceDataForStreamsBlockByBlock(i, n->SampleRate(), prevComputedTime, mStateComputedTime);
1112 ticksProcessed += TimeToTicksRoundDown(n->SampleRate(), mStateComputedTime - prevComputedTime);
1113 doneAllProducing = true;
1114 } else {
1115 ps->ProduceOutput(prevComputedTime, mStateComputedTime);
1116 NS_ASSERTION(stream->mBuffer.GetEnd() >=
1117 GraphTimeToStreamTime(stream, mStateComputedTime),
1118 "Stream did not produce enough data");
1122 NotifyHasCurrentData(stream);
1123 if (mRealtime) {
1124 // Only playback audio and video in real-time mode
1125 CreateOrDestroyAudioStreams(prevComputedTime, stream);
1126 PlayAudio(stream, prevComputedTime, mStateComputedTime);
1127 audioStreamsActive += stream->mAudioOutputStreams.Length();
1128 PlayVideo(stream);
1130 SourceMediaStream* is = stream->AsSourceStream();
1131 if (is) {
1132 UpdateBufferSufficiencyState(is);
1134 GraphTime end;
1135 if (!stream->mBlocked.GetAt(mCurrentTime, &end) || end < GRAPH_TIME_MAX) {
1136 allBlockedForever = false;
1139 if (!mRealtime) {
1140 // Terminate processing if we've produce enough non-realtime ticks.
1141 if (!mForceShutDown && ticksProcessed >= mNonRealtimeTicksToProcess) {
1142 // Wait indefinitely when we've processed enough non-realtime ticks.
1143 // We'll be woken up when the graph shuts down.
1144 MonitorAutoLock lock(mMonitor);
1145 PrepareUpdatesToMainThreadState(true);
1146 mWaitState = WAITSTATE_WAITING_INDEFINITELY;
1147 mMonitor.Wait(PR_INTERVAL_NO_TIMEOUT);
1150 if (ensureNextIteration || !allBlockedForever || audioStreamsActive > 0) {
1151 EnsureNextIteration();
1154 // Send updates to the main thread and wait for the next control loop
1155 // iteration.
1157 MonitorAutoLock lock(mMonitor);
1158 bool finalUpdate = (mForceShutDown ||
1159 (IsEmpty() && mMessageQueue.IsEmpty()));
1160 PrepareUpdatesToMainThreadState(finalUpdate);
1161 if (finalUpdate) {
1162 // Enter shutdown mode. The stable-state handler will detect this
1163 // and complete shutdown. Destroy any streams immediately.
1164 LOG(PR_LOG_DEBUG, ("MediaStreamGraph %p waiting for main thread cleanup", this));
1165 // Commit to shutting down this graph object.
1166 mLifecycleState = LIFECYCLE_WAITING_FOR_MAIN_THREAD_CLEANUP;
1167 // No need to Destroy streams here. The main-thread owner of each
1168 // stream is responsible for calling Destroy them.
1169 return;
1172 // No need to wait in non-realtime mode, just churn through the input as soon
1173 // as possible.
1174 if (mRealtime) {
1175 PRIntervalTime timeout = PR_INTERVAL_NO_TIMEOUT;
1176 TimeStamp now = TimeStamp::Now();
1177 if (mNeedAnotherIteration) {
1178 int64_t timeoutMS = MEDIA_GRAPH_TARGET_PERIOD_MS -
1179 int64_t((now - mCurrentTimeStamp).ToMilliseconds());
1180 // Make sure timeoutMS doesn't overflow 32 bits by waking up at
1181 // least once a minute, if we need to wake up at all
1182 timeoutMS = std::max<int64_t>(0, std::min<int64_t>(timeoutMS, 60*1000));
1183 timeout = PR_MillisecondsToInterval(uint32_t(timeoutMS));
1184 LOG(PR_LOG_DEBUG+1, ("Waiting for next iteration; at %f, timeout=%f",
1185 (now - mInitialTimeStamp).ToSeconds(), timeoutMS/1000.0));
1186 mWaitState = WAITSTATE_WAITING_FOR_NEXT_ITERATION;
1187 } else {
1188 mWaitState = WAITSTATE_WAITING_INDEFINITELY;
1190 if (timeout > 0) {
1191 mMonitor.Wait(timeout);
1192 LOG(PR_LOG_DEBUG+1, ("Resuming after timeout; at %f, elapsed=%f",
1193 (TimeStamp::Now() - mInitialTimeStamp).ToSeconds(),
1194 (TimeStamp::Now() - now).ToSeconds()));
1197 mWaitState = WAITSTATE_RUNNING;
1198 mNeedAnotherIteration = false;
1199 messageQueue.SwapElements(mMessageQueue);
1203 if (!mRealtime) {
1204 mNonRealtimeIsRunning = false;
1206 profiler_unregister_thread();
1209 void
1210 MediaStreamGraphImpl::ApplyStreamUpdate(StreamUpdate* aUpdate)
1212 mMonitor.AssertCurrentThreadOwns();
1214 MediaStream* stream = aUpdate->mStream;
1215 if (!stream)
1216 return;
1217 stream->mMainThreadCurrentTime = aUpdate->mNextMainThreadCurrentTime;
1218 stream->mMainThreadFinished = aUpdate->mNextMainThreadFinished;
1220 for (int32_t i = stream->mMainThreadListeners.Length() - 1; i >= 0; --i) {
1221 stream->mMainThreadListeners[i]->NotifyMainThreadStateChanged();
1225 void
1226 MediaStreamGraphImpl::ShutdownThreads()
1228 NS_ASSERTION(NS_IsMainThread(), "Must be called on main thread");
1229 // mGraph's thread is not running so it's OK to do whatever here
1230 LOG(PR_LOG_DEBUG, ("Stopping threads for MediaStreamGraph %p", this));
1232 if (mThread) {
1233 mThread->Shutdown();
1234 mThread = nullptr;
1238 void
1239 MediaStreamGraphImpl::ForceShutDown()
1241 NS_ASSERTION(NS_IsMainThread(), "Must be called on main thread");
1242 LOG(PR_LOG_DEBUG, ("MediaStreamGraph %p ForceShutdown", this));
1244 MonitorAutoLock lock(mMonitor);
1245 mForceShutDown = true;
1246 EnsureImmediateWakeUpLocked(lock);
1250 namespace {
1252 class MediaStreamGraphInitThreadRunnable : public nsRunnable {
1253 public:
1254 explicit MediaStreamGraphInitThreadRunnable(MediaStreamGraphImpl* aGraph)
1255 : mGraph(aGraph)
1258 NS_IMETHOD Run()
1260 char aLocal;
1261 profiler_register_thread("MediaStreamGraph", &aLocal);
1262 mGraph->RunThread();
1263 return NS_OK;
1265 private:
1266 MediaStreamGraphImpl* mGraph;
1269 class MediaStreamGraphThreadRunnable : public nsRunnable {
1270 public:
1271 explicit MediaStreamGraphThreadRunnable(MediaStreamGraphImpl* aGraph)
1272 : mGraph(aGraph)
1275 NS_IMETHOD Run()
1277 mGraph->RunThread();
1278 return NS_OK;
1280 private:
1281 MediaStreamGraphImpl* mGraph;
1284 class MediaStreamGraphShutDownRunnable : public nsRunnable {
1285 public:
1286 MediaStreamGraphShutDownRunnable(MediaStreamGraphImpl* aGraph) : mGraph(aGraph) {}
1287 NS_IMETHOD Run()
1289 NS_ASSERTION(mGraph->mDetectedNotRunning,
1290 "We should know the graph thread control loop isn't running!");
1292 mGraph->ShutdownThreads();
1294 // mGraph's thread is not running so it's OK to do whatever here
1295 if (mGraph->IsEmpty()) {
1296 // mGraph is no longer needed, so delete it. If the graph is not empty
1297 // then we must be in a forced shutdown and some later AppendMessage will
1298 // detect that the manager has been emptied, and delete it.
1299 delete mGraph;
1300 } else {
1301 for (uint32_t i = 0; i < mGraph->mStreams.Length(); ++i) {
1302 DOMMediaStream* s = mGraph->mStreams[i]->GetWrapper();
1303 if (s) {
1304 s->NotifyMediaStreamGraphShutdown();
1308 NS_ASSERTION(mGraph->mForceShutDown, "Not in forced shutdown?");
1309 mGraph->mLifecycleState =
1310 MediaStreamGraphImpl::LIFECYCLE_WAITING_FOR_STREAM_DESTRUCTION;
1312 return NS_OK;
1314 private:
1315 MediaStreamGraphImpl* mGraph;
1318 class MediaStreamGraphStableStateRunnable : public nsRunnable {
1319 public:
1320 explicit MediaStreamGraphStableStateRunnable(MediaStreamGraphImpl* aGraph)
1321 : mGraph(aGraph)
1324 NS_IMETHOD Run()
1326 if (mGraph) {
1327 mGraph->RunInStableState();
1329 return NS_OK;
1331 private:
1332 MediaStreamGraphImpl* mGraph;
1336 * Control messages forwarded from main thread to graph manager thread
1338 class CreateMessage : public ControlMessage {
1339 public:
1340 CreateMessage(MediaStream* aStream) : ControlMessage(aStream) {}
1341 virtual void Run() MOZ_OVERRIDE
1343 mStream->GraphImpl()->AddStream(mStream);
1344 mStream->Init();
1346 virtual void RunDuringShutdown() MOZ_OVERRIDE
1348 // Make sure to run this message during shutdown too, to make sure
1349 // that we balance the number of streams registered with the graph
1350 // as they're destroyed during shutdown.
1351 Run();
1355 class MediaStreamGraphShutdownObserver MOZ_FINAL : public nsIObserver
1357 public:
1358 NS_DECL_ISUPPORTS
1359 NS_DECL_NSIOBSERVER
1364 void
1365 MediaStreamGraphImpl::RunInStableState()
1367 NS_ASSERTION(NS_IsMainThread(), "Must be called on main thread");
1369 nsTArray<nsCOMPtr<nsIRunnable> > runnables;
1370 // When we're doing a forced shutdown, pending control messages may be
1371 // run on the main thread via RunDuringShutdown. Those messages must
1372 // run without the graph monitor being held. So, we collect them here.
1373 nsTArray<nsAutoPtr<ControlMessage> > controlMessagesToRunDuringShutdown;
1376 MonitorAutoLock lock(mMonitor);
1377 mPostedRunInStableStateEvent = false;
1379 runnables.SwapElements(mUpdateRunnables);
1380 for (uint32_t i = 0; i < mStreamUpdates.Length(); ++i) {
1381 StreamUpdate* update = &mStreamUpdates[i];
1382 if (update->mStream) {
1383 ApplyStreamUpdate(update);
1386 mStreamUpdates.Clear();
1388 if (mLifecycleState == LIFECYCLE_WAITING_FOR_MAIN_THREAD_CLEANUP && mForceShutDown) {
1389 // Defer calls to RunDuringShutdown() to happen while mMonitor is not held.
1390 for (uint32_t i = 0; i < mMessageQueue.Length(); ++i) {
1391 MessageBlock& mb = mMessageQueue[i];
1392 controlMessagesToRunDuringShutdown.MoveElementsFrom(mb.mMessages);
1394 mMessageQueue.Clear();
1395 controlMessagesToRunDuringShutdown.MoveElementsFrom(mCurrentTaskMessageQueue);
1396 // Stop MediaStreamGraph threads. Do not clear gGraph since
1397 // we have outstanding DOM objects that may need it.
1398 mLifecycleState = LIFECYCLE_WAITING_FOR_THREAD_SHUTDOWN;
1399 nsCOMPtr<nsIRunnable> event = new MediaStreamGraphShutDownRunnable(this);
1400 NS_DispatchToMainThread(event);
1403 if (mLifecycleState == LIFECYCLE_THREAD_NOT_STARTED) {
1404 mLifecycleState = LIFECYCLE_RUNNING;
1405 // Start the thread now. We couldn't start it earlier because
1406 // the graph might exit immediately on finding it has no streams. The
1407 // first message for a new graph must create a stream.
1408 nsCOMPtr<nsIRunnable> event = new MediaStreamGraphInitThreadRunnable(this);
1409 NS_NewNamedThread("MediaStreamGrph", getter_AddRefs(mThread), event);
1412 if (mCurrentTaskMessageQueue.IsEmpty()) {
1413 if (mLifecycleState == LIFECYCLE_WAITING_FOR_MAIN_THREAD_CLEANUP && IsEmpty()) {
1414 // Complete shutdown. First, ensure that this graph is no longer used.
1415 // A new graph graph will be created if one is needed.
1416 LOG(PR_LOG_DEBUG, ("Disconnecting MediaStreamGraph %p", this));
1417 if (this == gGraph) {
1418 // null out gGraph if that's the graph being shut down
1419 gGraph = nullptr;
1421 // Asynchronously clean up old graph. We don't want to do this
1422 // synchronously because it spins the event loop waiting for threads
1423 // to shut down, and we don't want to do that in a stable state handler.
1424 mLifecycleState = LIFECYCLE_WAITING_FOR_THREAD_SHUTDOWN;
1425 nsCOMPtr<nsIRunnable> event = new MediaStreamGraphShutDownRunnable(this);
1426 NS_DispatchToMainThread(event);
1428 } else {
1429 if (mLifecycleState <= LIFECYCLE_WAITING_FOR_MAIN_THREAD_CLEANUP) {
1430 MessageBlock* block = mMessageQueue.AppendElement();
1431 block->mMessages.SwapElements(mCurrentTaskMessageQueue);
1432 block->mGraphUpdateIndex = mGraphUpdatesSent;
1433 ++mGraphUpdatesSent;
1434 EnsureNextIterationLocked(lock);
1437 if (mLifecycleState == LIFECYCLE_WAITING_FOR_MAIN_THREAD_CLEANUP) {
1438 mLifecycleState = LIFECYCLE_RUNNING;
1439 // Revive the MediaStreamGraph since we have more messages going to it.
1440 // Note that we need to put messages into its queue before reviving it,
1441 // or it might exit immediately.
1442 nsCOMPtr<nsIRunnable> event = new MediaStreamGraphThreadRunnable(this);
1443 mThread->Dispatch(event, 0);
1447 mDetectedNotRunning = mLifecycleState > LIFECYCLE_RUNNING;
1450 // Make sure we get a new current time in the next event loop task
1451 mPostedRunInStableState = false;
1453 for (uint32_t i = 0; i < runnables.Length(); ++i) {
1454 runnables[i]->Run();
1456 for (uint32_t i = 0; i < controlMessagesToRunDuringShutdown.Length(); ++i) {
1457 controlMessagesToRunDuringShutdown[i]->RunDuringShutdown();
1461 static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
1463 void
1464 MediaStreamGraphImpl::EnsureRunInStableState()
1466 NS_ASSERTION(NS_IsMainThread(), "main thread only");
1468 if (mPostedRunInStableState)
1469 return;
1470 mPostedRunInStableState = true;
1471 nsCOMPtr<nsIRunnable> event = new MediaStreamGraphStableStateRunnable(this);
1472 nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
1473 if (appShell) {
1474 appShell->RunInStableState(event);
1475 } else {
1476 NS_ERROR("Appshell already destroyed?");
1480 void
1481 MediaStreamGraphImpl::EnsureStableStateEventPosted()
1483 mMonitor.AssertCurrentThreadOwns();
1485 if (mPostedRunInStableStateEvent)
1486 return;
1487 mPostedRunInStableStateEvent = true;
1488 nsCOMPtr<nsIRunnable> event = new MediaStreamGraphStableStateRunnable(this);
1489 NS_DispatchToMainThread(event);
1492 void
1493 MediaStreamGraphImpl::AppendMessage(ControlMessage* aMessage)
1495 NS_ASSERTION(NS_IsMainThread(), "main thread only");
1496 NS_ASSERTION(!aMessage->GetStream() ||
1497 !aMessage->GetStream()->IsDestroyed(),
1498 "Stream already destroyed");
1500 if (mDetectedNotRunning &&
1501 mLifecycleState > LIFECYCLE_WAITING_FOR_MAIN_THREAD_CLEANUP) {
1502 // The graph control loop is not running and main thread cleanup has
1503 // happened. From now on we can't append messages to mCurrentTaskMessageQueue,
1504 // because that will never be processed again, so just RunDuringShutdown
1505 // this message.
1506 // This should only happen during forced shutdown.
1507 aMessage->RunDuringShutdown();
1508 delete aMessage;
1509 if (IsEmpty() &&
1510 mLifecycleState >= LIFECYCLE_WAITING_FOR_STREAM_DESTRUCTION) {
1511 if (gGraph == this) {
1512 gGraph = nullptr;
1514 delete this;
1515 } else if (!mRealtime) {
1516 // Make sure to mark the graph as not doing non-realtime processing,
1517 // because otherwise AppendMessage will try to ensure that the graph
1518 // is running, and we will never manage to release our resources.
1519 mNonRealtimeProcessing = false;
1521 return;
1524 mCurrentTaskMessageQueue.AppendElement(aMessage);
1525 // Do not start running the non-realtime graph unless processing has
1526 // explicitly started.
1527 if (mRealtime || mNonRealtimeProcessing) {
1528 EnsureRunInStableState();
1532 void
1533 MediaStream::Init()
1535 MediaStreamGraphImpl* graph = GraphImpl();
1536 mBlocked.SetAtAndAfter(graph->mCurrentTime, true);
1537 mExplicitBlockerCount.SetAtAndAfter(graph->mCurrentTime, true);
1538 mExplicitBlockerCount.SetAtAndAfter(graph->mStateComputedTime, false);
1541 MediaStreamGraphImpl*
1542 MediaStream::GraphImpl()
1544 return mGraph;
1547 MediaStreamGraph*
1548 MediaStream::Graph()
1550 return mGraph;
1553 void
1554 MediaStream::SetGraphImpl(MediaStreamGraphImpl* aGraph)
1556 MOZ_ASSERT(!mGraph, "Should only be called once");
1557 mGraph = aGraph;
1560 void
1561 MediaStream::SetGraphImpl(MediaStreamGraph* aGraph)
1563 MediaStreamGraphImpl* graph = static_cast<MediaStreamGraphImpl*>(aGraph);
1564 SetGraphImpl(graph);
1567 StreamTime
1568 MediaStream::GraphTimeToStreamTime(GraphTime aTime)
1570 return GraphImpl()->GraphTimeToStreamTime(this, aTime);
1573 StreamTime
1574 MediaStream::GraphTimeToStreamTimeOptimistic(GraphTime aTime)
1576 return GraphImpl()->GraphTimeToStreamTimeOptimistic(this, aTime);
1579 GraphTime
1580 MediaStream::StreamTimeToGraphTime(StreamTime aTime)
1582 return GraphImpl()->StreamTimeToGraphTime(this, aTime, 0);
1585 void
1586 MediaStream::FinishOnGraphThread()
1588 GraphImpl()->FinishStream(this);
1591 int64_t
1592 MediaStream::GetProcessingGraphUpdateIndex()
1594 return GraphImpl()->GetProcessingGraphUpdateIndex();
1597 StreamBuffer::Track*
1598 MediaStream::EnsureTrack(TrackID aTrackId, TrackRate aSampleRate)
1600 StreamBuffer::Track* track = mBuffer.FindTrack(aTrackId);
1601 if (!track) {
1602 nsAutoPtr<MediaSegment> segment(new AudioSegment());
1603 for (uint32_t j = 0; j < mListeners.Length(); ++j) {
1604 MediaStreamListener* l = mListeners[j];
1605 l->NotifyQueuedTrackChanges(Graph(), aTrackId, aSampleRate, 0,
1606 MediaStreamListener::TRACK_EVENT_CREATED,
1607 *segment);
1609 track = &mBuffer.AddTrack(aTrackId, aSampleRate, 0, segment.forget());
1611 return track;
1614 void
1615 MediaStream::RemoveAllListenersImpl()
1617 for (int32_t i = mListeners.Length() - 1; i >= 0; --i) {
1618 nsRefPtr<MediaStreamListener> listener = mListeners[i].forget();
1619 listener->NotifyRemoved(GraphImpl());
1621 mListeners.Clear();
1624 void
1625 MediaStream::DestroyImpl()
1627 RemoveAllListenersImpl();
1629 for (int32_t i = mConsumers.Length() - 1; i >= 0; --i) {
1630 mConsumers[i]->Disconnect();
1632 for (uint32_t i = 0; i < mAudioOutputStreams.Length(); ++i) {
1633 mAudioOutputStreams[i].mStream->Shutdown();
1635 mAudioOutputStreams.Clear();
1638 void
1639 MediaStream::Destroy()
1641 // Keep this stream alive until we leave this method
1642 nsRefPtr<MediaStream> kungFuDeathGrip = this;
1644 class Message : public ControlMessage {
1645 public:
1646 Message(MediaStream* aStream) : ControlMessage(aStream) {}
1647 virtual void Run()
1649 mStream->DestroyImpl();
1650 mStream->GraphImpl()->RemoveStream(mStream);
1652 virtual void RunDuringShutdown()
1653 { Run(); }
1655 mWrapper = nullptr;
1656 GraphImpl()->AppendMessage(new Message(this));
1657 // Message::RunDuringShutdown may have removed this stream from the graph,
1658 // but our kungFuDeathGrip above will have kept this stream alive if
1659 // necessary.
1660 mMainThreadDestroyed = true;
1663 void
1664 MediaStream::AddAudioOutput(void* aKey)
1666 class Message : public ControlMessage {
1667 public:
1668 Message(MediaStream* aStream, void* aKey) : ControlMessage(aStream), mKey(aKey) {}
1669 virtual void Run()
1671 mStream->AddAudioOutputImpl(mKey);
1673 void* mKey;
1675 GraphImpl()->AppendMessage(new Message(this, aKey));
1678 void
1679 MediaStream::SetAudioOutputVolumeImpl(void* aKey, float aVolume)
1681 for (uint32_t i = 0; i < mAudioOutputs.Length(); ++i) {
1682 if (mAudioOutputs[i].mKey == aKey) {
1683 mAudioOutputs[i].mVolume = aVolume;
1684 return;
1687 NS_ERROR("Audio output key not found");
1690 void
1691 MediaStream::SetAudioOutputVolume(void* aKey, float aVolume)
1693 class Message : public ControlMessage {
1694 public:
1695 Message(MediaStream* aStream, void* aKey, float aVolume) :
1696 ControlMessage(aStream), mKey(aKey), mVolume(aVolume) {}
1697 virtual void Run()
1699 mStream->SetAudioOutputVolumeImpl(mKey, mVolume);
1701 void* mKey;
1702 float mVolume;
1704 GraphImpl()->AppendMessage(new Message(this, aKey, aVolume));
1707 void
1708 MediaStream::RemoveAudioOutputImpl(void* aKey)
1710 for (uint32_t i = 0; i < mAudioOutputs.Length(); ++i) {
1711 if (mAudioOutputs[i].mKey == aKey) {
1712 mAudioOutputs.RemoveElementAt(i);
1713 return;
1716 NS_ERROR("Audio output key not found");
1719 void
1720 MediaStream::RemoveAudioOutput(void* aKey)
1722 class Message : public ControlMessage {
1723 public:
1724 Message(MediaStream* aStream, void* aKey) :
1725 ControlMessage(aStream), mKey(aKey) {}
1726 virtual void Run()
1728 mStream->RemoveAudioOutputImpl(mKey);
1730 void* mKey;
1732 GraphImpl()->AppendMessage(new Message(this, aKey));
1735 void
1736 MediaStream::AddVideoOutput(VideoFrameContainer* aContainer)
1738 class Message : public ControlMessage {
1739 public:
1740 Message(MediaStream* aStream, VideoFrameContainer* aContainer) :
1741 ControlMessage(aStream), mContainer(aContainer) {}
1742 virtual void Run()
1744 mStream->AddVideoOutputImpl(mContainer.forget());
1746 nsRefPtr<VideoFrameContainer> mContainer;
1748 GraphImpl()->AppendMessage(new Message(this, aContainer));
1751 void
1752 MediaStream::RemoveVideoOutput(VideoFrameContainer* aContainer)
1754 class Message : public ControlMessage {
1755 public:
1756 Message(MediaStream* aStream, VideoFrameContainer* aContainer) :
1757 ControlMessage(aStream), mContainer(aContainer) {}
1758 virtual void Run()
1760 mStream->RemoveVideoOutputImpl(mContainer);
1762 nsRefPtr<VideoFrameContainer> mContainer;
1764 GraphImpl()->AppendMessage(new Message(this, aContainer));
1767 void
1768 MediaStream::ChangeExplicitBlockerCount(int32_t aDelta)
1770 class Message : public ControlMessage {
1771 public:
1772 Message(MediaStream* aStream, int32_t aDelta) :
1773 ControlMessage(aStream), mDelta(aDelta) {}
1774 virtual void Run()
1776 mStream->ChangeExplicitBlockerCountImpl(
1777 mStream->GraphImpl()->mStateComputedTime, mDelta);
1779 int32_t mDelta;
1781 GraphImpl()->AppendMessage(new Message(this, aDelta));
1784 void
1785 MediaStream::AddListenerImpl(already_AddRefed<MediaStreamListener> aListener)
1787 MediaStreamListener* listener = *mListeners.AppendElement() = aListener;
1788 listener->NotifyBlockingChanged(GraphImpl(),
1789 mNotifiedBlocked ? MediaStreamListener::BLOCKED : MediaStreamListener::UNBLOCKED);
1790 if (mNotifiedFinished) {
1791 listener->NotifyFinished(GraphImpl());
1793 if (mNotifiedHasCurrentData) {
1794 listener->NotifyHasCurrentData(GraphImpl());
1798 void
1799 MediaStream::AddListener(MediaStreamListener* aListener)
1801 class Message : public ControlMessage {
1802 public:
1803 Message(MediaStream* aStream, MediaStreamListener* aListener) :
1804 ControlMessage(aStream), mListener(aListener) {}
1805 virtual void Run()
1807 mStream->AddListenerImpl(mListener.forget());
1809 nsRefPtr<MediaStreamListener> mListener;
1811 GraphImpl()->AppendMessage(new Message(this, aListener));
1814 void
1815 MediaStream::RemoveListenerImpl(MediaStreamListener* aListener)
1817 // wouldn't need this if we could do it in the opposite order
1818 nsRefPtr<MediaStreamListener> listener(aListener);
1819 mListeners.RemoveElement(aListener);
1820 listener->NotifyRemoved(GraphImpl());
1823 void
1824 MediaStream::RemoveListener(MediaStreamListener* aListener)
1826 class Message : public ControlMessage {
1827 public:
1828 Message(MediaStream* aStream, MediaStreamListener* aListener) :
1829 ControlMessage(aStream), mListener(aListener) {}
1830 virtual void Run()
1832 mStream->RemoveListenerImpl(mListener);
1834 nsRefPtr<MediaStreamListener> mListener;
1836 // If the stream is destroyed the Listeners have or will be
1837 // removed.
1838 if (!IsDestroyed()) {
1839 GraphImpl()->AppendMessage(new Message(this, aListener));
1843 void
1844 MediaStream::SetTrackEnabledImpl(TrackID aTrackID, bool aEnabled)
1846 if (aEnabled) {
1847 mDisabledTrackIDs.RemoveElement(aTrackID);
1848 } else {
1849 if (!mDisabledTrackIDs.Contains(aTrackID)) {
1850 mDisabledTrackIDs.AppendElement(aTrackID);
1855 void
1856 MediaStream::SetTrackEnabled(TrackID aTrackID, bool aEnabled)
1858 class Message : public ControlMessage {
1859 public:
1860 Message(MediaStream* aStream, TrackID aTrackID, bool aEnabled) :
1861 ControlMessage(aStream), mTrackID(aTrackID), mEnabled(aEnabled) {}
1862 virtual void Run()
1864 mStream->SetTrackEnabledImpl(mTrackID, mEnabled);
1866 TrackID mTrackID;
1867 bool mEnabled;
1869 GraphImpl()->AppendMessage(new Message(this, aTrackID, aEnabled));
1872 void
1873 MediaStream::ApplyTrackDisabling(TrackID aTrackID, MediaSegment* aSegment)
1875 if (!mDisabledTrackIDs.Contains(aTrackID)) {
1876 return;
1879 switch (aSegment->GetType()) {
1880 case MediaSegment::AUDIO: {
1881 TrackTicks duration = aSegment->GetDuration();
1882 aSegment->Clear();
1883 aSegment->AppendNullData(duration);
1884 break;
1886 case MediaSegment::VIDEO: {
1887 for (VideoSegment::ChunkIterator i(*static_cast<VideoSegment*>(aSegment));
1888 !i.IsEnded(); i.Next()) {
1889 VideoChunk& chunk = *i;
1890 chunk.SetForceBlack(true);
1892 break;
1894 default:
1895 MOZ_CRASH("Unknown track type");
1899 void
1900 SourceMediaStream::DestroyImpl()
1903 MutexAutoLock lock(mMutex);
1904 mDestroyed = true;
1906 MediaStream::DestroyImpl();
1909 void
1910 SourceMediaStream::SetPullEnabled(bool aEnabled)
1912 MutexAutoLock lock(mMutex);
1913 mPullEnabled = aEnabled;
1914 if (mPullEnabled && !mDestroyed) {
1915 GraphImpl()->EnsureNextIteration();
1919 void
1920 SourceMediaStream::AddTrack(TrackID aID, TrackRate aRate, TrackTicks aStart,
1921 MediaSegment* aSegment)
1923 MutexAutoLock lock(mMutex);
1924 TrackData* data = mUpdateTracks.AppendElement();
1925 data->mID = aID;
1926 data->mRate = aRate;
1927 data->mStart = aStart;
1928 data->mCommands = TRACK_CREATE;
1929 data->mData = aSegment;
1930 data->mHaveEnough = false;
1931 if (!mDestroyed) {
1932 GraphImpl()->EnsureNextIteration();
1936 bool
1937 SourceMediaStream::AppendToTrack(TrackID aID, MediaSegment* aSegment)
1939 MutexAutoLock lock(mMutex);
1940 // ::EndAllTrackAndFinished() can end these before the sources notice
1941 bool appended = false;
1942 if (!mFinished) {
1943 TrackData *track = FindDataForTrack(aID);
1944 if (track) {
1945 track->mData->AppendFrom(aSegment);
1946 appended = true;
1947 } else {
1948 aSegment->Clear();
1951 if (!mDestroyed) {
1952 GraphImpl()->EnsureNextIteration();
1954 return appended;
1957 bool
1958 SourceMediaStream::HaveEnoughBuffered(TrackID aID)
1960 MutexAutoLock lock(mMutex);
1961 TrackData *track = FindDataForTrack(aID);
1962 if (track) {
1963 return track->mHaveEnough;
1965 return false;
1968 void
1969 SourceMediaStream::DispatchWhenNotEnoughBuffered(TrackID aID,
1970 nsIThread* aSignalThread, nsIRunnable* aSignalRunnable)
1972 MutexAutoLock lock(mMutex);
1973 TrackData* data = FindDataForTrack(aID);
1974 if (!data) {
1975 aSignalThread->Dispatch(aSignalRunnable, 0);
1976 return;
1979 if (data->mHaveEnough) {
1980 data->mDispatchWhenNotEnough.AppendElement()->Init(aSignalThread, aSignalRunnable);
1981 } else {
1982 aSignalThread->Dispatch(aSignalRunnable, 0);
1986 void
1987 SourceMediaStream::EndTrack(TrackID aID)
1989 MutexAutoLock lock(mMutex);
1990 // ::EndAllTrackAndFinished() can end these before the sources call this
1991 if (!mFinished) {
1992 TrackData *track = FindDataForTrack(aID);
1993 if (track) {
1994 track->mCommands |= TRACK_END;
1997 if (!mDestroyed) {
1998 GraphImpl()->EnsureNextIteration();
2002 void
2003 SourceMediaStream::AdvanceKnownTracksTime(StreamTime aKnownTime)
2005 MutexAutoLock lock(mMutex);
2006 mUpdateKnownTracksTime = aKnownTime;
2007 if (!mDestroyed) {
2008 GraphImpl()->EnsureNextIteration();
2012 void
2013 SourceMediaStream::FinishWithLockHeld()
2015 mMutex.AssertCurrentThreadOwns();
2016 mUpdateFinished = true;
2017 if (!mDestroyed) {
2018 GraphImpl()->EnsureNextIteration();
2022 void
2023 SourceMediaStream::EndAllTrackAndFinish()
2025 MutexAutoLock lock(mMutex);
2026 for (uint32_t i = 0; i < mUpdateTracks.Length(); ++i) {
2027 SourceMediaStream::TrackData* data = &mUpdateTracks[i];
2028 data->mCommands |= TRACK_END;
2030 FinishWithLockHeld();
2031 // we will call NotifyFinished() to let GetUserMedia know
2034 void
2035 MediaInputPort::Init()
2037 LOG(PR_LOG_DEBUG, ("Adding MediaInputPort %p (from %p to %p) to the graph",
2038 this, mSource, mDest));
2039 mSource->AddConsumer(this);
2040 mDest->AddInput(this);
2041 // mPortCount decremented via MediaInputPort::Destroy's message
2042 ++mDest->GraphImpl()->mPortCount;
2045 void
2046 MediaInputPort::Disconnect()
2048 NS_ASSERTION(!mSource == !mDest,
2049 "mSource must either both be null or both non-null");
2050 if (!mSource)
2051 return;
2053 mSource->RemoveConsumer(this);
2054 mSource = nullptr;
2055 mDest->RemoveInput(this);
2056 mDest = nullptr;
2059 MediaInputPort::InputInterval
2060 MediaInputPort::GetNextInputInterval(GraphTime aTime)
2062 InputInterval result = { GRAPH_TIME_MAX, GRAPH_TIME_MAX, false };
2063 GraphTime t = aTime;
2064 GraphTime end;
2065 for (;;) {
2066 if (!mDest->mBlocked.GetAt(t, &end))
2067 break;
2068 if (end == GRAPH_TIME_MAX)
2069 return result;
2070 t = end;
2072 result.mStart = t;
2073 GraphTime sourceEnd;
2074 result.mInputIsBlocked = mSource->mBlocked.GetAt(t, &sourceEnd);
2075 result.mEnd = std::min(end, sourceEnd);
2076 return result;
2079 void
2080 MediaInputPort::Destroy()
2082 class Message : public ControlMessage {
2083 public:
2084 Message(MediaInputPort* aPort)
2085 : ControlMessage(nullptr), mPort(aPort) {}
2086 virtual void Run()
2088 mPort->Disconnect();
2089 --mPort->GraphImpl()->mPortCount;
2090 NS_RELEASE(mPort);
2092 virtual void RunDuringShutdown()
2094 Run();
2096 MediaInputPort* mPort;
2098 GraphImpl()->AppendMessage(new Message(this));
2101 MediaStreamGraphImpl*
2102 MediaInputPort::GraphImpl()
2104 return mGraph;
2107 MediaStreamGraph*
2108 MediaInputPort::Graph()
2110 return mGraph;
2113 void
2114 MediaInputPort::SetGraphImpl(MediaStreamGraphImpl* aGraph)
2116 MOZ_ASSERT(!mGraph, "Should only be called once");
2117 mGraph = aGraph;
2120 already_AddRefed<MediaInputPort>
2121 ProcessedMediaStream::AllocateInputPort(MediaStream* aStream, uint32_t aFlags,
2122 uint16_t aInputNumber, uint16_t aOutputNumber)
2124 // This method creates two references to the MediaInputPort: one for
2125 // the main thread, and one for the MediaStreamGraph.
2126 class Message : public ControlMessage {
2127 public:
2128 Message(MediaInputPort* aPort)
2129 : ControlMessage(aPort->GetDestination()),
2130 mPort(aPort) {}
2131 virtual void Run()
2133 mPort->Init();
2134 // The graph holds its reference implicitly
2135 mPort.forget();
2137 virtual void RunDuringShutdown()
2139 Run();
2141 nsRefPtr<MediaInputPort> mPort;
2143 nsRefPtr<MediaInputPort> port = new MediaInputPort(aStream, this, aFlags,
2144 aInputNumber, aOutputNumber);
2145 port->SetGraphImpl(GraphImpl());
2146 GraphImpl()->AppendMessage(new Message(port));
2147 return port.forget();
2150 void
2151 ProcessedMediaStream::Finish()
2153 class Message : public ControlMessage {
2154 public:
2155 Message(ProcessedMediaStream* aStream)
2156 : ControlMessage(aStream) {}
2157 virtual void Run()
2159 mStream->GraphImpl()->FinishStream(mStream);
2162 GraphImpl()->AppendMessage(new Message(this));
2165 void
2166 ProcessedMediaStream::SetAutofinish(bool aAutofinish)
2168 class Message : public ControlMessage {
2169 public:
2170 Message(ProcessedMediaStream* aStream, bool aAutofinish)
2171 : ControlMessage(aStream), mAutofinish(aAutofinish) {}
2172 virtual void Run()
2174 static_cast<ProcessedMediaStream*>(mStream)->SetAutofinishImpl(mAutofinish);
2176 bool mAutofinish;
2178 GraphImpl()->AppendMessage(new Message(this, aAutofinish));
2181 void
2182 ProcessedMediaStream::DestroyImpl()
2184 for (int32_t i = mInputs.Length() - 1; i >= 0; --i) {
2185 mInputs[i]->Disconnect();
2187 MediaStream::DestroyImpl();
2191 * We make the initial mCurrentTime nonzero so that zero times can have
2192 * special meaning if necessary.
2194 static const int32_t INITIAL_CURRENT_TIME = 1;
2196 MediaStreamGraphImpl::MediaStreamGraphImpl(bool aRealtime)
2197 : mCurrentTime(INITIAL_CURRENT_TIME)
2198 , mStateComputedTime(INITIAL_CURRENT_TIME)
2199 , mProcessingGraphUpdateIndex(0)
2200 , mPortCount(0)
2201 , mMonitor("MediaStreamGraphImpl")
2202 , mLifecycleState(LIFECYCLE_THREAD_NOT_STARTED)
2203 , mWaitState(WAITSTATE_RUNNING)
2204 , mNonRealtimeTicksToProcess(0)
2205 , mNeedAnotherIteration(false)
2206 , mForceShutDown(false)
2207 , mPostedRunInStableStateEvent(false)
2208 , mNonRealtimeIsRunning(false)
2209 , mDetectedNotRunning(false)
2210 , mPostedRunInStableState(false)
2211 , mRealtime(aRealtime)
2212 , mNonRealtimeProcessing(false)
2214 #ifdef PR_LOGGING
2215 if (!gMediaStreamGraphLog) {
2216 gMediaStreamGraphLog = PR_NewLogModule("MediaStreamGraph");
2218 #endif
2220 mCurrentTimeStamp = mInitialTimeStamp = mLastMainThreadUpdate = TimeStamp::Now();
2223 NS_IMPL_ISUPPORTS1(MediaStreamGraphShutdownObserver, nsIObserver)
2225 static bool gShutdownObserverRegistered = false;
2227 NS_IMETHODIMP
2228 MediaStreamGraphShutdownObserver::Observe(nsISupports *aSubject,
2229 const char *aTopic,
2230 const PRUnichar *aData)
2232 if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
2233 if (gGraph) {
2234 gGraph->ForceShutDown();
2236 nsContentUtils::UnregisterShutdownObserver(this);
2237 gShutdownObserverRegistered = false;
2239 return NS_OK;
2242 MediaStreamGraph*
2243 MediaStreamGraph::GetInstance()
2245 NS_ASSERTION(NS_IsMainThread(), "Main thread only");
2247 if (!gGraph) {
2248 if (!gShutdownObserverRegistered) {
2249 gShutdownObserverRegistered = true;
2250 nsContentUtils::RegisterShutdownObserver(new MediaStreamGraphShutdownObserver());
2253 gGraph = new MediaStreamGraphImpl(true);
2254 LOG(PR_LOG_DEBUG, ("Starting up MediaStreamGraph %p", gGraph));
2257 return gGraph;
2260 MediaStreamGraph*
2261 MediaStreamGraph::CreateNonRealtimeInstance()
2263 NS_ASSERTION(NS_IsMainThread(), "Main thread only");
2265 MediaStreamGraphImpl* graph = new MediaStreamGraphImpl(false);
2266 return graph;
2269 void
2270 MediaStreamGraph::DestroyNonRealtimeInstance(MediaStreamGraph* aGraph)
2272 NS_ASSERTION(NS_IsMainThread(), "Main thread only");
2273 MOZ_ASSERT(aGraph != gGraph, "Should not destroy the global graph here");
2275 MediaStreamGraphImpl* graph = static_cast<MediaStreamGraphImpl*>(aGraph);
2276 if (!graph->mNonRealtimeProcessing) {
2277 // Start the graph, but don't produce anything
2278 graph->StartNonRealtimeProcessing(0);
2280 graph->ForceShutDown();
2283 SourceMediaStream*
2284 MediaStreamGraph::CreateSourceStream(DOMMediaStream* aWrapper)
2286 SourceMediaStream* stream = new SourceMediaStream(aWrapper);
2287 NS_ADDREF(stream);
2288 MediaStreamGraphImpl* graph = static_cast<MediaStreamGraphImpl*>(this);
2289 stream->SetGraphImpl(graph);
2290 graph->AppendMessage(new CreateMessage(stream));
2291 return stream;
2294 ProcessedMediaStream*
2295 MediaStreamGraph::CreateTrackUnionStream(DOMMediaStream* aWrapper)
2297 TrackUnionStream* stream = new TrackUnionStream(aWrapper);
2298 NS_ADDREF(stream);
2299 MediaStreamGraphImpl* graph = static_cast<MediaStreamGraphImpl*>(this);
2300 stream->SetGraphImpl(graph);
2301 graph->AppendMessage(new CreateMessage(stream));
2302 return stream;
2305 AudioNodeStream*
2306 MediaStreamGraph::CreateAudioNodeStream(AudioNodeEngine* aEngine,
2307 AudioNodeStreamKind aKind,
2308 TrackRate aSampleRate)
2310 MOZ_ASSERT(NS_IsMainThread());
2311 if (!aSampleRate) {
2312 aSampleRate = aEngine->NodeMainThread()->Context()->SampleRate();
2314 AudioNodeStream* stream = new AudioNodeStream(aEngine, aKind, aSampleRate);
2315 NS_ADDREF(stream);
2316 MediaStreamGraphImpl* graph = static_cast<MediaStreamGraphImpl*>(this);
2317 stream->SetGraphImpl(graph);
2318 if (aEngine->HasNode()) {
2319 stream->SetChannelMixingParametersImpl(aEngine->NodeMainThread()->ChannelCount(),
2320 aEngine->NodeMainThread()->ChannelCountModeValue(),
2321 aEngine->NodeMainThread()->ChannelInterpretationValue());
2323 graph->AppendMessage(new CreateMessage(stream));
2324 return stream;
2327 void
2328 MediaStreamGraph::StartNonRealtimeProcessing(uint32_t aTicksToProcess)
2330 NS_ASSERTION(NS_IsMainThread(), "main thread only");
2332 MediaStreamGraphImpl* graph = static_cast<MediaStreamGraphImpl*>(this);
2333 NS_ASSERTION(!graph->mRealtime, "non-realtime only");
2335 if (graph->mNonRealtimeProcessing)
2336 return;
2337 graph->mNonRealtimeTicksToProcess = aTicksToProcess;
2338 graph->mNonRealtimeProcessing = true;
2339 graph->EnsureRunInStableState();