void
AudioCaptureStream::ProcessInput(GraphTime aFrom, GraphTime aTo,
                                 uint32_t aFlags)
{
    if (!mStarted) {
        return;
    }

    uint32_t inputCount = mInputs.Length();
    StreamTracks::Track* track = EnsureTrack(mTrackId);
    // Notify the DOM everything is in order.
    if (!mTrackCreated) {
        for (uint32_t i = 0; i < mListeners.Length(); i++) {
            MediaStreamListener* l = mListeners[i];
            AudioSegment tmp;
            l->NotifyQueuedTrackChanges(
                Graph(), mTrackId, 0, TrackEventCommand::TRACK_EVENT_CREATED, tmp);
            l->NotifyFinishedTrackCreation(Graph());
        }
        mTrackCreated = true;
    }

    if (IsFinishedOnGraphThread()) {
        return;
    }

    // If the captured stream is connected back to a object on the page (be it an
    // HTMLMediaElement with a stream as source, or an AudioContext), a cycle
    // situation occur. This can work if it's an AudioContext with at least one
    // DelayNode, but the MSG will mute the whole cycle otherwise.
    if (InMutedCycle() || inputCount == 0) {
        track->Get<AudioSegment>()->AppendNullData(aTo - aFrom);
    } else {
        // We mix down all the tracks of all inputs, to a stereo track. Everything
        // is {up,down}-mixed to stereo.
        mMixer.StartMixing();
        AudioSegment output;
        for (uint32_t i = 0; i < inputCount; i++) {
            MediaStream* s = mInputs[i]->GetSource();
            StreamTracks::TrackIter tracks(s->GetStreamTracks(), MediaSegment::AUDIO);
            while (!tracks.IsEnded()) {
                AudioSegment* inputSegment = tracks->Get<AudioSegment>();
                StreamTime inputStart = s->GraphTimeToStreamTimeWithBlocking(aFrom);
                StreamTime inputEnd = s->GraphTimeToStreamTimeWithBlocking(aTo);
                if (tracks->IsEnded() && inputSegment->GetDuration() <= inputEnd) {
                    // If the input track has ended and we have consumed all its data it
                    // can be ignored.
                    continue;
                }
                AudioSegment toMix;
                toMix.AppendSlice(*inputSegment, inputStart, inputEnd);
                // Care for streams blocked in the [aTo, aFrom] range.
                if (inputEnd - inputStart < aTo - aFrom) {
                    toMix.AppendNullData((aTo - aFrom) - (inputEnd - inputStart));
                }
                toMix.Mix(mMixer, MONO, Graph()->GraphRate());
                tracks.Next();
            }
        }
        // This calls MixerCallback below
        mMixer.FinishMixing();
    }

    // Regardless of the status of the input tracks, we go foward.
    mTracks.AdvanceKnownTracksTime(GraphTimeToStreamTimeWithBlocking((aTo)));
}
 void TrackUnionStream::ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags)
 {
   if (IsFinishedOnGraphThread()) {
     return;
   }
   AutoTArray<bool,8> mappedTracksFinished;
   AutoTArray<bool,8> mappedTracksWithMatchingInputTracks;
   for (uint32_t i = 0; i < mTrackMap.Length(); ++i) {
     mappedTracksFinished.AppendElement(true);
     mappedTracksWithMatchingInputTracks.AppendElement(false);
   }
   bool allFinished = !mInputs.IsEmpty();
   bool allHaveCurrentData = !mInputs.IsEmpty();
   for (uint32_t i = 0; i < mInputs.Length(); ++i) {
     MediaStream* stream = mInputs[i]->GetSource();
     if (!stream->IsFinishedOnGraphThread()) {
       // XXX we really should check whether 'stream' has finished within time aTo,
       // not just that it's finishing when all its queued data eventually runs
       // out.
       allFinished = false;
     }
     if (!stream->HasCurrentData()) {
       allHaveCurrentData = false;
     }
     bool trackAdded = false;
     for (StreamTracks::TrackIter tracks(stream->GetStreamTracks());
          !tracks.IsEnded(); tracks.Next()) {
       bool found = false;
       for (uint32_t j = 0; j < mTrackMap.Length(); ++j) {
         TrackMapEntry* map = &mTrackMap[j];
         if (map->mInputPort == mInputs[i] && map->mInputTrackID == tracks->GetID()) {
           bool trackFinished = false;
           StreamTracks::Track* outputTrack = mTracks.FindTrack(map->mOutputTrackID);
           found = true;
           if (!outputTrack || outputTrack->IsEnded() ||
               !mInputs[i]->PassTrackThrough(tracks->GetID())) {
             trackFinished = true;
           } else {
             CopyTrackData(tracks.get(), j, aFrom, aTo, &trackFinished);
           }
           mappedTracksFinished[j] = trackFinished;
           mappedTracksWithMatchingInputTracks[j] = true;
           break;
         }
       }
       if (!found && mInputs[i]->AllowCreationOf(tracks->GetID())) {
         bool trackFinished = false;
         trackAdded = true;
         uint32_t mapIndex = AddTrack(mInputs[i], tracks.get(), aFrom);
         CopyTrackData(tracks.get(), mapIndex, aFrom, aTo, &trackFinished);
         mappedTracksFinished.AppendElement(trackFinished);
         mappedTracksWithMatchingInputTracks.AppendElement(true);
       }
     }
     if (trackAdded) {
       for (MediaStreamListener* l : mListeners) {
         l->NotifyFinishedTrackCreation(Graph());
       }
     }
   }
   for (int32_t i = mTrackMap.Length() - 1; i >= 0; --i) {
     if (mappedTracksFinished[i]) {
       EndTrack(i);
     } else {
       allFinished = false;
     }
     if (!mappedTracksWithMatchingInputTracks[i]) {
       for (auto listener : mTrackMap[i].mOwnedDirectListeners) {
         // Remove listeners while the entry still exists.
         RemoveDirectTrackListenerImpl(listener, mTrackMap[i].mOutputTrackID);
       }
       mTrackMap.RemoveElementAt(i);
     }
   }
   if (allFinished && mAutofinish && (aFlags & ALLOW_FINISH)) {
     // All streams have finished and won't add any more tracks, and
     // all our tracks have actually finished and been removed from our map,
     // so we're finished now.
     FinishOnGraphThread();
   } else {
     mTracks.AdvanceKnownTracksTime(GraphTimeToStreamTimeWithBlocking(aTo));
   }
   if (allHaveCurrentData) {
     // We can make progress if we're not blocked
     mHasCurrentData = true;
   }
 }