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
parent 963e0c5797
commit 71d449f3c6
4 changed files with 54 additions and 42 deletions

View File

@@ -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;