зеркало из https://github.com/GoogleChrome/kino.git
Tweak casting and add a casting article.
This commit is contained in:
Родитель
de2543e2e2
Коммит
5c0ea9ae56
|
@ -545,11 +545,15 @@ main {
|
||||||
* Single Video
|
* Single Video
|
||||||
*/
|
*/
|
||||||
article {
|
article {
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
padding-bottom: calc(var(--gutter) * 3);
|
padding-bottom: calc(var(--gutter) * 3);
|
||||||
margin-bottom: calc(var(--gutter) * 3);
|
margin-bottom: calc(var(--gutter) * 3);
|
||||||
border-bottom: 1px solid var(--separator);
|
border-bottom: 1px solid var(--separator);
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
article .video-content {
|
article .video-content {
|
||||||
|
width: 100%;
|
||||||
max-width: 760px;
|
max-width: 760px;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
|
@ -613,16 +617,15 @@ h3[id*="example"]{
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
margin-bottom: calc(var(--gutter) / 2);
|
margin-bottom: calc(var(--gutter) / 2);
|
||||||
}
|
}
|
||||||
article pre,
|
article pre code,
|
||||||
.code-sample .code-sample--content {
|
.code-sample .code-sample--content {
|
||||||
max-width: 760px;
|
display: block;
|
||||||
white-space: pre-wrap;
|
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
padding: var(--gutter);
|
padding: var(--gutter);
|
||||||
border: 1px solid var(--code-border);
|
border: 1px solid var(--code-border);
|
||||||
background: var(--code-background);
|
background: var(--code-background);
|
||||||
overflow-y: auto;
|
overflow-x: auto;
|
||||||
}
|
}
|
||||||
.video-container {
|
.video-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
|
@ -8,7 +8,6 @@ length: '1:04'
|
||||||
video-sources:
|
video-sources:
|
||||||
- src: https://storage.googleapis.com/kino-assets/single-video/video.mp4
|
- src: https://storage.googleapis.com/kino-assets/single-video/video.mp4
|
||||||
type: video/mp4; codecs="avc1.640032,mp4a.40.2"
|
type: video/mp4; codecs="avc1.640032,mp4a.40.2"
|
||||||
cast: true
|
|
||||||
thumbnail: https://storage.googleapis.com/kino-assets/single-video/thumbnail.png
|
thumbnail: https://storage.googleapis.com/kino-assets/single-video/thumbnail.png
|
||||||
media-session-artwork:
|
media-session-artwork:
|
||||||
- sizes: 96x96
|
- sizes: 96x96
|
||||||
|
|
|
@ -7,6 +7,7 @@ length: '1:04'
|
||||||
video-sources:
|
video-sources:
|
||||||
- src: https://storage.googleapis.com/kino-assets/google-cast/manifest.mpd
|
- src: https://storage.googleapis.com/kino-assets/google-cast/manifest.mpd
|
||||||
type: application/dash+xml
|
type: application/dash+xml
|
||||||
|
cast: true
|
||||||
- src: https://storage.googleapis.com/kino-assets/google-cast/master.m3u8
|
- src: https://storage.googleapis.com/kino-assets/google-cast/master.m3u8
|
||||||
type: application/x-mpegURL
|
type: application/x-mpegURL
|
||||||
video-subtitles:
|
video-subtitles:
|
||||||
|
@ -45,4 +46,202 @@ cast: true
|
||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
|
|
||||||
Google Cast article WIP.
|
When one device controls media content playback on another device, we call it **casting**. These devices have to follow the same protocol in order to communicate. One of the most widely used casting protocols is Google Cast.
|
||||||
|
|
||||||
|
## Basic concepts
|
||||||
|
|
||||||
|
Before you can use Google Cast to stream your media to other devices, there are a few basic concepts you should understand first:
|
||||||
|
|
||||||
|
* **Sender application**: A mobile or web application that uses Google Cast SDK to discover target devices on the network and to initialize and remotely control playback of media being cast to receiver.
|
||||||
|
* **Receiver application**: A web application that runs on a Google Cast compatible device such as a Chromecast dongle or a Chromecast built-in TV or speaker and implements the media player UI and handles messages from the *Sender application*.
|
||||||
|
* **Google Cast SDK**: Collection of API libraries and code samples to allow Android, iOS and web developers to implement sender and receiver applications.
|
||||||
|
|
||||||
|
**Note:** Developing a custom Receiver application is optional in many use cases. Two prebuilt options are available:
|
||||||
|
|
||||||
|
* [Default Media Web Receiver] – predefined UI and colors, but you don't need to [register] a custom receiver application.
|
||||||
|
* [Styled Media Web Receiver] – ability to customize the UI and colors using CSS, but it is necessary to [register] a custom receiver application.
|
||||||
|
|
||||||
|
## Using the Cast Application Framework
|
||||||
|
|
||||||
|
The [Cast Application Framework] is the part of Google Cast SDK which exposes objects, methods and events necessary for building web sender applications.
|
||||||
|
|
||||||
|
```html
|
||||||
|
<script>
|
||||||
|
// 1. When CAF initializes, the `__onGCastApiAvailable` is called.
|
||||||
|
window.__onGCastApiAvailable = (isAvailable) => {
|
||||||
|
if (isAvailable) initCastApi();
|
||||||
|
};
|
||||||
|
|
||||||
|
function initCastApi() {
|
||||||
|
// 2. `CastContext` holds all global context for the CAF.
|
||||||
|
const context = cast.framework.CastContext.getInstance();
|
||||||
|
|
||||||
|
// 3. We use a default receiver app ID. If you want custom
|
||||||
|
// styling or features, you must register your own app.
|
||||||
|
//
|
||||||
|
// @see https://developers.google.com/cast/docs/registration
|
||||||
|
const receiverId = chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID;
|
||||||
|
|
||||||
|
context.setOptions({
|
||||||
|
receiverApplicationId: receiverId,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 4. A `session` is a connection to a target device. Whenever its
|
||||||
|
// state changes, we run the `castVideo` method.
|
||||||
|
context.addEventListener(
|
||||||
|
cast.framework.CastContextEventType.SESSION_STATE_CHANGED,
|
||||||
|
e => castVideo(e.sessionState),
|
||||||
|
);
|
||||||
|
|
||||||
|
const castVideo = asnyc (state) => {
|
||||||
|
// 5. When the `session` state change event is `SESSION_STARTED`,
|
||||||
|
// we try to load the video.
|
||||||
|
if (state === 'SESSION_STARTED') {
|
||||||
|
const session = context.getCurrentSession();
|
||||||
|
const mediaInfo = new chrome.cast.media.MediaInfo(
|
||||||
|
'http://localhost/video.mp4', // Provide a URL.
|
||||||
|
'video/mp4; codecs="avc1.640032,mp4a.40.2"', // MIME + codecs.
|
||||||
|
);
|
||||||
|
const request = new chrome.cast.media.LoadRequest(mediaInfo);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 6. Instruct the receiver application to load the media,
|
||||||
|
// if it fails, be prepared to handle the error.
|
||||||
|
await session.loadMedia(request);
|
||||||
|
} catch (error) { /* Handle the error. */ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script src="https://www.gstatic.com/cv/js/sender/v1/cast_sender.js?loadCastFramework=1"></script>
|
||||||
|
|
||||||
|
<video src="video.mp4"></video>
|
||||||
|
|
||||||
|
<!-- Custom element defined by the framework. -->
|
||||||
|
<google-cast-launcher></google-cast-launcher>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Metadata
|
||||||
|
|
||||||
|
Receiver applications may expose additional information about the media in their UI, or provide additional features depending on metadata passed to them.
|
||||||
|
|
||||||
|
The [Cast Application Framework] defines several [Metadata classes] that developers can use to convey information like the content title, thumbnail, release dates, authors etc.
|
||||||
|
|
||||||
|
```js
|
||||||
|
const mediaInfo = new chrome.cast.media.MediaInfo(
|
||||||
|
'http://localhost/video.mp4', // Provide a URL.
|
||||||
|
'video/mp4; codecs="avc1.640032,mp4a.40.2"', // MIME + codecs.
|
||||||
|
);
|
||||||
|
|
||||||
|
// Image URLs need to be "wrapped" by the Image class.
|
||||||
|
const thumbnail = new chrome.cast.Image('http://localhost/thumb.jpg');
|
||||||
|
|
||||||
|
// Our video is a short clip, but if it was a full-length movie,
|
||||||
|
// we would use `MovieMediaMetadata` here.
|
||||||
|
mediaInfo.metadata = new chrome.cast.media.GenericMediaMetadata();
|
||||||
|
mediaInfo.metadata.title = 'My Dog Chasing Geese';
|
||||||
|
mediaInfo.metadata.images = [ thumbnail ];
|
||||||
|
```
|
||||||
|
|
||||||
|
## Captions and subtitles
|
||||||
|
|
||||||
|
Media can contain additional [tracks of different types]. You can use text tracks to provide captions or subitles for your videos.
|
||||||
|
|
||||||
|
```js
|
||||||
|
const mediaInfo = new chrome.cast.media.MediaInfo(
|
||||||
|
'http://localhost/video.mp4', // Provide a URL.
|
||||||
|
'video/mp4; codecs="avc1.640032,mp4a.40.2"', // MIME + codecs.
|
||||||
|
);
|
||||||
|
|
||||||
|
// Instantiate the Track object first.
|
||||||
|
const captionsTrack = new chrome.cast.media.Track(
|
||||||
|
'captions-en', // Unique track identifier.
|
||||||
|
chrome.cast.media.TrackType.TEXT,
|
||||||
|
);
|
||||||
|
|
||||||
|
captionsTrack.trackContentId = 'http://localhost/captions-en.vtt';
|
||||||
|
captionsTrack.subtype = chrome.cast.media.TextTrackType.CAPTIONS;
|
||||||
|
captionsTrack.name = 'English CC';
|
||||||
|
captionsTrack.language = 'en-US'; // RFC 5646 Language Tag.
|
||||||
|
captionsTrack.trackContentType = 'text/vtt';
|
||||||
|
|
||||||
|
// The `MediaInfo` object may contain several tracks of various types,
|
||||||
|
// how and whether additional tracks are exposed in the UI depends
|
||||||
|
// on the receiver application implementation.
|
||||||
|
mediaInfo.tracks = [ captionsTrack ];
|
||||||
|
```
|
||||||
|
|
||||||
|
## UX considerations
|
||||||
|
|
||||||
|
When the casting session starts, video playback pauses in the sender application and the `<google-cast-launcher>` changes its styling to indicate the session in progress.
|
||||||
|
|
||||||
|
However in many cases it is useful to provide additional indication. One of the common patterns is rendering a video overlay along with displaying a name of the device the media is being casted to.
|
||||||
|
|
||||||
|
To achieve this, we can listen for `SESSION_STATE_CHANGED` events and add or remove an overlay element when session is started and ended:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<style>
|
||||||
|
.video-wrapper {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.cast-overlay {
|
||||||
|
position: absolute;
|
||||||
|
display: grid;
|
||||||
|
place-content: center;
|
||||||
|
inset: 0;
|
||||||
|
background: #222;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div class="video-wrapper">
|
||||||
|
<video src="video.mp4"></video>
|
||||||
|
<div class="cast-overlay hidden">
|
||||||
|
Casting<span class="cast-target"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// The following code should be defined within
|
||||||
|
// `function initCastApi() { ... }` defined previously.
|
||||||
|
const context = cast.framework.CastContext.getInstance();
|
||||||
|
|
||||||
|
const overlay = document.querySelector('.cast-overlay');
|
||||||
|
const target = document.querySelector('.cast-target');
|
||||||
|
|
||||||
|
// Bind the overlay visibility state to session state changes.
|
||||||
|
context.addEventListener(
|
||||||
|
cast.framework.CastContextEventType.SESSION_STATE_CHANGED,
|
||||||
|
e => {
|
||||||
|
switch (e.sessionState) {
|
||||||
|
case 'SESSION_ENDED':
|
||||||
|
overlay.classList.remove('hidden');
|
||||||
|
break;
|
||||||
|
case 'SESSION_STARTED':
|
||||||
|
case 'SESSION_RESUMED':
|
||||||
|
const session = context.getCurrentSession();
|
||||||
|
const targetName = session.getCastDevice().friendlyName;
|
||||||
|
|
||||||
|
overlay.classList.add('hidden');
|
||||||
|
target.innerText = ` to ${targetName}`;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
## What's next?
|
||||||
|
|
||||||
|
In the [next article], we are going to shift gears and talk about media encryption and the Encrypted Media Extensions API.
|
||||||
|
|
||||||
|
[Default Media Web Receiver]: https://developers.google.com/cast/docs/web_receiver#default_media_web_receiver
|
||||||
|
[Styled Media Web Receiver]: https://developers.google.com/cast/docs/web_receiver#styled_media_web_receiver
|
||||||
|
[register]: https://developers.google.com/cast/docs/registration
|
||||||
|
[Cast Application Framework]: https://developers.google.com/cast/docs/web_sender
|
||||||
|
[Metadata classes]: https://developers.google.com/cast/docs/reference/web_sender/chrome.cast.media?hl=en
|
||||||
|
[tracks of different types]: https://developers.google.com/cast/docs/reference/web_sender/chrome.cast.media#.TrackType
|
||||||
|
[next article]: /encrypted-media-extensions/
|
||||||
|
|
Загрузка…
Ссылка в новой задаче