Commit 6dbce881 by Oliver Woodman

Merge branch 'dev-v2-id3' of persistent-https://github.com/google/ExoPlayer into dev-v2-id3

parents 00f9fc67 929620c1
Showing with 721 additions and 230 deletions
...@@ -19,7 +19,7 @@ buildscript { ...@@ -19,7 +19,7 @@ buildscript {
jcenter() jcenter()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:2.1.2' classpath 'com.android.tools.build:gradle:2.2.1'
classpath 'com.novoda:bintray-release:0.3.4' classpath 'com.novoda:bintray-release:0.3.4'
} }
} }
......
...@@ -37,7 +37,7 @@ android { ...@@ -37,7 +37,7 @@ android {
dependencies { dependencies {
compile project(':library') compile project(':library')
compile('com.squareup.okhttp3:okhttp:+') { compile('com.squareup.okhttp3:okhttp:3.4.1') {
exclude group: 'org.json' exclude group: 'org.json'
} }
} }
......
...@@ -65,7 +65,8 @@ public class OkHttpDataSource implements HttpDataSource { ...@@ -65,7 +65,8 @@ public class OkHttpDataSource implements HttpDataSource {
private long bytesRead; private long bytesRead;
/** /**
* @param callFactory A {@link Call.Factory} for use by the source. * @param callFactory A {@link Call.Factory} (typically an {@link okhttp3.OkHttpClient}) for use
* by the source.
* @param userAgent The User-Agent string that should be used. * @param userAgent The User-Agent string that should be used.
* @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the * @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the
* predicate then a InvalidContentTypeException} is thrown from {@link #open(DataSpec)}. * predicate then a InvalidContentTypeException} is thrown from {@link #open(DataSpec)}.
...@@ -76,7 +77,8 @@ public class OkHttpDataSource implements HttpDataSource { ...@@ -76,7 +77,8 @@ public class OkHttpDataSource implements HttpDataSource {
} }
/** /**
* @param callFactory A {@link Call.Factory} for use by the source. * @param callFactory A {@link Call.Factory} (typically an {@link okhttp3.OkHttpClient}) for use
* by the source.
* @param userAgent The User-Agent string that should be used. * @param userAgent The User-Agent string that should be used.
* @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the * @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the
* predicate then a {@link InvalidContentTypeException} is thrown from * predicate then a {@link InvalidContentTypeException} is thrown from
...@@ -89,14 +91,14 @@ public class OkHttpDataSource implements HttpDataSource { ...@@ -89,14 +91,14 @@ public class OkHttpDataSource implements HttpDataSource {
} }
/** /**
* @param callFactory An {@link Call.Factory} for use by the source. * @param callFactory A {@link Call.Factory} (typically an {@link okhttp3.OkHttpClient}) for use
* by the source.
* @param userAgent The User-Agent string that should be used. * @param userAgent The User-Agent string that should be used.
* @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the * @param contentTypePredicate An optional {@link Predicate}. If a content type is rejected by the
* predicate then a {@link InvalidContentTypeException} is thrown from * predicate then a {@link InvalidContentTypeException} is thrown from
* {@link #open(DataSpec)}. * {@link #open(DataSpec)}.
* @param listener An optional listener. * @param listener An optional listener.
* @param cacheControl An optional {@link CacheControl} which sets all requests' Cache-Control * @param cacheControl An optional {@link CacheControl} for setting the Cache-Control header.
* header. For example, you could force the network response for all requests.
*/ */
public OkHttpDataSource(Call.Factory callFactory, String userAgent, public OkHttpDataSource(Call.Factory callFactory, String userAgent,
Predicate<String> contentTypePredicate, TransferListener<? super OkHttpDataSource> listener, Predicate<String> contentTypePredicate, TransferListener<? super OkHttpDataSource> listener,
......
...@@ -28,25 +28,38 @@ public final class OkHttpDataSourceFactory implements Factory { ...@@ -28,25 +28,38 @@ public final class OkHttpDataSourceFactory implements Factory {
private final Call.Factory callFactory; private final Call.Factory callFactory;
private final String userAgent; private final String userAgent;
private final TransferListener<? super DataSource> transferListener; private final TransferListener<? super DataSource> listener;
private final CacheControl cacheControl; private final CacheControl cacheControl;
/**
* @param callFactory A {@link Call.Factory} (typically an {@link okhttp3.OkHttpClient}) for use
* by the sources created by the factory.
* @param userAgent The User-Agent string that should be used.
* @param listener An optional listener.
*/
public OkHttpDataSourceFactory(Call.Factory callFactory, String userAgent, public OkHttpDataSourceFactory(Call.Factory callFactory, String userAgent,
TransferListener<? super DataSource> transferListener) { TransferListener<? super DataSource> listener) {
this(callFactory, userAgent, transferListener, null); this(callFactory, userAgent, listener, null);
} }
/**
* @param callFactory A {@link Call.Factory} (typically an {@link okhttp3.OkHttpClient}) for use
* by the sources created by the factory.
* @param userAgent The User-Agent string that should be used.
* @param listener An optional listener.
* @param cacheControl An optional {@link CacheControl} for setting the Cache-Control header.
*/
public OkHttpDataSourceFactory(Call.Factory callFactory, String userAgent, public OkHttpDataSourceFactory(Call.Factory callFactory, String userAgent,
TransferListener<? super DataSource> transferListener, CacheControl cacheControl) { TransferListener<? super DataSource> listener, CacheControl cacheControl) {
this.callFactory = callFactory; this.callFactory = callFactory;
this.userAgent = userAgent; this.userAgent = userAgent;
this.transferListener = transferListener; this.listener = listener;
this.cacheControl = cacheControl; this.cacheControl = cacheControl;
} }
@Override @Override
public OkHttpDataSource createDataSource() { public OkHttpDataSource createDataSource() {
return new OkHttpDataSource(callFactory, userAgent, null, transferListener, cacheControl); return new OkHttpDataSource(callFactory, userAgent, null, listener, cacheControl);
} }
} }
...@@ -73,10 +73,13 @@ import javax.microedition.khronos.opengles.GL10; ...@@ -73,10 +73,13 @@ import javax.microedition.khronos.opengles.GL10;
private final int[] yuvTextures = new int[3]; private final int[] yuvTextures = new int[3];
private final AtomicReference<VpxOutputBuffer> pendingOutputBufferReference; private final AtomicReference<VpxOutputBuffer> pendingOutputBufferReference;
// Kept in a field rather than a local variable so that it doesn't get garbage collected before
// glDrawArrays uses it.
@SuppressWarnings("FieldCanBeLocal")
private FloatBuffer textureCoords;
private int program; private int program;
private int texLocation; private int texLocation;
private int colorMatrixLocation; private int colorMatrixLocation;
private FloatBuffer textureCoords;
private int previousWidth; private int previousWidth;
private int previousStride; private int previousStride;
......
#Thu Sep 01 11:39:15 BST 2016 #Mon Oct 24 14:40:37 BST 2016
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
...@@ -35,9 +35,11 @@ android { ...@@ -35,9 +35,11 @@ android {
minifyEnabled false minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
} }
debug { // Re-enable test coverage when the following issue is fixed:
testCoverageEnabled = true // https://code.google.com/p/android/issues/detail?id=226070
} // debug {
// testCoverageEnabled = true
// }
} }
lintOptions { lintOptions {
...@@ -55,7 +57,7 @@ dependencies { ...@@ -55,7 +57,7 @@ dependencies {
androidTestCompile 'com.google.dexmaker:dexmaker:1.2' androidTestCompile 'com.google.dexmaker:dexmaker:1.2'
androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.2' androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.2'
androidTestCompile 'org.mockito:mockito-core:1.9.5' androidTestCompile 'org.mockito:mockito-core:1.9.5'
compile 'com.android.support:support-annotations:24.2.0' compile 'com.android.support:support-annotations:24.2.1'
} }
android.libraryVariants.all { variant -> android.libraryVariants.all { variant ->
......
...@@ -27,9 +27,9 @@ import junit.framework.TestCase; ...@@ -27,9 +27,9 @@ import junit.framework.TestCase;
*/ */
public final class DefaultOggSeekerTest extends TestCase { public final class DefaultOggSeekerTest extends TestCase {
public void testSetupUnboundAudioLength() { public void testSetupWithUnsetEndPositionFails() {
try { try {
new DefaultOggSeeker(0, C.LENGTH_UNSET, new TestStreamReader()); new DefaultOggSeeker(0, C.LENGTH_UNSET, new TestStreamReader(), 1, 1);
fail(); fail();
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
// ignored // ignored
...@@ -43,11 +43,12 @@ public final class DefaultOggSeekerTest extends TestCase { ...@@ -43,11 +43,12 @@ public final class DefaultOggSeekerTest extends TestCase {
} }
} }
public void testSeeking(Random random) throws IOException, InterruptedException { private void testSeeking(Random random) throws IOException, InterruptedException {
OggTestFile testFile = OggTestFile.generate(random, 1000); OggTestFile testFile = OggTestFile.generate(random, 1000);
FakeExtractorInput input = new FakeExtractorInput.Builder().setData(testFile.data).build(); FakeExtractorInput input = new FakeExtractorInput.Builder().setData(testFile.data).build();
TestStreamReader streamReader = new TestStreamReader(); TestStreamReader streamReader = new TestStreamReader();
DefaultOggSeeker oggSeeker = new DefaultOggSeeker(0, testFile.data.length, streamReader); DefaultOggSeeker oggSeeker = new DefaultOggSeeker(0, testFile.data.length, streamReader,
testFile.firstPayloadPageSize, testFile.firstPayloadPageGranulePosition);
OggPageHeader pageHeader = new OggPageHeader(); OggPageHeader pageHeader = new OggPageHeader();
while (true) { while (true) {
......
...@@ -29,7 +29,7 @@ import junit.framework.TestCase; ...@@ -29,7 +29,7 @@ import junit.framework.TestCase;
public class DefaultOggSeekerUtilMethodsTest extends TestCase { public class DefaultOggSeekerUtilMethodsTest extends TestCase {
private Random random = new Random(0); private Random random = new Random(0);
public void testSkipToNextPage() throws Exception { public void testSkipToNextPage() throws Exception {
FakeExtractorInput extractorInput = TestData.createInput( FakeExtractorInput extractorInput = TestData.createInput(
TestUtil.joinByteArrays( TestUtil.joinByteArrays(
...@@ -75,7 +75,7 @@ public class DefaultOggSeekerUtilMethodsTest extends TestCase { ...@@ -75,7 +75,7 @@ public class DefaultOggSeekerUtilMethodsTest extends TestCase {
private static void skipToNextPage(ExtractorInput extractorInput) private static void skipToNextPage(ExtractorInput extractorInput)
throws IOException, InterruptedException { throws IOException, InterruptedException {
DefaultOggSeeker oggSeeker = new DefaultOggSeeker(0, extractorInput.getLength(), DefaultOggSeeker oggSeeker = new DefaultOggSeeker(0, extractorInput.getLength(),
new FlacReader()); new FlacReader(), 1, 2);
while (true) { while (true) {
try { try {
oggSeeker.skipToNextPage(extractorInput); oggSeeker.skipToNextPage(extractorInput);
...@@ -143,7 +143,7 @@ public class DefaultOggSeekerUtilMethodsTest extends TestCase { ...@@ -143,7 +143,7 @@ public class DefaultOggSeekerUtilMethodsTest extends TestCase {
private void skipToPageOfGranule(ExtractorInput input, long granule, private void skipToPageOfGranule(ExtractorInput input, long granule,
long elapsedSamplesExpected) throws IOException, InterruptedException { long elapsedSamplesExpected) throws IOException, InterruptedException {
DefaultOggSeeker oggSeeker = new DefaultOggSeeker(0, input.getLength(), new FlacReader()); DefaultOggSeeker oggSeeker = new DefaultOggSeeker(0, input.getLength(), new FlacReader(), 1, 2);
while (true) { while (true) {
try { try {
assertEquals(elapsedSamplesExpected, oggSeeker.skipToPageOfGranule(input, granule, -1)); assertEquals(elapsedSamplesExpected, oggSeeker.skipToPageOfGranule(input, granule, -1));
...@@ -193,7 +193,7 @@ public class DefaultOggSeekerUtilMethodsTest extends TestCase { ...@@ -193,7 +193,7 @@ public class DefaultOggSeekerUtilMethodsTest extends TestCase {
private void assertReadGranuleOfLastPage(FakeExtractorInput input, int expected) private void assertReadGranuleOfLastPage(FakeExtractorInput input, int expected)
throws IOException, InterruptedException { throws IOException, InterruptedException {
DefaultOggSeeker oggSeeker = new DefaultOggSeeker(0, input.getLength(), new FlacReader()); DefaultOggSeeker oggSeeker = new DefaultOggSeeker(0, input.getLength(), new FlacReader(), 1, 2);
while (true) { while (true) {
try { try {
assertEquals(expected, oggSeeker.readGranuleOfLastPage(input)); assertEquals(expected, oggSeeker.readGranuleOfLastPage(input));
......
...@@ -33,12 +33,17 @@ import junit.framework.Assert; ...@@ -33,12 +33,17 @@ import junit.framework.Assert;
long lastGranule; long lastGranule;
int packetCount; int packetCount;
int pageCount; int pageCount;
int firstPayloadPageSize;
long firstPayloadPageGranulePosition;
private OggTestFile(byte[] data, long lastGranule, int packetCount, int pageCount) { private OggTestFile(byte[] data, long lastGranule, int packetCount, int pageCount,
int firstPayloadPageSize, long firstPayloadPageGranulePosition) {
this.data = data; this.data = data;
this.lastGranule = lastGranule; this.lastGranule = lastGranule;
this.packetCount = packetCount; this.packetCount = packetCount;
this.pageCount = pageCount; this.pageCount = pageCount;
this.firstPayloadPageSize = firstPayloadPageSize;
this.firstPayloadPageGranulePosition = firstPayloadPageGranulePosition;
} }
public static OggTestFile generate(Random random, int pageCount) { public static OggTestFile generate(Random random, int pageCount) {
...@@ -47,6 +52,8 @@ import junit.framework.Assert; ...@@ -47,6 +52,8 @@ import junit.framework.Assert;
long granule = 0; long granule = 0;
int packetLength = -1; int packetLength = -1;
int packetCount = 0; int packetCount = 0;
int firstPayloadPageSize = 0;
long firstPayloadPageGranulePosition = 0;
for (int i = 0; i < pageCount; i++) { for (int i = 0; i < pageCount; i++) {
int headerType = 0x00; int headerType = 0x00;
...@@ -89,6 +96,10 @@ import junit.framework.Assert; ...@@ -89,6 +96,10 @@ import junit.framework.Assert;
byte[] payload = TestUtil.buildTestData(bodySize, random); byte[] payload = TestUtil.buildTestData(bodySize, random);
fileData.add(payload); fileData.add(payload);
fileSize += payload.length; fileSize += payload.length;
if (i == 0) {
firstPayloadPageSize = header.length + bodySize;
firstPayloadPageGranulePosition = granule;
}
} }
byte[] file = new byte[fileSize]; byte[] file = new byte[fileSize];
...@@ -97,7 +108,8 @@ import junit.framework.Assert; ...@@ -97,7 +108,8 @@ import junit.framework.Assert;
System.arraycopy(data, 0, file, position, data.length); System.arraycopy(data, 0, file, position, data.length);
position += data.length; position += data.length;
} }
return new OggTestFile(file, granule, packetCount, pageCount); return new OggTestFile(file, granule, packetCount, pageCount, firstPayloadPageSize,
firstPayloadPageGranulePosition);
} }
public int findPreviousPageStart(long position) { public int findPreviousPageStart(long position) {
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
package com.google.android.exoplayer2.extractor.ts; package com.google.android.exoplayer2.extractor.ts;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.extractor.ts.ElementaryStreamReader.TrackIdGenerator; import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.TrackIdGenerator;
import com.google.android.exoplayer2.testutil.FakeExtractorOutput; import com.google.android.exoplayer2.testutil.FakeExtractorOutput;
import com.google.android.exoplayer2.testutil.FakeTrackOutput; import com.google.android.exoplayer2.testutil.FakeTrackOutput;
import com.google.android.exoplayer2.testutil.TestUtil; import com.google.android.exoplayer2.testutil.TestUtil;
...@@ -52,7 +52,7 @@ public class AdtsReaderTest extends TestCase { ...@@ -52,7 +52,7 @@ public class AdtsReaderTest extends TestCase {
public static final byte[] ADTS_CONTENT = TestUtil.createByteArray( public static final byte[] ADTS_CONTENT = TestUtil.createByteArray(
0x20, 0x00, 0x20, 0x00, 0x00, 0x80, 0x0e); 0x20, 0x00, 0x20, 0x00, 0x00, 0x80, 0x0e);
private static final byte TEST_DATA[] = TestUtil.joinByteArrays( private static final byte[] TEST_DATA = TestUtil.joinByteArrays(
ID3_DATA_1, ID3_DATA_1,
ID3_DATA_2, ID3_DATA_2,
ADTS_HEADER, ADTS_HEADER,
...@@ -73,7 +73,7 @@ public class AdtsReaderTest extends TestCase { ...@@ -73,7 +73,7 @@ public class AdtsReaderTest extends TestCase {
id3Output = fakeExtractorOutput.track(1); id3Output = fakeExtractorOutput.track(1);
adtsReader = new AdtsReader(true); adtsReader = new AdtsReader(true);
TrackIdGenerator idGenerator = new TrackIdGenerator(0, 1); TrackIdGenerator idGenerator = new TrackIdGenerator(0, 1);
adtsReader.init(fakeExtractorOutput, idGenerator); adtsReader.createTracks(fakeExtractorOutput, idGenerator);
data = new ParsableByteArray(TEST_DATA); data = new ParsableByteArray(TEST_DATA);
firstFeed = true; firstFeed = true;
} }
......
...@@ -22,7 +22,8 @@ import com.google.android.exoplayer2.extractor.ExtractorOutput; ...@@ -22,7 +22,8 @@ import com.google.android.exoplayer2.extractor.ExtractorOutput;
import com.google.android.exoplayer2.extractor.PositionHolder; import com.google.android.exoplayer2.extractor.PositionHolder;
import com.google.android.exoplayer2.extractor.TimestampAdjuster; import com.google.android.exoplayer2.extractor.TimestampAdjuster;
import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.extractor.ts.ElementaryStreamReader.EsInfo; import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.EsInfo;
import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.TrackIdGenerator;
import com.google.android.exoplayer2.testutil.FakeExtractorInput; import com.google.android.exoplayer2.testutil.FakeExtractorInput;
import com.google.android.exoplayer2.testutil.FakeExtractorOutput; import com.google.android.exoplayer2.testutil.FakeExtractorOutput;
import com.google.android.exoplayer2.testutil.FakeTrackOutput; import com.google.android.exoplayer2.testutil.FakeTrackOutput;
...@@ -106,7 +107,7 @@ public final class TsExtractorTest extends InstrumentationTestCase { ...@@ -106,7 +107,7 @@ public final class TsExtractorTest extends InstrumentationTestCase {
} }
} }
private static final class CustomEsReader extends ElementaryStreamReader { private static final class CustomEsReader implements ElementaryStreamReader {
private final String language; private final String language;
private TrackOutput output; private TrackOutput output;
...@@ -121,7 +122,7 @@ public final class TsExtractorTest extends InstrumentationTestCase { ...@@ -121,7 +122,7 @@ public final class TsExtractorTest extends InstrumentationTestCase {
} }
@Override @Override
public void init(ExtractorOutput extractorOutput, TrackIdGenerator idGenerator) { public void createTracks(ExtractorOutput extractorOutput, TrackIdGenerator idGenerator) {
output = extractorOutput.track(idGenerator.getNextId()); output = extractorOutput.track(idGenerator.getNextId());
output.format(Format.createTextSampleFormat("Overriding format", "mime", null, 0, 0, output.format(Format.createTextSampleFormat("Overriding format", "mime", null, 0, 0,
language, null, 0)); language, null, 0));
...@@ -146,22 +147,22 @@ public final class TsExtractorTest extends InstrumentationTestCase { ...@@ -146,22 +147,22 @@ public final class TsExtractorTest extends InstrumentationTestCase {
} }
private static final class CustomEsReaderFactory implements ElementaryStreamReader.Factory { private static final class CustomEsReaderFactory implements TsPayloadReader.Factory {
private final ElementaryStreamReader.Factory defaultFactory; private final TsPayloadReader.Factory defaultFactory;
private CustomEsReader reader; private CustomEsReader reader;
public CustomEsReaderFactory() { public CustomEsReaderFactory() {
defaultFactory = new DefaultStreamReaderFactory(); defaultFactory = new DefaultTsPayloadReaderFactory();
} }
@Override @Override
public ElementaryStreamReader createStreamReader(int streamType, EsInfo esInfo) { public TsPayloadReader createPayloadReader(int streamType, EsInfo esInfo) {
if (streamType == 3) { if (streamType == 3) {
reader = new CustomEsReader(esInfo.language); reader = new CustomEsReader(esInfo.language);
return reader; return new PesReader(reader);
} else { } else {
return defaultFactory.createStreamReader(streamType, esInfo); return defaultFactory.createPayloadReader(streamType, esInfo);
} }
} }
......
...@@ -249,6 +249,7 @@ import java.io.IOException; ...@@ -249,6 +249,7 @@ import java.io.IOException;
// Handler.Callback implementation. // Handler.Callback implementation.
@SuppressWarnings("unchecked")
@Override @Override
public boolean handleMessage(Message msg) { public boolean handleMessage(Message msg) {
try { try {
...@@ -335,8 +336,7 @@ import java.io.IOException; ...@@ -335,8 +336,7 @@ import java.io.IOException;
} }
} }
private void prepareInternal(MediaSource mediaSource, boolean resetPosition) private void prepareInternal(MediaSource mediaSource, boolean resetPosition) {
throws ExoPlaybackException {
resetInternal(); resetInternal();
loadControl.onPrepared(); loadControl.onPrepared();
if (resetPosition) { if (resetPosition) {
...@@ -884,8 +884,7 @@ import java.io.IOException; ...@@ -884,8 +884,7 @@ import java.io.IOException;
} }
} }
private void attemptRestart(Timeline newTimeline, Timeline oldTimeline, private void attemptRestart(Timeline newTimeline, Timeline oldTimeline, int oldPeriodIndex) {
int oldPeriodIndex) throws ExoPlaybackException {
int newPeriodIndex = C.INDEX_UNSET; int newPeriodIndex = C.INDEX_UNSET;
while (newPeriodIndex == C.INDEX_UNSET while (newPeriodIndex == C.INDEX_UNSET
&& oldPeriodIndex < oldTimeline.getPeriodCount() - 1) { && oldPeriodIndex < oldTimeline.getPeriodCount() - 1) {
...@@ -1260,7 +1259,7 @@ import java.io.IOException; ...@@ -1260,7 +1259,7 @@ import java.io.IOException;
} }
public long updatePeriodTrackSelection(long positionUs, LoadControl loadControl, public long updatePeriodTrackSelection(long positionUs, LoadControl loadControl,
boolean forceRecreateStreams, boolean[] streamResetFlags) throws ExoPlaybackException { boolean forceRecreateStreams, boolean[] streamResetFlags) {
for (int i = 0; i < trackSelections.length; i++) { for (int i = 0; i < trackSelections.length; i++) {
mayRetainStreamFlags[i] = !forceRecreateStreams mayRetainStreamFlags[i] = !forceRecreateStreams
&& Util.areEqual(periodTrackSelections == null ? null : periodTrackSelections.get(i), && Util.areEqual(periodTrackSelections == null ? null : periodTrackSelections.get(i),
......
...@@ -23,6 +23,7 @@ import android.os.ConditionVariable; ...@@ -23,6 +23,7 @@ import android.os.ConditionVariable;
import android.os.SystemClock; import android.os.SystemClock;
import android.util.Log; import android.util.Log;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
...@@ -981,6 +982,9 @@ public final class AudioTrack { ...@@ -981,6 +982,9 @@ public final class AudioTrack {
case C.ENCODING_PCM_32BIT: case C.ENCODING_PCM_32BIT:
resampledSize = size / 2; resampledSize = size / 2;
break; break;
case C.ENCODING_PCM_16BIT:
case C.ENCODING_INVALID:
case Format.NO_VALUE:
default: default:
// Never happens. // Never happens.
throw new IllegalStateException(); throw new IllegalStateException();
...@@ -1016,6 +1020,9 @@ public final class AudioTrack { ...@@ -1016,6 +1020,9 @@ public final class AudioTrack {
resampledBuffer.put(buffer.get(i + 3)); resampledBuffer.put(buffer.get(i + 3));
} }
break; break;
case C.ENCODING_PCM_16BIT:
case C.ENCODING_INVALID:
case Format.NO_VALUE:
default: default:
// Never happens. // Never happens.
throw new IllegalStateException(); throw new IllegalStateException();
......
...@@ -76,7 +76,8 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media ...@@ -76,7 +76,8 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
* has obtained the keys necessary to decrypt encrypted regions of the media. * has obtained the keys necessary to decrypt encrypted regions of the media.
*/ */
public MediaCodecAudioRenderer(MediaCodecSelector mediaCodecSelector, public MediaCodecAudioRenderer(MediaCodecSelector mediaCodecSelector,
DrmSessionManager drmSessionManager, boolean playClearSamplesWithoutKeys) { DrmSessionManager<FrameworkMediaCrypto> drmSessionManager,
boolean playClearSamplesWithoutKeys) {
this(mediaCodecSelector, drmSessionManager, playClearSamplesWithoutKeys, null, null); this(mediaCodecSelector, drmSessionManager, playClearSamplesWithoutKeys, null, null);
} }
......
...@@ -40,7 +40,7 @@ import java.io.IOException; ...@@ -40,7 +40,7 @@ import java.io.IOException;
} }
/** /**
* @see Extractor#sniff * @see com.google.android.exoplayer2.extractor.Extractor#sniff(ExtractorInput)
*/ */
public boolean sniff(ExtractorInput input) throws IOException, InterruptedException { public boolean sniff(ExtractorInput input) throws IOException, InterruptedException {
long inputLength = input.getLength(); long inputLength = input.getLength();
......
...@@ -59,13 +59,21 @@ import java.io.IOException; ...@@ -59,13 +59,21 @@ import java.io.IOException;
* @param startPosition Start position of the payload (inclusive). * @param startPosition Start position of the payload (inclusive).
* @param endPosition End position of the payload (exclusive). * @param endPosition End position of the payload (exclusive).
* @param streamReader StreamReader instance which owns this OggSeeker * @param streamReader StreamReader instance which owns this OggSeeker
* @param firstPayloadPageSize The total size of the first payload page, in bytes.
* @param firstPayloadPageGranulePosition The granule position of the first payload page.
*/ */
public DefaultOggSeeker(long startPosition, long endPosition, StreamReader streamReader) { public DefaultOggSeeker(long startPosition, long endPosition, StreamReader streamReader,
int firstPayloadPageSize, long firstPayloadPageGranulePosition) {
Assertions.checkArgument(startPosition >= 0 && endPosition > startPosition); Assertions.checkArgument(startPosition >= 0 && endPosition > startPosition);
this.streamReader = streamReader; this.streamReader = streamReader;
this.startPosition = startPosition; this.startPosition = startPosition;
this.endPosition = endPosition; this.endPosition = endPosition;
this.state = STATE_SEEK_TO_END; if (firstPayloadPageSize == endPosition - startPosition) {
totalGranules = firstPayloadPageGranulePosition;
state = STATE_IDLE;
} else {
state = STATE_SEEK_TO_END;
}
} }
@Override @Override
...@@ -77,9 +85,9 @@ import java.io.IOException; ...@@ -77,9 +85,9 @@ import java.io.IOException;
positionBeforeSeekToEnd = input.getPosition(); positionBeforeSeekToEnd = input.getPosition();
state = STATE_READ_LAST_PAGE; state = STATE_READ_LAST_PAGE;
// Seek to the end just before the last page of stream to get the duration. // Seek to the end just before the last page of stream to get the duration.
long lastPagePosition = endPosition - OggPageHeader.MAX_PAGE_SIZE; long lastPageSearchPosition = endPosition - OggPageHeader.MAX_PAGE_SIZE;
if (lastPagePosition > positionBeforeSeekToEnd) { if (lastPageSearchPosition > positionBeforeSeekToEnd) {
return lastPagePosition; return lastPageSearchPosition;
} }
// Fall through. // Fall through.
case STATE_READ_LAST_PAGE: case STATE_READ_LAST_PAGE:
......
...@@ -144,7 +144,10 @@ import java.io.IOException; ...@@ -144,7 +144,10 @@ import java.io.IOException;
} else if (input.getLength() == C.LENGTH_UNSET) { } else if (input.getLength() == C.LENGTH_UNSET) {
oggSeeker = new UnseekableOggSeeker(); oggSeeker = new UnseekableOggSeeker();
} else { } else {
oggSeeker = new DefaultOggSeeker(payloadStartPosition, input.getLength(), this); OggPageHeader firstPayloadPageHeader = oggPacket.getPageHeader();
oggSeeker = new DefaultOggSeeker(payloadStartPosition, input.getLength(), this,
firstPayloadPageHeader.headerSize + firstPayloadPageHeader.bodySize,
firstPayloadPageHeader.granulePosition);
} }
setupData = null; setupData = null;
......
...@@ -26,7 +26,7 @@ import com.google.android.exoplayer2.util.Assertions; ...@@ -26,7 +26,7 @@ import com.google.android.exoplayer2.util.Assertions;
/* package */ final class VorbisBitArray { /* package */ final class VorbisBitArray {
public final byte[] data; public final byte[] data;
private int limit; private final int limit;
private int byteOffset; private int byteOffset;
private int bitOffset; private int bitOffset;
......
...@@ -47,7 +47,6 @@ public final class RawCcExtractor implements Extractor { ...@@ -47,7 +47,6 @@ public final class RawCcExtractor implements Extractor {
private final ParsableByteArray dataScratch; private final ParsableByteArray dataScratch;
private ExtractorOutput extractorOutput;
private TrackOutput trackOutput; private TrackOutput trackOutput;
private int parserState; private int parserState;
...@@ -63,10 +62,9 @@ public final class RawCcExtractor implements Extractor { ...@@ -63,10 +62,9 @@ public final class RawCcExtractor implements Extractor {
@Override @Override
public void init(ExtractorOutput output) { public void init(ExtractorOutput output) {
this.extractorOutput = output; output.seekMap(new SeekMap.Unseekable(C.TIME_UNSET));
extractorOutput.seekMap(new SeekMap.Unseekable(C.TIME_UNSET)); trackOutput = output.track(0);
trackOutput = extractorOutput.track(0); output.endTracks();
extractorOutput.endTracks();
trackOutput.format(Format.createTextSampleFormat(null, MimeTypes.APPLICATION_CEA608, trackOutput.format(Format.createTextSampleFormat(null, MimeTypes.APPLICATION_CEA608,
null, Format.NO_VALUE, 0, null, null)); null, Format.NO_VALUE, 0, null, null));
......
...@@ -23,7 +23,7 @@ import com.google.android.exoplayer2.extractor.ExtractorOutput; ...@@ -23,7 +23,7 @@ import com.google.android.exoplayer2.extractor.ExtractorOutput;
import com.google.android.exoplayer2.extractor.ExtractorsFactory; import com.google.android.exoplayer2.extractor.ExtractorsFactory;
import com.google.android.exoplayer2.extractor.PositionHolder; import com.google.android.exoplayer2.extractor.PositionHolder;
import com.google.android.exoplayer2.extractor.SeekMap; import com.google.android.exoplayer2.extractor.SeekMap;
import com.google.android.exoplayer2.extractor.ts.ElementaryStreamReader.TrackIdGenerator; import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.TrackIdGenerator;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
...@@ -119,7 +119,7 @@ public final class Ac3Extractor implements Extractor { ...@@ -119,7 +119,7 @@ public final class Ac3Extractor implements Extractor {
@Override @Override
public void init(ExtractorOutput output) { public void init(ExtractorOutput output) {
reader = new Ac3Reader(); // TODO: Add support for embedded ID3. reader = new Ac3Reader(); // TODO: Add support for embedded ID3.
reader.init(output, new TrackIdGenerator(0, 1)); reader.createTracks(output, new TrackIdGenerator(0, 1));
output.endTracks(); output.endTracks();
output.seekMap(new SeekMap.Unseekable(C.TIME_UNSET)); output.seekMap(new SeekMap.Unseekable(C.TIME_UNSET));
} }
......
...@@ -20,13 +20,14 @@ import com.google.android.exoplayer2.Format; ...@@ -20,13 +20,14 @@ import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.audio.Ac3Util; import com.google.android.exoplayer2.audio.Ac3Util;
import com.google.android.exoplayer2.extractor.ExtractorOutput; import com.google.android.exoplayer2.extractor.ExtractorOutput;
import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.TrackIdGenerator;
import com.google.android.exoplayer2.util.ParsableBitArray; import com.google.android.exoplayer2.util.ParsableBitArray;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
/** /**
* Parses a continuous (E-)AC-3 byte stream and extracts individual samples. * Parses a continuous (E-)AC-3 byte stream and extracts individual samples.
*/ */
/* package */ final class Ac3Reader extends ElementaryStreamReader { /* package */ final class Ac3Reader implements ElementaryStreamReader {
private static final int STATE_FINDING_SYNC = 0; private static final int STATE_FINDING_SYNC = 0;
private static final int STATE_READING_HEADER = 1; private static final int STATE_READING_HEADER = 1;
...@@ -82,7 +83,7 @@ import com.google.android.exoplayer2.util.ParsableByteArray; ...@@ -82,7 +83,7 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
} }
@Override @Override
public void init(ExtractorOutput extractorOutput, TrackIdGenerator generator) { public void createTracks(ExtractorOutput extractorOutput, TrackIdGenerator generator) {
output = extractorOutput.track(generator.getNextId()); output = extractorOutput.track(generator.getNextId());
} }
......
...@@ -22,7 +22,7 @@ import com.google.android.exoplayer2.extractor.ExtractorOutput; ...@@ -22,7 +22,7 @@ import com.google.android.exoplayer2.extractor.ExtractorOutput;
import com.google.android.exoplayer2.extractor.ExtractorsFactory; import com.google.android.exoplayer2.extractor.ExtractorsFactory;
import com.google.android.exoplayer2.extractor.PositionHolder; import com.google.android.exoplayer2.extractor.PositionHolder;
import com.google.android.exoplayer2.extractor.SeekMap; import com.google.android.exoplayer2.extractor.SeekMap;
import com.google.android.exoplayer2.extractor.ts.ElementaryStreamReader.TrackIdGenerator; import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.TrackIdGenerator;
import com.google.android.exoplayer2.util.ParsableBitArray; import com.google.android.exoplayer2.util.ParsableBitArray;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
...@@ -128,7 +128,7 @@ public final class AdtsExtractor implements Extractor { ...@@ -128,7 +128,7 @@ public final class AdtsExtractor implements Extractor {
@Override @Override
public void init(ExtractorOutput output) { public void init(ExtractorOutput output) {
reader = new AdtsReader(true); reader = new AdtsReader(true);
reader.init(output, new TrackIdGenerator(0, 1)); reader.createTracks(output, new TrackIdGenerator(0, 1));
output.endTracks(); output.endTracks();
output.seekMap(new SeekMap.Unseekable(C.TIME_UNSET)); output.seekMap(new SeekMap.Unseekable(C.TIME_UNSET));
} }
......
...@@ -22,6 +22,7 @@ import com.google.android.exoplayer2.Format; ...@@ -22,6 +22,7 @@ import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.extractor.DummyTrackOutput; import com.google.android.exoplayer2.extractor.DummyTrackOutput;
import com.google.android.exoplayer2.extractor.ExtractorOutput; import com.google.android.exoplayer2.extractor.ExtractorOutput;
import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.TrackIdGenerator;
import com.google.android.exoplayer2.util.CodecSpecificDataUtil; import com.google.android.exoplayer2.util.CodecSpecificDataUtil;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.ParsableBitArray; import com.google.android.exoplayer2.util.ParsableBitArray;
...@@ -32,7 +33,7 @@ import java.util.Collections; ...@@ -32,7 +33,7 @@ import java.util.Collections;
/** /**
* Parses a continuous ADTS byte stream and extracts individual frames. * Parses a continuous ADTS byte stream and extracts individual frames.
*/ */
/* package */ final class AdtsReader extends ElementaryStreamReader { /* package */ final class AdtsReader implements ElementaryStreamReader {
private static final String TAG = "AdtsReader"; private static final String TAG = "AdtsReader";
...@@ -106,7 +107,7 @@ import java.util.Collections; ...@@ -106,7 +107,7 @@ import java.util.Collections;
} }
@Override @Override
public void init(ExtractorOutput extractorOutput, TrackIdGenerator idGenerator) { public void createTracks(ExtractorOutput extractorOutput, TrackIdGenerator idGenerator) {
output = extractorOutput.track(idGenerator.getNextId()); output = extractorOutput.track(idGenerator.getNextId());
if (exposeId3) { if (exposeId3) {
id3Output = extractorOutput.track(idGenerator.getNextId()); id3Output = extractorOutput.track(idGenerator.getNextId());
......
...@@ -16,14 +16,14 @@ ...@@ -16,14 +16,14 @@
package com.google.android.exoplayer2.extractor.ts; package com.google.android.exoplayer2.extractor.ts;
import android.support.annotation.IntDef; import android.support.annotation.IntDef;
import com.google.android.exoplayer2.extractor.ts.ElementaryStreamReader.EsInfo; import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.EsInfo;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
/** /**
* Default implementation for {@link ElementaryStreamReader.Factory}. * Default implementation for {@link TsPayloadReader.Factory}.
*/ */
public final class DefaultStreamReaderFactory implements ElementaryStreamReader.Factory { public final class DefaultTsPayloadReaderFactory implements TsPayloadReader.Factory {
/** /**
* Flags controlling elementary stream readers behaviour. * Flags controlling elementary stream readers behaviour.
...@@ -41,39 +41,39 @@ public final class DefaultStreamReaderFactory implements ElementaryStreamReader. ...@@ -41,39 +41,39 @@ public final class DefaultStreamReaderFactory implements ElementaryStreamReader.
@Flags @Flags
private final int flags; private final int flags;
public DefaultStreamReaderFactory() { public DefaultTsPayloadReaderFactory() {
this(0); this(0);
} }
public DefaultStreamReaderFactory(@Flags int flags) { public DefaultTsPayloadReaderFactory(@Flags int flags) {
this.flags = flags; this.flags = flags;
} }
@Override @Override
public ElementaryStreamReader createStreamReader(int streamType, EsInfo esInfo) { public TsPayloadReader createPayloadReader(int streamType, EsInfo esInfo) {
switch (streamType) { switch (streamType) {
case TsExtractor.TS_STREAM_TYPE_MPA: case TsExtractor.TS_STREAM_TYPE_MPA:
case TsExtractor.TS_STREAM_TYPE_MPA_LSF: case TsExtractor.TS_STREAM_TYPE_MPA_LSF:
return new MpegAudioReader(esInfo.language); return new PesReader(new MpegAudioReader(esInfo.language));
case TsExtractor.TS_STREAM_TYPE_AAC: case TsExtractor.TS_STREAM_TYPE_AAC:
return (flags & FLAG_IGNORE_AAC_STREAM) != 0 ? null return (flags & FLAG_IGNORE_AAC_STREAM) != 0 ? null
: new AdtsReader(false, esInfo.language); : new PesReader(new AdtsReader(false, esInfo.language));
case TsExtractor.TS_STREAM_TYPE_AC3: case TsExtractor.TS_STREAM_TYPE_AC3:
case TsExtractor.TS_STREAM_TYPE_E_AC3: case TsExtractor.TS_STREAM_TYPE_E_AC3:
return new Ac3Reader(esInfo.language); return new PesReader(new Ac3Reader(esInfo.language));
case TsExtractor.TS_STREAM_TYPE_DTS: case TsExtractor.TS_STREAM_TYPE_DTS:
case TsExtractor.TS_STREAM_TYPE_HDMV_DTS: case TsExtractor.TS_STREAM_TYPE_HDMV_DTS:
return new DtsReader(esInfo.language); return new PesReader(new DtsReader(esInfo.language));
case TsExtractor.TS_STREAM_TYPE_H262: case TsExtractor.TS_STREAM_TYPE_H262:
return new H262Reader(); return new PesReader(new H262Reader());
case TsExtractor.TS_STREAM_TYPE_H264: case TsExtractor.TS_STREAM_TYPE_H264:
return (flags & FLAG_IGNORE_H264_STREAM) != 0 ? null return (flags & FLAG_IGNORE_H264_STREAM) != 0 ? null
: new H264Reader((flags & FLAG_ALLOW_NON_IDR_KEYFRAMES) != 0, : new PesReader(new H264Reader((flags & FLAG_ALLOW_NON_IDR_KEYFRAMES) != 0,
(flags & FLAG_DETECT_ACCESS_UNITS) != 0); (flags & FLAG_DETECT_ACCESS_UNITS) != 0));
case TsExtractor.TS_STREAM_TYPE_H265: case TsExtractor.TS_STREAM_TYPE_H265:
return new H265Reader(); return new PesReader(new H265Reader());
case TsExtractor.TS_STREAM_TYPE_ID3: case TsExtractor.TS_STREAM_TYPE_ID3:
return new Id3Reader(); return new PesReader(new Id3Reader());
default: default:
return null; return null;
} }
......
...@@ -20,12 +20,13 @@ import com.google.android.exoplayer2.Format; ...@@ -20,12 +20,13 @@ import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.audio.DtsUtil; import com.google.android.exoplayer2.audio.DtsUtil;
import com.google.android.exoplayer2.extractor.ExtractorOutput; import com.google.android.exoplayer2.extractor.ExtractorOutput;
import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.TrackIdGenerator;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
/** /**
* Parses a continuous DTS byte stream and extracts individual samples. * Parses a continuous DTS byte stream and extracts individual samples.
*/ */
/* package */ final class DtsReader extends ElementaryStreamReader { /* package */ final class DtsReader implements ElementaryStreamReader {
private static final int STATE_FINDING_SYNC = 0; private static final int STATE_FINDING_SYNC = 0;
private static final int STATE_READING_HEADER = 1; private static final int STATE_READING_HEADER = 1;
...@@ -77,7 +78,7 @@ import com.google.android.exoplayer2.util.ParsableByteArray; ...@@ -77,7 +78,7 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
} }
@Override @Override
public void init(ExtractorOutput extractorOutput, TrackIdGenerator idGenerator) { public void createTracks(ExtractorOutput extractorOutput, TrackIdGenerator idGenerator) {
output = extractorOutput.track(idGenerator.getNextId()); output = extractorOutput.track(idGenerator.getNextId());
} }
......
...@@ -22,82 +22,21 @@ import com.google.android.exoplayer2.util.ParsableByteArray; ...@@ -22,82 +22,21 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
/** /**
* Extracts individual samples from an elementary media stream, preserving original order. * Extracts individual samples from an elementary media stream, preserving original order.
*/ */
public abstract class ElementaryStreamReader { public interface ElementaryStreamReader {
/**
* Factory of {@link ElementaryStreamReader} instances.
*/
public interface Factory {
/**
* Returns an {@link ElementaryStreamReader} for a given PMT entry. May return null if the
* stream type is not supported or if the stream already has a reader assigned to it.
*
* @param streamType Stream type value as defined in the PMT entry or associated descriptors.
* @param esInfo Information associated to the elementary stream provided in the PMT.
* @return An {@link ElementaryStreamReader} for the elementary streams carried by the provided
* pid. {@code null} if the stream is not supported or if it should be ignored.
*/
ElementaryStreamReader createStreamReader(int streamType, EsInfo esInfo);
}
/**
* Holds descriptor information associated with an elementary stream.
*/
public static final class EsInfo {
public final int streamType;
public String language;
public byte[] descriptorBytes;
/**
* @param streamType The type of the stream as defined by the
* {@link TsExtractor}{@code .TS_STREAM_TYPE_*}.
* @param language The language of the stream, as defined by ISO/IEC 13818-1, section 2.6.18.
* @param descriptorBytes The descriptor bytes associated to the stream.
*/
public EsInfo(int streamType, String language, byte[] descriptorBytes) {
this.streamType = streamType;
this.language = language;
this.descriptorBytes = descriptorBytes;
}
}
/**
* Generates track ids for initializing {@link ElementaryStreamReader}s' {@link TrackOutput}s.
*/
public static final class TrackIdGenerator {
private final int firstId;
private final int idIncrement;
private int generatedIdCount;
public TrackIdGenerator(int firstId, int idIncrement) {
this.firstId = firstId;
this.idIncrement = idIncrement;
}
public int getNextId() {
return firstId + idIncrement * generatedIdCount++;
}
}
/** /**
* Notifies the reader that a seek has occurred. * Notifies the reader that a seek has occurred.
*/ */
public abstract void seek(); void seek();
/** /**
* Initializes the reader by providing outputs and ids for the tracks. * Initializes the reader by providing outputs and ids for the tracks.
* *
* @param extractorOutput The {@link ExtractorOutput} that receives the extracted data. * @param extractorOutput The {@link ExtractorOutput} that receives the extracted data.
* @param idGenerator A {@link TrackIdGenerator} that generates unique track ids for the * @param idGenerator A {@link PesReader.TrackIdGenerator} that generates unique track ids for the
* {@link TrackOutput}s. * {@link TrackOutput}s.
*/ */
public abstract void init(ExtractorOutput extractorOutput, TrackIdGenerator idGenerator); void createTracks(ExtractorOutput extractorOutput, PesReader.TrackIdGenerator idGenerator);
/** /**
* Called when a packet starts. * Called when a packet starts.
...@@ -105,18 +44,18 @@ public abstract class ElementaryStreamReader { ...@@ -105,18 +44,18 @@ public abstract class ElementaryStreamReader {
* @param pesTimeUs The timestamp associated with the packet. * @param pesTimeUs The timestamp associated with the packet.
* @param dataAlignmentIndicator The data alignment indicator associated with the packet. * @param dataAlignmentIndicator The data alignment indicator associated with the packet.
*/ */
public abstract void packetStarted(long pesTimeUs, boolean dataAlignmentIndicator); void packetStarted(long pesTimeUs, boolean dataAlignmentIndicator);
/** /**
* Consumes (possibly partial) data from the current packet. * Consumes (possibly partial) data from the current packet.
* *
* @param data The data to consume. * @param data The data to consume.
*/ */
public abstract void consume(ParsableByteArray data); void consume(ParsableByteArray data);
/** /**
* Called when a packet ends. * Called when a packet ends.
*/ */
public abstract void packetFinished(); void packetFinished();
} }
...@@ -20,6 +20,7 @@ import com.google.android.exoplayer2.C; ...@@ -20,6 +20,7 @@ import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.extractor.ExtractorOutput; import com.google.android.exoplayer2.extractor.ExtractorOutput;
import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.TrackIdGenerator;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.NalUnitUtil; import com.google.android.exoplayer2.util.NalUnitUtil;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
...@@ -29,7 +30,7 @@ import java.util.Collections; ...@@ -29,7 +30,7 @@ import java.util.Collections;
/** /**
* Parses a continuous H262 byte stream and extracts individual frames. * Parses a continuous H262 byte stream and extracts individual frames.
*/ */
/* package */ final class H262Reader extends ElementaryStreamReader { /* package */ final class H262Reader implements ElementaryStreamReader {
private static final int START_PICTURE = 0x00; private static final int START_PICTURE = 0x00;
private static final int START_SEQUENCE_HEADER = 0xB3; private static final int START_SEQUENCE_HEADER = 0xB3;
...@@ -76,7 +77,7 @@ import java.util.Collections; ...@@ -76,7 +77,7 @@ import java.util.Collections;
} }
@Override @Override
public void init(ExtractorOutput extractorOutput, TrackIdGenerator idGenerator) { public void createTracks(ExtractorOutput extractorOutput, TrackIdGenerator idGenerator) {
output = extractorOutput.track(idGenerator.getNextId()); output = extractorOutput.track(idGenerator.getNextId());
} }
......
...@@ -20,6 +20,7 @@ import com.google.android.exoplayer2.C; ...@@ -20,6 +20,7 @@ import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.extractor.ExtractorOutput; import com.google.android.exoplayer2.extractor.ExtractorOutput;
import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.TrackIdGenerator;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.NalUnitUtil; import com.google.android.exoplayer2.util.NalUnitUtil;
import com.google.android.exoplayer2.util.NalUnitUtil.SpsData; import com.google.android.exoplayer2.util.NalUnitUtil.SpsData;
...@@ -32,7 +33,7 @@ import java.util.List; ...@@ -32,7 +33,7 @@ import java.util.List;
/** /**
* Parses a continuous H264 byte stream and extracts individual frames. * Parses a continuous H264 byte stream and extracts individual frames.
*/ */
/* package */ final class H264Reader extends ElementaryStreamReader { /* package */ final class H264Reader implements ElementaryStreamReader {
private static final int NAL_UNIT_TYPE_SEI = 6; // Supplemental enhancement information private static final int NAL_UNIT_TYPE_SEI = 6; // Supplemental enhancement information
private static final int NAL_UNIT_TYPE_SPS = 7; // Sequence parameter set private static final int NAL_UNIT_TYPE_SPS = 7; // Sequence parameter set
...@@ -86,7 +87,7 @@ import java.util.List; ...@@ -86,7 +87,7 @@ import java.util.List;
} }
@Override @Override
public void init(ExtractorOutput extractorOutput, TrackIdGenerator idGenerator) { public void createTracks(ExtractorOutput extractorOutput, TrackIdGenerator idGenerator) {
output = extractorOutput.track(idGenerator.getNextId()); output = extractorOutput.track(idGenerator.getNextId());
sampleReader = new SampleReader(output, allowNonIdrKeyframes, detectAccessUnits); sampleReader = new SampleReader(output, allowNonIdrKeyframes, detectAccessUnits);
seiReader = new SeiReader(extractorOutput.track(idGenerator.getNextId())); seiReader = new SeiReader(extractorOutput.track(idGenerator.getNextId()));
......
...@@ -20,6 +20,7 @@ import com.google.android.exoplayer2.C; ...@@ -20,6 +20,7 @@ import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.extractor.ExtractorOutput; import com.google.android.exoplayer2.extractor.ExtractorOutput;
import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.TrackIdGenerator;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.NalUnitUtil; import com.google.android.exoplayer2.util.NalUnitUtil;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
...@@ -29,7 +30,7 @@ import java.util.Collections; ...@@ -29,7 +30,7 @@ import java.util.Collections;
/** /**
* Parses a continuous H.265 byte stream and extracts individual frames. * Parses a continuous H.265 byte stream and extracts individual frames.
*/ */
/* package */ final class H265Reader extends ElementaryStreamReader { /* package */ final class H265Reader implements ElementaryStreamReader {
private static final String TAG = "H265Reader"; private static final String TAG = "H265Reader";
...@@ -88,7 +89,7 @@ import java.util.Collections; ...@@ -88,7 +89,7 @@ import java.util.Collections;
} }
@Override @Override
public void init(ExtractorOutput extractorOutput, TrackIdGenerator idGenerator) { public void createTracks(ExtractorOutput extractorOutput, TrackIdGenerator idGenerator) {
output = extractorOutput.track(idGenerator.getNextId()); output = extractorOutput.track(idGenerator.getNextId());
sampleReader = new SampleReader(output); sampleReader = new SampleReader(output);
seiReader = new SeiReader(extractorOutput.track(idGenerator.getNextId())); seiReader = new SeiReader(extractorOutput.track(idGenerator.getNextId()));
......
...@@ -15,17 +15,21 @@ ...@@ -15,17 +15,21 @@
*/ */
package com.google.android.exoplayer2.extractor.ts; package com.google.android.exoplayer2.extractor.ts;
import android.util.Log;
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.extractor.ExtractorOutput; import com.google.android.exoplayer2.extractor.ExtractorOutput;
import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.TrackIdGenerator;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
/** /**
* Parses ID3 data and extracts individual text information frames. * Parses ID3 data and extracts individual text information frames.
*/ */
/* package */ final class Id3Reader extends ElementaryStreamReader { /* package */ final class Id3Reader implements ElementaryStreamReader {
private static final String TAG = "Id3Reader";
private static final int ID3_HEADER_SIZE = 10; private static final int ID3_HEADER_SIZE = 10;
...@@ -51,7 +55,7 @@ import com.google.android.exoplayer2.util.ParsableByteArray; ...@@ -51,7 +55,7 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
} }
@Override @Override
public void init(ExtractorOutput extractorOutput, TrackIdGenerator idGenerator) { public void createTracks(ExtractorOutput extractorOutput, TrackIdGenerator idGenerator) {
output = extractorOutput.track(idGenerator.getNextId()); output = extractorOutput.track(idGenerator.getNextId());
output.format(Format.createSampleFormat(null, MimeTypes.APPLICATION_ID3, null, Format.NO_VALUE, output.format(Format.createSampleFormat(null, MimeTypes.APPLICATION_ID3, null, Format.NO_VALUE,
null)); null));
...@@ -81,7 +85,14 @@ import com.google.android.exoplayer2.util.ParsableByteArray; ...@@ -81,7 +85,14 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
headerBytesAvailable); headerBytesAvailable);
if (sampleBytesRead + headerBytesAvailable == ID3_HEADER_SIZE) { if (sampleBytesRead + headerBytesAvailable == ID3_HEADER_SIZE) {
// We've finished reading the ID3 header. Extract the sample size. // We've finished reading the ID3 header. Extract the sample size.
id3Header.setPosition(6); // 'ID3' (3) + version (2) + flags (1) id3Header.setPosition(0);
if ('I' != id3Header.readUnsignedByte() || 'D' != id3Header.readUnsignedByte()
|| '3' != id3Header.readUnsignedByte()) {
Log.w(TAG, "Discarding invalid ID3 tag");
writingSample = false;
return;
}
id3Header.skipBytes(3); // version (2) + flags (1)
sampleSize = ID3_HEADER_SIZE + id3Header.readSynchSafeInt(); sampleSize = ID3_HEADER_SIZE + id3Header.readSynchSafeInt();
} }
} }
......
...@@ -20,12 +20,13 @@ import com.google.android.exoplayer2.Format; ...@@ -20,12 +20,13 @@ import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.extractor.ExtractorOutput; import com.google.android.exoplayer2.extractor.ExtractorOutput;
import com.google.android.exoplayer2.extractor.MpegAudioHeader; import com.google.android.exoplayer2.extractor.MpegAudioHeader;
import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.TrackIdGenerator;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
/** /**
* Parses a continuous MPEG Audio byte stream and extracts individual frames. * Parses a continuous MPEG Audio byte stream and extracts individual frames.
*/ */
/* package */ final class MpegAudioReader extends ElementaryStreamReader { /* package */ final class MpegAudioReader implements ElementaryStreamReader {
private static final int STATE_FINDING_HEADER = 0; private static final int STATE_FINDING_HEADER = 0;
private static final int STATE_READING_HEADER = 1; private static final int STATE_READING_HEADER = 1;
...@@ -74,7 +75,7 @@ import com.google.android.exoplayer2.util.ParsableByteArray; ...@@ -74,7 +75,7 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
} }
@Override @Override
public void init(ExtractorOutput extractorOutput, TrackIdGenerator idGenerator) { public void createTracks(ExtractorOutput extractorOutput, TrackIdGenerator idGenerator) {
output = extractorOutput.track(idGenerator.getNextId()); output = extractorOutput.track(idGenerator.getNextId());
} }
......
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2.extractor.ts;
import android.util.Log;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.extractor.ExtractorOutput;
import com.google.android.exoplayer2.extractor.TimestampAdjuster;
import com.google.android.exoplayer2.util.ParsableBitArray;
import com.google.android.exoplayer2.util.ParsableByteArray;
/**
* Parses PES packet data and extracts samples.
*/
public final class PesReader implements TsPayloadReader {
private static final String TAG = "PesReader";
private static final int STATE_FINDING_HEADER = 0;
private static final int STATE_READING_HEADER = 1;
private static final int STATE_READING_HEADER_EXTENSION = 2;
private static final int STATE_READING_BODY = 3;
private static final int HEADER_SIZE = 9;
private static final int MAX_HEADER_EXTENSION_SIZE = 10;
private static final int PES_SCRATCH_SIZE = 10; // max(HEADER_SIZE, MAX_HEADER_EXTENSION_SIZE)
private final ElementaryStreamReader reader;
private final ParsableBitArray pesScratch;
private int state;
private int bytesRead;
private TimestampAdjuster timestampAdjuster;
private boolean ptsFlag;
private boolean dtsFlag;
private boolean seenFirstDts;
private int extendedHeaderLength;
private int payloadSize;
private boolean dataAlignmentIndicator;
private long timeUs;
public PesReader(ElementaryStreamReader reader) {
this.reader = reader;
pesScratch = new ParsableBitArray(new byte[PES_SCRATCH_SIZE]);
state = STATE_FINDING_HEADER;
}
@Override
public void init(TimestampAdjuster timestampAdjuster, ExtractorOutput extractorOutput,
TrackIdGenerator idGenerator) {
this.timestampAdjuster = timestampAdjuster;
reader.createTracks(extractorOutput, idGenerator);
}
// TsPayloadReader implementation.
@Override
public final void seek() {
state = STATE_FINDING_HEADER;
bytesRead = 0;
seenFirstDts = false;
reader.seek();
}
@Override
public final void consume(ParsableByteArray data, boolean payloadUnitStartIndicator) {
if (payloadUnitStartIndicator) {
switch (state) {
case STATE_FINDING_HEADER:
case STATE_READING_HEADER:
// Expected.
break;
case STATE_READING_HEADER_EXTENSION:
Log.w(TAG, "Unexpected start indicator reading extended header");
break;
case STATE_READING_BODY:
// If payloadSize == -1 then the length of the previous packet was unspecified, and so
// we only know that it's finished now that we've seen the start of the next one. This
// is expected. If payloadSize != -1, then the length of the previous packet was known,
// but we didn't receive that amount of data. This is not expected.
if (payloadSize != -1) {
Log.w(TAG, "Unexpected start indicator: expected " + payloadSize + " more bytes");
}
// Either way, notify the reader that it has now finished.
reader.packetFinished();
break;
}
setState(STATE_READING_HEADER);
}
while (data.bytesLeft() > 0) {
switch (state) {
case STATE_FINDING_HEADER:
data.skipBytes(data.bytesLeft());
break;
case STATE_READING_HEADER:
if (continueRead(data, pesScratch.data, HEADER_SIZE)) {
setState(parseHeader() ? STATE_READING_HEADER_EXTENSION : STATE_FINDING_HEADER);
}
break;
case STATE_READING_HEADER_EXTENSION:
int readLength = Math.min(MAX_HEADER_EXTENSION_SIZE, extendedHeaderLength);
// Read as much of the extended header as we're interested in, and skip the rest.
if (continueRead(data, pesScratch.data, readLength)
&& continueRead(data, null, extendedHeaderLength)) {
parseHeaderExtension();
reader.packetStarted(timeUs, dataAlignmentIndicator);
setState(STATE_READING_BODY);
}
break;
case STATE_READING_BODY:
readLength = data.bytesLeft();
int padding = payloadSize == -1 ? 0 : readLength - payloadSize;
if (padding > 0) {
readLength -= padding;
data.setLimit(data.getPosition() + readLength);
}
reader.consume(data);
if (payloadSize != -1) {
payloadSize -= readLength;
if (payloadSize == 0) {
reader.packetFinished();
setState(STATE_READING_HEADER);
}
}
break;
}
}
}
private void setState(int state) {
this.state = state;
bytesRead = 0;
}
/**
* Continues a read from the provided {@code source} into a given {@code target}. It's assumed
* that the data should be written into {@code target} starting from an offset of zero.
*
* @param source The source from which to read.
* @param target The target into which data is to be read, or {@code null} to skip.
* @param targetLength The target length of the read.
* @return Whether the target length has been reached.
*/
private boolean continueRead(ParsableByteArray source, byte[] target, int targetLength) {
int bytesToRead = Math.min(source.bytesLeft(), targetLength - bytesRead);
if (bytesToRead <= 0) {
return true;
} else if (target == null) {
source.skipBytes(bytesToRead);
} else {
source.readBytes(target, bytesRead, bytesToRead);
}
bytesRead += bytesToRead;
return bytesRead == targetLength;
}
private boolean parseHeader() {
// Note: see ISO/IEC 13818-1, section 2.4.3.6 for detailed information on the format of
// the header.
pesScratch.setPosition(0);
int startCodePrefix = pesScratch.readBits(24);
if (startCodePrefix != 0x000001) {
Log.w(TAG, "Unexpected start code prefix: " + startCodePrefix);
payloadSize = -1;
return false;
}
pesScratch.skipBits(8); // stream_id.
int packetLength = pesScratch.readBits(16);
pesScratch.skipBits(5); // '10' (2), PES_scrambling_control (2), PES_priority (1)
dataAlignmentIndicator = pesScratch.readBit();
pesScratch.skipBits(2); // copyright (1), original_or_copy (1)
ptsFlag = pesScratch.readBit();
dtsFlag = pesScratch.readBit();
// ESCR_flag (1), ES_rate_flag (1), DSM_trick_mode_flag (1),
// additional_copy_info_flag (1), PES_CRC_flag (1), PES_extension_flag (1)
pesScratch.skipBits(6);
extendedHeaderLength = pesScratch.readBits(8);
if (packetLength == 0) {
payloadSize = -1;
} else {
payloadSize = packetLength + 6 /* packetLength does not include the first 6 bytes */
- HEADER_SIZE - extendedHeaderLength;
}
return true;
}
private void parseHeaderExtension() {
pesScratch.setPosition(0);
timeUs = C.TIME_UNSET;
if (ptsFlag) {
pesScratch.skipBits(4); // '0010' or '0011'
long pts = (long) pesScratch.readBits(3) << 30;
pesScratch.skipBits(1); // marker_bit
pts |= pesScratch.readBits(15) << 15;
pesScratch.skipBits(1); // marker_bit
pts |= pesScratch.readBits(15);
pesScratch.skipBits(1); // marker_bit
if (!seenFirstDts && dtsFlag) {
pesScratch.skipBits(4); // '0011'
long dts = (long) pesScratch.readBits(3) << 30;
pesScratch.skipBits(1); // marker_bit
dts |= pesScratch.readBits(15) << 15;
pesScratch.skipBits(1); // marker_bit
dts |= pesScratch.readBits(15);
pesScratch.skipBits(1); // marker_bit
// Subsequent PES packets may have earlier presentation timestamps than this one, but they
// should all be greater than or equal to this packet's decode timestamp. We feed the
// decode timestamp to the adjuster here so that in the case that this is the first to be
// fed, the adjuster will be able to compute an offset to apply such that the adjusted
// presentation timestamps of all future packets are non-negative.
timestampAdjuster.adjustTsTimestamp(dts);
seenFirstDts = true;
}
timeUs = timestampAdjuster.adjustTsTimestamp(pts);
}
}
}
...@@ -24,7 +24,7 @@ import com.google.android.exoplayer2.extractor.ExtractorsFactory; ...@@ -24,7 +24,7 @@ import com.google.android.exoplayer2.extractor.ExtractorsFactory;
import com.google.android.exoplayer2.extractor.PositionHolder; import com.google.android.exoplayer2.extractor.PositionHolder;
import com.google.android.exoplayer2.extractor.SeekMap; import com.google.android.exoplayer2.extractor.SeekMap;
import com.google.android.exoplayer2.extractor.TimestampAdjuster; import com.google.android.exoplayer2.extractor.TimestampAdjuster;
import com.google.android.exoplayer2.extractor.ts.ElementaryStreamReader.TrackIdGenerator; import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.TrackIdGenerator;
import com.google.android.exoplayer2.util.ParsableBitArray; import com.google.android.exoplayer2.util.ParsableBitArray;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
import java.io.IOException; import java.io.IOException;
...@@ -202,7 +202,7 @@ public final class PsExtractor implements Extractor { ...@@ -202,7 +202,7 @@ public final class PsExtractor implements Extractor {
} }
if (elementaryStreamReader != null) { if (elementaryStreamReader != null) {
TrackIdGenerator idGenerator = new TrackIdGenerator(streamId, MAX_STREAM_ID_PLUS_ONE); TrackIdGenerator idGenerator = new TrackIdGenerator(streamId, MAX_STREAM_ID_PLUS_ONE);
elementaryStreamReader.init(output, idGenerator); elementaryStreamReader.createTracks(output, idGenerator);
payloadReader = new PesReader(elementaryStreamReader, timestampAdjuster); payloadReader = new PesReader(elementaryStreamReader, timestampAdjuster);
psPayloadReaders.put(streamId, payloadReader); psPayloadReaders.put(streamId, payloadReader);
} }
...@@ -253,8 +253,7 @@ public final class PsExtractor implements Extractor { ...@@ -253,8 +253,7 @@ public final class PsExtractor implements Extractor {
private int extendedHeaderLength; private int extendedHeaderLength;
private long timeUs; private long timeUs;
public PesReader(ElementaryStreamReader pesPayloadReader, public PesReader(ElementaryStreamReader pesPayloadReader, TimestampAdjuster timestampAdjuster) {
TimestampAdjuster timestampAdjuster) {
this.pesPayloadReader = pesPayloadReader; this.pesPayloadReader = pesPayloadReader;
this.timestampAdjuster = timestampAdjuster; this.timestampAdjuster = timestampAdjuster;
pesScratch = new ParsableBitArray(new byte[PES_SCRATCH_SIZE]); pesScratch = new ParsableBitArray(new byte[PES_SCRATCH_SIZE]);
......
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2.extractor.ts;
import com.google.android.exoplayer2.util.ParsableByteArray;
/**
* Reads section data.
*/
public interface SectionPayloadReader {
/**
* Called by a {@link SectionReader} when a full section is received.
*
* @param sectionData The data belonging to a section, including the section header but excluding
* the CRC_32 field.
*/
void consume(ParsableByteArray sectionData);
}
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2.extractor.ts;
import com.google.android.exoplayer2.extractor.ExtractorOutput;
import com.google.android.exoplayer2.extractor.TimestampAdjuster;
import com.google.android.exoplayer2.util.ParsableBitArray;
import com.google.android.exoplayer2.util.ParsableByteArray;
import com.google.android.exoplayer2.util.Util;
/**
* Reads section data packets and feeds the whole sections to a given {@link SectionPayloadReader}.
*/
public final class SectionReader implements TsPayloadReader {
private static final int SECTION_HEADER_LENGTH = 3;
private final ParsableByteArray sectionData;
private final ParsableBitArray headerScratch;
private final SectionPayloadReader reader;
private int sectionLength;
private int sectionBytesRead;
public SectionReader(SectionPayloadReader reader) {
this.reader = reader;
sectionData = new ParsableByteArray();
headerScratch = new ParsableBitArray(new byte[SECTION_HEADER_LENGTH]);
}
@Override
public void init(TimestampAdjuster timestampAdjuster, ExtractorOutput extractorOutput,
TrackIdGenerator idGenerator) {
// TODO: Injectable section readers might want to generate metadata tracks.
// Do nothing.
}
@Override
public void seek() {
// Do nothing.
}
@Override
public void consume(ParsableByteArray data, boolean payloadUnitStartIndicator) {
// Skip pointer.
if (payloadUnitStartIndicator) {
int pointerField = data.readUnsignedByte();
data.skipBytes(pointerField);
// Note: see ISO/IEC 13818-1, section 2.4.4.3 for detailed information on the format of
// the header.
data.readBytes(headerScratch, SECTION_HEADER_LENGTH);
data.setPosition(data.getPosition() - SECTION_HEADER_LENGTH);
headerScratch.skipBits(12); // table_id (8), section_syntax_indicator (1), 0 (1), reserved (2)
sectionLength = headerScratch.readBits(12) + SECTION_HEADER_LENGTH;
sectionBytesRead = 0;
sectionData.reset(sectionLength);
}
int bytesToRead = Math.min(data.bytesLeft(), sectionLength - sectionBytesRead);
data.readBytes(sectionData.data, sectionBytesRead, bytesToRead);
sectionBytesRead += bytesToRead;
if (sectionBytesRead < sectionLength) {
// Not yet fully read.
return;
}
if (Util.crc(sectionData.data, 0, sectionLength, 0xFFFFFFFF) != 0) {
// CRC Invalid. The section gets discarded.
return;
}
sectionData.setLimit(sectionData.limit() - 4); // Exclude the CRC_32 field.
reader.consume(sectionData);
}
}
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2.extractor.ts;
import com.google.android.exoplayer2.extractor.ExtractorOutput;
import com.google.android.exoplayer2.extractor.TimestampAdjuster;
import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.util.ParsableByteArray;
/**
* Parses TS packet payload data.
*/
public interface TsPayloadReader {
/**
* Factory of {@link TsPayloadReader} instances.
*/
interface Factory {
/**
* Returns a {@link TsPayloadReader} for a given stream type and elementary stream information.
* May return null if the stream type is not supported.
*
* @param streamType Stream type value as defined in the PMT entry or associated descriptors.
* @param esInfo Information associated to the elementary stream provided in the PMT.
* @return A {@link TsPayloadReader} for the packet stream carried by the provided pid.
* {@code null} if the stream is not supported.
*/
TsPayloadReader createPayloadReader(int streamType, EsInfo esInfo);
}
/**
* Holds information associated with a PMT entry.
*/
final class EsInfo {
public final int streamType;
public final String language;
public final byte[] descriptorBytes;
/**
* @param streamType The type of the stream as defined by the
* {@link TsExtractor}{@code .TS_STREAM_TYPE_*}.
* @param language The language of the stream, as defined by ISO/IEC 13818-1, section 2.6.18.
* @param descriptorBytes The descriptor bytes associated to the stream.
*/
public EsInfo(int streamType, String language, byte[] descriptorBytes) {
this.streamType = streamType;
this.language = language;
this.descriptorBytes = descriptorBytes;
}
}
/**
* Generates track ids for initializing {@link TsPayloadReader}s' {@link TrackOutput}s.
*/
final class TrackIdGenerator {
private final int firstId;
private final int idIncrement;
private int generatedIdCount;
public TrackIdGenerator(int firstId, int idIncrement) {
this.firstId = firstId;
this.idIncrement = idIncrement;
}
public int getNextId() {
return firstId + idIncrement * generatedIdCount++;
}
}
/**
* Initializes the payload reader.
*
* @param timestampAdjuster
* @param extractorOutput
* @param idGenerator
*/
void init(TimestampAdjuster timestampAdjuster, ExtractorOutput extractorOutput,
TrackIdGenerator idGenerator);
/**
* Notifies the reader that a seek has occurred.
* <p>
* Following a call to this method, the data passed to the next invocation of
* {@link #consume(ParsableByteArray, boolean)} will not be a continuation of the data that was
* previously passed. Hence the reader should reset any internal state.
*/
void seek();
/**
* Consumes the payload of a TS packet.
*
* @param data The TS packet. The position will be set to the start of the payload.
* @param payloadUnitStartIndicator Whether payloadUnitStartIndicator was set on the TS packet.
*/
void consume(ParsableByteArray data, boolean payloadUnitStartIndicator);
}
...@@ -23,6 +23,7 @@ import android.media.MediaCodecList; ...@@ -23,6 +23,7 @@ import android.media.MediaCodecList;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import android.util.Pair; import android.util.Pair;
import android.util.SparseIntArray;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import java.util.ArrayList; import java.util.ArrayList;
...@@ -63,8 +64,8 @@ public final class MediaCodecUtil { ...@@ -63,8 +64,8 @@ public final class MediaCodecUtil {
// Codecs to constant mappings. // Codecs to constant mappings.
// AVC. // AVC.
private static final Map<Integer, Integer> AVC_PROFILE_NUMBER_TO_CONST; private static final SparseIntArray AVC_PROFILE_NUMBER_TO_CONST;
private static final Map<Integer, Integer> AVC_LEVEL_NUMBER_TO_CONST; private static final SparseIntArray AVC_LEVEL_NUMBER_TO_CONST;
private static final String CODEC_ID_AVC1 = "avc1"; private static final String CODEC_ID_AVC1 = "avc1";
private static final String CODEC_ID_AVC2 = "avc2"; private static final String CODEC_ID_AVC2 = "avc2";
// HEVC. // HEVC.
...@@ -222,6 +223,7 @@ public final class MediaCodecUtil { ...@@ -222,6 +223,7 @@ public final class MediaCodecUtil {
&& ("CIPAACDecoder".equals(name) && ("CIPAACDecoder".equals(name)
|| "CIPMP3Decoder".equals(name) || "CIPMP3Decoder".equals(name)
|| "CIPVorbisDecoder".equals(name) || "CIPVorbisDecoder".equals(name)
|| "CIPAMRNBDecoder".equals(name)
|| "AACDecoder".equals(name) || "AACDecoder".equals(name)
|| "MP3Decoder".equals(name))) { || "MP3Decoder".equals(name))) {
return false; return false;
...@@ -364,8 +366,8 @@ public final class MediaCodecUtil { ...@@ -364,8 +366,8 @@ public final class MediaCodecUtil {
Log.w(TAG, "Ignoring malformed AVC codec string: " + codec); Log.w(TAG, "Ignoring malformed AVC codec string: " + codec);
return null; return null;
} }
Integer profileInteger = null; Integer profileInteger;
Integer levelInteger = null; Integer levelInteger;
try { try {
if (codecsParts[1].length() == 6) { if (codecsParts[1].length() == 6) {
// Format: avc1.xxccyy, where xx is profile and yy level, both hexadecimal. // Format: avc1.xxccyy, where xx is profile and yy level, both hexadecimal.
...@@ -555,13 +557,13 @@ public final class MediaCodecUtil { ...@@ -555,13 +557,13 @@ public final class MediaCodecUtil {
} }
static { static {
AVC_PROFILE_NUMBER_TO_CONST = new HashMap<>(); AVC_PROFILE_NUMBER_TO_CONST = new SparseIntArray();
AVC_PROFILE_NUMBER_TO_CONST.put(66, CodecProfileLevel.AVCProfileBaseline); AVC_PROFILE_NUMBER_TO_CONST.put(66, CodecProfileLevel.AVCProfileBaseline);
AVC_PROFILE_NUMBER_TO_CONST.put(77, CodecProfileLevel.AVCProfileMain); AVC_PROFILE_NUMBER_TO_CONST.put(77, CodecProfileLevel.AVCProfileMain);
AVC_PROFILE_NUMBER_TO_CONST.put(88, CodecProfileLevel.AVCProfileExtended); AVC_PROFILE_NUMBER_TO_CONST.put(88, CodecProfileLevel.AVCProfileExtended);
AVC_PROFILE_NUMBER_TO_CONST.put(100, CodecProfileLevel.AVCProfileHigh); AVC_PROFILE_NUMBER_TO_CONST.put(100, CodecProfileLevel.AVCProfileHigh);
AVC_LEVEL_NUMBER_TO_CONST = new HashMap<>(); AVC_LEVEL_NUMBER_TO_CONST = new SparseIntArray();
AVC_LEVEL_NUMBER_TO_CONST.put(10, CodecProfileLevel.AVCLevel1); AVC_LEVEL_NUMBER_TO_CONST.put(10, CodecProfileLevel.AVCLevel1);
// TODO: Find int for CodecProfileLevel.AVCLevel1b. // TODO: Find int for CodecProfileLevel.AVCLevel1b.
AVC_LEVEL_NUMBER_TO_CONST.put(11, CodecProfileLevel.AVCLevel11); AVC_LEVEL_NUMBER_TO_CONST.put(11, CodecProfileLevel.AVCLevel11);
......
...@@ -149,9 +149,9 @@ import java.util.IdentityHashMap; ...@@ -149,9 +149,9 @@ import java.util.IdentityHashMap;
} }
// It must be possible to seek enabled periods to the new position, if there is one. // It must be possible to seek enabled periods to the new position, if there is one.
if (positionUs != C.TIME_UNSET) { if (positionUs != C.TIME_UNSET) {
for (int i = 0; i < enabledPeriods.length; i++) { for (MediaPeriod enabledPeriod : enabledPeriods) {
if (enabledPeriods[i] != periods[0] if (enabledPeriod != periods[0]
&& enabledPeriods[i].seekToUs(positionUs) != positionUs) { && enabledPeriod.seekToUs(positionUs) != positionUs) {
throw new IllegalStateException("Children seeked to different positions"); throw new IllegalStateException("Children seeked to different positions");
} }
} }
......
...@@ -26,7 +26,7 @@ import com.google.android.exoplayer2.extractor.mp3.Mp3Extractor; ...@@ -26,7 +26,7 @@ import com.google.android.exoplayer2.extractor.mp3.Mp3Extractor;
import com.google.android.exoplayer2.extractor.mp4.FragmentedMp4Extractor; import com.google.android.exoplayer2.extractor.mp4.FragmentedMp4Extractor;
import com.google.android.exoplayer2.extractor.ts.Ac3Extractor; import com.google.android.exoplayer2.extractor.ts.Ac3Extractor;
import com.google.android.exoplayer2.extractor.ts.AdtsExtractor; import com.google.android.exoplayer2.extractor.ts.AdtsExtractor;
import com.google.android.exoplayer2.extractor.ts.DefaultStreamReaderFactory; import com.google.android.exoplayer2.extractor.ts.DefaultTsPayloadReaderFactory;
import com.google.android.exoplayer2.extractor.ts.TsExtractor; import com.google.android.exoplayer2.extractor.ts.TsExtractor;
import com.google.android.exoplayer2.source.BehindLiveWindowException; import com.google.android.exoplayer2.source.BehindLiveWindowException;
import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.TrackGroup;
...@@ -384,7 +384,7 @@ import java.util.Locale; ...@@ -384,7 +384,7 @@ import java.util.Locale;
timestampAdjuster = timestampAdjusterProvider.getAdjuster( timestampAdjuster = timestampAdjusterProvider.getAdjuster(
segment.discontinuitySequenceNumber, startTimeUs); segment.discontinuitySequenceNumber, startTimeUs);
// 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.
@DefaultStreamReaderFactory.Flags @DefaultTsPayloadReaderFactory.Flags
int esReaderFactoryFlags = 0; int esReaderFactoryFlags = 0;
String codecs = variants[newVariantIndex].format.codecs; String codecs = variants[newVariantIndex].format.codecs;
if (!TextUtils.isEmpty(codecs)) { if (!TextUtils.isEmpty(codecs)) {
...@@ -392,14 +392,14 @@ import java.util.Locale; ...@@ -392,14 +392,14 @@ import java.util.Locale;
// exist. If we know from the codec attribute that they don't exist, then we can // exist. If we know from the codec attribute that they don't exist, then we can
// explicitly ignore them even if they're declared. // explicitly ignore them even if they're declared.
if (!MimeTypes.AUDIO_AAC.equals(MimeTypes.getAudioMediaMimeType(codecs))) { if (!MimeTypes.AUDIO_AAC.equals(MimeTypes.getAudioMediaMimeType(codecs))) {
esReaderFactoryFlags |= DefaultStreamReaderFactory.FLAG_IGNORE_AAC_STREAM; esReaderFactoryFlags |= DefaultTsPayloadReaderFactory.FLAG_IGNORE_AAC_STREAM;
} }
if (!MimeTypes.VIDEO_H264.equals(MimeTypes.getVideoMediaMimeType(codecs))) { if (!MimeTypes.VIDEO_H264.equals(MimeTypes.getVideoMediaMimeType(codecs))) {
esReaderFactoryFlags |= DefaultStreamReaderFactory.FLAG_IGNORE_H264_STREAM; esReaderFactoryFlags |= DefaultTsPayloadReaderFactory.FLAG_IGNORE_H264_STREAM;
} }
} }
extractor = new TsExtractor(timestampAdjuster, extractor = new TsExtractor(timestampAdjuster,
new DefaultStreamReaderFactory(esReaderFactoryFlags), true); new DefaultTsPayloadReaderFactory(esReaderFactoryFlags), true);
} }
} else { } else {
// MPEG-2 TS segments, and we need to continue using the same extractor. // MPEG-2 TS segments, and we need to continue using the same extractor.
......
...@@ -69,7 +69,6 @@ import java.util.List; ...@@ -69,7 +69,6 @@ import java.util.List;
private int pendingPrepareCount; private int pendingPrepareCount;
private HlsPlaylist playlist; private HlsPlaylist playlist;
private boolean seenFirstTrackSelection; private boolean seenFirstTrackSelection;
private long durationUs;
private boolean isLive; private boolean isLive;
private TrackGroupArray trackGroups; private TrackGroupArray trackGroups;
private HlsSampleStreamWrapper[] sampleStreamWrappers; private HlsSampleStreamWrapper[] sampleStreamWrappers;
...@@ -280,7 +279,7 @@ import java.util.List; ...@@ -280,7 +279,7 @@ import java.util.List;
} }
// The wrapper at index 0 is the one of type TRACK_TYPE_DEFAULT. // The wrapper at index 0 is the one of type TRACK_TYPE_DEFAULT.
durationUs = sampleStreamWrappers[0].getDurationUs(); long durationUs = sampleStreamWrappers[0].getDurationUs();
isLive = sampleStreamWrappers[0].isLive(); isLive = sampleStreamWrappers[0].isLive();
int totalTrackGroupCount = 0; int totalTrackGroupCount = 0;
......
...@@ -20,7 +20,7 @@ package com.google.android.exoplayer2.text; ...@@ -20,7 +20,7 @@ package com.google.android.exoplayer2.text;
*/ */
/* package */ final class SimpleSubtitleOutputBuffer extends SubtitleOutputBuffer { /* package */ final class SimpleSubtitleOutputBuffer extends SubtitleOutputBuffer {
private SimpleSubtitleDecoder owner; private final SimpleSubtitleDecoder owner;
/** /**
* @param owner The decoder that owns this buffer. * @param owner The decoder that owns this buffer.
......
...@@ -28,6 +28,7 @@ import com.google.android.exoplayer2.util.ParsableByteArray; ...@@ -28,6 +28,7 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
public final class Cea608Decoder extends CeaDecoder { public final class Cea608Decoder extends CeaDecoder {
private static final int NTSC_CC_FIELD_1 = 0x00; private static final int NTSC_CC_FIELD_1 = 0x00;
private static final int CC_TYPE_MASK = 0x03;
private static final int CC_VALID_FLAG = 0x04; private static final int CC_VALID_FLAG = 0x04;
private static final int PAYLOAD_TYPE_CC = 4; private static final int PAYLOAD_TYPE_CC = 4;
...@@ -223,7 +224,8 @@ public final class Cea608Decoder extends CeaDecoder { ...@@ -223,7 +224,8 @@ public final class Cea608Decoder extends CeaDecoder {
byte ccData2 = (byte) (ccData.readUnsignedByte() & 0x7F); byte ccData2 = (byte) (ccData.readUnsignedByte() & 0x7F);
// Only examine valid NTSC_CC_FIELD_1 packets // Only examine valid NTSC_CC_FIELD_1 packets
if (ccTypeAndValid != (CC_VALID_FLAG | NTSC_CC_FIELD_1)) { if ((ccTypeAndValid & CC_VALID_FLAG) == 0
|| (ccTypeAndValid & CC_TYPE_MASK) != NTSC_CC_FIELD_1) {
// TODO: Add support for NTSC_CC_FIELD_2 packets // TODO: Add support for NTSC_CC_FIELD_2 packets
continue; continue;
} }
......
...@@ -92,21 +92,22 @@ import java.util.Map; ...@@ -92,21 +92,22 @@ import java.util.Map;
builder.setSpan(new AlignmentSpan.Standard(style.getTextAlign()), start, end, builder.setSpan(new AlignmentSpan.Standard(style.getTextAlign()), start, end,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
} }
if (style.getFontSizeUnit() != TtmlStyle.UNSPECIFIED) { switch (style.getFontSizeUnit()) {
switch (style.getFontSizeUnit()) { case TtmlStyle.FONT_SIZE_UNIT_PIXEL:
case TtmlStyle.FONT_SIZE_UNIT_PIXEL: builder.setSpan(new AbsoluteSizeSpan((int) style.getFontSize(), true), start, end,
builder.setSpan(new AbsoluteSizeSpan((int) style.getFontSize(), true), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); break;
break; case TtmlStyle.FONT_SIZE_UNIT_EM:
case TtmlStyle.FONT_SIZE_UNIT_EM: builder.setSpan(new RelativeSizeSpan(style.getFontSize()), start, end,
builder.setSpan(new RelativeSizeSpan(style.getFontSize()), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); break;
break; case TtmlStyle.FONT_SIZE_UNIT_PERCENT:
case TtmlStyle.FONT_SIZE_UNIT_PERCENT: builder.setSpan(new RelativeSizeSpan(style.getFontSize() / 100), start, end,
builder.setSpan(new RelativeSizeSpan(style.getFontSize() / 100), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); break;
break; case TtmlStyle.UNSPECIFIED:
} // Do nothing.
break;
} }
} }
......
...@@ -413,21 +413,22 @@ import java.util.regex.Pattern; ...@@ -413,21 +413,22 @@ import java.util.regex.Pattern;
spannedText.setSpan(new AlignmentSpan.Standard(style.getTextAlign()), start, end, spannedText.setSpan(new AlignmentSpan.Standard(style.getTextAlign()), start, end,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
} }
if (style.getFontSizeUnit() != WebvttCssStyle.UNSPECIFIED) { switch (style.getFontSizeUnit()) {
switch (style.getFontSizeUnit()) { case WebvttCssStyle.FONT_SIZE_UNIT_PIXEL:
case WebvttCssStyle.FONT_SIZE_UNIT_PIXEL: spannedText.setSpan(new AbsoluteSizeSpan((int) style.getFontSize(), true), start, end,
spannedText.setSpan(new AbsoluteSizeSpan((int) style.getFontSize(), true), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); break;
break; case WebvttCssStyle.FONT_SIZE_UNIT_EM:
case WebvttCssStyle.FONT_SIZE_UNIT_EM: spannedText.setSpan(new RelativeSizeSpan(style.getFontSize()), start, end,
spannedText.setSpan(new RelativeSizeSpan(style.getFontSize()), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); break;
break; case WebvttCssStyle.FONT_SIZE_UNIT_PERCENT:
case WebvttCssStyle.FONT_SIZE_UNIT_PERCENT: spannedText.setSpan(new RelativeSizeSpan(style.getFontSize() / 100), start, end,
spannedText.setSpan(new RelativeSizeSpan(style.getFontSize() / 100), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); break;
break; case WebvttCssStyle.UNSPECIFIED:
} // Do nothing.
break;
} }
} }
......
...@@ -70,8 +70,8 @@ public abstract class MappingTrackSelector extends TrackSelector<MappedTrackInfo ...@@ -70,8 +70,8 @@ public abstract class MappingTrackSelector extends TrackSelector<MappedTrackInfo
* Returns whether this override contains the specified track index. * Returns whether this override contains the specified track index.
*/ */
public boolean containsTrack(int track) { public boolean containsTrack(int track) {
for (int i = 0; i < tracks.length; i++) { for (int overrideTrack : tracks) {
if (tracks[i] == track) { if (overrideTrack == track) {
return true; return true;
} }
} }
......
...@@ -54,7 +54,7 @@ public final class RawResourceDataSource implements DataSource { ...@@ -54,7 +54,7 @@ public final class RawResourceDataSource implements DataSource {
* @param rawResourceId A raw resource identifier (i.e. a constant defined in {@code R.raw}). * @param rawResourceId A raw resource identifier (i.e. a constant defined in {@code R.raw}).
* @return The corresponding {@link Uri}. * @return The corresponding {@link Uri}.
*/ */
public static final Uri buildRawResourceUri(int rawResourceId) { public static Uri buildRawResourceUri(int rawResourceId) {
return Uri.parse(RAW_RESOURCE_SCHEME + ":///" + rawResourceId); return Uri.parse(RAW_RESOURCE_SCHEME + ":///" + rawResourceId);
} }
......
...@@ -211,10 +211,10 @@ public final class MimeTypes { ...@@ -211,10 +211,10 @@ public final class MimeTypes {
} else if (isVideo(mimeType)) { } else if (isVideo(mimeType)) {
return C.TRACK_TYPE_VIDEO; return C.TRACK_TYPE_VIDEO;
} else if (isText(mimeType) || APPLICATION_CEA608.equals(mimeType) } else if (isText(mimeType) || APPLICATION_CEA608.equals(mimeType)
|| APPLICATION_SUBRIP.equals(mimeType) || APPLICATION_TTML.equals(mimeType) || APPLICATION_CEA708.equals(mimeType) || APPLICATION_SUBRIP.equals(mimeType)
|| APPLICATION_TX3G.equals(mimeType) || APPLICATION_MP4VTT.equals(mimeType) || APPLICATION_TTML.equals(mimeType) || APPLICATION_TX3G.equals(mimeType)
|| APPLICATION_RAWCC.equals(mimeType) || APPLICATION_VOBSUB.equals(mimeType) || APPLICATION_MP4VTT.equals(mimeType) || APPLICATION_RAWCC.equals(mimeType)
|| APPLICATION_PGS.equals(mimeType)) { || APPLICATION_VOBSUB.equals(mimeType) || APPLICATION_PGS.equals(mimeType)) {
return C.TRACK_TYPE_TEXT; return C.TRACK_TYPE_TEXT;
} else if (APPLICATION_ID3.equals(mimeType)) { } else if (APPLICATION_ID3.equals(mimeType)) {
return C.TRACK_TYPE_METADATA; return C.TRACK_TYPE_METADATA;
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
limitations under the License. limitations under the License.
--> -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="bottom" android:layout_gravity="bottom"
...@@ -37,6 +38,7 @@ ...@@ -37,6 +38,7 @@
style="@style/ExoMediaButton.Rewind"/> style="@style/ExoMediaButton.Rewind"/>
<ImageButton android:id="@+id/play" <ImageButton android:id="@+id/play"
tools:ignore="ContentDescription"
style="@style/ExoMediaButton"/> style="@style/ExoMediaButton"/>
<ImageButton android:id="@+id/ffwd" <ImageButton android:id="@+id/ffwd"
......
...@@ -28,6 +28,7 @@ import com.google.android.exoplayer2.ExoPlaybackException; ...@@ -28,6 +28,7 @@ import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.RendererCapabilities; import com.google.android.exoplayer2.RendererCapabilities;
import com.google.android.exoplayer2.decoder.DecoderCounters; import com.google.android.exoplayer2.decoder.DecoderCounters;
import com.google.android.exoplayer2.drm.FrameworkMediaCrypto;
import com.google.android.exoplayer2.drm.HttpMediaDrmCallback; import com.google.android.exoplayer2.drm.HttpMediaDrmCallback;
import com.google.android.exoplayer2.drm.StreamingDrmSessionManager; import com.google.android.exoplayer2.drm.StreamingDrmSessionManager;
import com.google.android.exoplayer2.drm.UnsupportedDrmException; import com.google.android.exoplayer2.drm.UnsupportedDrmException;
...@@ -696,8 +697,9 @@ public final class DashTest extends ActivityInstrumentationTestCase2<HostActivit ...@@ -696,8 +697,9 @@ public final class DashTest extends ActivityInstrumentationTestCase2<HostActivit
@Override @Override
@TargetApi(18) @TargetApi(18)
@SuppressWarnings("ResourceType") @SuppressWarnings("ResourceType")
protected final StreamingDrmSessionManager buildDrmSessionManager(final String userAgent) { protected final StreamingDrmSessionManager<FrameworkMediaCrypto> buildDrmSessionManager(
StreamingDrmSessionManager drmSessionManager = null; final String userAgent) {
StreamingDrmSessionManager<FrameworkMediaCrypto> drmSessionManager = null;
if (isWidevineEncrypted) { if (isWidevineEncrypted) {
try { try {
// Force L3 if secure decoder is not available. // Force L3 if secure decoder is not available.
......
...@@ -30,6 +30,7 @@ import com.google.android.exoplayer2.audio.AudioRendererEventListener; ...@@ -30,6 +30,7 @@ import com.google.android.exoplayer2.audio.AudioRendererEventListener;
import com.google.android.exoplayer2.audio.AudioTrack; import com.google.android.exoplayer2.audio.AudioTrack;
import com.google.android.exoplayer2.decoder.DecoderCounters; import com.google.android.exoplayer2.decoder.DecoderCounters;
import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.drm.DrmSessionManager;
import com.google.android.exoplayer2.drm.FrameworkMediaCrypto;
import com.google.android.exoplayer2.playbacktests.util.HostActivity.HostedTest; import com.google.android.exoplayer2.playbacktests.util.HostActivity.HostedTest;
import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.trackselection.AdaptiveVideoTrackSelection; import com.google.android.exoplayer2.trackselection.AdaptiveVideoTrackSelection;
...@@ -130,7 +131,7 @@ public abstract class ExoHostedTest implements HostedTest, ExoPlayer.EventListen ...@@ -130,7 +131,7 @@ public abstract class ExoHostedTest implements HostedTest, ExoPlayer.EventListen
DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter(); DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
trackSelector = buildTrackSelector(host, bandwidthMeter); trackSelector = buildTrackSelector(host, bandwidthMeter);
String userAgent = "ExoPlayerPlaybackTests"; String userAgent = "ExoPlayerPlaybackTests";
DrmSessionManager drmSessionManager = buildDrmSessionManager(userAgent); DrmSessionManager<FrameworkMediaCrypto> drmSessionManager = buildDrmSessionManager(userAgent);
player = buildExoPlayer(host, surface, trackSelector, drmSessionManager); player = buildExoPlayer(host, surface, trackSelector, drmSessionManager);
player.prepare(buildSource(host, Util.getUserAgent(host, userAgent), bandwidthMeter)); player.prepare(buildSource(host, Util.getUserAgent(host, userAgent), bandwidthMeter));
player.addListener(this); player.addListener(this);
...@@ -296,7 +297,7 @@ public abstract class ExoHostedTest implements HostedTest, ExoPlayer.EventListen ...@@ -296,7 +297,7 @@ public abstract class ExoHostedTest implements HostedTest, ExoPlayer.EventListen
// Internal logic // Internal logic
protected DrmSessionManager buildDrmSessionManager(String userAgent) { protected DrmSessionManager<FrameworkMediaCrypto> buildDrmSessionManager(String userAgent) {
// Do nothing. Interested subclasses may override. // Do nothing. Interested subclasses may override.
return null; return null;
} }
...@@ -309,7 +310,8 @@ public abstract class ExoHostedTest implements HostedTest, ExoPlayer.EventListen ...@@ -309,7 +310,8 @@ public abstract class ExoHostedTest implements HostedTest, ExoPlayer.EventListen
@SuppressWarnings("unused") @SuppressWarnings("unused")
protected SimpleExoPlayer buildExoPlayer(HostActivity host, Surface surface, protected SimpleExoPlayer buildExoPlayer(HostActivity host, Surface surface,
MappingTrackSelector trackSelector, DrmSessionManager drmSessionManager) { MappingTrackSelector trackSelector,
DrmSessionManager<FrameworkMediaCrypto> drmSessionManager) {
SimpleExoPlayer player = ExoPlayerFactory.newSimpleInstance(host, trackSelector, SimpleExoPlayer player = ExoPlayerFactory.newSimpleInstance(host, trackSelector,
new DefaultLoadControl(), drmSessionManager, false, 0); new DefaultLoadControl(), drmSessionManager, false, 0);
player.setVideoSurface(surface); player.setVideoSurface(surface);
......
...@@ -232,7 +232,7 @@ public final class HostActivity extends Activity implements SurfaceHolder.Callba ...@@ -232,7 +232,7 @@ public final class HostActivity extends Activity implements SurfaceHolder.Callba
} }
@SuppressLint("InlinedApi") @SuppressLint("InlinedApi")
private static final int getWifiLockMode() { private static int getWifiLockMode() {
return Util.SDK_INT < 12 ? WifiManager.WIFI_MODE_FULL : WifiManager.WIFI_MODE_FULL_HIGH_PERF; return Util.SDK_INT < 12 ? WifiManager.WIFI_MODE_FULL : WifiManager.WIFI_MODE_FULL_HIGH_PERF;
} }
......
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