Add support for subtitle files in the demo app

issue:#5523
PiperOrigin-RevId: 277927555
This commit is contained in:
ibaker 2019-11-01 15:02:55 +00:00 committed by Oliver Woodman
parent c5c50078d7
commit 07e93c15f9
5 changed files with 102 additions and 7 deletions

View File

@ -113,6 +113,8 @@
* Update the ffmpeg extension to release 4.2. It is necessary to rebuild the * Update the ffmpeg extension to release 4.2. It is necessary to rebuild the
native part of the extension after this change, following the instructions in native part of the extension after this change, following the instructions in
the extension's readme. the extension's readme.
* Add support for subtitle files to the demo app
([#5523](https://github.com/google/ExoPlayer/issues/5523)).
### 2.10.6 (2019-10-17) ### ### 2.10.6 (2019-10-17) ###

View File

@ -582,5 +582,17 @@
"spherical_stereo_mode": "top_bottom" "spherical_stereo_mode": "top_bottom"
} }
] ]
},
{
"name": "Subtitles",
"samples": [
{
"name": "TTML",
"uri": "https://html5demos.com/assets/dizzy.mp4",
"subtitle_uri": "https://storage.googleapis.com/exoplayer-test-media-1/ttml/netflix_ttml_sample.xml",
"subtitle_mime_type": "application/ttml+xml",
"subtitle_language": "en"
}
]
} }
] ]

View File

@ -34,6 +34,7 @@ import androidx.appcompat.app.AppCompatActivity;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.C.ContentType; import com.google.android.exoplayer2.C.ContentType;
import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.PlaybackPreparer; import com.google.android.exoplayer2.PlaybackPreparer;
import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.RenderersFactory; import com.google.android.exoplayer2.RenderersFactory;
@ -53,7 +54,9 @@ import com.google.android.exoplayer2.source.BehindLiveWindowException;
import com.google.android.exoplayer2.source.ConcatenatingMediaSource; import com.google.android.exoplayer2.source.ConcatenatingMediaSource;
import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.MediaSourceFactory; import com.google.android.exoplayer2.source.MediaSourceFactory;
import com.google.android.exoplayer2.source.MergingMediaSource;
import com.google.android.exoplayer2.source.ProgressiveMediaSource; import com.google.android.exoplayer2.source.ProgressiveMediaSource;
import com.google.android.exoplayer2.source.SingleSampleMediaSource;
import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.source.ads.AdsLoader; import com.google.android.exoplayer2.source.ads.AdsLoader;
import com.google.android.exoplayer2.source.ads.AdsMediaSource; import com.google.android.exoplayer2.source.ads.AdsMediaSource;
@ -114,8 +117,11 @@ public class PlayerActivity extends AppCompatActivity
public static final String DRM_KEY_REQUEST_PROPERTIES_EXTRA = "drm_key_request_properties"; public static final String DRM_KEY_REQUEST_PROPERTIES_EXTRA = "drm_key_request_properties";
public static final String DRM_MULTI_SESSION_EXTRA = "drm_multi_session"; public static final String DRM_MULTI_SESSION_EXTRA = "drm_multi_session";
public static final String PREFER_EXTENSION_DECODERS_EXTRA = "prefer_extension_decoders"; public static final String PREFER_EXTENSION_DECODERS_EXTRA = "prefer_extension_decoders";
public static final String TUNNELING = "tunneling"; public static final String TUNNELING_EXTRA = "tunneling";
public static final String AD_TAG_URI_EXTRA = "ad_tag_uri"; public static final String AD_TAG_URI_EXTRA = "ad_tag_uri";
public static final String SUBTITLE_URI_EXTRA = "subtitle_uri";
public static final String SUBTITLE_MIME_TYPE_EXTRA = "subtitle_mime_type";
public static final String SUBTITLE_LANGUAGE_EXTRA = "subtitle_language";
// For backwards compatibility only. // For backwards compatibility only.
public static final String DRM_SCHEME_UUID_EXTRA = "drm_scheme_uuid"; public static final String DRM_SCHEME_UUID_EXTRA = "drm_scheme_uuid";
@ -204,7 +210,7 @@ public class PlayerActivity extends AppCompatActivity
} else { } else {
DefaultTrackSelector.ParametersBuilder builder = DefaultTrackSelector.ParametersBuilder builder =
new DefaultTrackSelector.ParametersBuilder(/* context= */ this); new DefaultTrackSelector.ParametersBuilder(/* context= */ this);
boolean tunneling = intent.getBooleanExtra(TUNNELING, false); boolean tunneling = intent.getBooleanExtra(TUNNELING_EXTRA, false);
if (Util.SDK_INT >= 21 && tunneling) { if (Util.SDK_INT >= 21 && tunneling) {
builder.setTunnelingAudioSessionId(C.generateAudioSessionIdV21(/* context= */ this)); builder.setTunnelingAudioSessionId(C.generateAudioSessionIdV21(/* context= */ this));
} }
@ -424,6 +430,19 @@ public class PlayerActivity extends AppCompatActivity
MediaSource[] mediaSources = new MediaSource[samples.length]; MediaSource[] mediaSources = new MediaSource[samples.length];
for (int i = 0; i < samples.length; i++) { for (int i = 0; i < samples.length; i++) {
mediaSources[i] = createLeafMediaSource(samples[i]); mediaSources[i] = createLeafMediaSource(samples[i]);
Sample.SubtitleInfo subtitleInfo = samples[i].subtitleInfo;
if (subtitleInfo != null) {
Format subtitleFormat =
Format.createTextSampleFormat(
/* id= */ null,
subtitleInfo.mimeType,
/* selectionFlags= */ 0,
subtitleInfo.language);
MediaSource subtitleMediaSource =
new SingleSampleMediaSource.Factory(dataSourceFactory)
.createMediaSource(subtitleInfo.uri, subtitleFormat, C.TIME_UNSET);
mediaSources[i] = new MergingMediaSource(mediaSources[i], subtitleMediaSource);
}
} }
MediaSource mediaSource = MediaSource mediaSource =
mediaSources.length == 1 ? mediaSources[0] : new ConcatenatingMediaSource(mediaSources); mediaSources.length == 1 ? mediaSources[0] : new ConcatenatingMediaSource(mediaSources);

View File

@ -24,6 +24,9 @@ import static com.google.android.exoplayer2.demo.PlayerActivity.DRM_SCHEME_EXTRA
import static com.google.android.exoplayer2.demo.PlayerActivity.DRM_SCHEME_UUID_EXTRA; import static com.google.android.exoplayer2.demo.PlayerActivity.DRM_SCHEME_UUID_EXTRA;
import static com.google.android.exoplayer2.demo.PlayerActivity.EXTENSION_EXTRA; import static com.google.android.exoplayer2.demo.PlayerActivity.EXTENSION_EXTRA;
import static com.google.android.exoplayer2.demo.PlayerActivity.IS_LIVE_EXTRA; import static com.google.android.exoplayer2.demo.PlayerActivity.IS_LIVE_EXTRA;
import static com.google.android.exoplayer2.demo.PlayerActivity.SUBTITLE_LANGUAGE_EXTRA;
import static com.google.android.exoplayer2.demo.PlayerActivity.SUBTITLE_MIME_TYPE_EXTRA;
import static com.google.android.exoplayer2.demo.PlayerActivity.SUBTITLE_URI_EXTRA;
import static com.google.android.exoplayer2.demo.PlayerActivity.URI_EXTRA; import static com.google.android.exoplayer2.demo.PlayerActivity.URI_EXTRA;
import android.content.Intent; import android.content.Intent;
@ -51,7 +54,8 @@ import java.util.UUID;
isLive, isLive,
DrmInfo.createFromIntent(intent, extrasKeySuffix), DrmInfo.createFromIntent(intent, extrasKeySuffix),
adTagUri, adTagUri,
/* sphericalStereoMode= */ null); /* sphericalStereoMode= */ null,
SubtitleInfo.createFromIntent(intent, extrasKeySuffix));
} }
public final Uri uri; public final Uri uri;
@ -60,6 +64,7 @@ import java.util.UUID;
public final DrmInfo drmInfo; public final DrmInfo drmInfo;
public final Uri adTagUri; public final Uri adTagUri;
@Nullable public final String sphericalStereoMode; @Nullable public final String sphericalStereoMode;
@Nullable SubtitleInfo subtitleInfo;
public UriSample( public UriSample(
String name, String name,
@ -68,7 +73,8 @@ import java.util.UUID;
boolean isLive, boolean isLive,
DrmInfo drmInfo, DrmInfo drmInfo,
Uri adTagUri, Uri adTagUri,
@Nullable String sphericalStereoMode) { @Nullable String sphericalStereoMode,
@Nullable SubtitleInfo subtitleInfo) {
super(name); super(name);
this.uri = uri; this.uri = uri;
this.extension = extension; this.extension = extension;
@ -76,6 +82,7 @@ import java.util.UUID;
this.drmInfo = drmInfo; this.drmInfo = drmInfo;
this.adTagUri = adTagUri; this.adTagUri = adTagUri;
this.sphericalStereoMode = sphericalStereoMode; this.sphericalStereoMode = sphericalStereoMode;
this.subtitleInfo = subtitleInfo;
} }
@Override @Override
@ -100,6 +107,9 @@ import java.util.UUID;
if (drmInfo != null) { if (drmInfo != null) {
drmInfo.addToIntent(intent, extrasKeySuffix); drmInfo.addToIntent(intent, extrasKeySuffix);
} }
if (subtitleInfo != null) {
subtitleInfo.addToIntent(intent, extrasKeySuffix);
}
} }
} }
@ -167,6 +177,36 @@ import java.util.UUID;
} }
} }
public static final class SubtitleInfo {
@Nullable
public static SubtitleInfo createFromIntent(Intent intent, String extrasKeySuffix) {
if (!intent.hasExtra(SUBTITLE_URI_EXTRA + extrasKeySuffix)) {
return null;
}
return new SubtitleInfo(
Uri.parse(intent.getStringExtra(SUBTITLE_URI_EXTRA + extrasKeySuffix)),
intent.getStringExtra(SUBTITLE_MIME_TYPE_EXTRA + extrasKeySuffix),
intent.getStringExtra(SUBTITLE_LANGUAGE_EXTRA + extrasKeySuffix));
}
public final Uri uri;
public final String mimeType;
@Nullable public final String language;
public SubtitleInfo(Uri uri, String mimeType, @Nullable String language) {
this.uri = Assertions.checkNotNull(uri);
this.mimeType = Assertions.checkNotNull(mimeType);
this.language = language;
}
public void addToIntent(Intent intent, String extrasKeySuffix) {
intent.putExtra(SUBTITLE_URI_EXTRA + extrasKeySuffix, uri.toString());
intent.putExtra(SUBTITLE_MIME_TYPE_EXTRA + extrasKeySuffix, mimeType);
intent.putExtra(SUBTITLE_LANGUAGE_EXTRA + extrasKeySuffix, language);
}
}
public static Sample createFromIntent(Intent intent) { public static Sample createFromIntent(Intent intent) {
if (ACTION_VIEW_LIST.equals(intent.getAction())) { if (ACTION_VIEW_LIST.equals(intent.getAction())) {
ArrayList<String> intentUris = new ArrayList<>(); ArrayList<String> intentUris = new ArrayList<>();

View File

@ -178,7 +178,7 @@ public class SampleChooserActivity extends AppCompatActivity
? PlayerActivity.ABR_ALGORITHM_RANDOM ? PlayerActivity.ABR_ALGORITHM_RANDOM
: PlayerActivity.ABR_ALGORITHM_DEFAULT; : PlayerActivity.ABR_ALGORITHM_DEFAULT;
intent.putExtra(PlayerActivity.ABR_ALGORITHM_EXTRA, abrAlgorithm); intent.putExtra(PlayerActivity.ABR_ALGORITHM_EXTRA, abrAlgorithm);
intent.putExtra(PlayerActivity.TUNNELING, isNonNullAndChecked(tunnelingMenuItem)); intent.putExtra(PlayerActivity.TUNNELING_EXTRA, isNonNullAndChecked(tunnelingMenuItem));
sample.addToIntent(intent); sample.addToIntent(intent);
startActivity(intent); startActivity(intent);
return true; return true;
@ -311,6 +311,10 @@ public class SampleChooserActivity extends AppCompatActivity
ArrayList<UriSample> playlistSamples = null; ArrayList<UriSample> playlistSamples = null;
String adTagUri = null; String adTagUri = null;
String sphericalStereoMode = null; String sphericalStereoMode = null;
List<Sample.SubtitleInfo> subtitleInfos = new ArrayList<>();
Uri subtitleUri = null;
String subtitleMimeType = null;
String subtitleLanguage = null;
reader.beginObject(); reader.beginObject();
while (reader.hasNext()) { while (reader.hasNext()) {
@ -352,7 +356,7 @@ public class SampleChooserActivity extends AppCompatActivity
playlistSamples = new ArrayList<>(); playlistSamples = new ArrayList<>();
reader.beginArray(); reader.beginArray();
while (reader.hasNext()) { while (reader.hasNext()) {
playlistSamples.add((UriSample) readEntry(reader, true)); playlistSamples.add((UriSample) readEntry(reader, /* insidePlaylist= */ true));
} }
reader.endArray(); reader.endArray();
break; break;
@ -364,6 +368,15 @@ public class SampleChooserActivity extends AppCompatActivity
!insidePlaylist, "Invalid attribute on nested item: spherical_stereo_mode"); !insidePlaylist, "Invalid attribute on nested item: spherical_stereo_mode");
sphericalStereoMode = reader.nextString(); sphericalStereoMode = reader.nextString();
break; break;
case "subtitle_uri":
subtitleUri = Uri.parse(reader.nextString());
break;
case "subtitle_mime_type":
subtitleMimeType = reader.nextString();
break;
case "subtitle_language":
subtitleLanguage = reader.nextString();
break;
default: default:
throw new ParserException("Unsupported attribute name: " + name); throw new ParserException("Unsupported attribute name: " + name);
} }
@ -377,6 +390,14 @@ public class SampleChooserActivity extends AppCompatActivity
drmLicenseUrl, drmLicenseUrl,
drmKeyRequestProperties, drmKeyRequestProperties,
drmMultiSession); drmMultiSession);
Sample.SubtitleInfo subtitleInfo =
subtitleUri == null
? null
: new Sample.SubtitleInfo(
subtitleUri,
Assertions.checkNotNull(
subtitleMimeType, "subtitle_mime_type is required if subtitle_uri is set."),
subtitleLanguage);
if (playlistSamples != null) { if (playlistSamples != null) {
UriSample[] playlistSamplesArray = playlistSamples.toArray(new UriSample[0]); UriSample[] playlistSamplesArray = playlistSamples.toArray(new UriSample[0]);
return new PlaylistSample(sampleName, playlistSamplesArray); return new PlaylistSample(sampleName, playlistSamplesArray);
@ -388,7 +409,8 @@ public class SampleChooserActivity extends AppCompatActivity
isLive, isLive,
drmInfo, drmInfo,
adTagUri != null ? Uri.parse(adTagUri) : null, adTagUri != null ? Uri.parse(adTagUri) : null,
sphericalStereoMode); sphericalStereoMode,
subtitleInfo);
} }
} }