Fix remaining bugs and issues with PiP

This commit is contained in:
wuggy 2026-09-13 13:02:36 -07:00
commit 27af37ea7b
3 changed files with 24 additions and 5 deletions

View file

@ -19,6 +19,11 @@ function update() {
if (!source) { if (!source) {
return; return;
} }
// Gate already-buffered audio here rather than waiting for source mute
// changes to travel through the decoder's capture queue.
if (output) {
output.muted = !!source.srcObject || source.muted;
}
// Capture streams do not produce frames while their source is paused. // Capture streams do not produce frames while their source is paused.
// Keep a snapshot for that case, without changing the source's play state. // Keep a snapshot for that case, without changing the source's play state.
still.hidden = !source.paused && !source.ended; still.hidden = !source.paused && !source.ended;
@ -116,14 +121,14 @@ addMessageListener("PictureInPicture:Init", message => {
"max-width:none;max-height:none;object-fit:contain"; "max-width:none;max-height:none;object-fit:contain";
doc.body.appendChild(element); doc.body.appendChild(element);
} }
// Decoder capture redirects audio to the captured stream. Its samples // Decoder capture redirects audio to this output. PiP content mute is
// already include the source's volume/mute settings. MediaStream sources // applied here; other volume/mute policies remain in the decoder.
// keep their own audio output, so mute only that case to avoid an echo. // MediaStream sources keep their own output, so avoid doubling it.
output.muted = !!source.srcObject; output.muted = !!source.srcObject || source.muted;
output.volume = 1; output.volume = 1;
source.mozPictureInPicture = true;
stream = source.mozCaptureStream(); stream = source.mozCaptureStream();
output.srcObject = stream; output.srcObject = stream;
source.mozPictureInPicture = true;
output.play().catch(error => { output.play().catch(error => {
Components.utils.reportError(error); Components.utils.reportError(error);
closePlayer(); closePlayer();
@ -161,6 +166,7 @@ addMessageListener("PictureInPicture:Command", message => {
break; break;
case "mute": case "mute":
source.muted = !source.muted; source.muted = !source.muted;
update();
break; break;
case "seek": case "seek":
seekTo(message.data.time); seekTo(message.data.time);

View file

@ -2404,6 +2404,12 @@ void HTMLMediaElement::SetVolumeInternal()
float effectiveVolume = ComputedVolume(); float effectiveVolume = ComputedVolume();
if (mDecoder) { if (mDecoder) {
// PiP applies content mute at its playback element, after the capture
// queue. Keep those samples available for immediate unmute, while still
// honoring audio-channel, playback-rate and disabled-track muting.
if (mPictureInPicture && !(mMuted & ~MUTED_BY_CONTENT)) {
effectiveVolume = float(mVolume * mAudioChannelVolume);
}
mDecoder->SetVolume(effectiveVolume); mDecoder->SetVolume(effectiveVolume);
} else if (MediaStream* stream = GetSrcMediaStream()) { } else if (MediaStream* stream = GetSrcMediaStream()) {
if (mSrcStreamIsPlaying) { if (mSrcStreamIsPlaying) {
@ -5188,6 +5194,7 @@ void HTMLMediaElement::SetMozPictureInPicture(bool aEnabled)
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
mPictureInPicture = aEnabled; mPictureInPicture = aEnabled;
if (mDecoder && !mShuttingDown) { if (mDecoder && !mShuttingDown) {
SetVolumeInternal();
mDecoder->NotifyOwnerActivityChanged(!IsHidden()); mDecoder->NotifyOwnerActivityChanged(!IsHidden());
} }
} }

View file

@ -3207,6 +3207,12 @@ NS_IMETHODIMP nsWindow::HideWindowChrome(bool aShouldHide)
::SetWindowLongPtrW(hwnd, GWL_STYLE, style); ::SetWindowLongPtrW(hwnd, GWL_STYLE, style);
::SetWindowLongPtrW(hwnd, GWL_EXSTYLE, exStyle); ::SetWindowLongPtrW(hwnd, GWL_EXSTYLE, exStyle);
// Apply the new non-client metrics immediately. Otherwise the client area
// keeps its old titlebar/border dimensions until the first move or resize.
::SetWindowPos(hwnd, nullptr, 0, 0, 0, 0,
SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE |
SWP_NOZORDER | SWP_NOACTIVATE);
return NS_OK; return NS_OK;
} }