Use Media Tracks

MediaTrack represents a media track, which can be an audio stream, a video stream, or text (such as subtitles or closed caption). Your app can group, style and activate media tracks.

Configure a track

You can configure a track and assign a unique ID to it. The following code creates an English text track, a French text track and a French audio track, each with their own ID:

Kotlin
val englishSubtitle = MediaTrack.Builder(1 /* ID */, MediaTrack.TYPE_TEXT)
    .setName("English Subtitle")
    .setSubtype(MediaTrack.SUBTYPE_SUBTITLES)
    .setContentId("https://some-url/caption_en.vtt")
    /* language is required for subtitle type but optional otherwise */
    .setLanguage("en-US")
    .build()

val frenchSubtitle = MediaTrack.Builder(2, MediaTrack.TYPE_TEXT)
    .setName("French Subtitle")
    .setSubtype(MediaTrack.SUBTYPE_SUBTITLES)
    .setContentId("https://some-url/caption_fr.vtt")
    .setLanguage("fr")
    .build()

val frenchAudio = MediaTrack.Builder(3, MediaTrack.TYPE_AUDIO)
    .setName("French Audio")
    .setContentId("trk0001")
    .setLanguage("fr")
    .build()
Java
MediaTrack englishSubtitle = new MediaTrack.Builder(1 /* ID */,
MediaTrack.TYPE_TEXT)
  .setName("English Subtitle")
  .setSubtype(MediaTrack.SUBTYPE_SUBTITLES)
  .setContentId("https://some-url/caption_en.vtt")
  /* language is required for subtitle type but optional otherwise */
  .setLanguage("en-US")
  .build();

MediaTrack frenchSubtitle = new MediaTrack.Builder(2, MediaTrack.TYPE_TEXT)
  .setName("French Subtitle")
  .setSubtype(MediaTrack.SUBTYPE_SUBTITLES)
  .setContentId("https://some-url/caption_fr.vtt")
  .setLanguage("fr")
  .build();

MediaTrack frenchAudio = new MediaTrack.Builder(3, MediaTrack.TYPE_AUDIO)
  .setName("French Audio")
  .setContentId("trk0001")
  .setLanguage("fr")
  .build();

Group tracks

You can group multiple tracks into a media item, which is represented by MediaInfo. An instance of MediaInfo takes an array of tracks and aggregates other information about the media item. Building on the example, your app can add those three media tracks to a media item by passing a list of those three tracks into MediaInfo.Builder.setMediaTracks(List). Your app needs to associate tracks in a MediaInfo in this way before it loads the media to the receiver.

Kotlin
val tracks: MutableList<MediaTrack> = ArrayList<MediaTrack>()
tracks.add(englishSubtitle)
tracks.add(frenchSubtitle)
tracks.add(frenchAudio)
val mediaInfo = MediaInfo.Builder(url)
    .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
    .setContentType(getContentType())
    .setMetadata(getMetadata())
    .setMediaTracks(tracks)
    .build()
Java
List tracks = new ArrayList();
tracks.add(englishSubtitle);
tracks.add(frenchSubtitle);
tracks.add(frenchAudio);
MediaInfo mediaInfo = MediaInfo.Builder(url)
  .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
  .setContentType(getContentType())
  .setMetadata(getMetadata())
  .setMediaTracks(tracks)
  .build();

Remove tracks

To remove all the tracks from the current media (such as turning off the three subtitles in the example), call MediaInfo.Builder.setMediaTracks(List) and pass an empty list of IDs.

Update tracks

Your app can activate one or more tracks that were associated with the media item (after the media is loaded), by calling RemoteMediaClient.setActiveMediaTracks(long[]) and passing the IDs of the tracks to be activated. This example activates the French subtitle and French audio:

Kotlin
// the ID for the French subtitle is '2' and for the French audio '3'
remoteMediaClient.setActiveMediaTracks(longArrayOf(2, 3))
    .setResultCallback(ResultCallback {
            mediaChannelResult: RemoteMediaClient.MediaChannelResult ->
                if (!mediaChannelResult.status.isSuccess) {
                    Log.e(TAG, "Failed with status code:" +
                            mediaChannelResult.status.statusCode
                    )
                }
    })
Java
// the ID for the French subtitle is '2' and for the French audio '3'
remoteMediaClient.setActiveMediaTracks(new long[]{2, 3})
    .setResultCallback(mediaChannelResult -> {
        if (!mediaChannelResult.getStatus().isSuccess()) {
            Log.e(TAG, "Failed with status code:" +
                    mediaChannelResult.getStatus().getStatusCode());
        }
    });

Style text tracks

TextTrackStyle encapsulates the styling information of a text track. After creating or updating an existing TextTrackStyle, you can apply that style to the currently playing media item by calling RemoteMediaClient.setTextTrackStyle, like this:

Kotlin
// the ID for the French subtitle is '2' and for the French audio '3'
remoteMediaClient.setTextTrackStyle(style)
    .setResultCallback(ResultCallback {
            mediaChannelResult: RemoteMediaClient.MediaChannelResult ->
                if (!mediaChannelResult.status.isSuccess) {
                    Log.e(TAG, "Failed to set the style, status code: " +
                            mediaChannelResult.status.statusCode
                    )
                }
    })
Java
remoteMediaClient.setTextTrackStyle(style)
    .setResultCallback(mediaChannelResult -> {
        if (!mediaChannelResult.getStatus().isSuccess()) {
            Log.e(TAG, "Failed to set the style, status code: " +
                    mediaChannelResult.getStatus().getStatusCode());
        }
    });

Your app should allow users to update the style for text tracks, either using the settings provided by the system or by the app itself. In Android KitKat and later, you can use the system-wide closed caption settings provided by the framework:

Kotlin
val textTrackStyle = TextTrackStyle.fromSystemSettings(context)
Java
TextTrackStyle textTrackStyle = TextTrackStyle.fromSystemSettings(context);

For versions prior to KitKat, the above call will return an object whose fields are undefined, so you need to populate those fields in your app, based on user selections and some default values. 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 (FF) with 50% opacity (80) as follows:

Kotlin
textTrackStyle.foregroundColor = Color.parseColor("#80FF0000")
Java
textTrackStyle.setForegroundColor(Color.parseColor("#80FF0000"));

In KitKat and later versions, you should register your app to be notified when system-wide closed caption settings are updated. To this end, you need to implement CaptioningManager.CaptioningChangeListener in your app and register this listener by calling:

Kotlin
CaptioningManager.addCaptioningChangeListener(yourChangeListener)
Java
CaptioningManager.addCaptioningChangeListener(yourChangeListener);

When your app receives a call back that the caption settings have changed, you would then need to extract the new settings and update the style of the text caption for the media that is currently playing by calling RemoteMediaClient.setTextTrackStyle and passing in the new style.

Receive status updates

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

To this end, your app should register a RemoteMediaClient.Listener and a RemoteMediaClient.ProgressListener.

If the TextTrackStyle of the current media changes, then all of the connected senders will be notified through both of the above registered listeners. In this case, the receiver SDK does not verify whether the new style is different from the previous one and notifies all of the connected senders regardless. If, however, the status of active tracks changes, only the RemoteMediaClient.ProgressListener in connected senders will be notified.

Satisfy 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 header. In addition, you need to allow at least the following headers: Content-Type, Accept-Encoding, and Range. Note that the last two headers are additional headers that you may not have been needed previously.