Line data Source code
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/. */
5 :
6 : #include "MediaStreamGraphImpl.h"
7 : #include "mozilla/MathAlgorithms.h"
8 : #include "mozilla/SizePrintfMacros.h"
9 : #include "mozilla/Unused.h"
10 :
11 : #include "AudioSegment.h"
12 : #include "VideoSegment.h"
13 : #include "nsContentUtils.h"
14 : #include "nsIObserver.h"
15 : #include "nsPrintfCString.h"
16 : #include "nsServiceManagerUtils.h"
17 : #include "prerror.h"
18 : #include "mozilla/Logging.h"
19 : #include "mozilla/Attributes.h"
20 : #include "TrackUnionStream.h"
21 : #include "ImageContainer.h"
22 : #include "AudioCaptureStream.h"
23 : #include "AudioChannelService.h"
24 : #include "AudioNodeStream.h"
25 : #include "AudioNodeExternalInputStream.h"
26 : #include "MediaStreamListener.h"
27 : #include "MediaStreamVideoSink.h"
28 : #include "mozilla/dom/BaseAudioContextBinding.h"
29 : #include "mozilla/media/MediaUtils.h"
30 : #include <algorithm>
31 : #include "GeckoProfiler.h"
32 : #include "VideoFrameContainer.h"
33 : #include "mozilla/AbstractThread.h"
34 : #include "mozilla/Unused.h"
35 : #ifdef MOZ_WEBRTC
36 : #include "AudioOutputObserver.h"
37 : #endif
38 : #include "mtransport/runnable_utils.h"
39 :
40 : #include "webaudio/blink/DenormalDisabler.h"
41 : #include "webaudio/blink/HRTFDatabaseLoader.h"
42 :
43 : using namespace mozilla::layers;
44 : using namespace mozilla::dom;
45 : using namespace mozilla::gfx;
46 : using namespace mozilla::media;
47 :
48 : namespace mozilla {
49 :
50 : LazyLogModule gMediaStreamGraphLog("MediaStreamGraph");
51 : #ifdef LOG
52 : #undef LOG
53 : #endif // LOG
54 : #define LOG(type, msg) MOZ_LOG(gMediaStreamGraphLog, type, msg)
55 :
56 : enum SourceMediaStream::TrackCommands : uint32_t {
57 : TRACK_CREATE = TrackEventCommand::TRACK_EVENT_CREATED,
58 : TRACK_END = TrackEventCommand::TRACK_EVENT_ENDED,
59 : TRACK_UNUSED = TrackEventCommand::TRACK_EVENT_UNUSED,
60 : };
61 :
62 : /**
63 : * A hash table containing the graph instances, one per AudioChannel.
64 : */
65 3 : static nsDataHashtable<nsUint32HashKey, MediaStreamGraphImpl*> gGraphs;
66 :
67 0 : MediaStreamGraphImpl::~MediaStreamGraphImpl()
68 : {
69 0 : NS_ASSERTION(IsEmpty(),
70 : "All streams should have been destroyed by messages from the main thread");
71 0 : LOG(LogLevel::Debug, ("MediaStreamGraph %p destroyed", this));
72 0 : LOG(LogLevel::Debug, ("MediaStreamGraphImpl::~MediaStreamGraphImpl"));
73 0 : }
74 :
75 : void
76 0 : MediaStreamGraphImpl::FinishStream(MediaStream* aStream)
77 : {
78 0 : if (aStream->mFinished)
79 0 : return;
80 0 : LOG(LogLevel::Debug, ("MediaStream %p will finish", aStream));
81 : #ifdef DEBUG
82 0 : for (StreamTracks::TrackIter track(aStream->mTracks);
83 0 : !track.IsEnded(); track.Next()) {
84 0 : if (!track->IsEnded()) {
85 0 : LOG(LogLevel::Error,
86 : ("MediaStream %p will finish, but track %d has not ended.",
87 : aStream,
88 : track->GetID()));
89 0 : NS_ASSERTION(false, "Finished stream cannot contain live track");
90 : }
91 : }
92 : #endif
93 0 : aStream->mFinished = true;
94 0 : aStream->mTracks.AdvanceKnownTracksTime(STREAM_TIME_MAX);
95 :
96 0 : SetStreamOrderDirty();
97 : }
98 :
99 : void
100 0 : MediaStreamGraphImpl::AddStreamGraphThread(MediaStream* aStream)
101 : {
102 0 : aStream->mTracksStartTime = mProcessedTime;
103 :
104 0 : if (aStream->AsSourceStream()) {
105 0 : SourceMediaStream* source = aStream->AsSourceStream();
106 0 : TimeStamp currentTimeStamp = CurrentDriver()->GetCurrentTimeStamp();
107 : TimeStamp processedTimeStamp = currentTimeStamp +
108 0 : TimeDuration::FromSeconds(MediaTimeToSeconds(mProcessedTime - IterationEnd()));
109 0 : source->SetStreamTracksStartTimeStamp(processedTimeStamp);
110 : }
111 :
112 0 : if (aStream->IsSuspended()) {
113 0 : mSuspendedStreams.AppendElement(aStream);
114 0 : LOG(LogLevel::Debug,
115 : ("Adding media stream %p to the graph, in the suspended stream array",
116 : aStream));
117 : } else {
118 0 : mStreams.AppendElement(aStream);
119 0 : LOG(LogLevel::Debug,
120 : ("Adding media stream %p to graph %p, count %" PRIuSIZE,
121 : aStream,
122 : this,
123 : mStreams.Length()));
124 0 : LOG(LogLevel::Debug,
125 : ("Adding media stream %p to graph %p, count %" PRIuSIZE,
126 : aStream,
127 : this,
128 : mStreams.Length()));
129 : }
130 :
131 0 : SetStreamOrderDirty();
132 0 : }
133 :
134 : void
135 0 : MediaStreamGraphImpl::RemoveStreamGraphThread(MediaStream* aStream)
136 : {
137 : // Remove references in mStreamUpdates before we allow aStream to die.
138 : // Pending updates are not needed (since the main thread has already given
139 : // up the stream) so we will just drop them.
140 : {
141 0 : MonitorAutoLock lock(mMonitor);
142 0 : for (uint32_t i = 0; i < mStreamUpdates.Length(); ++i) {
143 0 : if (mStreamUpdates[i].mStream == aStream) {
144 0 : mStreamUpdates[i].mStream = nullptr;
145 : }
146 : }
147 : }
148 :
149 : // Ensure that mFirstCycleBreaker and mMixer are updated when necessary.
150 0 : SetStreamOrderDirty();
151 :
152 0 : if (aStream->IsSuspended()) {
153 0 : mSuspendedStreams.RemoveElement(aStream);
154 : } else {
155 0 : mStreams.RemoveElement(aStream);
156 : }
157 :
158 0 : LOG(LogLevel::Debug,
159 : ("Removed media stream %p from graph %p, count %" PRIuSIZE,
160 : aStream,
161 : this,
162 : mStreams.Length()));
163 0 : LOG(LogLevel::Debug,
164 : ("Removed media stream %p from graph %p, count %" PRIuSIZE,
165 : aStream,
166 : this,
167 : mStreams.Length()));
168 :
169 0 : NS_RELEASE(aStream); // probably destroying it
170 0 : }
171 :
172 : void
173 0 : MediaStreamGraphImpl::ExtractPendingInput(SourceMediaStream* aStream,
174 : GraphTime aDesiredUpToTime,
175 : bool* aEnsureNextIteration)
176 : {
177 : bool finished;
178 : {
179 0 : MutexAutoLock lock(aStream->mMutex);
180 0 : if (aStream->mPullEnabled && !aStream->mFinished &&
181 0 : !aStream->mListeners.IsEmpty()) {
182 : // Compute how much stream time we'll need assuming we don't block
183 : // the stream at all.
184 0 : StreamTime t = aStream->GraphTimeToStreamTime(aDesiredUpToTime);
185 0 : LOG(LogLevel::Verbose,
186 : ("Calling NotifyPull aStream=%p t=%f current end=%f",
187 : aStream,
188 : MediaTimeToSeconds(t),
189 : MediaTimeToSeconds(aStream->mTracks.GetEnd())));
190 0 : if (t > aStream->mTracks.GetEnd()) {
191 0 : *aEnsureNextIteration = true;
192 : #ifdef DEBUG
193 0 : if (aStream->mListeners.Length() == 0) {
194 0 : LOG(
195 : LogLevel::Error,
196 : ("No listeners in NotifyPull aStream=%p desired=%f current end=%f",
197 : aStream,
198 : MediaTimeToSeconds(t),
199 : MediaTimeToSeconds(aStream->mTracks.GetEnd())));
200 0 : aStream->DumpTrackInfo();
201 : }
202 : #endif
203 0 : for (uint32_t j = 0; j < aStream->mListeners.Length(); ++j) {
204 0 : MediaStreamListener* l = aStream->mListeners[j];
205 : {
206 0 : MutexAutoUnlock unlock(aStream->mMutex);
207 0 : l->NotifyPull(this, t);
208 : }
209 : }
210 : }
211 : }
212 0 : finished = aStream->mUpdateFinished;
213 0 : bool shouldNotifyTrackCreated = false;
214 0 : for (int32_t i = aStream->mUpdateTracks.Length() - 1; i >= 0; --i) {
215 0 : SourceMediaStream::TrackData* data = &aStream->mUpdateTracks[i];
216 0 : aStream->ApplyTrackDisabling(data->mID, data->mData);
217 : // Dealing with NotifyQueuedTrackChanges and NotifyQueuedAudioData part.
218 :
219 : // The logic is different from the manipulating of aStream->mTracks part.
220 : // So it is not combined with the manipulating of aStream->mTracks part.
221 : StreamTime offset =
222 0 : (data->mCommands & SourceMediaStream::TRACK_CREATE)
223 0 : ? data->mStart
224 0 : : aStream->mTracks.FindTrack(data->mID)->GetSegment()->GetDuration();
225 :
226 : // Audio case.
227 0 : if (data->mData->GetType() == MediaSegment::AUDIO) {
228 0 : if (data->mCommands) {
229 0 : MOZ_ASSERT(!(data->mCommands & SourceMediaStream::TRACK_UNUSED));
230 0 : for (MediaStreamListener* l : aStream->mListeners) {
231 0 : if (data->mCommands & SourceMediaStream::TRACK_END) {
232 0 : l->NotifyQueuedAudioData(this, data->mID,
233 0 : offset, *(static_cast<AudioSegment*>(data->mData.get())));
234 : }
235 0 : l->NotifyQueuedTrackChanges(this, data->mID,
236 0 : offset, static_cast<TrackEventCommand>(data->mCommands), *data->mData);
237 0 : if (data->mCommands & SourceMediaStream::TRACK_CREATE) {
238 0 : l->NotifyQueuedAudioData(this, data->mID,
239 0 : offset, *(static_cast<AudioSegment*>(data->mData.get())));
240 : }
241 : }
242 : } else {
243 0 : for (MediaStreamListener* l : aStream->mListeners) {
244 0 : l->NotifyQueuedAudioData(this, data->mID,
245 0 : offset, *(static_cast<AudioSegment*>(data->mData.get())));
246 : }
247 : }
248 : }
249 :
250 : // Video case.
251 0 : if (data->mData->GetType() == MediaSegment::VIDEO) {
252 0 : if (data->mCommands) {
253 0 : MOZ_ASSERT(!(data->mCommands & SourceMediaStream::TRACK_UNUSED));
254 0 : for (MediaStreamListener* l : aStream->mListeners) {
255 0 : l->NotifyQueuedTrackChanges(this, data->mID,
256 0 : offset, static_cast<TrackEventCommand>(data->mCommands), *data->mData);
257 : }
258 : }
259 : }
260 :
261 0 : for (TrackBound<MediaStreamTrackListener>& b : aStream->mTrackListeners) {
262 0 : if (b.mTrackID != data->mID) {
263 0 : continue;
264 : }
265 0 : b.mListener->NotifyQueuedChanges(this, offset, *data->mData);
266 0 : if (data->mCommands & SourceMediaStream::TRACK_END) {
267 0 : b.mListener->NotifyEnded();
268 : }
269 : }
270 0 : if (data->mCommands & SourceMediaStream::TRACK_CREATE) {
271 0 : MediaSegment* segment = data->mData.forget();
272 0 : LOG(LogLevel::Debug,
273 : ("SourceMediaStream %p creating track %d, start %" PRId64
274 : ", initial end %" PRId64,
275 : aStream,
276 : data->mID,
277 : int64_t(data->mStart),
278 : int64_t(segment->GetDuration())));
279 :
280 0 : data->mEndOfFlushedData += segment->GetDuration();
281 0 : aStream->mTracks.AddTrack(data->mID, data->mStart, segment);
282 : // The track has taken ownership of data->mData, so let's replace
283 : // data->mData with an empty clone.
284 0 : data->mData = segment->CreateEmptyClone();
285 0 : data->mCommands &= ~SourceMediaStream::TRACK_CREATE;
286 0 : shouldNotifyTrackCreated = true;
287 0 : } else if (data->mData->GetDuration() > 0) {
288 0 : MediaSegment* dest = aStream->mTracks.FindTrack(data->mID)->GetSegment();
289 0 : LOG(LogLevel::Verbose,
290 : ("SourceMediaStream %p track %d, advancing end from %" PRId64
291 : " to %" PRId64,
292 : aStream,
293 : data->mID,
294 : int64_t(dest->GetDuration()),
295 : int64_t(dest->GetDuration() + data->mData->GetDuration())));
296 0 : data->mEndOfFlushedData += data->mData->GetDuration();
297 0 : dest->AppendFrom(data->mData);
298 : }
299 0 : if (data->mCommands & SourceMediaStream::TRACK_END) {
300 0 : aStream->mTracks.FindTrack(data->mID)->SetEnded();
301 0 : aStream->mUpdateTracks.RemoveElementAt(i);
302 : }
303 : }
304 0 : if (shouldNotifyTrackCreated) {
305 0 : for (MediaStreamListener* l : aStream->mListeners) {
306 0 : l->NotifyFinishedTrackCreation(this);
307 : }
308 : }
309 0 : if (!aStream->mFinished) {
310 0 : aStream->mTracks.AdvanceKnownTracksTime(aStream->mUpdateKnownTracksTime);
311 : }
312 : }
313 0 : if (aStream->mTracks.GetEnd() > 0) {
314 0 : aStream->mHasCurrentData = true;
315 : }
316 0 : if (finished) {
317 0 : FinishStream(aStream);
318 : }
319 0 : }
320 :
321 : StreamTime
322 0 : MediaStreamGraphImpl::GraphTimeToStreamTimeWithBlocking(MediaStream* aStream,
323 : GraphTime aTime)
324 : {
325 0 : MOZ_ASSERT(aTime <= mStateComputedTime,
326 : "Don't ask about times where we haven't made blocking decisions yet");
327 : return std::max<StreamTime>(0,
328 0 : std::min(aTime, aStream->mStartBlocking) - aStream->mTracksStartTime);
329 : }
330 :
331 : GraphTime
332 0 : MediaStreamGraphImpl::IterationEnd() const
333 : {
334 0 : return CurrentDriver()->IterationEnd();
335 : }
336 :
337 : void
338 0 : MediaStreamGraphImpl::UpdateCurrentTimeForStreams(GraphTime aPrevCurrentTime)
339 : {
340 0 : for (MediaStream* stream : AllStreams()) {
341 0 : bool isAnyBlocked = stream->mStartBlocking < mStateComputedTime;
342 0 : bool isAnyUnblocked = stream->mStartBlocking > aPrevCurrentTime;
343 :
344 : // Calculate blocked time and fire Blocked/Unblocked events
345 0 : GraphTime blockedTime = mStateComputedTime - stream->mStartBlocking;
346 0 : NS_ASSERTION(blockedTime >= 0, "Error in blocking time");
347 0 : stream->AdvanceTimeVaryingValuesToCurrentTime(mStateComputedTime,
348 0 : blockedTime);
349 0 : LOG(LogLevel::Verbose,
350 : ("MediaStream %p bufferStartTime=%f blockedTime=%f",
351 : stream,
352 : MediaTimeToSeconds(stream->mTracksStartTime),
353 : MediaTimeToSeconds(blockedTime)));
354 0 : stream->mStartBlocking = mStateComputedTime;
355 :
356 0 : if (isAnyUnblocked && stream->mNotifiedBlocked) {
357 0 : for (uint32_t j = 0; j < stream->mListeners.Length(); ++j) {
358 0 : MediaStreamListener* l = stream->mListeners[j];
359 0 : l->NotifyBlockingChanged(this, MediaStreamListener::UNBLOCKED);
360 : }
361 0 : stream->mNotifiedBlocked = false;
362 : }
363 0 : if (isAnyBlocked && !stream->mNotifiedBlocked) {
364 0 : for (uint32_t j = 0; j < stream->mListeners.Length(); ++j) {
365 0 : MediaStreamListener* l = stream->mListeners[j];
366 0 : l->NotifyBlockingChanged(this, MediaStreamListener::BLOCKED);
367 : }
368 0 : stream->mNotifiedBlocked = true;
369 : }
370 :
371 0 : if (isAnyUnblocked) {
372 0 : NS_ASSERTION(!stream->mNotifiedFinished,
373 : "Shouldn't have already notified of finish *and* have output!");
374 0 : for (uint32_t j = 0; j < stream->mListeners.Length(); ++j) {
375 0 : MediaStreamListener* l = stream->mListeners[j];
376 0 : l->NotifyOutput(this, mProcessedTime);
377 : }
378 : }
379 :
380 : // The stream is fully finished when all of its track data has been played
381 : // out.
382 0 : if (stream->mFinished && !stream->mNotifiedFinished &&
383 0 : mProcessedTime >=
384 0 : stream->StreamTimeToGraphTime(stream->GetStreamTracks().GetAllTracksEnd())) {
385 0 : stream->mNotifiedFinished = true;
386 0 : SetStreamOrderDirty();
387 0 : for (uint32_t j = 0; j < stream->mListeners.Length(); ++j) {
388 0 : MediaStreamListener* l = stream->mListeners[j];
389 0 : l->NotifyEvent(this, MediaStreamGraphEvent::EVENT_FINISHED);
390 : }
391 : }
392 : }
393 0 : }
394 :
395 : template<typename C, typename Chunk>
396 : void
397 0 : MediaStreamGraphImpl::ProcessChunkMetadataForInterval(MediaStream* aStream,
398 : TrackID aTrackID,
399 : C& aSegment,
400 : StreamTime aStart,
401 : StreamTime aEnd)
402 : {
403 0 : MOZ_ASSERT(aStream);
404 0 : MOZ_ASSERT(IsTrackIDExplicit(aTrackID));
405 :
406 0 : StreamTime offset = 0;
407 0 : for (typename C::ConstChunkIterator chunk(aSegment);
408 0 : !chunk.IsEnded(); chunk.Next()) {
409 0 : if (offset >= aEnd) {
410 0 : break;
411 : }
412 0 : offset += chunk->GetDuration();
413 0 : if (chunk->IsNull() || offset < aStart) {
414 0 : continue;
415 : }
416 0 : PrincipalHandle principalHandle = chunk->GetPrincipalHandle();
417 0 : if (principalHandle != aSegment.GetLastPrincipalHandle()) {
418 0 : aSegment.SetLastPrincipalHandle(principalHandle);
419 0 : LOG(LogLevel::Debug,
420 : ("MediaStream %p track %d, principalHandle "
421 : "changed in %sChunk with duration %lld",
422 : aStream,
423 : aTrackID,
424 : aSegment.GetType() == MediaSegment::AUDIO ? "Audio" : "Video",
425 : (long long)chunk->GetDuration()));
426 0 : for (const TrackBound<MediaStreamTrackListener>& listener :
427 : aStream->mTrackListeners) {
428 0 : if (listener.mTrackID == aTrackID) {
429 0 : listener.mListener->NotifyPrincipalHandleChanged(this, principalHandle);
430 : }
431 : }
432 : }
433 : }
434 0 : }
435 :
436 : void
437 0 : MediaStreamGraphImpl::ProcessChunkMetadata(GraphTime aPrevCurrentTime)
438 : {
439 0 : for (MediaStream* stream : AllStreams()) {
440 0 : StreamTime iterationStart = stream->GraphTimeToStreamTime(aPrevCurrentTime);
441 0 : StreamTime iterationEnd = stream->GraphTimeToStreamTime(mProcessedTime);
442 0 : for (StreamTracks::TrackIter tracks(stream->mTracks);
443 0 : !tracks.IsEnded(); tracks.Next()) {
444 0 : MediaSegment* segment = tracks->GetSegment();
445 0 : if (!segment) {
446 0 : continue;
447 : }
448 0 : if (tracks->GetType() == MediaSegment::AUDIO) {
449 0 : AudioSegment* audio = static_cast<AudioSegment*>(segment);
450 0 : ProcessChunkMetadataForInterval<AudioSegment, AudioChunk>(
451 0 : stream, tracks->GetID(), *audio, iterationStart, iterationEnd);
452 0 : } else if (tracks->GetType() == MediaSegment::VIDEO) {
453 0 : VideoSegment* video = static_cast<VideoSegment*>(segment);
454 0 : ProcessChunkMetadataForInterval<VideoSegment, VideoChunk>(
455 0 : stream, tracks->GetID(), *video, iterationStart, iterationEnd);
456 : } else {
457 0 : MOZ_CRASH("Unknown track type");
458 : }
459 : }
460 : }
461 0 : }
462 :
463 : GraphTime
464 0 : MediaStreamGraphImpl::WillUnderrun(MediaStream* aStream,
465 : GraphTime aEndBlockingDecisions)
466 : {
467 : // Finished streams can't underrun. ProcessedMediaStreams also can't cause
468 : // underrun currently, since we'll always be able to produce data for them
469 : // unless they block on some other stream.
470 0 : if (aStream->mFinished || aStream->AsProcessedStream()) {
471 0 : return aEndBlockingDecisions;
472 : }
473 : // This stream isn't finished or suspended. We don't need to call
474 : // StreamTimeToGraphTime since an underrun is the only thing that can block
475 : // it.
476 0 : GraphTime bufferEnd = aStream->GetTracksEnd() + aStream->mTracksStartTime;
477 : #ifdef DEBUG
478 0 : if (bufferEnd < mProcessedTime) {
479 0 : LOG(LogLevel::Error,
480 : ("MediaStream %p underrun, "
481 : "bufferEnd %f < mProcessedTime %f (%" PRId64 " < %" PRId64
482 : "), Streamtime %" PRId64,
483 : aStream,
484 : MediaTimeToSeconds(bufferEnd),
485 : MediaTimeToSeconds(mProcessedTime),
486 : bufferEnd,
487 : mProcessedTime,
488 : aStream->GetTracksEnd()));
489 0 : aStream->DumpTrackInfo();
490 0 : NS_ASSERTION(bufferEnd >= mProcessedTime, "Buffer underran");
491 : }
492 : #endif
493 0 : return std::min(bufferEnd, aEndBlockingDecisions);
494 : }
495 :
496 : namespace {
497 : // Value of mCycleMarker for unvisited streams in cycle detection.
498 : const uint32_t NOT_VISITED = UINT32_MAX;
499 : // Value of mCycleMarker for ordered streams in muted cycles.
500 : const uint32_t IN_MUTED_CYCLE = 1;
501 : } // namespace
502 :
503 : bool
504 0 : MediaStreamGraphImpl::AudioTrackPresent(bool& aNeedsAEC)
505 : {
506 0 : AssertOnGraphThreadOrNotRunning();
507 :
508 0 : bool audioTrackPresent = false;
509 0 : for (uint32_t i = 0; i < mStreams.Length() && audioTrackPresent == false; ++i) {
510 0 : MediaStream* stream = mStreams[i];
511 0 : SourceMediaStream* source = stream->AsSourceStream();
512 : #ifdef MOZ_WEBRTC
513 0 : if (source && source->NeedsMixing()) {
514 0 : aNeedsAEC = true;
515 : }
516 : #endif
517 : // If this is a AudioNodeStream, force a AudioCallbackDriver.
518 0 : if (stream->AsAudioNodeStream()) {
519 0 : audioTrackPresent = true;
520 : } else {
521 0 : for (StreamTracks::TrackIter tracks(stream->GetStreamTracks(), MediaSegment::AUDIO);
522 0 : !tracks.IsEnded(); tracks.Next()) {
523 0 : audioTrackPresent = true;
524 : }
525 : }
526 0 : if (source) {
527 0 : audioTrackPresent = source->HasPendingAudioTrack();
528 : }
529 : }
530 :
531 : // XXX For some reason, there are race conditions when starting an audio input where
532 : // we find no active audio tracks. In any case, if we have an active audio input we
533 : // should not allow a switch back to a SystemClockDriver
534 0 : if (!audioTrackPresent && mInputDeviceUsers.Count() != 0) {
535 0 : NS_WARNING("No audio tracks, but full-duplex audio is enabled!!!!!");
536 0 : audioTrackPresent = true;
537 : #ifdef MOZ_WEBRTC
538 0 : aNeedsAEC = true;
539 : #endif
540 : }
541 :
542 0 : return audioTrackPresent;
543 : }
544 :
545 : void
546 0 : MediaStreamGraphImpl::UpdateStreamOrder()
547 : {
548 0 : bool shouldAEC = false;
549 0 : bool audioTrackPresent = AudioTrackPresent(shouldAEC);
550 :
551 : // Note that this looks for any audio streams, input or output, and switches to a
552 : // SystemClockDriver if there are none. However, if another is already pending, let that
553 : // switch happen.
554 :
555 0 : if (!audioTrackPresent && mRealtime &&
556 0 : CurrentDriver()->AsAudioCallbackDriver()) {
557 0 : MonitorAutoLock mon(mMonitor);
558 0 : if (CurrentDriver()->AsAudioCallbackDriver()->IsStarted() &&
559 0 : !(CurrentDriver()->Switching())) {
560 0 : if (mLifecycleState == LIFECYCLE_RUNNING) {
561 0 : SystemClockDriver* driver = new SystemClockDriver(this);
562 0 : CurrentDriver()->SwitchAtNextIteration(driver);
563 : }
564 : }
565 : }
566 :
567 0 : bool switching = false;
568 : {
569 0 : MonitorAutoLock mon(mMonitor);
570 0 : switching = CurrentDriver()->Switching();
571 : }
572 :
573 0 : if (audioTrackPresent && mRealtime &&
574 0 : !CurrentDriver()->AsAudioCallbackDriver() &&
575 0 : !switching) {
576 0 : MonitorAutoLock mon(mMonitor);
577 0 : if (mLifecycleState == LIFECYCLE_RUNNING) {
578 0 : AudioCallbackDriver* driver = new AudioCallbackDriver(this);
579 0 : CurrentDriver()->SwitchAtNextIteration(driver);
580 : }
581 : }
582 :
583 0 : if (!mStreamOrderDirty) {
584 0 : return;
585 : }
586 :
587 0 : mStreamOrderDirty = false;
588 :
589 : // The algorithm for finding cycles is based on Tim Leslie's iterative
590 : // implementation [1][2] of Pearce's variant [3] of Tarjan's strongly
591 : // connected components (SCC) algorithm. There are variations (a) to
592 : // distinguish whether streams in SCCs of size 1 are in a cycle and (b) to
593 : // re-run the algorithm over SCCs with breaks at DelayNodes.
594 : //
595 : // [1] http://www.timl.id.au/?p=327
596 : // [2] https://github.com/scipy/scipy/blob/e2c502fca/scipy/sparse/csgraph/_traversal.pyx#L582
597 : // [3] http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.102.1707
598 : //
599 : // There are two stacks. One for the depth-first search (DFS),
600 0 : mozilla::LinkedList<MediaStream> dfsStack;
601 : // and another for streams popped from the DFS stack, but still being
602 : // considered as part of SCCs involving streams on the stack.
603 0 : mozilla::LinkedList<MediaStream> sccStack;
604 :
605 : // An index into mStreams for the next stream found with no unsatisfied
606 : // upstream dependencies.
607 0 : uint32_t orderedStreamCount = 0;
608 :
609 0 : for (uint32_t i = 0; i < mStreams.Length(); ++i) {
610 0 : MediaStream* s = mStreams[i];
611 0 : ProcessedMediaStream* ps = s->AsProcessedStream();
612 0 : if (ps) {
613 : // The dfsStack initially contains a list of all processed streams in
614 : // unchanged order.
615 0 : dfsStack.insertBack(s);
616 0 : ps->mCycleMarker = NOT_VISITED;
617 : } else {
618 : // SourceMediaStreams have no inputs and so can be ordered now.
619 0 : mStreams[orderedStreamCount] = s;
620 0 : ++orderedStreamCount;
621 : }
622 : }
623 :
624 : // mNextStackMarker corresponds to "index" in Tarjan's algorithm. It is a
625 : // counter to label mCycleMarker on the next visited stream in the DFS
626 : // uniquely in the set of visited streams that are still being considered.
627 : //
628 : // In this implementation, the counter descends so that the values are
629 : // strictly greater than the values that mCycleMarker takes when the stream
630 : // has been ordered (0 or IN_MUTED_CYCLE).
631 : //
632 : // Each new stream labelled, as the DFS searches upstream, receives a value
633 : // less than those used for all other streams being considered.
634 0 : uint32_t nextStackMarker = NOT_VISITED - 1;
635 : // Reset list of DelayNodes in cycles stored at the tail of mStreams.
636 0 : mFirstCycleBreaker = mStreams.Length();
637 :
638 : // Rearrange dfsStack order as required to DFS upstream and pop streams
639 : // in processing order to place in mStreams.
640 0 : while (auto ps = static_cast<ProcessedMediaStream*>(dfsStack.getFirst())) {
641 0 : const auto& inputs = ps->mInputs;
642 0 : MOZ_ASSERT(ps->AsProcessedStream());
643 0 : if (ps->mCycleMarker == NOT_VISITED) {
644 : // Record the position on the visited stack, so that any searches
645 : // finding this stream again know how much of the stack is in the cycle.
646 0 : ps->mCycleMarker = nextStackMarker;
647 0 : --nextStackMarker;
648 : // Not-visited input streams should be processed first.
649 : // SourceMediaStreams have already been ordered.
650 0 : for (uint32_t i = inputs.Length(); i--; ) {
651 0 : if (inputs[i]->mSource->IsSuspended()) {
652 0 : continue;
653 : }
654 0 : auto input = inputs[i]->mSource->AsProcessedStream();
655 0 : if (input && input->mCycleMarker == NOT_VISITED) {
656 : // It can be that this stream has an input which is from a suspended
657 : // AudioContext.
658 0 : if (input->isInList()) {
659 0 : input->remove();
660 0 : dfsStack.insertFront(input);
661 : }
662 : }
663 : }
664 0 : continue;
665 : }
666 :
667 : // Returning from DFS. Pop from dfsStack.
668 0 : ps->remove();
669 :
670 : // cycleStackMarker keeps track of the highest marker value on any
671 : // upstream stream, if any, found receiving input, directly or indirectly,
672 : // from the visited stack (and so from |ps|, making a cycle). In a
673 : // variation from Tarjan's SCC algorithm, this does not include |ps|
674 : // unless it is part of the cycle.
675 0 : uint32_t cycleStackMarker = 0;
676 0 : for (uint32_t i = inputs.Length(); i--; ) {
677 0 : if (inputs[i]->mSource->IsSuspended()) {
678 0 : continue;
679 : }
680 0 : auto input = inputs[i]->mSource->AsProcessedStream();
681 0 : if (input) {
682 0 : cycleStackMarker = std::max(cycleStackMarker, input->mCycleMarker);
683 : }
684 : }
685 :
686 0 : if (cycleStackMarker <= IN_MUTED_CYCLE) {
687 : // All inputs have been ordered and their stack markers have been removed.
688 : // This stream is not part of a cycle. It can be processed next.
689 0 : ps->mCycleMarker = 0;
690 0 : mStreams[orderedStreamCount] = ps;
691 0 : ++orderedStreamCount;
692 0 : continue;
693 : }
694 :
695 : // A cycle has been found. Record this stream for ordering when all
696 : // streams in this SCC have been popped from the DFS stack.
697 0 : sccStack.insertFront(ps);
698 :
699 0 : if (cycleStackMarker > ps->mCycleMarker) {
700 : // Cycles have been found that involve streams that remain on the stack.
701 : // Leave mCycleMarker indicating the most downstream (last) stream on
702 : // the stack known to be part of this SCC. In this way, any searches on
703 : // other paths that find |ps| will know (without having to traverse from
704 : // this stream again) that they are part of this SCC (i.e. part of an
705 : // intersecting cycle).
706 0 : ps->mCycleMarker = cycleStackMarker;
707 0 : continue;
708 : }
709 :
710 : // |ps| is the root of an SCC involving no other streams on dfsStack, the
711 : // complete SCC has been recorded, and streams in this SCC are part of at
712 : // least one cycle.
713 0 : MOZ_ASSERT(cycleStackMarker == ps->mCycleMarker);
714 : // If there are DelayNodes in this SCC, then they may break the cycles.
715 0 : bool haveDelayNode = false;
716 0 : auto next = sccStack.getFirst();
717 : // Streams in this SCC are identified by mCycleMarker <= cycleStackMarker.
718 : // (There may be other streams later in sccStack from other incompletely
719 : // searched SCCs, involving streams still on dfsStack.)
720 : //
721 : // DelayNodes in cycles must behave differently from those not in cycles,
722 : // so all DelayNodes in the SCC must be identified.
723 0 : while (next && static_cast<ProcessedMediaStream*>(next)->
724 0 : mCycleMarker <= cycleStackMarker) {
725 0 : auto ns = next->AsAudioNodeStream();
726 : // Get next before perhaps removing from list below.
727 0 : next = next->getNext();
728 0 : if (ns && ns->Engine()->AsDelayNodeEngine()) {
729 0 : haveDelayNode = true;
730 : // DelayNodes break cycles by producing their output in a
731 : // preprocessing phase; they do not need to be ordered before their
732 : // consumers. Order them at the tail of mStreams so that they can be
733 : // handled specially. Do so now, so that DFS ignores them.
734 0 : ns->remove();
735 0 : ns->mCycleMarker = 0;
736 0 : --mFirstCycleBreaker;
737 0 : mStreams[mFirstCycleBreaker] = ns;
738 : }
739 : }
740 0 : auto after_scc = next;
741 0 : while ((next = sccStack.getFirst()) != after_scc) {
742 0 : next->remove();
743 0 : auto removed = static_cast<ProcessedMediaStream*>(next);
744 0 : if (haveDelayNode) {
745 : // Return streams to the DFS stack again (to order and detect cycles
746 : // without delayNodes). Any of these streams that are still inputs
747 : // for streams on the visited stack must be returned to the front of
748 : // the stack to be ordered before their dependents. We know that none
749 : // of these streams need input from streams on the visited stack, so
750 : // they can all be searched and ordered before the current stack head
751 : // is popped.
752 0 : removed->mCycleMarker = NOT_VISITED;
753 0 : dfsStack.insertFront(removed);
754 : } else {
755 : // Streams in cycles without any DelayNodes must be muted, and so do
756 : // not need input and can be ordered now. They must be ordered before
757 : // their consumers so that their muted output is available.
758 0 : removed->mCycleMarker = IN_MUTED_CYCLE;
759 0 : mStreams[orderedStreamCount] = removed;
760 0 : ++orderedStreamCount;
761 : }
762 : }
763 0 : }
764 :
765 0 : MOZ_ASSERT(orderedStreamCount == mFirstCycleBreaker);
766 : }
767 :
768 : void
769 0 : MediaStreamGraphImpl::NotifyHasCurrentData(MediaStream* aStream)
770 : {
771 0 : if (!aStream->mNotifiedHasCurrentData && aStream->mHasCurrentData) {
772 0 : for (uint32_t j = 0; j < aStream->mListeners.Length(); ++j) {
773 0 : MediaStreamListener* l = aStream->mListeners[j];
774 0 : l->NotifyHasCurrentData(this);
775 : }
776 0 : aStream->mNotifiedHasCurrentData = true;
777 : }
778 0 : }
779 :
780 : void
781 0 : MediaStreamGraphImpl::CreateOrDestroyAudioStreams(MediaStream* aStream)
782 : {
783 0 : MOZ_ASSERT(mRealtime, "Should only attempt to create audio streams in real-time mode");
784 :
785 0 : if (aStream->mAudioOutputs.IsEmpty()) {
786 0 : aStream->mAudioOutputStreams.Clear();
787 0 : return;
788 : }
789 :
790 0 : if (!aStream->GetStreamTracks().GetAndResetTracksDirty() &&
791 0 : !aStream->mAudioOutputStreams.IsEmpty()) {
792 0 : return;
793 : }
794 :
795 0 : LOG(LogLevel::Debug,
796 : ("Updating AudioOutputStreams for MediaStream %p", aStream));
797 :
798 0 : AutoTArray<bool,2> audioOutputStreamsFound;
799 0 : for (uint32_t i = 0; i < aStream->mAudioOutputStreams.Length(); ++i) {
800 0 : audioOutputStreamsFound.AppendElement(false);
801 : }
802 :
803 0 : for (StreamTracks::TrackIter tracks(aStream->GetStreamTracks(), MediaSegment::AUDIO);
804 0 : !tracks.IsEnded(); tracks.Next()) {
805 : uint32_t i;
806 0 : for (i = 0; i < audioOutputStreamsFound.Length(); ++i) {
807 0 : if (aStream->mAudioOutputStreams[i].mTrackID == tracks->GetID()) {
808 0 : break;
809 : }
810 : }
811 0 : if (i < audioOutputStreamsFound.Length()) {
812 0 : audioOutputStreamsFound[i] = true;
813 : } else {
814 : MediaStream::AudioOutputStream* audioOutputStream =
815 0 : aStream->mAudioOutputStreams.AppendElement();
816 0 : audioOutputStream->mAudioPlaybackStartTime = mProcessedTime;
817 0 : audioOutputStream->mBlockedAudioTime = 0;
818 0 : audioOutputStream->mLastTickWritten = 0;
819 0 : audioOutputStream->mTrackID = tracks->GetID();
820 :
821 0 : bool switching = false;
822 :
823 : {
824 0 : MonitorAutoLock lock(mMonitor);
825 0 : switching = CurrentDriver()->Switching();
826 : }
827 :
828 0 : if (!CurrentDriver()->AsAudioCallbackDriver() &&
829 0 : !switching) {
830 0 : MonitorAutoLock mon(mMonitor);
831 0 : if (mLifecycleState == LIFECYCLE_RUNNING) {
832 0 : AudioCallbackDriver* driver = new AudioCallbackDriver(this);
833 0 : CurrentDriver()->SwitchAtNextIteration(driver);
834 : }
835 : }
836 : }
837 : }
838 :
839 0 : for (int32_t i = audioOutputStreamsFound.Length() - 1; i >= 0; --i) {
840 0 : if (!audioOutputStreamsFound[i]) {
841 0 : aStream->mAudioOutputStreams.RemoveElementAt(i);
842 : }
843 : }
844 : }
845 :
846 : StreamTime
847 0 : MediaStreamGraphImpl::PlayAudio(MediaStream* aStream)
848 : {
849 0 : MOZ_ASSERT(mRealtime, "Should only attempt to play audio in realtime mode");
850 :
851 0 : float volume = 0.0f;
852 0 : for (uint32_t i = 0; i < aStream->mAudioOutputs.Length(); ++i) {
853 0 : volume += aStream->mAudioOutputs[i].mVolume;
854 : }
855 :
856 0 : StreamTime ticksWritten = 0;
857 :
858 0 : for (uint32_t i = 0; i < aStream->mAudioOutputStreams.Length(); ++i) {
859 0 : ticksWritten = 0;
860 :
861 0 : MediaStream::AudioOutputStream& audioOutput = aStream->mAudioOutputStreams[i];
862 0 : StreamTracks::Track* track = aStream->mTracks.FindTrack(audioOutput.mTrackID);
863 0 : AudioSegment* audio = track->Get<AudioSegment>();
864 0 : AudioSegment output;
865 :
866 0 : StreamTime offset = aStream->GraphTimeToStreamTime(mProcessedTime);
867 :
868 : // We don't update aStream->mTracksStartTime here to account for time spent
869 : // blocked. Instead, we'll update it in UpdateCurrentTimeForStreams after
870 : // the blocked period has completed. But we do need to make sure we play
871 : // from the right offsets in the stream buffer, even if we've already
872 : // written silence for some amount of blocked time after the current time.
873 0 : GraphTime t = mProcessedTime;
874 0 : while (t < mStateComputedTime) {
875 0 : bool blocked = t >= aStream->mStartBlocking;
876 0 : GraphTime end = blocked ? mStateComputedTime : aStream->mStartBlocking;
877 0 : NS_ASSERTION(end <= mStateComputedTime, "mStartBlocking is wrong!");
878 :
879 : // Check how many ticks of sound we can provide if we are blocked some
880 : // time in the middle of this cycle.
881 0 : StreamTime toWrite = end - t;
882 :
883 0 : if (blocked) {
884 0 : output.InsertNullDataAtStart(toWrite);
885 0 : ticksWritten += toWrite;
886 0 : LOG(LogLevel::Verbose,
887 : ("MediaStream %p writing %" PRId64 " blocking-silence samples for "
888 : "%f to %f (%" PRId64 " to %" PRId64 ")",
889 : aStream,
890 : toWrite,
891 : MediaTimeToSeconds(t),
892 : MediaTimeToSeconds(end),
893 : offset,
894 : offset + toWrite));
895 : } else {
896 0 : StreamTime endTicksNeeded = offset + toWrite;
897 0 : StreamTime endTicksAvailable = audio->GetDuration();
898 :
899 0 : if (endTicksNeeded <= endTicksAvailable) {
900 0 : LOG(LogLevel::Verbose,
901 : ("MediaStream %p writing %" PRId64 " samples for %f to %f "
902 : "(samples %" PRId64 " to %" PRId64 ")",
903 : aStream,
904 : toWrite,
905 : MediaTimeToSeconds(t),
906 : MediaTimeToSeconds(end),
907 : offset,
908 : endTicksNeeded));
909 0 : output.AppendSlice(*audio, offset, endTicksNeeded);
910 0 : ticksWritten += toWrite;
911 0 : offset = endTicksNeeded;
912 : } else {
913 : // MOZ_ASSERT(track->IsEnded(), "Not enough data, and track not ended.");
914 : // If we are at the end of the track, maybe write the remaining
915 : // samples, and pad with/output silence.
916 0 : if (endTicksNeeded > endTicksAvailable &&
917 : offset < endTicksAvailable) {
918 0 : output.AppendSlice(*audio, offset, endTicksAvailable);
919 0 : LOG(LogLevel::Verbose,
920 : ("MediaStream %p writing %" PRId64 " samples for %f to %f "
921 : "(samples %" PRId64 " to %" PRId64 ")",
922 : aStream,
923 : toWrite,
924 : MediaTimeToSeconds(t),
925 : MediaTimeToSeconds(end),
926 : offset,
927 : endTicksNeeded));
928 0 : uint32_t available = endTicksAvailable - offset;
929 0 : ticksWritten += available;
930 0 : toWrite -= available;
931 0 : offset = endTicksAvailable;
932 : }
933 0 : output.AppendNullData(toWrite);
934 0 : LOG(LogLevel::Verbose,
935 : ("MediaStream %p writing %" PRId64 " padding slsamples for %f to "
936 : "%f (samples %" PRId64 " to %" PRId64 ")",
937 : aStream,
938 : toWrite,
939 : MediaTimeToSeconds(t),
940 : MediaTimeToSeconds(end),
941 : offset,
942 : endTicksNeeded));
943 0 : ticksWritten += toWrite;
944 : }
945 0 : output.ApplyVolume(volume);
946 : }
947 0 : t = end;
948 : }
949 0 : audioOutput.mLastTickWritten = offset;
950 :
951 : // Need unique id for stream & track - and we want it to match the inserter
952 0 : output.WriteTo(LATENCY_STREAM_ID(aStream, track->GetID()),
953 : mMixer, AudioChannelCount(),
954 0 : mSampleRate);
955 : }
956 0 : return ticksWritten;
957 : }
958 :
959 : void
960 0 : MediaStreamGraphImpl::OpenAudioInputImpl(int aID,
961 : AudioDataListener *aListener)
962 : {
963 : // Bug 1238038 Need support for multiple mics at once
964 0 : if (mInputDeviceUsers.Count() > 0 &&
965 0 : !mInputDeviceUsers.Get(aListener, nullptr)) {
966 0 : NS_ASSERTION(false, "Input from multiple mics not yet supported; bug 1238038");
967 : // Need to support separate input-only AudioCallback drivers; they'll
968 : // call us back on "other" threads. We will need to echo-cancel them, though.
969 0 : return;
970 : }
971 0 : mInputWanted = true;
972 :
973 : // Add to count of users for this ID.
974 : // XXX Since we can't rely on IDs staying valid (ugh), use the listener as
975 : // a stand-in for the ID. Fix as part of support for multiple-captures
976 : // (Bug 1238038)
977 0 : uint32_t count = 0;
978 0 : mInputDeviceUsers.Get(aListener, &count); // ok if this fails
979 0 : count++;
980 0 : mInputDeviceUsers.Put(aListener, count); // creates a new entry in the hash if needed
981 :
982 0 : if (count == 1) { // first open for this listener
983 : // aID is a cubeb_devid, and we assume that opaque ptr is valid until
984 : // we close cubeb.
985 0 : mInputDeviceID = aID;
986 0 : mAudioInputs.AppendElement(aListener); // always monitor speaker data
987 :
988 : // Switch Drivers since we're adding input (to input-only or full-duplex)
989 0 : MonitorAutoLock mon(mMonitor);
990 0 : if (mLifecycleState == LIFECYCLE_RUNNING) {
991 0 : AudioCallbackDriver* driver = new AudioCallbackDriver(this);
992 0 : driver->SetMicrophoneActive(true);
993 0 : LOG(
994 : LogLevel::Debug,
995 : ("OpenAudioInput: starting new AudioCallbackDriver(input) %p", driver));
996 0 : LOG(
997 : LogLevel::Debug,
998 : ("OpenAudioInput: starting new AudioCallbackDriver(input) %p", driver));
999 0 : driver->SetInputListener(aListener);
1000 0 : CurrentDriver()->SwitchAtNextIteration(driver);
1001 : } else {
1002 0 : LOG(LogLevel::Error, ("OpenAudioInput in shutdown!"));
1003 0 : LOG(LogLevel::Debug, ("OpenAudioInput in shutdown!"));
1004 0 : NS_ASSERTION(false, "Can't open cubeb inputs in shutdown");
1005 : }
1006 : }
1007 : }
1008 :
1009 : nsresult
1010 0 : MediaStreamGraphImpl::OpenAudioInput(int aID,
1011 : AudioDataListener *aListener)
1012 : {
1013 : // So, so, so annoying. Can't AppendMessage except on Mainthread
1014 0 : if (!NS_IsMainThread()) {
1015 : RefPtr<nsIRunnable> runnable =
1016 0 : WrapRunnable(this,
1017 : &MediaStreamGraphImpl::OpenAudioInput,
1018 : aID,
1019 0 : RefPtr<AudioDataListener>(aListener));
1020 0 : mAbstractMainThread->Dispatch(runnable.forget());
1021 0 : return NS_OK;
1022 : }
1023 0 : class Message : public ControlMessage {
1024 : public:
1025 0 : Message(MediaStreamGraphImpl *aGraph, int aID,
1026 0 : AudioDataListener *aListener) :
1027 0 : ControlMessage(nullptr), mGraph(aGraph), mID(aID), mListener(aListener) {}
1028 0 : virtual void Run()
1029 : {
1030 0 : mGraph->OpenAudioInputImpl(mID, mListener);
1031 0 : }
1032 : MediaStreamGraphImpl *mGraph;
1033 : int mID;
1034 : RefPtr<AudioDataListener> mListener;
1035 : };
1036 : // XXX Check not destroyed!
1037 0 : this->AppendMessage(MakeUnique<Message>(this, aID, aListener));
1038 0 : return NS_OK;
1039 : }
1040 :
1041 : void
1042 0 : MediaStreamGraphImpl::CloseAudioInputImpl(AudioDataListener *aListener)
1043 : {
1044 : uint32_t count;
1045 0 : DebugOnly<bool> result = mInputDeviceUsers.Get(aListener, &count);
1046 0 : MOZ_ASSERT(result);
1047 0 : if (--count > 0) {
1048 0 : mInputDeviceUsers.Put(aListener, count);
1049 0 : return; // still in use
1050 : }
1051 0 : mInputDeviceUsers.Remove(aListener);
1052 0 : mInputDeviceID = -1;
1053 0 : mInputWanted = false;
1054 0 : AudioCallbackDriver *driver = CurrentDriver()->AsAudioCallbackDriver();
1055 0 : if (driver) {
1056 0 : driver->RemoveInputListener(aListener);
1057 : }
1058 0 : mAudioInputs.RemoveElement(aListener);
1059 :
1060 : // Switch Drivers since we're adding or removing an input (to nothing/system or output only)
1061 0 : bool shouldAEC = false;
1062 0 : bool audioTrackPresent = AudioTrackPresent(shouldAEC);
1063 :
1064 0 : MonitorAutoLock mon(mMonitor);
1065 0 : if (mLifecycleState == LIFECYCLE_RUNNING) {
1066 : GraphDriver* driver;
1067 0 : if (audioTrackPresent) {
1068 : // We still have audio output
1069 0 : LOG(LogLevel::Debug, ("CloseInput: output present (AudioCallback)"));
1070 :
1071 0 : driver = new AudioCallbackDriver(this);
1072 0 : CurrentDriver()->SwitchAtNextIteration(driver);
1073 0 : } else if (CurrentDriver()->AsAudioCallbackDriver()) {
1074 0 : LOG(LogLevel::Debug,
1075 : ("CloseInput: no output present (SystemClockCallback)"));
1076 :
1077 0 : driver = new SystemClockDriver(this);
1078 0 : CurrentDriver()->SwitchAtNextIteration(driver);
1079 : } // else SystemClockDriver->SystemClockDriver, no switch
1080 : }
1081 : }
1082 :
1083 : void
1084 0 : MediaStreamGraphImpl::CloseAudioInput(AudioDataListener *aListener)
1085 : {
1086 : // So, so, so annoying. Can't AppendMessage except on Mainthread
1087 0 : if (!NS_IsMainThread()) {
1088 : RefPtr<nsIRunnable> runnable =
1089 0 : WrapRunnable(this,
1090 : &MediaStreamGraphImpl::CloseAudioInput,
1091 0 : RefPtr<AudioDataListener>(aListener));
1092 0 : mAbstractMainThread->Dispatch(runnable.forget());
1093 0 : return;
1094 : }
1095 0 : class Message : public ControlMessage {
1096 : public:
1097 0 : Message(MediaStreamGraphImpl *aGraph, AudioDataListener *aListener) :
1098 0 : ControlMessage(nullptr), mGraph(aGraph), mListener(aListener) {}
1099 0 : virtual void Run()
1100 : {
1101 0 : mGraph->CloseAudioInputImpl(mListener);
1102 0 : }
1103 : MediaStreamGraphImpl *mGraph;
1104 : RefPtr<AudioDataListener> mListener;
1105 : };
1106 0 : this->AppendMessage(MakeUnique<Message>(this, aListener));
1107 : }
1108 :
1109 : // All AudioInput listeners get the same speaker data (at least for now).
1110 : void
1111 0 : MediaStreamGraph::NotifyOutputData(AudioDataValue* aBuffer, size_t aFrames,
1112 : TrackRate aRate, uint32_t aChannels)
1113 : {
1114 0 : for (auto& listener : mAudioInputs) {
1115 0 : listener->NotifyOutputData(this, aBuffer, aFrames, aRate, aChannels);
1116 : }
1117 0 : }
1118 :
1119 : void
1120 0 : MediaStreamGraph::AssertOnGraphThreadOrNotRunning() const
1121 : {
1122 : // either we're on the right thread (and calling CurrentDriver() is safe),
1123 : // or we're going to assert anyways, so don't cross-check CurrentDriver
1124 : #ifdef DEBUG
1125 : MediaStreamGraphImpl const * graph =
1126 0 : static_cast<MediaStreamGraphImpl const *>(this);
1127 : // if all the safety checks fail, assert we own the monitor
1128 0 : if (!graph->mDriver->OnThread()) {
1129 0 : if (!(graph->mDetectedNotRunning &&
1130 0 : graph->mLifecycleState > MediaStreamGraphImpl::LIFECYCLE_RUNNING &&
1131 0 : NS_IsMainThread())) {
1132 0 : graph->mMonitor.AssertCurrentThreadOwns();
1133 : }
1134 : }
1135 : #endif
1136 0 : }
1137 :
1138 : bool
1139 0 : MediaStreamGraphImpl::ShouldUpdateMainThread()
1140 : {
1141 0 : if (mRealtime) {
1142 0 : return true;
1143 : }
1144 :
1145 0 : TimeStamp now = TimeStamp::Now();
1146 0 : if ((now - mLastMainThreadUpdate).ToMilliseconds() > CurrentDriver()->IterationDuration()) {
1147 0 : mLastMainThreadUpdate = now;
1148 0 : return true;
1149 : }
1150 0 : return false;
1151 : }
1152 :
1153 : void
1154 0 : MediaStreamGraphImpl::PrepareUpdatesToMainThreadState(bool aFinalUpdate)
1155 : {
1156 0 : mMonitor.AssertCurrentThreadOwns();
1157 :
1158 : // We don't want to frequently update the main thread about timing update
1159 : // when we are not running in realtime.
1160 0 : if (aFinalUpdate || ShouldUpdateMainThread()) {
1161 : // Strip updates that will be obsoleted below, so as to keep the length of
1162 : // mStreamUpdates sane.
1163 0 : size_t keptUpdateCount = 0;
1164 0 : for (size_t i = 0; i < mStreamUpdates.Length(); ++i) {
1165 0 : MediaStream* stream = mStreamUpdates[i].mStream;
1166 : // RemoveStreamGraphThread() clears mStream in updates for
1167 : // streams that are removed from the graph.
1168 0 : MOZ_ASSERT(!stream || stream->GraphImpl() == this);
1169 0 : if (!stream || stream->MainThreadNeedsUpdates()) {
1170 : // Discard this update as it has either been cleared when the stream
1171 : // was destroyed or there will be a newer update below.
1172 0 : continue;
1173 : }
1174 0 : if (keptUpdateCount != i) {
1175 0 : mStreamUpdates[keptUpdateCount] = Move(mStreamUpdates[i]);
1176 0 : MOZ_ASSERT(!mStreamUpdates[i].mStream);
1177 : }
1178 0 : ++keptUpdateCount;
1179 : }
1180 0 : mStreamUpdates.TruncateLength(keptUpdateCount);
1181 :
1182 0 : mStreamUpdates.SetCapacity(mStreamUpdates.Length() + mStreams.Length() +
1183 0 : mSuspendedStreams.Length());
1184 0 : for (MediaStream* stream : AllStreams()) {
1185 0 : if (!stream->MainThreadNeedsUpdates()) {
1186 0 : continue;
1187 : }
1188 0 : StreamUpdate* update = mStreamUpdates.AppendElement();
1189 0 : update->mStream = stream;
1190 : // No blocking to worry about here, since we've passed
1191 : // UpdateCurrentTimeForStreams.
1192 0 : update->mNextMainThreadCurrentTime =
1193 0 : stream->GraphTimeToStreamTime(mProcessedTime);
1194 0 : update->mNextMainThreadFinished = stream->mNotifiedFinished;
1195 : }
1196 0 : if (!mPendingUpdateRunnables.IsEmpty()) {
1197 0 : mUpdateRunnables.AppendElements(Move(mPendingUpdateRunnables));
1198 : }
1199 : }
1200 :
1201 : // Don't send the message to the main thread if it's not going to have
1202 : // any work to do.
1203 0 : if (aFinalUpdate ||
1204 0 : !mUpdateRunnables.IsEmpty() ||
1205 0 : !mStreamUpdates.IsEmpty()) {
1206 0 : EnsureStableStateEventPosted();
1207 : }
1208 0 : }
1209 :
1210 : GraphTime
1211 0 : MediaStreamGraphImpl::RoundUpToNextAudioBlock(GraphTime aTime)
1212 : {
1213 0 : StreamTime ticks = aTime;
1214 0 : uint64_t block = ticks >> WEBAUDIO_BLOCK_SIZE_BITS;
1215 0 : uint64_t nextBlock = block + 1;
1216 0 : StreamTime nextTicks = nextBlock << WEBAUDIO_BLOCK_SIZE_BITS;
1217 0 : return nextTicks;
1218 : }
1219 :
1220 : void
1221 0 : MediaStreamGraphImpl::ProduceDataForStreamsBlockByBlock(uint32_t aStreamIndex,
1222 : TrackRate aSampleRate)
1223 : {
1224 0 : MOZ_ASSERT(aStreamIndex <= mFirstCycleBreaker,
1225 : "Cycle breaker is not AudioNodeStream?");
1226 0 : GraphTime t = mProcessedTime;
1227 0 : while (t < mStateComputedTime) {
1228 0 : GraphTime next = RoundUpToNextAudioBlock(t);
1229 0 : for (uint32_t i = mFirstCycleBreaker; i < mStreams.Length(); ++i) {
1230 0 : auto ns = static_cast<AudioNodeStream*>(mStreams[i]);
1231 0 : MOZ_ASSERT(ns->AsAudioNodeStream());
1232 0 : ns->ProduceOutputBeforeInput(t);
1233 : }
1234 0 : for (uint32_t i = aStreamIndex; i < mStreams.Length(); ++i) {
1235 0 : ProcessedMediaStream* ps = mStreams[i]->AsProcessedStream();
1236 0 : if (ps) {
1237 0 : ps->ProcessInput(t, next,
1238 0 : (next == mStateComputedTime) ? ProcessedMediaStream::ALLOW_FINISH : 0);
1239 : }
1240 : }
1241 0 : t = next;
1242 : }
1243 0 : NS_ASSERTION(t == mStateComputedTime,
1244 : "Something went wrong with rounding to block boundaries");
1245 0 : }
1246 :
1247 : bool
1248 0 : MediaStreamGraphImpl::AllFinishedStreamsNotified()
1249 : {
1250 0 : for (MediaStream* stream : AllStreams()) {
1251 0 : if (stream->mFinished && !stream->mNotifiedFinished) {
1252 0 : return false;
1253 : }
1254 : }
1255 0 : return true;
1256 : }
1257 :
1258 : void
1259 0 : MediaStreamGraphImpl::RunMessageAfterProcessing(UniquePtr<ControlMessage> aMessage)
1260 : {
1261 0 : MOZ_ASSERT(CurrentDriver()->OnThread());
1262 :
1263 0 : if (mFrontMessageQueue.IsEmpty()) {
1264 0 : mFrontMessageQueue.AppendElement();
1265 : }
1266 :
1267 : // Only one block is used for messages from the graph thread.
1268 0 : MOZ_ASSERT(mFrontMessageQueue.Length() == 1);
1269 0 : mFrontMessageQueue[0].mMessages.AppendElement(Move(aMessage));
1270 0 : }
1271 :
1272 : void
1273 0 : MediaStreamGraphImpl::RunMessagesInQueue()
1274 : {
1275 : // Calculate independent action times for each batch of messages (each
1276 : // batch corresponding to an event loop task). This isolates the performance
1277 : // of different scripts to some extent.
1278 0 : for (uint32_t i = 0; i < mFrontMessageQueue.Length(); ++i) {
1279 0 : nsTArray<UniquePtr<ControlMessage>>& messages = mFrontMessageQueue[i].mMessages;
1280 :
1281 0 : for (uint32_t j = 0; j < messages.Length(); ++j) {
1282 0 : messages[j]->Run();
1283 : }
1284 : }
1285 0 : mFrontMessageQueue.Clear();
1286 0 : }
1287 :
1288 : void
1289 0 : MediaStreamGraphImpl::UpdateGraph(GraphTime aEndBlockingDecisions)
1290 : {
1291 0 : MOZ_ASSERT(aEndBlockingDecisions >= mProcessedTime);
1292 : // The next state computed time can be the same as the previous: it
1293 : // means the driver would be have been blocking indefinitly, but the graph has
1294 : // been woken up right after having been to sleep.
1295 0 : MOZ_ASSERT(aEndBlockingDecisions >= mStateComputedTime);
1296 :
1297 0 : UpdateStreamOrder();
1298 :
1299 0 : bool ensureNextIteration = false;
1300 :
1301 : // Grab pending stream input and compute blocking time
1302 0 : for (MediaStream* stream : mStreams) {
1303 0 : if (SourceMediaStream* is = stream->AsSourceStream()) {
1304 0 : ExtractPendingInput(is, aEndBlockingDecisions, &ensureNextIteration);
1305 : }
1306 :
1307 0 : if (stream->mFinished) {
1308 : // The stream's not suspended, and since it's finished, underruns won't
1309 : // stop it playing out. So there's no blocking other than what we impose
1310 : // here.
1311 0 : GraphTime endTime = stream->GetStreamTracks().GetAllTracksEnd() +
1312 0 : stream->mTracksStartTime;
1313 0 : if (endTime <= mStateComputedTime) {
1314 0 : LOG(LogLevel::Verbose,
1315 : ("MediaStream %p is blocked due to being finished", stream));
1316 0 : stream->mStartBlocking = mStateComputedTime;
1317 : } else {
1318 0 : LOG(LogLevel::Verbose,
1319 : ("MediaStream %p is finished, but not blocked yet (end at %f, with "
1320 : "blocking at %f)",
1321 : stream,
1322 : MediaTimeToSeconds(stream->GetTracksEnd()),
1323 : MediaTimeToSeconds(endTime)));
1324 : // Data can't be added to a finished stream, so underruns are irrelevant.
1325 0 : stream->mStartBlocking = std::min(endTime, aEndBlockingDecisions);
1326 : }
1327 : } else {
1328 0 : stream->mStartBlocking = WillUnderrun(stream, aEndBlockingDecisions);
1329 : }
1330 : }
1331 :
1332 0 : for (MediaStream* stream : mSuspendedStreams) {
1333 0 : stream->mStartBlocking = mStateComputedTime;
1334 : }
1335 :
1336 : // The loop is woken up so soon that IterationEnd() barely advances and we
1337 : // end up having aEndBlockingDecision == mStateComputedTime.
1338 : // Since stream blocking is computed in the interval of
1339 : // [mStateComputedTime, aEndBlockingDecision), it won't be computed at all.
1340 : // We should ensure next iteration so that pending blocking changes will be
1341 : // computed in next loop.
1342 0 : if (ensureNextIteration ||
1343 0 : aEndBlockingDecisions == mStateComputedTime) {
1344 0 : EnsureNextIteration();
1345 : }
1346 0 : }
1347 :
1348 : void
1349 0 : MediaStreamGraphImpl::Process()
1350 : {
1351 : // Play stream contents.
1352 0 : bool allBlockedForever = true;
1353 : // True when we've done ProcessInput for all processed streams.
1354 0 : bool doneAllProducing = false;
1355 : // This is the number of frame that are written to the AudioStreams, for
1356 : // this cycle.
1357 0 : StreamTime ticksPlayed = 0;
1358 :
1359 0 : mMixer.StartMixing();
1360 :
1361 : // Figure out what each stream wants to do
1362 0 : for (uint32_t i = 0; i < mStreams.Length(); ++i) {
1363 0 : MediaStream* stream = mStreams[i];
1364 0 : if (!doneAllProducing) {
1365 0 : ProcessedMediaStream* ps = stream->AsProcessedStream();
1366 0 : if (ps) {
1367 0 : AudioNodeStream* n = stream->AsAudioNodeStream();
1368 0 : if (n) {
1369 : #ifdef DEBUG
1370 : // Verify that the sampling rate for all of the following streams is the same
1371 0 : for (uint32_t j = i + 1; j < mStreams.Length(); ++j) {
1372 0 : AudioNodeStream* nextStream = mStreams[j]->AsAudioNodeStream();
1373 0 : if (nextStream) {
1374 0 : MOZ_ASSERT(n->SampleRate() == nextStream->SampleRate(),
1375 : "All AudioNodeStreams in the graph must have the same sampling rate");
1376 : }
1377 : }
1378 : #endif
1379 : // Since an AudioNodeStream is present, go ahead and
1380 : // produce audio block by block for all the rest of the streams.
1381 0 : ProduceDataForStreamsBlockByBlock(i, n->SampleRate());
1382 0 : doneAllProducing = true;
1383 : } else {
1384 0 : ps->ProcessInput(mProcessedTime, mStateComputedTime,
1385 0 : ProcessedMediaStream::ALLOW_FINISH);
1386 0 : NS_ASSERTION(stream->mTracks.GetEnd() >=
1387 : GraphTimeToStreamTimeWithBlocking(stream, mStateComputedTime),
1388 : "Stream did not produce enough data");
1389 : }
1390 : }
1391 : }
1392 0 : NotifyHasCurrentData(stream);
1393 : // Only playback audio and video in real-time mode
1394 0 : if (mRealtime) {
1395 0 : CreateOrDestroyAudioStreams(stream);
1396 0 : if (CurrentDriver()->AsAudioCallbackDriver()) {
1397 0 : StreamTime ticksPlayedForThisStream = PlayAudio(stream);
1398 0 : if (!ticksPlayed) {
1399 0 : ticksPlayed = ticksPlayedForThisStream;
1400 : } else {
1401 0 : MOZ_ASSERT(!ticksPlayedForThisStream || ticksPlayedForThisStream == ticksPlayed,
1402 : "Each stream should have the same number of frame.");
1403 : }
1404 : }
1405 : }
1406 0 : if (stream->mStartBlocking > mProcessedTime) {
1407 0 : allBlockedForever = false;
1408 : }
1409 : }
1410 :
1411 0 : if (CurrentDriver()->AsAudioCallbackDriver() && ticksPlayed) {
1412 0 : mMixer.FinishMixing();
1413 : }
1414 :
1415 0 : if (!allBlockedForever) {
1416 0 : EnsureNextIteration();
1417 : }
1418 0 : }
1419 :
1420 : bool
1421 0 : MediaStreamGraphImpl::UpdateMainThreadState()
1422 : {
1423 0 : MonitorAutoLock lock(mMonitor);
1424 0 : bool finalUpdate = mForceShutDown ||
1425 0 : (mProcessedTime >= mEndTime && AllFinishedStreamsNotified()) ||
1426 0 : (IsEmpty() && mBackMessageQueue.IsEmpty());
1427 0 : PrepareUpdatesToMainThreadState(finalUpdate);
1428 0 : if (finalUpdate) {
1429 : // Enter shutdown mode. The stable-state handler will detect this
1430 : // and complete shutdown. Destroy any streams immediately.
1431 0 : LOG(LogLevel::Debug,
1432 : ("MediaStreamGraph %p waiting for main thread cleanup", this));
1433 : // We'll shut down this graph object if it does not get restarted.
1434 0 : mLifecycleState = LIFECYCLE_WAITING_FOR_MAIN_THREAD_CLEANUP;
1435 : // No need to Destroy streams here. The main-thread owner of each
1436 : // stream is responsible for calling Destroy on them.
1437 0 : return false;
1438 : }
1439 :
1440 0 : CurrentDriver()->WaitForNextIteration();
1441 :
1442 0 : SwapMessageQueues();
1443 0 : return true;
1444 : }
1445 :
1446 : bool
1447 0 : MediaStreamGraphImpl::OneIteration(GraphTime aStateEnd)
1448 : {
1449 0 : WebCore::DenormalDisabler disabler;
1450 :
1451 : // Process graph message from the main thread for this iteration.
1452 0 : RunMessagesInQueue();
1453 :
1454 0 : GraphTime stateEnd = std::min(aStateEnd, mEndTime);
1455 0 : UpdateGraph(stateEnd);
1456 :
1457 0 : mStateComputedTime = stateEnd;
1458 :
1459 0 : Process();
1460 :
1461 0 : GraphTime oldProcessedTime = mProcessedTime;
1462 0 : mProcessedTime = stateEnd;
1463 :
1464 0 : UpdateCurrentTimeForStreams(oldProcessedTime);
1465 :
1466 0 : ProcessChunkMetadata(oldProcessedTime);
1467 :
1468 : // Process graph messages queued from RunMessageAfterProcessing() on this
1469 : // thread during the iteration.
1470 0 : RunMessagesInQueue();
1471 :
1472 0 : return UpdateMainThreadState();
1473 : }
1474 :
1475 : void
1476 0 : MediaStreamGraphImpl::ApplyStreamUpdate(StreamUpdate* aUpdate)
1477 : {
1478 0 : mMonitor.AssertCurrentThreadOwns();
1479 :
1480 0 : MediaStream* stream = aUpdate->mStream;
1481 0 : if (!stream)
1482 0 : return;
1483 0 : stream->mMainThreadCurrentTime = aUpdate->mNextMainThreadCurrentTime;
1484 0 : stream->mMainThreadFinished = aUpdate->mNextMainThreadFinished;
1485 :
1486 0 : if (stream->ShouldNotifyStreamFinished()) {
1487 0 : stream->NotifyMainThreadListeners();
1488 : }
1489 : }
1490 :
1491 : void
1492 0 : MediaStreamGraphImpl::ForceShutDown(ShutdownTicket* aShutdownTicket)
1493 : {
1494 0 : NS_ASSERTION(NS_IsMainThread(), "Must be called on main thread");
1495 0 : LOG(LogLevel::Debug, ("MediaStreamGraph %p ForceShutdown", this));
1496 :
1497 0 : MonitorAutoLock lock(mMonitor);
1498 0 : if (aShutdownTicket) {
1499 0 : MOZ_ASSERT(!mForceShutdownTicket);
1500 : // Avoid waiting forever for a graph to shut down
1501 : // synchronously. Reports are that some 3rd-party audio drivers
1502 : // occasionally hang in shutdown (both for us and Chrome).
1503 0 : mShutdownTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
1504 0 : if (!mShutdownTimer) {
1505 0 : return;
1506 : }
1507 0 : mShutdownTimer->InitWithCallback(this,
1508 : MediaStreamGraph::AUDIO_CALLBACK_DRIVER_SHUTDOWN_TIMEOUT,
1509 0 : nsITimer::TYPE_ONE_SHOT);
1510 : }
1511 0 : mForceShutDown = true;
1512 0 : mForceShutdownTicket = aShutdownTicket;
1513 0 : if (mLifecycleState == LIFECYCLE_THREAD_NOT_STARTED) {
1514 : // We *could* have just sent this a message to start up, so don't
1515 : // yank the rug out from under it. Tell it to startup and let it
1516 : // shut down.
1517 0 : RefPtr<GraphDriver> driver = CurrentDriver();
1518 0 : MonitorAutoUnlock unlock(mMonitor);
1519 0 : driver->Start();
1520 : }
1521 0 : EnsureNextIterationLocked();
1522 : }
1523 :
1524 : NS_IMETHODIMP
1525 0 : MediaStreamGraphImpl::Notify(nsITimer* aTimer)
1526 : {
1527 0 : MonitorAutoLock lock(mMonitor);
1528 0 : NS_ASSERTION(!mForceShutdownTicket, "MediaStreamGraph took too long to shut down!");
1529 : // Sigh, graph took too long to shut down. Stop blocking system
1530 : // shutdown and hope all is well.
1531 0 : mForceShutdownTicket = nullptr;
1532 0 : return NS_OK;
1533 : }
1534 :
1535 :
1536 3 : /* static */ StaticRefPtr<nsIAsyncShutdownBlocker> gMediaStreamGraphShutdownBlocker;
1537 :
1538 : namespace {
1539 :
1540 0 : class MediaStreamGraphShutDownRunnable : public Runnable {
1541 : public:
1542 0 : explicit MediaStreamGraphShutDownRunnable(MediaStreamGraphImpl* aGraph)
1543 0 : : Runnable("MediaStreamGraphShutDownRunnable")
1544 0 : , mGraph(aGraph)
1545 0 : {}
1546 0 : NS_IMETHOD Run()
1547 : {
1548 0 : NS_ASSERTION(mGraph->mDetectedNotRunning,
1549 : "We should know the graph thread control loop isn't running!");
1550 :
1551 0 : LOG(LogLevel::Debug, ("Shutting down graph %p", mGraph.get()));
1552 :
1553 : // We've asserted the graph isn't running. Use mDriver instead of CurrentDriver
1554 : // to avoid thread-safety checks
1555 : #if 0 // AudioCallbackDrivers are released asynchronously anyways
1556 : // XXX a better test would be have setting mDetectedNotRunning make sure
1557 : // any current callback has finished and block future ones -- or just
1558 : // handle it all in Shutdown()!
1559 : if (mGraph->mDriver->AsAudioCallbackDriver()) {
1560 : MOZ_ASSERT(!mGraph->mDriver->AsAudioCallbackDriver()->InCallback());
1561 : }
1562 : #endif
1563 :
1564 0 : mGraph->mDriver->Shutdown(); // This will wait until it's shutdown since
1565 : // we'll start tearing down the graph after this
1566 :
1567 : // Safe to access these without the monitor since the graph isn't running.
1568 : // We may be one of several graphs. Drop ticket to eventually unblock shutdown.
1569 0 : if (mGraph->mShutdownTimer && !mGraph->mForceShutdownTicket) {
1570 0 : MOZ_ASSERT(false,
1571 : "AudioCallbackDriver took too long to shut down and we let shutdown"
1572 : " continue - freezing and leaking");
1573 :
1574 : // The timer fired, so we may be deeper in shutdown now. Block any further
1575 : // teardown and just leak, for safety.
1576 : return NS_OK;
1577 : }
1578 0 : mGraph->mForceShutdownTicket = nullptr;
1579 :
1580 : // We can't block past the final LIFECYCLE_WAITING_FOR_STREAM_DESTRUCTION
1581 : // stage, since completion of that stage requires all streams to be freed,
1582 : // which requires shutdown to proceed.
1583 :
1584 : // mGraph's thread is not running so it's OK to do whatever here
1585 0 : if (mGraph->IsEmpty()) {
1586 : // mGraph is no longer needed, so delete it.
1587 0 : mGraph->Destroy();
1588 : } else {
1589 : // The graph is not empty. We must be in a forced shutdown, or a
1590 : // non-realtime graph that has finished processing. Some later
1591 : // AppendMessage will detect that the manager has been emptied, and
1592 : // delete it.
1593 0 : NS_ASSERTION(mGraph->mForceShutDown || !mGraph->mRealtime,
1594 : "Not in forced shutdown?");
1595 0 : for (MediaStream* stream : mGraph->AllStreams()) {
1596 : // Clean up all MediaSegments since we cannot release Images too
1597 : // late during shutdown.
1598 0 : if (SourceMediaStream* source = stream->AsSourceStream()) {
1599 : // Finishing a SourceStream prevents new data from being appended.
1600 0 : source->Finish();
1601 : }
1602 0 : stream->GetStreamTracks().Clear();
1603 : }
1604 :
1605 0 : mGraph->mLifecycleState =
1606 : MediaStreamGraphImpl::LIFECYCLE_WAITING_FOR_STREAM_DESTRUCTION;
1607 : }
1608 0 : return NS_OK;
1609 : }
1610 : private:
1611 : RefPtr<MediaStreamGraphImpl> mGraph;
1612 : };
1613 :
1614 0 : class MediaStreamGraphStableStateRunnable : public Runnable {
1615 : public:
1616 0 : explicit MediaStreamGraphStableStateRunnable(MediaStreamGraphImpl* aGraph,
1617 : bool aSourceIsMSG)
1618 0 : : Runnable("MediaStreamGraphStableStateRunnable")
1619 : , mGraph(aGraph)
1620 0 : , mSourceIsMSG(aSourceIsMSG)
1621 : {
1622 0 : }
1623 0 : NS_IMETHOD Run() override
1624 : {
1625 0 : if (mGraph) {
1626 0 : mGraph->RunInStableState(mSourceIsMSG);
1627 : }
1628 0 : return NS_OK;
1629 : }
1630 : private:
1631 : RefPtr<MediaStreamGraphImpl> mGraph;
1632 : bool mSourceIsMSG;
1633 : };
1634 :
1635 : /*
1636 : * Control messages forwarded from main thread to graph manager thread
1637 : */
1638 0 : class CreateMessage : public ControlMessage {
1639 : public:
1640 0 : explicit CreateMessage(MediaStream* aStream) : ControlMessage(aStream) {}
1641 0 : void Run() override
1642 : {
1643 0 : mStream->GraphImpl()->AddStreamGraphThread(mStream);
1644 0 : }
1645 0 : void RunDuringShutdown() override
1646 : {
1647 : // Make sure to run this message during shutdown too, to make sure
1648 : // that we balance the number of streams registered with the graph
1649 : // as they're destroyed during shutdown.
1650 0 : Run();
1651 0 : }
1652 : };
1653 :
1654 : } // namespace
1655 :
1656 : void
1657 0 : MediaStreamGraphImpl::RunInStableState(bool aSourceIsMSG)
1658 : {
1659 0 : NS_ASSERTION(NS_IsMainThread(), "Must be called on main thread");
1660 :
1661 0 : nsTArray<nsCOMPtr<nsIRunnable> > runnables;
1662 : // When we're doing a forced shutdown, pending control messages may be
1663 : // run on the main thread via RunDuringShutdown. Those messages must
1664 : // run without the graph monitor being held. So, we collect them here.
1665 0 : nsTArray<UniquePtr<ControlMessage>> controlMessagesToRunDuringShutdown;
1666 :
1667 : {
1668 0 : MonitorAutoLock lock(mMonitor);
1669 0 : if (aSourceIsMSG) {
1670 0 : MOZ_ASSERT(mPostedRunInStableStateEvent);
1671 0 : mPostedRunInStableStateEvent = false;
1672 : }
1673 :
1674 : // This should be kept in sync with the LifecycleState enum in
1675 : // MediaStreamGraphImpl.h
1676 : const char* LifecycleState_str[] = {
1677 : "LIFECYCLE_THREAD_NOT_STARTED",
1678 : "LIFECYCLE_RUNNING",
1679 : "LIFECYCLE_WAITING_FOR_MAIN_THREAD_CLEANUP",
1680 : "LIFECYCLE_WAITING_FOR_THREAD_SHUTDOWN",
1681 : "LIFECYCLE_WAITING_FOR_STREAM_DESTRUCTION"
1682 0 : };
1683 :
1684 0 : if (mLifecycleState != LIFECYCLE_RUNNING) {
1685 0 : LOG(LogLevel::Debug,
1686 : ("Running %p in stable state. Current state: %s",
1687 : this,
1688 : LifecycleState_str[mLifecycleState]));
1689 : }
1690 :
1691 0 : runnables.SwapElements(mUpdateRunnables);
1692 0 : for (uint32_t i = 0; i < mStreamUpdates.Length(); ++i) {
1693 0 : StreamUpdate* update = &mStreamUpdates[i];
1694 0 : if (update->mStream) {
1695 0 : ApplyStreamUpdate(update);
1696 : }
1697 : }
1698 0 : mStreamUpdates.Clear();
1699 :
1700 0 : if (mCurrentTaskMessageQueue.IsEmpty()) {
1701 0 : if (mLifecycleState == LIFECYCLE_WAITING_FOR_MAIN_THREAD_CLEANUP && IsEmpty()) {
1702 : // Complete shutdown. First, ensure that this graph is no longer used.
1703 : // A new graph graph will be created if one is needed.
1704 : // Asynchronously clean up old graph. We don't want to do this
1705 : // synchronously because it spins the event loop waiting for threads
1706 : // to shut down, and we don't want to do that in a stable state handler.
1707 0 : mLifecycleState = LIFECYCLE_WAITING_FOR_THREAD_SHUTDOWN;
1708 0 : LOG(LogLevel::Debug,
1709 : ("Sending MediaStreamGraphShutDownRunnable %p", this));
1710 0 : nsCOMPtr<nsIRunnable> event = new MediaStreamGraphShutDownRunnable(this );
1711 0 : mAbstractMainThread->Dispatch(event.forget());
1712 :
1713 0 : LOG(LogLevel::Debug, ("Disconnecting MediaStreamGraph %p", this));
1714 :
1715 : // Find the graph in the hash table and remove it.
1716 0 : for (auto iter = gGraphs.Iter(); !iter.Done(); iter.Next()) {
1717 0 : if (iter.UserData() == this) {
1718 0 : iter.Remove();
1719 0 : break;
1720 : }
1721 : }
1722 : }
1723 : } else {
1724 0 : if (mLifecycleState <= LIFECYCLE_WAITING_FOR_MAIN_THREAD_CLEANUP) {
1725 0 : MessageBlock* block = mBackMessageQueue.AppendElement();
1726 0 : block->mMessages.SwapElements(mCurrentTaskMessageQueue);
1727 0 : EnsureNextIterationLocked();
1728 : }
1729 :
1730 : // If the MediaStreamGraph has more messages going to it, try to revive
1731 : // it to process those messages. Don't do this if we're in a forced
1732 : // shutdown or it's a non-realtime graph that has already terminated
1733 : // processing.
1734 0 : if (mLifecycleState == LIFECYCLE_WAITING_FOR_MAIN_THREAD_CLEANUP &&
1735 0 : mRealtime && !mForceShutDown) {
1736 0 : mLifecycleState = LIFECYCLE_RUNNING;
1737 : // Revive the MediaStreamGraph since we have more messages going to it.
1738 : // Note that we need to put messages into its queue before reviving it,
1739 : // or it might exit immediately.
1740 : {
1741 0 : LOG(LogLevel::Debug,
1742 : ("Reviving a graph (%p) ! %s",
1743 : this,
1744 : CurrentDriver()->AsAudioCallbackDriver() ? "AudioDriver"
1745 : : "SystemDriver"));
1746 0 : RefPtr<GraphDriver> driver = CurrentDriver();
1747 0 : MonitorAutoUnlock unlock(mMonitor);
1748 0 : driver->Revive();
1749 : }
1750 : }
1751 : }
1752 :
1753 : // Don't start the thread for a non-realtime graph until it has been
1754 : // explicitly started by StartNonRealtimeProcessing.
1755 0 : if (mLifecycleState == LIFECYCLE_THREAD_NOT_STARTED &&
1756 0 : (mRealtime || mNonRealtimeProcessing)) {
1757 0 : mLifecycleState = LIFECYCLE_RUNNING;
1758 : // Start the thread now. We couldn't start it earlier because
1759 : // the graph might exit immediately on finding it has no streams. The
1760 : // first message for a new graph must create a stream.
1761 : {
1762 : // We should exit the monitor for now, because starting a stream might
1763 : // take locks, and we don't want to deadlock.
1764 0 : LOG(LogLevel::Debug,
1765 : ("Starting a graph (%p) ! %s",
1766 : this,
1767 : CurrentDriver()->AsAudioCallbackDriver() ? "AudioDriver"
1768 : : "SystemDriver"));
1769 0 : RefPtr<GraphDriver> driver = CurrentDriver();
1770 0 : MonitorAutoUnlock unlock(mMonitor);
1771 0 : driver->Start();
1772 : // It's not safe to Shutdown() a thread from StableState, and
1773 : // releasing this may shutdown a SystemClockDriver thread.
1774 : // Proxy the release to outside of StableState.
1775 : NS_ReleaseOnMainThread(
1776 0 : "MediaStreamGraphImpl::CurrentDriver", driver.forget(),
1777 0 : true); // always proxy
1778 : }
1779 : }
1780 :
1781 0 : if ((mForceShutDown || !mRealtime) &&
1782 0 : mLifecycleState == LIFECYCLE_WAITING_FOR_MAIN_THREAD_CLEANUP) {
1783 : // Defer calls to RunDuringShutdown() to happen while mMonitor is not held.
1784 0 : for (uint32_t i = 0; i < mBackMessageQueue.Length(); ++i) {
1785 0 : MessageBlock& mb = mBackMessageQueue[i];
1786 0 : controlMessagesToRunDuringShutdown.AppendElements(Move(mb.mMessages));
1787 : }
1788 0 : mBackMessageQueue.Clear();
1789 0 : MOZ_ASSERT(mCurrentTaskMessageQueue.IsEmpty());
1790 : // Stop MediaStreamGraph threads. Do not clear gGraph since
1791 : // we have outstanding DOM objects that may need it.
1792 0 : mLifecycleState = LIFECYCLE_WAITING_FOR_THREAD_SHUTDOWN;
1793 0 : nsCOMPtr<nsIRunnable> event = new MediaStreamGraphShutDownRunnable(this);
1794 0 : mAbstractMainThread->Dispatch(event.forget());
1795 : }
1796 :
1797 0 : mDetectedNotRunning = mLifecycleState > LIFECYCLE_RUNNING;
1798 : }
1799 :
1800 : // Make sure we get a new current time in the next event loop task
1801 0 : if (!aSourceIsMSG) {
1802 0 : MOZ_ASSERT(mPostedRunInStableState);
1803 0 : mPostedRunInStableState = false;
1804 : }
1805 :
1806 0 : for (uint32_t i = 0; i < controlMessagesToRunDuringShutdown.Length(); ++i) {
1807 0 : controlMessagesToRunDuringShutdown[i]->RunDuringShutdown();
1808 : }
1809 :
1810 : #ifdef DEBUG
1811 0 : mCanRunMessagesSynchronously = mDetectedNotRunning &&
1812 0 : mLifecycleState >= LIFECYCLE_WAITING_FOR_THREAD_SHUTDOWN;
1813 : #endif
1814 :
1815 0 : for (uint32_t i = 0; i < runnables.Length(); ++i) {
1816 0 : runnables[i]->Run();
1817 : }
1818 0 : }
1819 :
1820 :
1821 : void
1822 0 : MediaStreamGraphImpl::EnsureRunInStableState()
1823 : {
1824 0 : NS_ASSERTION(NS_IsMainThread(), "main thread only");
1825 :
1826 0 : if (mPostedRunInStableState)
1827 0 : return;
1828 0 : mPostedRunInStableState = true;
1829 0 : nsCOMPtr<nsIRunnable> event = new MediaStreamGraphStableStateRunnable(this, false);
1830 0 : nsContentUtils::RunInStableState(event.forget());
1831 : }
1832 :
1833 : void
1834 0 : MediaStreamGraphImpl::EnsureStableStateEventPosted()
1835 : {
1836 0 : mMonitor.AssertCurrentThreadOwns();
1837 :
1838 0 : if (mPostedRunInStableStateEvent)
1839 0 : return;
1840 0 : mPostedRunInStableStateEvent = true;
1841 0 : nsCOMPtr<nsIRunnable> event = new MediaStreamGraphStableStateRunnable(this, true);
1842 0 : mAbstractMainThread->Dispatch(event.forget());
1843 : }
1844 :
1845 : void
1846 0 : MediaStreamGraphImpl::AppendMessage(UniquePtr<ControlMessage> aMessage)
1847 : {
1848 0 : MOZ_ASSERT(NS_IsMainThread(), "main thread only");
1849 0 : MOZ_ASSERT(!aMessage->GetStream() ||
1850 : !aMessage->GetStream()->IsDestroyed(),
1851 : "Stream already destroyed");
1852 :
1853 0 : if (mDetectedNotRunning &&
1854 0 : mLifecycleState > LIFECYCLE_WAITING_FOR_MAIN_THREAD_CLEANUP) {
1855 : // The graph control loop is not running and main thread cleanup has
1856 : // happened. From now on we can't append messages to mCurrentTaskMessageQueue,
1857 : // because that will never be processed again, so just RunDuringShutdown
1858 : // this message.
1859 : // This should only happen during forced shutdown, or after a non-realtime
1860 : // graph has finished processing.
1861 : #ifdef DEBUG
1862 0 : MOZ_ASSERT(mCanRunMessagesSynchronously);
1863 0 : mCanRunMessagesSynchronously = false;
1864 : #endif
1865 0 : aMessage->RunDuringShutdown();
1866 : #ifdef DEBUG
1867 0 : mCanRunMessagesSynchronously = true;
1868 : #endif
1869 0 : if (IsEmpty() &&
1870 0 : mLifecycleState >= LIFECYCLE_WAITING_FOR_STREAM_DESTRUCTION) {
1871 :
1872 : // Find the graph in the hash table and remove it.
1873 0 : for (auto iter = gGraphs.Iter(); !iter.Done(); iter.Next()) {
1874 0 : if (iter.UserData() == this) {
1875 0 : iter.Remove();
1876 0 : break;
1877 : }
1878 : }
1879 :
1880 0 : Destroy();
1881 : }
1882 0 : return;
1883 : }
1884 :
1885 0 : mCurrentTaskMessageQueue.AppendElement(Move(aMessage));
1886 0 : EnsureRunInStableState();
1887 : }
1888 :
1889 : void
1890 0 : MediaStreamGraphImpl::Dispatch(already_AddRefed<nsIRunnable>&& aRunnable)
1891 : {
1892 0 : mAbstractMainThread->Dispatch(Move(aRunnable));
1893 0 : }
1894 :
1895 0 : MediaStream::MediaStream()
1896 : : mTracksStartTime(0)
1897 : , mStartBlocking(GRAPH_TIME_MAX)
1898 : , mSuspendedCount(0)
1899 : , mFinished(false)
1900 : , mNotifiedFinished(false)
1901 : , mNotifiedBlocked(false)
1902 : , mHasCurrentData(false)
1903 : , mNotifiedHasCurrentData(false)
1904 : , mMainThreadCurrentTime(0)
1905 : , mMainThreadFinished(false)
1906 : , mFinishedNotificationSent(false)
1907 : , mMainThreadDestroyed(false)
1908 : , mNrOfMainThreadUsers(0)
1909 : , mGraph(nullptr)
1910 0 : , mAudioChannelType(dom::AudioChannel::Normal)
1911 : {
1912 0 : MOZ_COUNT_CTOR(MediaStream);
1913 0 : }
1914 :
1915 0 : MediaStream::~MediaStream()
1916 : {
1917 0 : MOZ_COUNT_DTOR(MediaStream);
1918 0 : NS_ASSERTION(mMainThreadDestroyed, "Should have been destroyed already");
1919 0 : NS_ASSERTION(mMainThreadListeners.IsEmpty(),
1920 : "All main thread listeners should have been removed");
1921 0 : }
1922 :
1923 : size_t
1924 0 : MediaStream::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
1925 : {
1926 0 : size_t amount = 0;
1927 :
1928 : // Not owned:
1929 : // - mGraph - Not reported here
1930 : // - mConsumers - elements
1931 : // Future:
1932 : // - mVideoOutputs - elements
1933 : // - mLastPlayedVideoFrame
1934 : // - mListeners - elements
1935 : // - mAudioOutputStream - elements
1936 :
1937 0 : amount += mTracks.SizeOfExcludingThis(aMallocSizeOf);
1938 0 : amount += mAudioOutputs.ShallowSizeOfExcludingThis(aMallocSizeOf);
1939 0 : amount += mVideoOutputs.ShallowSizeOfExcludingThis(aMallocSizeOf);
1940 0 : amount += mListeners.ShallowSizeOfExcludingThis(aMallocSizeOf);
1941 0 : amount += mMainThreadListeners.ShallowSizeOfExcludingThis(aMallocSizeOf);
1942 0 : amount += mDisabledTracks.ShallowSizeOfExcludingThis(aMallocSizeOf);
1943 0 : amount += mConsumers.ShallowSizeOfExcludingThis(aMallocSizeOf);
1944 :
1945 0 : return amount;
1946 : }
1947 :
1948 : size_t
1949 0 : MediaStream::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
1950 : {
1951 0 : return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
1952 : }
1953 :
1954 : void
1955 0 : MediaStream::IncrementSuspendCount()
1956 : {
1957 0 : ++mSuspendedCount;
1958 0 : if (mSuspendedCount == 1) {
1959 0 : for (uint32_t i = 0; i < mConsumers.Length(); ++i) {
1960 0 : mConsumers[i]->Suspended();
1961 : }
1962 : }
1963 0 : }
1964 :
1965 : void
1966 0 : MediaStream::DecrementSuspendCount()
1967 : {
1968 0 : NS_ASSERTION(mSuspendedCount > 0, "Suspend count underrun");
1969 0 : --mSuspendedCount;
1970 :
1971 0 : if (mSuspendedCount == 0) {
1972 0 : for (uint32_t i = 0; i < mConsumers.Length(); ++i) {
1973 0 : mConsumers[i]->Resumed();
1974 : }
1975 : }
1976 0 : }
1977 :
1978 : MediaStreamGraphImpl*
1979 0 : MediaStream::GraphImpl()
1980 : {
1981 0 : return mGraph;
1982 : }
1983 :
1984 : MediaStreamGraph*
1985 0 : MediaStream::Graph()
1986 : {
1987 0 : return mGraph;
1988 : }
1989 :
1990 : void
1991 0 : MediaStream::SetGraphImpl(MediaStreamGraphImpl* aGraph)
1992 : {
1993 0 : MOZ_ASSERT(!mGraph, "Should only be called once");
1994 0 : mGraph = aGraph;
1995 0 : mAudioChannelType = aGraph->AudioChannel();
1996 0 : mTracks.InitGraphRate(aGraph->GraphRate());
1997 0 : }
1998 :
1999 : void
2000 0 : MediaStream::SetGraphImpl(MediaStreamGraph* aGraph)
2001 : {
2002 0 : MediaStreamGraphImpl* graph = static_cast<MediaStreamGraphImpl*>(aGraph);
2003 0 : SetGraphImpl(graph);
2004 0 : }
2005 :
2006 : StreamTime
2007 0 : MediaStream::GraphTimeToStreamTime(GraphTime aTime)
2008 : {
2009 0 : NS_ASSERTION(mStartBlocking == GraphImpl()->mStateComputedTime ||
2010 : aTime <= mStartBlocking,
2011 : "Incorrectly ignoring blocking!");
2012 0 : return aTime - mTracksStartTime;
2013 : }
2014 :
2015 : GraphTime
2016 0 : MediaStream::StreamTimeToGraphTime(StreamTime aTime)
2017 : {
2018 0 : NS_ASSERTION(mStartBlocking == GraphImpl()->mStateComputedTime ||
2019 : aTime + mTracksStartTime <= mStartBlocking,
2020 : "Incorrectly ignoring blocking!");
2021 0 : return aTime + mTracksStartTime;
2022 : }
2023 :
2024 : StreamTime
2025 0 : MediaStream::GraphTimeToStreamTimeWithBlocking(GraphTime aTime)
2026 : {
2027 0 : return GraphImpl()->GraphTimeToStreamTimeWithBlocking(this, aTime);
2028 : }
2029 :
2030 : void
2031 0 : MediaStream::FinishOnGraphThread()
2032 : {
2033 0 : GraphImpl()->FinishStream(this);
2034 0 : }
2035 :
2036 : StreamTracks::Track*
2037 0 : MediaStream::FindTrack(TrackID aID)
2038 : {
2039 0 : return mTracks.FindTrack(aID);
2040 : }
2041 :
2042 : StreamTracks::Track*
2043 0 : MediaStream::EnsureTrack(TrackID aTrackId)
2044 : {
2045 0 : StreamTracks::Track* track = mTracks.FindTrack(aTrackId);
2046 0 : if (!track) {
2047 0 : nsAutoPtr<MediaSegment> segment(new AudioSegment());
2048 0 : for (uint32_t j = 0; j < mListeners.Length(); ++j) {
2049 0 : MediaStreamListener* l = mListeners[j];
2050 0 : l->NotifyQueuedTrackChanges(Graph(), aTrackId, 0,
2051 : TrackEventCommand::TRACK_EVENT_CREATED,
2052 0 : *segment);
2053 : // TODO If we ever need to ensure several tracks at once, we will have to
2054 : // change this.
2055 0 : l->NotifyFinishedTrackCreation(Graph());
2056 : }
2057 0 : track = &mTracks.AddTrack(aTrackId, 0, segment.forget());
2058 : }
2059 0 : return track;
2060 : }
2061 :
2062 : void
2063 0 : MediaStream::RemoveAllListenersImpl()
2064 : {
2065 0 : for (int32_t i = mListeners.Length() - 1; i >= 0; --i) {
2066 0 : RefPtr<MediaStreamListener> listener = mListeners[i].forget();
2067 0 : listener->NotifyEvent(GraphImpl(), MediaStreamGraphEvent::EVENT_REMOVED);
2068 : }
2069 0 : mListeners.Clear();
2070 0 : }
2071 :
2072 : void
2073 0 : MediaStream::DestroyImpl()
2074 : {
2075 0 : for (int32_t i = mConsumers.Length() - 1; i >= 0; --i) {
2076 0 : mConsumers[i]->Disconnect();
2077 : }
2078 0 : mGraph = nullptr;
2079 0 : }
2080 :
2081 : void
2082 0 : MediaStream::Destroy()
2083 : {
2084 0 : NS_ASSERTION(mNrOfMainThreadUsers == 0,
2085 : "Do not mix Destroy() and RegisterUser()/UnregisterUser()");
2086 : // Keep this stream alive until we leave this method
2087 0 : RefPtr<MediaStream> kungFuDeathGrip = this;
2088 :
2089 0 : class Message : public ControlMessage {
2090 : public:
2091 0 : explicit Message(MediaStream* aStream) : ControlMessage(aStream) {}
2092 0 : void Run() override
2093 : {
2094 0 : mStream->RemoveAllListenersImpl();
2095 0 : auto graph = mStream->GraphImpl();
2096 0 : mStream->DestroyImpl();
2097 0 : graph->RemoveStreamGraphThread(mStream);
2098 0 : }
2099 0 : void RunDuringShutdown() override
2100 0 : { Run(); }
2101 : };
2102 0 : GraphImpl()->AppendMessage(MakeUnique<Message>(this));
2103 : // Message::RunDuringShutdown may have removed this stream from the graph,
2104 : // but our kungFuDeathGrip above will have kept this stream alive if
2105 : // necessary.
2106 0 : mMainThreadDestroyed = true;
2107 0 : }
2108 :
2109 : void
2110 0 : MediaStream::RegisterUser()
2111 : {
2112 0 : MOZ_ASSERT(NS_IsMainThread());
2113 0 : ++mNrOfMainThreadUsers;
2114 0 : }
2115 :
2116 : void
2117 0 : MediaStream::UnregisterUser()
2118 : {
2119 0 : MOZ_ASSERT(NS_IsMainThread());
2120 :
2121 0 : --mNrOfMainThreadUsers;
2122 0 : NS_ASSERTION(mNrOfMainThreadUsers >= 0, "Double-removal of main thread user");
2123 0 : NS_ASSERTION(!IsDestroyed(), "Do not mix Destroy() and RegisterUser()/UnregisterUser()");
2124 0 : if (mNrOfMainThreadUsers == 0) {
2125 0 : Destroy();
2126 : }
2127 0 : }
2128 :
2129 : void
2130 0 : MediaStream::AddAudioOutput(void* aKey)
2131 : {
2132 0 : class Message : public ControlMessage {
2133 : public:
2134 0 : Message(MediaStream* aStream, void* aKey) : ControlMessage(aStream), mKey(aKey) {}
2135 0 : void Run() override
2136 : {
2137 0 : mStream->AddAudioOutputImpl(mKey);
2138 0 : }
2139 : void* mKey;
2140 : };
2141 0 : GraphImpl()->AppendMessage(MakeUnique<Message>(this, aKey));
2142 0 : }
2143 :
2144 : void
2145 0 : MediaStream::SetAudioOutputVolumeImpl(void* aKey, float aVolume)
2146 : {
2147 0 : for (uint32_t i = 0; i < mAudioOutputs.Length(); ++i) {
2148 0 : if (mAudioOutputs[i].mKey == aKey) {
2149 0 : mAudioOutputs[i].mVolume = aVolume;
2150 0 : return;
2151 : }
2152 : }
2153 0 : NS_ERROR("Audio output key not found");
2154 : }
2155 :
2156 : void
2157 0 : MediaStream::SetAudioOutputVolume(void* aKey, float aVolume)
2158 : {
2159 0 : class Message : public ControlMessage {
2160 : public:
2161 0 : Message(MediaStream* aStream, void* aKey, float aVolume) :
2162 0 : ControlMessage(aStream), mKey(aKey), mVolume(aVolume) {}
2163 0 : void Run() override
2164 : {
2165 0 : mStream->SetAudioOutputVolumeImpl(mKey, mVolume);
2166 0 : }
2167 : void* mKey;
2168 : float mVolume;
2169 : };
2170 0 : GraphImpl()->AppendMessage(MakeUnique<Message>(this, aKey, aVolume));
2171 0 : }
2172 :
2173 : void
2174 0 : MediaStream::AddAudioOutputImpl(void* aKey)
2175 : {
2176 0 : LOG(LogLevel::Info,
2177 : ("MediaStream %p Adding AudioOutput for key %p", this, aKey));
2178 0 : mAudioOutputs.AppendElement(AudioOutput(aKey));
2179 0 : }
2180 :
2181 : void
2182 0 : MediaStream::RemoveAudioOutputImpl(void* aKey)
2183 : {
2184 0 : LOG(LogLevel::Info,
2185 : ("MediaStream %p Removing AudioOutput for key %p", this, aKey));
2186 0 : for (uint32_t i = 0; i < mAudioOutputs.Length(); ++i) {
2187 0 : if (mAudioOutputs[i].mKey == aKey) {
2188 0 : mAudioOutputs.RemoveElementAt(i);
2189 0 : return;
2190 : }
2191 : }
2192 0 : NS_ERROR("Audio output key not found");
2193 : }
2194 :
2195 : void
2196 0 : MediaStream::RemoveAudioOutput(void* aKey)
2197 : {
2198 0 : class Message : public ControlMessage {
2199 : public:
2200 0 : Message(MediaStream* aStream, void* aKey) :
2201 0 : ControlMessage(aStream), mKey(aKey) {}
2202 0 : void Run() override
2203 : {
2204 0 : mStream->RemoveAudioOutputImpl(mKey);
2205 0 : }
2206 : void* mKey;
2207 : };
2208 0 : GraphImpl()->AppendMessage(MakeUnique<Message>(this, aKey));
2209 0 : }
2210 :
2211 : void
2212 0 : MediaStream::AddVideoOutputImpl(already_AddRefed<MediaStreamVideoSink> aSink,
2213 : TrackID aID)
2214 : {
2215 0 : RefPtr<MediaStreamVideoSink> sink = aSink;
2216 0 : LOG(LogLevel::Info,
2217 : ("MediaStream %p Adding MediaStreamVideoSink %p as output",
2218 : this,
2219 : sink.get()));
2220 0 : MOZ_ASSERT(aID != TRACK_NONE);
2221 0 : for (auto entry : mVideoOutputs) {
2222 0 : if (entry.mListener == sink &&
2223 0 : (entry.mTrackID == TRACK_ANY || entry.mTrackID == aID)) {
2224 0 : return;
2225 : }
2226 : }
2227 0 : TrackBound<MediaStreamVideoSink>* l = mVideoOutputs.AppendElement();
2228 0 : l->mListener = sink;
2229 0 : l->mTrackID = aID;
2230 :
2231 0 : AddDirectTrackListenerImpl(sink.forget(), aID);
2232 : }
2233 :
2234 : void
2235 0 : MediaStream::RemoveVideoOutputImpl(MediaStreamVideoSink* aSink,
2236 : TrackID aID)
2237 : {
2238 0 : LOG(
2239 : LogLevel::Info,
2240 : ("MediaStream %p Removing MediaStreamVideoSink %p as output", this, aSink));
2241 0 : MOZ_ASSERT(aID != TRACK_NONE);
2242 :
2243 : // Ensure that any frames currently queued for playback by the compositor
2244 : // are removed.
2245 0 : aSink->ClearFrames();
2246 0 : for (size_t i = 0; i < mVideoOutputs.Length(); ++i) {
2247 0 : if (mVideoOutputs[i].mListener == aSink &&
2248 0 : (mVideoOutputs[i].mTrackID == TRACK_ANY ||
2249 0 : mVideoOutputs[i].mTrackID == aID)) {
2250 0 : mVideoOutputs.RemoveElementAt(i);
2251 : }
2252 : }
2253 :
2254 0 : RemoveDirectTrackListenerImpl(aSink, aID);
2255 0 : }
2256 :
2257 : void
2258 0 : MediaStream::AddVideoOutput(MediaStreamVideoSink* aSink, TrackID aID)
2259 : {
2260 0 : class Message : public ControlMessage {
2261 : public:
2262 0 : Message(MediaStream* aStream, MediaStreamVideoSink* aSink, TrackID aID) :
2263 0 : ControlMessage(aStream), mSink(aSink), mID(aID) {}
2264 0 : void Run() override
2265 : {
2266 0 : mStream->AddVideoOutputImpl(mSink.forget(), mID);
2267 0 : }
2268 : RefPtr<MediaStreamVideoSink> mSink;
2269 : TrackID mID;
2270 : };
2271 0 : GraphImpl()->AppendMessage(MakeUnique<Message>(this, aSink, aID));
2272 0 : }
2273 :
2274 : void
2275 0 : MediaStream::RemoveVideoOutput(MediaStreamVideoSink* aSink, TrackID aID)
2276 : {
2277 0 : class Message : public ControlMessage {
2278 : public:
2279 0 : Message(MediaStream* aStream, MediaStreamVideoSink* aSink, TrackID aID) :
2280 0 : ControlMessage(aStream), mSink(aSink), mID(aID) {}
2281 0 : void Run() override
2282 : {
2283 0 : mStream->RemoveVideoOutputImpl(mSink, mID);
2284 0 : }
2285 : RefPtr<MediaStreamVideoSink> mSink;
2286 : TrackID mID;
2287 : };
2288 0 : GraphImpl()->AppendMessage(MakeUnique<Message>(this, aSink, aID));
2289 0 : }
2290 :
2291 : void
2292 0 : MediaStream::Suspend()
2293 : {
2294 0 : class Message : public ControlMessage {
2295 : public:
2296 0 : explicit Message(MediaStream* aStream) :
2297 0 : ControlMessage(aStream) {}
2298 0 : void Run() override
2299 : {
2300 0 : mStream->GraphImpl()->IncrementSuspendCount(mStream);
2301 0 : }
2302 : };
2303 :
2304 : // This can happen if this method has been called asynchronously, and the
2305 : // stream has been destroyed since then.
2306 0 : if (mMainThreadDestroyed) {
2307 0 : return;
2308 : }
2309 0 : GraphImpl()->AppendMessage(MakeUnique<Message>(this));
2310 : }
2311 :
2312 : void
2313 0 : MediaStream::Resume()
2314 : {
2315 0 : class Message : public ControlMessage {
2316 : public:
2317 0 : explicit Message(MediaStream* aStream) :
2318 0 : ControlMessage(aStream) {}
2319 0 : void Run() override
2320 : {
2321 0 : mStream->GraphImpl()->DecrementSuspendCount(mStream);
2322 0 : }
2323 : };
2324 :
2325 : // This can happen if this method has been called asynchronously, and the
2326 : // stream has been destroyed since then.
2327 0 : if (mMainThreadDestroyed) {
2328 0 : return;
2329 : }
2330 0 : GraphImpl()->AppendMessage(MakeUnique<Message>(this));
2331 : }
2332 :
2333 : void
2334 0 : MediaStream::AddListenerImpl(already_AddRefed<MediaStreamListener> aListener)
2335 : {
2336 0 : MediaStreamListener* listener = *mListeners.AppendElement() = aListener;
2337 0 : listener->NotifyBlockingChanged(GraphImpl(),
2338 0 : mNotifiedBlocked ? MediaStreamListener::BLOCKED : MediaStreamListener::UNBLOCKED);
2339 :
2340 0 : for (StreamTracks::TrackIter it(mTracks); !it.IsEnded(); it.Next()) {
2341 0 : MediaStream* inputStream = nullptr;
2342 0 : TrackID inputTrackID = TRACK_INVALID;
2343 0 : if (ProcessedMediaStream* ps = AsProcessedStream()) {
2344 : // The only ProcessedMediaStream where we should have listeners is
2345 : // TrackUnionStream - it's what's used as owned stream in DOMMediaStream,
2346 : // the only main-thread exposed stream type.
2347 : // TrackUnionStream guarantees that each of its tracks has an input track.
2348 : // Other types do not implement GetInputStreamFor() and will return null.
2349 0 : inputStream = ps->GetInputStreamFor(it->GetID());
2350 0 : MOZ_ASSERT(inputStream);
2351 0 : inputTrackID = ps->GetInputTrackIDFor(it->GetID());
2352 0 : MOZ_ASSERT(IsTrackIDExplicit(inputTrackID));
2353 : }
2354 :
2355 0 : uint32_t flags = TrackEventCommand::TRACK_EVENT_CREATED;
2356 0 : if (it->IsEnded()) {
2357 0 : flags |= TrackEventCommand::TRACK_EVENT_ENDED;
2358 : }
2359 0 : nsAutoPtr<MediaSegment> segment(it->GetSegment()->CreateEmptyClone());
2360 0 : listener->NotifyQueuedTrackChanges(Graph(), it->GetID(), it->GetEnd(),
2361 0 : static_cast<TrackEventCommand>(flags), *segment,
2362 0 : inputStream, inputTrackID);
2363 : }
2364 0 : if (mNotifiedFinished) {
2365 0 : listener->NotifyEvent(GraphImpl(), MediaStreamGraphEvent::EVENT_FINISHED);
2366 : }
2367 0 : if (mNotifiedHasCurrentData) {
2368 0 : listener->NotifyHasCurrentData(GraphImpl());
2369 : }
2370 0 : }
2371 :
2372 : void
2373 0 : MediaStream::AddListener(MediaStreamListener* aListener)
2374 : {
2375 0 : class Message : public ControlMessage {
2376 : public:
2377 0 : Message(MediaStream* aStream, MediaStreamListener* aListener) :
2378 0 : ControlMessage(aStream), mListener(aListener) {}
2379 0 : void Run() override
2380 : {
2381 0 : mStream->AddListenerImpl(mListener.forget());
2382 0 : }
2383 : RefPtr<MediaStreamListener> mListener;
2384 : };
2385 0 : GraphImpl()->AppendMessage(MakeUnique<Message>(this, aListener));
2386 0 : }
2387 :
2388 : void
2389 0 : MediaStream::RemoveListenerImpl(MediaStreamListener* aListener)
2390 : {
2391 : // wouldn't need this if we could do it in the opposite order
2392 0 : RefPtr<MediaStreamListener> listener(aListener);
2393 0 : mListeners.RemoveElement(aListener);
2394 0 : listener->NotifyEvent(GraphImpl(), MediaStreamGraphEvent::EVENT_REMOVED);
2395 0 : }
2396 :
2397 : void
2398 0 : MediaStream::RemoveListener(MediaStreamListener* aListener)
2399 : {
2400 0 : class Message : public ControlMessage {
2401 : public:
2402 0 : Message(MediaStream* aStream, MediaStreamListener* aListener) :
2403 0 : ControlMessage(aStream), mListener(aListener) {}
2404 0 : void Run() override
2405 : {
2406 0 : mStream->RemoveListenerImpl(mListener);
2407 0 : }
2408 : RefPtr<MediaStreamListener> mListener;
2409 : };
2410 : // If the stream is destroyed the Listeners have or will be
2411 : // removed.
2412 0 : if (!IsDestroyed()) {
2413 0 : GraphImpl()->AppendMessage(MakeUnique<Message>(this, aListener));
2414 : }
2415 0 : }
2416 :
2417 : void
2418 0 : MediaStream::AddTrackListenerImpl(already_AddRefed<MediaStreamTrackListener> aListener,
2419 : TrackID aTrackID)
2420 : {
2421 0 : TrackBound<MediaStreamTrackListener>* l = mTrackListeners.AppendElement();
2422 0 : l->mListener = aListener;
2423 0 : l->mTrackID = aTrackID;
2424 :
2425 0 : StreamTracks::Track* track = FindTrack(aTrackID);
2426 0 : if (!track) {
2427 0 : return;
2428 : }
2429 : PrincipalHandle lastPrincipalHandle =
2430 0 : track->GetSegment()->GetLastPrincipalHandle();
2431 0 : l->mListener->NotifyPrincipalHandleChanged(Graph(), lastPrincipalHandle);
2432 : }
2433 :
2434 : void
2435 0 : MediaStream::AddTrackListener(MediaStreamTrackListener* aListener,
2436 : TrackID aTrackID)
2437 : {
2438 0 : class Message : public ControlMessage {
2439 : public:
2440 0 : Message(MediaStream* aStream, MediaStreamTrackListener* aListener,
2441 0 : TrackID aTrackID) :
2442 0 : ControlMessage(aStream), mListener(aListener), mTrackID(aTrackID) {}
2443 0 : virtual void Run()
2444 : {
2445 0 : mStream->AddTrackListenerImpl(mListener.forget(), mTrackID);
2446 0 : }
2447 : RefPtr<MediaStreamTrackListener> mListener;
2448 : TrackID mTrackID;
2449 : };
2450 0 : GraphImpl()->AppendMessage(MakeUnique<Message>(this, aListener, aTrackID));
2451 0 : }
2452 :
2453 : void
2454 0 : MediaStream::RemoveTrackListenerImpl(MediaStreamTrackListener* aListener,
2455 : TrackID aTrackID)
2456 : {
2457 0 : for (size_t i = 0; i < mTrackListeners.Length(); ++i) {
2458 0 : if (mTrackListeners[i].mListener == aListener &&
2459 0 : mTrackListeners[i].mTrackID == aTrackID) {
2460 0 : mTrackListeners[i].mListener->NotifyRemoved();
2461 0 : mTrackListeners.RemoveElementAt(i);
2462 0 : return;
2463 : }
2464 : }
2465 : }
2466 :
2467 : void
2468 0 : MediaStream::RemoveTrackListener(MediaStreamTrackListener* aListener,
2469 : TrackID aTrackID)
2470 : {
2471 0 : class Message : public ControlMessage {
2472 : public:
2473 0 : Message(MediaStream* aStream, MediaStreamTrackListener* aListener,
2474 0 : TrackID aTrackID) :
2475 0 : ControlMessage(aStream), mListener(aListener), mTrackID(aTrackID) {}
2476 0 : virtual void Run()
2477 : {
2478 0 : mStream->RemoveTrackListenerImpl(mListener, mTrackID);
2479 0 : }
2480 : RefPtr<MediaStreamTrackListener> mListener;
2481 : TrackID mTrackID;
2482 : };
2483 0 : GraphImpl()->AppendMessage(MakeUnique<Message>(this, aListener, aTrackID));
2484 0 : }
2485 :
2486 : void
2487 0 : MediaStream::AddDirectTrackListenerImpl(already_AddRefed<DirectMediaStreamTrackListener> aListener,
2488 : TrackID aTrackID)
2489 : {
2490 : // Base implementation, for streams that don't support direct track listeners.
2491 0 : RefPtr<DirectMediaStreamTrackListener> listener = aListener;
2492 0 : listener->NotifyDirectListenerInstalled(
2493 0 : DirectMediaStreamTrackListener::InstallationResult::STREAM_NOT_SUPPORTED);
2494 0 : }
2495 :
2496 : void
2497 0 : MediaStream::AddDirectTrackListener(DirectMediaStreamTrackListener* aListener,
2498 : TrackID aTrackID)
2499 : {
2500 0 : class Message : public ControlMessage {
2501 : public:
2502 0 : Message(MediaStream* aStream, DirectMediaStreamTrackListener* aListener,
2503 0 : TrackID aTrackID) :
2504 0 : ControlMessage(aStream), mListener(aListener), mTrackID(aTrackID) {}
2505 0 : virtual void Run()
2506 : {
2507 0 : mStream->AddDirectTrackListenerImpl(mListener.forget(), mTrackID);
2508 0 : }
2509 : RefPtr<DirectMediaStreamTrackListener> mListener;
2510 : TrackID mTrackID;
2511 : };
2512 0 : GraphImpl()->AppendMessage(MakeUnique<Message>(this, aListener, aTrackID));
2513 0 : }
2514 :
2515 : void
2516 0 : MediaStream::RemoveDirectTrackListenerImpl(DirectMediaStreamTrackListener* aListener,
2517 : TrackID aTrackID)
2518 : {
2519 : // Base implementation, the listener was never added so nothing to do.
2520 0 : RefPtr<DirectMediaStreamTrackListener> listener = aListener;
2521 0 : }
2522 :
2523 : void
2524 0 : MediaStream::RemoveDirectTrackListener(DirectMediaStreamTrackListener* aListener,
2525 : TrackID aTrackID)
2526 : {
2527 0 : class Message : public ControlMessage {
2528 : public:
2529 0 : Message(MediaStream* aStream, DirectMediaStreamTrackListener* aListener,
2530 0 : TrackID aTrackID) :
2531 0 : ControlMessage(aStream), mListener(aListener), mTrackID(aTrackID) {}
2532 0 : virtual void Run()
2533 : {
2534 0 : mStream->RemoveDirectTrackListenerImpl(mListener, mTrackID);
2535 0 : }
2536 : RefPtr<DirectMediaStreamTrackListener> mListener;
2537 : TrackID mTrackID;
2538 : };
2539 0 : GraphImpl()->AppendMessage(MakeUnique<Message>(this, aListener, aTrackID));
2540 0 : }
2541 :
2542 : void
2543 0 : MediaStream::RunAfterPendingUpdates(already_AddRefed<nsIRunnable> aRunnable)
2544 : {
2545 0 : MOZ_ASSERT(NS_IsMainThread());
2546 0 : MediaStreamGraphImpl* graph = GraphImpl();
2547 0 : nsCOMPtr<nsIRunnable> runnable(aRunnable);
2548 :
2549 : // Special case when a non-realtime graph has not started, to ensure the
2550 : // runnable will run in finite time.
2551 0 : if (!(graph->mRealtime || graph->mNonRealtimeProcessing)) {
2552 0 : runnable->Run();
2553 0 : return;
2554 : }
2555 :
2556 0 : class Message : public ControlMessage {
2557 : public:
2558 0 : Message(MediaStream* aStream, already_AddRefed<nsIRunnable> aRunnable)
2559 0 : : ControlMessage(aStream)
2560 0 : , mRunnable(aRunnable)
2561 0 : {}
2562 0 : void Run() override
2563 : {
2564 0 : mStream->Graph()->DispatchToMainThreadAfterStreamStateUpdate(
2565 0 : mRunnable.forget());
2566 0 : }
2567 0 : void RunDuringShutdown() override
2568 : {
2569 : // Don't run mRunnable now as it may call AppendMessage() which would
2570 : // assume that there are no remaining controlMessagesToRunDuringShutdown.
2571 0 : MOZ_ASSERT(NS_IsMainThread());
2572 0 : mStream->GraphImpl()->Dispatch(mRunnable.forget());
2573 0 : }
2574 : private:
2575 : nsCOMPtr<nsIRunnable> mRunnable;
2576 : };
2577 :
2578 0 : graph->AppendMessage(MakeUnique<Message>(this, runnable.forget()));
2579 : }
2580 :
2581 : void
2582 0 : MediaStream::SetTrackEnabledImpl(TrackID aTrackID, DisabledTrackMode aMode)
2583 : {
2584 0 : if (aMode == DisabledTrackMode::ENABLED) {
2585 0 : for (int32_t i = mDisabledTracks.Length() - 1; i >= 0; --i) {
2586 0 : if (aTrackID == mDisabledTracks[i].mTrackID) {
2587 0 : mDisabledTracks.RemoveElementAt(i);
2588 0 : return;
2589 : }
2590 : }
2591 : } else {
2592 0 : for (const DisabledTrack& t : mDisabledTracks) {
2593 0 : if (aTrackID == t.mTrackID) {
2594 0 : NS_ERROR("Changing disabled track mode for a track is not allowed");
2595 0 : return;
2596 : }
2597 : }
2598 0 : mDisabledTracks.AppendElement(Move(DisabledTrack(aTrackID, aMode)));
2599 : }
2600 : }
2601 :
2602 : DisabledTrackMode
2603 0 : MediaStream::GetDisabledTrackMode(TrackID aTrackID)
2604 : {
2605 0 : for (const DisabledTrack& t : mDisabledTracks) {
2606 0 : if (t.mTrackID == aTrackID) {
2607 0 : return t.mMode;
2608 : }
2609 : }
2610 0 : return DisabledTrackMode::ENABLED;
2611 : }
2612 :
2613 : void
2614 0 : MediaStream::SetTrackEnabled(TrackID aTrackID, DisabledTrackMode aMode)
2615 : {
2616 0 : class Message : public ControlMessage {
2617 : public:
2618 0 : Message(MediaStream* aStream, TrackID aTrackID, DisabledTrackMode aMode) :
2619 : ControlMessage(aStream),
2620 : mTrackID(aTrackID),
2621 0 : mMode(aMode) {}
2622 0 : void Run() override
2623 : {
2624 0 : mStream->SetTrackEnabledImpl(mTrackID, mMode);
2625 0 : }
2626 : TrackID mTrackID;
2627 : DisabledTrackMode mMode;
2628 : };
2629 0 : GraphImpl()->AppendMessage(MakeUnique<Message>(this, aTrackID, aMode));
2630 0 : }
2631 :
2632 : void
2633 0 : MediaStream::ApplyTrackDisabling(TrackID aTrackID, MediaSegment* aSegment, MediaSegment* aRawSegment)
2634 : {
2635 0 : DisabledTrackMode mode = GetDisabledTrackMode(aTrackID);
2636 0 : if (mode == DisabledTrackMode::ENABLED) {
2637 0 : return;
2638 : }
2639 0 : if (mode == DisabledTrackMode::SILENCE_BLACK) {
2640 0 : aSegment->ReplaceWithDisabled();
2641 0 : if (aRawSegment) {
2642 0 : aRawSegment->ReplaceWithDisabled();
2643 : }
2644 0 : } else if (mode == DisabledTrackMode::SILENCE_FREEZE) {
2645 0 : aSegment->ReplaceWithNull();
2646 0 : if (aRawSegment) {
2647 0 : aRawSegment->ReplaceWithNull();
2648 : }
2649 : } else {
2650 0 : MOZ_CRASH("Unsupported mode");
2651 : }
2652 : }
2653 :
2654 : void
2655 0 : MediaStream::AddMainThreadListener(MainThreadMediaStreamListener* aListener)
2656 : {
2657 0 : MOZ_ASSERT(NS_IsMainThread());
2658 0 : MOZ_ASSERT(aListener);
2659 0 : MOZ_ASSERT(!mMainThreadListeners.Contains(aListener));
2660 :
2661 0 : mMainThreadListeners.AppendElement(aListener);
2662 :
2663 : // If it is not yet time to send the notification, then finish here.
2664 0 : if (!mFinishedNotificationSent) {
2665 0 : return;
2666 : }
2667 :
2668 : class NotifyRunnable final : public Runnable
2669 : {
2670 : public:
2671 0 : explicit NotifyRunnable(MediaStream* aStream)
2672 0 : : Runnable("MediaStream::NotifyRunnable")
2673 0 : , mStream(aStream)
2674 0 : {}
2675 :
2676 0 : NS_IMETHOD Run() override
2677 : {
2678 0 : MOZ_ASSERT(NS_IsMainThread());
2679 0 : mStream->NotifyMainThreadListeners();
2680 0 : return NS_OK;
2681 : }
2682 :
2683 : private:
2684 0 : ~NotifyRunnable() {}
2685 :
2686 : RefPtr<MediaStream> mStream;
2687 : };
2688 :
2689 0 : nsCOMPtr<nsIRunnable> runnable = new NotifyRunnable(this);
2690 0 : GraphImpl()->Dispatch(runnable.forget());
2691 : }
2692 :
2693 0 : SourceMediaStream::SourceMediaStream()
2694 : : MediaStream()
2695 : , mMutex("mozilla::media::SourceMediaStream")
2696 : , mUpdateKnownTracksTime(0)
2697 : , mPullEnabled(false)
2698 : , mUpdateFinished(false)
2699 0 : , mNeedsMixing(false)
2700 : {
2701 0 : }
2702 :
2703 : nsresult
2704 0 : SourceMediaStream::OpenAudioInput(int aID,
2705 : AudioDataListener *aListener)
2706 : {
2707 0 : if (GraphImpl()) {
2708 0 : mInputListener = aListener;
2709 0 : return GraphImpl()->OpenAudioInput(aID, aListener);
2710 : }
2711 0 : return NS_ERROR_FAILURE;
2712 : }
2713 :
2714 : void
2715 0 : SourceMediaStream::CloseAudioInput()
2716 : {
2717 : // Destroy() may have run already and cleared this
2718 0 : if (GraphImpl() && mInputListener) {
2719 0 : GraphImpl()->CloseAudioInput(mInputListener);
2720 : }
2721 0 : mInputListener = nullptr;
2722 0 : }
2723 :
2724 : void
2725 0 : SourceMediaStream::DestroyImpl()
2726 : {
2727 0 : CloseAudioInput();
2728 :
2729 0 : GraphImpl()->AssertOnGraphThreadOrNotRunning();
2730 0 : for (int32_t i = mConsumers.Length() - 1; i >= 0; --i) {
2731 : // Disconnect before we come under mMutex's lock since it can call back
2732 : // through RemoveDirectTrackListenerImpl() and deadlock.
2733 0 : mConsumers[i]->Disconnect();
2734 : }
2735 :
2736 : // Hold mMutex while mGraph is reset so that other threads holding mMutex
2737 : // can null-check know that the graph will not destroyed.
2738 0 : MutexAutoLock lock(mMutex);
2739 0 : MediaStream::DestroyImpl();
2740 0 : }
2741 :
2742 : void
2743 0 : SourceMediaStream::SetPullEnabled(bool aEnabled)
2744 : {
2745 0 : MutexAutoLock lock(mMutex);
2746 0 : mPullEnabled = aEnabled;
2747 0 : if (mPullEnabled && GraphImpl()) {
2748 0 : GraphImpl()->EnsureNextIteration();
2749 : }
2750 0 : }
2751 :
2752 : void
2753 0 : SourceMediaStream::AddTrackInternal(TrackID aID, TrackRate aRate, StreamTime aStart,
2754 : MediaSegment* aSegment, uint32_t aFlags)
2755 : {
2756 0 : MutexAutoLock lock(mMutex);
2757 0 : nsTArray<TrackData> *track_data = (aFlags & ADDTRACK_QUEUED) ?
2758 0 : &mPendingTracks : &mUpdateTracks;
2759 0 : TrackData* data = track_data->AppendElement();
2760 0 : LOG(LogLevel::Debug,
2761 : ("AddTrackInternal: %lu/%lu",
2762 : (long)mPendingTracks.Length(),
2763 : (long)mUpdateTracks.Length()));
2764 0 : data->mID = aID;
2765 0 : data->mInputRate = aRate;
2766 0 : data->mResamplerChannelCount = 0;
2767 0 : data->mStart = aStart;
2768 0 : data->mEndOfFlushedData = aStart;
2769 0 : data->mCommands = TRACK_CREATE;
2770 0 : data->mData = aSegment;
2771 0 : ResampleAudioToGraphSampleRate(data, aSegment);
2772 0 : if (!(aFlags & ADDTRACK_QUEUED) && GraphImpl()) {
2773 0 : GraphImpl()->EnsureNextIteration();
2774 : }
2775 0 : }
2776 :
2777 : void
2778 0 : SourceMediaStream::AddAudioTrack(TrackID aID, TrackRate aRate, StreamTime aStart,
2779 : AudioSegment* aSegment, uint32_t aFlags)
2780 : {
2781 0 : AddTrackInternal(aID, aRate, aStart, aSegment, aFlags);
2782 0 : }
2783 :
2784 : void
2785 0 : SourceMediaStream::FinishAddTracks()
2786 : {
2787 0 : MutexAutoLock lock(mMutex);
2788 0 : mUpdateTracks.AppendElements(Move(mPendingTracks));
2789 0 : LOG(LogLevel::Debug,
2790 : ("FinishAddTracks: %lu/%lu",
2791 : (long)mPendingTracks.Length(),
2792 : (long)mUpdateTracks.Length()));
2793 0 : if (GraphImpl()) {
2794 0 : GraphImpl()->EnsureNextIteration();
2795 : }
2796 0 : }
2797 :
2798 : void
2799 0 : SourceMediaStream::ResampleAudioToGraphSampleRate(TrackData* aTrackData, MediaSegment* aSegment)
2800 : {
2801 0 : if (aSegment->GetType() != MediaSegment::AUDIO ||
2802 0 : aTrackData->mInputRate == GraphImpl()->GraphRate()) {
2803 0 : return;
2804 : }
2805 0 : AudioSegment* segment = static_cast<AudioSegment*>(aSegment);
2806 0 : int channels = segment->ChannelCount();
2807 :
2808 : // If this segment is just silence, we delay instanciating the resampler. We
2809 : // also need to recreate the resampler if the channel count changes.
2810 0 : if (channels && aTrackData->mResamplerChannelCount != channels) {
2811 0 : SpeexResamplerState* state = speex_resampler_init(channels,
2812 0 : aTrackData->mInputRate,
2813 0 : GraphImpl()->GraphRate(),
2814 : SPEEX_RESAMPLER_QUALITY_MIN,
2815 0 : nullptr);
2816 0 : if (!state) {
2817 0 : return;
2818 : }
2819 0 : aTrackData->mResampler.own(state);
2820 0 : aTrackData->mResamplerChannelCount = channels;
2821 : }
2822 0 : segment->ResampleChunks(aTrackData->mResampler, aTrackData->mInputRate, GraphImpl()->GraphRate());
2823 : }
2824 :
2825 : void
2826 0 : SourceMediaStream::AdvanceTimeVaryingValuesToCurrentTime(GraphTime aCurrentTime,
2827 : GraphTime aBlockedTime)
2828 : {
2829 0 : MutexAutoLock lock(mMutex);
2830 0 : mTracksStartTime += aBlockedTime;
2831 0 : mStreamTracksStartTimeStamp += TimeDuration::FromSeconds(GraphImpl()->MediaTimeToSeconds(aBlockedTime));
2832 0 : mTracks.ForgetUpTo(aCurrentTime - mTracksStartTime);
2833 0 : }
2834 :
2835 : bool
2836 0 : SourceMediaStream::AppendToTrack(TrackID aID, MediaSegment* aSegment, MediaSegment *aRawSegment)
2837 : {
2838 0 : MutexAutoLock lock(mMutex);
2839 : // ::EndAllTrackAndFinished() can end these before the sources notice
2840 0 : bool appended = false;
2841 0 : auto graph = GraphImpl();
2842 0 : if (!mFinished && graph) {
2843 0 : TrackData *track = FindDataForTrack(aID);
2844 0 : if (track) {
2845 : // Data goes into mData, and on the next iteration of the MSG moves
2846 : // into the track's segment after NotifyQueuedTrackChanges(). This adds
2847 : // 0-10ms of delay before data gets to direct listeners.
2848 : // Indirect listeners (via subsequent TrackUnion nodes) are synced to
2849 : // playout time, and so can be delayed by buffering.
2850 :
2851 : // Apply track disabling before notifying any consumers directly
2852 : // or inserting into the graph
2853 0 : ApplyTrackDisabling(aID, aSegment, aRawSegment);
2854 :
2855 0 : ResampleAudioToGraphSampleRate(track, aSegment);
2856 :
2857 : // Must notify first, since AppendFrom() will empty out aSegment
2858 0 : NotifyDirectConsumers(track, aRawSegment ? aRawSegment : aSegment);
2859 0 : track->mData->AppendFrom(aSegment); // note: aSegment is now dead
2860 0 : appended = true;
2861 0 : GraphImpl()->EnsureNextIteration();
2862 : } else {
2863 0 : aSegment->Clear();
2864 : }
2865 : }
2866 0 : return appended;
2867 : }
2868 :
2869 : void
2870 0 : SourceMediaStream::NotifyDirectConsumers(TrackData *aTrack,
2871 : MediaSegment *aSegment)
2872 : {
2873 0 : mMutex.AssertCurrentThreadOwns();
2874 0 : MOZ_ASSERT(aTrack);
2875 :
2876 0 : for (uint32_t j = 0; j < mDirectListeners.Length(); ++j) {
2877 0 : DirectMediaStreamListener* l = mDirectListeners[j];
2878 0 : StreamTime offset = 0; // FIX! need a separate StreamTime.... or the end of the internal buffer
2879 0 : l->NotifyRealtimeData(static_cast<MediaStreamGraph*>(GraphImpl()), aTrack->mID,
2880 0 : offset, aTrack->mCommands, *aSegment);
2881 : }
2882 :
2883 0 : for (const TrackBound<DirectMediaStreamTrackListener>& source
2884 0 : : mDirectTrackListeners) {
2885 0 : if (aTrack->mID != source.mTrackID) {
2886 0 : continue;
2887 : }
2888 0 : StreamTime offset = 0; // FIX! need a separate StreamTime.... or the end of the internal buffer
2889 0 : source.mListener->NotifyRealtimeTrackDataAndApplyTrackDisabling(Graph(), offset, *aSegment);
2890 : }
2891 0 : }
2892 :
2893 : // These handle notifying all the listeners of an event
2894 : void
2895 0 : SourceMediaStream::NotifyListenersEventImpl(MediaStreamGraphEvent aEvent)
2896 : {
2897 0 : for (uint32_t j = 0; j < mListeners.Length(); ++j) {
2898 0 : MediaStreamListener* l = mListeners[j];
2899 0 : l->NotifyEvent(GraphImpl(), aEvent);
2900 : }
2901 0 : }
2902 :
2903 : void
2904 0 : SourceMediaStream::NotifyListenersEvent(MediaStreamGraphEvent aNewEvent)
2905 : {
2906 0 : class Message : public ControlMessage {
2907 : public:
2908 0 : Message(SourceMediaStream* aStream, MediaStreamGraphEvent aEvent) :
2909 0 : ControlMessage(aStream), mEvent(aEvent) {}
2910 0 : void Run() override
2911 : {
2912 0 : mStream->AsSourceStream()->NotifyListenersEventImpl(mEvent);
2913 0 : }
2914 : MediaStreamGraphEvent mEvent;
2915 : };
2916 0 : GraphImpl()->AppendMessage(MakeUnique<Message>(this, aNewEvent));
2917 0 : }
2918 :
2919 : void
2920 0 : SourceMediaStream::AddDirectListener(DirectMediaStreamListener* aListener)
2921 : {
2922 : bool wasEmpty;
2923 : {
2924 0 : MutexAutoLock lock(mMutex);
2925 0 : wasEmpty = mDirectListeners.IsEmpty();
2926 0 : mDirectListeners.AppendElement(aListener);
2927 : }
2928 :
2929 0 : if (wasEmpty) {
2930 : // Async
2931 0 : NotifyListenersEvent(MediaStreamGraphEvent::EVENT_HAS_DIRECT_LISTENERS);
2932 : }
2933 0 : }
2934 :
2935 : void
2936 0 : SourceMediaStream::RemoveDirectListener(DirectMediaStreamListener* aListener)
2937 : {
2938 : bool isEmpty;
2939 : {
2940 0 : MutexAutoLock lock(mMutex);
2941 0 : mDirectListeners.RemoveElement(aListener);
2942 0 : isEmpty = mDirectListeners.IsEmpty();
2943 : }
2944 :
2945 0 : if (isEmpty) {
2946 : // Async
2947 0 : NotifyListenersEvent(MediaStreamGraphEvent::EVENT_HAS_NO_DIRECT_LISTENERS);
2948 : }
2949 0 : }
2950 :
2951 : void
2952 0 : SourceMediaStream::AddDirectTrackListenerImpl(already_AddRefed<DirectMediaStreamTrackListener> aListener,
2953 : TrackID aTrackID)
2954 : {
2955 0 : MOZ_ASSERT(IsTrackIDExplicit(aTrackID));
2956 0 : TrackData* updateData = nullptr;
2957 0 : StreamTracks::Track* track = nullptr;
2958 0 : bool isAudio = false;
2959 0 : bool isVideo = false;
2960 0 : RefPtr<DirectMediaStreamTrackListener> listener = aListener;
2961 0 : LOG(LogLevel::Debug,
2962 : ("Adding direct track listener %p bound to track %d to source stream %p",
2963 : listener.get(),
2964 : aTrackID,
2965 : this));
2966 :
2967 : {
2968 0 : MutexAutoLock lock(mMutex);
2969 0 : updateData = FindDataForTrack(aTrackID);
2970 0 : track = FindTrack(aTrackID);
2971 0 : if (track) {
2972 0 : isAudio = track->GetType() == MediaSegment::AUDIO;
2973 0 : isVideo = track->GetType() == MediaSegment::VIDEO;
2974 : }
2975 :
2976 0 : if (track && isVideo && listener->AsMediaStreamVideoSink()) {
2977 : // Re-send missed VideoSegment to new added MediaStreamVideoSink.
2978 0 : VideoSegment* trackSegment = static_cast<VideoSegment*>(track->GetSegment());
2979 0 : VideoSegment videoSegment;
2980 0 : if (mTracks.GetForgottenDuration() < trackSegment->GetDuration()) {
2981 0 : videoSegment.AppendSlice(*trackSegment,
2982 : mTracks.GetForgottenDuration(),
2983 0 : trackSegment->GetDuration());
2984 : }
2985 0 : if (updateData) {
2986 0 : videoSegment.AppendSlice(*updateData->mData, 0, updateData->mData->GetDuration());
2987 : }
2988 0 : listener->NotifyRealtimeTrackData(Graph(), 0, videoSegment);
2989 : }
2990 :
2991 0 : if (track && (isAudio || isVideo)) {
2992 0 : for (auto entry : mDirectTrackListeners) {
2993 0 : if (entry.mListener == listener &&
2994 0 : (entry.mTrackID == TRACK_ANY || entry.mTrackID == aTrackID)) {
2995 0 : listener->NotifyDirectListenerInstalled(
2996 0 : DirectMediaStreamTrackListener::InstallationResult::ALREADY_EXISTS);
2997 0 : return;
2998 : }
2999 : }
3000 :
3001 : TrackBound<DirectMediaStreamTrackListener>* sourceListener =
3002 0 : mDirectTrackListeners.AppendElement();
3003 0 : sourceListener->mListener = listener;
3004 0 : sourceListener->mTrackID = aTrackID;
3005 : }
3006 : }
3007 0 : if (!track) {
3008 0 : LOG(LogLevel::Warning,
3009 : ("Couldn't find source track for direct track listener %p",
3010 : listener.get()));
3011 0 : listener->NotifyDirectListenerInstalled(
3012 0 : DirectMediaStreamTrackListener::InstallationResult::TRACK_NOT_FOUND_AT_SOURCE);
3013 0 : return;
3014 : }
3015 0 : if (!isAudio && !isVideo) {
3016 0 : LOG(
3017 : LogLevel::Warning,
3018 : ("Source track for direct track listener %p is unknown", listener.get()));
3019 : // It is not a video or audio track.
3020 0 : MOZ_ASSERT(false);
3021 : return;
3022 : }
3023 0 : LOG(
3024 : LogLevel::Debug,
3025 : ("Added direct track listener %p. ended=%d", listener.get(), !updateData));
3026 0 : listener->NotifyDirectListenerInstalled(
3027 0 : DirectMediaStreamTrackListener::InstallationResult::SUCCESS);
3028 0 : if (!updateData) {
3029 : // The track exists but the mUpdateTracks entry was removed.
3030 : // This means that the track has ended.
3031 0 : listener->NotifyEnded();
3032 : }
3033 : }
3034 :
3035 : void
3036 0 : SourceMediaStream::RemoveDirectTrackListenerImpl(DirectMediaStreamTrackListener* aListener,
3037 : TrackID aTrackID)
3038 : {
3039 0 : MutexAutoLock lock(mMutex);
3040 0 : for (int32_t i = mDirectTrackListeners.Length() - 1; i >= 0; --i) {
3041 : const TrackBound<DirectMediaStreamTrackListener>& source =
3042 0 : mDirectTrackListeners[i];
3043 0 : if (source.mListener == aListener && source.mTrackID == aTrackID) {
3044 0 : aListener->NotifyDirectListenerUninstalled();
3045 0 : mDirectTrackListeners.RemoveElementAt(i);
3046 : }
3047 : }
3048 0 : }
3049 :
3050 : StreamTime
3051 0 : SourceMediaStream::GetEndOfAppendedData(TrackID aID)
3052 : {
3053 0 : MutexAutoLock lock(mMutex);
3054 0 : TrackData *track = FindDataForTrack(aID);
3055 0 : if (track) {
3056 0 : return track->mEndOfFlushedData + track->mData->GetDuration();
3057 : }
3058 0 : NS_ERROR("Track not found");
3059 0 : return 0;
3060 : }
3061 :
3062 : void
3063 0 : SourceMediaStream::EndTrack(TrackID aID)
3064 : {
3065 0 : MutexAutoLock lock(mMutex);
3066 0 : TrackData *track = FindDataForTrack(aID);
3067 0 : if (track) {
3068 0 : track->mCommands |= TrackEventCommand::TRACK_EVENT_ENDED;
3069 : }
3070 0 : if (auto graph = GraphImpl()) {
3071 0 : graph->EnsureNextIteration();
3072 : }
3073 0 : }
3074 :
3075 : void
3076 0 : SourceMediaStream::AdvanceKnownTracksTime(StreamTime aKnownTime)
3077 : {
3078 0 : MutexAutoLock lock(mMutex);
3079 0 : MOZ_ASSERT(aKnownTime >= mUpdateKnownTracksTime);
3080 0 : mUpdateKnownTracksTime = aKnownTime;
3081 0 : if (auto graph = GraphImpl()) {
3082 0 : graph->EnsureNextIteration();
3083 : }
3084 0 : }
3085 :
3086 : void
3087 0 : SourceMediaStream::FinishWithLockHeld()
3088 : {
3089 0 : mMutex.AssertCurrentThreadOwns();
3090 0 : mUpdateFinished = true;
3091 0 : if (auto graph = GraphImpl()) {
3092 0 : graph->EnsureNextIteration();
3093 : }
3094 0 : }
3095 :
3096 : void
3097 0 : SourceMediaStream::SetTrackEnabledImpl(TrackID aTrackID, DisabledTrackMode aMode)
3098 : {
3099 : {
3100 0 : MutexAutoLock lock(mMutex);
3101 0 : for (TrackBound<DirectMediaStreamTrackListener>& l: mDirectTrackListeners) {
3102 0 : if (l.mTrackID != aTrackID) {
3103 0 : continue;
3104 : }
3105 0 : DisabledTrackMode oldMode = GetDisabledTrackMode(aTrackID);
3106 0 : bool oldEnabled = oldMode == DisabledTrackMode::ENABLED;
3107 0 : if (!oldEnabled && aMode == DisabledTrackMode::ENABLED) {
3108 0 : LOG(LogLevel::Debug,
3109 : ("SourceMediaStream %p track %d setting "
3110 : "direct listener enabled",
3111 : this,
3112 : aTrackID));
3113 0 : l.mListener->DecreaseDisabled(oldMode);
3114 0 : } else if (oldEnabled && aMode != DisabledTrackMode::ENABLED) {
3115 0 : LOG(LogLevel::Debug,
3116 : ("SourceMediaStream %p track %d setting "
3117 : "direct listener disabled",
3118 : this,
3119 : aTrackID));
3120 0 : l.mListener->IncreaseDisabled(aMode);
3121 : }
3122 : }
3123 : }
3124 0 : MediaStream::SetTrackEnabledImpl(aTrackID, aMode);
3125 0 : }
3126 :
3127 : void
3128 0 : SourceMediaStream::EndAllTrackAndFinish()
3129 : {
3130 0 : MutexAutoLock lock(mMutex);
3131 0 : for (uint32_t i = 0; i < mUpdateTracks.Length(); ++i) {
3132 0 : SourceMediaStream::TrackData* data = &mUpdateTracks[i];
3133 0 : data->mCommands |= TrackEventCommand::TRACK_EVENT_ENDED;
3134 : }
3135 0 : mPendingTracks.Clear();
3136 0 : FinishWithLockHeld();
3137 : // we will call NotifyEvent() to let GetUserMedia know
3138 0 : }
3139 :
3140 0 : SourceMediaStream::~SourceMediaStream()
3141 : {
3142 0 : }
3143 :
3144 : void
3145 0 : SourceMediaStream::RegisterForAudioMixing()
3146 : {
3147 0 : MutexAutoLock lock(mMutex);
3148 0 : mNeedsMixing = true;
3149 0 : }
3150 :
3151 : bool
3152 0 : SourceMediaStream::NeedsMixing()
3153 : {
3154 0 : MutexAutoLock lock(mMutex);
3155 0 : return mNeedsMixing;
3156 : }
3157 :
3158 : bool
3159 0 : SourceMediaStream::HasPendingAudioTrack()
3160 : {
3161 0 : MutexAutoLock lock(mMutex);
3162 0 : bool audioTrackPresent = false;
3163 :
3164 0 : for (auto& data : mPendingTracks) {
3165 0 : if (data.mData->GetType() == MediaSegment::AUDIO) {
3166 0 : audioTrackPresent = true;
3167 0 : break;
3168 : }
3169 : }
3170 :
3171 0 : return audioTrackPresent;
3172 : }
3173 :
3174 : bool
3175 0 : SourceMediaStream::OpenNewAudioCallbackDriver(AudioDataListener * aListener)
3176 : {
3177 0 : MOZ_ASSERT(GraphImpl()->mLifecycleState ==
3178 : MediaStreamGraphImpl::LifecycleState::LIFECYCLE_RUNNING);
3179 0 : AudioCallbackDriver* nextDriver = new AudioCallbackDriver(GraphImpl());
3180 0 : nextDriver->SetInputListener(aListener);
3181 : {
3182 0 : MonitorAutoLock lock(GraphImpl()->GetMonitor());
3183 0 : GraphImpl()->CurrentDriver()->SwitchAtNextIteration(nextDriver);
3184 : }
3185 :
3186 0 : return true;
3187 : }
3188 :
3189 :
3190 : void
3191 0 : MediaInputPort::Init()
3192 : {
3193 0 : LOG(LogLevel::Debug,
3194 : ("Adding MediaInputPort %p (from %p to %p) to the graph",
3195 : this,
3196 : mSource,
3197 : mDest));
3198 0 : mSource->AddConsumer(this);
3199 0 : mDest->AddInput(this);
3200 : // mPortCount decremented via MediaInputPort::Destroy's message
3201 0 : ++mDest->GraphImpl()->mPortCount;
3202 0 : }
3203 :
3204 : void
3205 0 : MediaInputPort::Disconnect()
3206 : {
3207 0 : GraphImpl()->AssertOnGraphThreadOrNotRunning();
3208 0 : NS_ASSERTION(!mSource == !mDest,
3209 : "mSource must either both be null or both non-null");
3210 0 : if (!mSource)
3211 0 : return;
3212 :
3213 0 : mSource->RemoveConsumer(this);
3214 0 : mDest->RemoveInput(this);
3215 0 : mSource = nullptr;
3216 0 : mDest = nullptr;
3217 :
3218 0 : GraphImpl()->SetStreamOrderDirty();
3219 : }
3220 :
3221 : MediaInputPort::InputInterval
3222 0 : MediaInputPort::GetNextInputInterval(GraphTime aTime)
3223 : {
3224 0 : InputInterval result = { GRAPH_TIME_MAX, GRAPH_TIME_MAX, false };
3225 0 : if (aTime >= mDest->mStartBlocking) {
3226 0 : return result;
3227 : }
3228 0 : result.mStart = aTime;
3229 0 : result.mEnd = mDest->mStartBlocking;
3230 0 : result.mInputIsBlocked = aTime >= mSource->mStartBlocking;
3231 0 : if (!result.mInputIsBlocked) {
3232 0 : result.mEnd = std::min(result.mEnd, mSource->mStartBlocking);
3233 : }
3234 0 : return result;
3235 : }
3236 :
3237 : void
3238 0 : MediaInputPort::Suspended()
3239 : {
3240 0 : mDest->InputSuspended(this);
3241 0 : }
3242 :
3243 : void
3244 0 : MediaInputPort::Resumed()
3245 : {
3246 0 : mDest->InputResumed(this);
3247 0 : }
3248 :
3249 : void
3250 0 : MediaInputPort::Destroy()
3251 : {
3252 0 : class Message : public ControlMessage {
3253 : public:
3254 0 : explicit Message(MediaInputPort* aPort)
3255 0 : : ControlMessage(nullptr), mPort(aPort) {}
3256 0 : void Run() override
3257 : {
3258 0 : mPort->Disconnect();
3259 0 : --mPort->GraphImpl()->mPortCount;
3260 0 : mPort->SetGraphImpl(nullptr);
3261 0 : NS_RELEASE(mPort);
3262 0 : }
3263 0 : void RunDuringShutdown() override
3264 : {
3265 0 : Run();
3266 0 : }
3267 : MediaInputPort* mPort;
3268 : };
3269 0 : GraphImpl()->AppendMessage(MakeUnique<Message>(this));
3270 0 : }
3271 :
3272 : MediaStreamGraphImpl*
3273 0 : MediaInputPort::GraphImpl()
3274 : {
3275 0 : return mGraph;
3276 : }
3277 :
3278 : MediaStreamGraph*
3279 0 : MediaInputPort::Graph()
3280 : {
3281 0 : return mGraph;
3282 : }
3283 :
3284 : void
3285 0 : MediaInputPort::SetGraphImpl(MediaStreamGraphImpl* aGraph)
3286 : {
3287 0 : MOZ_ASSERT(!mGraph || !aGraph, "Should only be set once");
3288 0 : mGraph = aGraph;
3289 0 : }
3290 :
3291 : void
3292 0 : MediaInputPort::BlockSourceTrackIdImpl(TrackID aTrackId, BlockingMode aBlockingMode)
3293 : {
3294 0 : mBlockedTracks.AppendElement(Pair<TrackID, BlockingMode>(aTrackId, aBlockingMode));
3295 0 : }
3296 :
3297 : already_AddRefed<Pledge<bool>>
3298 0 : MediaInputPort::BlockSourceTrackId(TrackID aTrackId, BlockingMode aBlockingMode)
3299 : {
3300 0 : class Message : public ControlMessage {
3301 : public:
3302 0 : Message(MediaInputPort* aPort,
3303 : TrackID aTrackId,
3304 : BlockingMode aBlockingMode,
3305 : already_AddRefed<nsIRunnable> aRunnable)
3306 0 : : ControlMessage(aPort->GetDestination())
3307 : , mPort(aPort)
3308 : , mTrackId(aTrackId)
3309 : , mBlockingMode(aBlockingMode)
3310 0 : , mRunnable(aRunnable)
3311 : {
3312 0 : }
3313 0 : void Run() override
3314 : {
3315 0 : mPort->BlockSourceTrackIdImpl(mTrackId, mBlockingMode);
3316 0 : if (mRunnable) {
3317 0 : mStream->Graph()->DispatchToMainThreadAfterStreamStateUpdate(
3318 0 : mRunnable.forget());
3319 : }
3320 0 : }
3321 0 : void RunDuringShutdown() override
3322 : {
3323 0 : Run();
3324 0 : }
3325 : RefPtr<MediaInputPort> mPort;
3326 : TrackID mTrackId;
3327 : BlockingMode mBlockingMode;
3328 : nsCOMPtr<nsIRunnable> mRunnable;
3329 : };
3330 :
3331 0 : MOZ_ASSERT(IsTrackIDExplicit(aTrackId),
3332 : "Only explicit TrackID is allowed");
3333 :
3334 0 : auto pledge = MakeRefPtr<Pledge<bool>>();
3335 0 : nsCOMPtr<nsIRunnable> runnable = NewRunnableFrom([pledge]() {
3336 0 : MOZ_ASSERT(NS_IsMainThread());
3337 0 : pledge->Resolve(true);
3338 0 : return NS_OK;
3339 0 : });
3340 0 : GraphImpl()->AppendMessage(
3341 0 : MakeUnique<Message>(this, aTrackId, aBlockingMode, runnable.forget()));
3342 0 : return pledge.forget();
3343 : }
3344 :
3345 : already_AddRefed<MediaInputPort>
3346 0 : ProcessedMediaStream::AllocateInputPort(MediaStream* aStream, TrackID aTrackID,
3347 : TrackID aDestTrackID,
3348 : uint16_t aInputNumber, uint16_t aOutputNumber,
3349 : nsTArray<TrackID>* aBlockedTracks)
3350 : {
3351 : // This method creates two references to the MediaInputPort: one for
3352 : // the main thread, and one for the MediaStreamGraph.
3353 0 : class Message : public ControlMessage {
3354 : public:
3355 0 : explicit Message(MediaInputPort* aPort)
3356 0 : : ControlMessage(aPort->GetDestination()),
3357 0 : mPort(aPort) {}
3358 0 : void Run() override
3359 : {
3360 0 : mPort->Init();
3361 : // The graph holds its reference implicitly
3362 0 : mPort->GraphImpl()->SetStreamOrderDirty();
3363 0 : Unused << mPort.forget();
3364 0 : }
3365 0 : void RunDuringShutdown() override
3366 : {
3367 0 : Run();
3368 0 : }
3369 : RefPtr<MediaInputPort> mPort;
3370 : };
3371 :
3372 0 : MOZ_ASSERT(aStream->GraphImpl() == GraphImpl());
3373 0 : MOZ_ASSERT(aTrackID == TRACK_ANY || IsTrackIDExplicit(aTrackID),
3374 : "Only TRACK_ANY and explicit ID are allowed for source track");
3375 0 : MOZ_ASSERT(aDestTrackID == TRACK_ANY || IsTrackIDExplicit(aDestTrackID),
3376 : "Only TRACK_ANY and explicit ID are allowed for destination track");
3377 0 : MOZ_ASSERT(aTrackID != TRACK_ANY || aDestTrackID == TRACK_ANY,
3378 : "Generic MediaInputPort cannot produce a single destination track");
3379 : RefPtr<MediaInputPort> port = new MediaInputPort(
3380 0 : aStream, aTrackID, this, aDestTrackID, aInputNumber, aOutputNumber);
3381 0 : if (aBlockedTracks) {
3382 0 : for (TrackID trackID : *aBlockedTracks) {
3383 0 : port->BlockSourceTrackIdImpl(trackID, BlockingMode::CREATION);
3384 : }
3385 : }
3386 0 : port->SetGraphImpl(GraphImpl());
3387 0 : GraphImpl()->AppendMessage(MakeUnique<Message>(port));
3388 0 : return port.forget();
3389 : }
3390 :
3391 : void
3392 0 : ProcessedMediaStream::Finish()
3393 : {
3394 0 : class Message : public ControlMessage {
3395 : public:
3396 0 : explicit Message(ProcessedMediaStream* aStream)
3397 0 : : ControlMessage(aStream) {}
3398 0 : void Run() override
3399 : {
3400 0 : mStream->GraphImpl()->FinishStream(mStream);
3401 0 : }
3402 : };
3403 0 : GraphImpl()->AppendMessage(MakeUnique<Message>(this));
3404 0 : }
3405 :
3406 : void
3407 0 : ProcessedMediaStream::SetAutofinish(bool aAutofinish)
3408 : {
3409 0 : class Message : public ControlMessage {
3410 : public:
3411 0 : Message(ProcessedMediaStream* aStream, bool aAutofinish)
3412 0 : : ControlMessage(aStream), mAutofinish(aAutofinish) {}
3413 0 : void Run() override
3414 : {
3415 0 : static_cast<ProcessedMediaStream*>(mStream)->SetAutofinishImpl(mAutofinish);
3416 0 : }
3417 : bool mAutofinish;
3418 : };
3419 0 : GraphImpl()->AppendMessage(MakeUnique<Message>(this, aAutofinish));
3420 0 : }
3421 :
3422 : void
3423 0 : ProcessedMediaStream::DestroyImpl()
3424 : {
3425 0 : for (int32_t i = mInputs.Length() - 1; i >= 0; --i) {
3426 0 : mInputs[i]->Disconnect();
3427 : }
3428 :
3429 0 : for (int32_t i = mSuspendedInputs.Length() - 1; i >= 0; --i) {
3430 0 : mSuspendedInputs[i]->Disconnect();
3431 : }
3432 :
3433 0 : MediaStream::DestroyImpl();
3434 : // The stream order is only important if there are connections, in which
3435 : // case MediaInputPort::Disconnect() called SetStreamOrderDirty().
3436 : // MediaStreamGraphImpl::RemoveStreamGraphThread() will also call
3437 : // SetStreamOrderDirty(), for other reasons.
3438 0 : }
3439 :
3440 0 : MediaStreamGraphImpl::MediaStreamGraphImpl(GraphDriverType aDriverRequested,
3441 : TrackRate aSampleRate,
3442 : dom::AudioChannel aChannel,
3443 0 : AbstractThread* aMainThread)
3444 : : MediaStreamGraph(aSampleRate)
3445 : , mPortCount(0)
3446 : , mInputWanted(false)
3447 : , mInputDeviceID(-1)
3448 : , mOutputWanted(true)
3449 : , mOutputDeviceID(-1)
3450 : , mNeedAnotherIteration(false)
3451 : , mGraphDriverAsleep(false)
3452 : , mMonitor("MediaStreamGraphImpl")
3453 : , mLifecycleState(LIFECYCLE_THREAD_NOT_STARTED)
3454 : , mEndTime(GRAPH_TIME_MAX)
3455 : , mForceShutDown(false)
3456 : , mPostedRunInStableStateEvent(false)
3457 : , mDetectedNotRunning(false)
3458 : , mPostedRunInStableState(false)
3459 0 : , mRealtime(aDriverRequested != OFFLINE_THREAD_DRIVER)
3460 : , mNonRealtimeProcessing(false)
3461 : , mStreamOrderDirty(false)
3462 : , mLatencyLog(AsyncLatencyLogger::Get())
3463 : , mAbstractMainThread(aMainThread)
3464 : #ifdef MOZ_WEBRTC
3465 : , mFarendObserverRef(nullptr)
3466 : #endif
3467 : , mSelfRef(this)
3468 : #ifdef DEBUG
3469 : , mCanRunMessagesSynchronously(false)
3470 : #endif
3471 0 : , mAudioChannel(aChannel)
3472 : {
3473 0 : if (mRealtime) {
3474 0 : if (aDriverRequested == AUDIO_THREAD_DRIVER) {
3475 0 : AudioCallbackDriver* driver = new AudioCallbackDriver(this);
3476 0 : mDriver = driver;
3477 : } else {
3478 0 : mDriver = new SystemClockDriver(this);
3479 : }
3480 : } else {
3481 0 : mDriver = new OfflineClockDriver(this, MEDIA_GRAPH_TARGET_PERIOD_MS);
3482 : }
3483 :
3484 0 : mLastMainThreadUpdate = TimeStamp::Now();
3485 :
3486 0 : RegisterWeakAsyncMemoryReporter(this);
3487 0 : }
3488 :
3489 : AbstractThread*
3490 0 : MediaStreamGraph::AbstractMainThread()
3491 : {
3492 0 : MOZ_ASSERT(static_cast<MediaStreamGraphImpl*>(this)->mAbstractMainThread);
3493 0 : return static_cast<MediaStreamGraphImpl*>(this)->mAbstractMainThread;
3494 : }
3495 :
3496 : void
3497 0 : MediaStreamGraphImpl::Destroy()
3498 : {
3499 : // First unregister from memory reporting.
3500 0 : UnregisterWeakMemoryReporter(this);
3501 :
3502 : // Clear the self reference which will destroy this instance.
3503 0 : mSelfRef = nullptr;
3504 0 : }
3505 :
3506 : static
3507 0 : uint32_t ChannelAndWindowToHash(dom::AudioChannel aChannel,
3508 : nsPIDOMWindowInner* aWindow)
3509 : {
3510 0 : uint32_t hashkey = 0;
3511 :
3512 0 : hashkey = AddToHash(hashkey, static_cast<uint32_t>(aChannel));
3513 0 : hashkey = AddToHash(hashkey, aWindow);
3514 :
3515 0 : return hashkey;
3516 : }
3517 :
3518 : MediaStreamGraph*
3519 0 : MediaStreamGraph::GetInstance(MediaStreamGraph::GraphDriverType aGraphDriverRequested,
3520 : dom::AudioChannel aChannel,
3521 : nsPIDOMWindowInner* aWindow)
3522 : {
3523 0 : NS_ASSERTION(NS_IsMainThread(), "Main thread only");
3524 :
3525 0 : uint32_t channel = static_cast<uint32_t>(aChannel);
3526 0 : MediaStreamGraphImpl* graph = nullptr;
3527 :
3528 : // We hash the AudioChannel and the nsPIDOMWindowInner together to form a key
3529 : // to the gloabl MediaStreamGraph hashtable. Effectively, this means there is
3530 : // a graph per document and AudioChannel.
3531 :
3532 :
3533 0 : uint32_t hashkey = ChannelAndWindowToHash(aChannel, aWindow);
3534 :
3535 0 : if (!gGraphs.Get(hashkey, &graph)) {
3536 0 : if (!gMediaStreamGraphShutdownBlocker) {
3537 :
3538 0 : class Blocker : public media::ShutdownBlocker
3539 : {
3540 : public:
3541 0 : Blocker()
3542 0 : : media::ShutdownBlocker(NS_LITERAL_STRING(
3543 0 : "MediaStreamGraph shutdown: blocking on msg thread"))
3544 0 : {}
3545 :
3546 : NS_IMETHOD
3547 0 : BlockShutdown(nsIAsyncShutdownClient* aProfileBeforeChange) override
3548 : {
3549 : // Distribute the global async shutdown blocker in a ticket. If there
3550 : // are zero graphs then shutdown is unblocked when we go out of scope.
3551 : RefPtr<MediaStreamGraphImpl::ShutdownTicket> ticket =
3552 0 : new MediaStreamGraphImpl::ShutdownTicket(gMediaStreamGraphShutdownBlocker.get());
3553 0 : gMediaStreamGraphShutdownBlocker = nullptr;
3554 :
3555 0 : for (auto iter = gGraphs.Iter(); !iter.Done(); iter.Next()) {
3556 0 : iter.UserData()->ForceShutDown(ticket);
3557 : }
3558 0 : return NS_OK;
3559 : }
3560 : };
3561 :
3562 0 : gMediaStreamGraphShutdownBlocker = new Blocker();
3563 0 : nsCOMPtr<nsIAsyncShutdownClient> barrier = MediaStreamGraphImpl::GetShutdownBarrier();
3564 0 : nsresult rv = barrier->
3565 0 : AddBlocker(gMediaStreamGraphShutdownBlocker,
3566 0 : NS_LITERAL_STRING(__FILE__), __LINE__,
3567 0 : NS_LITERAL_STRING("MediaStreamGraph shutdown"));
3568 0 : MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
3569 : }
3570 :
3571 : AbstractThread* mainThread;
3572 0 : if (aWindow) {
3573 0 : nsCOMPtr<nsIGlobalObject> parentObject = do_QueryInterface(aWindow);
3574 0 : mainThread = parentObject->AbstractMainThreadFor(TaskCategory::Other);
3575 : } else {
3576 : // Uncommon case, only for some old configuration of webspeech.
3577 0 : mainThread = AbstractThread::MainThread();
3578 : }
3579 0 : graph = new MediaStreamGraphImpl(aGraphDriverRequested,
3580 0 : CubebUtils::PreferredSampleRate(),
3581 : aChannel,
3582 0 : mainThread);
3583 :
3584 0 : gGraphs.Put(hashkey, graph);
3585 :
3586 0 : LOG(LogLevel::Debug,
3587 : ("Starting up MediaStreamGraph %p for channel %s and window %p",
3588 : graph, AudioChannelValues::strings[channel].value, aWindow));
3589 : }
3590 :
3591 0 : return graph;
3592 : }
3593 :
3594 : MediaStreamGraph*
3595 0 : MediaStreamGraph::CreateNonRealtimeInstance(TrackRate aSampleRate,
3596 : nsPIDOMWindowInner* aWindow)
3597 : {
3598 0 : NS_ASSERTION(NS_IsMainThread(), "Main thread only");
3599 :
3600 0 : nsCOMPtr<nsIGlobalObject> parentObject = do_QueryInterface(aWindow);
3601 : MediaStreamGraphImpl* graph = new MediaStreamGraphImpl(
3602 : OFFLINE_THREAD_DRIVER,
3603 : aSampleRate,
3604 : AudioChannel::Normal,
3605 0 : parentObject->AbstractMainThreadFor(TaskCategory::Other));
3606 :
3607 0 : LOG(LogLevel::Debug, ("Starting up Offline MediaStreamGraph %p", graph));
3608 :
3609 0 : return graph;
3610 : }
3611 :
3612 : void
3613 0 : MediaStreamGraph::DestroyNonRealtimeInstance(MediaStreamGraph* aGraph)
3614 : {
3615 0 : NS_ASSERTION(NS_IsMainThread(), "Main thread only");
3616 0 : MOZ_ASSERT(aGraph->IsNonRealtime(), "Should not destroy the global graph here");
3617 :
3618 0 : MediaStreamGraphImpl* graph = static_cast<MediaStreamGraphImpl*>(aGraph);
3619 :
3620 0 : if (!graph->mNonRealtimeProcessing) {
3621 : // Start the graph, but don't produce anything
3622 0 : graph->StartNonRealtimeProcessing(0);
3623 : }
3624 0 : graph->ForceShutDown(nullptr);
3625 0 : }
3626 :
3627 0 : NS_IMPL_ISUPPORTS(MediaStreamGraphImpl, nsIMemoryReporter, nsITimerCallback)
3628 :
3629 : NS_IMETHODIMP
3630 0 : MediaStreamGraphImpl::CollectReports(nsIHandleReportCallback* aHandleReport,
3631 : nsISupports* aData, bool aAnonymize)
3632 : {
3633 0 : if (mLifecycleState >= LIFECYCLE_WAITING_FOR_THREAD_SHUTDOWN) {
3634 : // Shutting down, nothing to report.
3635 0 : FinishCollectReports(aHandleReport, aData, nsTArray<AudioNodeSizes>());
3636 0 : return NS_OK;
3637 : }
3638 :
3639 0 : class Message final : public ControlMessage {
3640 : public:
3641 0 : Message(MediaStreamGraphImpl *aGraph,
3642 : nsIHandleReportCallback* aHandleReport,
3643 : nsISupports *aHandlerData)
3644 0 : : ControlMessage(nullptr)
3645 : , mGraph(aGraph)
3646 : , mHandleReport(aHandleReport)
3647 0 : , mHandlerData(aHandlerData) {}
3648 0 : void Run() override
3649 : {
3650 0 : mGraph->CollectSizesForMemoryReport(mHandleReport.forget(),
3651 0 : mHandlerData.forget());
3652 0 : }
3653 0 : void RunDuringShutdown() override
3654 : {
3655 : // Run this message during shutdown too, so that endReports is called.
3656 0 : Run();
3657 0 : }
3658 : MediaStreamGraphImpl *mGraph;
3659 : // nsMemoryReporterManager keeps the callback and data alive only if it
3660 : // does not time out.
3661 : nsCOMPtr<nsIHandleReportCallback> mHandleReport;
3662 : nsCOMPtr<nsISupports> mHandlerData;
3663 : };
3664 :
3665 : // When a non-realtime graph has not started, there is no thread yet, so
3666 : // collect sizes on this thread.
3667 0 : if (!(mRealtime || mNonRealtimeProcessing)) {
3668 0 : CollectSizesForMemoryReport(do_AddRef(aHandleReport), do_AddRef(aData));
3669 0 : return NS_OK;
3670 : }
3671 :
3672 0 : AppendMessage(MakeUnique<Message>(this, aHandleReport, aData));
3673 :
3674 0 : return NS_OK;
3675 : }
3676 :
3677 : void
3678 0 : MediaStreamGraphImpl::CollectSizesForMemoryReport(
3679 : already_AddRefed<nsIHandleReportCallback> aHandleReport,
3680 : already_AddRefed<nsISupports> aHandlerData)
3681 : {
3682 : class FinishCollectRunnable final : public Runnable
3683 : {
3684 : public:
3685 0 : explicit FinishCollectRunnable(
3686 : already_AddRefed<nsIHandleReportCallback> aHandleReport,
3687 : already_AddRefed<nsISupports> aHandlerData)
3688 0 : : mozilla::Runnable("FinishCollectRunnable")
3689 : , mHandleReport(aHandleReport)
3690 0 : , mHandlerData(aHandlerData)
3691 0 : {}
3692 :
3693 0 : NS_IMETHOD Run() override
3694 : {
3695 0 : MediaStreamGraphImpl::FinishCollectReports(mHandleReport, mHandlerData,
3696 0 : Move(mAudioStreamSizes));
3697 0 : return NS_OK;
3698 : }
3699 :
3700 : nsTArray<AudioNodeSizes> mAudioStreamSizes;
3701 :
3702 : private:
3703 0 : ~FinishCollectRunnable() {}
3704 :
3705 : // Avoiding nsCOMPtr because NSCAP_ASSERT_NO_QUERY_NEEDED in its
3706 : // constructor modifies the ref-count, which cannot be done off main
3707 : // thread.
3708 : RefPtr<nsIHandleReportCallback> mHandleReport;
3709 : RefPtr<nsISupports> mHandlerData;
3710 : };
3711 :
3712 : RefPtr<FinishCollectRunnable> runnable =
3713 0 : new FinishCollectRunnable(Move(aHandleReport), Move(aHandlerData));
3714 :
3715 0 : auto audioStreamSizes = &runnable->mAudioStreamSizes;
3716 :
3717 0 : for (MediaStream* s : AllStreams()) {
3718 0 : AudioNodeStream* stream = s->AsAudioNodeStream();
3719 0 : if (stream) {
3720 0 : AudioNodeSizes* usage = audioStreamSizes->AppendElement();
3721 0 : stream->SizeOfAudioNodesIncludingThis(MallocSizeOf, *usage);
3722 : }
3723 : }
3724 :
3725 0 : mAbstractMainThread->Dispatch(runnable.forget());
3726 0 : }
3727 :
3728 : void
3729 0 : MediaStreamGraphImpl::
3730 : FinishCollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData,
3731 : const nsTArray<AudioNodeSizes>& aAudioStreamSizes)
3732 : {
3733 0 : MOZ_ASSERT(NS_IsMainThread());
3734 :
3735 : nsCOMPtr<nsIMemoryReporterManager> manager =
3736 0 : do_GetService("@mozilla.org/memory-reporter-manager;1");
3737 :
3738 0 : if (!manager)
3739 0 : return;
3740 :
3741 : #define REPORT(_path, _amount, _desc) \
3742 : aHandleReport->Callback(EmptyCString(), _path, KIND_HEAP, UNITS_BYTES, \
3743 : _amount, NS_LITERAL_CSTRING(_desc), aData);
3744 :
3745 0 : for (size_t i = 0; i < aAudioStreamSizes.Length(); i++) {
3746 0 : const AudioNodeSizes& usage = aAudioStreamSizes[i];
3747 : const char* const nodeType =
3748 0 : usage.mNodeType ? usage.mNodeType : "<unknown>";
3749 :
3750 : nsPrintfCString enginePath("explicit/webaudio/audio-node/%s/engine-objects",
3751 0 : nodeType);
3752 0 : REPORT(enginePath, usage.mEngine,
3753 : "Memory used by AudioNode engine objects (Web Audio).");
3754 :
3755 : nsPrintfCString streamPath("explicit/webaudio/audio-node/%s/stream-objects",
3756 0 : nodeType);
3757 0 : REPORT(streamPath, usage.mStream,
3758 : "Memory used by AudioNode stream objects (Web Audio).");
3759 :
3760 : }
3761 :
3762 0 : size_t hrtfLoaders = WebCore::HRTFDatabaseLoader::sizeOfLoaders(MallocSizeOf);
3763 0 : if (hrtfLoaders) {
3764 0 : REPORT(NS_LITERAL_CSTRING(
3765 : "explicit/webaudio/audio-node/PannerNode/hrtf-databases"),
3766 : hrtfLoaders,
3767 : "Memory used by PannerNode databases (Web Audio).");
3768 : }
3769 :
3770 : #undef REPORT
3771 :
3772 0 : manager->EndReport();
3773 : }
3774 :
3775 : SourceMediaStream*
3776 0 : MediaStreamGraph::CreateSourceStream()
3777 : {
3778 0 : SourceMediaStream* stream = new SourceMediaStream();
3779 0 : AddStream(stream);
3780 0 : return stream;
3781 : }
3782 :
3783 : ProcessedMediaStream*
3784 0 : MediaStreamGraph::CreateTrackUnionStream()
3785 : {
3786 0 : TrackUnionStream* stream = new TrackUnionStream();
3787 0 : AddStream(stream);
3788 0 : return stream;
3789 : }
3790 :
3791 : ProcessedMediaStream*
3792 0 : MediaStreamGraph::CreateAudioCaptureStream(TrackID aTrackId)
3793 : {
3794 0 : AudioCaptureStream* stream = new AudioCaptureStream(aTrackId);
3795 0 : AddStream(stream);
3796 0 : return stream;
3797 : }
3798 :
3799 : void
3800 0 : MediaStreamGraph::AddStream(MediaStream* aStream)
3801 : {
3802 0 : NS_ADDREF(aStream);
3803 0 : MediaStreamGraphImpl* graph = static_cast<MediaStreamGraphImpl*>(this);
3804 0 : aStream->SetGraphImpl(graph);
3805 0 : graph->AppendMessage(MakeUnique<CreateMessage>(aStream));
3806 0 : }
3807 :
3808 0 : class GraphStartedRunnable final : public Runnable
3809 : {
3810 : public:
3811 0 : GraphStartedRunnable(AudioNodeStream* aStream, MediaStreamGraph* aGraph)
3812 0 : : Runnable("GraphStartedRunnable")
3813 : , mStream(aStream)
3814 0 : , mGraph(aGraph)
3815 0 : { }
3816 :
3817 0 : NS_IMETHOD Run() override {
3818 0 : mGraph->NotifyWhenGraphStarted(mStream);
3819 0 : return NS_OK;
3820 : }
3821 :
3822 : private:
3823 : RefPtr<AudioNodeStream> mStream;
3824 : MediaStreamGraph* mGraph;
3825 : };
3826 :
3827 : void
3828 0 : MediaStreamGraph::NotifyWhenGraphStarted(AudioNodeStream* aStream)
3829 : {
3830 0 : MOZ_ASSERT(NS_IsMainThread());
3831 :
3832 0 : class GraphStartedNotificationControlMessage : public ControlMessage
3833 : {
3834 : public:
3835 0 : explicit GraphStartedNotificationControlMessage(AudioNodeStream* aStream)
3836 0 : : ControlMessage(aStream)
3837 : {
3838 0 : }
3839 0 : void Run() override
3840 : {
3841 : // This runs on the graph thread, so when this runs, and the current
3842 : // driver is an AudioCallbackDriver, we know the audio hardware is
3843 : // started. If not, we are going to switch soon, keep reposting this
3844 : // ControlMessage.
3845 0 : MediaStreamGraphImpl* graphImpl = mStream->GraphImpl();
3846 0 : if (graphImpl->CurrentDriver()->AsAudioCallbackDriver()) {
3847 : nsCOMPtr<nsIRunnable> event = new dom::StateChangeTask(
3848 0 : mStream->AsAudioNodeStream(), nullptr, AudioContextState::Running);
3849 0 : graphImpl->Dispatch(event.forget());
3850 : } else {
3851 : nsCOMPtr<nsIRunnable> event = new GraphStartedRunnable(
3852 0 : mStream->AsAudioNodeStream(), mStream->Graph());
3853 0 : graphImpl->Dispatch(event.forget());
3854 : }
3855 0 : }
3856 0 : void RunDuringShutdown() override
3857 : {
3858 0 : }
3859 : };
3860 :
3861 0 : if (!aStream->IsDestroyed()) {
3862 0 : MediaStreamGraphImpl* graphImpl = static_cast<MediaStreamGraphImpl*>(this);
3863 0 : graphImpl->AppendMessage(MakeUnique<GraphStartedNotificationControlMessage>(aStream));
3864 : }
3865 0 : }
3866 :
3867 : void
3868 0 : MediaStreamGraphImpl::IncrementSuspendCount(MediaStream* aStream)
3869 : {
3870 0 : if (!aStream->IsSuspended()) {
3871 0 : MOZ_ASSERT(mStreams.Contains(aStream));
3872 0 : mStreams.RemoveElement(aStream);
3873 0 : mSuspendedStreams.AppendElement(aStream);
3874 0 : SetStreamOrderDirty();
3875 : }
3876 0 : aStream->IncrementSuspendCount();
3877 0 : }
3878 :
3879 : void
3880 0 : MediaStreamGraphImpl::DecrementSuspendCount(MediaStream* aStream)
3881 : {
3882 0 : bool wasSuspended = aStream->IsSuspended();
3883 0 : aStream->DecrementSuspendCount();
3884 0 : if (wasSuspended && !aStream->IsSuspended()) {
3885 0 : MOZ_ASSERT(mSuspendedStreams.Contains(aStream));
3886 0 : mSuspendedStreams.RemoveElement(aStream);
3887 0 : mStreams.AppendElement(aStream);
3888 0 : ProcessedMediaStream* ps = aStream->AsProcessedStream();
3889 0 : if (ps) {
3890 0 : ps->mCycleMarker = NOT_VISITED;
3891 : }
3892 0 : SetStreamOrderDirty();
3893 : }
3894 0 : }
3895 :
3896 : void
3897 0 : MediaStreamGraphImpl::SuspendOrResumeStreams(AudioContextOperation aAudioContextOperation,
3898 : const nsTArray<MediaStream*>& aStreamSet)
3899 : {
3900 : // For our purpose, Suspend and Close are equivalent: we want to remove the
3901 : // streams from the set of streams that are going to be processed.
3902 0 : for (MediaStream* stream : aStreamSet) {
3903 0 : if (aAudioContextOperation == AudioContextOperation::Resume) {
3904 0 : DecrementSuspendCount(stream);
3905 : } else {
3906 0 : IncrementSuspendCount(stream);
3907 : }
3908 : }
3909 0 : LOG(LogLevel::Debug,
3910 : ("Moving streams between suspended and running"
3911 : "state: mStreams: %" PRIuSIZE ", mSuspendedStreams: %" PRIuSIZE,
3912 : mStreams.Length(),
3913 : mSuspendedStreams.Length()));
3914 : #ifdef DEBUG
3915 : // The intersection of the two arrays should be null.
3916 0 : for (uint32_t i = 0; i < mStreams.Length(); i++) {
3917 0 : for (uint32_t j = 0; j < mSuspendedStreams.Length(); j++) {
3918 0 : MOZ_ASSERT(
3919 : mStreams[i] != mSuspendedStreams[j],
3920 : "The suspended stream set and running stream set are not disjoint.");
3921 : }
3922 : }
3923 : #endif
3924 0 : }
3925 :
3926 : void
3927 0 : MediaStreamGraphImpl::AudioContextOperationCompleted(MediaStream* aStream,
3928 : void* aPromise,
3929 : AudioContextOperation aOperation)
3930 : {
3931 : // This can be called from the thread created to do cubeb operation, or the
3932 : // MSG thread. The pointers passed back here are refcounted, so are still
3933 : // alive.
3934 0 : MonitorAutoLock lock(mMonitor);
3935 :
3936 : AudioContextState state;
3937 0 : switch (aOperation) {
3938 : case AudioContextOperation::Suspend:
3939 0 : state = AudioContextState::Suspended;
3940 0 : break;
3941 : case AudioContextOperation::Resume:
3942 0 : state = AudioContextState::Running;
3943 0 : break;
3944 : case AudioContextOperation::Close:
3945 0 : state = AudioContextState::Closed;
3946 0 : break;
3947 0 : default: MOZ_CRASH("Not handled.");
3948 : }
3949 :
3950 : nsCOMPtr<nsIRunnable> event = new dom::StateChangeTask(
3951 0 : aStream->AsAudioNodeStream(), aPromise, state);
3952 0 : mAbstractMainThread->Dispatch(event.forget());
3953 0 : }
3954 :
3955 : void
3956 0 : MediaStreamGraphImpl::ApplyAudioContextOperationImpl(
3957 : MediaStream* aDestinationStream, const nsTArray<MediaStream*>& aStreams,
3958 : AudioContextOperation aOperation, void* aPromise)
3959 : {
3960 0 : MOZ_ASSERT(CurrentDriver()->OnThread());
3961 :
3962 0 : SuspendOrResumeStreams(aOperation, aStreams);
3963 :
3964 0 : bool switching = false;
3965 0 : GraphDriver* nextDriver = nullptr;
3966 : {
3967 0 : MonitorAutoLock lock(mMonitor);
3968 0 : switching = CurrentDriver()->Switching();
3969 0 : if (switching) {
3970 0 : nextDriver = CurrentDriver()->NextDriver();
3971 : }
3972 : }
3973 :
3974 : // If we have suspended the last AudioContext, and we don't have other
3975 : // streams that have audio, this graph will automatically switch to a
3976 : // SystemCallbackDriver, because it can't find a MediaStream that has an audio
3977 : // track. When resuming, force switching to an AudioCallbackDriver (if we're
3978 : // not already switching). It would have happened at the next iteration
3979 : // anyways, but doing this now save some time.
3980 0 : if (aOperation == AudioContextOperation::Resume) {
3981 0 : if (!CurrentDriver()->AsAudioCallbackDriver()) {
3982 : AudioCallbackDriver* driver;
3983 0 : if (switching) {
3984 0 : MOZ_ASSERT(nextDriver->AsAudioCallbackDriver());
3985 0 : driver = nextDriver->AsAudioCallbackDriver();
3986 : } else {
3987 0 : driver = new AudioCallbackDriver(this);
3988 0 : MonitorAutoLock lock(mMonitor);
3989 0 : CurrentDriver()->SwitchAtNextIteration(driver);
3990 : }
3991 : driver->EnqueueStreamAndPromiseForOperation(aDestinationStream,
3992 0 : aPromise, aOperation);
3993 : } else {
3994 : // We are resuming a context, but we are already using an
3995 : // AudioCallbackDriver, we can resolve the promise now.
3996 0 : AudioContextOperationCompleted(aDestinationStream, aPromise, aOperation);
3997 : }
3998 : }
3999 : // Close, suspend: check if we are going to switch to a
4000 : // SystemAudioCallbackDriver, and pass the promise to the AudioCallbackDriver
4001 : // if that's the case, so it can notify the content.
4002 : // This is the same logic as in UpdateStreamOrder, but it's simpler to have it
4003 : // here as well so we don't have to store the Promise(s) on the Graph.
4004 0 : if (aOperation != AudioContextOperation::Resume) {
4005 0 : bool shouldAEC = false;
4006 0 : bool audioTrackPresent = AudioTrackPresent(shouldAEC);
4007 :
4008 0 : if (!audioTrackPresent && CurrentDriver()->AsAudioCallbackDriver()) {
4009 0 : CurrentDriver()->AsAudioCallbackDriver()->
4010 0 : EnqueueStreamAndPromiseForOperation(aDestinationStream, aPromise,
4011 0 : aOperation);
4012 :
4013 : SystemClockDriver* driver;
4014 0 : if (nextDriver) {
4015 0 : MOZ_ASSERT(!nextDriver->AsAudioCallbackDriver());
4016 : } else {
4017 0 : driver = new SystemClockDriver(this);
4018 0 : MonitorAutoLock lock(mMonitor);
4019 0 : CurrentDriver()->SwitchAtNextIteration(driver);
4020 : }
4021 : // We are closing or suspending an AudioContext, but we just got resumed.
4022 : // Queue the operation on the next driver so that the ordering is
4023 : // preserved.
4024 0 : } else if (!audioTrackPresent && switching) {
4025 0 : MOZ_ASSERT(nextDriver->AsAudioCallbackDriver());
4026 0 : nextDriver->AsAudioCallbackDriver()->
4027 0 : EnqueueStreamAndPromiseForOperation(aDestinationStream, aPromise,
4028 0 : aOperation);
4029 : } else {
4030 : // We are closing or suspending an AudioContext, but something else is
4031 : // using the audio stream, we can resolve the promise now.
4032 0 : AudioContextOperationCompleted(aDestinationStream, aPromise, aOperation);
4033 : }
4034 : }
4035 0 : }
4036 :
4037 : void
4038 0 : MediaStreamGraph::ApplyAudioContextOperation(MediaStream* aDestinationStream,
4039 : const nsTArray<MediaStream*>& aStreams,
4040 : AudioContextOperation aOperation,
4041 : void* aPromise)
4042 : {
4043 0 : class AudioContextOperationControlMessage : public ControlMessage
4044 : {
4045 : public:
4046 0 : AudioContextOperationControlMessage(MediaStream* aDestinationStream,
4047 : const nsTArray<MediaStream*>& aStreams,
4048 : AudioContextOperation aOperation,
4049 : void* aPromise)
4050 0 : : ControlMessage(aDestinationStream)
4051 : , mStreams(aStreams)
4052 : , mAudioContextOperation(aOperation)
4053 0 : , mPromise(aPromise)
4054 : {
4055 0 : }
4056 0 : void Run() override
4057 : {
4058 0 : mStream->GraphImpl()->ApplyAudioContextOperationImpl(mStream,
4059 0 : mStreams, mAudioContextOperation, mPromise);
4060 0 : }
4061 0 : void RunDuringShutdown() override
4062 : {
4063 0 : MOZ_ASSERT(mAudioContextOperation == AudioContextOperation::Close,
4064 : "We should be reviving the graph?");
4065 0 : }
4066 :
4067 : private:
4068 : // We don't need strong references here for the same reason ControlMessage
4069 : // doesn't.
4070 : nsTArray<MediaStream*> mStreams;
4071 : AudioContextOperation mAudioContextOperation;
4072 : void* mPromise;
4073 : };
4074 :
4075 0 : MediaStreamGraphImpl* graphImpl = static_cast<MediaStreamGraphImpl*>(this);
4076 0 : graphImpl->AppendMessage(
4077 0 : MakeUnique<AudioContextOperationControlMessage>(aDestinationStream, aStreams,
4078 0 : aOperation, aPromise));
4079 0 : }
4080 :
4081 : bool
4082 0 : MediaStreamGraph::IsNonRealtime() const
4083 : {
4084 0 : return !static_cast<const MediaStreamGraphImpl*>(this)->mRealtime;
4085 : }
4086 :
4087 : void
4088 0 : MediaStreamGraph::StartNonRealtimeProcessing(uint32_t aTicksToProcess)
4089 : {
4090 0 : NS_ASSERTION(NS_IsMainThread(), "main thread only");
4091 :
4092 0 : MediaStreamGraphImpl* graph = static_cast<MediaStreamGraphImpl*>(this);
4093 0 : NS_ASSERTION(!graph->mRealtime, "non-realtime only");
4094 :
4095 0 : if (graph->mNonRealtimeProcessing)
4096 0 : return;
4097 :
4098 0 : graph->mEndTime =
4099 0 : graph->RoundUpToNextAudioBlock(graph->mStateComputedTime +
4100 : aTicksToProcess - 1);
4101 0 : graph->mNonRealtimeProcessing = true;
4102 0 : graph->EnsureRunInStableState();
4103 : }
4104 :
4105 : void
4106 0 : ProcessedMediaStream::AddInput(MediaInputPort* aPort)
4107 : {
4108 0 : MediaStream* s = aPort->GetSource();
4109 0 : if (!s->IsSuspended()) {
4110 0 : mInputs.AppendElement(aPort);
4111 : } else {
4112 0 : mSuspendedInputs.AppendElement(aPort);
4113 : }
4114 0 : GraphImpl()->SetStreamOrderDirty();
4115 0 : }
4116 :
4117 : void
4118 0 : ProcessedMediaStream::InputSuspended(MediaInputPort* aPort)
4119 : {
4120 0 : GraphImpl()->AssertOnGraphThreadOrNotRunning();
4121 0 : mInputs.RemoveElement(aPort);
4122 0 : mSuspendedInputs.AppendElement(aPort);
4123 0 : GraphImpl()->SetStreamOrderDirty();
4124 0 : }
4125 :
4126 : void
4127 0 : ProcessedMediaStream::InputResumed(MediaInputPort* aPort)
4128 : {
4129 0 : GraphImpl()->AssertOnGraphThreadOrNotRunning();
4130 0 : mSuspendedInputs.RemoveElement(aPort);
4131 0 : mInputs.AppendElement(aPort);
4132 0 : GraphImpl()->SetStreamOrderDirty();
4133 0 : }
4134 :
4135 : void
4136 0 : MediaStreamGraph::RegisterCaptureStreamForWindow(
4137 : uint64_t aWindowId, ProcessedMediaStream* aCaptureStream)
4138 : {
4139 0 : MOZ_ASSERT(NS_IsMainThread());
4140 0 : MediaStreamGraphImpl* graphImpl = static_cast<MediaStreamGraphImpl*>(this);
4141 0 : graphImpl->RegisterCaptureStreamForWindow(aWindowId, aCaptureStream);
4142 0 : }
4143 :
4144 : void
4145 0 : MediaStreamGraphImpl::RegisterCaptureStreamForWindow(
4146 : uint64_t aWindowId, ProcessedMediaStream* aCaptureStream)
4147 : {
4148 0 : MOZ_ASSERT(NS_IsMainThread());
4149 0 : WindowAndStream winAndStream;
4150 0 : winAndStream.mWindowId = aWindowId;
4151 0 : winAndStream.mCaptureStreamSink = aCaptureStream;
4152 0 : mWindowCaptureStreams.AppendElement(winAndStream);
4153 0 : }
4154 :
4155 : void
4156 0 : MediaStreamGraph::UnregisterCaptureStreamForWindow(uint64_t aWindowId)
4157 : {
4158 0 : MOZ_ASSERT(NS_IsMainThread());
4159 0 : MediaStreamGraphImpl* graphImpl = static_cast<MediaStreamGraphImpl*>(this);
4160 0 : graphImpl->UnregisterCaptureStreamForWindow(aWindowId);
4161 0 : }
4162 :
4163 : void
4164 0 : MediaStreamGraphImpl::UnregisterCaptureStreamForWindow(uint64_t aWindowId)
4165 : {
4166 0 : MOZ_ASSERT(NS_IsMainThread());
4167 0 : for (int32_t i = mWindowCaptureStreams.Length() - 1; i >= 0; i--) {
4168 0 : if (mWindowCaptureStreams[i].mWindowId == aWindowId) {
4169 0 : mWindowCaptureStreams.RemoveElementAt(i);
4170 : }
4171 : }
4172 0 : }
4173 :
4174 : already_AddRefed<MediaInputPort>
4175 0 : MediaStreamGraph::ConnectToCaptureStream(uint64_t aWindowId,
4176 : MediaStream* aMediaStream)
4177 : {
4178 : return aMediaStream->GraphImpl()->ConnectToCaptureStream(aWindowId,
4179 0 : aMediaStream);
4180 : }
4181 :
4182 : already_AddRefed<MediaInputPort>
4183 0 : MediaStreamGraphImpl::ConnectToCaptureStream(uint64_t aWindowId,
4184 : MediaStream* aMediaStream)
4185 : {
4186 0 : MOZ_ASSERT(NS_IsMainThread());
4187 0 : for (uint32_t i = 0; i < mWindowCaptureStreams.Length(); i++) {
4188 0 : if (mWindowCaptureStreams[i].mWindowId == aWindowId) {
4189 0 : ProcessedMediaStream* sink = mWindowCaptureStreams[i].mCaptureStreamSink;
4190 0 : return sink->AllocateInputPort(aMediaStream);
4191 : }
4192 : }
4193 0 : return nullptr;
4194 : }
4195 :
4196 : void
4197 0 : MediaStreamGraph::DispatchToMainThreadAfterStreamStateUpdate(
4198 : already_AddRefed<nsIRunnable> aRunnable)
4199 : {
4200 0 : AssertOnGraphThreadOrNotRunning();
4201 : *mPendingUpdateRunnables.AppendElement() =
4202 0 : AbstractMainThread()->CreateDirectTaskDrainer(Move(aRunnable));
4203 0 : }
4204 :
4205 : } // namespace mozilla
|