Expose no CC tracks if CLOSED-CAPTIONS=NONE is present

Issue:#2743

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=155182859
This commit is contained in:
aquilescanta 2017-05-05 03:49:22 -07:00 committed by Oliver Woodman
parent 4c39627ed1
commit 5d459c8103
5 changed files with 90 additions and 13 deletions

View File

@ -23,6 +23,7 @@ import com.google.android.exoplayer2.util.MimeTypes;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Collections;
import java.util.List;
import junit.framework.TestCase;
@ -56,16 +57,22 @@ public class HlsMasterPlaylistParserTest extends TestCase {
private static final String MASTER_PLAYLIST_WITH_CC = " #EXTM3U \n"
+ "#EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS,LANGUAGE=\"es\",NAME=\"Eng\",INSTREAM-ID=\"SERVICE4\"\n"
+ "\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128\n"
+ "http://example.com/low.m3u8\n";
private static final String MASTER_PLAYLIST_WITHOUT_CC = " #EXTM3U \n"
+ "#EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS,LANGUAGE=\"es\",NAME=\"Eng\",INSTREAM-ID=\"SERVICE4\"\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128,"
+ "CLOSED-CAPTIONS=NONE\n"
+ "http://example.com/low.m3u8\n";
public void testParseMasterPlaylist() throws IOException{
HlsMasterPlaylist masterPlaylist = parseMasterPlaylist(PLAYLIST_URI, MASTER_PLAYLIST);
List<HlsMasterPlaylist.HlsUrl> variants = masterPlaylist.variants;
assertNotNull(variants);
assertEquals(5, variants.size());
assertNull(masterPlaylist.muxedCaptionFormats);
assertEquals(1280000, variants.get(0).format.bitrate);
assertNotNull(variants.get(0).format.codecs);
@ -117,6 +124,11 @@ public class HlsMasterPlaylistParserTest extends TestCase {
assertEquals("es", closedCaptionFormat.language);
}
public void testPlaylistWithoutClosedCaptions() throws IOException {
HlsMasterPlaylist playlist = parseMasterPlaylist(PLAYLIST_URI, MASTER_PLAYLIST_WITHOUT_CC);
assertEquals(Collections.emptyList(), playlist.muxedCaptionFormats);
}
private static HlsMasterPlaylist parseMasterPlaylist(String uri, String playlistString)
throws IOException {
Uri playlistUri = Uri.parse(uri);

View File

@ -111,7 +111,8 @@ import java.util.Locale;
* @param timestampAdjusterProvider A provider of {@link TimestampAdjuster} instances. If
* multiple {@link HlsChunkSource}s are used for a single playback, they should all share the
* same provider.
* @param muxedCaptionFormats List of muxed caption {@link Format}s.
* @param muxedCaptionFormats List of muxed caption {@link Format}s. Null if no closed caption
* information is available in the master playlist.
*/
public HlsChunkSource(HlsPlaylistTracker playlistTracker, HlsUrl[] variants,
HlsDataSourceFactory dataSourceFactory, TimestampAdjusterProvider timestampAdjusterProvider,

View File

@ -39,6 +39,7 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
import com.google.android.exoplayer2.util.TimestampAdjuster;
import com.google.android.exoplayer2.util.Util;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
@ -104,7 +105,8 @@ import java.util.concurrent.atomic.AtomicInteger;
* @param dataSpec Defines the data to be loaded.
* @param initDataSpec Defines the initialization data to be fed to new extractors. May be null.
* @param hlsUrl The url of the playlist from which this chunk was obtained.
* @param muxedCaptionFormats List of muxed caption {@link Format}s.
* @param muxedCaptionFormats List of muxed caption {@link Format}s. Null if no closed caption
* information is available in the master playlist.
* @param trackSelectionReason See {@link #trackSelectionReason}.
* @param trackSelectionData See {@link #trackSelectionData}.
* @param startTimeUs The start time of the chunk in microseconds.
@ -356,9 +358,12 @@ import java.util.concurrent.atomic.AtomicInteger;
// This flag ensures the change of pid between streams does not affect the sample queues.
@DefaultTsPayloadReaderFactory.Flags
int esReaderFactoryFlags = DefaultTsPayloadReaderFactory.FLAG_IGNORE_SPLICE_INFO_STREAM;
if (!muxedCaptionFormats.isEmpty()) {
List<Format> closedCaptionFormats = muxedCaptionFormats;
if (closedCaptionFormats != null) {
// The playlist declares closed caption renditions, we should ignore descriptors.
esReaderFactoryFlags |= DefaultTsPayloadReaderFactory.FLAG_OVERRIDE_CAPTION_DESCRIPTORS;
} else {
closedCaptionFormats = Collections.emptyList();
}
String codecs = trackFormat.codecs;
if (!TextUtils.isEmpty(codecs)) {
@ -373,7 +378,7 @@ import java.util.concurrent.atomic.AtomicInteger;
}
}
extractor = new TsExtractor(TsExtractor.MODE_HLS, timestampAdjuster,
new DefaultTsPayloadReaderFactory(esReaderFactoryFlags, muxedCaptionFormats));
new DefaultTsPayloadReaderFactory(esReaderFactoryFlags, closedCaptionFormats));
}
if (usingNewExtractor) {
extractor.init(extractorOutput);

View File

@ -30,15 +30,31 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
*/
public static final class HlsUrl {
/**
* The http url from which the media playlist can be obtained.
*/
public final String url;
/**
* Format information associated with the HLS url.
*/
public final Format format;
public static HlsUrl createMediaPlaylistHlsUrl(String baseUri) {
/**
* Creates an HLS url from a given http url.
*
* @param url The url.
* @return An HLS url.
*/
public static HlsUrl createMediaPlaylistHlsUrl(String url) {
Format format = Format.createContainerFormat("0", MimeTypes.APPLICATION_M3U8, null, null,
Format.NO_VALUE, 0, null);
return new HlsUrl(baseUri, format);
return new HlsUrl(url, format);
}
/**
* @param url See {@link #url}.
* @param format See {@link #format}.
*/
public HlsUrl(String url, Format format) {
this.url = url;
this.format = format;
@ -46,13 +62,39 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
}
/**
* The list of variants declared by the playlist.
*/
public final List<HlsUrl> variants;
/**
* The list of demuxed audios declared by the playlist.
*/
public final List<HlsUrl> audios;
/**
* The list of subtitles declared by the playlist.
*/
public final List<HlsUrl> subtitles;
/**
* The format of the audio muxed in the variants. May be null if the playlist does not declare any
* muxed audio.
*/
public final Format muxedAudioFormat;
/**
* The format of the closed captions declared by the playlist. May be empty if the playlist
* explicitly declares no captions are available, or null if the playlist does not declare any
* captions information.
*/
public final List<Format> muxedCaptionFormats;
/**
* @param baseUri The base uri. Used to resolve relative paths.
* @param variants See {@link #variants}.
* @param audios See {@link #audios}.
* @param subtitles See {@link #subtitles}.
* @param muxedAudioFormat See {@link #muxedAudioFormat}.
* @param muxedCaptionFormats See {@link #muxedCaptionFormats}.
*/
public HlsMasterPlaylist(String baseUri, List<HlsUrl> variants, List<HlsUrl> audios,
List<HlsUrl> subtitles, Format muxedAudioFormat, List<Format> muxedCaptionFormats) {
super(baseUri);
@ -60,14 +102,20 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
this.audios = Collections.unmodifiableList(audios);
this.subtitles = Collections.unmodifiableList(subtitles);
this.muxedAudioFormat = muxedAudioFormat;
this.muxedCaptionFormats = Collections.unmodifiableList(muxedCaptionFormats);
this.muxedCaptionFormats = muxedCaptionFormats != null
? Collections.unmodifiableList(muxedCaptionFormats) : null;
}
public static HlsMasterPlaylist createSingleVariantMasterPlaylist(String variantUri) {
List<HlsUrl> variant = Collections.singletonList(HlsUrl.createMediaPlaylistHlsUrl(variantUri));
/**
* Creates a playlist with a single variant.
*
* @param variantUrl The url of the single variant.
* @return A master playlist with a single variant for the provided url.
*/
public static HlsMasterPlaylist createSingleVariantMasterPlaylist(String variantUrl) {
List<HlsUrl> variant = Collections.singletonList(HlsUrl.createMediaPlaylistHlsUrl(variantUrl));
List<HlsUrl> emptyList = Collections.emptyList();
return new HlsMasterPlaylist(null, variant, emptyList, emptyList, null,
Collections.<Format>emptyList());
return new HlsMasterPlaylist(null, variant, emptyList, emptyList, null, null);
}
}

View File

@ -29,6 +29,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
@ -70,6 +71,8 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
private static final String BOOLEAN_TRUE = "YES";
private static final String BOOLEAN_FALSE = "NO";
private static final String ATTR_CLOSED_CAPTIONS_NONE = "CLOSED-CAPTIONS=NONE";
private static final Pattern REGEX_BANDWIDTH = Pattern.compile("BANDWIDTH=(\\d+)\\b");
private static final Pattern REGEX_CODECS = Pattern.compile("CODECS=\"(.+?)\"");
private static final Pattern REGEX_RESOLUTION = Pattern.compile("RESOLUTION=(\\d+x\\d+)");
@ -173,7 +176,8 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
ArrayList<HlsMasterPlaylist.HlsUrl> audios = new ArrayList<>();
ArrayList<HlsMasterPlaylist.HlsUrl> subtitles = new ArrayList<>();
Format muxedAudioFormat = null;
ArrayList<Format> muxedCaptionFormats = new ArrayList<>();
List<Format> muxedCaptionFormats = null;
boolean noClosedCaptions = false;
String line;
while (iterator.hasNext()) {
@ -210,6 +214,9 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
mimeType = MimeTypes.APPLICATION_CEA708;
accessibilityChannel = Integer.parseInt(instreamId.substring(7));
}
if (muxedCaptionFormats == null) {
muxedCaptionFormats = new ArrayList<>();
}
muxedCaptionFormats.add(Format.createTextContainerFormat(id, null, mimeType, null,
Format.NO_VALUE, selectionFlags, language, accessibilityChannel));
break;
@ -221,6 +228,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
int bitrate = parseIntAttr(line, REGEX_BANDWIDTH);
String codecs = parseOptionalStringAttr(line, REGEX_CODECS);
String resolutionString = parseOptionalStringAttr(line, REGEX_RESOLUTION);
noClosedCaptions |= line.contains(ATTR_CLOSED_CAPTIONS_NONE);
int width;
int height;
if (resolutionString != null) {
@ -243,6 +251,9 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
variants.add(new HlsMasterPlaylist.HlsUrl(line, format));
}
}
if (noClosedCaptions) {
muxedCaptionFormats = Collections.emptyList();
}
return new HlsMasterPlaylist(baseUri, variants, audios, subtitles, muxedAudioFormat,
muxedCaptionFormats);
}