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:
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()
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.
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()
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:
// 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 ) } })
// 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:
// 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 ) } })
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:
val textTrackStyle = TextTrackStyle.fromSystemSettings(context)
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:
textTrackStyle.foregroundColor = Color.parseColor("#80FF0000")
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:
CaptioningManager.addCaptioningChangeListener(yourChangeListener)
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.