зеркало из 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
|
||||
*/
|
||||
article {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding-bottom: calc(var(--gutter) * 3);
|
||||
margin-bottom: calc(var(--gutter) * 3);
|
||||
border-bottom: 1px solid var(--separator);
|
||||
overflow: hidden;
|
||||
}
|
||||
article .video-content {
|
||||
width: 100%;
|
||||
max-width: 760px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
|
@ -613,16 +617,15 @@ h3[id*="example"]{
|
|||
font-size: 24px;
|
||||
margin-bottom: calc(var(--gutter) / 2);
|
||||
}
|
||||
article pre,
|
||||
article pre code,
|
||||
.code-sample .code-sample--content {
|
||||
max-width: 760px;
|
||||
white-space: pre-wrap;
|
||||
display: block;
|
||||
font-size: 18px;
|
||||
font-family: monospace;
|
||||
padding: var(--gutter);
|
||||
border: 1px solid var(--code-border);
|
||||
background: var(--code-background);
|
||||
overflow-y: auto;
|
||||
overflow-x: auto;
|
||||
}
|
||||
.video-container {
|
||||
position: relative;
|
||||
|
|
|
@ -8,7 +8,6 @@ length: '1:04'
|
|||
video-sources:
|
||||
- src: https://storage.googleapis.com/kino-assets/single-video/video.mp4
|
||||
type: video/mp4; codecs="avc1.640032,mp4a.40.2"
|
||||
cast: true
|
||||
thumbnail: https://storage.googleapis.com/kino-assets/single-video/thumbnail.png
|
||||
media-session-artwork:
|
||||
- sizes: 96x96
|
||||
|
|
|
@ -7,6 +7,7 @@ length: '1:04'
|
|||
video-sources:
|
||||
- src: https://storage.googleapis.com/kino-assets/google-cast/manifest.mpd
|
||||
type: application/dash+xml
|
||||
cast: true
|
||||
- src: https://storage.googleapis.com/kino-assets/google-cast/master.m3u8
|
||||
type: application/x-mpegURL
|
||||
video-subtitles:
|
||||
|
@ -45,4 +46,202 @@ cast: true
|
|||
|
||||
## 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/
|
||||
|
|
Загрузка…
Ссылка в новой задаче