Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat. better handling of media keys in browser #4386

Merged
merged 9 commits into from
Oct 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/docs/docs/player/TableOfContents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ export const PlayerGuide: React.FC = () => {
<strong>Custom controls</strong>
<div>Recipes for custom Play buttons, volume sliders, etc.</div>
</TOCItem>
<TOCItem link="/docs/player/media-keys">
<strong>Media Keys</strong>
<div>Control what happens when users presses ⏯️</div>
</TOCItem>
</Grid>
</div>
);
Expand Down
5 changes: 5 additions & 0 deletions packages/docs/docs/player/api.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,11 @@ This allows you to flexibly implement custom UI for the buffer state.

Makes the Player render things outside of the canvas. Useful if you have interactive elements in the video such as draggable elements.

### `browserMediaControlsBehavior`<AvailableFrom v="4.0.221" />

Controls what happens when the user presses the Play/Pause button on their keyboard or uses other controls such as Chromes built-in controls.
See [Media Keys Behavior](/docs/player/media-keys) for more information.

## `PlayerRef`

You may attach a ref to the player and control it in an imperative manner.
Expand Down
133 changes: 133 additions & 0 deletions packages/docs/docs/player/media-keys.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
---
image: /generated/articles-docs-player-media-keys.png
id: media-keys
sidebar_label: 'Media Keys'
title: 'Media Key Behavior (Web MediaSession API)'
crumb: '@remotion/player'
---

# Media Keys Behavior<AvailableFrom v="4.0.221" />

This document is about the behavior when a user:

- Presses the Play/Pause (⏯️) or Previous track (⏪) button on their keyboard
- Uses other controls such as Chromes built-in controls next to the user avatar to control the playback

These behaviors are controlled by the [`browserMediaControlsBehavior`](/docs/player/player#browsermediacontrolsbehavior) prop.
The underlying Web API used is the [Media Session API](https://developer.mozilla.org/en-US/docs/Web/API/Media_Session_API).

## Modes

### `prevent-media-session` (default)

```tsx twoslash
const otherProps = {
compositionHeight: 720,
compositionWidth: 1280,
inputProps: {},
durationInFrames: 120,
fps: 30,
component: () => null,
};
// ---cut---

import {Player} from '@remotion/player';

export const MyComp: React.FC = () => {
return (
<Player
browserMediaControlsBehavior={{
mode: 'prevent-media-session',
}}
{...otherProps}
/>
);
};
```

In this mode, Remotion will not act on the user's media keys, but map any keybord action to a no-op.
This prevents that the Player is paused but the audio tags get resumed by pressing the Play/Pause button on the keyboard, which was a problem prior to Remotion v4.0.221.

### `register-media-session`

```tsx twoslash
const otherProps = {
compositionHeight: 720,
compositionWidth: 1280,
inputProps: {},
durationInFrames: 120,
fps: 30,
component: () => null,
};
// ---cut---

import {Player} from '@remotion/player';

export const MyComp: React.FC = () => {
return (
<Player
browserMediaControlsBehavior={{
mode: 'register-media-session',
}}
{...otherProps}
/>
);
};
```

In this mode, Remotion will use the Media Session API to register handlers:

- When the user presses the Play/Pause button on the keyboard
- Toggle the Remotion Player's state
- When the user presses the Previous track button on the keyboard
- Seek to the beginning of the video
- When the user presses the Fast Forward button on the keyboard
- Seek 10 seconds forward
- When the user presses the Rewind button on the keyboard
- Seek 10 seconds backward

Also, Remotion will react to seeking events and inform the device about the current playback position and duration.

### `do-nothing`

```tsx twoslash
const otherProps = {
compositionHeight: 720,
compositionWidth: 1280,
inputProps: {},
durationInFrames: 120,
fps: 30,
component: () => null,
};
// ---cut---

import {Player} from '@remotion/player';

export const MyComp: React.FC = () => {
return (
<Player
browserMediaControlsBehavior={{
mode: 'do-nothing',
}}
{...otherProps}
/>
);
};
```

Reverts to the behavior prior to Remotion v4.0.221.
Remotion will not react to any media keys, leaving the browser to handle the media keys.
This leads to the problem that the user can resume any media tag by pressing the Play/Pause button on the keyboard, without the Remotion Player also resuming.

## When using multiple `<Player>`'s

Remotion's `register-media-session` handler is supposed to only work with 1 Player mounted.
It is not defined which Player reacts to the media keys.

When working with multiple Players, set one to `do-nothing` mode and the other to `register-media-session` mode to explicitly set the Media Keys for only 1 Player.

If you want all Players to react to the media keys, you need to use `do-nothing` mode and implement this behavior yourself with the Player API.

## In the Remotion Studio

The behavior is set to `register-media-session` as of v4.0.221 and previously it behaved like `do-nothing`.
1 change: 1 addition & 0 deletions packages/docs/sidebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -739,6 +739,7 @@ module.exports = {
'player/premounting',
'player/best-practices',
'player/custom-controls',
'player/media-keys',
],
},

Expand Down
7 changes: 7 additions & 0 deletions packages/docs/src/data/articles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3737,6 +3737,13 @@ export const articles = [
compId: 'articles-docs-player-custom-controls',
crumb: '@remotion/player',
},
{
id: 'media-keys',
title: 'Media Key Behavior (Web MediaSession API)',
relativePath: 'docs/player/media-keys.mdx',
compId: 'articles-docs-player-media-keys',
crumb: '@remotion/player',
},
{
id: 'player/index',
title: '@remotion/player',
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 13 additions & 0 deletions packages/player/src/Player.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import type {
import type {PosterFillMode, RenderLoading, RenderPoster} from './PlayerUI.js';
import PlayerUI from './PlayerUI.js';
import {PLAYER_COMP_ID, SharedPlayerContexts} from './SharedPlayerContext.js';
import type {BrowserMediaControlsBehavior} from './browser-mediasession.js';
import {PLAYER_CSS_CLASSNAME} from './player-css-classname.js';
import type {PlayerRef} from './player-methods.js';
import type {RenderVolumeSlider} from './render-volume-slider.js';
Expand Down Expand Up @@ -85,6 +86,7 @@ export type PlayerProps<
readonly bufferStateDelayInMilliseconds?: number;
readonly hideControlsWhenPointerDoesntMove?: boolean | number;
readonly overflowVisible?: boolean;
readonly browserMediaControlsBehavior?: BrowserMediaControlsBehavior;
} & CompProps<Props> &
PropsIfHasProps<Schema, Props>;

Expand Down Expand Up @@ -146,6 +148,7 @@ const PlayerFn = <
hideControlsWhenPointerDoesntMove = true,
overflowVisible = false,
renderMuteButton,
browserMediaControlsBehavior: passedBrowserMediaControlsBehavior,
...componentProps
}: PlayerProps<Schema, Props>,
ref: MutableRefObject<PlayerRef>,
Expand Down Expand Up @@ -348,6 +351,15 @@ const PlayerFn = <

const actualInputProps = useMemo(() => inputProps ?? {}, [inputProps]);

const browserMediaControlsBehavior: BrowserMediaControlsBehavior =
useMemo(() => {
return (
passedBrowserMediaControlsBehavior ?? {
mode: 'prevent-media-session',
}
);
}, [passedBrowserMediaControlsBehavior]);

return (
<Internals.IsPlayerContextProvider>
<SharedPlayerContexts
Expand Down Expand Up @@ -407,6 +419,7 @@ const PlayerFn = <
hideControlsWhenPointerDoesntMove
}
overflowVisible={overflowVisible}
browserMediaControlsBehavior={browserMediaControlsBehavior}
/>
</PlayerEmitterProvider>
</Internals.Timeline.SetTimelineContext.Provider>
Expand Down
4 changes: 4 additions & 0 deletions packages/player/src/PlayerUI.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import type {
RenderPlayPauseButton,
} from './PlayerControls.js';
import {Controls} from './PlayerControls.js';
import type {BrowserMediaControlsBehavior} from './browser-mediasession.js';
import {
calculateCanvasTransformation,
calculateContainerStyle,
Expand Down Expand Up @@ -87,6 +88,7 @@ const PlayerUI: React.ForwardRefRenderFunction<
readonly bufferStateDelayInMilliseconds: number;
readonly hideControlsWhenPointerDoesntMove: boolean | number;
readonly overflowVisible: boolean;
readonly browserMediaControlsBehavior: BrowserMediaControlsBehavior;
}
> = (
{
Expand Down Expand Up @@ -123,6 +125,7 @@ const PlayerUI: React.ForwardRefRenderFunction<
bufferStateDelayInMilliseconds,
hideControlsWhenPointerDoesntMove,
overflowVisible,
browserMediaControlsBehavior,
},
ref,
) => {
Expand Down Expand Up @@ -159,6 +162,7 @@ const PlayerUI: React.ForwardRefRenderFunction<
inFrame,
outFrame,
frameRef: player.remotionInternal_currentFrameRef,
browserMediaControlsBehavior,
});

useEffect(() => {
Expand Down
Loading
Loading