Skip to content

Commit

Permalink
Add playback rate support for media player (#477)
Browse files Browse the repository at this point in the history
* Add playback rate support for media player

* Doc update from code review
  • Loading branch information
Dananji authored Apr 25, 2024
1 parent 9f24f0e commit 59cf7c7
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 40 deletions.
2 changes: 1 addition & 1 deletion demo/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ const App = ({ manifestURL }) => {
manifestUrl={manifestUrl}
>
<div className="iiif-player-demo">
<MediaPlayer enableFileDownload={true} />
<MediaPlayer enableFileDownload={true} enablePlaybackRate={true} />
<div className="components-row">
<div className="nav">
<AutoAdvanceToggle />
Expand Down
11 changes: 9 additions & 2 deletions src/components/MediaPlayer/MediaPlayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ import { IS_ANDROID, IS_MOBILE, IS_SAFARI, IS_TOUCH_ONLY } from '@Services/brows

const PLAYER_ID = "iiif-media-player";

const MediaPlayer = ({ enableFileDownload = false, enablePIP = false }) => {
const MediaPlayer = ({
enableFileDownload = false,
enablePIP = false,
enablePlaybackRate = false,
}) => {
const manifestState = useManifestState();
const playerState = usePlayerState();
const playerDispatch = usePlayerDispatch();
Expand Down Expand Up @@ -311,6 +315,7 @@ const MediaPlayer = ({ enableFileDownload = false, enablePIP = false }) => {
autoplay: false,
bigPlayButton: isVideo,
id: PLAYER_ID,
playbackRates: enablePlaybackRate ? [0.5, 0.75, 1, 1.5, 2] : [],
// Setting inactivity timeout to zero in mobile and tablet devices translates to
// user is always active. And the control bar is not hidden when user is active.
// With this user can always use the controls when the media is playing.
Expand All @@ -335,6 +340,7 @@ const MediaPlayer = ({ enableFileDownload = false, enablePIP = false }) => {
(playerConfig.tracks.length > 0 && isVideo) ? 'subsCapsButton' : '',
IS_MOBILE ? 'muteToggle' : 'volumePanel',
'qualitySelector',
enablePlaybackRate ? 'playbackRateMenuButton' : '',
enablePIP ? 'pictureInPictureToggle' : '',
enableFileDownload ? 'videoJSFileDownload' : '',
'fullscreenToggle'
Expand Down Expand Up @@ -444,7 +450,8 @@ const MediaPlayer = ({ enableFileDownload = false, enablePIP = false }) => {

MediaPlayer.propTypes = {
enableFileDownload: PropTypes.bool,
enablePIP: PropTypes.bool
enablePIP: PropTypes.bool,
enablePlaybackRate: PropTypes.bool,
};

export default MediaPlayer;
5 changes: 3 additions & 2 deletions src/components/MediaPlayer/MediaPlayer.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ MediaPlayer component provides a player that facilitates both audio and video me

`MediaPlayer` component accepts the following props;

- `enableFileDownload` : accepts a Boolean value, which has a default value of `false` and is not required. Once this is set to `true` it adds an icon to the player's toolbar to display `rendering` files in the Canvas and enables downloading them. This is a custom feature added to the VideoJS instance in Ramp.
- `enablePIP` : accepts a Boolean value, which has a default value of `false` and is not required. When this is set to `true`, it adds an icon to the player's toolbar to enable Picture-In-Picture feature for the current player. This icon is a VideoJS feature.
- `enableFileDownload` : accepts a Boolean value, which has a default value of `false` and is _not required_. Once this is set to `true` it adds an icon to the player's control bar to display `rendering` files in the Canvas and enables downloading them. This is a custom VideoJS componend added to the VideoJS instance in Ramp.
- `enablePIP` : accepts a Boolean value, which has a default value of `false` and is _not required_. When this is set to `true`, it adds an icon to the player's control bar to enable Picture-In-Picture feature for the current player. This icon is a VideoJS component.
- `enablePlaybackRate`: accepts a Boolean value, which has a default value of `false` and is _not required_. When this is set to `true`, it adds an icon to the player's control bar which provides a menu to select a different playback speed for the media. The available speed options are 0.5x, 0.75x, 1x, 1.5x, and 2x. This icon is a VideoJS component.

To import and use this component from the library;
```js static
Expand Down
102 changes: 80 additions & 22 deletions src/components/MediaPlayer/MediaPlayer.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,32 +91,90 @@ describe('MediaPlayer component', () => {
});

describe('with props', () => {
test('enableFileDownload = false', () => {
const PlayerWithManifest = withManifestAndPlayerProvider(MediaPlayer, {
initialManifestState: { ...manifestState, manifest: videoManifest, canvasIndex: 0 },
initialPlayerState: {},
enableFileDownload: false,
describe('enableFileDownload', () => {
test('with default value: `false`', () => {
const PlayerWithManifest = withManifestAndPlayerProvider(MediaPlayer, {
initialManifestState: { ...manifestState, manifest: videoManifest, canvasIndex: 0 },
initialPlayerState: {},
enableFileDownload: false,
});
render(
<ErrorBoundary>
<PlayerWithManifest />
</ErrorBoundary>
);
expect(screen.queryByTestId('videojs-file-download')).not.toBeInTheDocument();
});

test('set to `true`', async () => {
const PlayerWithManifest = withManifestAndPlayerProvider(MediaPlayer, {
initialManifestState: { ...manifestState, manifest: videoManifest, canvasIndex: 0 },
initialPlayerState: {},
enableFileDownload: true,
});
await act(async () => render(
<ErrorBoundary>
<PlayerWithManifest />
</ErrorBoundary>
));
expect(screen.queryByTestId('videojs-file-download')).toBeInTheDocument();
});
render(
<ErrorBoundary>
<PlayerWithManifest />
</ErrorBoundary>
);
expect(screen.queryByTestId('videojs-file-download')).not.toBeInTheDocument();
});

test('enableFileDownload = true', async () => {
const PlayerWithManifest = withManifestAndPlayerProvider(MediaPlayer, {
initialManifestState: { ...manifestState, manifest: videoManifest, canvasIndex: 0 },
initialPlayerState: {},
enableFileDownload: true,
describe('enablePIP', () => {
test('with default value: `false`', async () => {
const PlayerWithManifest = withManifestAndPlayerProvider(MediaPlayer, {
initialManifestState: { ...manifestState, manifest: videoManifest, canvasIndex: 0 },
initialPlayerState: {},
});
await act(async () => render(
<ErrorBoundary>
<PlayerWithManifest />
</ErrorBoundary>
));
expect(screen.queryByTitle('Picture-in-Picture')).not.toBeInTheDocument();
});
test('set to `true`', async () => {
const PlayerWithManifest = withManifestAndPlayerProvider(MediaPlayer, {
initialManifestState: { ...manifestState, manifest: videoManifest, canvasIndex: 0 },
initialPlayerState: {},
enablePIP: true,
});
await act(async () => render(
<ErrorBoundary>
<PlayerWithManifest />
</ErrorBoundary>
));
expect(screen.queryByTitle('Picture-in-Picture')).toBeInTheDocument();
});
});

describe('enablePlaybackRate', () => {
test('with default value: `false`', async () => {
const PlayerWithManifest = withManifestAndPlayerProvider(MediaPlayer, {
initialManifestState: { ...manifestState, manifest: videoManifest, canvasIndex: 0 },
initialPlayerState: {},
});
await act(async () => render(
<ErrorBoundary>
<PlayerWithManifest />
</ErrorBoundary>
));
expect(screen.queryByTitle('Playback Rate')).not.toBeInTheDocument();
});
test('set to `true`', async () => {
const PlayerWithManifest = withManifestAndPlayerProvider(MediaPlayer, {
initialManifestState: { ...manifestState, manifest: videoManifest, canvasIndex: 0 },
initialPlayerState: {},
enablePlaybackRate: true,
});
await act(async () => render(
<ErrorBoundary>
<PlayerWithManifest />
</ErrorBoundary>
));
expect(screen.queryByTitle('Playback Rate')).toBeInTheDocument();
});
await act(async () => render(
<ErrorBoundary>
<PlayerWithManifest />
</ErrorBoundary>
));
expect(screen.queryByTestId('videojs-file-download')).toBeInTheDocument();
});
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,5 @@
@import '../../../../../styles/vars';

.vjs-download-btn {
color: white;
border: none;
cursor: pointer;
width: 2rem;
height: 2rem;
}

.vjs-file-download {
position: relative;
display: inline-block;
}

.vjs-file-download-icon {
background-image: url("data:image/svg+xml;utf8, <svg version='1.1' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 330 330' fill='white'><g id='XMLID_23_'><path id='XMLID_24_' d='M154.389,255.602c0.351,0.351,0.719,0.683,1.103,0.998c0.169,0.138,0.347,0.258,0.52,0.388 c0.218,0.164,0.432,0.333,0.659,0.484c0.212,0.142,0.432,0.265,0.649,0.395c0.202,0.121,0.4,0.248,0.608,0.359 c0.223,0.12,0.453,0.221,0.681,0.328c0.215,0.102,0.427,0.21,0.648,0.301c0.223,0.092,0.45,0.167,0.676,0.247 c0.236,0.085,0.468,0.175,0.709,0.248c0.226,0.068,0.456,0.119,0.684,0.176c0.246,0.062,0.489,0.131,0.739,0.181 c0.263,0.052,0.529,0.083,0.794,0.121c0.219,0.031,0.435,0.073,0.658,0.095c0.492,0.048,0.986,0.075,1.48,0.075 c0.494,0,0.988-0.026,1.479-0.075c0.226-0.022,0.444-0.064,0.667-0.096c0.262-0.037,0.524-0.068,0.784-0.12 c0.255-0.05,0.504-0.121,0.754-0.184c0.223-0.057,0.448-0.105,0.669-0.172c0.246-0.075,0.483-0.167,0.724-0.253 c0.221-0.08,0.444-0.152,0.662-0.242c0.225-0.093,0.44-0.202,0.659-0.306c0.225-0.106,0.452-0.206,0.672-0.324 c0.21-0.112,0.408-0.239,0.611-0.361c0.217-0.13,0.437-0.252,0.648-0.394c0.222-0.148,0.431-0.314,0.644-0.473 c0.179-0.134,0.362-0.258,0.536-0.4c0.365-0.3,0.714-0.617,1.049-0.949c0.016-0.016,0.034-0.028,0.049-0.044l70.002-69.998 c5.858-5.858,5.858-15.355,0-21.213c-5.857-5.857-15.355-5.858-21.213-0.001l-44.396,44.393V25c0-8.284-6.716-15-15-15 c-8.284,0-15,6.716-15,15v183.785l-44.392-44.391c-5.857-5.858-15.355-5.858-21.213,0c-5.858,5.858-5.858,15.355,0,21.213 L154.389,255.602z'/><path id='XMLID_25_' d='M315,160c-8.284,0-15,6.716-15,15v115H30V175c0-8.284-6.716-15-15-15c-8.284,0-15,6.716-15,15v130 c0,8.284,6.716,15,15,15h300c8.284,0,15-6.716,15-15V175C330,166.716,323.284,160,315,160z'/></g></svg>");
background-repeat: no-repeat;
Expand Down
4 changes: 4 additions & 0 deletions src/styles/main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,10 @@ video/poster area the controls are displayed correctly. */
display: block !important;
}

.vjs-playback-rate-value {
scale: 0.75 !important;
}

/* big-play button */
.video-js .vjs-big-play-button {
border-radius: 50%;
Expand Down

0 comments on commit 59cf7c7

Please sign in to comment.