feat(web): implement web pip method and event (#4370)

Co-authored-by: Olivier Bouillet <62574056+freeboub@users.noreply.github.com>
This commit is contained in:
YangJH 2025-01-18 23:19:55 +09:00 committed by GitHub
parent 449dfb62b5
commit 8dc10fd4b7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 54 additions and 5 deletions

View File

@ -317,7 +317,7 @@ Example:
### `onPictureInPictureStatusChanged`
<PlatformsList types={['iOS', 'Android']} />
<PlatformsList types={['iOS', 'Android', 'web']} />
Callback function that is called when picture in picture becomes active or inactive.

View File

@ -80,7 +80,7 @@ Future:
### `enterPictureInPicture`
<PlatformsList types={['Android', 'iOS']} />
<PlatformsList types={['Android', 'iOS', 'web']} />
`enterPictureInPicture()`
@ -114,7 +114,7 @@ NOTE: Video ads cannot start when you are using the PIP on iOS (more info availa
### `exitPictureInPicture`
<PlatformsList types={['Android', 'iOS']} />
<PlatformsList types={['Android', 'iOS', 'web']} />
`exitPictureInPicture()`

View File

@ -50,6 +50,7 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
onVolumeChange,
onEnd,
onPlaybackStateChanged,
onPictureInPictureStatusChanged,
},
ref,
) => {
@ -180,6 +181,31 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
[setFullScreen],
);
const enterPictureInPicture = useCallback(() => {
try {
if (!nativeRef.current) {
console.error('Video Component is not mounted');
} else {
nativeRef.current.requestPictureInPicture();
}
} catch (e) {
console.error(e);
}
}, []);
const exitPictureInPicture = useCallback(() => {
if (
nativeRef.current &&
nativeRef.current === document.pictureInPictureElement
) {
try {
document.exitPictureInPicture();
} catch (e) {
console.error(e);
}
}
}, []);
useImperativeHandle(
ref,
() => ({
@ -193,8 +219,8 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
dismissFullscreenPlayer,
setFullScreen,
save: unsupported,
enterPictureInPicture: unsupported,
exitPictureInPicture: unsupported,
enterPictureInPicture,
exitPictureInPicture,
restoreUserInterfaceForPictureInPictureStopCompleted: unsupported,
nativeHtmlVideoRef: nativeRef,
}),
@ -210,6 +236,8 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
presentFullscreenPlayer,
dismissFullscreenPlayer,
setFullScreen,
enterPictureInPicture,
exitPictureInPicture,
],
);
@ -254,6 +282,27 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
nativeRef.current.playbackRate = rate;
}, [rate]);
useEffect(() => {
if (
typeof onPictureInPictureStatusChanged !== 'function' ||
!nativeRef.current
) {
return;
}
const onEnterPip = () =>
onPictureInPictureStatusChanged({isActive: true});
const onLeavePip = () =>
onPictureInPictureStatusChanged({isActive: false});
const video = nativeRef.current;
video.addEventListener('enterpictureinpicture', onEnterPip);
video.addEventListener('leavepictureinpicture', onLeavePip);
return () => {
video.removeEventListener('enterpictureinpicture', onEnterPip);
video.removeEventListener('leavepictureinpicture', onLeavePip);
};
}, [onPictureInPictureStatusChanged]);
useMediaSession(src?.metadata, nativeRef, showNotificationControls);
return (