Commit 89655088 by aquilescanta Committed by Oliver Woodman

Add support for multiple CC channels in HLS

Issue:#2161

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=148203980
parent e86629ef
...@@ -19,6 +19,7 @@ import android.net.Uri; ...@@ -19,6 +19,7 @@ import android.net.Uri;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.ParserException; import com.google.android.exoplayer2.ParserException;
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;
...@@ -53,12 +54,14 @@ public class HlsMasterPlaylistParserTest extends TestCase { ...@@ -53,12 +54,14 @@ public class HlsMasterPlaylistParserTest extends TestCase {
+ "#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";
public void testParseMasterPlaylist() throws IOException{ private static final String MASTER_PLAYLIST_WITH_CC = " #EXTM3U \n"
HlsPlaylist playlist = parsePlaylist(PLAYLIST_URI, MASTER_PLAYLIST); + "#EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS,LANGUAGE=\"es\",NAME=\"Eng\",INSTREAM-ID=\"SERVICE4\"\n"
assertNotNull(playlist); + "\n"
assertEquals(HlsPlaylist.TYPE_MASTER, playlist.type); + "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128\n"
+ "http://example.com/low.m3u8\n";
HlsMasterPlaylist masterPlaylist = (HlsMasterPlaylist) playlist; public void testParseMasterPlaylist() throws IOException{
HlsMasterPlaylist masterPlaylist = parseMasterPlaylist(PLAYLIST_URI, MASTER_PLAYLIST);
List<HlsMasterPlaylist.HlsUrl> variants = masterPlaylist.variants; List<HlsMasterPlaylist.HlsUrl> variants = masterPlaylist.variants;
assertNotNull(variants); assertNotNull(variants);
...@@ -98,18 +101,28 @@ public class HlsMasterPlaylistParserTest extends TestCase { ...@@ -98,18 +101,28 @@ public class HlsMasterPlaylistParserTest extends TestCase {
public void testPlaylistWithInvalidHeader() throws IOException { public void testPlaylistWithInvalidHeader() throws IOException {
try { try {
parsePlaylist(PLAYLIST_URI, PLAYLIST_WITH_INVALID_HEADER); parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_WITH_INVALID_HEADER);
fail("Expected exception not thrown."); fail("Expected exception not thrown.");
} catch (ParserException e) { } catch (ParserException e) {
// Expected due to invalid header. // Expected due to invalid header.
} }
} }
private static HlsPlaylist parsePlaylist(String uri, String playlistString) throws IOException { public void testPlaylistWithClosedCaption() throws IOException {
HlsMasterPlaylist playlist = parseMasterPlaylist(PLAYLIST_URI, MASTER_PLAYLIST_WITH_CC);
assertEquals(1, playlist.muxedCaptionFormats.size());
Format closedCaptionFormat = playlist.muxedCaptionFormats.get(0);
assertEquals(MimeTypes.APPLICATION_CEA708, closedCaptionFormat.sampleMimeType);
assertEquals(4, closedCaptionFormat.accessibilityChannel);
assertEquals("es", closedCaptionFormat.language);
}
private static HlsMasterPlaylist parseMasterPlaylist(String uri, String playlistString)
throws IOException {
Uri playlistUri = Uri.parse(uri); Uri playlistUri = Uri.parse(uri);
ByteArrayInputStream inputStream = new ByteArrayInputStream( ByteArrayInputStream inputStream = new ByteArrayInputStream(
playlistString.getBytes(Charset.forName(C.UTF8_NAME))); playlistString.getBytes(Charset.forName(C.UTF8_NAME)));
return new HlsPlaylistParser().parse(playlistUri, inputStream); return (HlsMasterPlaylist) new HlsPlaylistParser().parse(playlistUri, inputStream);
} }
} }
...@@ -38,6 +38,7 @@ import com.google.android.exoplayer2.util.Util; ...@@ -38,6 +38,7 @@ import com.google.android.exoplayer2.util.Util;
import java.io.IOException; import java.io.IOException;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.Arrays; import java.util.Arrays;
import java.util.List;
import java.util.Locale; import java.util.Locale;
/** /**
...@@ -85,6 +86,7 @@ import java.util.Locale; ...@@ -85,6 +86,7 @@ import java.util.Locale;
private final HlsUrl[] variants; private final HlsUrl[] variants;
private final HlsPlaylistTracker playlistTracker; private final HlsPlaylistTracker playlistTracker;
private final TrackGroup trackGroup; private final TrackGroup trackGroup;
private final List<Format> muxedCaptionFormats;
private boolean isTimestampMaster; private boolean isTimestampMaster;
private byte[] scratchSpace; private byte[] scratchSpace;
...@@ -107,14 +109,16 @@ import java.util.Locale; ...@@ -107,14 +109,16 @@ 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.
*/ */
public HlsChunkSource(HlsPlaylistTracker playlistTracker, HlsUrl[] variants, public HlsChunkSource(HlsPlaylistTracker playlistTracker, HlsUrl[] variants,
DataSource dataSource, TimestampAdjusterProvider timestampAdjusterProvider) { DataSource dataSource, TimestampAdjusterProvider timestampAdjusterProvider,
List<Format> muxedCaptionFormats) {
this.playlistTracker = playlistTracker; this.playlistTracker = playlistTracker;
this.variants = variants; this.variants = variants;
this.dataSource = dataSource; this.dataSource = dataSource;
this.timestampAdjusterProvider = timestampAdjusterProvider; this.timestampAdjusterProvider = timestampAdjusterProvider;
this.muxedCaptionFormats = muxedCaptionFormats;
Format[] variantFormats = new Format[variants.length]; Format[] variantFormats = new Format[variants.length];
int[] initialTrackSelection = new int[variants.length]; int[] initialTrackSelection = new int[variants.length];
for (int i = 0; i < variants.length; i++) { for (int i = 0; i < variants.length; i++) {
...@@ -282,7 +286,7 @@ import java.util.Locale; ...@@ -282,7 +286,7 @@ import java.util.Locale;
DataSpec dataSpec = new DataSpec(chunkUri, segment.byterangeOffset, segment.byterangeLength, DataSpec dataSpec = new DataSpec(chunkUri, segment.byterangeOffset, segment.byterangeLength,
null); null);
out.chunk = new HlsMediaChunk(dataSource, dataSpec, initDataSpec, selectedUrl, out.chunk = new HlsMediaChunk(dataSource, dataSpec, initDataSpec, selectedUrl,
trackSelection.getSelectionReason(), trackSelection.getSelectionData(), muxedCaptionFormats, trackSelection.getSelectionReason(), trackSelection.getSelectionData(),
startTimeUs, startTimeUs + segment.durationUs, chunkMediaSequence, discontinuitySequence, startTimeUs, startTimeUs + segment.durationUs, chunkMediaSequence, discontinuitySequence,
isTimestampMaster, timestampAdjuster, previous, encryptionKey, encryptionIv); isTimestampMaster, timestampAdjuster, previous, encryptionKey, encryptionIv);
} }
......
...@@ -17,6 +17,7 @@ package com.google.android.exoplayer2.source.hls; ...@@ -17,6 +17,7 @@ package com.google.android.exoplayer2.source.hls;
import android.text.TextUtils; import android.text.TextUtils;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.ParserException; import com.google.android.exoplayer2.ParserException;
import com.google.android.exoplayer2.extractor.DefaultExtractorInput; import com.google.android.exoplayer2.extractor.DefaultExtractorInput;
import com.google.android.exoplayer2.extractor.Extractor; import com.google.android.exoplayer2.extractor.Extractor;
...@@ -39,6 +40,7 @@ import com.google.android.exoplayer2.util.ParsableByteArray; ...@@ -39,6 +40,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.List;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
/** /**
...@@ -84,6 +86,7 @@ import java.util.concurrent.atomic.AtomicInteger; ...@@ -84,6 +86,7 @@ import java.util.concurrent.atomic.AtomicInteger;
private final Extractor previousExtractor; private final Extractor previousExtractor;
private final boolean shouldSpliceIn; private final boolean shouldSpliceIn;
private final boolean needNewExtractor; private final boolean needNewExtractor;
private final List<Format> muxedCaptionFormats;
private final boolean isPackedAudio; private final boolean isPackedAudio;
private final Id3Decoder id3Decoder; private final Id3Decoder id3Decoder;
...@@ -102,6 +105,7 @@ import java.util.concurrent.atomic.AtomicInteger; ...@@ -102,6 +105,7 @@ 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 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.
...@@ -115,17 +119,19 @@ import java.util.concurrent.atomic.AtomicInteger; ...@@ -115,17 +119,19 @@ import java.util.concurrent.atomic.AtomicInteger;
* @param encryptionIv For AES encryption chunks, the encryption initialization vector. * @param encryptionIv For AES encryption chunks, the encryption initialization vector.
*/ */
public HlsMediaChunk(DataSource dataSource, DataSpec dataSpec, DataSpec initDataSpec, public HlsMediaChunk(DataSource dataSource, DataSpec dataSpec, DataSpec initDataSpec,
HlsUrl hlsUrl, int trackSelectionReason, Object trackSelectionData, long startTimeUs, HlsUrl hlsUrl, List<Format> muxedCaptionFormats, int trackSelectionReason,
long endTimeUs, int chunkIndex, int discontinuitySequenceNumber, Object trackSelectionData, long startTimeUs, long endTimeUs, int chunkIndex,
boolean isMasterTimestampSource, TimestampAdjuster timestampAdjuster, int discontinuitySequenceNumber, boolean isMasterTimestampSource,
HlsMediaChunk previousChunk, byte[] encryptionKey, byte[] encryptionIv) { TimestampAdjuster timestampAdjuster, HlsMediaChunk previousChunk, byte[] encryptionKey,
byte[] encryptionIv) {
super(buildDataSource(dataSource, encryptionKey, encryptionIv), dataSpec, hlsUrl.format, super(buildDataSource(dataSource, encryptionKey, encryptionIv), dataSpec, hlsUrl.format,
trackSelectionReason, trackSelectionData, startTimeUs, endTimeUs, chunkIndex); trackSelectionReason, trackSelectionData, startTimeUs, endTimeUs, chunkIndex);
this.discontinuitySequenceNumber = discontinuitySequenceNumber;
this.initDataSpec = initDataSpec; this.initDataSpec = initDataSpec;
this.hlsUrl = hlsUrl; this.hlsUrl = hlsUrl;
this.muxedCaptionFormats = muxedCaptionFormats;
this.isMasterTimestampSource = isMasterTimestampSource; this.isMasterTimestampSource = isMasterTimestampSource;
this.timestampAdjuster = timestampAdjuster; this.timestampAdjuster = timestampAdjuster;
this.discontinuitySequenceNumber = discontinuitySequenceNumber;
// Note: this.dataSource and dataSource may be different. // Note: this.dataSource and dataSource may be different.
this.isEncrypted = this.dataSource instanceof Aes128DataSource; this.isEncrypted = this.dataSource instanceof Aes128DataSource;
lastPathSegment = dataSpec.uri.getLastPathSegment(); lastPathSegment = dataSpec.uri.getLastPathSegment();
...@@ -363,7 +369,7 @@ import java.util.concurrent.atomic.AtomicInteger; ...@@ -363,7 +369,7 @@ import java.util.concurrent.atomic.AtomicInteger;
} }
} }
extractor = new TsExtractor(timestampAdjuster, extractor = new TsExtractor(timestampAdjuster,
new DefaultTsPayloadReaderFactory(esReaderFactoryFlags), true); new DefaultTsPayloadReaderFactory(esReaderFactoryFlags, muxedCaptionFormats), true);
} }
if (usingNewExtractor) { if (usingNewExtractor) {
extractor.init(extractorOutput); extractor.init(extractorOutput);
......
...@@ -317,7 +317,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper ...@@ -317,7 +317,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
HlsUrl[] variants = new HlsMasterPlaylist.HlsUrl[selectedVariants.size()]; HlsUrl[] variants = new HlsMasterPlaylist.HlsUrl[selectedVariants.size()];
selectedVariants.toArray(variants); selectedVariants.toArray(variants);
HlsSampleStreamWrapper sampleStreamWrapper = buildSampleStreamWrapper(C.TRACK_TYPE_DEFAULT, HlsSampleStreamWrapper sampleStreamWrapper = buildSampleStreamWrapper(C.TRACK_TYPE_DEFAULT,
variants, masterPlaylist.muxedAudioFormat, masterPlaylist.muxedCaptionFormat); variants, masterPlaylist.muxedAudioFormat, masterPlaylist.muxedCaptionFormats);
sampleStreamWrappers[currentWrapperIndex++] = sampleStreamWrapper; sampleStreamWrappers[currentWrapperIndex++] = sampleStreamWrapper;
sampleStreamWrapper.setIsTimestampMaster(true); sampleStreamWrapper.setIsTimestampMaster(true);
sampleStreamWrapper.continuePreparing(); sampleStreamWrapper.continuePreparing();
...@@ -343,13 +343,12 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper ...@@ -343,13 +343,12 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
} }
private HlsSampleStreamWrapper buildSampleStreamWrapper(int trackType, HlsUrl[] variants, private HlsSampleStreamWrapper buildSampleStreamWrapper(int trackType, HlsUrl[] variants,
Format muxedAudioFormat, Format muxedCaptionFormat) { Format muxedAudioFormat, List<Format> muxedCaptionFormats) {
DataSource dataSource = dataSourceFactory.createDataSource(); DataSource dataSource = dataSourceFactory.createDataSource();
HlsChunkSource defaultChunkSource = new HlsChunkSource(playlistTracker, variants, dataSource, HlsChunkSource defaultChunkSource = new HlsChunkSource(playlistTracker, variants, dataSource,
timestampAdjusterProvider); timestampAdjusterProvider, muxedCaptionFormats);
return new HlsSampleStreamWrapper(trackType, this, defaultChunkSource, allocator, return new HlsSampleStreamWrapper(trackType, this, defaultChunkSource, allocator,
preparePositionUs, muxedAudioFormat, muxedCaptionFormat, minLoadableRetryCount, preparePositionUs, muxedAudioFormat, minLoadableRetryCount, eventDispatcher);
eventDispatcher);
} }
private void continuePreparingOrLoading() { private void continuePreparingOrLoading() {
......
...@@ -77,7 +77,6 @@ import java.util.LinkedList; ...@@ -77,7 +77,6 @@ import java.util.LinkedList;
private final HlsChunkSource chunkSource; private final HlsChunkSource chunkSource;
private final Allocator allocator; private final Allocator allocator;
private final Format muxedAudioFormat; private final Format muxedAudioFormat;
private final Format muxedCaptionFormat;
private final int minLoadableRetryCount; private final int minLoadableRetryCount;
private final Loader loader; private final Loader loader;
private final EventDispatcher eventDispatcher; private final EventDispatcher eventDispatcher;
...@@ -113,21 +112,18 @@ import java.util.LinkedList; ...@@ -113,21 +112,18 @@ import java.util.LinkedList;
* @param allocator An {@link Allocator} from which to obtain media buffer allocations. * @param allocator An {@link Allocator} from which to obtain media buffer allocations.
* @param positionUs The position from which to start loading media. * @param positionUs The position from which to start loading media.
* @param muxedAudioFormat Optional muxed audio {@link Format} as defined by the master playlist. * @param muxedAudioFormat Optional muxed audio {@link Format} as defined by the master playlist.
* @param muxedCaptionFormat Optional muxed closed caption {@link Format} as defined by the master
* playlist.
* @param minLoadableRetryCount The minimum number of times that the source should retry a load * @param minLoadableRetryCount The minimum number of times that the source should retry a load
* before propagating an error. * before propagating an error.
* @param eventDispatcher A dispatcher to notify of events. * @param eventDispatcher A dispatcher to notify of events.
*/ */
public HlsSampleStreamWrapper(int trackType, Callback callback, HlsChunkSource chunkSource, public HlsSampleStreamWrapper(int trackType, Callback callback, HlsChunkSource chunkSource,
Allocator allocator, long positionUs, Format muxedAudioFormat, Format muxedCaptionFormat, Allocator allocator, long positionUs, Format muxedAudioFormat, int minLoadableRetryCount,
int minLoadableRetryCount, EventDispatcher eventDispatcher) { EventDispatcher eventDispatcher) {
this.trackType = trackType; this.trackType = trackType;
this.callback = callback; this.callback = callback;
this.chunkSource = chunkSource; this.chunkSource = chunkSource;
this.allocator = allocator; this.allocator = allocator;
this.muxedAudioFormat = muxedAudioFormat; this.muxedAudioFormat = muxedAudioFormat;
this.muxedCaptionFormat = muxedCaptionFormat;
this.minLoadableRetryCount = minLoadableRetryCount; this.minLoadableRetryCount = minLoadableRetryCount;
this.eventDispatcher = eventDispatcher; this.eventDispatcher = eventDispatcher;
loader = new Loader("Loader:HlsSampleStreamWrapper"); loader = new Loader("Loader:HlsSampleStreamWrapper");
...@@ -589,14 +585,8 @@ import java.util.LinkedList; ...@@ -589,14 +585,8 @@ import java.util.LinkedList;
trackGroups[i] = new TrackGroup(formats); trackGroups[i] = new TrackGroup(formats);
primaryTrackGroupIndex = i; primaryTrackGroupIndex = i;
} else { } else {
Format trackFormat = null; Format trackFormat = primaryExtractorTrackType == PRIMARY_TYPE_VIDEO
if (primaryExtractorTrackType == PRIMARY_TYPE_VIDEO) { && MimeTypes.isAudio(sampleFormat.sampleMimeType) ? muxedAudioFormat : null;
if (MimeTypes.isAudio(sampleFormat.sampleMimeType)) {
trackFormat = muxedAudioFormat;
} else if (MimeTypes.APPLICATION_CEA608.equals(sampleFormat.sampleMimeType)) {
trackFormat = muxedCaptionFormat;
}
}
trackGroups[i] = new TrackGroup(deriveFormat(trackFormat, sampleFormat)); trackGroups[i] = new TrackGroup(deriveFormat(trackFormat, sampleFormat));
} }
} }
......
...@@ -52,22 +52,23 @@ public final class HlsMasterPlaylist extends HlsPlaylist { ...@@ -52,22 +52,23 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
public final List<HlsUrl> subtitles; public final List<HlsUrl> subtitles;
public final Format muxedAudioFormat; public final Format muxedAudioFormat;
public final Format muxedCaptionFormat; public final List<Format> 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, Format muxedCaptionFormat) { List<HlsUrl> subtitles, Format muxedAudioFormat, List<Format> muxedCaptionFormats) {
super(baseUri, HlsPlaylist.TYPE_MASTER); super(baseUri, HlsPlaylist.TYPE_MASTER);
this.variants = Collections.unmodifiableList(variants); this.variants = Collections.unmodifiableList(variants);
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.muxedCaptionFormat = muxedCaptionFormat; this.muxedCaptionFormats = Collections.unmodifiableList(muxedCaptionFormats);
} }
public static HlsMasterPlaylist createSingleVariantMasterPlaylist(String variantUri) { public static HlsMasterPlaylist createSingleVariantMasterPlaylist(String variantUri) {
List<HlsUrl> variant = Collections.singletonList(HlsUrl.createMediaPlaylistHlsUrl(variantUri)); List<HlsUrl> variant = Collections.singletonList(HlsUrl.createMediaPlaylistHlsUrl(variantUri));
List<HlsUrl> emptyList = Collections.emptyList(); List<HlsUrl> emptyList = Collections.emptyList();
return new HlsMasterPlaylist(null, variant, emptyList, emptyList, null, null); return new HlsMasterPlaylist(null, variant, emptyList, emptyList, null,
Collections.<Format>emptyList());
} }
} }
...@@ -94,7 +94,8 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli ...@@ -94,7 +94,8 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
+ "|" + TYPE_SUBTITLES + "|" + TYPE_CLOSED_CAPTIONS + ")"); + "|" + TYPE_SUBTITLES + "|" + TYPE_CLOSED_CAPTIONS + ")");
private static final Pattern REGEX_LANGUAGE = Pattern.compile("LANGUAGE=\"(.+?)\""); private static final Pattern REGEX_LANGUAGE = Pattern.compile("LANGUAGE=\"(.+?)\"");
private static final Pattern REGEX_NAME = Pattern.compile("NAME=\"(.+?)\""); private static final Pattern REGEX_NAME = Pattern.compile("NAME=\"(.+?)\"");
private static final Pattern REGEX_INSTREAM_ID = Pattern.compile("INSTREAM-ID=\"(.+?)\""); private static final Pattern REGEX_INSTREAM_ID =
Pattern.compile("INSTREAM-ID=\"((?:CC|SERVICE)\\d+)\"");
private static final Pattern REGEX_AUTOSELECT = compileBooleanAttrPattern("AUTOSELECT"); private static final Pattern REGEX_AUTOSELECT = compileBooleanAttrPattern("AUTOSELECT");
private static final Pattern REGEX_DEFAULT = compileBooleanAttrPattern("DEFAULT"); private static final Pattern REGEX_DEFAULT = compileBooleanAttrPattern("DEFAULT");
private static final Pattern REGEX_FORCED = compileBooleanAttrPattern("FORCED"); private static final Pattern REGEX_FORCED = compileBooleanAttrPattern("FORCED");
...@@ -171,7 +172,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli ...@@ -171,7 +172,7 @@ 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;
Format muxedCaptionFormat = null; ArrayList<Format> muxedCaptionFormats = new ArrayList<>();
String line; String line;
while (iterator.hasNext()) { while (iterator.hasNext()) {
...@@ -198,10 +199,18 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli ...@@ -198,10 +199,18 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
subtitles.add(new HlsMasterPlaylist.HlsUrl(uri, format)); subtitles.add(new HlsMasterPlaylist.HlsUrl(uri, format));
break; break;
case TYPE_CLOSED_CAPTIONS: case TYPE_CLOSED_CAPTIONS:
if ("CC1".equals(parseOptionalStringAttr(line, REGEX_INSTREAM_ID))) { String instreamId = parseStringAttr(line, REGEX_INSTREAM_ID);
muxedCaptionFormat = Format.createTextContainerFormat(id, MimeTypes.APPLICATION_M3U8, String mimeType;
MimeTypes.APPLICATION_CEA608, null, Format.NO_VALUE, selectionFlags, language); int accessibilityChannel;
if (instreamId.startsWith("CC")) {
mimeType = MimeTypes.APPLICATION_CEA608;
accessibilityChannel = Integer.parseInt(instreamId.substring(2));
} else /* starts with SERVICE */ {
mimeType = MimeTypes.APPLICATION_CEA708;
accessibilityChannel = Integer.parseInt(instreamId.substring(7));
} }
muxedCaptionFormats.add(Format.createTextContainerFormat(id, null, mimeType, null,
Format.NO_VALUE, selectionFlags, language, accessibilityChannel));
break; break;
default: default:
// Do nothing. // Do nothing.
...@@ -234,7 +243,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli ...@@ -234,7 +243,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
} }
} }
return new HlsMasterPlaylist(baseUri, variants, audios, subtitles, muxedAudioFormat, return new HlsMasterPlaylist(baseUri, variants, audios, subtitles, muxedAudioFormat,
muxedCaptionFormat); muxedCaptionFormats);
} }
@C.SelectionFlags @C.SelectionFlags
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment