Bug 1651049 - Move the sink change logic in MediaStreamRenderer. r=padenot

The MediaStreamRenderer handles the AudioOutput of an HTMLMediaElement. it register/unregister the AudioOutput according to the rendering status. The sink-change follows the logic of AudioOutput thus it has been moved in it.

The previous way created an error when the element had been muted and the sink had been changed before the source MediaStream was attached to the element. The error occured because it was possible, the same entry to be registered more than once in the AudioOutput's list, which resulted in the track to be unmuted when it should not.

Differential Revision: https://phabricator.services.mozilla.com/D82529
This commit is contained in:
Alex Chronopoulos 2020-07-09 14:32:21 +00:00
Родитель baf7486d56
Коммит d4383d4b9d
4 изменённых файлов: 54 добавлений и 42 удалений

Просмотреть файл

@ -829,6 +829,10 @@ class HTMLMediaElement::MediaStreamRenderer
for (const auto& t : mAudioTracks) {
if (t) {
if (mAudioOutputSink) {
t->AsAudioStreamTrack()->SetAudioOutputDevice(mAudioOutputKey,
mAudioOutputSink);
}
t->AsAudioStreamTrack()->AddAudioOutput(mAudioOutputKey);
t->AsAudioStreamTrack()->SetAudioOutputVolume(mAudioOutputKey,
mAudioOutputVolume);
@ -878,11 +882,47 @@ class HTMLMediaElement::MediaStreamRenderer
}
}
RefPtr<GenericPromise::AllPromiseType> SetAudioOutputDevice(
AudioDeviceInfo* aSink) {
MOZ_ASSERT(aSink);
MOZ_ASSERT(mAudioOutputSink != aSink);
mAudioOutputSink = aSink;
if (!mRendering) {
return GenericPromise::AllPromiseType::CreateAndResolve(nsTArray<bool>(),
__func__);
}
nsTArray<RefPtr<GenericPromise>> promises;
for (const auto& t : mAudioTracks) {
// SetAudioOutputDevice will create a new output MediaTrack, so the
// AudioOutput is removed for the current MediaTrack and re-added after
// the new MediaTrack has been created.
t->AsAudioStreamTrack()->RemoveAudioOutput(mAudioOutputKey);
promises.AppendElement(t->AsAudioStreamTrack()->SetAudioOutputDevice(
mAudioOutputKey, mAudioOutputSink));
t->AsAudioStreamTrack()->AddAudioOutput(mAudioOutputKey);
t->AsAudioStreamTrack()->SetAudioOutputVolume(mAudioOutputKey,
mAudioOutputVolume);
}
if (!promises.Length()) {
// Not active track, save it for later
return GenericPromise::AllPromiseType::CreateAndResolve(nsTArray<bool>(),
__func__);
}
return GenericPromise::All(GetCurrentSerialEventTarget(), promises);
}
void AddTrack(AudioStreamTrack* aTrack) {
MOZ_DIAGNOSTIC_ASSERT(!mAudioTracks.Contains(aTrack));
mAudioTracks.AppendElement(aTrack);
EnsureGraphTimeDummy();
if (mRendering) {
if (mAudioOutputSink) {
aTrack->SetAudioOutputDevice(mAudioOutputKey, mAudioOutputSink);
}
aTrack->AddAudioOutput(mAudioOutputKey);
aTrack->SetAudioOutputVolume(mAudioOutputKey, mAudioOutputVolume);
}
@ -972,6 +1012,9 @@ class HTMLMediaElement::MediaStreamRenderer
// The audio output volume for all audio tracks.
float mAudioOutputVolume = 1.0f;
// The sink device for all audio tracks.
RefPtr<AudioDeviceInfo> mAudioOutputSink;
// WatchManager for mGraphTime.
WatchManager<MediaStreamRenderer> mWatchManager;
@ -5230,7 +5273,7 @@ void HTMLMediaElement::SetupSrcMediaStreamPlayback(DOMMediaStream* aStream) {
&HTMLMediaElement::UpdateSrcStreamTime);
SetVolumeInternal();
if (mSink.second) {
SetSrcMediaStreamSink(mSink.second);
mMediaStreamRenderer->SetAudioOutputDevice(mSink.second);
}
UpdateSrcMediaStreamPlaying();
@ -7678,8 +7721,9 @@ already_AddRefed<Promise> HTMLMediaElement::SetSinkId(const nsAString& aSinkId,
return p;
}
if (self->mSrcStream) {
MOZ_ASSERT(self->mMediaStreamRenderer);
RefPtr<SinkInfoPromise> p =
self->SetSrcMediaStreamSink(aInfo)->Then(
self->mMediaStreamRenderer->SetAudioOutputDevice(aInfo)->Then(
self->mAbstractMainThread, __func__,
[aInfo](const GenericPromise::AllPromiseType::
ResolveOrRejectValue& aValue) {
@ -7728,43 +7772,6 @@ already_AddRefed<Promise> HTMLMediaElement::SetSinkId(const nsAString& aSinkId,
return promise.forget();
}
RefPtr<GenericPromise::AllPromiseType> HTMLMediaElement::SetSrcMediaStreamSink(
AudioDeviceInfo* aSink) {
MOZ_ASSERT(mSrcStream);
nsTArray<RefPtr<AudioStreamTrack>> audioTracks;
mSrcStream->GetAudioTracks(audioTracks);
if (audioTracks.Length() == 0) {
// Save it for later. At the moment the element does not contain any audio
// track. Nevertheless, the requested sink-id is saved to be used when the
// first audio track is available.
return GenericPromise::AllPromiseType::CreateAndResolve(nsTArray<bool>(),
__func__);
}
nsTArray<RefPtr<GenericPromise>> promises;
for (const auto& audioTrack : audioTracks) {
if (audioTrack->Ended()) {
continue;
}
if (!mPaused) {
audioTrack->RemoveAudioOutput(this);
}
promises.AppendElement(audioTrack->SetAudioOutputDevice(this, aSink));
if (!mPaused) {
audioTrack->AddAudioOutput(this);
audioTrack->SetAudioOutputVolume(this, ComputedVolume());
}
}
if (!promises.Length()) {
// Not active track, save it for later
return GenericPromise::AllPromiseType::CreateAndResolve(nsTArray<bool>(),
__func__);
}
return GenericPromise::All(GetCurrentSerialEventTarget(), promises);
}
void HTMLMediaElement::NotifyTextTrackModeChanged() {
if (mPendingTextTrackChanged) {
return;

Просмотреть файл

@ -1826,9 +1826,6 @@ class HTMLMediaElement : public nsGenericHTMLElement,
*/
void AfterMaybeChangeAttr(int32_t aNamespaceID, nsAtom* aName, bool aNotify);
RefPtr<GenericPromise::AllPromiseType> SetSrcMediaStreamSink(
AudioDeviceInfo* aSink);
// Total time a video has spent playing.
TimeDurationAccumulator mPlayTime;

Просмотреть файл

@ -72,6 +72,10 @@ void AudioStreamTrack::SetReadyState(MediaStreamTrackState aState) {
RefPtr<GenericPromise> AudioStreamTrack::SetAudioOutputDevice(
void* key, AudioDeviceInfo* aSink) {
MOZ_ASSERT(aSink);
if (Ended()) {
return GenericPromise::CreateAndResolve(true, __func__);
}
CrossGraphManager* manager = CrossGraphManager::Connect(this, aSink, mWindow);
if (!manager) {
auto entry = mCrossGraphs.Lookup(key);

Просмотреть файл

@ -25,6 +25,8 @@ extern LazyLogModule gForwardedInputTrackLog;
CrossGraphManager* CrossGraphManager::Connect(
const RefPtr<dom::AudioStreamTrack>& aStreamTrack, AudioDeviceInfo* aSink,
nsPIDOMWindowInner* aWindow) {
MOZ_ASSERT(aSink);
MOZ_ASSERT(aStreamTrack);
uint32_t defaultRate;
aSink->GetDefaultRate(&defaultRate);
LOG(LogLevel::Debug,
@ -45,6 +47,8 @@ CrossGraphManager* CrossGraphManager::Connect(
CrossGraphManager* CrossGraphManager::Connect(
const RefPtr<dom::AudioStreamTrack>& aStreamTrack,
MediaTrackGraph* aPartnerGraph) {
MOZ_ASSERT(aStreamTrack);
MOZ_ASSERT(aPartnerGraph);
if (aStreamTrack->Graph() == aPartnerGraph) {
// Primary graph the same as partner graph, just remove the existing cross
// graph connection