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:
parent
4c39627ed1
commit
5d459c8103
@ -23,6 +23,7 @@ import com.google.android.exoplayer2.util.MimeTypes;
|
|||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
@ -56,16 +57,22 @@ public class HlsMasterPlaylistParserTest extends TestCase {
|
|||||||
|
|
||||||
private static final String MASTER_PLAYLIST_WITH_CC = " #EXTM3U \n"
|
private static final String MASTER_PLAYLIST_WITH_CC = " #EXTM3U \n"
|
||||||
+ "#EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS,LANGUAGE=\"es\",NAME=\"Eng\",INSTREAM-ID=\"SERVICE4\"\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"
|
+ "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128\n"
|
||||||
+ "http://example.com/low.m3u8\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{
|
public void testParseMasterPlaylist() throws IOException{
|
||||||
HlsMasterPlaylist masterPlaylist = parseMasterPlaylist(PLAYLIST_URI, MASTER_PLAYLIST);
|
HlsMasterPlaylist masterPlaylist = parseMasterPlaylist(PLAYLIST_URI, MASTER_PLAYLIST);
|
||||||
|
|
||||||
List<HlsMasterPlaylist.HlsUrl> variants = masterPlaylist.variants;
|
List<HlsMasterPlaylist.HlsUrl> variants = masterPlaylist.variants;
|
||||||
assertNotNull(variants);
|
assertNotNull(variants);
|
||||||
assertEquals(5, variants.size());
|
assertEquals(5, variants.size());
|
||||||
|
assertNull(masterPlaylist.muxedCaptionFormats);
|
||||||
|
|
||||||
assertEquals(1280000, variants.get(0).format.bitrate);
|
assertEquals(1280000, variants.get(0).format.bitrate);
|
||||||
assertNotNull(variants.get(0).format.codecs);
|
assertNotNull(variants.get(0).format.codecs);
|
||||||
@ -117,6 +124,11 @@ public class HlsMasterPlaylistParserTest extends TestCase {
|
|||||||
assertEquals("es", closedCaptionFormat.language);
|
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)
|
private static HlsMasterPlaylist parseMasterPlaylist(String uri, String playlistString)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
Uri playlistUri = Uri.parse(uri);
|
Uri playlistUri = Uri.parse(uri);
|
||||||
|
@ -111,7 +111,8 @@ import java.util.Locale;
|
|||||||
* @param timestampAdjusterProvider A provider of {@link TimestampAdjuster} instances. If
|
* @param timestampAdjusterProvider A provider of {@link TimestampAdjuster} instances. If
|
||||||
* multiple {@link HlsChunkSource}s are used for a single playback, they should all share the
|
* multiple {@link HlsChunkSource}s are used for a single playback, they should all share the
|
||||||
* same provider.
|
* 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,
|
public HlsChunkSource(HlsPlaylistTracker playlistTracker, HlsUrl[] variants,
|
||||||
HlsDataSourceFactory dataSourceFactory, TimestampAdjusterProvider timestampAdjusterProvider,
|
HlsDataSourceFactory dataSourceFactory, TimestampAdjusterProvider timestampAdjusterProvider,
|
||||||
|
@ -39,6 +39,7 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
|
|||||||
import com.google.android.exoplayer2.util.TimestampAdjuster;
|
import com.google.android.exoplayer2.util.TimestampAdjuster;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
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 dataSpec Defines the data to be loaded.
|
||||||
* @param initDataSpec Defines the initialization data to be fed to new extractors. May be null.
|
* @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 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 trackSelectionReason See {@link #trackSelectionReason}.
|
||||||
* @param trackSelectionData See {@link #trackSelectionData}.
|
* @param trackSelectionData See {@link #trackSelectionData}.
|
||||||
* @param startTimeUs The start time of the chunk in microseconds.
|
* @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.
|
// This flag ensures the change of pid between streams does not affect the sample queues.
|
||||||
@DefaultTsPayloadReaderFactory.Flags
|
@DefaultTsPayloadReaderFactory.Flags
|
||||||
int esReaderFactoryFlags = DefaultTsPayloadReaderFactory.FLAG_IGNORE_SPLICE_INFO_STREAM;
|
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.
|
// The playlist declares closed caption renditions, we should ignore descriptors.
|
||||||
esReaderFactoryFlags |= DefaultTsPayloadReaderFactory.FLAG_OVERRIDE_CAPTION_DESCRIPTORS;
|
esReaderFactoryFlags |= DefaultTsPayloadReaderFactory.FLAG_OVERRIDE_CAPTION_DESCRIPTORS;
|
||||||
|
} else {
|
||||||
|
closedCaptionFormats = Collections.emptyList();
|
||||||
}
|
}
|
||||||
String codecs = trackFormat.codecs;
|
String codecs = trackFormat.codecs;
|
||||||
if (!TextUtils.isEmpty(codecs)) {
|
if (!TextUtils.isEmpty(codecs)) {
|
||||||
@ -373,7 +378,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
extractor = new TsExtractor(TsExtractor.MODE_HLS, timestampAdjuster,
|
extractor = new TsExtractor(TsExtractor.MODE_HLS, timestampAdjuster,
|
||||||
new DefaultTsPayloadReaderFactory(esReaderFactoryFlags, muxedCaptionFormats));
|
new DefaultTsPayloadReaderFactory(esReaderFactoryFlags, closedCaptionFormats));
|
||||||
}
|
}
|
||||||
if (usingNewExtractor) {
|
if (usingNewExtractor) {
|
||||||
extractor.init(extractorOutput);
|
extractor.init(extractorOutput);
|
||||||
|
@ -30,15 +30,31 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
|
|||||||
*/
|
*/
|
||||||
public static final class HlsUrl {
|
public static final class HlsUrl {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The http url from which the media playlist can be obtained.
|
||||||
|
*/
|
||||||
public final String url;
|
public final String url;
|
||||||
|
/**
|
||||||
|
* Format information associated with the HLS url.
|
||||||
|
*/
|
||||||
public final Format format;
|
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 format = Format.createContainerFormat("0", MimeTypes.APPLICATION_M3U8, null, null,
|
||||||
Format.NO_VALUE, 0, 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) {
|
public HlsUrl(String url, Format format) {
|
||||||
this.url = url;
|
this.url = url;
|
||||||
this.format = format;
|
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;
|
public final List<HlsUrl> variants;
|
||||||
|
/**
|
||||||
|
* The list of demuxed audios declared by the playlist.
|
||||||
|
*/
|
||||||
public final List<HlsUrl> audios;
|
public final List<HlsUrl> audios;
|
||||||
|
/**
|
||||||
|
* The list of subtitles declared by the playlist.
|
||||||
|
*/
|
||||||
public final List<HlsUrl> subtitles;
|
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;
|
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;
|
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,
|
public HlsMasterPlaylist(String baseUri, List<HlsUrl> variants, List<HlsUrl> audios,
|
||||||
List<HlsUrl> subtitles, Format muxedAudioFormat, List<Format> muxedCaptionFormats) {
|
List<HlsUrl> subtitles, Format muxedAudioFormat, List<Format> muxedCaptionFormats) {
|
||||||
super(baseUri);
|
super(baseUri);
|
||||||
@ -60,14 +102,20 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
|
|||||||
this.audios = Collections.unmodifiableList(audios);
|
this.audios = Collections.unmodifiableList(audios);
|
||||||
this.subtitles = Collections.unmodifiableList(subtitles);
|
this.subtitles = Collections.unmodifiableList(subtitles);
|
||||||
this.muxedAudioFormat = muxedAudioFormat;
|
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();
|
List<HlsUrl> emptyList = Collections.emptyList();
|
||||||
return new HlsMasterPlaylist(null, variant, emptyList, emptyList, null,
|
return new HlsMasterPlaylist(null, variant, emptyList, emptyList, null, null);
|
||||||
Collections.<Format>emptyList());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@ import java.io.IOException;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Queue;
|
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_TRUE = "YES";
|
||||||
private static final String BOOLEAN_FALSE = "NO";
|
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_BANDWIDTH = Pattern.compile("BANDWIDTH=(\\d+)\\b");
|
||||||
private static final Pattern REGEX_CODECS = Pattern.compile("CODECS=\"(.+?)\"");
|
private static final Pattern REGEX_CODECS = Pattern.compile("CODECS=\"(.+?)\"");
|
||||||
private static final Pattern REGEX_RESOLUTION = Pattern.compile("RESOLUTION=(\\d+x\\d+)");
|
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> audios = new ArrayList<>();
|
||||||
ArrayList<HlsMasterPlaylist.HlsUrl> subtitles = new ArrayList<>();
|
ArrayList<HlsMasterPlaylist.HlsUrl> subtitles = new ArrayList<>();
|
||||||
Format muxedAudioFormat = null;
|
Format muxedAudioFormat = null;
|
||||||
ArrayList<Format> muxedCaptionFormats = new ArrayList<>();
|
List<Format> muxedCaptionFormats = null;
|
||||||
|
boolean noClosedCaptions = false;
|
||||||
|
|
||||||
String line;
|
String line;
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
@ -210,6 +214,9 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
|
|||||||
mimeType = MimeTypes.APPLICATION_CEA708;
|
mimeType = MimeTypes.APPLICATION_CEA708;
|
||||||
accessibilityChannel = Integer.parseInt(instreamId.substring(7));
|
accessibilityChannel = Integer.parseInt(instreamId.substring(7));
|
||||||
}
|
}
|
||||||
|
if (muxedCaptionFormats == null) {
|
||||||
|
muxedCaptionFormats = new ArrayList<>();
|
||||||
|
}
|
||||||
muxedCaptionFormats.add(Format.createTextContainerFormat(id, null, mimeType, null,
|
muxedCaptionFormats.add(Format.createTextContainerFormat(id, null, mimeType, null,
|
||||||
Format.NO_VALUE, selectionFlags, language, accessibilityChannel));
|
Format.NO_VALUE, selectionFlags, language, accessibilityChannel));
|
||||||
break;
|
break;
|
||||||
@ -221,6 +228,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
|
|||||||
int bitrate = parseIntAttr(line, REGEX_BANDWIDTH);
|
int bitrate = parseIntAttr(line, REGEX_BANDWIDTH);
|
||||||
String codecs = parseOptionalStringAttr(line, REGEX_CODECS);
|
String codecs = parseOptionalStringAttr(line, REGEX_CODECS);
|
||||||
String resolutionString = parseOptionalStringAttr(line, REGEX_RESOLUTION);
|
String resolutionString = parseOptionalStringAttr(line, REGEX_RESOLUTION);
|
||||||
|
noClosedCaptions |= line.contains(ATTR_CLOSED_CAPTIONS_NONE);
|
||||||
int width;
|
int width;
|
||||||
int height;
|
int height;
|
||||||
if (resolutionString != null) {
|
if (resolutionString != null) {
|
||||||
@ -243,6 +251,9 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
|
|||||||
variants.add(new HlsMasterPlaylist.HlsUrl(line, format));
|
variants.add(new HlsMasterPlaylist.HlsUrl(line, format));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (noClosedCaptions) {
|
||||||
|
muxedCaptionFormats = Collections.emptyList();
|
||||||
|
}
|
||||||
return new HlsMasterPlaylist(baseUri, variants, audios, subtitles, muxedAudioFormat,
|
return new HlsMasterPlaylist(baseUri, variants, audios, subtitles, muxedAudioFormat,
|
||||||
muxedCaptionFormats);
|
muxedCaptionFormats);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user