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
native part of the extension after this change, following the instructions in
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) ###

View File

@ -582,5 +582,17 @@
"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.ContentType;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.PlaybackPreparer;
import com.google.android.exoplayer2.Player;
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.MediaSource;
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.SingleSampleMediaSource;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.source.ads.AdsLoader;
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_MULTI_SESSION_EXTRA = "drm_multi_session";
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 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.
public static final String DRM_SCHEME_UUID_EXTRA = "drm_scheme_uuid";
@ -204,7 +210,7 @@ public class PlayerActivity extends AppCompatActivity
} else {
DefaultTrackSelector.ParametersBuilder builder =
new DefaultTrackSelector.ParametersBuilder(/* context= */ this);
boolean tunneling = intent.getBooleanExtra(TUNNELING, false);
boolean tunneling = intent.getBooleanExtra(TUNNELING_EXTRA, false);
if (Util.SDK_INT >= 21 && tunneling) {
builder.setTunnelingAudioSessionId(C.generateAudioSessionIdV21(/* context= */ this));
}
@ -424,6 +430,19 @@ public class PlayerActivity extends AppCompatActivity
MediaSource[] mediaSources = new MediaSource[samples.length];
for (int i = 0; i < samples.length; 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 =
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.EXTENSION_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 android.content.Intent;
@ -51,7 +54,8 @@ import java.util.UUID;
isLive,
DrmInfo.createFromIntent(intent, extrasKeySuffix),
adTagUri,
/* sphericalStereoMode= */ null);
/* sphericalStereoMode= */ null,
SubtitleInfo.createFromIntent(intent, extrasKeySuffix));
}
public final Uri uri;
@ -60,6 +64,7 @@ import java.util.UUID;
public final DrmInfo drmInfo;
public final Uri adTagUri;
@Nullable public final String sphericalStereoMode;
@Nullable SubtitleInfo subtitleInfo;
public UriSample(
String name,
@ -68,7 +73,8 @@ import java.util.UUID;
boolean isLive,
DrmInfo drmInfo,
Uri adTagUri,
@Nullable String sphericalStereoMode) {
@Nullable String sphericalStereoMode,
@Nullable SubtitleInfo subtitleInfo) {
super(name);
this.uri = uri;
this.extension = extension;
@ -76,6 +82,7 @@ import java.util.UUID;
this.drmInfo = drmInfo;
this.adTagUri = adTagUri;
this.sphericalStereoMode = sphericalStereoMode;
this.subtitleInfo = subtitleInfo;
}
@Override
@ -100,6 +107,9 @@ import java.util.UUID;
if (drmInfo != null) {
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) {
if (ACTION_VIEW_LIST.equals(intent.getAction())) {
ArrayList<String> intentUris = new ArrayList<>();

View File

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