diff --git a/src/components/MediaPlayer/MediaPlayer.js b/src/components/MediaPlayer/MediaPlayer.js
index 271a2b5b..698f8927 100644
--- a/src/components/MediaPlayer/MediaPlayer.js
+++ b/src/components/MediaPlayer/MediaPlayer.js
@@ -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();
@@ -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.
@@ -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'
@@ -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;
diff --git a/src/components/MediaPlayer/MediaPlayer.md b/src/components/MediaPlayer/MediaPlayer.md
index 0a25d78a..9243e472 100644
--- a/src/components/MediaPlayer/MediaPlayer.md
+++ b/src/components/MediaPlayer/MediaPlayer.md
@@ -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
diff --git a/src/components/MediaPlayer/MediaPlayer.test.js b/src/components/MediaPlayer/MediaPlayer.test.js
index 89518fac..f4a9e973 100644
--- a/src/components/MediaPlayer/MediaPlayer.test.js
+++ b/src/components/MediaPlayer/MediaPlayer.test.js
@@ -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(
+
+
+
+ );
+ 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(
+
+
+
+ ));
+ expect(screen.queryByTestId('videojs-file-download')).toBeInTheDocument();
});
- render(
-
-
-
- );
- 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(
+
+
+
+ ));
+ 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(
+
+
+
+ ));
+ 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(
+
+
+
+ ));
+ 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(
+
+
+
+ ));
+ expect(screen.queryByTitle('Playback Rate')).toBeInTheDocument();
});
- await act(async () => render(
-
-
-
- ));
- expect(screen.queryByTestId('videojs-file-download')).toBeInTheDocument();
});
});
diff --git a/src/components/MediaPlayer/VideoJS/components/styles/VideoJSFileDownload.scss b/src/components/MediaPlayer/VideoJS/components/styles/VideoJSFileDownload.scss
index 49632518..39e96ed4 100644
--- a/src/components/MediaPlayer/VideoJS/components/styles/VideoJSFileDownload.scss
+++ b/src/components/MediaPlayer/VideoJS/components/styles/VideoJSFileDownload.scss
@@ -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,
");
background-repeat: no-repeat;
diff --git a/src/styles/main.scss b/src/styles/main.scss
index dc897308..8ae129cf 100644
--- a/src/styles/main.scss
+++ b/src/styles/main.scss
@@ -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%;