/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "ForwardedInputTrack.h" #include #include "AudioChannelService.h" #include "AudioNodeEngine.h" #include "AudioNodeExternalInputTrack.h" #include "AudioNodeTrack.h" #include "AudioSegment.h" #include "DOMMediaStream.h" #include "GeckoProfiler.h" #include "ImageContainer.h" #include "MediaTrackGraphImpl.h" #include "mozilla/Attributes.h" #include "mozilla/Logging.h" #include "mozilla/MathAlgorithms.h" #include "mozilla/Unused.h" #include "nsContentUtils.h" #include "nsPrintfCString.h" #include "nsServiceManagerUtils.h" #include "nsWidgetsCID.h" #include "prerror.h" #include "Tracing.h" #include "VideoSegment.h" #include "webaudio/MediaStreamAudioDestinationNode.h" using namespace mozilla::layers; using namespace mozilla::dom; using namespace mozilla::gfx; namespace mozilla { #ifdef TRACK_LOG # undef TRACK_LOG #endif LazyLogModule gForwardedInputTrackLog("ForwardedInputTrack"); #define TRACK_LOG(type, msg) MOZ_LOG(gForwardedInputTrackLog, type, msg) ForwardedInputTrack::ForwardedInputTrack(TrackRate aSampleRate, MediaSegment::Type aType) : ProcessedMediaTrack( aSampleRate, aType, aType == MediaSegment::AUDIO ? static_cast(new AudioSegment()) : static_cast(new VideoSegment())) {} void ForwardedInputTrack::AddInput(MediaInputPort* aPort) { SetInput(aPort); ProcessedMediaTrack::AddInput(aPort); } void ForwardedInputTrack::RemoveInput(MediaInputPort* aPort) { TRACK_LOG(LogLevel::Debug, ("ForwardedInputTrack %p removing input %p", this, aPort)); MOZ_ASSERT(aPort == mInputPort); for (const auto& listener : mOwnedDirectListeners) { MediaTrack* source = mInputPort->GetSource(); TRACK_LOG(LogLevel::Debug, ("ForwardedInputTrack %p removing direct listener " "%p. Forwarding to input track %p.", this, listener.get(), aPort->GetSource())); source->RemoveDirectListenerImpl(listener); } DisabledTrackMode oldMode = CombinedDisabledMode(); mInputDisabledMode = DisabledTrackMode::ENABLED; NotifyIfDisabledModeChangedFrom(oldMode); mInputPort = nullptr; ProcessedMediaTrack::RemoveInput(aPort); } void ForwardedInputTrack::SetInput(MediaInputPort* aPort) { MOZ_ASSERT(aPort); MOZ_ASSERT(aPort->GetSource()); MOZ_ASSERT(aPort->GetSource()->GetData()); MOZ_ASSERT(!mInputPort); MOZ_ASSERT(mInputDisabledMode == DisabledTrackMode::ENABLED); mInputPort = aPort; for (const auto& listener : mOwnedDirectListeners) { MediaTrack* source = mInputPort->GetSource(); TRACK_LOG(LogLevel::Debug, ("ForwardedInputTrack %p adding direct listener " "%p. Forwarding to input track %p.", this, listener.get(), aPort->GetSource())); source->AddDirectListenerImpl(do_AddRef(listener)); } DisabledTrackMode oldMode = CombinedDisabledMode(); mInputDisabledMode = mInputPort->GetSource()->CombinedDisabledMode(); NotifyIfDisabledModeChangedFrom(oldMode); } void ForwardedInputTrack::ProcessInputImpl(MediaTrack* aSource, MediaSegment* aSegment, GraphTime aFrom, GraphTime aTo, uint32_t aFlags) { GraphTime next; for (GraphTime t = aFrom; t < aTo; t = next) { MediaInputPort::InputInterval interval = MediaInputPort::GetNextInputInterval(mInputPort, t); interval.mEnd = std::min(interval.mEnd, aTo); const bool inputEnded = !aSource || (aSource->Ended() && aSource->GetEnd() <= aSource->GraphTimeToTrackTimeWithBlocking(interval.mEnd)); TrackTime ticks = interval.mEnd - interval.mStart; next = interval.mEnd; if (interval.mStart >= interval.mEnd) { break; } if (inputEnded) { if (mAutoend && (aFlags & ALLOW_END)) { mEnded = true; break; } aSegment->AppendNullData(ticks); TRACK_LOG(LogLevel::Verbose, ("ForwardedInputTrack %p appending %lld ticks " "of null data (ended input)", this, (long long)ticks)); } else if (interval.mInputIsBlocked) { aSegment->AppendNullData(ticks); TRACK_LOG(LogLevel::Verbose, ("ForwardedInputTrack %p appending %lld ticks " "of null data (blocked input)", this, (long long)ticks)); } else if (InMutedCycle()) { aSegment->AppendNullData(ticks); } else if (aSource->IsSuspended()) { aSegment->AppendNullData(ticks); } else { MOZ_ASSERT(GetEnd() == GraphTimeToTrackTimeWithBlocking(interval.mStart), "Samples missing"); TrackTime inputStart = aSource->GraphTimeToTrackTimeWithBlocking(interval.mStart); TrackTime inputEnd = aSource->GraphTimeToTrackTimeWithBlocking(interval.mEnd); aSegment->AppendSlice(*aSource->GetData(), inputStart, inputEnd); } ApplyTrackDisabling(aSegment); for (const auto& listener : mTrackListeners) { listener->NotifyQueuedChanges(Graph(), GetEnd(), *aSegment); } mSegment->AppendFrom(aSegment); } } void ForwardedInputTrack::ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) { TRACE_COMMENT("ForwardedInputTrack::ProcessInput", "ForwardedInputTrack %p", this); if (mEnded) { return; } MediaInputPort* input = mInputPort; MediaTrack* source = input ? input->GetSource() : nullptr; if (mType == MediaSegment::AUDIO) { AudioSegment audio; ProcessInputImpl(source, &audio, aFrom, aTo, aFlags); } else if (mType == MediaSegment::VIDEO) { VideoSegment video; ProcessInputImpl(source, &video, aFrom, aTo, aFlags); } else { MOZ_CRASH("Unknown segment type"); } if (mEnded) { RemoveAllDirectListenersImpl(); } } DisabledTrackMode ForwardedInputTrack::CombinedDisabledMode() const { if (mDisabledMode == DisabledTrackMode::SILENCE_BLACK || mInputDisabledMode == DisabledTrackMode::SILENCE_BLACK) { return DisabledTrackMode::SILENCE_BLACK; } if (mDisabledMode == DisabledTrackMode::SILENCE_FREEZE || mInputDisabledMode == DisabledTrackMode::SILENCE_FREEZE) { return DisabledTrackMode::SILENCE_FREEZE; } return DisabledTrackMode::ENABLED; } void ForwardedInputTrack::SetDisabledTrackModeImpl(DisabledTrackMode aMode) { bool enabled = aMode == DisabledTrackMode::ENABLED; TRACK_LOG(LogLevel::Info, ("ForwardedInputTrack %p was explicitly %s", this, enabled ? "enabled" : "disabled")); for (DirectMediaTrackListener* listener : mOwnedDirectListeners) { DisabledTrackMode oldMode = mDisabledMode; bool oldEnabled = oldMode == DisabledTrackMode::ENABLED; if (!oldEnabled && enabled) { TRACK_LOG(LogLevel::Debug, ("ForwardedInputTrack %p setting " "direct listener enabled", this)); listener->DecreaseDisabled(oldMode); } else if (oldEnabled && !enabled) { TRACK_LOG(LogLevel::Debug, ("ForwardedInputTrack %p setting " "direct listener disabled", this)); listener->IncreaseDisabled(aMode); } } MediaTrack::SetDisabledTrackModeImpl(aMode); } void ForwardedInputTrack::OnInputDisabledModeChanged( DisabledTrackMode aInputMode) { MOZ_ASSERT(mInputs.Length() == 1); MOZ_ASSERT(mInputs[0]->GetSource()); DisabledTrackMode oldMode = CombinedDisabledMode(); if (mInputDisabledMode == DisabledTrackMode::SILENCE_BLACK && aInputMode == DisabledTrackMode::SILENCE_FREEZE) { // Don't allow demoting from SILENCE_BLACK to SILENCE_FREEZE. Frames will // remain black so we shouldn't notify that the track got enabled. aInputMode = DisabledTrackMode::SILENCE_BLACK; } mInputDisabledMode = aInputMode; NotifyIfDisabledModeChangedFrom(oldMode); } uint32_t ForwardedInputTrack::NumberOfChannels() const { MOZ_DIAGNOSTIC_ASSERT(mSegment->GetType() == MediaSegment::AUDIO); if (!mInputPort || !mInputPort->GetSource()) { return GetData()->MaxChannelCount(); } return mInputPort->GetSource()->NumberOfChannels(); } void ForwardedInputTrack::AddDirectListenerImpl( already_AddRefed aListener) { RefPtr listener = aListener; mOwnedDirectListeners.AppendElement(listener); DisabledTrackMode currentMode = mDisabledMode; if (currentMode != DisabledTrackMode::ENABLED) { listener->IncreaseDisabled(currentMode); } if (mInputPort) { MediaTrack* source = mInputPort->GetSource(); TRACK_LOG(LogLevel::Debug, ("ForwardedInputTrack %p adding direct listener " "%p. Forwarding to input track %p.", this, listener.get(), source)); source->AddDirectListenerImpl(listener.forget()); } } void ForwardedInputTrack::RemoveDirectListenerImpl( DirectMediaTrackListener* aListener) { for (size_t i = 0; i < mOwnedDirectListeners.Length(); ++i) { if (mOwnedDirectListeners[i] == aListener) { TRACK_LOG(LogLevel::Debug, ("ForwardedInputTrack %p removing direct listener %p", this, aListener)); DisabledTrackMode currentMode = mDisabledMode; if (currentMode != DisabledTrackMode::ENABLED) { // Reset the listener's state. aListener->DecreaseDisabled(currentMode); } mOwnedDirectListeners.RemoveElementAt(i); break; } } if (mInputPort) { // Forward to the input MediaTrack* source = mInputPort->GetSource(); source->RemoveDirectListenerImpl(aListener); } } void ForwardedInputTrack::RemoveAllDirectListenersImpl() { for (const auto& listener : mOwnedDirectListeners.Clone()) { RemoveDirectListenerImpl(listener); } MOZ_DIAGNOSTIC_ASSERT(mOwnedDirectListeners.IsEmpty()); } } // namespace mozilla