We mainly focus on four files, SampleChooserActivity
, PlayerActivity
, media.exolist.json
, AndroidManifest.xml
.
-
SampleChooserActivity
is the main UI, where we choose a stream to play. -
media.exolist.json
is the file storing the stream info, uri, extension, drm_scheme etc. -
PlayerActivity
receives the data passed in and deal with them. -
AndroidManifest.xml
stores activity info.
In this case, possible stream info are: uri
, extension
, drm_scheme
, drm_license_url
, ad_tag_uri
.
Step 1: We take a look at AndroidManifest.xml
:
<activity android:name="com.google.android.exoplayer2.demo.PlayerActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|smallestScreenSize|uiMode"
android:launchMode="singleTop"
android:label="@string/application_name"
android:theme="@style/PlayerTheme">
<intent-filter>
<action android:name="com.google.android.exoplayer.demo.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="http"/>
<data android:scheme="https"/>
<data android:scheme="content"/>
<data android:scheme="asset"/>
<data android:scheme="file"/>
</intent-filter>
<intent-filter>
<action android:name="com.google.android.exoplayer.demo.action.VIEW_LIST"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
We can see the PlayerActivity
receives two kinds of intent:
<intent-filter>
<action android:name="com.google.android.exoplayer.demo.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
...
</intent-filter>
<intent-filter>
<action android:name="com.google.android.exoplayer.demo.action.VIEW_LIST"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
Step 2: Let's have a look how the info are passed to PlayerActivity
.
The following code is found in this class:
public Intent buildIntent(Context context) {
Intent intent = new Intent(context, PlayerActivity.class);
intent.putExtra(PlayerActivity.PREFER_EXTENSION_DECODERS, preferExtensionDecoders);
if (drmInfo != null) {
drmInfo.updateIntent(intent);
}
return intent;
}
@Override
public Intent buildIntent(Context context) {
return super.buildIntent(context)
.setData(Uri.parse(uri))
.putExtra(PlayerActivity.EXTENSION_EXTRA, extension)
.putExtra(PlayerActivity.AD_TAG_URI_EXTRA, adTagUri)
.setAction(PlayerActivity.ACTION_VIEW);
}
@Override
public Intent buildIntent(Context context) {
String[] uris = new String[children.length];
String[] extensions = new String[children.length];
for (int i = 0; i < children.length; i++) {
uris[i] = children[i].uri;
extensions[i] = children[i].extension;
}
return super.buildIntent(context)
.putExtra(PlayerActivity.URI_LIST_EXTRA, uris)
.putExtra(PlayerActivity.EXTENSION_LIST_EXTRA, extensions)
.setAction(PlayerActivity.ACTION_VIEW_LIST);
}
public void updateIntent(Intent intent) {
Assertions.checkNotNull(intent);
intent.putExtra(PlayerActivity.DRM_SCHEME_UUID_EXTRA, drmSchemeUuid.toString());
intent.putExtra(PlayerActivity.DRM_LICENSE_URL, drmLicenseUrl);
intent.putExtra(PlayerActivity.DRM_KEY_REQUEST_PROPERTIES, drmKeyRequestProperties);
intent.putExtra(PlayerActivity.DRM_MULTI_SESSION, drmMultiSession);
}
private UUID getDrmUuid(String typeString) throws ParserException {
switch (Util.toLowerInvariant(typeString)) {
case "widevine":
return C.WIDEVINE_UUID;
case "playready":
return C.PLAYREADY_UUID;
case "clearkey":
return C.CLEARKEY_UUID;
default:
try {
return UUID.fromString(typeString);
catch (RuntimeException e) {
throw new ParserException("Unsupported drm type: " + typeString);
}
}
}
This means if the steam we choose contains one single uri, we can pass an intent this way:
Context context = InstrumentationRegistry.getTargetContext();
String uri;
String extension;
String drm_scheme_uuid;
String drm_license_url;
String ad_tag_uri;
Intent intent = new Intent(context, PlayerActivity.class);
intent.setAction(PlayerActivity.ACTION_VIEW);
intent.addCategory("android.intent.category.DEFAULT");
intent.setData(Uri.parse(uri));
intent.putExtra(PlayerActivity.EXTENSION_EXTRA, extension);
intent.putExtra(PlayerActivity.DRM_SCHEME_UUID_EXTRA, drm_scheme_uuid);
intent.putExtra(PlayerActivity.DRM_LICENSE_URL, drm_license_url);
intent.putExtra(PlayerActivity.AD_TAG_URI_EXTRA, ad_tag_uri)
Or if the stream we choose contains multiple uris, we can pass an intent this way:
Context context = InstrumentationRegistry.getTargetContext();
String uri;
String extension;
String drm_scheme_uuid;
String drm_license_url;
String ad_tag_uri;
Intent intent = new Intent(context, PlayerActivity.class);
intent.setAction(PlayerActivity.ACTION_VIEW_LIST);
intent.addCategory("android.intent.category.DEFAULT");
intent.putExtra(PlayerActivity.URI_LIST_EXTRA, uris);
intent.putExtra(PlayerActivity.EXTENSION_EXTRA, extension);
intent.putExtra(PlayerActivity.DRM_SCHEME_UUID_EXTRA, drm_scheme_uuid);
intent.putExtra(PlayerActivity.DRM_LICENSE_URL, drm_license_url);
intent.putExtra(PlayerActivity.AD_TAG_URI_EXTRA, ad_tag_uri)
Step 3: Let's have a look at how to info are received by PlayerActivity
:
UUID drmSchemeUuid = intent.hasExtra(DRM_SCHEME_UUID_EXTRA)
? UUID.fromString(intent.getStringExtra(DRM_SCHEME_UUID_EXTRA)) : null;
Intent intent = getIntent();
String action = intent.getAction();
if (ACTION_VIEW.equals(action)) {
uris = new Uri[]{intent.getData()};
extensions = new String[]{intent.getStringExtra(EXTENSION_EXTRA)};
} else if (ACTION_VIEW_LIST.equals(action)) {
String[] uriStrings = intent.getStringArrayExtra(URI_LIST_EXTRA);
uris = new Uri[uriStrings.length];
for (int i = 0; i < uriStrings.length; i++) {
uris[i] = Uri.parse(uriStrings[i]);
}
extensions = intent.getStringArrayExtra(EXTENSION_LIST_EXTRA);
if (extensions == null) {
extensions = new String[uriStrings.length];
}
} else {
showToast(getString(R.string.unexpected_intent_action, action));
return;
}
UUID drmSchemeUuid = intent.hasExtra(DRM_SCHEME_UUID_EXTRA)?UUID.fromString(intent.getStringExtra(DRM_SCHEME_UUID_EXTRA)) : null;
uris = new Uri[]{intent.getData()
String[] uriStrings = intent.getStringArrayExtra(URI_LIST_EXTRA);
extensions = new String[]{intent.getStringExtra(EXTENSION_EXTRA)};
String drmLicenseUrl = intent.getStringExtra(DRM_LICENSE_URL);
String adTagUriString = intent.getStringExtra(AD_TAG_URI_EXTRA);
In general, intent extras are passed as key value pair
: a value for a key, except when we wish to pass a uri
, we use Intent.setData(Uri uri)
.
Additional info, the json
file looks like this:
"name": "WV: HDCP not specified",
"uri": "https://storage.googleapis.com/wvmedia/cenc/h264/tears/tears.mpd",
"drm_scheme": "widevine",
"drm_license_url": "https://proxy.uat.widevine.com/proxy?video_id=d286538032258a1c&provider=widevine_test"
{
"name": "Google Play (WebM,VP9)",
"uri": "http://www.youtube.com/api/manifest/dash/id/3aa39fa2cc27967f/source/youtube?as=fmp4_audio_clear,webm2_sd_hd_clear&sparams=ip,ipbits,expire,source,id,as&ip=0.0.0.0&ipbits=0&expire=19000000000&signature=B1C2A74783AC1CC4865EB312D7DD2D48230CC9FD.BD153B9882175F1F94BFE5141A5482313EA38E8D&key=ik0",
"extension": "mpd"
}
or
{
"name": "Clear -> Enc -> Clear -> Enc -> Enc",
"drm_scheme": "widevine",
"drm_license_url": "https://proxy.uat.widevine.com/proxy?provider=widevine_test",
"playlist": [
{
"uri": "https://html5demos.com/assets/dizzy.mp4"
},
{
"uri": "https://storage.googleapis.com/wvmedia/cenc/h264/tears/tears_sd.mpd"
},
{
"uri": "https://html5demos.com/assets/dizzy.mp4"
},
{
"uri": "https://storage.googleapis.com/wvmedia/cenc/h264/tears/tears_sd.mpd"
},
{
"uri": "https://storage.googleapis.com/wvmedia/cenc/h264/tears/tears_sd.mpd"
}
]
}
Of course, the last one needs to be passed in as
Intent.setAction(PlayerActivity.ACTION_VIEW_LIST);