Android ChromeCast Coding – Passing MediaMetadata from Android to JS

Recently I was coding demo app including Chromecast support and custom reciever.
And, quite surprisingly, I had lot of problems passing metaData from Android part (sender) to JavaScript code (reciever).

Finnaly, after lot of googling and vasted time, I found out that
PROBLEM 1 : one can not create custom medaData, you have to use what Google prepared for you.
PROBLEM 2 : one can not pass any MediaMetadata field, because it is dependent on MediaMetadata type.
PROBLEM 3 : MediaMetadata naming is different at Android and JS part.

GENERAL

Google guide https://developer.android.com/reference/android/media/MediaMetadata
and https://developers.google.com/android/reference/com/google/android/gms/cast/MediaMetadata

There are 6 MediaMetadata types :

MEDIA_TYPE_GENERIC
MEDIA_TYPE_PHOTO
MEDIA_TYPE_MOVIE
MEDIA_TYPE_TV_SHOW
MEDIA_TYPE_MUSIC TRACK
MEDIA_TYPE_USER

And various MediaMetadata fields :

// String
movieMetadata.putString(MediaMetadata.KEY_TITLE, "TITLE");
movieMetadata.putString(MediaMetadata.KEY_SUBTITLE, "SUBTITLE");
movieMetadata.putString(MediaMetadata.KEY_ARTIST, "ARTIST");
movieMetadata.putString(MediaMetadata.KEY_ALBUM_ARTIST, "ALBUM_ARTIST");
movieMetadata.putString(MediaMetadata.KEY_ALBUM_TITLE, "ALBUM_TITLE");
movieMetadata.putString(MediaMetadata.KEY_COMPOSER, "COMPOSER");
movieMetadata.putString(MediaMetadata.KEY_SERIES_TITLE, "SERIES_TITLE");
movieMetadata.putString(MediaMetadata.KEY_STUDIO, "STUDIO");
movieMetadata.putString(MediaMetadata.KEY_LOCATION_NAME, "LOCATION_NAME");

// Date - java.util.Calendar
Calendar rightNow = Calendar.getInstance();
movieMetadata.putDate(MediaMetadata.KEY_CREATION_DATE, rightNow);
movieMetadata.putDate(MediaMetadata.KEY_BROADCAST_DATE, rightNow);
movieMetadata.putDate(MediaMetadata.KEY_RELEASE_DATE, rightNow);

// Int
movieMetadata.putInt(MediaMetadata.KEY_DISC_NUMBER, 1);
movieMetadata.putInt(MediaMetadata.KEY_TRACK_NUMBER, 2);
movieMetadata.putInt(MediaMetadata.KEY_SEASON_NUMBER,3);
movieMetadata.putInt(MediaMetadata.KEY_EPISODE_NUMBER, 4);
movieMetadata.putInt(MediaMetadata.KEY_WIDTH, 5);
movieMetadata.putInt(MediaMetadata.KEY_HEIGHT, 6);

// Double
movieMetadata.putDouble(MediaMetadata.KEY_LOCATION_LATITUDE, 11);
movieMetadata.putDouble(MediaMetadata.KEY_LOCATION_LONGITUDE, 12);

PROBLEM 1 : one can not create custom medaData, you have to use what Google prepared for you.
nothing to add here, you have to deal with it. If you need to pass your metaData – for example lenght, you can pass it using integer MediaMetadata fiel like KEY_DISC_NUMBER.

PROBLEM 2 : one can not pass any MediaMetadata field, because it is dependent on MediaMetadata type.
here comes the funny part. Google has restricted usage of MediaMetadata field for particular MediaMetadata types. So – for example – for type MEDIA_TYPE_MOVIE you can only pass fields :
– images (movieMetadata.addImage)
– Strings KEY_TITLE,KEY_STUDIO,KEY_SUBTITLE
– and Date KEY_RELEASE_DATE.
Nothing more.
See Googles table for details
https://developers.google.com/android/reference/com/google/android/gms/cast/MediaMetadata

PROBLEM 3 : MediaMetadata naming is different at Android and JS part, which is shocking for me.
This is really bad. One would expect for MediaMetadata fields name like „KEY_LOCATION_NAME“ at Android part will be recieved in JS part as something line „locationName“. No it is „location“. For „KEY_BROADCAST_DATE“ it is „originalAirdate“. Shame on you Google :).

Below details are based on my testing :

Android part (send as) / JavaScript part (recieved as)
KEY_TITLE / title
KEY_SUBTITLE / subtitle
KEY_ARTIST / artist
ALBUM_ARTIST / albumArtist
ALBUM_TITLE / albumName
COMPOSER / composer
SERIES_TITLE / seriesTitle
STUDIO / studio
LOCATION_NAME / location
KEY_CREATION_DATE / creationDateTime
KEY_BROADCAST_DATE / originalAirdate
KEY_RELEASE_DATE / releaseDate
KEY_DISC_NUMBER / discNumber
KEY_TRACK_NUMBER / trackNumber
KEY_SEASON_NUMBER / season
KEY_EPISODE_NUMBER / episode
KEY_WIDTH / width
KEY_HEIGHT / height
KEY_LOCATION_LATITUDE / latitude
KEY_LOCATION_LONGITUDE / longitude

CODING
As reference I am adding part of code so you can try it

1) Android (sender) part :

private MediaInfo buildMediaInfo() {

MediaMetadata movieMetadata = new MediaMetadata(MediaMetadata.MEDIA_TYPE_GENERIC);

movieMetadata.addImage(new WebImage(Uri.parse(mSelectedMedia.getImage(0))));
movieMetadata.addImage(new WebImage(Uri.parse(mSelectedMedia.getImage(1))));

// all available Keys - TEST / 20 in total

// String
movieMetadata.putString(MediaMetadata.KEY_TITLE, "TITLE");
movieMetadata.putString(MediaMetadata.KEY_SUBTITLE, "SUBTITLE");
movieMetadata.putString(MediaMetadata.KEY_ARTIST, "ARTIST");
movieMetadata.putString(MediaMetadata.KEY_ALBUM_ARTIST, "ALBUM_ARTIST");
movieMetadata.putString(MediaMetadata.KEY_ALBUM_TITLE, "ALBUM_TITLE");
movieMetadata.putString(MediaMetadata.KEY_COMPOSER, "COMPOSER");
movieMetadata.putString(MediaMetadata.KEY_SERIES_TITLE, "SERIES_TITLE");
movieMetadata.putString(MediaMetadata.KEY_STUDIO, "STUDIO");
movieMetadata.putString(MediaMetadata.KEY_LOCATION_NAME, "LOCATION_NAME");

// Date - java.util.Calendar
Calendar rightNow = Calendar.getInstance();
movieMetadata.putDate(MediaMetadata.KEY_CREATION_DATE, rightNow);
movieMetadata.putDate(MediaMetadata.KEY_BROADCAST_DATE, rightNow);
movieMetadata.putDate(MediaMetadata.KEY_RELEASE_DATE, rightNow);

// Int
movieMetadata.putInt(MediaMetadata.KEY_DISC_NUMBER, 1);
movieMetadata.putInt(MediaMetadata.KEY_TRACK_NUMBER, 2);
movieMetadata.putInt(MediaMetadata.KEY_SEASON_NUMBER,3);
movieMetadata.putInt(MediaMetadata.KEY_EPISODE_NUMBER, 4);
movieMetadata.putInt(MediaMetadata.KEY_WIDTH, 5);
movieMetadata.putInt(MediaMetadata.KEY_HEIGHT, 6);

// Double
movieMetadata.putDouble(MediaMetadata.KEY_LOCATION_LATITUDE, 11);
movieMetadata.putDouble(MediaMetadata.KEY_LOCATION_LONGITUDE, 12);

return new MediaInfo.Builder(mSelectedMedia.getUrl())
.setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
.setContentType("videos/mp4")
.setMetadata(movieMetadata)
.setStreamDuration(mSelectedMedia.getDuration() * 1000)
.build();
}

2) JavaScript (reciever) part :

download whole code from view-source:http://vancura.cz/android/cast/reciever.html


// metaData sent by Android Sender part
var senderMetadata = request.media.metadata;

// list keys in senderMetadata
var keys = Object.keys(senderMetadata);
console.log('senderMetadata contains ' + keys.length + ' keys: '+ keys);

// list values by keys
console.log(senderMetadata);

Then you will see in ChromeCast inspection log below MetaDate passed from Android to JS