AudioStreamTrack.cpp (5284B)
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 "AudioStreamTrack.h" 7 8 #include "MediaTrackGraph.h" 9 #include "nsContentUtils.h" 10 11 extern mozilla::LazyLogModule gMediaStreamTrackLog; 12 #define LOG(type, msg) MOZ_LOG(gMediaStreamTrackLog, type, msg) 13 14 namespace mozilla::dom { 15 16 RefPtr<GenericPromise> AudioStreamTrack::AddAudioOutput( 17 void* aKey, AudioDeviceInfo* aSink) { 18 if (Ended()) { 19 return GenericPromise::CreateAndResolve(true, __func__); 20 } 21 22 mTrack->AddAudioOutput(aKey, aSink); 23 return mTrack->Graph()->NotifyWhenDeviceStarted(aSink); 24 } 25 26 void AudioStreamTrack::RemoveAudioOutput(void* aKey) { 27 if (Ended()) { 28 return; 29 } 30 31 mTrack->RemoveAudioOutput(aKey); 32 } 33 34 void AudioStreamTrack::SetAudioOutputVolume(void* aKey, float aVolume) { 35 if (Ended()) { 36 return; 37 } 38 39 mTrack->SetAudioOutputVolume(aKey, aVolume); 40 } 41 42 already_AddRefed<MediaInputPort> AudioStreamTrack::AddConsumerPort( 43 ProcessedMediaTrack* aTrack) { 44 MOZ_ASSERT(NS_IsMainThread()); 45 MOZ_ASSERT(!mTrack == Ended()); 46 47 if (!mTrack || !aTrack || aTrack->IsDestroyed()) { 48 LOG(LogLevel::Warning, 49 ("AudioStreamTrack %p cannot forward contents: track ended or " 50 "data/destination track ended/destroyed", 51 this)); 52 return nullptr; 53 } 54 55 MOZ_ASSERT(!mTrack->IsDestroyed()); 56 if (mTrack->Graph() == aTrack->Graph()) { 57 return ForwardTrackContentsTo(aTrack); 58 } 59 60 LOG(LogLevel::Verbose, 61 ("AudioStreamTrack %p forwarding cross-graph contents from track %p " 62 "(graph %p) to track %p (graph %p)", 63 this, mTrack.get(), mTrack->Graph(), aTrack, aTrack->Graph())); 64 65 // Route audio from mTrack through a cross-graph transmitter and receiver to 66 // aTrack. 67 MediaTrackGraph* rcvrGraph = aTrack->Graph(); 68 69 // Find existing connection for this graph 70 for (auto& conn : mCrossGraphs) { 71 if (conn.mPort->mReceiver->Graph() == rcvrGraph) { 72 conn.mRefCount++; 73 LOG(LogLevel::Verbose, 74 ("AudioStreamTrack %p reusing cross-graph port " 75 "to graph %p (rate %u), refcount now %zu", 76 this, rcvrGraph, rcvrGraph->GraphRate(), conn.mRefCount)); 77 return aTrack->AllocateInputPort(conn.mPort->mReceiver); 78 } 79 } 80 81 // Create new connection if none exists 82 LOG(LogLevel::Verbose, 83 ("AudioStreamTrack %p creating cross-graph port to graph %p (rate %u)", 84 this, rcvrGraph, rcvrGraph->GraphRate())); 85 CrossGraphConnection* conn = mCrossGraphs.AppendElement( 86 CrossGraphConnection(CrossGraphPort::Connect(RefPtr{this}, rcvrGraph))); 87 return aTrack->AllocateInputPort(conn->mPort->mReceiver); 88 } 89 90 void AudioStreamTrack::RemoveConsumerPort(MediaInputPort* aPort) { 91 MOZ_ASSERT(NS_IsMainThread()); 92 93 if (!aPort) { 94 return; 95 } 96 97 MediaTrackGraph* receiverGraph = aPort->Graph(); 98 99 // Decrement refcount for this graph's connection and remove if it reaches 0 100 for (size_t i = 0; i < mCrossGraphs.Length(); ++i) { 101 auto& conn = mCrossGraphs[i]; 102 if (conn.mPort->mReceiver->Graph() == receiverGraph) { 103 MOZ_ASSERT(conn.mRefCount > 0); 104 --conn.mRefCount; 105 LOG(LogLevel::Verbose, 106 ("AudioStreamTrack %p decrementing cross-graph port refcount to " 107 "graph %p (rate %u), refcount now %zu", 108 this, receiverGraph, receiverGraph->GraphRate(), conn.mRefCount)); 109 if (conn.mRefCount == 0) { 110 LOG(LogLevel::Verbose, 111 ("AudioStreamTrack %p removing cross-graph forwarding to graph %p " 112 "(rate %u)", 113 this, receiverGraph, receiverGraph->GraphRate())); 114 mCrossGraphs.UnorderedRemoveElementAt(i); 115 } 116 return; 117 } 118 } 119 } 120 121 void AudioStreamTrack::GetLabel(nsAString& aLabel, CallerType aCallerType) { 122 MediaStreamTrack::GetLabel(aLabel, aCallerType); 123 } 124 125 already_AddRefed<MediaStreamTrack> AudioStreamTrack::Clone() { 126 return MediaStreamTrack::CloneInternal<AudioStreamTrack>(); 127 } 128 129 void AudioStreamTrack::SetReadyState(MediaStreamTrackState aState) { 130 MOZ_ASSERT(NS_IsMainThread()); 131 132 // When transitioning from Live to Ended, mTrack will be destroyed. Since 133 // mTrack is the source for cross-graph data forwarding, keeping cross-graph 134 // ports is unnecessary. Clearing them here ensures all related connections 135 // are properly disconnected and prevents an assertion failure in 136 // CrossGraphTransmitters::ProcessInput due to a missing source. 137 // 138 // This state transition may occur in various situations, such as when the 139 // track is stopped by a user action, or when mTrack is ended during its 140 // ProcessInput (because its source has ended), which is then detected by 141 // MediaTrackGraph and ultimately notifies the ended-signal via MTGListener, 142 // reaching this point. 143 if (!mCrossGraphs.IsEmpty() && aState == MediaStreamTrackState::Ended) { 144 MOZ_ASSERT(!Ended()); 145 LOG(LogLevel::Verbose, 146 ("AudioStreamTrack %p ending, destroying %zu cross-graph ports", this, 147 mCrossGraphs.Length())); 148 mCrossGraphs.Clear(); 149 } 150 151 MediaStreamTrack::SetReadyState(aState); 152 } 153 154 } // namespace mozilla::dom 155 156 #undef LOG