Skip to content

Commit

Permalink
feat!: replace Mux player w/ Media Chrome + media (#303)
Browse files Browse the repository at this point in the history
* feat: replace Mux player w/ Media Chrome + media

* fix: slotted poster image

* fix: upgrade pkgs

* fix: add custom theme prop and example

* fix: upgrade Media Chrome

* fix: use Sutro as default

* docs: add default player docs

* fix: add hls-video, dash-video. src adapting types

* fix: test fixture

* docs: update readme

* site: fix active link

* docs: move change player theme to guides section

* docs: make more clear it's on the Video comp

* fix: use @mux/mux-video/react pkg
  • Loading branch information
luwes authored Dec 20, 2024
1 parent ed0ab4f commit 439d96a
Show file tree
Hide file tree
Showing 21 changed files with 713 additions and 235 deletions.
72 changes: 72 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,22 @@ export default function Page() {
```
</details>


### Change player theme ([Demo](https://next-video-demo.vercel.app/custom-theme))

You can change the player theme by passing the `theme` prop to the `<Video>` component.
See [player.style](https://player.style/) for more themes.

```tsx
import Video from 'next-video';
import Instaplay from 'player.style/instaplay/react';
import awesomeVideo from '/videos/awesome-video.mp4';

export default function Page() {
return <Video src={awesomeVideo} theme={Instaplay} />;
}
```

### Player only ([Demo](https://next-video-demo.vercel.app/player-only))

Since [`v1.1.0`](https://github.com/muxinc/next-video/releases/tag/v1.1.0) you can import the player component directly and use it without any upload and processing features.
Expand Down Expand Up @@ -483,6 +499,62 @@ export { handler as default } from '@/next-video';
```


## Default Player

The default player is built with [Media Chrome](https://github.com/muxinc/media-chrome).

- The default theme is [Sutro](https://player.style/themes/sutro) by Mux.
- The video engine changes automatically based on the source format:
- Video files (like MP4, MP3, WEBM) that are progressively downloaded are played with the native `<video>` element.
- Mux videos are played with [`<mux-video>`](https://github.com/muxinc/elements/tree/main/packages/mux-video).
- HLS streams are played with [`<hls-video>`](https://github.com/muxinc/media-elements/tree/main/packages/hls-video-element).
- DASH streams are played with [`<dash-video>`](https://github.com/muxinc/media-elements/tree/main/packages/dash-video-element).

### Props

The `<Video>` component accepts all the props of the `<video>` element and the following additional props:

- `src` (Asset | string): The video asset object (import) or source URL.
- `poster` (StaticImageData | string): A placeholder image for the video. (Auto generated for Mux videos)
- `blurDataURL` (string): A base64 image source URL that can be used as a placeholder. (Auto generated for Mux videos)
- `theme` (React Component): The player theme component. See [player.style](https://player.style/) for more themes.
- `as` (React Component): A custom player component. See [Custom player](#custom-player-demo).
- `transform` (function): A custom function to transform the asset object (src and poster).
- `loader` (function): A custom function used to resolve string based video URLs (not imports).

#### Mux video props

The `<Video>` component with a Mux video source accepts the following additional props:

- `startTime` (number): The start time of the video in seconds.
- `streamType` ("on-demand" | "live"): The stream type of the video. Default is "on-demand".
- `customDomain` (string): Assigns a custom domain to be used for Mux Video.
- `beaconCollectionDomain` (string): Assigns a custom domain to be used for Mux Data collection. NOTE: Must be set before playbackId to apply to Mux Data monitoring.
- `envKey` (string): This is the environment key for Mux Data. If you use Mux video this is automatically
set for you. If you use a different provider you can set this to your own key.
- `disableTracking` (boolean): Disables Mux data tracking of video playback.
- `disableCookies` (boolean): Disables cookies used by Mux Data.
- `preferPlayback` ("mse" | "native"): Specify if `<mux-video>` should try to use Media Source Extension or native playback (if available). If no value is provided, `<mux-video>` will choose based on what's deemed optimal for content and playback environment.
- `maxResolution` ("720p" | "1080p" | "1440p" | "2160p"): Specify the maximum resolution you want delivered for this video.
- `minResolution` ("480p" | "540p" | "720p" | "1080p" | "1440p" | "2160p"): Specify the minimum resolution you want delivered for this video.
- `programStartTime` (number): Apply PDT-based [instant clips](https://docs.mux.com/guides/create-instant-clips) to the beginning of the media stream.
- `programEndTime` (number): Apply PDT-based [instant clips](https://docs.mux.com/guides/create-instant-clips) to the end of the media stream.
- `assetStartTime` (number): Apply media timeline-based [instant clips](https://docs.mux.com/guides/create-instant-clips) to the beginning of the media stream.
- `assetEndTime` (number): Apply media timeline-based [instant clips](https://docs.mux.com/guides/create-instant-clips) to the end of the media stream.
- `renditionOrder` (string): Change the order in which renditions are provided in the src playlist. Can impact initial segment loads. Currently only support "desc" for descending order.
- `metadataVideoId` (string): This is an arbitrary ID sent to Mux Data that should map back to a record of this video in your database.
- `metadataTitle` (string): This is an arbitrary title for your video that will be passed in as metadata into Mux Data. Adding a title will give you useful context in your Mux Data dashboard. (optional, but encouraged)
- `metadataViewerUserId` (string): If you have a logged-in user, this should be an anonymized ID value that maps back to the user in your database that will be sent to Mux Data. Take care to not expose personal identifiable information like names, usernames or email addresses. (optional, but encouraged)
- `metadata*` (string): This metadata* syntax can be used to pass any arbitrary Mux Data metadata fields.
- `playbackToken` (string): The playback token for signing the `src` URL.
- `thumbnailToken` (string): The token for signing the `poster` URL.
- `storyboardToken` (string): The token for signing the storyboard URL.
- `drmToken` (string): The token for signing DRM license and related URLs.
- `targetLiveWindow` (number): An offset representing the seekable range for live content, where `Infinity` means the entire live content is seekable (aka "standard DVR"). Used along with `streamType` to determine what UI/controls to show.
- `liveEdgeOffset` (number): The earliest playback time that will be treated as playing "at the live edge" for live content.
- `debug` (boolean): Enables debug mode for the underlying playback engine (currently hls.js) and mux-embed, providing additional information in the console.


## Required Permissions for Amazon S3

<details>
Expand Down
20 changes: 20 additions & 0 deletions examples/default-provider/app/(default)/custom-theme/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Metadata } from 'next';
import Video from 'next-video';
import Instaplay from 'player.style/instaplay/react';
import getStarted from '/videos/get-started.mp4?thumbnailTime=0';

export const metadata: Metadata = {
title: 'next-video - Custom theme',
};

export default function Page() {
return (
<>
<section>
<Video src={getStarted} theme={Instaplay}>
<track kind="captions" src="/get-started.vtt" srcLang="en" label="English" default />
</Video>
</section>
</>
);
}
19 changes: 19 additions & 0 deletions examples/default-provider/app/(default)/dash-source/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Metadata } from 'next';
import Player from 'next-video/player';

export const metadata: Metadata = {
title: 'next-video - DASH source',
};

export default function Page() {
return (
<>
<section>
<Player
style={{ display: 'grid', width: '100%', aspectRatio: '16/9' }}
src="https://player.vimeo.com/external/648359100.mpd?s=a4419a2e2113cc24a87aef2f93ef69a8e4c8fb0c"
/>
</section>
</>
);
}
19 changes: 19 additions & 0 deletions examples/default-provider/app/(default)/hls-source/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Metadata } from 'next';
import Player from 'next-video/player';

export const metadata: Metadata = {
title: 'next-video - HLS source',
};

export default function Page() {
return (
<>
<section>
<Player
style={{ display: 'grid', width: '100%', aspectRatio: '16/9' }}
src="https://stream.mux.com/sxY31L6Opl02RWPpm3Gro9XTe7fRHBjs92x93kiB1vpc.m3u8"
/>
</section>
</>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ import { Metadata } from 'next';
import Player from 'next-video/player';

export const metadata: Metadata = {
title: 'next-video - Player only',
title: 'next-video - MP4 Source',
};

export default function Page() {
return (
<>
<section>
<Player
style={{ display: 'grid', width: '100%', aspectRatio: '16/9' }}
style={{ width: '100%', aspectRatio: '16/9' }}
src="https://storage.googleapis.com/muxdemofiles/mux.mp4"
poster="https://image.mux.com/jxEf6XiJs6JY017pSzpv8Hd6tTbdAOecHTq4FiFAn564/thumbnail.webp"
blurDataURL='data:image/webp;base64,UklGRlAAAABXRUJQVlA4IEQAAACwAQCdASoQAAkAAQAcJZwAAueBHFYwAP7+sPJ01xp5AM+XuhDsRQ67ZYXXhHDkrqsIkUGjQSCMuENc5y3Qg0o9pZgAAA=='
Expand Down
2 changes: 1 addition & 1 deletion examples/default-provider/app/(default)/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Metadata } from 'next';
import Video from 'next-video';
import getStarted from '/videos/get-started.mp4?thumbnailTime=35';
import getStarted from '/videos/get-started.mp4?thumbnailTime=37';

export const metadata: Metadata = {
title: 'next-video - Basic example',
Expand Down
17 changes: 16 additions & 1 deletion examples/default-provider/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,8 @@ aside nav li {
}

aside nav a {
display: flex;
display: grid;
grid-template-columns: 1.15rem auto auto;
align-items: center;
padding: .375rem var(--size-4) .375rem .75rem;
margin: 0;
Expand Down Expand Up @@ -191,6 +192,20 @@ aside nav a.active::before {
background-color: var(--text-1);
}

aside nav a span {
justify-self: end;
background: var(--yellow-0);
border: 1px solid var(--choco-1);
color: var(--choco-4);
font-family: var(--font-mono);
text-transform: uppercase;
font-size: .6em;
margin-left: 7px;
padding: .25em .7em;
white-space: nowrap;
word-spacing: -2px;
}

.theme-toggle {
transition: none;
box-shadow: none;
Expand Down
13 changes: 11 additions & 2 deletions examples/default-provider/app/sidebar-nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ export default function SidebarNav() {
<li>
<Link className={`link ${pathname === '/background-video' ? 'active' : ''}`} href="/background-video">Background video</Link>
</li>
<li>
<Link className={`link ${pathname === '/custom-theme' ? 'active' : ''}`} href="/custom-theme">Custom theme</Link>
</li>
<li>
<Link className={`link ${pathname === '/custom-player' ? 'active' : ''}`} href="/custom-player">Custom player</Link>
</li>
Expand All @@ -24,10 +27,16 @@ export default function SidebarNav() {
<Link className={`link ${pathname === '/string-source' ? 'active' : ''}`} href="/string-source">String video source</Link>
</li>
<li>
<Link className={`link ${pathname === '/player-only' ? 'active' : ''}`} href="/player-only">Player only</Link>
<Link className={`link ${pathname === '/hls-source' ? 'active' : ''}`} href="/hls-source">HLS source<span>player only</span></Link>
</li>
<li>
<Link className={`link ${pathname === '/dash-source' ? 'active' : ''}`} href="/dash-source">DASH source<span>player only</span></Link>
</li>
<li>
<Link className={`link ${pathname === '/mp4-source' ? 'active' : ''}`} href="/mp4-source">MP4 source<span>player only</span></Link>
</li>
<li>
<Link className={`link ${pathname === '/source-tag' ? 'active' : ''}`} href="/source-tag">Source tag</Link>
<Link className={`link ${pathname === '/source-tag' ? 'active' : ''}`} href="/source-tag">Source tag<span>player only</span></Link>
</li>
</ul>
</nav>
Expand Down
Loading

0 comments on commit 439d96a

Please sign in to comment.