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"
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"
26 #include "DOMMediaStream.h"
27 #include "GeckoProfiler.h"
29 using namespace mozilla::layers
;
30 using namespace mozilla::dom
;
35 PRLogModuleInfo
* gMediaStreamGraphLog
;
39 * The singleton graph instance.
41 static MediaStreamGraphImpl
* gGraph
;
44 MediaStreamGraphImpl::GetDesiredBufferEnd(MediaStream
* aStream
)
46 StreamTime current
= mCurrentTime
- aStream
->mBufferStartTime
;
48 MillisecondsToMediaTime(std::max(AUDIO_TARGET_MS
, VIDEO_TARGET_MS
));
52 MediaStreamGraphImpl::FinishStream(MediaStream
* aStream
)
54 if (aStream
->mFinished
)
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
61 EnsureNextIteration();
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
));
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
));
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
);
109 MediaStreamGraphImpl::ExtractPendingInput(SourceMediaStream
* aStream
,
110 GraphTime aDesiredUpToTime
,
111 bool* aEnsureNextIteration
)
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
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;
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();
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",
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;
186 FinishStream(aStream
);
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
207 if (data
->mCommands
& SourceMediaStream::TRACK_END
) {
208 // This track will end, so no point in firing not-enough-data
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);
229 MediaStreamGraphImpl::GraphTimeToStreamTime(MediaStream
* aStream
,
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
;
241 if (!aStream
->mBlocked
.GetAt(t
, &end
)) {
242 s
+= std::min(aTime
, end
) - t
;
246 return std::max
<StreamTime
>(0, s
);
250 MediaStreamGraphImpl::GraphTimeToStreamTimeOptimistic(MediaStream
* aStream
,
253 GraphTime computedUpToTime
= std::min(mStateComputedTime
, aTime
);
254 StreamTime s
= GraphTimeToStreamTime(aStream
, computedUpToTime
);
255 return s
+ (aTime
- computedUpToTime
);
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
) {
278 if (t
< mStateComputedTime
) {
279 blocked
= aStream
->mBlocked
.GetAt(t
, &end
);
280 end
= std::min(end
, mStateComputedTime
);
283 end
= GRAPH_TIME_MAX
;
288 if (streamAmount
== 0) {
289 // No more stream time to consume at time t, so we're done.
292 MediaTime consume
= std::min(end
- t
, streamAmount
);
293 streamAmount
-= consume
;
301 MediaStreamGraphImpl::GetAudioPosition(MediaStream
* aStream
)
303 if (aStream
->mAudioOutputStreams
.IsEmpty()) {
306 int64_t positionInFrames
= aStream
->mAudioOutputStreams
[0].mStream
->GetPositionInFrames();
307 if (positionInFrames
< 0) {
310 return aStream
->mAudioOutputStreams
[0].mAudioPlaybackStartTime
+
311 TicksToTimeRoundDown(aStream
->mAudioOutputStreams
[0].mStream
->GetRate(),
316 MediaStreamGraphImpl::UpdateCurrentTime()
318 GraphTime prevCurrentTime
, nextCurrentTime
;
320 TimeStamp now
= TimeStamp::Now();
321 prevCurrentTime
= mCurrentTime
;
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
)));
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
) {
359 bool blocked
= stream
->mBlocked
.GetAt(t
, &end
);
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
;
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
;
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()) {
414 GraphTime bufferEnd
=
415 StreamTimeToGraphTime(aStream
, aStream
->GetBufferEnd(),
416 INCLUDE_TRAILING_BLOCKED_INTERVAL
);
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");
427 // We should block after bufferEnd.
428 if (bufferEnd
<= aTime
) {
429 LOG(PR_LOG_DEBUG
+1, ("MediaStream %p will block due to data underrun, "
431 aStream
, MediaTimeToSeconds(bufferEnd
)));
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
439 if (bufferEnd
<= aEndBlockingDecisions
&& aStream
->mBlocked
.GetBefore(aTime
)) {
440 LOG(PR_LOG_DEBUG
+1, ("MediaStream %p will block due to speculative data underrun, "
442 aStream
, MediaTimeToSeconds(bufferEnd
)));
445 // Reconsider decisions at bufferEnd
446 *aEnd
= std::min(*aEnd
, bufferEnd
);
451 MediaStreamGraphImpl::MarkConsumed(MediaStream
* aStream
)
453 if (aStream
->mIsConsumed
) {
456 aStream
->mIsConsumed
= true;
458 ProcessedMediaStream
* ps
= aStream
->AsProcessedStream();
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
);
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();
477 iter
->AsProcessedStream()->mInCycle
= true;
478 iter
= iter
->getPrevious();
479 } while (iter
!= stream
);
482 ProcessedMediaStream
* ps
= stream
->AsProcessedStream();
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());
494 stream
->mIsOnOrderingStack
= false;
497 stream
->mHasBeenOrdered
= true;
498 *mStreams
.AppendElement() = stream
.forget();
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();
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()) {
524 if (!s
->mHasBeenOrdered
) {
525 UpdateStreamOrderForStream(&stack
, s
.forget());
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
);
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;
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();
574 MediaStreamGraphImpl::AddBlockingRelatedStreamsToSet(nsTArray
<MediaStream
*>* aStreams
,
575 MediaStream
* aStream
)
577 if (aStream
->mInBlockingSet
)
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();
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
);
599 MediaStreamGraphImpl::MarkStreamBlocking(MediaStream
* aStream
)
601 if (aStream
->mBlockInThisPhase
)
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();
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
);
622 MediaStreamGraphImpl::RecomputeBlockingAt(const nsTArray
<MediaStream
*>& aStreams
,
624 GraphTime aEndBlockingDecisions
,
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
;
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
);
652 bool explicitBlock
= stream
->mExplicitBlockerCount
.GetAt(aTime
, &end
) > 0;
653 *aEnd
= std::min(*aEnd
, end
);
655 LOG(PR_LOG_DEBUG
+1, ("MediaStream %p is blocked due to explicit blocker", stream
));
656 MarkStreamBlocking(stream
);
660 bool underrun
= WillUnderrun(stream
, aTime
, aEndBlockingDecisions
, aEnd
);
662 // We'll block indefinitely
663 MarkStreamBlocking(stream
);
664 *aEnd
= aEndBlockingDecisions
;
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
);
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;
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()) {
703 for (i
= 0; i
< audioOutputStreamsFound
.Length(); ++i
) {
704 if (aStream
->mAudioOutputStreams
[i
].mTrackID
== tracks
->GetID()) {
708 if (i
< audioOutputStreamsFound
.Length()) {
709 audioOutputStreamsFound
[i
] = true;
711 // No output stream created for this track yet. Check if it's time to
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.
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
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
);
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()) {
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.
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.
776 bool blocked
= aStream
->mBlocked
.GetAt(t
, &end
);
777 end
= std::min(end
, aTo
);
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
)));
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
);
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
);
837 MediaStreamGraphImpl::PlayVideo(MediaStream
* aStream
)
839 MOZ_ASSERT(mRealtime
, "Should only attempt to play video in realtime mode");
841 if (aStream
->mVideoOutputs
.IsEmpty())
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
);
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()) {
862 track
= tracks
.get();
865 if (!frame
|| *frame
== aStream
->mLastPlayedVideoFrame
)
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);
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
,
890 output
->SetCurrentFrame(frame
->GetIntrinsicSize(), frame
->GetImage(),
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
;
904 MediaStreamGraphImpl::ShouldUpdateMainThread()
910 TimeStamp now
= TimeStamp::Now();
911 if ((now
- mLastMainThreadUpdate
).ToMilliseconds() > MEDIA_GRAPH_TARGET_PERIOD_MS
) {
912 mLastMainThreadUpdate
= now
;
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()) {
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
=
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
949 !mUpdateRunnables
.IsEmpty() ||
950 !mStreamUpdates
.IsEmpty()) {
951 EnsureStableStateEventPosted();
956 MediaStreamGraphImpl::EnsureImmediateWakeUpLocked(MonitorAutoLock
& aLock
)
958 if (mWaitState
== WAITSTATE_WAITING_FOR_NEXT_ITERATION
||
959 mWaitState
== WAITSTATE_WAITING_INDEFINITELY
) {
960 mWaitState
= WAITSTATE_WAKING_UP
;
966 MediaStreamGraphImpl::EnsureNextIteration()
968 MonitorAutoLock
lock(mMonitor
);
969 EnsureNextIterationLocked(lock
);
973 MediaStreamGraphImpl::EnsureNextIterationLocked(MonitorAutoLock
& aLock
)
975 if (mNeedAnotherIteration
)
977 mNeedAnotherIteration
= true;
978 if (mWaitState
== WAITSTATE_WAITING_INDEFINITELY
) {
979 mWaitState
= WAITSTATE_WAKING_UP
;
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
994 ((((blocksAtIdealaSampleRate
+ 1)*WEBAUDIO_BLOCK_SIZE
) << MEDIA_TIME_FRAC_BITS
)
995 + aSampleRate
- 1)/aSampleRate
;
999 MediaStreamGraphImpl::ProduceDataForStreamsBlockByBlock(uint32_t aStreamIndex
,
1000 TrackRate aSampleRate
,
1004 GraphTime t
= aFrom
;
1006 GraphTime next
= RoundUpToAudioBlock(aSampleRate
, t
+ 1);
1007 for (uint32_t i
= aStreamIndex
; i
< mStreams
.Length(); ++i
) {
1008 nsRefPtr
<ProcessedMediaStream
> ps
= mStreams
[i
]->AsProcessedStream();
1010 ps
->ProduceOutput(t
, next
);
1015 NS_ASSERTION(t
== aTo
, "Something went wrong with rounding to block boundaries");
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;
1031 NS_ASSERTION(!mNonRealtimeIsRunning
,
1032 "We should not be running in non-realtime mode already");
1033 mNonRealtimeIsRunning
= true;
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
) {
1052 messageQueue
.Clear();
1054 UpdateStreamOrder();
1056 // Find the sampling rate that we need to use for non-realtime graphs.
1057 TrackRate sampleRate
= IdealAudioRate();
1059 for (uint32_t i
= 0; i
< mStreams
.Length(); ++i
) {
1060 AudioNodeStream
* n
= mStreams
[i
]->AsAudioNodeStream();
1062 // We know that the rest of the streams will run at the same rate.
1063 sampleRate
= n
->SampleRate();
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();
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();
1097 AudioNodeStream
* n
= stream
->AsAudioNodeStream();
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();
1104 MOZ_ASSERT(n
->SampleRate() == nextStream
->SampleRate(),
1105 "All AudioNodeStreams in the graph must have the same sampling rate");
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;
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
);
1124 // Only playback audio and video in real-time mode
1125 CreateOrDestroyAudioStreams(prevComputedTime
, stream
);
1126 PlayAudio(stream
, prevComputedTime
, mStateComputedTime
);
1127 audioStreamsActive
+= stream
->mAudioOutputStreams
.Length();
1130 SourceMediaStream
* is
= stream
->AsSourceStream();
1132 UpdateBufferSufficiencyState(is
);
1135 if (!stream
->mBlocked
.GetAt(mCurrentTime
, &end
) || end
< GRAPH_TIME_MAX
) {
1136 allBlockedForever
= false;
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
1157 MonitorAutoLock
lock(mMonitor
);
1158 bool finalUpdate
= (mForceShutDown
||
1159 (IsEmpty() && mMessageQueue
.IsEmpty()));
1160 PrepareUpdatesToMainThreadState(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.
1172 // No need to wait in non-realtime mode, just churn through the input as soon
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
;
1188 mWaitState
= WAITSTATE_WAITING_INDEFINITELY
;
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
);
1204 mNonRealtimeIsRunning
= false;
1206 profiler_unregister_thread();
1210 MediaStreamGraphImpl::ApplyStreamUpdate(StreamUpdate
* aUpdate
)
1212 mMonitor
.AssertCurrentThreadOwns();
1214 MediaStream
* stream
= aUpdate
->mStream
;
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();
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));
1233 mThread
->Shutdown();
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
);
1252 class MediaStreamGraphInitThreadRunnable
: public nsRunnable
{
1254 explicit MediaStreamGraphInitThreadRunnable(MediaStreamGraphImpl
* aGraph
)
1261 profiler_register_thread("MediaStreamGraph", &aLocal
);
1262 mGraph
->RunThread();
1266 MediaStreamGraphImpl
* mGraph
;
1269 class MediaStreamGraphThreadRunnable
: public nsRunnable
{
1271 explicit MediaStreamGraphThreadRunnable(MediaStreamGraphImpl
* aGraph
)
1277 mGraph
->RunThread();
1281 MediaStreamGraphImpl
* mGraph
;
1284 class MediaStreamGraphShutDownRunnable
: public nsRunnable
{
1286 MediaStreamGraphShutDownRunnable(MediaStreamGraphImpl
* aGraph
) : mGraph(aGraph
) {}
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.
1301 for (uint32_t i
= 0; i
< mGraph
->mStreams
.Length(); ++i
) {
1302 DOMMediaStream
* s
= mGraph
->mStreams
[i
]->GetWrapper();
1304 s
->NotifyMediaStreamGraphShutdown();
1308 NS_ASSERTION(mGraph
->mForceShutDown
, "Not in forced shutdown?");
1309 mGraph
->mLifecycleState
=
1310 MediaStreamGraphImpl::LIFECYCLE_WAITING_FOR_STREAM_DESTRUCTION
;
1315 MediaStreamGraphImpl
* mGraph
;
1318 class MediaStreamGraphStableStateRunnable
: public nsRunnable
{
1320 explicit MediaStreamGraphStableStateRunnable(MediaStreamGraphImpl
* aGraph
)
1327 mGraph
->RunInStableState();
1332 MediaStreamGraphImpl
* mGraph
;
1336 * Control messages forwarded from main thread to graph manager thread
1338 class CreateMessage
: public ControlMessage
{
1340 CreateMessage(MediaStream
* aStream
) : ControlMessage(aStream
) {}
1341 virtual void Run() MOZ_OVERRIDE
1343 mStream
->GraphImpl()->AddStream(mStream
);
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.
1355 class MediaStreamGraphShutdownObserver MOZ_FINAL
: public nsIObserver
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
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
);
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
);
1464 MediaStreamGraphImpl::EnsureRunInStableState()
1466 NS_ASSERTION(NS_IsMainThread(), "main thread only");
1468 if (mPostedRunInStableState
)
1470 mPostedRunInStableState
= true;
1471 nsCOMPtr
<nsIRunnable
> event
= new MediaStreamGraphStableStateRunnable(this);
1472 nsCOMPtr
<nsIAppShell
> appShell
= do_GetService(kAppShellCID
);
1474 appShell
->RunInStableState(event
);
1476 NS_ERROR("Appshell already destroyed?");
1481 MediaStreamGraphImpl::EnsureStableStateEventPosted()
1483 mMonitor
.AssertCurrentThreadOwns();
1485 if (mPostedRunInStableStateEvent
)
1487 mPostedRunInStableStateEvent
= true;
1488 nsCOMPtr
<nsIRunnable
> event
= new MediaStreamGraphStableStateRunnable(this);
1489 NS_DispatchToMainThread(event
);
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
1506 // This should only happen during forced shutdown.
1507 aMessage
->RunDuringShutdown();
1510 mLifecycleState
>= LIFECYCLE_WAITING_FOR_STREAM_DESTRUCTION
) {
1511 if (gGraph
== 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;
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();
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()
1548 MediaStream::Graph()
1554 MediaStream::SetGraphImpl(MediaStreamGraphImpl
* aGraph
)
1556 MOZ_ASSERT(!mGraph
, "Should only be called once");
1561 MediaStream::SetGraphImpl(MediaStreamGraph
* aGraph
)
1563 MediaStreamGraphImpl
* graph
= static_cast<MediaStreamGraphImpl
*>(aGraph
);
1564 SetGraphImpl(graph
);
1568 MediaStream::GraphTimeToStreamTime(GraphTime aTime
)
1570 return GraphImpl()->GraphTimeToStreamTime(this, aTime
);
1574 MediaStream::GraphTimeToStreamTimeOptimistic(GraphTime aTime
)
1576 return GraphImpl()->GraphTimeToStreamTimeOptimistic(this, aTime
);
1580 MediaStream::StreamTimeToGraphTime(StreamTime aTime
)
1582 return GraphImpl()->StreamTimeToGraphTime(this, aTime
, 0);
1586 MediaStream::FinishOnGraphThread()
1588 GraphImpl()->FinishStream(this);
1592 MediaStream::GetProcessingGraphUpdateIndex()
1594 return GraphImpl()->GetProcessingGraphUpdateIndex();
1597 StreamBuffer::Track
*
1598 MediaStream::EnsureTrack(TrackID aTrackId
, TrackRate aSampleRate
)
1600 StreamBuffer::Track
* track
= mBuffer
.FindTrack(aTrackId
);
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
,
1609 track
= &mBuffer
.AddTrack(aTrackId
, aSampleRate
, 0, segment
.forget());
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());
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();
1639 MediaStream::Destroy()
1641 // Keep this stream alive until we leave this method
1642 nsRefPtr
<MediaStream
> kungFuDeathGrip
= this;
1644 class Message
: public ControlMessage
{
1646 Message(MediaStream
* aStream
) : ControlMessage(aStream
) {}
1649 mStream
->DestroyImpl();
1650 mStream
->GraphImpl()->RemoveStream(mStream
);
1652 virtual void RunDuringShutdown()
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
1660 mMainThreadDestroyed
= true;
1664 MediaStream::AddAudioOutput(void* aKey
)
1666 class Message
: public ControlMessage
{
1668 Message(MediaStream
* aStream
, void* aKey
) : ControlMessage(aStream
), mKey(aKey
) {}
1671 mStream
->AddAudioOutputImpl(mKey
);
1675 GraphImpl()->AppendMessage(new Message(this, aKey
));
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
;
1687 NS_ERROR("Audio output key not found");
1691 MediaStream::SetAudioOutputVolume(void* aKey
, float aVolume
)
1693 class Message
: public ControlMessage
{
1695 Message(MediaStream
* aStream
, void* aKey
, float aVolume
) :
1696 ControlMessage(aStream
), mKey(aKey
), mVolume(aVolume
) {}
1699 mStream
->SetAudioOutputVolumeImpl(mKey
, mVolume
);
1704 GraphImpl()->AppendMessage(new Message(this, aKey
, aVolume
));
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
);
1716 NS_ERROR("Audio output key not found");
1720 MediaStream::RemoveAudioOutput(void* aKey
)
1722 class Message
: public ControlMessage
{
1724 Message(MediaStream
* aStream
, void* aKey
) :
1725 ControlMessage(aStream
), mKey(aKey
) {}
1728 mStream
->RemoveAudioOutputImpl(mKey
);
1732 GraphImpl()->AppendMessage(new Message(this, aKey
));
1736 MediaStream::AddVideoOutput(VideoFrameContainer
* aContainer
)
1738 class Message
: public ControlMessage
{
1740 Message(MediaStream
* aStream
, VideoFrameContainer
* aContainer
) :
1741 ControlMessage(aStream
), mContainer(aContainer
) {}
1744 mStream
->AddVideoOutputImpl(mContainer
.forget());
1746 nsRefPtr
<VideoFrameContainer
> mContainer
;
1748 GraphImpl()->AppendMessage(new Message(this, aContainer
));
1752 MediaStream::RemoveVideoOutput(VideoFrameContainer
* aContainer
)
1754 class Message
: public ControlMessage
{
1756 Message(MediaStream
* aStream
, VideoFrameContainer
* aContainer
) :
1757 ControlMessage(aStream
), mContainer(aContainer
) {}
1760 mStream
->RemoveVideoOutputImpl(mContainer
);
1762 nsRefPtr
<VideoFrameContainer
> mContainer
;
1764 GraphImpl()->AppendMessage(new Message(this, aContainer
));
1768 MediaStream::ChangeExplicitBlockerCount(int32_t aDelta
)
1770 class Message
: public ControlMessage
{
1772 Message(MediaStream
* aStream
, int32_t aDelta
) :
1773 ControlMessage(aStream
), mDelta(aDelta
) {}
1776 mStream
->ChangeExplicitBlockerCountImpl(
1777 mStream
->GraphImpl()->mStateComputedTime
, mDelta
);
1781 GraphImpl()->AppendMessage(new Message(this, aDelta
));
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());
1799 MediaStream::AddListener(MediaStreamListener
* aListener
)
1801 class Message
: public ControlMessage
{
1803 Message(MediaStream
* aStream
, MediaStreamListener
* aListener
) :
1804 ControlMessage(aStream
), mListener(aListener
) {}
1807 mStream
->AddListenerImpl(mListener
.forget());
1809 nsRefPtr
<MediaStreamListener
> mListener
;
1811 GraphImpl()->AppendMessage(new Message(this, aListener
));
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());
1824 MediaStream::RemoveListener(MediaStreamListener
* aListener
)
1826 class Message
: public ControlMessage
{
1828 Message(MediaStream
* aStream
, MediaStreamListener
* aListener
) :
1829 ControlMessage(aStream
), mListener(aListener
) {}
1832 mStream
->RemoveListenerImpl(mListener
);
1834 nsRefPtr
<MediaStreamListener
> mListener
;
1836 // If the stream is destroyed the Listeners have or will be
1838 if (!IsDestroyed()) {
1839 GraphImpl()->AppendMessage(new Message(this, aListener
));
1844 MediaStream::SetTrackEnabledImpl(TrackID aTrackID
, bool aEnabled
)
1847 mDisabledTrackIDs
.RemoveElement(aTrackID
);
1849 if (!mDisabledTrackIDs
.Contains(aTrackID
)) {
1850 mDisabledTrackIDs
.AppendElement(aTrackID
);
1856 MediaStream::SetTrackEnabled(TrackID aTrackID
, bool aEnabled
)
1858 class Message
: public ControlMessage
{
1860 Message(MediaStream
* aStream
, TrackID aTrackID
, bool aEnabled
) :
1861 ControlMessage(aStream
), mTrackID(aTrackID
), mEnabled(aEnabled
) {}
1864 mStream
->SetTrackEnabledImpl(mTrackID
, mEnabled
);
1869 GraphImpl()->AppendMessage(new Message(this, aTrackID
, aEnabled
));
1873 MediaStream::ApplyTrackDisabling(TrackID aTrackID
, MediaSegment
* aSegment
)
1875 if (!mDisabledTrackIDs
.Contains(aTrackID
)) {
1879 switch (aSegment
->GetType()) {
1880 case MediaSegment::AUDIO
: {
1881 TrackTicks duration
= aSegment
->GetDuration();
1883 aSegment
->AppendNullData(duration
);
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);
1895 MOZ_CRASH("Unknown track type");
1900 SourceMediaStream::DestroyImpl()
1903 MutexAutoLock
lock(mMutex
);
1906 MediaStream::DestroyImpl();
1910 SourceMediaStream::SetPullEnabled(bool aEnabled
)
1912 MutexAutoLock
lock(mMutex
);
1913 mPullEnabled
= aEnabled
;
1914 if (mPullEnabled
&& !mDestroyed
) {
1915 GraphImpl()->EnsureNextIteration();
1920 SourceMediaStream::AddTrack(TrackID aID
, TrackRate aRate
, TrackTicks aStart
,
1921 MediaSegment
* aSegment
)
1923 MutexAutoLock
lock(mMutex
);
1924 TrackData
* data
= mUpdateTracks
.AppendElement();
1926 data
->mRate
= aRate
;
1927 data
->mStart
= aStart
;
1928 data
->mCommands
= TRACK_CREATE
;
1929 data
->mData
= aSegment
;
1930 data
->mHaveEnough
= false;
1932 GraphImpl()->EnsureNextIteration();
1937 SourceMediaStream::AppendToTrack(TrackID aID
, MediaSegment
* aSegment
)
1939 MutexAutoLock
lock(mMutex
);
1940 // ::EndAllTrackAndFinished() can end these before the sources notice
1941 bool appended
= false;
1943 TrackData
*track
= FindDataForTrack(aID
);
1945 track
->mData
->AppendFrom(aSegment
);
1952 GraphImpl()->EnsureNextIteration();
1958 SourceMediaStream::HaveEnoughBuffered(TrackID aID
)
1960 MutexAutoLock
lock(mMutex
);
1961 TrackData
*track
= FindDataForTrack(aID
);
1963 return track
->mHaveEnough
;
1969 SourceMediaStream::DispatchWhenNotEnoughBuffered(TrackID aID
,
1970 nsIThread
* aSignalThread
, nsIRunnable
* aSignalRunnable
)
1972 MutexAutoLock
lock(mMutex
);
1973 TrackData
* data
= FindDataForTrack(aID
);
1975 aSignalThread
->Dispatch(aSignalRunnable
, 0);
1979 if (data
->mHaveEnough
) {
1980 data
->mDispatchWhenNotEnough
.AppendElement()->Init(aSignalThread
, aSignalRunnable
);
1982 aSignalThread
->Dispatch(aSignalRunnable
, 0);
1987 SourceMediaStream::EndTrack(TrackID aID
)
1989 MutexAutoLock
lock(mMutex
);
1990 // ::EndAllTrackAndFinished() can end these before the sources call this
1992 TrackData
*track
= FindDataForTrack(aID
);
1994 track
->mCommands
|= TRACK_END
;
1998 GraphImpl()->EnsureNextIteration();
2003 SourceMediaStream::AdvanceKnownTracksTime(StreamTime aKnownTime
)
2005 MutexAutoLock
lock(mMutex
);
2006 mUpdateKnownTracksTime
= aKnownTime
;
2008 GraphImpl()->EnsureNextIteration();
2013 SourceMediaStream::FinishWithLockHeld()
2015 mMutex
.AssertCurrentThreadOwns();
2016 mUpdateFinished
= true;
2018 GraphImpl()->EnsureNextIteration();
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
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
;
2046 MediaInputPort::Disconnect()
2048 NS_ASSERTION(!mSource
== !mDest
,
2049 "mSource must either both be null or both non-null");
2053 mSource
->RemoveConsumer(this);
2055 mDest
->RemoveInput(this);
2059 MediaInputPort::InputInterval
2060 MediaInputPort::GetNextInputInterval(GraphTime aTime
)
2062 InputInterval result
= { GRAPH_TIME_MAX
, GRAPH_TIME_MAX
, false };
2063 GraphTime t
= aTime
;
2066 if (!mDest
->mBlocked
.GetAt(t
, &end
))
2068 if (end
== GRAPH_TIME_MAX
)
2073 GraphTime sourceEnd
;
2074 result
.mInputIsBlocked
= mSource
->mBlocked
.GetAt(t
, &sourceEnd
);
2075 result
.mEnd
= std::min(end
, sourceEnd
);
2080 MediaInputPort::Destroy()
2082 class Message
: public ControlMessage
{
2084 Message(MediaInputPort
* aPort
)
2085 : ControlMessage(nullptr), mPort(aPort
) {}
2088 mPort
->Disconnect();
2089 --mPort
->GraphImpl()->mPortCount
;
2092 virtual void RunDuringShutdown()
2096 MediaInputPort
* mPort
;
2098 GraphImpl()->AppendMessage(new Message(this));
2101 MediaStreamGraphImpl
*
2102 MediaInputPort::GraphImpl()
2108 MediaInputPort::Graph()
2114 MediaInputPort::SetGraphImpl(MediaStreamGraphImpl
* aGraph
)
2116 MOZ_ASSERT(!mGraph
, "Should only be called once");
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
{
2128 Message(MediaInputPort
* aPort
)
2129 : ControlMessage(aPort
->GetDestination()),
2134 // The graph holds its reference implicitly
2137 virtual void RunDuringShutdown()
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();
2151 ProcessedMediaStream::Finish()
2153 class Message
: public ControlMessage
{
2155 Message(ProcessedMediaStream
* aStream
)
2156 : ControlMessage(aStream
) {}
2159 mStream
->GraphImpl()->FinishStream(mStream
);
2162 GraphImpl()->AppendMessage(new Message(this));
2166 ProcessedMediaStream::SetAutofinish(bool aAutofinish
)
2168 class Message
: public ControlMessage
{
2170 Message(ProcessedMediaStream
* aStream
, bool aAutofinish
)
2171 : ControlMessage(aStream
), mAutofinish(aAutofinish
) {}
2174 static_cast<ProcessedMediaStream
*>(mStream
)->SetAutofinishImpl(mAutofinish
);
2178 GraphImpl()->AppendMessage(new Message(this, aAutofinish
));
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)
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)
2215 if (!gMediaStreamGraphLog
) {
2216 gMediaStreamGraphLog
= PR_NewLogModule("MediaStreamGraph");
2220 mCurrentTimeStamp
= mInitialTimeStamp
= mLastMainThreadUpdate
= TimeStamp::Now();
2223 NS_IMPL_ISUPPORTS1(MediaStreamGraphShutdownObserver
, nsIObserver
)
2225 static bool gShutdownObserverRegistered
= false;
2228 MediaStreamGraphShutdownObserver::Observe(nsISupports
*aSubject
,
2230 const PRUnichar
*aData
)
2232 if (strcmp(aTopic
, NS_XPCOM_SHUTDOWN_OBSERVER_ID
) == 0) {
2234 gGraph
->ForceShutDown();
2236 nsContentUtils::UnregisterShutdownObserver(this);
2237 gShutdownObserverRegistered
= false;
2243 MediaStreamGraph::GetInstance()
2245 NS_ASSERTION(NS_IsMainThread(), "Main thread only");
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
));
2261 MediaStreamGraph::CreateNonRealtimeInstance()
2263 NS_ASSERTION(NS_IsMainThread(), "Main thread only");
2265 MediaStreamGraphImpl
* graph
= new MediaStreamGraphImpl(false);
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();
2284 MediaStreamGraph::CreateSourceStream(DOMMediaStream
* aWrapper
)
2286 SourceMediaStream
* stream
= new SourceMediaStream(aWrapper
);
2288 MediaStreamGraphImpl
* graph
= static_cast<MediaStreamGraphImpl
*>(this);
2289 stream
->SetGraphImpl(graph
);
2290 graph
->AppendMessage(new CreateMessage(stream
));
2294 ProcessedMediaStream
*
2295 MediaStreamGraph::CreateTrackUnionStream(DOMMediaStream
* aWrapper
)
2297 TrackUnionStream
* stream
= new TrackUnionStream(aWrapper
);
2299 MediaStreamGraphImpl
* graph
= static_cast<MediaStreamGraphImpl
*>(this);
2300 stream
->SetGraphImpl(graph
);
2301 graph
->AppendMessage(new CreateMessage(stream
));
2306 MediaStreamGraph::CreateAudioNodeStream(AudioNodeEngine
* aEngine
,
2307 AudioNodeStreamKind aKind
,
2308 TrackRate aSampleRate
)
2310 MOZ_ASSERT(NS_IsMainThread());
2312 aSampleRate
= aEngine
->NodeMainThread()->Context()->SampleRate();
2314 AudioNodeStream
* stream
= new AudioNodeStream(aEngine
, aKind
, aSampleRate
);
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
));
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
)
2337 graph
->mNonRealtimeTicksToProcess
= aTicksToProcess
;
2338 graph
->mNonRealtimeProcessing
= true;
2339 graph
->EnsureRunInStableState();