## Ad Breaks

The Web Sender SDK provides support for Ad Breaks and companion ads within a
given media stream.

<br />


See the
[Web Receiver Ad Breaks Overview](https://developers.google.com/cast/docs/web_receiver/ad_breaks.md.txt) for more
information on how Ad Breaks work.


While breaks can be specified on both the sender and receiver, it is recommended for them to be
specified on the [Web Receiver](https://developers.google.com/cast/docs/web_receiver/ad_breaks.md.txt) and
[Android TV Receiver](https://developers.google.com/cast/docs/android_tv_receiver/ad_breaks.md.txt) to maintain consistent
behavior across platforms.

<br />

On the web, specify Ad Breaks in a load command using
[`BreakClip`](https://developers.google.com/cast/docs/reference/web_sender/chrome.cast.media.BreakClip.md.txt)
and [`Break`](https://developers.google.com/cast/docs/reference/web_sender/chrome.cast.media.Break.md.txt):

    let breakClip1 = new BreakClip('bc0');
    breakClip1.title = 'Clip title'
    breakClip1.posterUrl = 'https://www.some.url';
    breakClip1.duration = 60;
    breakClip.whenSKippable = 5;

    let breakClip2 = ...
    let breakClip3 = ...

    let break1 = new Break('b0', ['bc0', 'bc1', 'bc2'], 10);

    let mediaInfo = new chrome.cast.media.MediaInfo(<contentId>, '<contentType');
    ...
    mediaInfo.breakClips = [breakClip1, breakClip2, breakClip3];
    mediaInfo.breaks = [break1];

    let request = new chrome.cast.media.LoadRequest(mediaInfo);

    cast.framework.CastContext.getInstance().getCurrentSession().loadMedia(request)

## Using the tracks APIs

A track can be a text (subtitle or caption) object, or an audio or video stream
object. The Tracks APIs let you work with these objects in your application.

> [!NOTE]
> **Note:** The [Styled](https://developers.google.com/cast/docs/styled_receiver.md.txt) and [Default Media](https://developers.google.com/cast/docs/web_receiver.md.txt#default_media_web_receiver) Receivers allow you to use only the text tracks with the API. To work with the audio and video tracks, you must develop a [Custom Receiver](https://developers.google.com/cast/docs/web_receiver/basic.md.txt).

A [`Track`](https://developers.google.com/cast/docs/reference/chrome/chrome.cast.media.Track) object
represents a track in the SDK. You can configure a track and assign a unique ID
to it like this:

```javascript
var englishSubtitle = new chrome.cast.media.Track(1, // track ID
  chrome.cast.media.TrackType.TEXT);
englishSubtitle.trackContentId = 'https://some-url/caption_en.vtt';
englishSubtitle.trackContentType = 'text/vtt';
englishSubtitle.subtype = chrome.cast.media.TextTrackType.SUBTITLES;
englishSubtitle.name = 'English Subtitles';
englishSubtitle.language = 'en-US';
englishSubtitle.customData = null;

var frenchSubtitle = new chrome.cast.media.Track(2, // track ID
  chrome.cast.media.TrackType.TEXT);
frenchSubtitle.trackContentId = 'https://some-url/caption_fr.vtt';
frenchSubtitle.trackContentType = 'text/vtt';
frenchSubtitle.subtype = chrome.cast.media.TextTrackType.SUBTITLES;
frenchSubtitle.name = 'French Subtitles';
frenchSubtitle.language = 'fr';
frenchSubtitle.customData = null;

var frenchAudio = new chrome.cast.media.Track(3, // track ID
  chrome.cast.media.TrackType.AUDIO);
frenchAudio.trackContentId = 'trk0001';
frenchAudio.trackContentType = 'audio/mp3';
frenchAudio.subtype = null;
frenchAudio.name = 'French Audio';
frenchAudio.language = 'fr';
frenchAudio.customData = null;
```

A media item may have multiple tracks; for example, it can have multiple
subtitles (each for a different language) or multiple alternative audio streams
(for different languages).

[`MediaInfo`](https://developers.google.com/cast/docs/reference/chrome/chrome.cast.media.MediaInfo)
is the class that models a media item. To associate a collection of
[`Track`](https://developers.google.com/cast/docs/reference/chrome/chrome.cast.media.Track)
objects with a media item, you update its
[`tracks`](https://developers.google.com/cast/docs/reference/chrome/chrome.cast.media.MediaInfo#tracks)
property. This association needs to be made before the media is
loaded to the receiver:

```javascript
var tracks = [englishSubtitle, frenchSubtitle, frenchAudio];
var mediaInfo = new chrome.cast.media.MediaInfo(mediaURL);
mediaInfo.contentType = 'video/mp4';
mediaInfo.metadata = new chrome.cast.media.GenericMediaMetadata();
mediaInfo.customData = null;
mediaInfo.streamType = chrome.cast.media.StreamType.BUFFERED;
mediaInfo.textTrackStyle = new chrome.cast.media.TextTrackStyle();
mediaInfo.duration = null;
mediaInfo.tracks = tracks;
```

You can set the active tracks in the media
[`activeTrackIds`](https://developers.google.com/cast/docs/reference/chrome/chrome.cast.media.LoadRequest#activeTrackIds)
request.

You can also activate one or more tracks that were associated with the media
item, after the media is loaded, by calling
[`EditTracksInfoRequest(opt_activeTrackIds, opt_textTrackStyle)`](https://developers.google.com/cast/docs/reference/chrome/chrome.cast.media.EditTracksInfoRequest)
and passing the IDs of the tracks to be activated in `opt_activeTrackIds`. Note,
both parameters are optional and you may choose which, active tracks or styles,
to set at your discretion. For example, here is how to
activate the French subtitle (`2`) and French audio (`3`):

```javascript
var activeTrackIds = [2, 3];
var tracksInfoRequest = new chrome.cast.media.EditTracksInfoRequest(activeTrackIds);
media.editTracksInfo(tracksInfoRequest, successCallback, errorCallback);
```

To remove all audio or video tracks from the current media, simply set
`mediaInfo.tracks=null` (an empty array) and reload the media.

To remove all text tracks from the current media (for example, turning off
subtitles), do one of the following:

- Update `var activeTrackIds = [2, 3];` (shown previously) so it includes \[3\] only, the audio track.
- Set `mediaInfo.tracks=null`. Note that it's not necessary to reload the media to turn off text captions (`track.hidden`). Sending an `activeTracksId` array that *doesn't* contain a previously enabled `trackId` disables the text track.

## Styling text tracks

[`TextTrackStyle`](https://developers.google.com/cast/docs/reference/chrome/chrome.cast.media.TextTrackStyle)
is the object that encapsulates the styling information of a text track. After
creating or updating an existing `TextTrackStyle` object, you can apply that to
the currently playing media item by calling its
[`editTrackInfo`](https://developers.google.com/cast/docs/reference/chrome/chrome.cast.media.Media#editTracksInfo)
method, like this:

```javascript
var textTrackStyle = new chrome.cast.media.TextTrackStyle();
var tracksInfoRequest = new chrome.cast.media.EditTracksInfoRequest(textTrackStyle);
media.editTracksInfo(tracksInfoRequest, successCallback, errorCallback);
```

You can track the status of the request with result of the callbacks, either
success or error, and update the originating sender accordingly.

Applications should allow users to update the style for text tracks, either
using the settings provided by the system or by the application itself.

You can style the following text track style elements:

- Foreground (text) color and opacity
- Background color and opacity
- Edge type
- Edge Color
- Font Scale
- Font Family
- Font Style

For example, set the text color to red with 75% opacity as follows:

```javascript
var textTrackStyle = new chrome.cast.media.TextTrackStyle();
textTrackStyle.foregroundColor = '#80FF0000';
```

## Volume control

You can use the
[`RemotePlayer`](https://developers.google.com/cast/docs/reference/chrome/cast.framework.RemotePlayer) and
[`RemotePlayerController`](https://developers.google.com/cast/docs/reference/chrome/cast.framework.RemotePlayerController)
to set the receiver volume.

```javascript
function changeVolume(newVolume) {
  player.volumeLevel = newVolume;
  playerController.setVolumeLevel();
  // Update sender UI to reflect change
}
```

The sender app should adhere to the following guidelines for controlling volume:

- The sender application must synchronize with the receiver so that the sender UI always reports the volume per the receiver. Use the `RemotePlayerEventType.VOLUME_LEVEL_CHANGED` and `RemotePlayerEventType.IS_MUTED_CHANGED` callback to maintain the volume on the sender. See [Status updates](https://developers.google.com/cast/docs/web_sender/advanced.md.txt#status-updates) for more information.
- Sender apps must not set the volume level at a specific, pre-defined level or set the volume level to the sender device's ringer/media volume when the app loads on the receiver.

See
[Sender volume controls](https://developers.google.com/cast/docs/design_checklist/sender.md.txt#sender-control-volume)
in the [Design Checklist](https://developers.google.com/cast/docs/design_checklist.md.txt).

## Sending media messages to the receiver

[`Media Messages`](https://developers.google.com/cast/docs/reference/messages) can be sent from the sender to
receiver. For example, to send a `SKIP_AD` message to the receiver:

```javascript
// Get a handle to the skip button element
const skipButton = document.getElementById('skip');
skipButton.addEventListener("click", function() {
  if (castSession) {
    const media = castSession.getMediaSession();
    castSession.sendMessage('urn:x-cast:com.google.cast.media', {
      type: 'SKIP_AD',
      requestId: 1,
      mediaSessionId: media.mediaSessionId
    });
  }
});
```

## Status updates

When multiple senders are connected to the same receiver, it is important for
each sender to be aware of the changes on the receiver even if those changes
were initiated from other senders.

> [!NOTE]
> **Note:** This is important for all apps, not only those that explicitly support multiple senders, because some Cast devices have control inputs (remotes, buttons) that behave as virtual senders, affecting the status on the receiver.

To this end, your application should register all necessary listeners on the
[`RemotePlayerController`](https://developers.google.com/cast/docs/reference/chrome/cast.framework.RemotePlayerController).
If the
[`TextTrackStyle`](https://developers.google.com/cast/docs/reference/chrome/chrome.cast.media.TextTrackStyle)
of the current media changes, then all of the connected senders will be notified
and the corresponding properties of the current media session, such as
`activeTrackIds` and `textTrackStyle` of the
[`MediaInfo`](https://developers.google.com/cast/docs/reference/chrome/chrome.cast.media.MediaInfo)
field will be sent to senders in callbacks. In this case, the receiver SDK
does not verify whether the new style is different from the previous one and
notifies all the connected senders regardless.

> [!NOTE]
> **Note:** The list of tracks associated with the currently loaded media cannot be changed. If needed, you have to update the tracks information on the [`MediaInfo`](https://developers.google.com/cast/docs/reference/chrome/chrome.cast.media.MediaInfo) object and reload the media.

## Progress indicator

Showing the playback location with a progress indicator on the sender is a
requirement for most apps. The Cast APIs use the Cast media protocol, which
optimizes bandwidth consumption for this and other scenarios, so you do not
need to implement your own status synchronization. For the proper implementation
of a progress indicator for media playback using the APIs, see the
[CastVideos-chrome](https://github.com/googlecast/CastVideos-chrome) sample app.

## CORS requirements

For adaptive media streaming, Google Cast requires the presence of CORS headers,
but even simple mp4 media streams require CORS if they include Tracks. If you
want to enable Tracks for any media, you must enable CORS for both your track
streams and your media streams. So, if you do not have CORS headers available
for your simple mp4 media on your server, and you then add a simple subtitle
track, you will not be able to stream your media unless you update your server
to include the appropriate CORS headers.

You need the following headers: `Content-Type`,`Accept-Encoding`, and `Range`.
Note that the last two headers, `Accept-Encoding` and `Range`, are additional
headers that you may not have needed previously.

Wildcards "\*" cannot be used for the `Access-Control-Allow-Origin` header. If
the page has protected media content, it must use a domain instead of a
wildcard.

## Resuming a session without reloading the web page

To resume an existing `CastSession`, use
[`requestSessionById(sessionId)`](https://developers.google.com/cast/docs/reference/chrome/chrome.cast#.requestSessionById)
with the `sessionId` of the session you are attempting to join.

The `sessionId` can be found on the active `CastSession` using
[`getSessionId()`](https://developers.google.com/cast/docs/reference/chrome/cast.framework.CastSession#getSessionId)
after calling
[`loadMedia()`](https://developers.google.com/cast/docs/reference/chrome/cast.framework.CastSession#loadMedia).

The recommended approach is to:

1. Call [`loadMedia()`](https://developers.google.com/cast/docs/reference/chrome/cast.framework.CastSession#loadMedia) to start the session
2. Store the `sessionId` locally
3. Rejoin the session using [`requestSessionById(sessionId)`](https://developers.google.com/cast/docs/reference/chrome/chrome.cast#.requestSessionById) when needed

```javascript
let sessionId;

function rejoinCastSession() {
  chrome.cast.requestSessionById(sessionId);

  // Add any business logic to load new content or only resume the session
}

document.getElementById('play-button').addEventListener(("click"), function() {
  if (sessionId == null) {
    let castSession = cast.framework.CastContext.getInstance().getCurrentSession();
    if (castSession) {
      let mediaInfo = createMediaInfo();
      let request = new chrome.cast.media.LoadRequest(mediaInfo);
      castSession.loadMedia(request)

      sessionId = CastSession.getSessionId();
    } else {
      console.log("Error: Attempting to play media without a Cast Session");
    }
  } else {
    rejoinCastSession();
  }
});
```

## Next steps

This concludes the features that you can add to your Web Sender app.
You can now build a sender app for another platform
([Android](https://developers.google.com/cast/docs/android_sender.md.txt) or [iOS](https://developers.google.com/cast/docs/ios_sender.md.txt)), or
build a [receiver app](https://developers.google.com/cast/docs/caf_receiver).