Coverage Report

Created: 2018-09-25 14:53

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