void TrackUnionStream::AddDirectTrackListenerImpl( already_AddRefed<DirectMediaStreamTrackListener> aListener, TrackID aTrackID) { RefPtr<DirectMediaStreamTrackListener> listener = aListener; for (TrackMapEntry& entry : mTrackMap) { if (entry.mOutputTrackID == aTrackID) { MediaStream* source = entry.mInputPort->GetSource(); STREAM_LOG(LogLevel::Debug, ("TrackUnionStream %p adding direct listener " "%p for track %d. Forwarding to input " "stream %p track %d.", this, listener.get(), aTrackID, source, entry.mInputTrackID)); entry.mOwnedDirectListeners.AppendElement(listener); DisabledTrackMode currentMode = GetDisabledTrackMode(aTrackID); if (currentMode != DisabledTrackMode::ENABLED) { listener->IncreaseDisabled(currentMode); } source->AddDirectTrackListenerImpl(listener.forget(), entry.mInputTrackID); return; } } TrackBound<DirectMediaStreamTrackListener>* bound = mPendingDirectTrackListeners.AppendElement(); bound->mListener = listener.forget(); bound->mTrackID = aTrackID; }
void TrackUnionStream::SetTrackEnabledImpl(TrackID aTrackID, DisabledTrackMode aMode) { bool enabled = aMode == DisabledTrackMode::ENABLED; for (TrackMapEntry& entry : mTrackMap) { if (entry.mOutputTrackID == aTrackID) { STREAM_LOG(LogLevel::Info, ("TrackUnionStream %p track %d was explicitly %s", this, aTrackID, enabled ? "enabled" : "disabled")); for (DirectMediaStreamTrackListener* listener : entry.mOwnedDirectListeners) { DisabledTrackMode oldMode = GetDisabledTrackMode(aTrackID); bool oldEnabled = oldMode == DisabledTrackMode::ENABLED; if (!oldEnabled && enabled) { STREAM_LOG(LogLevel::Debug, ("TrackUnionStream %p track %d setting " "direct listener enabled", this, aTrackID)); listener->DecreaseDisabled(oldMode); } else if (oldEnabled && !enabled) { STREAM_LOG(LogLevel::Debug, ("TrackUnionStream %p track %d setting " "direct listener disabled", this, aTrackID)); listener->IncreaseDisabled(aMode); } } } } MediaStream::SetTrackEnabledImpl(aTrackID, aMode); }
void AudioNodeStream::ProduceOutputBeforeInput(GraphTime aFrom) { MOZ_ASSERT(mEngine->AsDelayNodeEngine()); MOZ_ASSERT(mEngine->OutputCount() == 1, "DelayNodeEngine output count should be 1"); MOZ_ASSERT(!InMutedCycle(), "DelayNodes should break cycles"); MOZ_ASSERT(mLastChunks.Length() == 1); if (!mIsActive) { mLastChunks[0].SetNull(WEBAUDIO_BLOCK_SIZE); } else { mEngine->ProduceBlockBeforeInput(this, aFrom, &mLastChunks[0]); NS_ASSERTION(mLastChunks[0].GetDuration() == WEBAUDIO_BLOCK_SIZE, "Invalid WebAudio chunk size"); if (GetDisabledTrackMode(static_cast<TrackID>(AUDIO_TRACK)) != DisabledTrackMode::ENABLED) { mLastChunks[0].SetNull(WEBAUDIO_BLOCK_SIZE); } } }
void TrackUnionStream::RemoveDirectTrackListenerImpl(DirectMediaStreamTrackListener* aListener, TrackID aTrackID) { for (TrackMapEntry& entry : mTrackMap) { // OutputTrackID is unique to this stream so we only need to do this once. if (entry.mOutputTrackID != aTrackID) { continue; } for (size_t i = 0; i < entry.mOwnedDirectListeners.Length(); ++i) { if (entry.mOwnedDirectListeners[i] == aListener) { STREAM_LOG(LogLevel::Debug, ("TrackUnionStream %p removing direct " "listener %p for track %d, forwarding " "to input stream %p track %d", this, aListener, aTrackID, entry.mInputPort->GetSource(), entry.mInputTrackID)); DisabledTrackMode currentMode = GetDisabledTrackMode(aTrackID); if (currentMode != DisabledTrackMode::ENABLED) { // Reset the listener's state. aListener->DecreaseDisabled(currentMode); } entry.mOwnedDirectListeners.RemoveElementAt(i); break; } } // Forward to the input MediaStream* source = entry.mInputPort->GetSource(); source->RemoveDirectTrackListenerImpl(aListener, entry.mInputTrackID); return; } for (size_t i = 0; i < mPendingDirectTrackListeners.Length(); ++i) { TrackBound<DirectMediaStreamTrackListener>& bound = mPendingDirectTrackListeners[i]; if (bound.mListener == aListener && bound.mTrackID == aTrackID) { mPendingDirectTrackListeners.RemoveElementAt(i); return; } } }
uint32_t TrackUnionStream::AddTrack(MediaInputPort* aPort, StreamTracks::Track* aTrack, GraphTime aFrom) { STREAM_LOG(LogLevel::Verbose, ("TrackUnionStream %p adding track %d for " "input stream %p track %d, desired id %d", this, aTrack->GetID(), aPort->GetSource(), aTrack->GetID(), aPort->GetDestinationTrackId())); TrackID id; if (IsTrackIDExplicit(id = aPort->GetDestinationTrackId())) { MOZ_ASSERT(id >= mNextAvailableTrackID && mUsedTracks.BinaryIndexOf(id) == mUsedTracks.NoIndex, "Desired destination id taken. Only provide a destination ID " "if you can assure its availability, or we may not be able " "to bind to the correct DOM-side track."); #ifdef DEBUG for (size_t i = 0; mInputs[i] != aPort; ++i) { MOZ_ASSERT(mInputs[i]->GetSourceTrackId() != TRACK_ANY, "You are adding a MediaInputPort with a track mapping " "while there already exist generic MediaInputPorts for this " "destination stream. This can lead to TrackID collisions!"); } #endif mUsedTracks.InsertElementSorted(id); } else if ((id = aTrack->GetID()) && id > mNextAvailableTrackID && mUsedTracks.BinaryIndexOf(id) == mUsedTracks.NoIndex) { // Input id available. Mark it used in mUsedTracks. mUsedTracks.InsertElementSorted(id); } else { // No desired destination id and Input id taken, allocate a new one. id = mNextAvailableTrackID; // Update mNextAvailableTrackID and prune any mUsedTracks members it now // covers. while (1) { if (!mUsedTracks.RemoveElementSorted(++mNextAvailableTrackID)) { // Not in use. We're done. break; } } } // Round up the track start time so the track, if anything, starts a // little later than the true time. This means we'll have enough // samples in our input stream to go just beyond the destination time. StreamTime outputStart = GraphTimeToStreamTimeWithBlocking(aFrom); nsAutoPtr<MediaSegment> segment; segment = aTrack->GetSegment()->CreateEmptyClone(); for (uint32_t j = 0; j < mListeners.Length(); ++j) { MediaStreamListener* l = mListeners[j]; l->NotifyQueuedTrackChanges(Graph(), id, outputStart, TrackEventCommand::TRACK_EVENT_CREATED, *segment, aPort->GetSource(), aTrack->GetID()); } segment->AppendNullData(outputStart); StreamTracks::Track* track = &mTracks.AddTrack(id, outputStart, segment.forget()); STREAM_LOG(LogLevel::Debug, ("TrackUnionStream %p added track %d for input stream %p track %d, start ticks %lld", this, track->GetID(), aPort->GetSource(), aTrack->GetID(), (long long)outputStart)); TrackMapEntry* map = mTrackMap.AppendElement(); map->mEndOfConsumedInputTicks = 0; map->mEndOfLastInputIntervalInInputStream = -1; map->mEndOfLastInputIntervalInOutputStream = -1; map->mInputPort = aPort; map->mInputTrackID = aTrack->GetID(); map->mOutputTrackID = track->GetID(); map->mSegment = aTrack->GetSegment()->CreateEmptyClone(); for (int32_t i = mPendingDirectTrackListeners.Length() - 1; i >= 0; --i) { TrackBound<DirectMediaStreamTrackListener>& bound = mPendingDirectTrackListeners[i]; if (bound.mTrackID != map->mOutputTrackID) { continue; } MediaStream* source = map->mInputPort->GetSource(); map->mOwnedDirectListeners.AppendElement(bound.mListener); DisabledTrackMode currentMode = GetDisabledTrackMode(bound.mTrackID); if (currentMode != DisabledTrackMode::ENABLED) { bound.mListener->IncreaseDisabled(currentMode); } STREAM_LOG(LogLevel::Debug, ("TrackUnionStream %p adding direct listener " "%p for track %d. Forwarding to input " "stream %p track %d.", this, bound.mListener.get(), bound.mTrackID, source, map->mInputTrackID)); source->AddDirectTrackListenerImpl(bound.mListener.forget(), map->mInputTrackID); mPendingDirectTrackListeners.RemoveElementAt(i); } return mTrackMap.Length() - 1; }
// The MediaStreamGraph guarantees that this is actually one block, for // AudioNodeStreams. void AudioNodeStream::ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) { uint16_t outputCount = mLastChunks.Length(); MOZ_ASSERT(outputCount == std::max(uint16_t(1), mEngine->OutputCount())); if (!mIsActive) { // mLastChunks are already null. #ifdef DEBUG for (const auto& chunk : mLastChunks) { MOZ_ASSERT(chunk.IsNull()); } #endif } else if (InMutedCycle()) { mInputChunks.Clear(); for (uint16_t i = 0; i < outputCount; ++i) { mLastChunks[i].SetNull(WEBAUDIO_BLOCK_SIZE); } } else { // We need to generate at least one input uint16_t maxInputs = std::max(uint16_t(1), mEngine->InputCount()); mInputChunks.SetLength(maxInputs); for (uint16_t i = 0; i < maxInputs; ++i) { ObtainInputBlock(mInputChunks[i], i); } bool finished = false; if (mPassThrough) { MOZ_ASSERT(outputCount == 1, "For now, we only support nodes that have one output port"); mLastChunks[0] = mInputChunks[0]; } else { if (maxInputs <= 1 && outputCount <= 1) { mEngine->ProcessBlock(this, aFrom, mInputChunks[0], &mLastChunks[0], &finished); } else { mEngine->ProcessBlocksOnPorts(this, mInputChunks, mLastChunks, &finished); } } for (uint16_t i = 0; i < outputCount; ++i) { NS_ASSERTION(mLastChunks[i].GetDuration() == WEBAUDIO_BLOCK_SIZE, "Invalid WebAudio chunk size"); } if (finished) { mMarkAsFinishedAfterThisBlock = true; if (mIsActive) { ScheduleCheckForInactive(); } } if (GetDisabledTrackMode(static_cast<TrackID>(AUDIO_TRACK)) != DisabledTrackMode::ENABLED) { for (uint32_t i = 0; i < outputCount; ++i) { mLastChunks[i].SetNull(WEBAUDIO_BLOCK_SIZE); } } } if (!mFinished) { // Don't output anything while finished if (mFlags & EXTERNAL_OUTPUT) { AdvanceOutputSegment(); } if (mMarkAsFinishedAfterThisBlock && (aFlags & ALLOW_FINISH)) { // This stream was finished the last time that we looked at it, and all // of the depending streams have finished their output as well, so now // it's time to mark this stream as finished. if (mFlags & EXTERNAL_OUTPUT) { FinishOutput(); } FinishOnGraphThread(); } } }