From b1f9798b892a1285675d0f900dab3efd869f91d5 Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Thu, 15 Sep 2016 03:21:00 -0700 Subject: [PATCH 01/36] Fix peeking the end of the stream then reading it. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=133240502 --- .../extractor/DefaultExtractorInputTest.java | 73 +++++++++++-------- .../extractor/DefaultExtractorInput.java | 2 +- 2 files changed, 45 insertions(+), 30 deletions(-) diff --git a/library/src/androidTest/java/com/google/android/exoplayer2/extractor/DefaultExtractorInputTest.java b/library/src/androidTest/java/com/google/android/exoplayer2/extractor/DefaultExtractorInputTest.java index d0ba4d800c..dcab0c6275 100644 --- a/library/src/androidTest/java/com/google/android/exoplayer2/extractor/DefaultExtractorInputTest.java +++ b/library/src/androidTest/java/com/google/android/exoplayer2/extractor/DefaultExtractorInputTest.java @@ -33,14 +33,14 @@ public class DefaultExtractorInputTest extends TestCase { private static final byte[] TEST_DATA = new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8}; private static final int LARGE_TEST_DATA_LENGTH = 8192; - public void testInitialPosition() throws IOException { + public void testInitialPosition() throws Exception { FakeDataSource testDataSource = buildDataSource(); DefaultExtractorInput input = new DefaultExtractorInput(testDataSource, 123, C.LENGTH_UNSET); assertEquals(123, input.getPosition()); } - public void testRead() throws IOException, InterruptedException { + public void testRead() throws Exception { DefaultExtractorInput input = createDefaultExtractorInput(); byte[] target = new byte[TEST_DATA.length]; // We expect to perform three reads of three bytes, as setup in buildTestDataSource. @@ -58,7 +58,7 @@ public class DefaultExtractorInputTest extends TestCase { assertEquals(C.RESULT_END_OF_INPUT, expectedEndOfInput); } - public void testReadPeeked() throws IOException, InterruptedException { + public void testReadPeeked() throws Exception { DefaultExtractorInput input = createDefaultExtractorInput(); byte[] target = new byte[TEST_DATA.length]; @@ -71,7 +71,7 @@ public class DefaultExtractorInputTest extends TestCase { assertTrue(Arrays.equals(TEST_DATA, target)); } - public void testReadMoreDataPeeked() throws IOException, InterruptedException { + public void testReadMoreDataPeeked() throws Exception { DefaultExtractorInput input = createDefaultExtractorInput(); byte[] target = new byte[TEST_DATA.length]; @@ -84,7 +84,7 @@ public class DefaultExtractorInputTest extends TestCase { assertTrue(Arrays.equals(TEST_DATA, target)); } - public void testReadFullyOnce() throws IOException, InterruptedException { + public void testReadFullyOnce() throws Exception { DefaultExtractorInput input = createDefaultExtractorInput(); byte[] target = new byte[TEST_DATA.length]; input.readFully(target, 0, TEST_DATA.length); @@ -103,7 +103,7 @@ public class DefaultExtractorInputTest extends TestCase { } } - public void testReadFullyTwice() throws IOException, InterruptedException { + public void testReadFullyTwice() throws Exception { // Read TEST_DATA in two parts. DefaultExtractorInput input = createDefaultExtractorInput(); byte[] target = new byte[5]; @@ -116,7 +116,7 @@ public class DefaultExtractorInputTest extends TestCase { assertEquals(5 + 4, input.getPosition()); } - public void testReadFullyTooMuch() throws IOException, InterruptedException { + public void testReadFullyTooMuch() throws Exception { // Read more than TEST_DATA. Should fail with an EOFException. Position should not update. DefaultExtractorInput input = createDefaultExtractorInput(); try { @@ -141,7 +141,7 @@ public class DefaultExtractorInputTest extends TestCase { assertEquals(0, input.getPosition()); } - public void testReadFullyWithFailingDataSource() throws IOException, InterruptedException { + public void testReadFullyWithFailingDataSource() throws Exception { FakeDataSource testDataSource = buildFailingDataSource(); DefaultExtractorInput input = new DefaultExtractorInput(testDataSource, 0, C.LENGTH_UNSET); try { @@ -155,7 +155,7 @@ public class DefaultExtractorInputTest extends TestCase { assertEquals(0, input.getPosition()); } - public void testReadFullyHalfPeeked() throws IOException, InterruptedException { + public void testReadFullyHalfPeeked() throws Exception { DefaultExtractorInput input = createDefaultExtractorInput(); byte[] target = new byte[TEST_DATA.length]; @@ -168,7 +168,7 @@ public class DefaultExtractorInputTest extends TestCase { assertEquals(TEST_DATA.length, input.getPosition()); } - public void testSkip() throws IOException, InterruptedException { + public void testSkip() throws Exception { FakeDataSource testDataSource = buildDataSource(); DefaultExtractorInput input = new DefaultExtractorInput(testDataSource, 0, C.LENGTH_UNSET); // We expect to perform three skips of three bytes, as setup in buildTestDataSource. @@ -180,7 +180,7 @@ public class DefaultExtractorInputTest extends TestCase { assertEquals(C.RESULT_END_OF_INPUT, expectedEndOfInput); } - public void testLargeSkip() throws IOException, InterruptedException { + public void testLargeSkip() throws Exception { FakeDataSource testDataSource = buildLargeDataSource(); DefaultExtractorInput input = new DefaultExtractorInput(testDataSource, 0, C.LENGTH_UNSET); // Check that skipping the entire data source succeeds. @@ -190,7 +190,7 @@ public class DefaultExtractorInputTest extends TestCase { } } - public void testSkipFullyOnce() throws IOException, InterruptedException { + public void testSkipFullyOnce() throws Exception { // Skip TEST_DATA. DefaultExtractorInput input = createDefaultExtractorInput(); input.skipFully(TEST_DATA.length); @@ -207,7 +207,7 @@ public class DefaultExtractorInputTest extends TestCase { } } - public void testSkipFullyTwice() throws IOException, InterruptedException { + public void testSkipFullyTwice() throws Exception { // Skip TEST_DATA in two parts. DefaultExtractorInput input = createDefaultExtractorInput(); input.skipFully(5); @@ -216,7 +216,7 @@ public class DefaultExtractorInputTest extends TestCase { assertEquals(5 + 4, input.getPosition()); } - public void testSkipFullyTwicePeeked() throws IOException, InterruptedException { + public void testSkipFullyTwicePeeked() throws Exception { // Skip TEST_DATA. DefaultExtractorInput input = createDefaultExtractorInput(); @@ -230,7 +230,7 @@ public class DefaultExtractorInputTest extends TestCase { assertEquals(TEST_DATA.length, input.getPosition()); } - public void testSkipFullyTooMuch() throws IOException, InterruptedException { + public void testSkipFullyTooMuch() throws Exception { // Skip more than TEST_DATA. Should fail with an EOFException. Position should not update. DefaultExtractorInput input = createDefaultExtractorInput(); try { @@ -253,7 +253,7 @@ public class DefaultExtractorInputTest extends TestCase { assertEquals(0, input.getPosition()); } - public void testSkipFullyWithFailingDataSource() throws IOException, InterruptedException { + public void testSkipFullyWithFailingDataSource() throws Exception { FakeDataSource testDataSource = buildFailingDataSource(); DefaultExtractorInput input = new DefaultExtractorInput(testDataSource, 0, C.LENGTH_UNSET); try { @@ -266,7 +266,7 @@ public class DefaultExtractorInputTest extends TestCase { assertEquals(0, input.getPosition()); } - public void testSkipFullyLarge() throws IOException, InterruptedException { + public void testSkipFullyLarge() throws Exception { // Tests skipping an amount of data that's larger than any internal scratch space. int largeSkipSize = 1024 * 1024; FakeDataSource.Builder builder = new FakeDataSource.Builder(); @@ -286,7 +286,7 @@ public class DefaultExtractorInputTest extends TestCase { } } - public void testPeekFully() throws IOException, InterruptedException { + public void testPeekFully() throws Exception { DefaultExtractorInput input = createDefaultExtractorInput(); byte[] target = new byte[TEST_DATA.length]; input.peekFully(target, 0, TEST_DATA.length); @@ -312,7 +312,7 @@ public class DefaultExtractorInputTest extends TestCase { } } - public void testResetPeekPosition() throws IOException, InterruptedException { + public void testResetPeekPosition() throws Exception { DefaultExtractorInput input = createDefaultExtractorInput(); byte[] target = new byte[TEST_DATA.length]; input.peekFully(target, 0, TEST_DATA.length); @@ -336,8 +336,7 @@ public class DefaultExtractorInputTest extends TestCase { } } - public void testPeekFullyAtEndOfStreamWithAllowEndOfInputSucceeds() - throws IOException, InterruptedException { + public void testPeekFullyAtEndOfStreamWithAllowEndOfInputSucceeds() throws Exception { DefaultExtractorInput input = createDefaultExtractorInput(); byte[] target = new byte[TEST_DATA.length]; @@ -348,8 +347,24 @@ public class DefaultExtractorInputTest extends TestCase { assertFalse(input.peekFully(target, 0, 1, true)); } - public void testPeekFullyAcrossEndOfInputWithAllowEndOfInputFails() - throws IOException, InterruptedException { + public void testPeekFullyAtEndThenReadEndOfInput() throws Exception { + DefaultExtractorInput input = createDefaultExtractorInput(); + byte[] target = new byte[TEST_DATA.length]; + + // Peek up to the end of the input. + assertTrue(input.peekFully(target, 0, TEST_DATA.length, false)); + + // Peek the end of the input. + assertFalse(input.peekFully(target, 0, 1, true)); + + // Read up to the end of the input. + assertTrue(input.readFully(target, 0, TEST_DATA.length, false)); + + // Read the end of the input. + assertFalse(input.readFully(target, 0, 1, true)); + } + + public void testPeekFullyAcrossEndOfInputWithAllowEndOfInputFails() throws Exception { DefaultExtractorInput input = createDefaultExtractorInput(); byte[] target = new byte[TEST_DATA.length]; @@ -365,8 +380,7 @@ public class DefaultExtractorInputTest extends TestCase { } } - public void testResetAndPeekFullyPastEndOfStreamWithAllowEndOfInputFails() - throws IOException, InterruptedException { + public void testResetAndPeekFullyPastEndOfStreamWithAllowEndOfInputFails() throws Exception { DefaultExtractorInput input = createDefaultExtractorInput(); byte[] target = new byte[TEST_DATA.length]; @@ -382,7 +396,7 @@ public class DefaultExtractorInputTest extends TestCase { } } - private static FakeDataSource buildDataSource() throws IOException { + private static FakeDataSource buildDataSource() throws Exception { FakeDataSource.Builder builder = new FakeDataSource.Builder(); builder.appendReadData(Arrays.copyOfRange(TEST_DATA, 0, 3)); builder.appendReadData(Arrays.copyOfRange(TEST_DATA, 3, 6)); @@ -392,7 +406,7 @@ public class DefaultExtractorInputTest extends TestCase { return testDataSource; } - private static FakeDataSource buildFailingDataSource() throws IOException { + private static FakeDataSource buildFailingDataSource() throws Exception { FakeDataSource.Builder builder = new FakeDataSource.Builder(); builder.appendReadData(Arrays.copyOfRange(TEST_DATA, 0, 6)); builder.appendReadError(new IOException()); @@ -402,7 +416,7 @@ public class DefaultExtractorInputTest extends TestCase { return testDataSource; } - private static FakeDataSource buildLargeDataSource() throws IOException { + private static FakeDataSource buildLargeDataSource() throws Exception { FakeDataSource.Builder builder = new FakeDataSource.Builder(); builder.appendReadData(new byte[LARGE_TEST_DATA_LENGTH]); FakeDataSource testDataSource = builder.build(); @@ -410,8 +424,9 @@ public class DefaultExtractorInputTest extends TestCase { return testDataSource; } - private static DefaultExtractorInput createDefaultExtractorInput() throws IOException { + private static DefaultExtractorInput createDefaultExtractorInput() throws Exception { FakeDataSource testDataSource = buildDataSource(); return new DefaultExtractorInput(testDataSource, 0, C.LENGTH_UNSET); } + } diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/DefaultExtractorInput.java b/library/src/main/java/com/google/android/exoplayer2/extractor/DefaultExtractorInput.java index 6a20b08113..bca5ecf3bd 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/DefaultExtractorInput.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/DefaultExtractorInput.java @@ -125,7 +125,6 @@ public final class DefaultExtractorInput implements ExtractorInput { throws IOException, InterruptedException { ensureSpaceForPeek(length); int bytesPeeked = Math.min(peekBufferLength - peekBufferPosition, length); - peekBufferLength += length - bytesPeeked; while (bytesPeeked < length) { bytesPeeked = readFromDataSource(peekBuffer, peekBufferPosition, length, bytesPeeked, allowEndOfInput); @@ -134,6 +133,7 @@ public final class DefaultExtractorInput implements ExtractorInput { } } peekBufferPosition += length; + peekBufferLength = Math.max(peekBufferLength, peekBufferPosition); return true; } From 6a099f1c5706642d407fa317a201776057820779 Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Thu, 15 Sep 2016 03:37:47 -0700 Subject: [PATCH 02/36] Clean up MP3 synchronization and fix handling < 4 frames. Also add a test MP3 stream with one frame. Make FakeExtractorInput's end of input detection to apply also for peekFully, and make its skip and read methods read at least one byte. Issue: #1732 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=133241641 --- .../androidTest/assets/mp3/play-trimmed.mp3 | Bin 0 -> 418 bytes .../assets/mp3/play-trimmed.mp3.0.dump | 33 +++++++ .../assets/mp3/play-trimmed.mp3.1.dump | 29 ++++++ .../assets/mp3/play-trimmed.mp3.2.dump | 29 ++++++ .../assets/mp3/play-trimmed.mp3.3.dump | 29 ++++++ .../assets/mp3/play-trimmed.mp3.unklen.dump | 33 +++++++ .../extractor/mp3/Mp3ExtractorTest.java | 9 ++ .../extractor/mp3/Mp3Extractor.java | 87 +++++++----------- .../testutil/FakeExtractorInput.java | 12 +-- 9 files changed, 201 insertions(+), 60 deletions(-) create mode 100644 library/src/androidTest/assets/mp3/play-trimmed.mp3 create mode 100644 library/src/androidTest/assets/mp3/play-trimmed.mp3.0.dump create mode 100644 library/src/androidTest/assets/mp3/play-trimmed.mp3.1.dump create mode 100644 library/src/androidTest/assets/mp3/play-trimmed.mp3.2.dump create mode 100644 library/src/androidTest/assets/mp3/play-trimmed.mp3.3.dump create mode 100644 library/src/androidTest/assets/mp3/play-trimmed.mp3.unklen.dump diff --git a/library/src/androidTest/assets/mp3/play-trimmed.mp3 b/library/src/androidTest/assets/mp3/play-trimmed.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..d103ac5781e5db19abdcfd4147a5df0bbc619115 GIT binary patch literal 418 zcmV;T0bTz8`;ufs5AXm0X#fBK2mk;LAOHXX08jz|4S)b33IIR>umA)ow?fnD@jkn- zzWO0Jg8!$`Gv4m)?V=vrF*bX>i6NPPKmF~`Pxpd?Cu2KbboTD=A6fLoG41vpvfDNy zJ9y9UUiRBB-M5F>f*F48Hu0UvW*9O%{yYEzKp)0%&fYPj!X&ft#)&C+CG}xN)Pi(1@obWnuV3AUq62ZOOzE_jFNxLKkP| zUK7D8W3Lc&JrQ^7ZyYd_cEJS?LgzORqp?c}{9Fe?d?{1v)LT;mfW69pnnk(m^&2@i zJR4QU^P}+4Gz|lw$t~yvfykVWWF=H>Igf?~d1F>GE>EQ*u~@7nkkI15mu@eGv5>Gk zaaA1cHi4FMNZe=|=X<_>JXe(Q7>&mg=*JEskd_uD0rH#Lf;!4Za}2qk#sfltm)2?Dlkp4_LJnRBT&1`zBv6hxtJUT@r3-$NwhN!b MLi#*G2USVL$q-+?@c;k- literal 0 HcmV?d00001 diff --git a/library/src/androidTest/assets/mp3/play-trimmed.mp3.0.dump b/library/src/androidTest/assets/mp3/play-trimmed.mp3.0.dump new file mode 100644 index 0000000000..37a04215ee --- /dev/null +++ b/library/src/androidTest/assets/mp3/play-trimmed.mp3.0.dump @@ -0,0 +1,33 @@ +seekMap: + isSeekable = true + duration = 26125 + getPosition(0) = 0 +numberOfTracks = 1 +track 0: + format: + bitrate = -1 + id = null + containerMimeType = null + sampleMimeType = audio/mpeg + maxInputSize = 4096 + width = -1 + height = -1 + frameRate = -1.0 + rotationDegrees = -1 + pixelWidthHeightRatio = -1.0 + channelCount = 2 + sampleRate = 44100 + pcmEncoding = -1 + encoderDelay = -1 + encoderPadding = -1 + subsampleOffsetUs = 9223372036854775807 + selectionFlags = 0 + language = null + drmInitData = - + initializationData: + sample count = 1 + sample 0: + time = 0 + flags = 1 + data = length 418, hash B819987 +tracksEnded = true diff --git a/library/src/androidTest/assets/mp3/play-trimmed.mp3.1.dump b/library/src/androidTest/assets/mp3/play-trimmed.mp3.1.dump new file mode 100644 index 0000000000..0b6516ccdb --- /dev/null +++ b/library/src/androidTest/assets/mp3/play-trimmed.mp3.1.dump @@ -0,0 +1,29 @@ +seekMap: + isSeekable = true + duration = 26125 + getPosition(0) = 0 +numberOfTracks = 1 +track 0: + format: + bitrate = -1 + id = null + containerMimeType = null + sampleMimeType = audio/mpeg + maxInputSize = 4096 + width = -1 + height = -1 + frameRate = -1.0 + rotationDegrees = -1 + pixelWidthHeightRatio = -1.0 + channelCount = 2 + sampleRate = 44100 + pcmEncoding = -1 + encoderDelay = -1 + encoderPadding = -1 + subsampleOffsetUs = 9223372036854775807 + selectionFlags = 0 + language = null + drmInitData = - + initializationData: + sample count = 0 +tracksEnded = true diff --git a/library/src/androidTest/assets/mp3/play-trimmed.mp3.2.dump b/library/src/androidTest/assets/mp3/play-trimmed.mp3.2.dump new file mode 100644 index 0000000000..0b6516ccdb --- /dev/null +++ b/library/src/androidTest/assets/mp3/play-trimmed.mp3.2.dump @@ -0,0 +1,29 @@ +seekMap: + isSeekable = true + duration = 26125 + getPosition(0) = 0 +numberOfTracks = 1 +track 0: + format: + bitrate = -1 + id = null + containerMimeType = null + sampleMimeType = audio/mpeg + maxInputSize = 4096 + width = -1 + height = -1 + frameRate = -1.0 + rotationDegrees = -1 + pixelWidthHeightRatio = -1.0 + channelCount = 2 + sampleRate = 44100 + pcmEncoding = -1 + encoderDelay = -1 + encoderPadding = -1 + subsampleOffsetUs = 9223372036854775807 + selectionFlags = 0 + language = null + drmInitData = - + initializationData: + sample count = 0 +tracksEnded = true diff --git a/library/src/androidTest/assets/mp3/play-trimmed.mp3.3.dump b/library/src/androidTest/assets/mp3/play-trimmed.mp3.3.dump new file mode 100644 index 0000000000..0b6516ccdb --- /dev/null +++ b/library/src/androidTest/assets/mp3/play-trimmed.mp3.3.dump @@ -0,0 +1,29 @@ +seekMap: + isSeekable = true + duration = 26125 + getPosition(0) = 0 +numberOfTracks = 1 +track 0: + format: + bitrate = -1 + id = null + containerMimeType = null + sampleMimeType = audio/mpeg + maxInputSize = 4096 + width = -1 + height = -1 + frameRate = -1.0 + rotationDegrees = -1 + pixelWidthHeightRatio = -1.0 + channelCount = 2 + sampleRate = 44100 + pcmEncoding = -1 + encoderDelay = -1 + encoderPadding = -1 + subsampleOffsetUs = 9223372036854775807 + selectionFlags = 0 + language = null + drmInitData = - + initializationData: + sample count = 0 +tracksEnded = true diff --git a/library/src/androidTest/assets/mp3/play-trimmed.mp3.unklen.dump b/library/src/androidTest/assets/mp3/play-trimmed.mp3.unklen.dump new file mode 100644 index 0000000000..b75aefd91b --- /dev/null +++ b/library/src/androidTest/assets/mp3/play-trimmed.mp3.unklen.dump @@ -0,0 +1,33 @@ +seekMap: + isSeekable = false + duration = UNSET TIME + getPosition(0) = 0 +numberOfTracks = 1 +track 0: + format: + bitrate = -1 + id = null + containerMimeType = null + sampleMimeType = audio/mpeg + maxInputSize = 4096 + width = -1 + height = -1 + frameRate = -1.0 + rotationDegrees = -1 + pixelWidthHeightRatio = -1.0 + channelCount = 2 + sampleRate = 44100 + pcmEncoding = -1 + encoderDelay = -1 + encoderPadding = -1 + subsampleOffsetUs = 9223372036854775807 + selectionFlags = 0 + language = null + drmInitData = - + initializationData: + sample count = 1 + sample 0: + time = 0 + flags = 1 + data = length 418, hash B819987 +tracksEnded = true diff --git a/library/src/androidTest/java/com/google/android/exoplayer2/extractor/mp3/Mp3ExtractorTest.java b/library/src/androidTest/java/com/google/android/exoplayer2/extractor/mp3/Mp3ExtractorTest.java index e268d9f12d..c70710f1ee 100644 --- a/library/src/androidTest/java/com/google/android/exoplayer2/extractor/mp3/Mp3ExtractorTest.java +++ b/library/src/androidTest/java/com/google/android/exoplayer2/extractor/mp3/Mp3ExtractorTest.java @@ -33,4 +33,13 @@ public final class Mp3ExtractorTest extends InstrumentationTestCase { }, "mp3/bear.mp3", getInstrumentation()); } + public void testTrimmedMp3Sample() throws Exception { + TestUtil.assertOutput(new TestUtil.ExtractorFactory() { + @Override + public Extractor create() { + return new Mp3Extractor(); + } + }, "mp3/play-trimmed.mp3", getInstrumentation()); + } + } diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/mp3/Mp3Extractor.java b/library/src/main/java/com/google/android/exoplayer2/extractor/mp3/Mp3Extractor.java index fdd037cde3..d201b3ace2 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/mp3/Mp3Extractor.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/mp3/Mp3Extractor.java @@ -131,8 +131,12 @@ public final class Mp3Extractor implements Extractor { @Override public int read(ExtractorInput input, PositionHolder seekPosition) throws IOException, InterruptedException { - if (synchronizedHeaderData == 0 && !synchronizeCatchingEndOfInput(input)) { - return RESULT_END_OF_INPUT; + if (synchronizedHeaderData == 0) { + try { + synchronize(input, false); + } catch (EOFException e) { + return RESULT_END_OF_INPUT; + } } if (seeker == null) { seeker = setupSeeker(input); @@ -147,9 +151,20 @@ public final class Mp3Extractor implements Extractor { private int readSample(ExtractorInput extractorInput) throws IOException, InterruptedException { if (sampleBytesRemaining == 0) { - if (!maybeResynchronize(extractorInput)) { + extractorInput.resetPeekPosition(); + if (!extractorInput.peekFully(scratch.data, 0, 4, true)) { return RESULT_END_OF_INPUT; } + scratch.setPosition(0); + int sampleHeaderData = scratch.readInt(); + if ((sampleHeaderData & HEADER_MASK) != (synchronizedHeaderData & HEADER_MASK) + || MpegAudioHeader.getFrameSize(sampleHeaderData) == C.LENGTH_UNSET) { + // We have lost synchronization, so attempt to resynchronize starting at the next byte. + extractorInput.skipFully(1); + synchronizedHeaderData = 0; + return RESULT_CONTINUE; + } + MpegAudioHeader.populateHeader(sampleHeaderData, synchronizedHeader); if (basisTimeUs == C.TIME_UNSET) { basisTimeUs = seeker.getTimeUs(extractorInput.getPosition()); if (forcedFirstSampleTimestampUs != C.TIME_UNSET) { @@ -175,49 +190,13 @@ public final class Mp3Extractor implements Extractor { return RESULT_CONTINUE; } - /** - * Attempts to read an MPEG audio header at the current offset, resynchronizing if necessary. - */ - private boolean maybeResynchronize(ExtractorInput extractorInput) - throws IOException, InterruptedException { - extractorInput.resetPeekPosition(); - if (!extractorInput.peekFully(scratch.data, 0, 4, true)) { - return false; - } - - scratch.setPosition(0); - int sampleHeaderData = scratch.readInt(); - if ((sampleHeaderData & HEADER_MASK) == (synchronizedHeaderData & HEADER_MASK)) { - int frameSize = MpegAudioHeader.getFrameSize(sampleHeaderData); - if (frameSize != C.LENGTH_UNSET) { - MpegAudioHeader.populateHeader(sampleHeaderData, synchronizedHeader); - return true; - } - } - - synchronizedHeaderData = 0; - extractorInput.skipFully(1); - return synchronizeCatchingEndOfInput(extractorInput); - } - - private boolean synchronizeCatchingEndOfInput(ExtractorInput input) - throws IOException, InterruptedException { - // An EOFException will be raised if any peek operation was partially satisfied. If a seek - // operation resulted in reading from within the last frame, we may try to peek past the end of - // the file in a partially-satisfied read operation, so we need to catch the exception. - try { - return synchronize(input, false); - } catch (EOFException e) { - return false; - } - } - private boolean synchronize(ExtractorInput input, boolean sniffing) throws IOException, InterruptedException { - int searched = 0; int validFrameCount = 0; int candidateSynchronizedHeaderData = 0; int peekedId3Bytes = 0; + int searchedBytes = 0; + int searchLimitBytes = sniffing ? MAX_SNIFF_BYTES : MAX_SYNC_BYTES; input.resetPeekPosition(); if (input.getPosition() == 0) { Id3Util.parseId3(input, gaplessInfoHolder); @@ -227,14 +206,9 @@ public final class Mp3Extractor implements Extractor { } } while (true) { - if (sniffing && searched == MAX_SNIFF_BYTES) { - return false; - } - if (!sniffing && searched == MAX_SYNC_BYTES) { - throw new ParserException("Searched too many bytes."); - } - if (!input.peekFully(scratch.data, 0, 4, true)) { - return false; + if (!input.peekFully(scratch.data, 0, 4, validFrameCount > 0)) { + // We reached the end of the stream but found at least one valid frame. + break; } scratch.setPosition(0); int headerData = scratch.readInt(); @@ -242,18 +216,23 @@ public final class Mp3Extractor implements Extractor { if ((candidateSynchronizedHeaderData != 0 && (headerData & HEADER_MASK) != (candidateSynchronizedHeaderData & HEADER_MASK)) || (frameSize = MpegAudioHeader.getFrameSize(headerData)) == C.LENGTH_UNSET) { - // The header is invalid or doesn't match the candidate header. Try the next byte offset. + // The header doesn't match the candidate header or is invalid. Try the next byte offset. + if (searchedBytes++ == searchLimitBytes) { + if (!sniffing) { + throw new ParserException("Searched too many bytes."); + } + return false; + } validFrameCount = 0; candidateSynchronizedHeaderData = 0; - searched++; if (sniffing) { input.resetPeekPosition(); - input.advancePeekPosition(peekedId3Bytes + searched); + input.advancePeekPosition(peekedId3Bytes + searchedBytes); } else { input.skipFully(1); } } else { - // The header is valid and matches the candidate header. + // The header matches the candidate header and/or is valid. validFrameCount++; if (validFrameCount == 1) { MpegAudioHeader.populateHeader(headerData, synchronizedHeader); @@ -266,7 +245,7 @@ public final class Mp3Extractor implements Extractor { } // Prepare to read the synchronized frame. if (sniffing) { - input.skipFully(peekedId3Bytes + searched); + input.skipFully(peekedId3Bytes + searchedBytes); } else { input.resetPeekPosition(); } diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeExtractorInput.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeExtractorInput.java index 090d8549be..5cb11fdd81 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeExtractorInput.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeExtractorInput.java @@ -84,7 +84,7 @@ public final class FakeExtractorInput implements ExtractorInput { * @param position The position to set. */ public void setPosition(int position) { - Assert.assertTrue(0 <= position && position < data.length); + Assert.assertTrue(0 <= position && position <= data.length); readPosition = position; peekPosition = position; } @@ -203,7 +203,7 @@ public final class FakeExtractorInput implements ExtractorInput { peekPosition = readPosition; throw new SimulatedIOException("Simulated IO error at position: " + position); } - if (isEof()) { + if (length > 0 && position == data.length) { if (allowEndOfInput) { return false; } @@ -217,6 +217,10 @@ public final class FakeExtractorInput implements ExtractorInput { } private int getReadLength(int requestedLength) { + if (readPosition == data.length) { + // If the requested length is non-zero, the end of the input will be read. + return requestedLength == 0 ? 0 : Integer.MAX_VALUE; + } int targetPosition = readPosition + requestedLength; if (simulatePartialReads && requestedLength > 1 && !partiallySatisfiedTargetPositions.get(targetPosition)) { @@ -226,10 +230,6 @@ public final class FakeExtractorInput implements ExtractorInput { return Math.min(requestedLength, data.length - readPosition); } - private boolean isEof() { - return readPosition == data.length; - } - /** * Builder of {@link FakeExtractorInput} instances. */ From 70351bfae2ef13f35c0aa7629e50ea26f028afd5 Mon Sep 17 00:00:00 2001 From: aquilescanta Date: Thu, 15 Sep 2016 03:56:08 -0700 Subject: [PATCH 03/36] Create one HlsSampleStreamWrapper per track group ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=133243031 --- .../exoplayer2/source/hls/HlsMediaPeriod.java | 61 +++++++++---------- .../source/hls/HlsSampleStreamWrapper.java | 9 +++ 2 files changed, 39 insertions(+), 31 deletions(-) diff --git a/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriod.java b/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriod.java index 288412ccbb..09b21c688f 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriod.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriod.java @@ -239,13 +239,7 @@ import java.util.List; eventDispatcher.loadCompleted(loadable.dataSpec, loadable.type, elapsedRealtimeMs, loadDurationMs, loadable.bytesLoaded()); playlist = loadable.getResult(); - List sampleStreamWrapperList = buildSampleStreamWrappers(); - sampleStreamWrappers = new HlsSampleStreamWrapper[sampleStreamWrapperList.size()]; - sampleStreamWrapperList.toArray(sampleStreamWrappers); - pendingPrepareCount = sampleStreamWrappers.length; - for (HlsSampleStreamWrapper sampleStreamWrapper : sampleStreamWrappers) { - sampleStreamWrapper.prepare(); - } + buildAndPrepareSampleStreamWrappers(); } @Override @@ -313,16 +307,16 @@ import java.util.List; // Internal methods. - private List buildSampleStreamWrappers() { - ArrayList sampleStreamWrappers = new ArrayList<>(); + private void buildAndPrepareSampleStreamWrappers() { String baseUri = playlist.baseUri; - if (playlist instanceof HlsMediaPlaylist) { HlsMasterPlaylist.HlsUrl[] variants = new HlsMasterPlaylist.HlsUrl[] { HlsMasterPlaylist.HlsUrl.createMediaPlaylistHlsUrl(playlist.baseUri)}; - sampleStreamWrappers.add(buildSampleStreamWrapper(C.TRACK_TYPE_DEFAULT, baseUri, variants, - null, null)); - return sampleStreamWrappers; + sampleStreamWrappers = new HlsSampleStreamWrapper[] { + buildSampleStreamWrapper(C.TRACK_TYPE_DEFAULT, baseUri, variants, null, null)}; + pendingPrepareCount = 1; + sampleStreamWrappers[0].prepare(); + return; } HlsMasterPlaylist masterPlaylist = (HlsMasterPlaylist) playlist; @@ -351,32 +345,37 @@ import java.util.List; } else { // Leave the enabled variants unchanged. They're likely either all video or all audio. } + List audioVariants = masterPlaylist.audios; + List subtitleVariants = masterPlaylist.subtitles; + sampleStreamWrappers = new HlsSampleStreamWrapper[(selectedVariants.isEmpty() ? 0 : 1) + + audioVariants.size() + subtitleVariants.size()]; + int currentWrapperIndex = 0; + pendingPrepareCount = sampleStreamWrappers.length; if (!selectedVariants.isEmpty()) { HlsMasterPlaylist.HlsUrl[] variants = new HlsMasterPlaylist.HlsUrl[selectedVariants.size()]; selectedVariants.toArray(variants); - sampleStreamWrappers.add(buildSampleStreamWrapper(C.TRACK_TYPE_DEFAULT, baseUri, variants, - masterPlaylist.muxedAudioFormat, masterPlaylist.muxedCaptionFormat)); + HlsSampleStreamWrapper sampleStreamWrapper = buildSampleStreamWrapper(C.TRACK_TYPE_DEFAULT, + baseUri, variants, masterPlaylist.muxedAudioFormat, masterPlaylist.muxedCaptionFormat); + sampleStreamWrapper.prepare(); + sampleStreamWrappers[currentWrapperIndex++] = sampleStreamWrapper; } - // Build the audio stream wrapper if applicable. - List audioVariants = masterPlaylist.audios; - if (!audioVariants.isEmpty()) { - HlsMasterPlaylist.HlsUrl[] variants = new HlsMasterPlaylist.HlsUrl[audioVariants.size()]; - audioVariants.toArray(variants); - sampleStreamWrappers.add(buildSampleStreamWrapper(C.TRACK_TYPE_AUDIO, baseUri, variants, null, - null)); + // Build audio stream wrappers. + for (int i = 0; i < audioVariants.size(); i++) { + HlsSampleStreamWrapper sampleStreamWrapper = buildSampleStreamWrapper(C.TRACK_TYPE_AUDIO, + baseUri, new HlsMasterPlaylist.HlsUrl[] {audioVariants.get(i)}, null, null); + sampleStreamWrapper.prepare(); + sampleStreamWrappers[currentWrapperIndex++] = sampleStreamWrapper; } - // Build the text stream wrapper if applicable. - List subtitleVariants = masterPlaylist.subtitles; - if (!subtitleVariants.isEmpty()) { - HlsMasterPlaylist.HlsUrl[] variants = new HlsMasterPlaylist.HlsUrl[subtitleVariants.size()]; - subtitleVariants.toArray(variants); - sampleStreamWrappers.add(buildSampleStreamWrapper(C.TRACK_TYPE_TEXT, baseUri, variants, null, - null)); + // Build subtitle stream wrappers. + for (int i = 0; i < subtitleVariants.size(); i++) { + HlsMasterPlaylist.HlsUrl url = subtitleVariants.get(i); + HlsSampleStreamWrapper sampleStreamWrapper = buildSampleStreamWrapper(C.TRACK_TYPE_TEXT, + baseUri, new HlsMasterPlaylist.HlsUrl[] {url}, null, null); + sampleStreamWrapper.prepareSingleTrack(url.format); + sampleStreamWrappers[currentWrapperIndex++] = sampleStreamWrapper; } - - return sampleStreamWrappers; } private HlsSampleStreamWrapper buildSampleStreamWrapper(int trackType, String baseUri, diff --git a/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java b/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java index 83de29c039..b67d6538dd 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java @@ -137,6 +137,15 @@ import java.util.LinkedList; continueLoading(lastSeekPositionUs); } + /** + * Prepares a sample stream wrapper for which the master playlist provides enough information to + * prepare. + */ + public void prepareSingleTrack(Format format) { + track(0).format(format); + endTracks(); + } + public void maybeThrowPrepareError() throws IOException { maybeThrowError(); } From c17190319b6faf0583fb65b973e8924420498c11 Mon Sep 17 00:00:00 2001 From: olly Date: Thu, 15 Sep 2016 09:01:30 -0700 Subject: [PATCH 04/36] Rename assets to avoid naming conflicts Issue #1823 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=133267642 --- .../exoplayer2/ui/PlaybackControlView.java | 10 ++++--- .../exoplayer2/ui/SimpleExoPlayerView.java | 5 ++-- ...ia_ff.png => exo_controls_fastforward.png} | Bin ...c_media_next.png => exo_controls_next.png} | Bin ...media_pause.png => exo_controls_pause.png} | Bin ...c_media_play.png => exo_controls_play.png} | Bin ...previous.png => exo_controls_previous.png} | Bin ..._media_rew.png => exo_controls_rewind.png} | Bin ...ia_ff.png => exo_controls_fastforward.png} | Bin ...c_media_next.png => exo_controls_next.png} | Bin ...media_pause.png => exo_controls_pause.png} | Bin ...c_media_play.png => exo_controls_play.png} | Bin ...previous.png => exo_controls_previous.png} | Bin ..._media_rew.png => exo_controls_rewind.png} | Bin ...ia_ff.png => exo_controls_fastforward.png} | Bin ...c_media_next.png => exo_controls_next.png} | Bin ...media_pause.png => exo_controls_pause.png} | Bin ...c_media_play.png => exo_controls_play.png} | Bin ...previous.png => exo_controls_previous.png} | Bin ..._media_rew.png => exo_controls_rewind.png} | Bin ...ia_ff.png => exo_controls_fastforward.png} | Bin ...c_media_next.png => exo_controls_next.png} | Bin ...media_pause.png => exo_controls_pause.png} | Bin ...c_media_play.png => exo_controls_play.png} | Bin ...previous.png => exo_controls_previous.png} | Bin ..._media_rew.png => exo_controls_rewind.png} | Bin ...ia_ff.png => exo_controls_fastforward.png} | Bin ...c_media_next.png => exo_controls_next.png} | Bin ...media_pause.png => exo_controls_pause.png} | Bin ...c_media_play.png => exo_controls_play.png} | Bin ...previous.png => exo_controls_previous.png} | Bin ..._media_rew.png => exo_controls_rewind.png} | Bin ...view.xml => exo_playback_control_view.xml} | 18 ++++++------ ...eo_view.xml => exo_simple_player_view.xml} | 0 library/src/main/res/values-af/strings.xml | 14 ++++----- library/src/main/res/values-am/strings.xml | 14 ++++----- library/src/main/res/values-ar/strings.xml | 14 ++++----- .../src/main/res/values-az-rAZ/strings.xml | 14 ++++----- .../src/main/res/values-b+sr+Latn/strings.xml | 14 ++++----- .../src/main/res/values-be-rBY/strings.xml | 14 ++++----- library/src/main/res/values-bg/strings.xml | 14 ++++----- .../src/main/res/values-bn-rBD/strings.xml | 14 ++++----- .../src/main/res/values-bs-rBA/strings.xml | 14 ++++----- library/src/main/res/values-ca/strings.xml | 14 ++++----- library/src/main/res/values-cs/strings.xml | 14 ++++----- library/src/main/res/values-da/strings.xml | 14 ++++----- library/src/main/res/values-de/strings.xml | 14 ++++----- library/src/main/res/values-el/strings.xml | 14 ++++----- .../src/main/res/values-en-rAU/strings.xml | 14 ++++----- .../src/main/res/values-en-rGB/strings.xml | 14 ++++----- .../src/main/res/values-en-rIN/strings.xml | 14 ++++----- .../src/main/res/values-es-rUS/strings.xml | 14 ++++----- library/src/main/res/values-es/strings.xml | 14 ++++----- .../src/main/res/values-et-rEE/strings.xml | 14 ++++----- .../src/main/res/values-eu-rES/strings.xml | 14 ++++----- library/src/main/res/values-fa/strings.xml | 14 ++++----- library/src/main/res/values-fi/strings.xml | 14 ++++----- .../src/main/res/values-fr-rCA/strings.xml | 14 ++++----- library/src/main/res/values-fr/strings.xml | 14 ++++----- .../src/main/res/values-gl-rES/strings.xml | 14 ++++----- .../src/main/res/values-gu-rIN/strings.xml | 14 ++++----- library/src/main/res/values-hi/strings.xml | 14 ++++----- library/src/main/res/values-hr/strings.xml | 14 ++++----- library/src/main/res/values-hu/strings.xml | 14 ++++----- .../src/main/res/values-hy-rAM/strings.xml | 14 ++++----- library/src/main/res/values-in/strings.xml | 14 ++++----- .../src/main/res/values-is-rIS/strings.xml | 14 ++++----- library/src/main/res/values-it/strings.xml | 14 ++++----- library/src/main/res/values-iw/strings.xml | 14 ++++----- library/src/main/res/values-ja/strings.xml | 14 ++++----- .../src/main/res/values-ka-rGE/strings.xml | 14 ++++----- .../src/main/res/values-kk-rKZ/strings.xml | 14 ++++----- .../src/main/res/values-km-rKH/strings.xml | 14 ++++----- .../src/main/res/values-kn-rIN/strings.xml | 14 ++++----- library/src/main/res/values-ko/strings.xml | 14 ++++----- .../src/main/res/values-ky-rKG/strings.xml | 14 ++++----- .../src/main/res/values-lo-rLA/strings.xml | 14 ++++----- library/src/main/res/values-lt/strings.xml | 14 ++++----- library/src/main/res/values-lv/strings.xml | 14 ++++----- .../src/main/res/values-mk-rMK/strings.xml | 14 ++++----- .../src/main/res/values-ml-rIN/strings.xml | 14 ++++----- .../src/main/res/values-mn-rMN/strings.xml | 14 ++++----- .../src/main/res/values-mr-rIN/strings.xml | 14 ++++----- .../src/main/res/values-ms-rMY/strings.xml | 14 ++++----- .../src/main/res/values-my-rMM/strings.xml | 14 ++++----- library/src/main/res/values-nb/strings.xml | 14 ++++----- .../src/main/res/values-ne-rNP/strings.xml | 14 ++++----- library/src/main/res/values-nl/strings.xml | 14 ++++----- .../src/main/res/values-pa-rIN/strings.xml | 14 ++++----- library/src/main/res/values-pl/strings.xml | 14 ++++----- .../src/main/res/values-pt-rBR/strings.xml | 14 ++++----- .../src/main/res/values-pt-rPT/strings.xml | 14 ++++----- library/src/main/res/values-pt/strings.xml | 14 ++++----- library/src/main/res/values-ro/strings.xml | 14 ++++----- library/src/main/res/values-ru/strings.xml | 14 ++++----- .../src/main/res/values-si-rLK/strings.xml | 14 ++++----- library/src/main/res/values-sk/strings.xml | 14 ++++----- library/src/main/res/values-sl/strings.xml | 14 ++++----- .../src/main/res/values-sq-rAL/strings.xml | 14 ++++----- library/src/main/res/values-sr/strings.xml | 14 ++++----- library/src/main/res/values-sv/strings.xml | 14 ++++----- library/src/main/res/values-sw/strings.xml | 14 ++++----- .../src/main/res/values-ta-rIN/strings.xml | 14 ++++----- .../src/main/res/values-te-rIN/strings.xml | 14 ++++----- library/src/main/res/values-th/strings.xml | 14 ++++----- library/src/main/res/values-tl/strings.xml | 14 ++++----- library/src/main/res/values-tr/strings.xml | 14 ++++----- library/src/main/res/values-uk/strings.xml | 14 ++++----- .../src/main/res/values-ur-rPK/strings.xml | 14 ++++----- .../src/main/res/values-uz-rUZ/strings.xml | 14 ++++----- library/src/main/res/values-vi/strings.xml | 14 ++++----- .../src/main/res/values-zh-rCN/strings.xml | 14 ++++----- .../src/main/res/values-zh-rHK/strings.xml | 14 ++++----- .../src/main/res/values-zh-rTW/strings.xml | 14 ++++----- library/src/main/res/values-zu/strings.xml | 14 ++++----- library/src/main/res/values/attrs.xml | 5 ++-- library/src/main/res/values/strings.xml | 15 +++++----- library/src/main/res/values/styles.xml | 27 +++++++++--------- 118 files changed, 606 insertions(+), 608 deletions(-) rename library/src/main/res/drawable-hdpi/{ic_media_ff.png => exo_controls_fastforward.png} (100%) rename library/src/main/res/drawable-hdpi/{ic_media_next.png => exo_controls_next.png} (100%) rename library/src/main/res/drawable-hdpi/{ic_media_pause.png => exo_controls_pause.png} (100%) rename library/src/main/res/drawable-hdpi/{ic_media_play.png => exo_controls_play.png} (100%) rename library/src/main/res/drawable-hdpi/{ic_media_previous.png => exo_controls_previous.png} (100%) rename library/src/main/res/drawable-hdpi/{ic_media_rew.png => exo_controls_rewind.png} (100%) rename library/src/main/res/drawable-ldpi/{ic_media_ff.png => exo_controls_fastforward.png} (100%) rename library/src/main/res/drawable-ldpi/{ic_media_next.png => exo_controls_next.png} (100%) rename library/src/main/res/drawable-ldpi/{ic_media_pause.png => exo_controls_pause.png} (100%) rename library/src/main/res/drawable-ldpi/{ic_media_play.png => exo_controls_play.png} (100%) rename library/src/main/res/drawable-ldpi/{ic_media_previous.png => exo_controls_previous.png} (100%) rename library/src/main/res/drawable-ldpi/{ic_media_rew.png => exo_controls_rewind.png} (100%) rename library/src/main/res/drawable-mdpi/{ic_media_ff.png => exo_controls_fastforward.png} (100%) rename library/src/main/res/drawable-mdpi/{ic_media_next.png => exo_controls_next.png} (100%) rename library/src/main/res/drawable-mdpi/{ic_media_pause.png => exo_controls_pause.png} (100%) rename library/src/main/res/drawable-mdpi/{ic_media_play.png => exo_controls_play.png} (100%) rename library/src/main/res/drawable-mdpi/{ic_media_previous.png => exo_controls_previous.png} (100%) rename library/src/main/res/drawable-mdpi/{ic_media_rew.png => exo_controls_rewind.png} (100%) rename library/src/main/res/drawable-xhdpi/{ic_media_ff.png => exo_controls_fastforward.png} (100%) rename library/src/main/res/drawable-xhdpi/{ic_media_next.png => exo_controls_next.png} (100%) rename library/src/main/res/drawable-xhdpi/{ic_media_pause.png => exo_controls_pause.png} (100%) rename library/src/main/res/drawable-xhdpi/{ic_media_play.png => exo_controls_play.png} (100%) rename library/src/main/res/drawable-xhdpi/{ic_media_previous.png => exo_controls_previous.png} (100%) rename library/src/main/res/drawable-xhdpi/{ic_media_rew.png => exo_controls_rewind.png} (100%) rename library/src/main/res/drawable-xxhdpi/{ic_media_ff.png => exo_controls_fastforward.png} (100%) rename library/src/main/res/drawable-xxhdpi/{ic_media_next.png => exo_controls_next.png} (100%) rename library/src/main/res/drawable-xxhdpi/{ic_media_pause.png => exo_controls_pause.png} (100%) rename library/src/main/res/drawable-xxhdpi/{ic_media_play.png => exo_controls_play.png} (100%) rename library/src/main/res/drawable-xxhdpi/{ic_media_previous.png => exo_controls_previous.png} (100%) rename library/src/main/res/drawable-xxhdpi/{ic_media_rew.png => exo_controls_rewind.png} (100%) rename library/src/main/res/layout/{playback_control_view.xml => exo_playback_control_view.xml} (82%) rename library/src/main/res/layout/{exoplayer_video_view.xml => exo_simple_player_view.xml} (100%) diff --git a/library/src/main/java/com/google/android/exoplayer2/ui/PlaybackControlView.java b/library/src/main/java/com/google/android/exoplayer2/ui/PlaybackControlView.java index 25a8e1e716..7f351afe8b 100644 --- a/library/src/main/java/com/google/android/exoplayer2/ui/PlaybackControlView.java +++ b/library/src/main/java/com/google/android/exoplayer2/ui/PlaybackControlView.java @@ -108,7 +108,7 @@ public class PlaybackControlView extends FrameLayout { formatter = new Formatter(formatBuilder, Locale.getDefault()); componentListener = new ComponentListener(); - LayoutInflater.from(context).inflate(R.layout.playback_control_view, this); + LayoutInflater.from(context).inflate(R.layout.exo_playback_control_view, this); time = (TextView) findViewById(R.id.time); timeCurrent = (TextView) findViewById(R.id.time_current); progressBar = (SeekBar) findViewById(R.id.mediacontroller_progress); @@ -240,9 +240,11 @@ public class PlaybackControlView extends FrameLayout { return; } boolean playing = player != null && player.getPlayWhenReady(); - playButton.setImageResource(playing ? R.drawable.ic_media_pause : R.drawable.ic_media_play); - playButton.setContentDescription( - getResources().getString(playing ? R.string.pause_description : R.string.play_description)); + String contentDescription = getResources().getString( + playing ? R.string.exo_controls_pause_description : R.string.exo_controls_play_description); + playButton.setContentDescription(contentDescription); + playButton.setImageResource( + playing ? R.drawable.exo_controls_pause : R.drawable.exo_controls_play); } private void updateNavigation() { diff --git a/library/src/main/java/com/google/android/exoplayer2/ui/SimpleExoPlayerView.java b/library/src/main/java/com/google/android/exoplayer2/ui/SimpleExoPlayerView.java index 390ab2ee7a..aff9e2c968 100644 --- a/library/src/main/java/com/google/android/exoplayer2/ui/SimpleExoPlayerView.java +++ b/library/src/main/java/com/google/android/exoplayer2/ui/SimpleExoPlayerView.java @@ -71,8 +71,7 @@ public final class SimpleExoPlayerView extends FrameLayout { TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.SimpleExoPlayerView, 0, 0); try { - useController = a.getBoolean(R.styleable.SimpleExoPlayerView_use_controller, - useController); + useController = a.getBoolean(R.styleable.SimpleExoPlayerView_use_controller, useController); useTextureView = a.getBoolean(R.styleable.SimpleExoPlayerView_use_texture_view, useTextureView); } finally { @@ -80,7 +79,7 @@ public final class SimpleExoPlayerView extends FrameLayout { } } - LayoutInflater.from(context).inflate(R.layout.exoplayer_video_view, this); + LayoutInflater.from(context).inflate(R.layout.exo_simple_player_view, this); componentListener = new ComponentListener(); layout = (AspectRatioFrameLayout) findViewById(R.id.video_frame); controller = (PlaybackControlView) findViewById(R.id.control); diff --git a/library/src/main/res/drawable-hdpi/ic_media_ff.png b/library/src/main/res/drawable-hdpi/exo_controls_fastforward.png similarity index 100% rename from library/src/main/res/drawable-hdpi/ic_media_ff.png rename to library/src/main/res/drawable-hdpi/exo_controls_fastforward.png diff --git a/library/src/main/res/drawable-hdpi/ic_media_next.png b/library/src/main/res/drawable-hdpi/exo_controls_next.png similarity index 100% rename from library/src/main/res/drawable-hdpi/ic_media_next.png rename to library/src/main/res/drawable-hdpi/exo_controls_next.png diff --git a/library/src/main/res/drawable-hdpi/ic_media_pause.png b/library/src/main/res/drawable-hdpi/exo_controls_pause.png similarity index 100% rename from library/src/main/res/drawable-hdpi/ic_media_pause.png rename to library/src/main/res/drawable-hdpi/exo_controls_pause.png diff --git a/library/src/main/res/drawable-hdpi/ic_media_play.png b/library/src/main/res/drawable-hdpi/exo_controls_play.png similarity index 100% rename from library/src/main/res/drawable-hdpi/ic_media_play.png rename to library/src/main/res/drawable-hdpi/exo_controls_play.png diff --git a/library/src/main/res/drawable-hdpi/ic_media_previous.png b/library/src/main/res/drawable-hdpi/exo_controls_previous.png similarity index 100% rename from library/src/main/res/drawable-hdpi/ic_media_previous.png rename to library/src/main/res/drawable-hdpi/exo_controls_previous.png diff --git a/library/src/main/res/drawable-hdpi/ic_media_rew.png b/library/src/main/res/drawable-hdpi/exo_controls_rewind.png similarity index 100% rename from library/src/main/res/drawable-hdpi/ic_media_rew.png rename to library/src/main/res/drawable-hdpi/exo_controls_rewind.png diff --git a/library/src/main/res/drawable-ldpi/ic_media_ff.png b/library/src/main/res/drawable-ldpi/exo_controls_fastforward.png similarity index 100% rename from library/src/main/res/drawable-ldpi/ic_media_ff.png rename to library/src/main/res/drawable-ldpi/exo_controls_fastforward.png diff --git a/library/src/main/res/drawable-ldpi/ic_media_next.png b/library/src/main/res/drawable-ldpi/exo_controls_next.png similarity index 100% rename from library/src/main/res/drawable-ldpi/ic_media_next.png rename to library/src/main/res/drawable-ldpi/exo_controls_next.png diff --git a/library/src/main/res/drawable-ldpi/ic_media_pause.png b/library/src/main/res/drawable-ldpi/exo_controls_pause.png similarity index 100% rename from library/src/main/res/drawable-ldpi/ic_media_pause.png rename to library/src/main/res/drawable-ldpi/exo_controls_pause.png diff --git a/library/src/main/res/drawable-ldpi/ic_media_play.png b/library/src/main/res/drawable-ldpi/exo_controls_play.png similarity index 100% rename from library/src/main/res/drawable-ldpi/ic_media_play.png rename to library/src/main/res/drawable-ldpi/exo_controls_play.png diff --git a/library/src/main/res/drawable-ldpi/ic_media_previous.png b/library/src/main/res/drawable-ldpi/exo_controls_previous.png similarity index 100% rename from library/src/main/res/drawable-ldpi/ic_media_previous.png rename to library/src/main/res/drawable-ldpi/exo_controls_previous.png diff --git a/library/src/main/res/drawable-ldpi/ic_media_rew.png b/library/src/main/res/drawable-ldpi/exo_controls_rewind.png similarity index 100% rename from library/src/main/res/drawable-ldpi/ic_media_rew.png rename to library/src/main/res/drawable-ldpi/exo_controls_rewind.png diff --git a/library/src/main/res/drawable-mdpi/ic_media_ff.png b/library/src/main/res/drawable-mdpi/exo_controls_fastforward.png similarity index 100% rename from library/src/main/res/drawable-mdpi/ic_media_ff.png rename to library/src/main/res/drawable-mdpi/exo_controls_fastforward.png diff --git a/library/src/main/res/drawable-mdpi/ic_media_next.png b/library/src/main/res/drawable-mdpi/exo_controls_next.png similarity index 100% rename from library/src/main/res/drawable-mdpi/ic_media_next.png rename to library/src/main/res/drawable-mdpi/exo_controls_next.png diff --git a/library/src/main/res/drawable-mdpi/ic_media_pause.png b/library/src/main/res/drawable-mdpi/exo_controls_pause.png similarity index 100% rename from library/src/main/res/drawable-mdpi/ic_media_pause.png rename to library/src/main/res/drawable-mdpi/exo_controls_pause.png diff --git a/library/src/main/res/drawable-mdpi/ic_media_play.png b/library/src/main/res/drawable-mdpi/exo_controls_play.png similarity index 100% rename from library/src/main/res/drawable-mdpi/ic_media_play.png rename to library/src/main/res/drawable-mdpi/exo_controls_play.png diff --git a/library/src/main/res/drawable-mdpi/ic_media_previous.png b/library/src/main/res/drawable-mdpi/exo_controls_previous.png similarity index 100% rename from library/src/main/res/drawable-mdpi/ic_media_previous.png rename to library/src/main/res/drawable-mdpi/exo_controls_previous.png diff --git a/library/src/main/res/drawable-mdpi/ic_media_rew.png b/library/src/main/res/drawable-mdpi/exo_controls_rewind.png similarity index 100% rename from library/src/main/res/drawable-mdpi/ic_media_rew.png rename to library/src/main/res/drawable-mdpi/exo_controls_rewind.png diff --git a/library/src/main/res/drawable-xhdpi/ic_media_ff.png b/library/src/main/res/drawable-xhdpi/exo_controls_fastforward.png similarity index 100% rename from library/src/main/res/drawable-xhdpi/ic_media_ff.png rename to library/src/main/res/drawable-xhdpi/exo_controls_fastforward.png diff --git a/library/src/main/res/drawable-xhdpi/ic_media_next.png b/library/src/main/res/drawable-xhdpi/exo_controls_next.png similarity index 100% rename from library/src/main/res/drawable-xhdpi/ic_media_next.png rename to library/src/main/res/drawable-xhdpi/exo_controls_next.png diff --git a/library/src/main/res/drawable-xhdpi/ic_media_pause.png b/library/src/main/res/drawable-xhdpi/exo_controls_pause.png similarity index 100% rename from library/src/main/res/drawable-xhdpi/ic_media_pause.png rename to library/src/main/res/drawable-xhdpi/exo_controls_pause.png diff --git a/library/src/main/res/drawable-xhdpi/ic_media_play.png b/library/src/main/res/drawable-xhdpi/exo_controls_play.png similarity index 100% rename from library/src/main/res/drawable-xhdpi/ic_media_play.png rename to library/src/main/res/drawable-xhdpi/exo_controls_play.png diff --git a/library/src/main/res/drawable-xhdpi/ic_media_previous.png b/library/src/main/res/drawable-xhdpi/exo_controls_previous.png similarity index 100% rename from library/src/main/res/drawable-xhdpi/ic_media_previous.png rename to library/src/main/res/drawable-xhdpi/exo_controls_previous.png diff --git a/library/src/main/res/drawable-xhdpi/ic_media_rew.png b/library/src/main/res/drawable-xhdpi/exo_controls_rewind.png similarity index 100% rename from library/src/main/res/drawable-xhdpi/ic_media_rew.png rename to library/src/main/res/drawable-xhdpi/exo_controls_rewind.png diff --git a/library/src/main/res/drawable-xxhdpi/ic_media_ff.png b/library/src/main/res/drawable-xxhdpi/exo_controls_fastforward.png similarity index 100% rename from library/src/main/res/drawable-xxhdpi/ic_media_ff.png rename to library/src/main/res/drawable-xxhdpi/exo_controls_fastforward.png diff --git a/library/src/main/res/drawable-xxhdpi/ic_media_next.png b/library/src/main/res/drawable-xxhdpi/exo_controls_next.png similarity index 100% rename from library/src/main/res/drawable-xxhdpi/ic_media_next.png rename to library/src/main/res/drawable-xxhdpi/exo_controls_next.png diff --git a/library/src/main/res/drawable-xxhdpi/ic_media_pause.png b/library/src/main/res/drawable-xxhdpi/exo_controls_pause.png similarity index 100% rename from library/src/main/res/drawable-xxhdpi/ic_media_pause.png rename to library/src/main/res/drawable-xxhdpi/exo_controls_pause.png diff --git a/library/src/main/res/drawable-xxhdpi/ic_media_play.png b/library/src/main/res/drawable-xxhdpi/exo_controls_play.png similarity index 100% rename from library/src/main/res/drawable-xxhdpi/ic_media_play.png rename to library/src/main/res/drawable-xxhdpi/exo_controls_play.png diff --git a/library/src/main/res/drawable-xxhdpi/ic_media_previous.png b/library/src/main/res/drawable-xxhdpi/exo_controls_previous.png similarity index 100% rename from library/src/main/res/drawable-xxhdpi/ic_media_previous.png rename to library/src/main/res/drawable-xxhdpi/exo_controls_previous.png diff --git a/library/src/main/res/drawable-xxhdpi/ic_media_rew.png b/library/src/main/res/drawable-xxhdpi/exo_controls_rewind.png similarity index 100% rename from library/src/main/res/drawable-xxhdpi/ic_media_rew.png rename to library/src/main/res/drawable-xxhdpi/exo_controls_rewind.png diff --git a/library/src/main/res/layout/playback_control_view.xml b/library/src/main/res/layout/exo_playback_control_view.xml similarity index 82% rename from library/src/main/res/layout/playback_control_view.xml rename to library/src/main/res/layout/exo_playback_control_view.xml index 21194ef54f..a0be4a8149 100644 --- a/library/src/main/res/layout/playback_control_view.xml +++ b/library/src/main/res/layout/exo_playback_control_view.xml @@ -29,23 +29,23 @@ android:orientation="horizontal"> + android:contentDescription="@string/exo_controls_previous_description" + style="@style/ExoMediaButton.Previous"/> + android:contentDescription="@string/exo_controls_rewind_description" + style="@style/ExoMediaButton.Rewind"/> + style="@style/ExoMediaButton"/> + android:contentDescription="@string/exo_controls_fastforward_description" + style="@style/ExoMediaButton.FastForward"/> + android:contentDescription="@string/exo_controls_previous_description" + style="@style/ExoMediaButton.Next"/> diff --git a/library/src/main/res/layout/exoplayer_video_view.xml b/library/src/main/res/layout/exo_simple_player_view.xml similarity index 100% rename from library/src/main/res/layout/exoplayer_video_view.xml rename to library/src/main/res/layout/exo_simple_player_view.xml diff --git a/library/src/main/res/values-af/strings.xml b/library/src/main/res/values-af/strings.xml index 2ce5c415de..9f1bce53d9 100644 --- a/library/src/main/res/values-af/strings.xml +++ b/library/src/main/res/values-af/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Vorige snit" - "Volgende snit" - "Wag" - "Speel" - "Stop" - "Spoel terug" - "Vinnig vorentoe" + "Vorige snit" + "Volgende snit" + "Wag" + "Speel" + "Stop" + "Spoel terug" + "Vinnig vorentoe" diff --git a/library/src/main/res/values-am/strings.xml b/library/src/main/res/values-am/strings.xml index 39591a86f3..f06c2a664e 100644 --- a/library/src/main/res/values-am/strings.xml +++ b/library/src/main/res/values-am/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "ቀዳሚ ትራክ" - "ቀጣይ ትራክ" - "ለአፍታ አቁም" - "አጫውት" - "አቁም" - "ወደኋላ አጠንጥን" - "በፍጥነት አሳልፍ" + "ቀዳሚ ትራክ" + "ቀጣይ ትራክ" + "ለአፍታ አቁም" + "አጫውት" + "አቁም" + "ወደኋላ አጠንጥን" + "በፍጥነት አሳልፍ" diff --git a/library/src/main/res/values-ar/strings.xml b/library/src/main/res/values-ar/strings.xml index 96b7772ba1..a40c961bf7 100644 --- a/library/src/main/res/values-ar/strings.xml +++ b/library/src/main/res/values-ar/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "المقطع الصوتي السابق" - "المقطع الصوتي التالي" - "إيقاف مؤقت" - "تشغيل" - "إيقاف" - "إرجاع" - "تقديم سريع" + "المقطع الصوتي السابق" + "المقطع الصوتي التالي" + "إيقاف مؤقت" + "تشغيل" + "إيقاف" + "إرجاع" + "تقديم سريع" diff --git a/library/src/main/res/values-az-rAZ/strings.xml b/library/src/main/res/values-az-rAZ/strings.xml index aa85203b4d..7b3b9366b5 100644 --- a/library/src/main/res/values-az-rAZ/strings.xml +++ b/library/src/main/res/values-az-rAZ/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Öncəki trek" - "Növbəti trek" - "Pauza" - "Oyun" - "Dayandır" - "Geri sarıma" - "Sürətlə irəli" + "Öncəki trek" + "Növbəti trek" + "Pauza" + "Oyun" + "Dayandır" + "Geri sarıma" + "Sürətlə irəli" diff --git a/library/src/main/res/values-b+sr+Latn/strings.xml b/library/src/main/res/values-b+sr+Latn/strings.xml index 5798cfb96d..b5fdd74402 100644 --- a/library/src/main/res/values-b+sr+Latn/strings.xml +++ b/library/src/main/res/values-b+sr+Latn/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Prethodna pesma" - "Sledeća pesma" - "Pauza" - "Pusti" - "Zaustavi" - "Premotaj unazad" - "Premotaj unapred" + "Prethodna pesma" + "Sledeća pesma" + "Pauza" + "Pusti" + "Zaustavi" + "Premotaj unazad" + "Premotaj unapred" diff --git a/library/src/main/res/values-be-rBY/strings.xml b/library/src/main/res/values-be-rBY/strings.xml index 7c77eef2f2..890c23ebd5 100644 --- a/library/src/main/res/values-be-rBY/strings.xml +++ b/library/src/main/res/values-be-rBY/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Папярэдні трэк" - "Наступны трэк" - "Прыпыніць" - "Прайграць" - "Спыніць" - "Перамотка назад" - "Перамотка ўперад" + "Папярэдні трэк" + "Наступны трэк" + "Прыпыніць" + "Прайграць" + "Спыніць" + "Перамотка назад" + "Перамотка ўперад" diff --git a/library/src/main/res/values-bg/strings.xml b/library/src/main/res/values-bg/strings.xml index 2f771368c4..30b905fb8e 100644 --- a/library/src/main/res/values-bg/strings.xml +++ b/library/src/main/res/values-bg/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Предишен запис" - "Следващ запис" - "Пауза" - "Пускане" - "Спиране" - "Превъртане назад" - "Превъртане напред" + "Предишен запис" + "Следващ запис" + "Пауза" + "Пускане" + "Спиране" + "Превъртане назад" + "Превъртане напред" diff --git a/library/src/main/res/values-bn-rBD/strings.xml b/library/src/main/res/values-bn-rBD/strings.xml index 7140ab7f8d..ca5d9461d3 100644 --- a/library/src/main/res/values-bn-rBD/strings.xml +++ b/library/src/main/res/values-bn-rBD/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "পূর্ববর্তী ট্র্যাক" - "পরবর্তী ট্র্যাক" - "বিরাম দিন" - "প্লে করুন" - "থামান" - "গুটিয়ে নিন" - "দ্রুত সামনে এগোন" + "পূর্ববর্তী ট্র্যাক" + "পরবর্তী ট্র্যাক" + "বিরাম দিন" + "প্লে করুন" + "থামান" + "গুটিয়ে নিন" + "দ্রুত সামনে এগোন" diff --git a/library/src/main/res/values-bs-rBA/strings.xml b/library/src/main/res/values-bs-rBA/strings.xml index 0f578946e6..9cb0ca4d76 100644 --- a/library/src/main/res/values-bs-rBA/strings.xml +++ b/library/src/main/res/values-bs-rBA/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Prethodna numera" - "Sljedeća numera" - "Pauziraj" - "Reproduciraj" - "Zaustavi" - "Premotaj" - "Ubrzaj" + "Prethodna numera" + "Sljedeća numera" + "Pauziraj" + "Reproduciraj" + "Zaustavi" + "Premotaj" + "Ubrzaj" diff --git a/library/src/main/res/values-ca/strings.xml b/library/src/main/res/values-ca/strings.xml index 03ea1f0790..0816c76b12 100644 --- a/library/src/main/res/values-ca/strings.xml +++ b/library/src/main/res/values-ca/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Ruta anterior" - "Ruta següent" - "Posa en pausa" - "Reprodueix" - "Atura" - "Rebobina" - "Avança ràpidament" + "Ruta anterior" + "Ruta següent" + "Posa en pausa" + "Reprodueix" + "Atura" + "Rebobina" + "Avança ràpidament" diff --git a/library/src/main/res/values-cs/strings.xml b/library/src/main/res/values-cs/strings.xml index eb28c8d756..22cff4041e 100644 --- a/library/src/main/res/values-cs/strings.xml +++ b/library/src/main/res/values-cs/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Předchozí skladba" - "Další skladba" - "Pozastavit" - "Přehrát" - "Zastavit" - "Přetočit zpět" - "Přetočit vpřed" + "Předchozí skladba" + "Další skladba" + "Pozastavit" + "Přehrát" + "Zastavit" + "Přetočit zpět" + "Přetočit vpřed" diff --git a/library/src/main/res/values-da/strings.xml b/library/src/main/res/values-da/strings.xml index 04d64f9632..a6710bea50 100644 --- a/library/src/main/res/values-da/strings.xml +++ b/library/src/main/res/values-da/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Forrige nummer" - "Næste nummer" - "Pause" - "Afspil" - "Stop" - "Spol tilbage" - "Spol frem" + "Forrige nummer" + "Næste nummer" + "Pause" + "Afspil" + "Stop" + "Spol tilbage" + "Spol frem" diff --git a/library/src/main/res/values-de/strings.xml b/library/src/main/res/values-de/strings.xml index 63ebd19301..cdfd2d4baf 100644 --- a/library/src/main/res/values-de/strings.xml +++ b/library/src/main/res/values-de/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Vorheriger Titel" - "Nächster Titel" - "Pausieren" - "Wiedergabe" - "Beenden" - "Zurückspulen" - "Vorspulen" + "Vorheriger Titel" + "Nächster Titel" + "Pausieren" + "Wiedergabe" + "Beenden" + "Zurückspulen" + "Vorspulen" diff --git a/library/src/main/res/values-el/strings.xml b/library/src/main/res/values-el/strings.xml index f1744519c4..1e11df3b14 100644 --- a/library/src/main/res/values-el/strings.xml +++ b/library/src/main/res/values-el/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Προηγούμενο κομμάτι" - "Επόμενο κομμάτι" - "Παύση" - "Αναπαραγωγή" - "Διακοπή" - "Επαναφορά" - "Γρήγορη προώθηση" + "Προηγούμενο κομμάτι" + "Επόμενο κομμάτι" + "Παύση" + "Αναπαραγωγή" + "Διακοπή" + "Επαναφορά" + "Γρήγορη προώθηση" diff --git a/library/src/main/res/values-en-rAU/strings.xml b/library/src/main/res/values-en-rAU/strings.xml index a89377fc1d..5077cf2b94 100644 --- a/library/src/main/res/values-en-rAU/strings.xml +++ b/library/src/main/res/values-en-rAU/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Previous track" - "Next track" - "Pause" - "Play" - "Stop" - "Rewind" - "Fast-forward" + "Previous track" + "Next track" + "Pause" + "Play" + "Stop" + "Rewind" + "Fast-forward" diff --git a/library/src/main/res/values-en-rGB/strings.xml b/library/src/main/res/values-en-rGB/strings.xml index a89377fc1d..5077cf2b94 100644 --- a/library/src/main/res/values-en-rGB/strings.xml +++ b/library/src/main/res/values-en-rGB/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Previous track" - "Next track" - "Pause" - "Play" - "Stop" - "Rewind" - "Fast-forward" + "Previous track" + "Next track" + "Pause" + "Play" + "Stop" + "Rewind" + "Fast-forward" diff --git a/library/src/main/res/values-en-rIN/strings.xml b/library/src/main/res/values-en-rIN/strings.xml index a89377fc1d..5077cf2b94 100644 --- a/library/src/main/res/values-en-rIN/strings.xml +++ b/library/src/main/res/values-en-rIN/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Previous track" - "Next track" - "Pause" - "Play" - "Stop" - "Rewind" - "Fast-forward" + "Previous track" + "Next track" + "Pause" + "Play" + "Stop" + "Rewind" + "Fast-forward" diff --git a/library/src/main/res/values-es-rUS/strings.xml b/library/src/main/res/values-es-rUS/strings.xml index 6e1dcf01de..72b176e538 100644 --- a/library/src/main/res/values-es-rUS/strings.xml +++ b/library/src/main/res/values-es-rUS/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Pista anterior" - "Siguiente pista" - "Pausar" - "Reproducir" - "Detener" - "Retroceder" - "Avanzar" + "Pista anterior" + "Siguiente pista" + "Pausar" + "Reproducir" + "Detener" + "Retroceder" + "Avanzar" diff --git a/library/src/main/res/values-es/strings.xml b/library/src/main/res/values-es/strings.xml index 4f108bc5df..3b188d266d 100644 --- a/library/src/main/res/values-es/strings.xml +++ b/library/src/main/res/values-es/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Canción anterior" - "Siguiente canción" - "Pausar" - "Reproducir" - "Detener" - "Rebobinar" - "Avance rápido" + "Canción anterior" + "Siguiente canción" + "Pausar" + "Reproducir" + "Detener" + "Rebobinar" + "Avance rápido" diff --git a/library/src/main/res/values-et-rEE/strings.xml b/library/src/main/res/values-et-rEE/strings.xml index 588510dc32..7a01bd9d5a 100644 --- a/library/src/main/res/values-et-rEE/strings.xml +++ b/library/src/main/res/values-et-rEE/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Eelmine lugu" - "Järgmine lugu" - "Peata" - "Esita" - "Peata" - "Keri tagasi" - "Keri edasi" + "Eelmine lugu" + "Järgmine lugu" + "Peata" + "Esita" + "Peata" + "Keri tagasi" + "Keri edasi" diff --git a/library/src/main/res/values-eu-rES/strings.xml b/library/src/main/res/values-eu-rES/strings.xml index fa96388522..3dd51d2138 100644 --- a/library/src/main/res/values-eu-rES/strings.xml +++ b/library/src/main/res/values-eu-rES/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Aurreko pista" - "Hurrengo pista" - "Pausatu" - "Erreproduzitu" - "Gelditu" - "Atzeratu" - "Aurreratu" + "Aurreko pista" + "Hurrengo pista" + "Pausatu" + "Erreproduzitu" + "Gelditu" + "Atzeratu" + "Aurreratu" diff --git a/library/src/main/res/values-fa/strings.xml b/library/src/main/res/values-fa/strings.xml index 84c99cae18..a8955ca2f3 100644 --- a/library/src/main/res/values-fa/strings.xml +++ b/library/src/main/res/values-fa/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "آهنگ قبلی" - "آهنگ بعدی" - "مکث" - "پخش" - "توقف" - "عقب بردن" - "جلو بردن سریع" + "آهنگ قبلی" + "آهنگ بعدی" + "مکث" + "پخش" + "توقف" + "عقب بردن" + "جلو بردن سریع" diff --git a/library/src/main/res/values-fi/strings.xml b/library/src/main/res/values-fi/strings.xml index ae0e25b1aa..5f1352d1af 100644 --- a/library/src/main/res/values-fi/strings.xml +++ b/library/src/main/res/values-fi/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Edellinen raita" - "Seuraava raita" - "Tauko" - "Toista" - "Seis" - "Kelaa taakse" - "Kelaa eteen" + "Edellinen raita" + "Seuraava raita" + "Tauko" + "Toista" + "Seis" + "Kelaa taakse" + "Kelaa eteen" diff --git a/library/src/main/res/values-fr-rCA/strings.xml b/library/src/main/res/values-fr-rCA/strings.xml index 5215b00d82..51ba11e0c0 100644 --- a/library/src/main/res/values-fr-rCA/strings.xml +++ b/library/src/main/res/values-fr-rCA/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Chanson précédente" - "Chanson suivante" - "Pause" - "Lecture" - "Arrêter" - "Reculer" - "Avance rapide" + "Chanson précédente" + "Chanson suivante" + "Pause" + "Lecture" + "Arrêter" + "Reculer" + "Avance rapide" diff --git a/library/src/main/res/values-fr/strings.xml b/library/src/main/res/values-fr/strings.xml index 7980c80b47..d55b32b6f7 100644 --- a/library/src/main/res/values-fr/strings.xml +++ b/library/src/main/res/values-fr/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Piste précédente" - "Piste suivante" - "Interrompre" - "Lire" - "Arrêter" - "Retour arrière" - "Avance rapide" + "Piste précédente" + "Piste suivante" + "Interrompre" + "Lire" + "Arrêter" + "Retour arrière" + "Avance rapide" diff --git a/library/src/main/res/values-gl-rES/strings.xml b/library/src/main/res/values-gl-rES/strings.xml index 05864b2633..99ae59c7f9 100644 --- a/library/src/main/res/values-gl-rES/strings.xml +++ b/library/src/main/res/values-gl-rES/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Pista anterior" - "Seguinte pista" - "Pausar" - "Reproducir" - "Deter" - "Rebobinar" - "Avance rápido" + "Pista anterior" + "Seguinte pista" + "Pausar" + "Reproducir" + "Deter" + "Rebobinar" + "Avance rápido" diff --git a/library/src/main/res/values-gu-rIN/strings.xml b/library/src/main/res/values-gu-rIN/strings.xml index b935154cc4..6feab0a3a6 100644 --- a/library/src/main/res/values-gu-rIN/strings.xml +++ b/library/src/main/res/values-gu-rIN/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "પહેલાનો ટ્રૅક" - "આગલો ટ્રૅક" - "થોભો" - "ચલાવો" - "રોકો" - "રીવાઇન્ડ કરો" - "ઝડપી ફોરવર્ડ કરો" + "પહેલાનો ટ્રૅક" + "આગલો ટ્રૅક" + "થોભો" + "ચલાવો" + "રોકો" + "રીવાઇન્ડ કરો" + "ઝડપી ફોરવર્ડ કરો" diff --git a/library/src/main/res/values-hi/strings.xml b/library/src/main/res/values-hi/strings.xml index 08415bc992..5229b67d0e 100644 --- a/library/src/main/res/values-hi/strings.xml +++ b/library/src/main/res/values-hi/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "पिछला ट्रैक" - "अगला ट्रैक" - "रोकें" - "चलाएं" - "बंद करें" - "रिवाइंड करें" - "फ़ास्ट फ़ॉरवर्ड" + "पिछला ट्रैक" + "अगला ट्रैक" + "रोकें" + "चलाएं" + "बंद करें" + "रिवाइंड करें" + "फ़ास्ट फ़ॉरवर्ड" diff --git a/library/src/main/res/values-hr/strings.xml b/library/src/main/res/values-hr/strings.xml index 6fe1ba9288..c0b075edde 100644 --- a/library/src/main/res/values-hr/strings.xml +++ b/library/src/main/res/values-hr/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Prethodna pjesma" - "Sljedeća pjesma" - "Pauziraj" - "Reproduciraj" - "Zaustavi" - "Unatrag" - "Brzo unaprijed" + "Prethodna pjesma" + "Sljedeća pjesma" + "Pauziraj" + "Reproduciraj" + "Zaustavi" + "Unatrag" + "Brzo unaprijed" diff --git a/library/src/main/res/values-hu/strings.xml b/library/src/main/res/values-hu/strings.xml index be28793dc3..2a34684edb 100644 --- a/library/src/main/res/values-hu/strings.xml +++ b/library/src/main/res/values-hu/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Előző szám" - "Következő szám" - "Szünet" - "Lejátszás" - "Leállítás" - "Visszatekerés" - "Előretekerés" + "Előző szám" + "Következő szám" + "Szünet" + "Lejátszás" + "Leállítás" + "Visszatekerés" + "Előretekerés" diff --git a/library/src/main/res/values-hy-rAM/strings.xml b/library/src/main/res/values-hy-rAM/strings.xml index 3052d6c26b..05f9d04ab7 100644 --- a/library/src/main/res/values-hy-rAM/strings.xml +++ b/library/src/main/res/values-hy-rAM/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Նախորդը" - "Հաջորդը" - "Դադարեցնել" - "Նվագարկել" - "Դադարեցնել" - "Հետ փաթաթել" - "Արագ առաջ անցնել" + "Նախորդը" + "Հաջորդը" + "Դադարեցնել" + "Նվագարկել" + "Դադարեցնել" + "Հետ փաթաթել" + "Արագ առաջ անցնել" diff --git a/library/src/main/res/values-in/strings.xml b/library/src/main/res/values-in/strings.xml index 6be2c2155c..062933a0a8 100644 --- a/library/src/main/res/values-in/strings.xml +++ b/library/src/main/res/values-in/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Lagu sebelumnya" - "Lagu berikutnya" - "Jeda" - "Putar" - "Berhenti" - "Putar Ulang" - "Maju cepat" + "Lagu sebelumnya" + "Lagu berikutnya" + "Jeda" + "Putar" + "Berhenti" + "Putar Ulang" + "Maju cepat" diff --git a/library/src/main/res/values-is-rIS/strings.xml b/library/src/main/res/values-is-rIS/strings.xml index 80b81fa7fb..9c4421a272 100644 --- a/library/src/main/res/values-is-rIS/strings.xml +++ b/library/src/main/res/values-is-rIS/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Fyrra lag" - "Næsta lag" - "Hlé" - "Spila" - "Stöðva" - "Spóla til baka" - "Spóla áfram" + "Fyrra lag" + "Næsta lag" + "Hlé" + "Spila" + "Stöðva" + "Spóla til baka" + "Spóla áfram" diff --git a/library/src/main/res/values-it/strings.xml b/library/src/main/res/values-it/strings.xml index 4d84822b8e..71525a2b3e 100644 --- a/library/src/main/res/values-it/strings.xml +++ b/library/src/main/res/values-it/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Traccia precedente" - "Traccia successiva" - "Metti in pausa" - "Riproduci" - "Interrompi" - "Riavvolgi" - "Avanti veloce" + "Traccia precedente" + "Traccia successiva" + "Metti in pausa" + "Riproduci" + "Interrompi" + "Riavvolgi" + "Avanti veloce" diff --git a/library/src/main/res/values-iw/strings.xml b/library/src/main/res/values-iw/strings.xml index 5449bb1a7c..f33cc2adb0 100644 --- a/library/src/main/res/values-iw/strings.xml +++ b/library/src/main/res/values-iw/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "הרצועה הקודמת" - "הרצועה הבאה" - "השהה" - "הפעל" - "הפסק" - "הרץ אחורה" - "הרץ קדימה" + "הרצועה הקודמת" + "הרצועה הבאה" + "השהה" + "הפעל" + "הפסק" + "הרץ אחורה" + "הרץ קדימה" diff --git a/library/src/main/res/values-ja/strings.xml b/library/src/main/res/values-ja/strings.xml index e745b18ee0..baa459aeca 100644 --- a/library/src/main/res/values-ja/strings.xml +++ b/library/src/main/res/values-ja/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "前のトラック" - "次のトラック" - "一時停止" - "再生" - "停止" - "巻き戻し" - "早送り" + "前のトラック" + "次のトラック" + "一時停止" + "再生" + "停止" + "巻き戻し" + "早送り" diff --git a/library/src/main/res/values-ka-rGE/strings.xml b/library/src/main/res/values-ka-rGE/strings.xml index 652186360c..5b87f86c34 100644 --- a/library/src/main/res/values-ka-rGE/strings.xml +++ b/library/src/main/res/values-ka-rGE/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "წინა ჩანაწერი" - "შემდეგი ჩანაწერი" - "პაუზა" - "დაკვრა" - "შეწყვეტა" - "უკან გადახვევა" - "წინ გადახვევა" + "წინა ჩანაწერი" + "შემდეგი ჩანაწერი" + "პაუზა" + "დაკვრა" + "შეწყვეტა" + "უკან გადახვევა" + "წინ გადახვევა" diff --git a/library/src/main/res/values-kk-rKZ/strings.xml b/library/src/main/res/values-kk-rKZ/strings.xml index eff4c334e0..c1bf5c8b4b 100644 --- a/library/src/main/res/values-kk-rKZ/strings.xml +++ b/library/src/main/res/values-kk-rKZ/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Алдыңғы трек" - "Келесі трек" - "Кідірту" - "Ойнату" - "Тоқтату" - "Кері айналдыру" - "Жылдам алға айналдыру" + "Алдыңғы трек" + "Келесі трек" + "Кідірту" + "Ойнату" + "Тоқтату" + "Кері айналдыру" + "Жылдам алға айналдыру" diff --git a/library/src/main/res/values-km-rKH/strings.xml b/library/src/main/res/values-km-rKH/strings.xml index 8298655078..dbeeab60a6 100644 --- a/library/src/main/res/values-km-rKH/strings.xml +++ b/library/src/main/res/values-km-rKH/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "បទ​មុន" - "បទ​បន្ទាប់" - "ផ្អាក" - "ចាក់" - "បញ្ឈប់" - "ខា​ថយក្រោយ" - "ទៅ​មុខ​​​រហ័ស" + "បទ​មុន" + "បទ​បន្ទាប់" + "ផ្អាក" + "ចាក់" + "បញ្ឈប់" + "ខា​ថយក្រោយ" + "ទៅ​មុខ​​​រហ័ស" diff --git a/library/src/main/res/values-kn-rIN/strings.xml b/library/src/main/res/values-kn-rIN/strings.xml index ac210680d8..b73cf0fdb0 100644 --- a/library/src/main/res/values-kn-rIN/strings.xml +++ b/library/src/main/res/values-kn-rIN/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "ಹಿಂದಿನ ಟ್ರ್ಯಾಕ್" - "ಮುಂದಿನ ಟ್ರ್ಯಾಕ್" - "ವಿರಾಮಗೊಳಿಸು" - "ಪ್ಲೇ ಮಾಡು" - "ನಿಲ್ಲಿಸು" - "ರಿವೈಂಡ್ ಮಾಡು" - "ವೇಗವಾಗಿ ಮುಂದಕ್ಕೆ" + "ಹಿಂದಿನ ಟ್ರ್ಯಾಕ್" + "ಮುಂದಿನ ಟ್ರ್ಯಾಕ್" + "ವಿರಾಮಗೊಳಿಸು" + "ಪ್ಲೇ ಮಾಡು" + "ನಿಲ್ಲಿಸು" + "ರಿವೈಂಡ್ ಮಾಡು" + "ವೇಗವಾಗಿ ಮುಂದಕ್ಕೆ" diff --git a/library/src/main/res/values-ko/strings.xml b/library/src/main/res/values-ko/strings.xml index 38f9b7aae9..7097e2d9f7 100644 --- a/library/src/main/res/values-ko/strings.xml +++ b/library/src/main/res/values-ko/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "이전 트랙" - "다음 트랙" - "일시중지" - "재생" - "중지" - "되감기" - "빨리 감기" + "이전 트랙" + "다음 트랙" + "일시중지" + "재생" + "중지" + "되감기" + "빨리 감기" diff --git a/library/src/main/res/values-ky-rKG/strings.xml b/library/src/main/res/values-ky-rKG/strings.xml index 43591d760c..7090c178c3 100644 --- a/library/src/main/res/values-ky-rKG/strings.xml +++ b/library/src/main/res/values-ky-rKG/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Мурунку трек" - "Кийинки трек" - "Тындыруу" - "Ойнотуу" - "Токтотуу" - "Артка түрүү" - "Алдыга түрүү" + "Мурунку трек" + "Кийинки трек" + "Тындыруу" + "Ойнотуу" + "Токтотуу" + "Артка түрүү" + "Алдыга түрүү" diff --git a/library/src/main/res/values-lo-rLA/strings.xml b/library/src/main/res/values-lo-rLA/strings.xml index 885005406d..44095e4323 100644 --- a/library/src/main/res/values-lo-rLA/strings.xml +++ b/library/src/main/res/values-lo-rLA/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "​ເພງ​ກ່ອນ​ໜ້າ" - "​ເພງ​ຕໍ່​ໄປ" - "ຢຸດຊົ່ວຄາວ" - "ຫຼິ້ນ" - "ຢຸດ" - "​ຣີ​​ວາຍກັບ" - "ເລື່ອນ​ໄປ​ໜ້າ" + "​ເພງ​ກ່ອນ​ໜ້າ" + "​ເພງ​ຕໍ່​ໄປ" + "ຢຸດຊົ່ວຄາວ" + "ຫຼິ້ນ" + "ຢຸດ" + "​ຣີ​​ວາຍກັບ" + "ເລື່ອນ​ໄປ​ໜ້າ" diff --git a/library/src/main/res/values-lt/strings.xml b/library/src/main/res/values-lt/strings.xml index f828226100..138caec322 100644 --- a/library/src/main/res/values-lt/strings.xml +++ b/library/src/main/res/values-lt/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Ankstesnis takelis" - "Kitas takelis" - "Pristabdyti" - "Leisti" - "Stabdyti" - "Sukti atgal" - "Sukti pirmyn" + "Ankstesnis takelis" + "Kitas takelis" + "Pristabdyti" + "Leisti" + "Stabdyti" + "Sukti atgal" + "Sukti pirmyn" diff --git a/library/src/main/res/values-lv/strings.xml b/library/src/main/res/values-lv/strings.xml index b5f8f00e84..4c91da86cc 100644 --- a/library/src/main/res/values-lv/strings.xml +++ b/library/src/main/res/values-lv/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Iepriekšējais ieraksts" - "Nākamais ieraksts" - "Pārtraukt" - "Atskaņot" - "Apturēt" - "Attīt atpakaļ" - "Ātri patīt" + "Iepriekšējais ieraksts" + "Nākamais ieraksts" + "Pārtraukt" + "Atskaņot" + "Apturēt" + "Attīt atpakaļ" + "Ātri patīt" diff --git a/library/src/main/res/values-mk-rMK/strings.xml b/library/src/main/res/values-mk-rMK/strings.xml index aa96af37a1..e9fedf689f 100644 --- a/library/src/main/res/values-mk-rMK/strings.xml +++ b/library/src/main/res/values-mk-rMK/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Претходна песна" - "Следна песна" - "Пауза" - "Пушти" - "Запри" - "Премотај назад" - "Брзо премотај напред" + "Претходна песна" + "Следна песна" + "Пауза" + "Пушти" + "Запри" + "Премотај назад" + "Брзо премотај напред" diff --git a/library/src/main/res/values-ml-rIN/strings.xml b/library/src/main/res/values-ml-rIN/strings.xml index f59e90d24a..acc33934fb 100644 --- a/library/src/main/res/values-ml-rIN/strings.xml +++ b/library/src/main/res/values-ml-rIN/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "മുമ്പത്തെ ട്രാക്ക്" - "അടുത്ത ട്രാക്ക്" - "താൽക്കാലികമായി നിർത്തുക" - "പ്ലേ ചെയ്യുക" - "നിര്‍ത്തുക" - "റിവൈൻഡുചെയ്യുക" - "വേഗത്തിലുള്ള കൈമാറൽ" + "മുമ്പത്തെ ട്രാക്ക്" + "അടുത്ത ട്രാക്ക്" + "താൽക്കാലികമായി നിർത്തുക" + "പ്ലേ ചെയ്യുക" + "നിര്‍ത്തുക" + "റിവൈൻഡുചെയ്യുക" + "വേഗത്തിലുള്ള കൈമാറൽ" diff --git a/library/src/main/res/values-mn-rMN/strings.xml b/library/src/main/res/values-mn-rMN/strings.xml index 2ab27a803e..6434e9ea16 100644 --- a/library/src/main/res/values-mn-rMN/strings.xml +++ b/library/src/main/res/values-mn-rMN/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Өмнөх трек" - "Дараагийн трек" - "Түр зогсоох" - "Тоглуулах" - "Зогсоох" - "Буцааж хураах" - "Хурдан урагшлуулах" + "Өмнөх трек" + "Дараагийн трек" + "Түр зогсоох" + "Тоглуулах" + "Зогсоох" + "Буцааж хураах" + "Хурдан урагшлуулах" diff --git a/library/src/main/res/values-mr-rIN/strings.xml b/library/src/main/res/values-mr-rIN/strings.xml index 827259e68b..8f4d0d75b1 100644 --- a/library/src/main/res/values-mr-rIN/strings.xml +++ b/library/src/main/res/values-mr-rIN/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "मागील ट्रॅक" - "पुढील ट्रॅक" - "विराम द्या" - "प्ले करा" - "थांबा" - "रिवाईँड करा" - "फास्ट फॉरवर्ड करा" + "मागील ट्रॅक" + "पुढील ट्रॅक" + "विराम द्या" + "प्ले करा" + "थांबा" + "रिवाईँड करा" + "फास्ट फॉरवर्ड करा" diff --git a/library/src/main/res/values-ms-rMY/strings.xml b/library/src/main/res/values-ms-rMY/strings.xml index e8bf6ec693..91f74bbc1c 100644 --- a/library/src/main/res/values-ms-rMY/strings.xml +++ b/library/src/main/res/values-ms-rMY/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Lagu sebelumnya" - "Lagu seterusnya" - "Jeda" - "Main" - "Berhenti" - "Gulung semula" - "Mara laju" + "Lagu sebelumnya" + "Lagu seterusnya" + "Jeda" + "Main" + "Berhenti" + "Gulung semula" + "Mara laju" diff --git a/library/src/main/res/values-my-rMM/strings.xml b/library/src/main/res/values-my-rMM/strings.xml index 62e4e6c0c7..4b68e6e950 100644 --- a/library/src/main/res/values-my-rMM/strings.xml +++ b/library/src/main/res/values-my-rMM/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "ယခင် တစ်ပုဒ်" - "နောက် တစ်ပုဒ်" - "ခဏရပ်ရန်" - "ဖွင့်ရန်" - "ရပ်ရန်" - "ပြန်ရစ်ရန်" - "ရှေ့သို့ သွားရန်" + "ယခင် တစ်ပုဒ်" + "နောက် တစ်ပုဒ်" + "ခဏရပ်ရန်" + "ဖွင့်ရန်" + "ရပ်ရန်" + "ပြန်ရစ်ရန်" + "ရှေ့သို့ သွားရန်" diff --git a/library/src/main/res/values-nb/strings.xml b/library/src/main/res/values-nb/strings.xml index b5e7c6d05b..37454235ad 100644 --- a/library/src/main/res/values-nb/strings.xml +++ b/library/src/main/res/values-nb/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Forrige spor" - "Neste spor" - "Sett på pause" - "Spill av" - "Stopp" - "Tilbakespoling" - "Fremoverspoling" + "Forrige spor" + "Neste spor" + "Sett på pause" + "Spill av" + "Stopp" + "Tilbakespoling" + "Fremoverspoling" diff --git a/library/src/main/res/values-ne-rNP/strings.xml b/library/src/main/res/values-ne-rNP/strings.xml index 47ce703544..375e44afce 100644 --- a/library/src/main/res/values-ne-rNP/strings.xml +++ b/library/src/main/res/values-ne-rNP/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "अघिल्लो ट्रयाक" - "अर्को ट्रयाक" - "रोक्नुहोस्" - "चलाउनुहोस्" - "रोक्नुहोस्" - "दोहोर्याउनुहोस्" - "फास्ट फर्वार्ड" + "अघिल्लो ट्रयाक" + "अर्को ट्रयाक" + "रोक्नुहोस्" + "चलाउनुहोस्" + "रोक्नुहोस्" + "दोहोर्याउनुहोस्" + "फास्ट फर्वार्ड" diff --git a/library/src/main/res/values-nl/strings.xml b/library/src/main/res/values-nl/strings.xml index 092165db53..2bdbf0bdae 100644 --- a/library/src/main/res/values-nl/strings.xml +++ b/library/src/main/res/values-nl/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Vorig nummer" - "Volgend nummer" - "Onderbreken" - "Afspelen" - "Stoppen" - "Terugspoelen" - "Vooruitspoelen" + "Vorig nummer" + "Volgend nummer" + "Onderbreken" + "Afspelen" + "Stoppen" + "Terugspoelen" + "Vooruitspoelen" diff --git a/library/src/main/res/values-pa-rIN/strings.xml b/library/src/main/res/values-pa-rIN/strings.xml index 96654b89f0..143508e071 100644 --- a/library/src/main/res/values-pa-rIN/strings.xml +++ b/library/src/main/res/values-pa-rIN/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "ਪਿਛਲਾ ਟਰੈਕ" - "ਅਗਲਾ ਟਰੈਕ" - "ਰੋਕੋ" - "ਪਲੇ ਕਰੋ" - "ਰੋਕੋ" - "ਰੀਵਾਈਂਡ ਕਰੋ" - "ਅੱਗੇ ਭੇਜੋ" + "ਪਿਛਲਾ ਟਰੈਕ" + "ਅਗਲਾ ਟਰੈਕ" + "ਰੋਕੋ" + "ਪਲੇ ਕਰੋ" + "ਰੋਕੋ" + "ਰੀਵਾਈਂਡ ਕਰੋ" + "ਅੱਗੇ ਭੇਜੋ" diff --git a/library/src/main/res/values-pl/strings.xml b/library/src/main/res/values-pl/strings.xml index 131e0630f3..64f52d5d09 100644 --- a/library/src/main/res/values-pl/strings.xml +++ b/library/src/main/res/values-pl/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Poprzedni utwór" - "Następny utwór" - "Wstrzymaj" - "Odtwórz" - "Zatrzymaj" - "Przewiń do tyłu" - "Przewiń do przodu" + "Poprzedni utwór" + "Następny utwór" + "Wstrzymaj" + "Odtwórz" + "Zatrzymaj" + "Przewiń do tyłu" + "Przewiń do przodu" diff --git a/library/src/main/res/values-pt-rBR/strings.xml b/library/src/main/res/values-pt-rBR/strings.xml index 3e3ac47be9..51bcf4d723 100644 --- a/library/src/main/res/values-pt-rBR/strings.xml +++ b/library/src/main/res/values-pt-rBR/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Faixa anterior" - "Próxima faixa" - "Pausar" - "Reproduzir" - "Parar" - "Retroceder" - "Avançar" + "Faixa anterior" + "Próxima faixa" + "Pausar" + "Reproduzir" + "Parar" + "Retroceder" + "Avançar" diff --git a/library/src/main/res/values-pt-rPT/strings.xml b/library/src/main/res/values-pt-rPT/strings.xml index 0d0d0ec183..5b3c9131d0 100644 --- a/library/src/main/res/values-pt-rPT/strings.xml +++ b/library/src/main/res/values-pt-rPT/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Faixa anterior" - "Faixa seguinte" - "Interromper" - "Reproduzir" - "Parar" - "Rebobinar" - "Avançar" + "Faixa anterior" + "Faixa seguinte" + "Interromper" + "Reproduzir" + "Parar" + "Rebobinar" + "Avançar" diff --git a/library/src/main/res/values-pt/strings.xml b/library/src/main/res/values-pt/strings.xml index 3e3ac47be9..51bcf4d723 100644 --- a/library/src/main/res/values-pt/strings.xml +++ b/library/src/main/res/values-pt/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Faixa anterior" - "Próxima faixa" - "Pausar" - "Reproduzir" - "Parar" - "Retroceder" - "Avançar" + "Faixa anterior" + "Próxima faixa" + "Pausar" + "Reproduzir" + "Parar" + "Retroceder" + "Avançar" diff --git a/library/src/main/res/values-ro/strings.xml b/library/src/main/res/values-ro/strings.xml index 3cb6730c99..5a7feda78c 100644 --- a/library/src/main/res/values-ro/strings.xml +++ b/library/src/main/res/values-ro/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Melodia anterioară" - "Melodia următoare" - "Pauză" - "Redați" - "Opriți" - "Derulați" - "Derulați rapid înainte" + "Melodia anterioară" + "Melodia următoare" + "Pauză" + "Redați" + "Opriți" + "Derulați" + "Derulați rapid înainte" diff --git a/library/src/main/res/values-ru/strings.xml b/library/src/main/res/values-ru/strings.xml index 59a6923954..da47546a8b 100644 --- a/library/src/main/res/values-ru/strings.xml +++ b/library/src/main/res/values-ru/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Предыдущий трек" - "Следующий трек" - "Приостановить" - "Воспроизвести" - "Остановить" - "Перемотать назад" - "Перемотать вперед" + "Предыдущий трек" + "Следующий трек" + "Приостановить" + "Воспроизвести" + "Остановить" + "Перемотать назад" + "Перемотать вперед" diff --git a/library/src/main/res/values-si-rLK/strings.xml b/library/src/main/res/values-si-rLK/strings.xml index d634b82374..0b579240e8 100644 --- a/library/src/main/res/values-si-rLK/strings.xml +++ b/library/src/main/res/values-si-rLK/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "පෙර ගීතය" - "ඊළඟ ගීතය" - "විරාමය" - "ධාවනය කරන්න" - "නතර කරන්න" - "නැවත ඔතන්න" - "වේගයෙන් ඉදිරියට යන" + "පෙර ගීතය" + "ඊළඟ ගීතය" + "විරාමය" + "ධාවනය කරන්න" + "නතර කරන්න" + "නැවත ඔතන්න" + "වේගයෙන් ඉදිරියට යන" diff --git a/library/src/main/res/values-sk/strings.xml b/library/src/main/res/values-sk/strings.xml index b0965864d6..7596497e06 100644 --- a/library/src/main/res/values-sk/strings.xml +++ b/library/src/main/res/values-sk/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Predchádzajúca stopa" - "Ďalšia stopa" - "Pozastaviť" - "Prehrať" - "Zastaviť" - "Pretočiť späť" - "Pretočiť dopredu" + "Predchádzajúca stopa" + "Ďalšia stopa" + "Pozastaviť" + "Prehrať" + "Zastaviť" + "Pretočiť späť" + "Pretočiť dopredu" diff --git a/library/src/main/res/values-sl/strings.xml b/library/src/main/res/values-sl/strings.xml index f2edda42dd..a77586b50c 100644 --- a/library/src/main/res/values-sl/strings.xml +++ b/library/src/main/res/values-sl/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Prejšnja skladba" - "Naslednja skladba" - "Zaustavi" - "Predvajaj" - "Ustavi" - "Previj nazaj" - "Previj naprej" + "Prejšnja skladba" + "Naslednja skladba" + "Zaustavi" + "Predvajaj" + "Ustavi" + "Previj nazaj" + "Previj naprej" diff --git a/library/src/main/res/values-sq-rAL/strings.xml b/library/src/main/res/values-sq-rAL/strings.xml index 6716a32486..1fb824366d 100644 --- a/library/src/main/res/values-sq-rAL/strings.xml +++ b/library/src/main/res/values-sq-rAL/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Kënga e mëparshme" - "Kënga tjetër" - "Pauzë" - "Luaj" - "Ndalo" - "Kthehu pas" - "Përparo me shpejtësi" + "Kënga e mëparshme" + "Kënga tjetër" + "Pauzë" + "Luaj" + "Ndalo" + "Kthehu pas" + "Përparo me shpejtësi" diff --git a/library/src/main/res/values-sr/strings.xml b/library/src/main/res/values-sr/strings.xml index cfb3d20c6a..175ad4fe7f 100644 --- a/library/src/main/res/values-sr/strings.xml +++ b/library/src/main/res/values-sr/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Претходна песма" - "Следећа песма" - "Пауза" - "Пусти" - "Заустави" - "Премотај уназад" - "Премотај унапред" + "Претходна песма" + "Следећа песма" + "Пауза" + "Пусти" + "Заустави" + "Премотај уназад" + "Премотај унапред" diff --git a/library/src/main/res/values-sv/strings.xml b/library/src/main/res/values-sv/strings.xml index 35b987db48..e6a8960458 100644 --- a/library/src/main/res/values-sv/strings.xml +++ b/library/src/main/res/values-sv/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Föregående spår" - "Nästa spår" - "Pausa" - "Spela upp" - "Avbryt" - "Spola tillbaka" - "Snabbspola framåt" + "Föregående spår" + "Nästa spår" + "Pausa" + "Spela upp" + "Avbryt" + "Spola tillbaka" + "Snabbspola framåt" diff --git a/library/src/main/res/values-sw/strings.xml b/library/src/main/res/values-sw/strings.xml index 32e2799e97..8055b7daff 100644 --- a/library/src/main/res/values-sw/strings.xml +++ b/library/src/main/res/values-sw/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Wimbo uliotangulia" - "Wimbo unaofuata" - "Sitisha" - "Cheza" - "Simamisha" - "Rudisha nyuma" - "Peleka mbele kwa kasi" + "Wimbo uliotangulia" + "Wimbo unaofuata" + "Sitisha" + "Cheza" + "Simamisha" + "Rudisha nyuma" + "Peleka mbele kwa kasi" diff --git a/library/src/main/res/values-ta-rIN/strings.xml b/library/src/main/res/values-ta-rIN/strings.xml index 32d88cf9f0..3eb995d467 100644 --- a/library/src/main/res/values-ta-rIN/strings.xml +++ b/library/src/main/res/values-ta-rIN/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "முந்தைய ட்ராக்" - "அடுத்த ட்ராக்" - "இடைநிறுத்து" - "இயக்கு" - "நிறுத்து" - "மீண்டும் காட்டு" - "வேகமாக முன்செல்" + "முந்தைய ட்ராக்" + "அடுத்த ட்ராக்" + "இடைநிறுத்து" + "இயக்கு" + "நிறுத்து" + "மீண்டும் காட்டு" + "வேகமாக முன்செல்" diff --git a/library/src/main/res/values-te-rIN/strings.xml b/library/src/main/res/values-te-rIN/strings.xml index f527c30270..fe7930455a 100644 --- a/library/src/main/res/values-te-rIN/strings.xml +++ b/library/src/main/res/values-te-rIN/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "మునుపటి ట్రాక్" - "తదుపరి ట్రాక్" - "పాజ్ చేయి" - "ప్లే చేయి" - "ఆపివేయి" - "రివైండ్ చేయి" - "వేగంగా ఫార్వార్డ్ చేయి" + "మునుపటి ట్రాక్" + "తదుపరి ట్రాక్" + "పాజ్ చేయి" + "ప్లే చేయి" + "ఆపివేయి" + "రివైండ్ చేయి" + "వేగంగా ఫార్వార్డ్ చేయి" diff --git a/library/src/main/res/values-th/strings.xml b/library/src/main/res/values-th/strings.xml index 3b86808ee7..deb2aac87d 100644 --- a/library/src/main/res/values-th/strings.xml +++ b/library/src/main/res/values-th/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "แทร็กก่อนหน้า" - "แทร็กถัดไป" - "หยุดชั่วคราว" - "เล่น" - "หยุด" - "กรอกลับ" - "กรอไปข้างหน้า" + "แทร็กก่อนหน้า" + "แทร็กถัดไป" + "หยุดชั่วคราว" + "เล่น" + "หยุด" + "กรอกลับ" + "กรอไปข้างหน้า" diff --git a/library/src/main/res/values-tl/strings.xml b/library/src/main/res/values-tl/strings.xml index 2381287624..28dcb3267e 100644 --- a/library/src/main/res/values-tl/strings.xml +++ b/library/src/main/res/values-tl/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Nakaraang track" - "Susunod na track" - "I-pause" - "I-play" - "Ihinto" - "I-rewind" - "I-fast forward" + "Nakaraang track" + "Susunod na track" + "I-pause" + "I-play" + "Ihinto" + "I-rewind" + "I-fast forward" diff --git a/library/src/main/res/values-tr/strings.xml b/library/src/main/res/values-tr/strings.xml index cd05be1551..4265d796fe 100644 --- a/library/src/main/res/values-tr/strings.xml +++ b/library/src/main/res/values-tr/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Önceki parça" - "Sonraki parça" - "Duraklat" - "Çal" - "Durdur" - "Geri sar" - "İleri sar" + "Önceki parça" + "Sonraki parça" + "Duraklat" + "Çal" + "Durdur" + "Geri sar" + "İleri sar" diff --git a/library/src/main/res/values-uk/strings.xml b/library/src/main/res/values-uk/strings.xml index ec249b7f9f..487ca07556 100644 --- a/library/src/main/res/values-uk/strings.xml +++ b/library/src/main/res/values-uk/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Попередня композиція" - "Наступна композиція" - "Пауза" - "Відтворити" - "Зупинити" - "Перемотати назад" - "Перемотати вперед" + "Попередня композиція" + "Наступна композиція" + "Пауза" + "Відтворити" + "Зупинити" + "Перемотати назад" + "Перемотати вперед" diff --git a/library/src/main/res/values-ur-rPK/strings.xml b/library/src/main/res/values-ur-rPK/strings.xml index 002bf8e780..55fa908bcd 100644 --- a/library/src/main/res/values-ur-rPK/strings.xml +++ b/library/src/main/res/values-ur-rPK/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "پچھلا ٹریک" - "اگلا ٹریک" - "موقوف کریں" - "چلائیں" - "روکیں" - "ریوائینڈ کریں" - "تیزی سے فارورڈ کریں" + "پچھلا ٹریک" + "اگلا ٹریک" + "موقوف کریں" + "چلائیں" + "روکیں" + "ریوائینڈ کریں" + "تیزی سے فارورڈ کریں" diff --git a/library/src/main/res/values-uz-rUZ/strings.xml b/library/src/main/res/values-uz-rUZ/strings.xml index 5b96983ad5..9cee926844 100644 --- a/library/src/main/res/values-uz-rUZ/strings.xml +++ b/library/src/main/res/values-uz-rUZ/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Avvalgi musiqa" - "Keyingi musiqa" - "To‘xtatib turish" - "Ijro qilish" - "To‘xtatish" - "Orqaga o‘tkazish" - "Oldinga o‘tkazish" + "Avvalgi musiqa" + "Keyingi musiqa" + "To‘xtatib turish" + "Ijro qilish" + "To‘xtatish" + "Orqaga o‘tkazish" + "Oldinga o‘tkazish" diff --git a/library/src/main/res/values-vi/strings.xml b/library/src/main/res/values-vi/strings.xml index 2f0dce399a..917ec8e95c 100644 --- a/library/src/main/res/values-vi/strings.xml +++ b/library/src/main/res/values-vi/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Bản nhạc trước" - "Bản nhạc tiếp theo" - "Tạm dừng" - "Phát" - "Ngừng" - "Tua lại" - "Tua đi" + "Bản nhạc trước" + "Bản nhạc tiếp theo" + "Tạm dừng" + "Phát" + "Ngừng" + "Tua lại" + "Tua đi" diff --git a/library/src/main/res/values-zh-rCN/strings.xml b/library/src/main/res/values-zh-rCN/strings.xml index ebec8de267..41e02409e2 100644 --- a/library/src/main/res/values-zh-rCN/strings.xml +++ b/library/src/main/res/values-zh-rCN/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "上一曲" - "下一曲" - "暂停" - "播放" - "停止" - "快退" - "快进" + "上一曲" + "下一曲" + "暂停" + "播放" + "停止" + "快退" + "快进" diff --git a/library/src/main/res/values-zh-rHK/strings.xml b/library/src/main/res/values-zh-rHK/strings.xml index b97fab76af..a3244bcd70 100644 --- a/library/src/main/res/values-zh-rHK/strings.xml +++ b/library/src/main/res/values-zh-rHK/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "上一首曲目" - "下一首曲目" - "暫停" - "播放" - "停止" - "倒帶" - "向前快轉" + "上一首曲目" + "下一首曲目" + "暫停" + "播放" + "停止" + "倒帶" + "向前快轉" diff --git a/library/src/main/res/values-zh-rTW/strings.xml b/library/src/main/res/values-zh-rTW/strings.xml index 7b8552f1b6..ee915c5d9d 100644 --- a/library/src/main/res/values-zh-rTW/strings.xml +++ b/library/src/main/res/values-zh-rTW/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "上一首曲目" - "下一首曲目" - "暫停" - "播放" - "停止" - "倒轉" - "快轉" + "上一首曲目" + "下一首曲目" + "暫停" + "播放" + "停止" + "倒轉" + "快轉" diff --git a/library/src/main/res/values-zu/strings.xml b/library/src/main/res/values-zu/strings.xml index 3ed91417f2..e998846454 100644 --- a/library/src/main/res/values-zu/strings.xml +++ b/library/src/main/res/values-zu/strings.xml @@ -15,11 +15,11 @@ limitations under the License. --> - "Ithrekhi yangaphambilini" - "Ithrekhi elandelayo" - "Misa isikhashana" - "Dlala" - "Misa" - "Buyisela emumva" - "Ukudlulisa ngokushesha" + "Ithrekhi yangaphambilini" + "Ithrekhi elandelayo" + "Misa isikhashana" + "Dlala" + "Misa" + "Buyisela emumva" + "Ukudlulisa ngokushesha" diff --git a/library/src/main/res/values/attrs.xml b/library/src/main/res/values/attrs.xml index 2aa6fcd86b..b210e30512 100644 --- a/library/src/main/res/values/attrs.xml +++ b/library/src/main/res/values/attrs.xml @@ -15,8 +15,7 @@ --> - - + + - diff --git a/library/src/main/res/values/strings.xml b/library/src/main/res/values/strings.xml index 83bb88cc04..1e652dddb3 100644 --- a/library/src/main/res/values/strings.xml +++ b/library/src/main/res/values/strings.xml @@ -14,12 +14,11 @@ limitations under the License. --> - Previous track - Next track - Pause - Play - Stop - Rewind - Fast forward + Previous track + Next track + Pause + Play + Stop + Rewind + Fast forward - diff --git a/library/src/main/res/values/styles.xml b/library/src/main/res/values/styles.xml index 18109e581c..fe1e26e967 100644 --- a/library/src/main/res/values/styles.xml +++ b/library/src/main/res/values/styles.xml @@ -15,31 +15,30 @@ --> - - - - - - From 22728d9ae44c09a23a59c4ccc5e7453ce1e23ef5 Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Fri, 16 Sep 2016 07:55:34 -0700 Subject: [PATCH 05/36] Use new ParsableByteArray.reset where possible. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=133384105 --- .../exoplayer2/extractor/DefaultTrackOutput.java | 14 +++----------- .../extractor/mkv/MatroskaExtractor.java | 6 +----- .../exoplayer2/extractor/mp4/Sniffer.java | 7 +++---- .../exoplayer2/extractor/ts/PsExtractor.java | 11 +++-------- .../exoplayer2/util/ParsableByteArray.java | 16 ++++++++-------- 5 files changed, 18 insertions(+), 36 deletions(-) diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/DefaultTrackOutput.java b/library/src/main/java/com/google/android/exoplayer2/extractor/DefaultTrackOutput.java index 847d429090..f7493a8b8a 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/DefaultTrackOutput.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/DefaultTrackOutput.java @@ -298,6 +298,7 @@ public final class DefaultTrackOutput implements TrackOutput { long offset = extrasHolder.offset; // Read the signal byte. + scratch.reset(1); readData(offset, scratch.data, 1); offset++; byte signalByte = scratch.data[0]; @@ -314,9 +315,9 @@ public final class DefaultTrackOutput implements TrackOutput { // Read the subsample count, if present. int subsampleCount; if (subsampleEncryption) { + scratch.reset(2); readData(offset, scratch.data, 2); offset += 2; - scratch.setPosition(0); subsampleCount = scratch.readUnsignedShort(); } else { subsampleCount = 1; @@ -333,7 +334,7 @@ public final class DefaultTrackOutput implements TrackOutput { } if (subsampleEncryption) { int subsampleDataLength = 6 * subsampleCount; - ensureCapacity(scratch, subsampleDataLength); + scratch.reset(subsampleDataLength); readData(offset, scratch.data, subsampleDataLength); offset += subsampleDataLength; scratch.setPosition(0); @@ -412,15 +413,6 @@ public final class DefaultTrackOutput implements TrackOutput { } } - /** - * Ensure that the passed {@link ParsableByteArray} is of at least the specified limit. - */ - private static void ensureCapacity(ParsableByteArray byteArray, int limit) { - if (byteArray.limit() < limit) { - byteArray.reset(new byte[limit], limit); - } - } - // Called by the loading thread. /** diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/mkv/MatroskaExtractor.java b/library/src/main/java/com/google/android/exoplayer2/extractor/mkv/MatroskaExtractor.java index c658ab3bcd..917af9c7e1 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/mkv/MatroskaExtractor.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/mkv/MatroskaExtractor.java @@ -950,13 +950,9 @@ public final class MatroskaExtractor implements Extractor { samplePartitionCountRead = true; } int samplePartitionDataSize = samplePartitionCount * 4; - if (scratch.limit() < samplePartitionDataSize) { - scratch.reset(new byte[samplePartitionDataSize], samplePartitionDataSize); - } + scratch.reset(samplePartitionDataSize); input.readFully(scratch.data, 0, samplePartitionDataSize); sampleBytesRead += samplePartitionDataSize; - scratch.setPosition(0); - scratch.setLimit(samplePartitionDataSize); short subsampleCount = (short) (1 + (samplePartitionCount / 2)); int subsampleDataSize = 2 + 6 * subsampleCount; if (encryptionSubsampleDataBuffer == null diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/mp4/Sniffer.java b/library/src/main/java/com/google/android/exoplayer2/extractor/mp4/Sniffer.java index 842b648ec2..44d5824945 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/mp4/Sniffer.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/mp4/Sniffer.java @@ -100,13 +100,14 @@ import java.io.IOException; while (bytesSearched < bytesToSearch) { // Read an atom header. int headerSize = Atom.HEADER_SIZE; + buffer.reset(headerSize); input.peekFully(buffer.data, 0, headerSize); - buffer.setPosition(0); long atomSize = buffer.readUnsignedInt(); int atomType = buffer.readInt(); if (atomSize == Atom.LONG_SIZE_PREFIX) { headerSize = Atom.LONG_HEADER_SIZE; input.peekFully(buffer.data, Atom.HEADER_SIZE, Atom.LONG_HEADER_SIZE - Atom.HEADER_SIZE); + buffer.setLimit(Atom.LONG_HEADER_SIZE); atomSize = buffer.readUnsignedLongToLong(); } @@ -139,9 +140,7 @@ import java.io.IOException; if (atomDataSize < 8) { return false; } - if (buffer.capacity() < atomDataSize) { - buffer.reset(new byte[atomDataSize], atomDataSize); - } + buffer.reset(atomDataSize); input.peekFully(buffer.data, 0, atomDataSize); int brandsCount = atomDataSize / 4; for (int i = 0; i < brandsCount; i++) { diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/PsExtractor.java b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/PsExtractor.java index ff86be4165..d69a2a1e96 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/PsExtractor.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/PsExtractor.java @@ -153,8 +153,7 @@ public final class PsExtractor implements Extractor { input.peekFully(psPacketBuffer.data, 0, 10); // We only care about the pack_stuffing_length in here, skip the first 77 bits. - psPacketBuffer.setPosition(0); - psPacketBuffer.skipBytes(9); + psPacketBuffer.setPosition(9); // Last 3 bits is the length. int packStuffingLength = psPacketBuffer.readUnsignedByte() & 0x07; @@ -209,7 +208,7 @@ public final class PsExtractor implements Extractor { } } - // The next 2 bytes are the length, once we have that we can consume the complete packet. + // The next 2 bytes are the length. Once we have that we can consume the complete packet. input.peekFully(psPacketBuffer.data, 0, 2); psPacketBuffer.setPosition(0); int payloadLength = psPacketBuffer.readUnsignedShort(); @@ -219,14 +218,10 @@ public final class PsExtractor implements Extractor { // Just skip this data. input.skipFully(pesLength); } else { - if (psPacketBuffer.capacity() < pesLength) { - // Reallocate for this and future packets. - psPacketBuffer.reset(new byte[pesLength], pesLength); - } + psPacketBuffer.reset(pesLength); // Read the whole packet and the header for consumption. input.readFully(psPacketBuffer.data, 0, pesLength); psPacketBuffer.setPosition(6); - psPacketBuffer.setLimit(pesLength); payloadReader.consume(psPacketBuffer); psPacketBuffer.setLimit(psPacketBuffer.capacity()); } diff --git a/library/src/main/java/com/google/android/exoplayer2/util/ParsableByteArray.java b/library/src/main/java/com/google/android/exoplayer2/util/ParsableByteArray.java index a499dc8012..e140484531 100644 --- a/library/src/main/java/com/google/android/exoplayer2/util/ParsableByteArray.java +++ b/library/src/main/java/com/google/android/exoplayer2/util/ParsableByteArray.java @@ -35,17 +35,17 @@ public final class ParsableByteArray { public ParsableByteArray() {} /** - * Creates a new instance with {@code length} bytes. + * Creates a new instance with {@code limit} bytes and sets the limit. * - * @param length The length of the array. + * @param limit The limit to set. */ - public ParsableByteArray(int length) { - this.data = new byte[length]; - limit = data.length; + public ParsableByteArray(int limit) { + this.data = new byte[limit]; + this.limit = limit; } /** - * Creates a new instance wrapping {@code data}. + * Creates a new instance wrapping {@code data}, and sets the limit to {@code data.length}. * * @param data The array to wrap. */ @@ -58,7 +58,7 @@ public final class ParsableByteArray { * Creates a new instance that wraps an existing array. * * @param data The data to wrap. - * @param limit The limit. + * @param limit The limit to set. */ public ParsableByteArray(byte[] data, int limit) { this.data = data; @@ -79,7 +79,7 @@ public final class ParsableByteArray { * Updates the instance to wrap {@code data}, and resets the position to zero. * * @param data The array to wrap. - * @param limit The limit. + * @param limit The limit to set. */ public void reset(byte[] data, int limit) { this.data = data; From 59ee4341c59c78c48bed0548af7694554fbaa68b Mon Sep 17 00:00:00 2001 From: olly Date: Fri, 16 Sep 2016 10:47:19 -0700 Subject: [PATCH 06/36] Fix buffering issue for live streams Issue: #1825 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=133401980 --- .../com/google/android/exoplayer2/ExoPlayerImpl.java | 12 ++---------- .../android/exoplayer2/ExoPlayerImplInternal.java | 2 +- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/library/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java b/library/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java index f4b21aac04..6ced2315f9 100644 --- a/library/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java +++ b/library/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java @@ -243,16 +243,8 @@ import java.util.concurrent.CopyOnWriteArraySet; if (timeline == null || pendingSeekAcks > 0) { return maskingWindowPositionMs; } else { - int periodIndex = playbackInfo.periodIndex; - timeline.getPeriod(periodIndex, period); - int windowIndex = period.windowIndex; - timeline.getWindow(windowIndex, window); - if (window.firstPeriodIndex == periodIndex && window.lastPeriodIndex == periodIndex - && window.getPositionInFirstPeriodUs() == 0 - && window.getDurationUs() == period.getDurationUs()) { - return C.usToMs(playbackInfo.bufferedPositionUs); - } - return getCurrentPosition(); + timeline.getPeriod(playbackInfo.periodIndex, period); + return period.getPositionInWindowMs() + C.usToMs(playbackInfo.bufferedPositionUs); } } diff --git a/library/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java b/library/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java index 8cb15a0271..00520b2400 100644 --- a/library/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java +++ b/library/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java @@ -1071,7 +1071,7 @@ import java.io.IOException; long nextLoadPositionUs = loadingPeriodHolder.mediaPeriod.getNextLoadPositionUs(); if (nextLoadPositionUs != C.TIME_END_OF_SOURCE) { long loadingPeriodPositionUs = rendererPositionUs - - loadingPeriodHolder.rendererPositionOffsetUs + loadingPeriodHolder.startPositionUs; + - loadingPeriodHolder.rendererPositionOffsetUs; long bufferedDurationUs = nextLoadPositionUs - loadingPeriodPositionUs; boolean continueLoading = loadControl.shouldContinueLoading(bufferedDurationUs); setIsLoading(continueLoading); From e20d7034c710cc3470bc483ffc7fa514cce97b36 Mon Sep 17 00:00:00 2001 From: mgersh Date: Wed, 21 Sep 2016 11:00:38 -0700 Subject: [PATCH 07/36] Switch to Cronet 55_0_2860_0 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=133850026 --- .../android/exoplayer2/ext/cronet/CronetDataSourceTest.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/extensions/cronet/src/androidTest/java/com/google/android/exoplayer2/ext/cronet/CronetDataSourceTest.java b/extensions/cronet/src/androidTest/java/com/google/android/exoplayer2/ext/cronet/CronetDataSourceTest.java index fca73af98b..f2364ac257 100644 --- a/extensions/cronet/src/androidTest/java/com/google/android/exoplayer2/ext/cronet/CronetDataSourceTest.java +++ b/extensions/cronet/src/androidTest/java/com/google/android/exoplayer2/ext/cronet/CronetDataSourceTest.java @@ -100,7 +100,8 @@ public final class CronetDataSourceTest { Executor executor, int priority, Collection connectionAnnotations, boolean disableCache, - boolean disableConnectionMigration); + boolean disableConnectionMigration, + boolean allowDirectExecutor); } @Mock @@ -143,6 +144,7 @@ public final class CronetDataSourceTest { anyInt(), eq(Collections.emptyList()), any(Boolean.class), + any(Boolean.class), any(Boolean.class))).thenReturn(mockUrlRequest); mockStatusResponse(); @@ -194,6 +196,7 @@ public final class CronetDataSourceTest { anyInt(), eq(Collections.emptyList()), any(Boolean.class), + any(Boolean.class), any(Boolean.class))).thenReturn(mockUrlRequest2); doAnswer(new Answer() { @Override @@ -224,6 +227,7 @@ public final class CronetDataSourceTest { anyInt(), eq(Collections.emptyList()), any(Boolean.class), + any(Boolean.class), any(Boolean.class)); verify(mockUrlRequest).start(); } From 1546da899bcba212fc269ad823802528e5f20eb5 Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Thu, 22 Sep 2016 02:53:01 -0700 Subject: [PATCH 08/36] Use @IntDef where possible. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=133932812 --- .../exoplayer2/demo/PlayerActivity.java | 8 +- library/build.gradle | 1 + .../java/com/google/android/exoplayer2/C.java | 127 +++++++++++++----- .../exoplayer2/ExoPlaybackException.java | 13 +- .../com/google/android/exoplayer2/Format.java | 58 ++++---- .../android/exoplayer2/audio/AudioTrack.java | 15 ++- .../android/exoplayer2/decoder/Buffer.java | 9 +- .../exoplayer2/decoder/CryptoInfo.java | 4 +- .../decoder/DecoderInputBuffer.java | 15 ++- .../android/exoplayer2/drm/DrmSession.java | 10 ++ .../drm/StreamingDrmSessionManager.java | 2 + .../drm/UnsupportedDrmException.java | 15 ++- .../extractor/DefaultTrackOutput.java | 7 +- .../extractor/DummyTrackOutput.java | 3 +- .../exoplayer2/extractor/TrackOutput.java | 3 +- .../extractor/mkv/MatroskaExtractor.java | 9 +- .../exoplayer2/extractor/mp4/AtomParsers.java | 4 +- .../extractor/mp4/FragmentedMp4Extractor.java | 33 +++-- .../exoplayer2/extractor/mp4/Track.java | 12 +- .../extractor/mp4/TrackSampleTable.java | 3 +- .../exoplayer2/extractor/ts/H262Reader.java | 2 +- .../exoplayer2/extractor/ts/H264Reader.java | 2 +- .../exoplayer2/extractor/ts/H265Reader.java | 2 +- .../exoplayer2/extractor/ts/TsExtractor.java | 13 +- .../exoplayer2/extractor/wav/WavHeader.java | 4 +- .../extractor/wav/WavHeaderReader.java | 2 +- .../mediacodec/MediaCodecRenderer.java | 4 +- .../exoplayer2/source/MergingMediaSource.java | 13 +- .../source/chunk/ChunkExtractorWrapper.java | 4 +- .../source/chunk/InitializationChunk.java | 3 +- .../exoplayer2/source/hls/HlsChunkSource.java | 2 +- .../source/hls/playlist/HlsPlaylist.java | 13 +- .../hls/playlist/HlsPlaylistParser.java | 11 +- .../exoplayer2/text/CaptionStyleCompat.java | 21 ++- .../google/android/exoplayer2/text/Cue.java | 24 +++- .../exoplayer2/text/ttml/TtmlRegion.java | 3 +- .../exoplayer2/text/ttml/TtmlStyle.java | 48 +++++-- .../text/webvtt/WebvttCssStyle.java | 43 ++++-- .../exoplayer2/text/webvtt/WebvttCue.java | 5 +- .../trackselection/DefaultTrackSelector.java | 8 +- .../exoplayer2/ui/SubtitlePainter.java | 4 + .../android/exoplayer2/upstream/DataSpec.java | 18 ++- .../exoplayer2/upstream/HttpDataSource.java | 16 ++- .../upstream/cache/CacheDataSource.java | 17 ++- .../google/android/exoplayer2/util/Util.java | 35 ++--- .../exoplayer2/testutil/FakeTrackOutput.java | 3 +- 46 files changed, 459 insertions(+), 212 deletions(-) diff --git a/demo/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java b/demo/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java index eaa4d9971f..0607c296c6 100644 --- a/demo/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java +++ b/demo/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java @@ -316,15 +316,15 @@ public class PlayerActivity extends Activity implements OnClickListener, ExoPlay int type = Util.inferContentType(!TextUtils.isEmpty(overrideExtension) ? "." + overrideExtension : uri.getLastPathSegment()); switch (type) { - case Util.TYPE_SS: + case C.TYPE_SS: return new SsMediaSource(uri, buildDataSourceFactory(false), new DefaultSsChunkSource.Factory(mediaDataSourceFactory), mainHandler, eventLogger); - case Util.TYPE_DASH: + case C.TYPE_DASH: return new DashMediaSource(uri, buildDataSourceFactory(false), new DefaultDashChunkSource.Factory(mediaDataSourceFactory), mainHandler, eventLogger); - case Util.TYPE_HLS: + case C.TYPE_HLS: return new HlsMediaSource(uri, mediaDataSourceFactory, mainHandler, eventLogger); - case Util.TYPE_OTHER: + case C.TYPE_OTHER: return new ExtractorMediaSource(uri, mediaDataSourceFactory, new DefaultExtractorsFactory(), mainHandler, eventLogger); default: { diff --git a/library/build.gradle b/library/build.gradle index d301e0903e..3c7a633f49 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -55,6 +55,7 @@ dependencies { androidTestCompile 'com.google.dexmaker:dexmaker:1.2' androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.2' androidTestCompile 'org.mockito:mockito-core:1.9.5' + compile 'com.android.support:support-annotations:24.2.0' } android.libraryVariants.all { variant -> diff --git a/library/src/main/java/com/google/android/exoplayer2/C.java b/library/src/main/java/com/google/android/exoplayer2/C.java index 85813b391a..8c69524e95 100644 --- a/library/src/main/java/com/google/android/exoplayer2/C.java +++ b/library/src/main/java/com/google/android/exoplayer2/C.java @@ -17,8 +17,11 @@ package com.google.android.exoplayer2; import android.media.AudioFormat; import android.media.MediaCodec; +import android.support.annotation.IntDef; import android.view.Surface; import com.google.android.exoplayer2.util.Util; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.UUID; /** @@ -70,55 +73,79 @@ public final class C { */ public static final String UTF8_NAME = "UTF-8"; + /** + * Crypto modes for a codec. + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({CRYPTO_MODE_UNENCRYPTED, CRYPTO_MODE_AES_CTR, CRYPTO_MODE_AES_CBC}) + public @interface CryptoMode {} + /** + * @see MediaCodec#CRYPTO_MODE_UNENCRYPTED + */ + @SuppressWarnings("InlinedApi") + public static final int CRYPTO_MODE_UNENCRYPTED = MediaCodec.CRYPTO_MODE_UNENCRYPTED; /** * @see MediaCodec#CRYPTO_MODE_AES_CTR */ @SuppressWarnings("InlinedApi") public static final int CRYPTO_MODE_AES_CTR = MediaCodec.CRYPTO_MODE_AES_CTR; + /** + * @see MediaCodec#CRYPTO_MODE_AES_CBC + */ + @SuppressWarnings("InlinedApi") + public static final int CRYPTO_MODE_AES_CBC = MediaCodec.CRYPTO_MODE_AES_CBC; + /** + * Represents an audio encoding, or an invalid or unset value. + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({Format.NO_VALUE, ENCODING_INVALID, ENCODING_PCM_8BIT, ENCODING_PCM_16BIT, + ENCODING_PCM_24BIT, ENCODING_PCM_32BIT, ENCODING_AC3, ENCODING_E_AC3, ENCODING_DTS, + ENCODING_DTS_HD}) + public @interface Encoding {} + + /** + * Represents a PCM audio encoding, or an invalid or unset value. + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({Format.NO_VALUE, ENCODING_INVALID, ENCODING_PCM_8BIT, ENCODING_PCM_16BIT, + ENCODING_PCM_24BIT, ENCODING_PCM_32BIT}) + public @interface PcmEncoding {} /** * @see AudioFormat#ENCODING_INVALID */ public static final int ENCODING_INVALID = AudioFormat.ENCODING_INVALID; - /** * @see AudioFormat#ENCODING_PCM_8BIT */ public static final int ENCODING_PCM_8BIT = AudioFormat.ENCODING_PCM_8BIT; - /** * @see AudioFormat#ENCODING_PCM_16BIT */ public static final int ENCODING_PCM_16BIT = AudioFormat.ENCODING_PCM_16BIT; - /** * PCM encoding with 24 bits per sample. */ public static final int ENCODING_PCM_24BIT = 0x80000000; - /** * PCM encoding with 32 bits per sample. */ public static final int ENCODING_PCM_32BIT = 0x40000000; - /** * @see AudioFormat#ENCODING_AC3 */ @SuppressWarnings("InlinedApi") public static final int ENCODING_AC3 = AudioFormat.ENCODING_AC3; - /** * @see AudioFormat#ENCODING_E_AC3 */ @SuppressWarnings("InlinedApi") public static final int ENCODING_E_AC3 = AudioFormat.ENCODING_E_AC3; - /** * @see AudioFormat#ENCODING_DTS */ @SuppressWarnings("InlinedApi") public static final int ENCODING_DTS = AudioFormat.ENCODING_DTS; - /** * @see AudioFormat#ENCODING_DTS_HD */ @@ -132,48 +159,93 @@ public final class C { public static final int CHANNEL_OUT_7POINT1_SURROUND = Util.SDK_INT < 23 ? AudioFormat.CHANNEL_OUT_7POINT1 : AudioFormat.CHANNEL_OUT_7POINT1_SURROUND; + /** + * Flags which can apply to a buffer containing a media sample. + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, value = {BUFFER_FLAG_KEY_FRAME, BUFFER_FLAG_END_OF_STREAM, + BUFFER_FLAG_ENCRYPTED, BUFFER_FLAG_DECODE_ONLY}) + public @interface BufferFlags {} /** * Indicates that a buffer holds a synchronization sample. */ @SuppressWarnings("InlinedApi") public static final int BUFFER_FLAG_KEY_FRAME = MediaCodec.BUFFER_FLAG_KEY_FRAME; - /** * Flag for empty buffers that signal that the end of the stream was reached. */ @SuppressWarnings("InlinedApi") public static final int BUFFER_FLAG_END_OF_STREAM = MediaCodec.BUFFER_FLAG_END_OF_STREAM; - /** * Indicates that a buffer is (at least partially) encrypted. */ public static final int BUFFER_FLAG_ENCRYPTED = 0x40000000; - /** * Indicates that a buffer should be decoded but not rendered. */ public static final int BUFFER_FLAG_DECODE_ONLY = 0x80000000; + /** + * Track selection flags. + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, value = {SELECTION_FLAG_DEFAULT, SELECTION_FLAG_FORCED, + SELECTION_FLAG_AUTOSELECT}) + public @interface SelectionFlags {} + /** + * Indicates that the track should be selected if user preferences do not state otherwise. + */ + public static final int SELECTION_FLAG_DEFAULT = 1; + /** + * Indicates that the track must be displayed. Only applies to text tracks. + */ + public static final int SELECTION_FLAG_FORCED = 2; + /** + * Indicates that the player may choose to play the track in absence of an explicit user + * preference. + */ + public static final int SELECTION_FLAG_AUTOSELECT = 4; + + /** + * Represents a streaming or other media type. + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({TYPE_DASH, TYPE_SS, TYPE_HLS, TYPE_OTHER}) + public @interface ContentType {} + /** + * Value returned by {@link Util#inferContentType(String)} for DASH manifests. + */ + public static final int TYPE_DASH = 0; + /** + * Value returned by {@link Util#inferContentType(String)} for Smooth Streaming manifests. + */ + public static final int TYPE_SS = 1; + /** + * Value returned by {@link Util#inferContentType(String)} for HLS manifests. + */ + public static final int TYPE_HLS = 2; + /** + * Value returned by {@link Util#inferContentType(String)} for files other than DASH, HLS or + * Smooth Streaming manifests. + */ + public static final int TYPE_OTHER = 3; + /** * A return value for methods where the end of an input was encountered. */ public static final int RESULT_END_OF_INPUT = -1; - /** * A return value for methods where the length of parsed data exceeds the maximum length allowed. */ public static final int RESULT_MAX_LENGTH_EXCEEDED = -2; - /** * A return value for methods where nothing was read. */ public static final int RESULT_NOTHING_READ = -3; - /** * A return value for methods where a buffer was read. */ public static final int RESULT_BUFFER_READ = -4; - /** * A return value for methods where a format was read. */ @@ -183,32 +255,26 @@ public final class C { * A data type constant for data of unknown or unspecified type. */ public static final int DATA_TYPE_UNKNOWN = 0; - /** * A data type constant for media, typically containing media samples. */ public static final int DATA_TYPE_MEDIA = 1; - /** * A data type constant for media, typically containing only initialization data. */ public static final int DATA_TYPE_MEDIA_INITIALIZATION = 2; - /** * A data type constant for drm or encryption data. */ public static final int DATA_TYPE_DRM = 3; - /** * A data type constant for a manifest file. */ public static final int DATA_TYPE_MANIFEST = 4; - /** * A data type constant for time synchronization data. */ public static final int DATA_TYPE_TIME_SYNCHRONIZATION = 5; - /** * Applications or extensions may define custom {@code DATA_TYPE_*} constants greater than or * equal to this value. @@ -219,32 +285,26 @@ public final class C { * A type constant for tracks of unknown type. */ public static final int TRACK_TYPE_UNKNOWN = -1; - /** * A type constant for tracks of some default type, where the type itself is unknown. */ public static final int TRACK_TYPE_DEFAULT = 0; - /** * A type constant for audio tracks. */ public static final int TRACK_TYPE_AUDIO = 1; - /** * A type constant for video tracks. */ public static final int TRACK_TYPE_VIDEO = 2; - /** * A type constant for text tracks. */ public static final int TRACK_TYPE_TEXT = 3; - /** * A type constant for metadata tracks. */ public static final int TRACK_TYPE_METADATA = 4; - /** * Applications or extensions may define custom {@code TRACK_TYPE_*} constants greater than or * equal to this value. @@ -255,27 +315,22 @@ public final class C { * A selection reason constant for selections whose reasons are unknown or unspecified. */ public static final int SELECTION_REASON_UNKNOWN = 0; - /** * A selection reason constant for an initial track selection. */ public static final int SELECTION_REASON_INITIAL = 1; - /** * A selection reason constant for an manual (i.e. user initiated) track selection. */ public static final int SELECTION_REASON_MANUAL = 2; - /** * A selection reason constant for an adaptive track selection. */ public static final int SELECTION_REASON_ADAPTIVE = 3; - /** * A selection reason constant for a trick play track selection. */ public static final int SELECTION_REASON_TRICK_PLAY = 4; - /** * Applications or extensions may define custom {@code SELECTION_REASON_*} constants greater than * or equal to this value. @@ -363,16 +418,20 @@ public final class C { */ public static final int MSG_CUSTOM_BASE = 10000; + /** + * The stereo mode for 360/3D/VR videos. + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({Format.NO_VALUE, STEREO_MODE_MONO, STEREO_MODE_TOP_BOTTOM, STEREO_MODE_LEFT_RIGHT}) + public @interface StereoMode {} /** * Indicates Monoscopic stereo layout, used with 360/3D/VR videos. */ public static final int STEREO_MODE_MONO = 0; - /** * Indicates Top-Bottom stereo layout, used with 360/3D/VR videos. */ public static final int STEREO_MODE_TOP_BOTTOM = 1; - /** * Indicates Left-Right stereo layout, used with 360/3D/VR videos. */ diff --git a/library/src/main/java/com/google/android/exoplayer2/ExoPlaybackException.java b/library/src/main/java/com/google/android/exoplayer2/ExoPlaybackException.java index 8f55451712..72ac72e981 100644 --- a/library/src/main/java/com/google/android/exoplayer2/ExoPlaybackException.java +++ b/library/src/main/java/com/google/android/exoplayer2/ExoPlaybackException.java @@ -15,15 +15,24 @@ */ package com.google.android.exoplayer2; +import android.support.annotation.IntDef; import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.util.Assertions; import java.io.IOException; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; /** * Thrown when a non-recoverable playback failure occurs. */ public final class ExoPlaybackException extends Exception { + /** + * The type of source that produced the error. + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({TYPE_SOURCE, TYPE_RENDERER, TYPE_UNEXPECTED}) + public @interface Type {} /** * The error occurred loading data from a {@link MediaSource}. *

@@ -47,6 +56,7 @@ public final class ExoPlaybackException extends Exception { * The type of the playback failure. One of {@link #TYPE_SOURCE}, {@link #TYPE_RENDERER} and * {@link #TYPE_UNEXPECTED}. */ + @Type public final int type; /** @@ -85,7 +95,8 @@ public final class ExoPlaybackException extends Exception { return new ExoPlaybackException(TYPE_UNEXPECTED, null, cause, C.INDEX_UNSET); } - private ExoPlaybackException(int type, String message, Throwable cause, int rendererIndex) { + private ExoPlaybackException(@Type int type, String message, Throwable cause, + int rendererIndex) { super(message, cause); this.type = type; this.rendererIndex = rendererIndex; diff --git a/library/src/main/java/com/google/android/exoplayer2/Format.java b/library/src/main/java/com/google/android/exoplayer2/Format.java index 9cfe019ef4..50112d5a0a 100644 --- a/library/src/main/java/com/google/android/exoplayer2/Format.java +++ b/library/src/main/java/com/google/android/exoplayer2/Format.java @@ -39,20 +39,6 @@ public final class Format implements Parcelable { */ public static final int NO_VALUE = -1; - /** - * Indicates that the track should be selected if user preferences do not state otherwise. - */ - public static final int SELECTION_FLAG_DEFAULT = 1; - /** - * Indicates that the track must be displayed. Only applies to text tracks. - */ - public static final int SELECTION_FLAG_FORCED = 2; - /** - * Indicates that the player may choose to play the track in absence of an explicit user - * preference. - */ - public static final int SELECTION_FLAG_AUTOSELECT = 4; - /** * A value for {@link #subsampleOffsetUs} to indicate that subsample timestamps are relative to * the timestamps of their parent samples. @@ -131,6 +117,7 @@ public final class Format implements Parcelable { * modes are {@link C#STEREO_MODE_MONO}, {@link C#STEREO_MODE_TOP_BOTTOM}, {@link * C#STEREO_MODE_LEFT_RIGHT}. */ + @C.StereoMode public final int stereoMode; /** * The projection data for 360/VR video, or null if not applicable. @@ -153,6 +140,7 @@ public final class Format implements Parcelable { * {@link C#ENCODING_PCM_24BIT} and {@link C#ENCODING_PCM_32BIT}. Set to {@link #NO_VALUE} for * other media types. */ + @C.PcmEncoding public final int pcmEncoding; /** * The number of samples to trim from the start of the decoded audio stream. @@ -177,6 +165,7 @@ public final class Format implements Parcelable { /** * Track selection flags. */ + @C.SelectionFlags public final int selectionFlags; /** @@ -218,7 +207,7 @@ public final class Format implements Parcelable { public static Format createVideoSampleFormat(String id, String sampleMimeType, String codecs, int bitrate, int maxInputSize, int width, int height, float frameRate, List initializationData, int rotationDegrees, float pixelWidthHeightRatio, - byte[] projectionData, int stereoMode, DrmInitData drmInitData) { + byte[] projectionData, @C.StereoMode int stereoMode, DrmInitData drmInitData) { return new Format(id, null, sampleMimeType, codecs, bitrate, maxInputSize, width, height, frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData, stereoMode, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, 0, null, OFFSET_SAMPLE_RELATIVE, initializationData, @@ -229,7 +218,7 @@ public final class Format implements Parcelable { public static Format createAudioContainerFormat(String id, String containerMimeType, String sampleMimeType, String codecs, int bitrate, int channelCount, int sampleRate, - List initializationData, int selectionFlags, String language) { + List initializationData, @C.SelectionFlags int selectionFlags, String language) { return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, channelCount, sampleRate, NO_VALUE, NO_VALUE, NO_VALUE, selectionFlags, language, OFFSET_SAMPLE_RELATIVE, initializationData, @@ -238,25 +227,26 @@ public final class Format implements Parcelable { public static Format createAudioSampleFormat(String id, String sampleMimeType, String codecs, int bitrate, int maxInputSize, int channelCount, int sampleRate, - List initializationData, DrmInitData drmInitData, int selectionFlags, - String language) { + List initializationData, DrmInitData drmInitData, + @C.SelectionFlags int selectionFlags, String language) { return createAudioSampleFormat(id, sampleMimeType, codecs, bitrate, maxInputSize, channelCount, sampleRate, NO_VALUE, initializationData, drmInitData, selectionFlags, language); } public static Format createAudioSampleFormat(String id, String sampleMimeType, String codecs, - int bitrate, int maxInputSize, int channelCount, int sampleRate, int pcmEncoding, - List initializationData, DrmInitData drmInitData, int selectionFlags, - String language) { + int bitrate, int maxInputSize, int channelCount, int sampleRate, + @C.PcmEncoding int pcmEncoding, List initializationData, DrmInitData drmInitData, + @C.SelectionFlags int selectionFlags, String language) { return createAudioSampleFormat(id, sampleMimeType, codecs, bitrate, maxInputSize, channelCount, sampleRate, pcmEncoding, NO_VALUE, NO_VALUE, initializationData, drmInitData, selectionFlags, language); } public static Format createAudioSampleFormat(String id, String sampleMimeType, String codecs, - int bitrate, int maxInputSize, int channelCount, int sampleRate, int pcmEncoding, - int encoderDelay, int encoderPadding, List initializationData, - DrmInitData drmInitData, int selectionFlags, String language) { + int bitrate, int maxInputSize, int channelCount, int sampleRate, + @C.PcmEncoding int pcmEncoding, int encoderDelay, int encoderPadding, + List initializationData, DrmInitData drmInitData, + @C.SelectionFlags int selectionFlags, String language) { return new Format(id, null, sampleMimeType, codecs, bitrate, maxInputSize, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, channelCount, sampleRate, pcmEncoding, encoderDelay, encoderPadding, selectionFlags, language, OFFSET_SAMPLE_RELATIVE, @@ -266,20 +256,21 @@ public final class Format implements Parcelable { // Text. public static Format createTextContainerFormat(String id, String containerMimeType, - String sampleMimeType, String codecs, int bitrate, int selectionFlags, String language) { + String sampleMimeType, String codecs, int bitrate, @C.SelectionFlags int selectionFlags, + String language) { return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, selectionFlags, language, OFFSET_SAMPLE_RELATIVE, null, null); } public static Format createTextSampleFormat(String id, String sampleMimeType, String codecs, - int bitrate, int selectionFlags, String language, DrmInitData drmInitData) { + int bitrate, @C.SelectionFlags int selectionFlags, String language, DrmInitData drmInitData) { return createTextSampleFormat(id, sampleMimeType, codecs, bitrate, selectionFlags, language, drmInitData, OFFSET_SAMPLE_RELATIVE); } public static Format createTextSampleFormat(String id, String sampleMimeType, String codecs, - int bitrate, int selectionFlags, String language, DrmInitData drmInitData, + int bitrate, @C.SelectionFlags int selectionFlags, String language, DrmInitData drmInitData, long subsampleOffsetUs) { return new Format(id, null, sampleMimeType, codecs, bitrate, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, @@ -313,10 +304,10 @@ public final class Format implements Parcelable { /* package */ Format(String id, String containerMimeType, String sampleMimeType, String codecs, int bitrate, int maxInputSize, int width, int height, float frameRate, int rotationDegrees, - float pixelWidthHeightRatio, byte[] projectionData, int stereoMode, int channelCount, - int sampleRate, int pcmEncoding, int encoderDelay, int encoderPadding, int selectionFlags, - String language, long subsampleOffsetUs, List initializationData, - DrmInitData drmInitData) { + float pixelWidthHeightRatio, byte[] projectionData, @C.StereoMode int stereoMode, + int channelCount, int sampleRate, @C.PcmEncoding int pcmEncoding, int encoderDelay, + int encoderPadding, @C.SelectionFlags int selectionFlags, String language, + long subsampleOffsetUs, List initializationData, DrmInitData drmInitData) { this.id = id; this.containerMimeType = containerMimeType; this.sampleMimeType = sampleMimeType; @@ -343,6 +334,7 @@ public final class Format implements Parcelable { this.drmInitData = drmInitData; } + @SuppressWarnings("ResourceType") /* package */ Format(Parcel in) { id = in.readString(); containerMimeType = in.readString(); @@ -389,7 +381,7 @@ public final class Format implements Parcelable { } public Format copyWithContainerInfo(String id, int bitrate, int width, int height, - int selectionFlags, String language) { + @C.SelectionFlags int selectionFlags, String language) { return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, maxInputSize, width, height, frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData, stereoMode, channelCount, sampleRate, pcmEncoding, encoderDelay, encoderPadding, @@ -402,7 +394,7 @@ public final class Format implements Parcelable { String codecs = this.codecs == null ? manifestFormat.codecs : this.codecs; int bitrate = this.bitrate == NO_VALUE ? manifestFormat.bitrate : this.bitrate; float frameRate = this.frameRate == NO_VALUE ? manifestFormat.frameRate : this.frameRate; - int selectionFlags = this.selectionFlags | manifestFormat.selectionFlags; + @C.SelectionFlags int selectionFlags = this.selectionFlags | manifestFormat.selectionFlags; String language = this.language == null ? manifestFormat.language : this.language; DrmInitData drmInitData = (preferManifestDrmInitData && manifestFormat.drmInitData != null) || this.drmInitData == null ? manifestFormat.drmInitData : this.drmInitData; diff --git a/library/src/main/java/com/google/android/exoplayer2/audio/AudioTrack.java b/library/src/main/java/com/google/android/exoplayer2/audio/AudioTrack.java index 02fa2b8f85..87f6546e1f 100644 --- a/library/src/main/java/com/google/android/exoplayer2/audio/AudioTrack.java +++ b/library/src/main/java/com/google/android/exoplayer2/audio/AudioTrack.java @@ -208,7 +208,9 @@ public final class AudioTrack { private android.media.AudioTrack audioTrack; private int sampleRate; private int channelConfig; + @C.Encoding private int sourceEncoding; + @C.Encoding private int targetEncoding; private boolean passthrough; private int pcmFrameSize; @@ -348,8 +350,8 @@ public final class AudioTrack { * @param specifiedBufferSize A specific size for the playback buffer in bytes, or 0 to infer a * suitable buffer size automatically. */ - public void configure(String mimeType, int channelCount, int sampleRate, int pcmEncoding, - int specifiedBufferSize) { + public void configure(String mimeType, int channelCount, int sampleRate, + @C.PcmEncoding int pcmEncoding, int specifiedBufferSize) { int channelConfig; switch (channelCount) { case 1: @@ -381,7 +383,7 @@ public final class AudioTrack { } boolean passthrough = !MimeTypes.AUDIO_RAW.equals(mimeType); - int sourceEncoding; + @C.Encoding int sourceEncoding; if (passthrough) { sourceEncoding = getEncodingForMimeType(mimeType); } else if (pcmEncoding == C.ENCODING_PCM_8BIT || pcmEncoding == C.ENCODING_PCM_16BIT @@ -470,7 +472,7 @@ public final class AudioTrack { if (keepSessionIdAudioTrack == null) { int sampleRate = 4000; // Equal to private android.media.AudioTrack.MIN_SAMPLE_RATE. int channelConfig = AudioFormat.CHANNEL_OUT_MONO; - int encoding = C.ENCODING_PCM_16BIT; + @C.PcmEncoding int encoding = C.ENCODING_PCM_16BIT; int bufferSize = 2; // Use a two byte buffer, as it is not actually used for playback. keepSessionIdAudioTrack = new android.media.AudioTrack(streamType, sampleRate, channelConfig, encoding, bufferSize, android.media.AudioTrack.MODE_STATIC, sessionId); @@ -962,7 +964,7 @@ public final class AudioTrack { * @return The 16-bit PCM output. Different to the out parameter if null was passed, or if the * capacity was insufficient for the output. */ - private static ByteBuffer resampleTo16BitPcm(ByteBuffer buffer, int sourceEncoding, + private static ByteBuffer resampleTo16BitPcm(ByteBuffer buffer, @C.PcmEncoding int sourceEncoding, ByteBuffer out) { int offset = buffer.position(); int limit = buffer.limit(); @@ -1023,6 +1025,7 @@ public final class AudioTrack { return resampledBuffer; } + @C.Encoding private static int getEncodingForMimeType(String mimeType) { switch (mimeType) { case MimeTypes.AUDIO_AC3: @@ -1038,7 +1041,7 @@ public final class AudioTrack { } } - private static int getFramesPerEncodedSample(int encoding, ByteBuffer buffer) { + private static int getFramesPerEncodedSample(@C.Encoding int encoding, ByteBuffer buffer) { if (encoding == C.ENCODING_DTS || encoding == C.ENCODING_DTS_HD) { return DtsUtil.parseDtsAudioSampleCount(buffer); } else if (encoding == C.ENCODING_AC3) { diff --git a/library/src/main/java/com/google/android/exoplayer2/decoder/Buffer.java b/library/src/main/java/com/google/android/exoplayer2/decoder/Buffer.java index cb408714f4..773959fbfc 100644 --- a/library/src/main/java/com/google/android/exoplayer2/decoder/Buffer.java +++ b/library/src/main/java/com/google/android/exoplayer2/decoder/Buffer.java @@ -22,6 +22,7 @@ import com.google.android.exoplayer2.C; */ public abstract class Buffer { + @C.BufferFlags private int flags; /** @@ -58,7 +59,7 @@ public abstract class Buffer { * @param flags The flags to set, which should be a combination of the {@code C.BUFFER_FLAG_*} * constants. */ - public final void setFlags(int flags) { + public final void setFlags(@C.BufferFlags int flags) { this.flags = flags; } @@ -68,7 +69,7 @@ public abstract class Buffer { * @param flag The flag to add to this buffer's flags, which should be one of the * {@code C.BUFFER_FLAG_*} constants. */ - public final void addFlag(int flag) { + public final void addFlag(@C.BufferFlags int flag) { flags |= flag; } @@ -77,7 +78,7 @@ public abstract class Buffer { * * @param flag The flag to remove. */ - public final void clearFlag(int flag) { + public final void clearFlag(@C.BufferFlags int flag) { flags &= ~flag; } @@ -87,7 +88,7 @@ public abstract class Buffer { * @param flag The flag to check. * @return Whether the flag is set. */ - protected final boolean getFlag(int flag) { + protected final boolean getFlag(@C.BufferFlags int flag) { return (flags & flag) == flag; } diff --git a/library/src/main/java/com/google/android/exoplayer2/decoder/CryptoInfo.java b/library/src/main/java/com/google/android/exoplayer2/decoder/CryptoInfo.java index bde46738fe..866e421acc 100644 --- a/library/src/main/java/com/google/android/exoplayer2/decoder/CryptoInfo.java +++ b/library/src/main/java/com/google/android/exoplayer2/decoder/CryptoInfo.java @@ -16,6 +16,7 @@ package com.google.android.exoplayer2.decoder; import android.annotation.TargetApi; +import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.util.Util; /** @@ -34,6 +35,7 @@ public final class CryptoInfo { /** * @see android.media.MediaCodec.CryptoInfo#mode */ + @C.CryptoMode public int mode; /** * @see android.media.MediaCodec.CryptoInfo#numBytesOfClearData @@ -58,7 +60,7 @@ public final class CryptoInfo { * @see android.media.MediaCodec.CryptoInfo#set(int, int[], int[], byte[], byte[], int) */ public void set(int numSubSamples, int[] numBytesOfClearData, int[] numBytesOfEncryptedData, - byte[] key, byte[] iv, int mode) { + byte[] key, byte[] iv, @C.CryptoMode int mode) { this.numSubSamples = numSubSamples; this.numBytesOfClearData = numBytesOfClearData; this.numBytesOfEncryptedData = numBytesOfEncryptedData; diff --git a/library/src/main/java/com/google/android/exoplayer2/decoder/DecoderInputBuffer.java b/library/src/main/java/com/google/android/exoplayer2/decoder/DecoderInputBuffer.java index f34861a0e0..b76f3e8d0c 100644 --- a/library/src/main/java/com/google/android/exoplayer2/decoder/DecoderInputBuffer.java +++ b/library/src/main/java/com/google/android/exoplayer2/decoder/DecoderInputBuffer.java @@ -15,7 +15,10 @@ */ package com.google.android.exoplayer2.decoder; +import android.support.annotation.IntDef; import com.google.android.exoplayer2.C; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.nio.ByteBuffer; /** @@ -23,16 +26,21 @@ import java.nio.ByteBuffer; */ public class DecoderInputBuffer extends Buffer { + /** + * The buffer replacement mode, which may disable replacement. + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({BUFFER_REPLACEMENT_MODE_DISABLED, BUFFER_REPLACEMENT_MODE_NORMAL, + BUFFER_REPLACEMENT_MODE_DIRECT}) + public @interface BufferReplacementMode {} /** * Disallows buffer replacement. */ public static final int BUFFER_REPLACEMENT_MODE_DISABLED = 0; - /** * Allows buffer replacement using {@link ByteBuffer#allocate(int)}. */ public static final int BUFFER_REPLACEMENT_MODE_NORMAL = 1; - /** * Allows buffer replacement using {@link ByteBuffer#allocateDirect(int)}. */ @@ -53,6 +61,7 @@ public class DecoderInputBuffer extends Buffer { */ public long timeUs; + @BufferReplacementMode private final int bufferReplacementMode; /** @@ -60,7 +69,7 @@ public class DecoderInputBuffer extends Buffer { * of {@link #BUFFER_REPLACEMENT_MODE_DISABLED}, {@link #BUFFER_REPLACEMENT_MODE_NORMAL} and * {@link #BUFFER_REPLACEMENT_MODE_DIRECT}. */ - public DecoderInputBuffer(int bufferReplacementMode) { + public DecoderInputBuffer(@BufferReplacementMode int bufferReplacementMode) { this.cryptoInfo = new CryptoInfo(); this.bufferReplacementMode = bufferReplacementMode; } diff --git a/library/src/main/java/com/google/android/exoplayer2/drm/DrmSession.java b/library/src/main/java/com/google/android/exoplayer2/drm/DrmSession.java index 8f0ff961a9..6f84395072 100644 --- a/library/src/main/java/com/google/android/exoplayer2/drm/DrmSession.java +++ b/library/src/main/java/com/google/android/exoplayer2/drm/DrmSession.java @@ -16,6 +16,9 @@ package com.google.android.exoplayer2.drm; import android.annotation.TargetApi; +import android.support.annotation.IntDef; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; /** * A DRM session. @@ -23,6 +26,12 @@ import android.annotation.TargetApi; @TargetApi(16) public interface DrmSession { + /** + * The state of the DRM session. + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({STATE_ERROR, STATE_CLOSED, STATE_OPENING, STATE_OPENED, STATE_OPENED_WITH_KEYS}) + @interface State {} /** * The session has encountered an error. {@link #getError()} can be used to retrieve the cause. */ @@ -50,6 +59,7 @@ public interface DrmSession { * @return One of {@link #STATE_ERROR}, {@link #STATE_CLOSED}, {@link #STATE_OPENING}, * {@link #STATE_OPENED} and {@link #STATE_OPENED_WITH_KEYS}. */ + @State int getState(); /** diff --git a/library/src/main/java/com/google/android/exoplayer2/drm/StreamingDrmSessionManager.java b/library/src/main/java/com/google/android/exoplayer2/drm/StreamingDrmSessionManager.java index 8428b0cdf9..2882111ac9 100644 --- a/library/src/main/java/com/google/android/exoplayer2/drm/StreamingDrmSessionManager.java +++ b/library/src/main/java/com/google/android/exoplayer2/drm/StreamingDrmSessionManager.java @@ -87,6 +87,7 @@ public class StreamingDrmSessionManager implements Drm private int openCount; private boolean provisioningInProgress; + @DrmSession.State private int state; private T mediaCrypto; private Exception lastException; @@ -291,6 +292,7 @@ public class StreamingDrmSessionManager implements Drm // DrmSession implementation. @Override + @DrmSession.State public final int getState() { return state; } diff --git a/library/src/main/java/com/google/android/exoplayer2/drm/UnsupportedDrmException.java b/library/src/main/java/com/google/android/exoplayer2/drm/UnsupportedDrmException.java index d1b70279ce..505750efaa 100644 --- a/library/src/main/java/com/google/android/exoplayer2/drm/UnsupportedDrmException.java +++ b/library/src/main/java/com/google/android/exoplayer2/drm/UnsupportedDrmException.java @@ -15,11 +15,21 @@ */ package com.google.android.exoplayer2.drm; +import android.support.annotation.IntDef; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * Thrown when the requested DRM scheme is not supported. */ public final class UnsupportedDrmException extends Exception { + /** + * The reason for the exception. + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({REASON_UNSUPPORTED_SCHEME, REASON_INSTANTIATION_ERROR}) + public @interface Reason {} /** * The requested DRM scheme is unsupported by the device. */ @@ -33,12 +43,13 @@ public final class UnsupportedDrmException extends Exception { /** * Either {@link #REASON_UNSUPPORTED_SCHEME} or {@link #REASON_INSTANTIATION_ERROR}. */ + @Reason public final int reason; /** * @param reason {@link #REASON_UNSUPPORTED_SCHEME} or {@link #REASON_INSTANTIATION_ERROR}. */ - public UnsupportedDrmException(int reason) { + public UnsupportedDrmException(@Reason int reason) { this.reason = reason; } @@ -46,7 +57,7 @@ public final class UnsupportedDrmException extends Exception { * @param reason {@link #REASON_UNSUPPORTED_SCHEME} or {@link #REASON_INSTANTIATION_ERROR}. * @param cause The cause of this exception. */ - public UnsupportedDrmException(int reason, Exception cause) { + public UnsupportedDrmException(@Reason int reason, Exception cause) { super(cause); this.reason = reason; } diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/DefaultTrackOutput.java b/library/src/main/java/com/google/android/exoplayer2/extractor/DefaultTrackOutput.java index f7493a8b8a..cb9e41aa62 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/DefaultTrackOutput.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/DefaultTrackOutput.java @@ -496,7 +496,8 @@ public final class DefaultTrackOutput implements TrackOutput { } @Override - public void sampleMetadata(long timeUs, int flags, int size, int offset, byte[] encryptionKey) { + public void sampleMetadata(long timeUs, @C.BufferFlags int flags, int size, int offset, + byte[] encryptionKey) { if (!startWriteOperation()) { infoQueue.commitSampleTimestamp(timeUs); return; @@ -836,8 +837,8 @@ public final class DefaultTrackOutput implements TrackOutput { } } - public synchronized void commitSample(long timeUs, int sampleFlags, long offset, int size, - byte[] encryptionKey) { + public synchronized void commitSample(long timeUs, @C.BufferFlags int sampleFlags, long offset, + int size, byte[] encryptionKey) { Assertions.checkState(!upstreamFormatRequired); commitSampleTimestamp(timeUs); timesUs[relativeWriteIndex] = timeUs; diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/DummyTrackOutput.java b/library/src/main/java/com/google/android/exoplayer2/extractor/DummyTrackOutput.java index e317dd589c..61f97887be 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/DummyTrackOutput.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/DummyTrackOutput.java @@ -50,7 +50,8 @@ public final class DummyTrackOutput implements TrackOutput { } @Override - public void sampleMetadata(long timeUs, int flags, int size, int offset, byte[] encryptionKey) { + public void sampleMetadata(long timeUs, @C.BufferFlags int flags, int size, int offset, + byte[] encryptionKey) { // Do nothing. } diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/TrackOutput.java b/library/src/main/java/com/google/android/exoplayer2/extractor/TrackOutput.java index 0ce807ebb1..c4dee4b6a7 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/TrackOutput.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/TrackOutput.java @@ -72,6 +72,7 @@ public interface TrackOutput { * whose metadata is being passed. * @param encryptionKey The encryption key associated with the sample. May be null. */ - void sampleMetadata(long timeUs, int flags, int size, int offset, byte[] encryptionKey); + void sampleMetadata(long timeUs, @C.BufferFlags int flags, int size, int offset, + byte[] encryptionKey); } diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/mkv/MatroskaExtractor.java b/library/src/main/java/com/google/android/exoplayer2/extractor/mkv/MatroskaExtractor.java index 917af9c7e1..2d84e73c1e 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/mkv/MatroskaExtractor.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/mkv/MatroskaExtractor.java @@ -264,6 +264,7 @@ public final class MatroskaExtractor implements Extractor { private int[] blockLacingSampleSizes; private int blockTrackNumber; private int blockTrackNumberLength; + @C.BufferFlags private int blockFlags; // Sample reading state. @@ -1314,7 +1315,7 @@ public final class MatroskaExtractor implements Extractor { public void initializeOutput(ExtractorOutput output, int trackId) throws ParserException { String mimeType; int maxInputSize = Format.NO_VALUE; - int pcmEncoding = Format.NO_VALUE; + @C.PcmEncoding int pcmEncoding = Format.NO_VALUE; List initializationData = null; switch (codecId) { case CODEC_ID_VP8: @@ -1429,9 +1430,9 @@ public final class MatroskaExtractor implements Extractor { } Format format; - int selectionFlags = 0; - selectionFlags |= flagDefault ? Format.SELECTION_FLAG_DEFAULT : 0; - selectionFlags |= flagForced ? Format.SELECTION_FLAG_FORCED : 0; + @C.SelectionFlags int selectionFlags = 0; + selectionFlags |= flagDefault ? C.SELECTION_FLAG_DEFAULT : 0; + selectionFlags |= flagForced ? C.SELECTION_FLAG_FORCED : 0; // TODO: Consider reading the name elements of the tracks and, if present, incorporating them // into the trackId passed when creating the formats. if (MimeTypes.isAudio(mimeType)) { diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java b/library/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java index 11a25fe419..87be7029c9 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java @@ -665,6 +665,7 @@ import java.util.List; List initializationData = null; String mimeType = null; byte[] projectionData = null; + @C.StereoMode int stereoMode = Format.NO_VALUE; while (childPosition - position < size) { parent.setPosition(childPosition); @@ -889,7 +890,7 @@ import java.util.List; if (out.format == null && mimeType != null) { // TODO: Determine the correct PCM encoding. - int pcmEncoding = + @C.PcmEncoding int pcmEncoding = MimeTypes.AUDIO_RAW.equals(mimeType) ? C.ENCODING_PCM_16BIT : Format.NO_VALUE; out.format = Format.createAudioSampleFormat(Integer.toString(trackId), mimeType, null, Format.NO_VALUE, Format.NO_VALUE, channelCount, sampleRate, pcmEncoding, @@ -1169,6 +1170,7 @@ import java.util.List; public Format format; public int nalUnitLengthFieldLength; + @Track.Transformation public int requiredSampleTransformation; public StsdData(int numberOfEntries) { diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java b/library/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java index 103acdd6f9..71f9231a31 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java @@ -15,6 +15,7 @@ */ package com.google.android.exoplayer2.extractor.mp4; +import android.support.annotation.IntDef; import android.util.Log; import android.util.Pair; import android.util.SparseArray; @@ -38,6 +39,8 @@ import com.google.android.exoplayer2.util.NalUnitUtil; import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.Util; import java.io.IOException; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -64,6 +67,13 @@ public final class FragmentedMp4Extractor implements Extractor { private static final String TAG = "FragmentedMp4Extractor"; private static final int SAMPLE_GROUP_TYPE_seig = Util.getIntegerCodeForString("seig"); + /** + * Flags controlling the behavior of the extractor. + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, value = {FLAG_WORKAROUND_EVERY_VIDEO_FRAME_IS_SYNC_FRAME, + FLAG_WORKAROUND_IGNORE_TFDT_BOX, FLAG_SIDELOADED}) + public @interface Flags {} /** * Flag to work around an issue in some video streams where every frame is marked as a sync frame. * The workaround overrides the sync frame flags in the stream, forcing them to false except for @@ -72,12 +82,10 @@ public final class FragmentedMp4Extractor implements Extractor { * This flag does nothing if the stream is not a video stream. */ public static final int FLAG_WORKAROUND_EVERY_VIDEO_FRAME_IS_SYNC_FRAME = 1; - /** * Flag to ignore any tfdt boxes in the stream. */ public static final int FLAG_WORKAROUND_IGNORE_TFDT_BOX = 2; - /** * Flag to indicate that the {@link Track} was sideloaded, instead of being declared by the MP4 * container. @@ -95,6 +103,7 @@ public final class FragmentedMp4Extractor implements Extractor { private static final int STATE_READING_SAMPLE_CONTINUE = 4; // Workarounds. + @Flags private final int flags; private final Track sideloadedTrack; @@ -135,18 +144,18 @@ public final class FragmentedMp4Extractor implements Extractor { } /** - * @param flags Flags to allow parsing of faulty streams. + * @param flags Flags that control the extractor's behavior. */ - public FragmentedMp4Extractor(int flags) { + public FragmentedMp4Extractor(@Flags int flags) { this(flags, null); } /** - * @param flags Flags to allow parsing of faulty streams. + * @param flags Flags that control the extractor's behavior. * @param sideloadedTrack Sideloaded track information, in the case that the extractor * will not receive a moov box in the input data. */ - public FragmentedMp4Extractor(int flags, Track sideloadedTrack) { + public FragmentedMp4Extractor(@Flags int flags, Track sideloadedTrack) { this.sideloadedTrack = sideloadedTrack; this.flags = flags | (sideloadedTrack != null ? FLAG_SIDELOADED : 0); atomHeader = new ParsableByteArray(Atom.LONG_HEADER_SIZE); @@ -422,7 +431,7 @@ public final class FragmentedMp4Extractor implements Extractor { } private static void parseMoof(ContainerAtom moof, SparseArray trackBundleArray, - int flags, byte[] extendedTypeScratch) throws ParserException { + @Flags int flags, byte[] extendedTypeScratch) throws ParserException { int moofContainerChildrenSize = moof.containerChildren.size(); for (int i = 0; i < moofContainerChildrenSize; i++) { Atom.ContainerAtom child = moof.containerChildren.get(i); @@ -437,7 +446,7 @@ public final class FragmentedMp4Extractor implements Extractor { * Parses a traf atom (defined in 14496-12). */ private static void parseTraf(ContainerAtom traf, SparseArray trackBundleArray, - int flags, byte[] extendedTypeScratch) throws ParserException { + @Flags int flags, byte[] extendedTypeScratch) throws ParserException { LeafAtom tfhd = traf.getLeafAtomOfType(Atom.TYPE_tfhd); TrackBundle trackBundle = parseTfhd(tfhd.data, trackBundleArray, flags); if (trackBundle == null) { @@ -488,7 +497,7 @@ public final class FragmentedMp4Extractor implements Extractor { } private static void parseTruns(ContainerAtom traf, TrackBundle trackBundle, long decodeTime, - int flags) { + @Flags int flags) { int trunCount = 0; int totalSampleCount = 0; List leafChildren = traf.leafChildren; @@ -643,8 +652,8 @@ public final class FragmentedMp4Extractor implements Extractor { * @param trun The trun atom to decode. * @return The starting position of samples for the next run. */ - private static int parseTrun(TrackBundle trackBundle, int index, long decodeTime, int flags, - ParsableByteArray trun, int trackRunStart) { + private static int parseTrun(TrackBundle trackBundle, int index, long decodeTime, + @Flags int flags, ParsableByteArray trun, int trackRunStart) { trun.setPosition(Atom.HEADER_SIZE); int fullAtom = trun.readInt(); int atomFlags = Atom.parseFullAtomFlags(fullAtom); @@ -994,7 +1003,7 @@ public final class FragmentedMp4Extractor implements Extractor { } long sampleTimeUs = fragment.getSamplePresentationTime(sampleIndex) * 1000L; - int sampleFlags = (fragment.definesEncryptionData ? C.BUFFER_FLAG_ENCRYPTED : 0) + @C.BufferFlags int sampleFlags = (fragment.definesEncryptionData ? C.BUFFER_FLAG_ENCRYPTED : 0) | (fragment.sampleIsSyncFrameTable[sampleIndex] ? C.BUFFER_FLAG_KEY_FRAME : 0); int sampleDescriptionIndex = fragment.header.sampleDescriptionIndex; byte[] encryptionKey = null; diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/mp4/Track.java b/library/src/main/java/com/google/android/exoplayer2/extractor/mp4/Track.java index 5d48529794..c723704d37 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/mp4/Track.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/mp4/Track.java @@ -15,14 +15,23 @@ */ package com.google.android.exoplayer2.extractor.mp4; +import android.support.annotation.IntDef; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; /** * Encapsulates information describing an MP4 track. */ public final class Track { + /** + * The transformation to apply to samples in the track, if any. + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({TRANSFORMATION_NONE, TRANSFORMATION_CEA608_CDAT}) + public @interface Transformation {} /** * A no-op sample transformation. */ @@ -66,6 +75,7 @@ public final class Track { * One of {@code TRANSFORMATION_*}. Defines the transformation to apply before outputting each * sample. */ + @Transformation public final int sampleTransformation; /** @@ -90,7 +100,7 @@ public final class Track { public final int nalUnitLengthFieldLength; public Track(int id, int type, long timescale, long movieTimescale, long durationUs, - Format format, int sampleTransformation, + Format format, @Transformation int sampleTransformation, TrackEncryptionBox[] sampleDescriptionEncryptionBoxes, int nalUnitLengthFieldLength, long[] editListDurations, long[] editListMediaTimes) { this.id = id; diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/mp4/TrackSampleTable.java b/library/src/main/java/com/google/android/exoplayer2/extractor/mp4/TrackSampleTable.java index 54e3fcd957..cf479eaf3e 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/mp4/TrackSampleTable.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/mp4/TrackSampleTable.java @@ -49,7 +49,8 @@ import com.google.android.exoplayer2.util.Util; */ public final int[] flags; - TrackSampleTable(long[] offsets, int[] sizes, int maximumSize, long[] timestampsUs, int[] flags) { + public TrackSampleTable(long[] offsets, int[] sizes, int maximumSize, long[] timestampsUs, + int[] flags) { Assertions.checkArgument(sizes.length == timestampsUs.length); Assertions.checkArgument(offsets.length == timestampsUs.length); Assertions.checkArgument(flags.length == timestampsUs.length); diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/H262Reader.java b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/H262Reader.java index a898ff598c..f74f771bb7 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/H262Reader.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/H262Reader.java @@ -128,7 +128,7 @@ import java.util.Collections; if (hasOutputFormat && (startCodeValue == START_GROUP || startCodeValue == START_PICTURE)) { int bytesWrittenPastStartCode = limit - startCodeOffset; if (foundFirstFrameInGroup) { - int flags = isKeyframe ? C.BUFFER_FLAG_KEY_FRAME : 0; + @C.BufferFlags int flags = isKeyframe ? C.BUFFER_FLAG_KEY_FRAME : 0; int size = (int) (totalBytesWritten - framePosition) - bytesWrittenPastStartCode; output.sampleMetadata(frameTimeUs, flags, size, bytesWrittenPastStartCode, null); isKeyframe = false; diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/H264Reader.java b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/H264Reader.java index 8bb72f6e79..993ec4f872 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/H264Reader.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/H264Reader.java @@ -420,7 +420,7 @@ import java.util.List; } private void outputSample(int offset) { - int flags = sampleIsKeyframe ? C.BUFFER_FLAG_KEY_FRAME : 0; + @C.BufferFlags int flags = sampleIsKeyframe ? C.BUFFER_FLAG_KEY_FRAME : 0; int size = (int) (nalUnitStartPosition - samplePosition); output.sampleMetadata(sampleTimeUs, flags, size, offset, null); } diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/H265Reader.java b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/H265Reader.java index d6c431edec..e7ca3cc625 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/H265Reader.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/H265Reader.java @@ -471,7 +471,7 @@ import java.util.Collections; } private void outputSample(int offset) { - int flags = sampleIsKeyframe ? C.BUFFER_FLAG_KEY_FRAME : 0; + @C.BufferFlags int flags = sampleIsKeyframe ? C.BUFFER_FLAG_KEY_FRAME : 0; int size = (int) (nalUnitStartPosition - samplePosition); output.sampleMetadata(sampleTimeUs, flags, size, offset, null); } diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/TsExtractor.java b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/TsExtractor.java index 5c9e8cf673..7c6a13bbff 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/TsExtractor.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/TsExtractor.java @@ -15,6 +15,7 @@ */ package com.google.android.exoplayer2.extractor.ts; +import android.support.annotation.IntDef; import android.util.Log; import android.util.SparseArray; import android.util.SparseBooleanArray; @@ -32,6 +33,8 @@ import com.google.android.exoplayer2.util.ParsableBitArray; import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.Util; import java.io.IOException; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; /** * Facilitates the extraction of data from the MPEG-2 TS container format. @@ -50,6 +53,13 @@ public final class TsExtractor implements Extractor { }; + /** + * Flags controlling what workarounds are enabled for the extractor. + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, value = {WORKAROUND_ALLOW_NON_IDR_KEYFRAMES, WORKAROUND_IGNORE_AAC_STREAM, + WORKAROUND_IGNORE_H264_STREAM, WORKAROUND_DETECT_ACCESS_UNITS, WORKAROUND_MAP_BY_TYPE}) + public @interface WorkaroundFlags {} public static final int WORKAROUND_ALLOW_NON_IDR_KEYFRAMES = 1; public static final int WORKAROUND_IGNORE_AAC_STREAM = 2; public static final int WORKAROUND_IGNORE_H264_STREAM = 4; @@ -83,6 +93,7 @@ public final class TsExtractor implements Extractor { private static final int BUFFER_SIZE = TS_PACKET_SIZE * BUFFER_PACKET_COUNT; private final TimestampAdjuster timestampAdjuster; + @WorkaroundFlags private final int workaroundFlags; private final ParsableByteArray tsPacketBuffer; private final ParsableBitArray tsScratch; @@ -103,7 +114,7 @@ public final class TsExtractor implements Extractor { this(timestampAdjuster, 0); } - public TsExtractor(TimestampAdjuster timestampAdjuster, int workaroundFlags) { + public TsExtractor(TimestampAdjuster timestampAdjuster, @WorkaroundFlags int workaroundFlags) { this.timestampAdjuster = timestampAdjuster; this.workaroundFlags = workaroundFlags; tsPacketBuffer = new ParsableByteArray(BUFFER_SIZE); diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/wav/WavHeader.java b/library/src/main/java/com/google/android/exoplayer2/extractor/wav/WavHeader.java index b5e2268b53..a57060f604 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/wav/WavHeader.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/wav/WavHeader.java @@ -31,6 +31,7 @@ import com.google.android.exoplayer2.C; /** Bits per sample for the audio data. */ private final int bitsPerSample; /** The PCM encoding */ + @C.PcmEncoding private final int encoding; /** Offset to the start of sample data. */ @@ -39,7 +40,7 @@ import com.google.android.exoplayer2.C; private long dataSize; public WavHeader(int numChannels, int sampleRateHz, int averageBytesPerSecond, int blockAlignment, - int bitsPerSample, int encoding) { + int bitsPerSample, @C.PcmEncoding int encoding) { this.numChannels = numChannels; this.sampleRateHz = sampleRateHz; this.averageBytesPerSecond = averageBytesPerSecond; @@ -99,6 +100,7 @@ import com.google.android.exoplayer2.C; } /** Returns the PCM encoding. **/ + @C.PcmEncoding public int getEncoding() { return encoding; } diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/wav/WavHeaderReader.java b/library/src/main/java/com/google/android/exoplayer2/extractor/wav/WavHeaderReader.java index 36fbcf40af..0e99380a1c 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/wav/WavHeaderReader.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/wav/WavHeaderReader.java @@ -87,7 +87,7 @@ import java.io.IOException; + blockAlignment); } - int encoding = Util.getPcmEncoding(bitsPerSample); + @C.PcmEncoding int encoding = Util.getPcmEncoding(bitsPerSample); if (encoding == C.ENCODING_INVALID) { Log.e(TAG, "Unsupported WAV bit depth: " + bitsPerSample); return null; diff --git a/library/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java b/library/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java index dfc37f8472..63a77e2215 100644 --- a/library/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java +++ b/library/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java @@ -293,7 +293,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { MediaCrypto mediaCrypto = null; boolean drmSessionRequiresSecureDecoder = false; if (drmSession != null) { - int drmSessionState = drmSession.getState(); + @DrmSession.State int drmSessionState = drmSession.getState(); if (drmSessionState == DrmSession.STATE_ERROR) { throw ExoPlaybackException.createForRenderer(drmSession.getError(), getIndex()); } else if (drmSessionState == DrmSession.STATE_OPENED @@ -682,7 +682,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { if (drmSession == null) { return false; } - int drmSessionState = drmSession.getState(); + @DrmSession.State int drmSessionState = drmSession.getState(); if (drmSessionState == DrmSession.STATE_ERROR) { throw ExoPlaybackException.createForRenderer(drmSession.getError(), getIndex()); } diff --git a/library/src/main/java/com/google/android/exoplayer2/source/MergingMediaSource.java b/library/src/main/java/com/google/android/exoplayer2/source/MergingMediaSource.java index b213928516..8653a91a6d 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/MergingMediaSource.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/MergingMediaSource.java @@ -15,11 +15,14 @@ */ package com.google.android.exoplayer2.source; +import android.support.annotation.IntDef; import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.source.MediaPeriod.Callback; import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.util.Assertions; import java.io.IOException; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; @@ -36,11 +39,16 @@ public final class MergingMediaSource implements MediaSource { */ public static final class IllegalMergeException extends IOException { + /** + * The reason the merge failed. + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({REASON_WINDOWS_ARE_DYNAMIC, REASON_PERIOD_COUNT_MISMATCH}) + public @interface Reason {} /** * The merge failed because one of the sources being merged has a dynamic window. */ public static final int REASON_WINDOWS_ARE_DYNAMIC = 0; - /** * The merge failed because the sources have different period counts. */ @@ -50,13 +58,14 @@ public final class MergingMediaSource implements MediaSource { * The reason the merge failed. One of {@link #REASON_WINDOWS_ARE_DYNAMIC} and * {@link #REASON_PERIOD_COUNT_MISMATCH}. */ + @Reason public final int reason; /** * @param reason The reason the merge failed. One of {@link #REASON_WINDOWS_ARE_DYNAMIC} and * {@link #REASON_PERIOD_COUNT_MISMATCH}. */ - public IllegalMergeException(int reason) { + public IllegalMergeException(@Reason int reason) { this.reason = reason; } diff --git a/library/src/main/java/com/google/android/exoplayer2/source/chunk/ChunkExtractorWrapper.java b/library/src/main/java/com/google/android/exoplayer2/source/chunk/ChunkExtractorWrapper.java index e14daf0d20..e316215160 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/chunk/ChunkExtractorWrapper.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/chunk/ChunkExtractorWrapper.java @@ -15,6 +15,7 @@ */ package com.google.android.exoplayer2.source.chunk; +import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.drm.DrmInitData; import com.google.android.exoplayer2.extractor.Extractor; @@ -150,7 +151,8 @@ public final class ChunkExtractorWrapper implements ExtractorOutput, TrackOutput } @Override - public void sampleMetadata(long timeUs, int flags, int size, int offset, byte[] encryptionKey) { + public void sampleMetadata(long timeUs, @C.BufferFlags int flags, int size, int offset, + byte[] encryptionKey) { trackOutput.sampleMetadata(timeUs, flags, size, offset, encryptionKey); } diff --git a/library/src/main/java/com/google/android/exoplayer2/source/chunk/InitializationChunk.java b/library/src/main/java/com/google/android/exoplayer2/source/chunk/InitializationChunk.java index 945a9b7487..388dc63899 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/chunk/InitializationChunk.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/chunk/InitializationChunk.java @@ -111,7 +111,8 @@ public final class InitializationChunk extends Chunk implements SingleTrackMetad } @Override - public void sampleMetadata(long timeUs, int flags, int size, int offset, byte[] encryptionKey) { + public void sampleMetadata(long timeUs, @C.BufferFlags int flags, int size, int offset, + byte[] encryptionKey) { throw new IllegalStateException("Unexpected sample data in initialization chunk"); } diff --git a/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java b/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java index 79205ccbc6..8f2fc5a99b 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java @@ -355,7 +355,7 @@ import java.util.Locale; timestampAdjuster = timestampAdjusterProvider.getAdjuster(segment.discontinuitySequenceNumber, startTimeUs); // This flag ensures the change of pid between streams does not affect the sample queues. - int workaroundFlags = TsExtractor.WORKAROUND_MAP_BY_TYPE; + @TsExtractor.WorkaroundFlags int workaroundFlags = TsExtractor.WORKAROUND_MAP_BY_TYPE; String codecs = variants[newVariantIndex].format.codecs; if (!TextUtils.isEmpty(codecs)) { // Sometimes AAC and H264 streams are declared in TS chunks even though they don't really diff --git a/library/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylist.java b/library/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylist.java index 98f4b20723..fb62d9978e 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylist.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylist.java @@ -15,18 +15,29 @@ */ package com.google.android.exoplayer2.source.hls.playlist; +import android.support.annotation.IntDef; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * Represents an HLS playlist. */ public abstract class HlsPlaylist { + /** + * The type of playlist. + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({TYPE_MASTER, TYPE_MEDIA}) + public @interface Type {} public static final int TYPE_MASTER = 0; public static final int TYPE_MEDIA = 1; public final String baseUri; + @Type public final int type; - protected HlsPlaylist(String baseUri, int type) { + protected HlsPlaylist(String baseUri, @Type int type) { this.baseUri = baseUri; this.type = type; } diff --git a/library/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java b/library/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java index 96f69b69db..a0757c364a 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java @@ -62,7 +62,6 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser{@link #EDGE_TYPE_DEPRESSED} * */ + @EdgeType public final int edgeType; /** @@ -126,8 +133,8 @@ public final class CaptionStyleCompat { * @param edgeColor See {@link #edgeColor}. * @param typeface See {@link #typeface}. */ - public CaptionStyleCompat(int foregroundColor, int backgroundColor, int windowColor, int edgeType, - int edgeColor, Typeface typeface) { + public CaptionStyleCompat(int foregroundColor, int backgroundColor, int windowColor, + @EdgeType int edgeType, int edgeColor, Typeface typeface) { this.foregroundColor = foregroundColor; this.backgroundColor = backgroundColor; this.windowColor = windowColor; @@ -137,6 +144,7 @@ public final class CaptionStyleCompat { } @TargetApi(19) + @SuppressWarnings("ResourceType") private static CaptionStyleCompat createFromCaptionStyleV19( CaptioningManager.CaptionStyle captionStyle) { return new CaptionStyleCompat( @@ -145,6 +153,7 @@ public final class CaptionStyleCompat { } @TargetApi(21) + @SuppressWarnings("ResourceType") private static CaptionStyleCompat createFromCaptionStyleV21( CaptioningManager.CaptionStyle captionStyle) { return new CaptionStyleCompat( diff --git a/library/src/main/java/com/google/android/exoplayer2/text/Cue.java b/library/src/main/java/com/google/android/exoplayer2/text/Cue.java index 01d5b53c86..93b1dc1d9a 100644 --- a/library/src/main/java/com/google/android/exoplayer2/text/Cue.java +++ b/library/src/main/java/com/google/android/exoplayer2/text/Cue.java @@ -15,7 +15,10 @@ */ package com.google.android.exoplayer2.text; +import android.support.annotation.IntDef; import android.text.Layout.Alignment; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; /** * Contains information about a specific cue, including textual content and formatting data. @@ -26,6 +29,13 @@ public class Cue { * An unset position or width. */ public static final float DIMEN_UNSET = Float.MIN_VALUE; + + /** + * The type of anchor, which may be unset. + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({TYPE_UNSET, ANCHOR_TYPE_START, ANCHOR_TYPE_MIDDLE, ANCHOR_TYPE_END}) + public @interface AnchorType {} /** * An unset anchor or line type value. */ @@ -44,6 +54,13 @@ public class Cue { * box. */ public static final int ANCHOR_TYPE_END = 2; + + /** + * The type of line, which may be unset. + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({TYPE_UNSET, LINE_TYPE_FRACTION, LINE_TYPE_NUMBER}) + public @interface LineType {} /** * Value for {@link #lineType} when {@link #line} is a fractional position. */ @@ -83,6 +100,7 @@ public class Cue { * -1). For horizontal text the size of the first line of the cue is its height, and the start * and end of the viewport are the top and bottom respectively. */ + @LineType public final int lineType; /** * The cue box anchor positioned by {@link #line}. One of {@link #ANCHOR_TYPE_START}, @@ -92,6 +110,7 @@ public class Cue { * and {@link #ANCHOR_TYPE_END} correspond to the top, middle and bottom of the cue box * respectively. */ + @AnchorType public final int lineAnchor; /** * The fractional position of the {@link #positionAnchor} of the cue box within the viewport in @@ -110,6 +129,7 @@ public class Cue { * and {@link #ANCHOR_TYPE_END} correspond to the left, middle and right of the cue box * respectively. */ + @AnchorType public final int positionAnchor; /** * The size of the cue box in the writing direction specified as a fraction of the viewport size @@ -137,8 +157,8 @@ public class Cue { * @param positionAnchor See {@link #positionAnchor}. * @param size See {@link #size}. */ - public Cue(CharSequence text, Alignment textAlignment, float line, int lineType, int lineAnchor, - float position, int positionAnchor, float size) { + public Cue(CharSequence text, Alignment textAlignment, float line, @LineType int lineType, + @AnchorType int lineAnchor, float position, @AnchorType int positionAnchor, float size) { this.text = text; this.textAlignment = textAlignment; this.line = line; diff --git a/library/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlRegion.java b/library/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlRegion.java index 75fe1154a9..5f30834b4d 100644 --- a/library/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlRegion.java +++ b/library/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlRegion.java @@ -24,6 +24,7 @@ import com.google.android.exoplayer2.text.Cue; public final float position; public final float line; + @Cue.LineType public final int lineType; public final float width; @@ -31,7 +32,7 @@ import com.google.android.exoplayer2.text.Cue; this(Cue.DIMEN_UNSET, Cue.DIMEN_UNSET, Cue.TYPE_UNSET, Cue.DIMEN_UNSET); } - public TtmlRegion(float position, float line, int lineType, float width) { + public TtmlRegion(float position, float line, @Cue.LineType int lineType, float width) { this.position = position; this.line = line; this.lineType = lineType; diff --git a/library/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlStyle.java b/library/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlStyle.java index aa87af0e57..e4c36be03a 100644 --- a/library/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlStyle.java +++ b/library/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlStyle.java @@ -16,8 +16,11 @@ package com.google.android.exoplayer2.text.ttml; import android.graphics.Typeface; +import android.support.annotation.IntDef; import android.text.Layout; import com.google.android.exoplayer2.util.Assertions; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; /** * Style object of a TtmlNode @@ -26,15 +29,25 @@ import com.google.android.exoplayer2.util.Assertions; public static final int UNSPECIFIED = -1; + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, value = {UNSPECIFIED, STYLE_NORMAL, STYLE_BOLD, STYLE_ITALIC, + STYLE_BOLD_ITALIC}) + public @interface StyleFlags {} public static final int STYLE_NORMAL = Typeface.NORMAL; public static final int STYLE_BOLD = Typeface.BOLD; public static final int STYLE_ITALIC = Typeface.ITALIC; public static final int STYLE_BOLD_ITALIC = Typeface.BOLD_ITALIC; + @Retention(RetentionPolicy.SOURCE) + @IntDef({UNSPECIFIED, FONT_SIZE_UNIT_PIXEL, FONT_SIZE_UNIT_EM, FONT_SIZE_UNIT_PERCENT}) + public @interface FontSizeUnit {} public static final int FONT_SIZE_UNIT_PIXEL = 1; public static final int FONT_SIZE_UNIT_EM = 2; public static final int FONT_SIZE_UNIT_PERCENT = 3; + @Retention(RetentionPolicy.SOURCE) + @IntDef({UNSPECIFIED, OFF, ON}) + private @interface OptionalBoolean {} private static final int OFF = 0; private static final int ON = 1; @@ -43,10 +56,15 @@ import com.google.android.exoplayer2.util.Assertions; private boolean hasFontColor; private int backgroundColor; private boolean hasBackgroundColor; + @OptionalBoolean private int linethrough; + @OptionalBoolean private int underline; + @OptionalBoolean private int bold; + @OptionalBoolean private int italic; + @FontSizeUnit private int fontSizeUnit; private float fontSize; private String id; @@ -67,12 +85,13 @@ import com.google.android.exoplayer2.util.Assertions; * @return {@link #UNSPECIFIED}, {@link #STYLE_NORMAL}, {@link #STYLE_BOLD}, {@link #STYLE_BOLD} * or {@link #STYLE_BOLD_ITALIC}. */ + @StyleFlags public int getStyle() { if (bold == UNSPECIFIED && italic == UNSPECIFIED) { return UNSPECIFIED; } - return (bold != UNSPECIFIED ? bold : STYLE_NORMAL) - | (italic != UNSPECIFIED ? italic : STYLE_NORMAL); + return (bold == ON ? STYLE_BOLD : STYLE_NORMAL) + | (italic == ON ? STYLE_ITALIC : STYLE_NORMAL); } public boolean isLinethrough() { @@ -95,6 +114,18 @@ import com.google.android.exoplayer2.util.Assertions; return this; } + public TtmlStyle setBold(boolean bold) { + Assertions.checkState(inheritableStyle == null); + this.bold = bold ? ON : OFF; + return this; + } + + public TtmlStyle setItalic(boolean italic) { + Assertions.checkState(inheritableStyle == null); + this.italic = italic ? ON : OFF; + return this; + } + public String getFontFamily() { return fontFamily; } @@ -140,18 +171,6 @@ import com.google.android.exoplayer2.util.Assertions; return hasBackgroundColor; } - public TtmlStyle setBold(boolean isBold) { - Assertions.checkState(inheritableStyle == null); - bold = isBold ? STYLE_BOLD : STYLE_NORMAL; - return this; - } - - public TtmlStyle setItalic(boolean isItalic) { - Assertions.checkState(inheritableStyle == null); - italic = isItalic ? STYLE_ITALIC : STYLE_NORMAL; - return this; - } - /** * Inherits from an ancestor style. Properties like tts:backgroundColor which * are not inheritable are not inherited as well as properties which are already set locally @@ -236,6 +255,7 @@ import com.google.android.exoplayer2.util.Assertions; return this; } + @FontSizeUnit public int getFontSizeUnit() { return fontSizeUnit; } diff --git a/library/src/main/java/com/google/android/exoplayer2/text/webvtt/WebvttCssStyle.java b/library/src/main/java/com/google/android/exoplayer2/text/webvtt/WebvttCssStyle.java index 169bc6a8bf..157174a8f0 100644 --- a/library/src/main/java/com/google/android/exoplayer2/text/webvtt/WebvttCssStyle.java +++ b/library/src/main/java/com/google/android/exoplayer2/text/webvtt/WebvttCssStyle.java @@ -16,8 +16,11 @@ package com.google.android.exoplayer2.text.webvtt; import android.graphics.Typeface; +import android.support.annotation.IntDef; import android.text.Layout; import com.google.android.exoplayer2.util.Util; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -32,15 +35,25 @@ import java.util.List; public static final int UNSPECIFIED = -1; + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, value = {UNSPECIFIED, STYLE_NORMAL, STYLE_BOLD, STYLE_ITALIC, + STYLE_BOLD_ITALIC}) + public @interface StyleFlags {} public static final int STYLE_NORMAL = Typeface.NORMAL; public static final int STYLE_BOLD = Typeface.BOLD; public static final int STYLE_ITALIC = Typeface.ITALIC; public static final int STYLE_BOLD_ITALIC = Typeface.BOLD_ITALIC; + @Retention(RetentionPolicy.SOURCE) + @IntDef({UNSPECIFIED, FONT_SIZE_UNIT_PIXEL, FONT_SIZE_UNIT_EM, FONT_SIZE_UNIT_PERCENT}) + public @interface FontSizeUnit {} public static final int FONT_SIZE_UNIT_PIXEL = 1; public static final int FONT_SIZE_UNIT_EM = 2; public static final int FONT_SIZE_UNIT_PERCENT = 3; + @Retention(RetentionPolicy.SOURCE) + @IntDef({UNSPECIFIED, OFF, ON}) + private @interface OptionalBoolean {} private static final int OFF = 0; private static final int ON = 1; @@ -56,10 +69,15 @@ import java.util.List; private boolean hasFontColor; private int backgroundColor; private boolean hasBackgroundColor; + @OptionalBoolean private int linethrough; + @OptionalBoolean private int underline; + @OptionalBoolean private int bold; + @OptionalBoolean private int italic; + @FontSizeUnit private int fontSizeUnit; private float fontSize; private Layout.Alignment textAlign; @@ -144,12 +162,13 @@ import java.util.List; * @return {@link #UNSPECIFIED}, {@link #STYLE_NORMAL}, {@link #STYLE_BOLD}, {@link #STYLE_BOLD} * or {@link #STYLE_BOLD_ITALIC}. */ + @StyleFlags public int getStyle() { if (bold == UNSPECIFIED && italic == UNSPECIFIED) { return UNSPECIFIED; } - return (bold != UNSPECIFIED ? bold : STYLE_NORMAL) - | (italic != UNSPECIFIED ? italic : STYLE_NORMAL); + return (bold == ON ? STYLE_BOLD : STYLE_NORMAL) + | (italic == ON ? STYLE_ITALIC : STYLE_NORMAL); } public boolean isLinethrough() { @@ -169,6 +188,15 @@ import java.util.List; this.underline = underline ? ON : OFF; return this; } + public WebvttCssStyle setBold(boolean bold) { + this.bold = bold ? ON : OFF; + return this; + } + + public WebvttCssStyle setItalic(boolean italic) { + this.italic = italic ? ON : OFF; + return this; + } public String getFontFamily() { return fontFamily; @@ -213,16 +241,6 @@ import java.util.List; return hasBackgroundColor; } - public WebvttCssStyle setBold(boolean isBold) { - bold = isBold ? STYLE_BOLD : STYLE_NORMAL; - return this; - } - - public WebvttCssStyle setItalic(boolean isItalic) { - italic = isItalic ? STYLE_ITALIC : STYLE_NORMAL; - return this; - } - public Layout.Alignment getTextAlign() { return textAlign; } @@ -242,6 +260,7 @@ import java.util.List; return this; } + @FontSizeUnit public int getFontSizeUnit() { return fontSizeUnit; } diff --git a/library/src/main/java/com/google/android/exoplayer2/text/webvtt/WebvttCue.java b/library/src/main/java/com/google/android/exoplayer2/text/webvtt/WebvttCue.java index f1ce0e28fc..295fdc656f 100644 --- a/library/src/main/java/com/google/android/exoplayer2/text/webvtt/WebvttCue.java +++ b/library/src/main/java/com/google/android/exoplayer2/text/webvtt/WebvttCue.java @@ -37,8 +37,9 @@ import com.google.android.exoplayer2.text.Cue; Cue.DIMEN_UNSET, Cue.TYPE_UNSET, Cue.DIMEN_UNSET); } - public WebvttCue(long startTime, long endTime, CharSequence text, Alignment textAlignment, - float line, int lineType, int lineAnchor, float position, int positionAnchor, float width) { + public WebvttCue(long startTime, long endTime, CharSequence text, Alignment textAlignment, + float line, @Cue.LineType int lineType, @Cue.AnchorType int lineAnchor, float position, + @Cue.AnchorType int positionAnchor, float width) { super(text, textAlignment, line, lineType, lineAnchor, position, positionAnchor, width); this.startTime = startTime; this.endTime = endTime; diff --git a/library/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java b/library/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java index 4f76983d15..2351da716c 100644 --- a/library/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java +++ b/library/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java @@ -442,7 +442,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) { if (isSupported(trackFormatSupport[trackIndex])) { Format format = trackGroup.getFormat(trackIndex); - boolean isDefault = (format.selectionFlags & Format.SELECTION_FLAG_DEFAULT) != 0; + boolean isDefault = (format.selectionFlags & C.SELECTION_FLAG_DEFAULT) != 0; int trackScore; if (formatHasLanguage(format, preferredAudioLanguage)) { if (isDefault) { @@ -480,8 +480,8 @@ public class DefaultTrackSelector extends MappingTrackSelector { for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) { if (isSupported(trackFormatSupport[trackIndex])) { Format format = trackGroup.getFormat(trackIndex); - boolean isDefault = (format.selectionFlags & Format.SELECTION_FLAG_DEFAULT) != 0; - boolean isForced = (format.selectionFlags & Format.SELECTION_FLAG_FORCED) != 0; + boolean isDefault = (format.selectionFlags & C.SELECTION_FLAG_DEFAULT) != 0; + boolean isForced = (format.selectionFlags & C.SELECTION_FLAG_FORCED) != 0; int trackScore; if (formatHasLanguage(format, preferredTextLanguage)) { if (isDefault) { @@ -530,7 +530,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) { if (isSupported(trackFormatSupport[trackIndex])) { Format format = trackGroup.getFormat(trackIndex); - boolean isDefault = (format.selectionFlags & Format.SELECTION_FLAG_DEFAULT) != 0; + boolean isDefault = (format.selectionFlags & C.SELECTION_FLAG_DEFAULT) != 0; int trackScore = isDefault ? 2 : 1; if (trackScore > selectedTrackScore) { selectedGroup = trackGroup; diff --git a/library/src/main/java/com/google/android/exoplayer2/ui/SubtitlePainter.java b/library/src/main/java/com/google/android/exoplayer2/ui/SubtitlePainter.java index cb4eec40f1..8c3ac77cb2 100644 --- a/library/src/main/java/com/google/android/exoplayer2/ui/SubtitlePainter.java +++ b/library/src/main/java/com/google/android/exoplayer2/ui/SubtitlePainter.java @@ -66,9 +66,12 @@ import com.google.android.exoplayer2.util.Util; private CharSequence cueText; private Alignment cueTextAlignment; private float cueLine; + @Cue.LineType private int cueLineType; + @Cue.AnchorType private int cueLineAnchor; private float cuePosition; + @Cue.AnchorType private int cuePositionAnchor; private float cueSize; private boolean applyEmbeddedStyles; @@ -76,6 +79,7 @@ import com.google.android.exoplayer2.util.Util; private int backgroundColor; private int windowColor; private int edgeColor; + @CaptionStyleCompat.EdgeType private int edgeType; private float textSizePx; private float bottomPaddingFraction; diff --git a/library/src/main/java/com/google/android/exoplayer2/upstream/DataSpec.java b/library/src/main/java/com/google/android/exoplayer2/upstream/DataSpec.java index 5a469e1c90..d251446976 100644 --- a/library/src/main/java/com/google/android/exoplayer2/upstream/DataSpec.java +++ b/library/src/main/java/com/google/android/exoplayer2/upstream/DataSpec.java @@ -16,8 +16,11 @@ package com.google.android.exoplayer2.upstream; import android.net.Uri; +import android.support.annotation.IntDef; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.util.Assertions; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.Arrays; /** @@ -25,6 +28,12 @@ import java.util.Arrays; */ public final class DataSpec { + /** + * The flags that apply to any request for data. + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, value = {FLAG_ALLOW_GZIP}) + public @interface Flags {} /** * Permits an underlying network stack to request that the server use gzip compression. *

@@ -69,6 +78,7 @@ public final class DataSpec { /** * Request flags. Currently {@link #FLAG_ALLOW_GZIP} is the only supported flag. */ + @Flags public final int flags; /** @@ -86,7 +96,7 @@ public final class DataSpec { * @param uri {@link #uri}. * @param flags {@link #flags}. */ - public DataSpec(Uri uri, int flags) { + public DataSpec(Uri uri, @Flags int flags) { this(uri, 0, C.LENGTH_UNSET, null, flags); } @@ -111,7 +121,7 @@ public final class DataSpec { * @param key {@link #key}. * @param flags {@link #flags}. */ - public DataSpec(Uri uri, long absoluteStreamPosition, long length, String key, int flags) { + public DataSpec(Uri uri, long absoluteStreamPosition, long length, String key, @Flags int flags) { this(uri, absoluteStreamPosition, absoluteStreamPosition, length, key, flags); } @@ -127,7 +137,7 @@ public final class DataSpec { * @param flags {@link #flags}. */ public DataSpec(Uri uri, long absoluteStreamPosition, long position, long length, String key, - int flags) { + @Flags int flags) { this(uri, null, absoluteStreamPosition, position, length, key, flags); } @@ -144,7 +154,7 @@ public final class DataSpec { * @param flags {@link #flags}. */ public DataSpec(Uri uri, byte[] postBody, long absoluteStreamPosition, long position, long length, - String key, int flags) { + String key, @Flags int flags) { Assertions.checkArgument(absoluteStreamPosition >= 0); Assertions.checkArgument(position >= 0); Assertions.checkArgument(length > 0 || length == C.LENGTH_UNSET); diff --git a/library/src/main/java/com/google/android/exoplayer2/upstream/HttpDataSource.java b/library/src/main/java/com/google/android/exoplayer2/upstream/HttpDataSource.java index b96fe515fc..f915ee4e24 100644 --- a/library/src/main/java/com/google/android/exoplayer2/upstream/HttpDataSource.java +++ b/library/src/main/java/com/google/android/exoplayer2/upstream/HttpDataSource.java @@ -15,10 +15,13 @@ */ package com.google.android.exoplayer2.upstream; +import android.support.annotation.IntDef; import android.text.TextUtils; import com.google.android.exoplayer2.util.Predicate; import com.google.android.exoplayer2.util.Util; import java.io.IOException; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.List; import java.util.Map; @@ -57,10 +60,14 @@ public interface HttpDataSource extends DataSource { */ class HttpDataSourceException extends IOException { + @Retention(RetentionPolicy.SOURCE) + @IntDef({TYPE_OPEN, TYPE_READ, TYPE_CLOSE}) + public @interface Type {} public static final int TYPE_OPEN = 1; public static final int TYPE_READ = 2; public static final int TYPE_CLOSE = 3; + @Type public final int type; /** @@ -68,25 +75,26 @@ public interface HttpDataSource extends DataSource { */ public final DataSpec dataSpec; - public HttpDataSourceException(DataSpec dataSpec, int type) { + public HttpDataSourceException(DataSpec dataSpec, @Type int type) { super(); this.dataSpec = dataSpec; this.type = type; } - public HttpDataSourceException(String message, DataSpec dataSpec, int type) { + public HttpDataSourceException(String message, DataSpec dataSpec, @Type int type) { super(message); this.dataSpec = dataSpec; this.type = type; } - public HttpDataSourceException(IOException cause, DataSpec dataSpec, int type) { + public HttpDataSourceException(IOException cause, DataSpec dataSpec, @Type int type) { super(cause); this.dataSpec = dataSpec; this.type = type; } - public HttpDataSourceException(String message, IOException cause, DataSpec dataSpec, int type) { + public HttpDataSourceException(String message, IOException cause, DataSpec dataSpec, + @Type int type) { super(message, cause); this.dataSpec = dataSpec; this.type = type; diff --git a/library/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSource.java b/library/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSource.java index 0cd7d54564..af2555bfb8 100644 --- a/library/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSource.java +++ b/library/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSource.java @@ -16,6 +16,7 @@ package com.google.android.exoplayer2.upstream.cache; import android.net.Uri; +import android.support.annotation.IntDef; import android.util.Log; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.upstream.DataSink; @@ -27,6 +28,8 @@ import com.google.android.exoplayer2.upstream.TeeDataSource; import com.google.android.exoplayer2.upstream.cache.CacheDataSink.CacheDataSinkException; import java.io.IOException; import java.io.InterruptedIOException; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; /** * A {@link DataSource} that reads and writes a {@link Cache}. Requests are fulfilled from the cache @@ -43,6 +46,13 @@ public final class CacheDataSource implements DataSource { */ public static final long DEFAULT_MAX_CACHE_FILE_SIZE = 2 * 1024 * 1024; + /** + * Flags controlling the cache's behavior. + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, value = {FLAG_BLOCK_ON_CACHE, FLAG_IGNORE_CACHE_ON_ERROR, + FLAG_CACHE_UNBOUNDED_REQUESTS}) + public @interface Flags {} /** * A flag indicating whether we will block reads if the cache key is locked. If this flag is * set, then we will read from upstream if the cache key is locked. @@ -106,7 +116,7 @@ public final class CacheDataSource implements DataSource { * Constructs an instance with default {@link DataSource} and {@link DataSink} instances for * reading and writing the cache and with {@link #DEFAULT_MAX_CACHE_FILE_SIZE}. */ - public CacheDataSource(Cache cache, DataSource upstream, int flags) { + public CacheDataSource(Cache cache, DataSource upstream, @Flags int flags) { this(cache, upstream, flags, DEFAULT_MAX_CACHE_FILE_SIZE); } @@ -123,7 +133,8 @@ public final class CacheDataSource implements DataSource { * exceeds this value, then the data will be fragmented into multiple cache files. The * finer-grained this is the finer-grained the eviction policy can be. */ - public CacheDataSource(Cache cache, DataSource upstream, int flags, long maxCacheFileSize) { + public CacheDataSource(Cache cache, DataSource upstream, @Flags int flags, + long maxCacheFileSize) { this(cache, upstream, new FileDataSource(), new CacheDataSink(cache, maxCacheFileSize), flags, null); } @@ -142,7 +153,7 @@ public final class CacheDataSource implements DataSource { * @param eventListener An optional {@link EventListener} to receive events. */ public CacheDataSource(Cache cache, DataSource upstream, DataSource cacheReadDataSource, - DataSink cacheWriteDataSink, int flags, EventListener eventListener) { + DataSink cacheWriteDataSink, @Flags int flags, EventListener eventListener) { this.cache = cache; this.cacheReadDataSource = cacheReadDataSource; this.blockOnCache = (flags & FLAG_BLOCK_ON_CACHE) != 0; diff --git a/library/src/main/java/com/google/android/exoplayer2/util/Util.java b/library/src/main/java/com/google/android/exoplayer2/util/Util.java index 6e11c43db1..c4505fd8b9 100644 --- a/library/src/main/java/com/google/android/exoplayer2/util/Util.java +++ b/library/src/main/java/com/google/android/exoplayer2/util/Util.java @@ -88,27 +88,6 @@ public final class Util { */ public static final String MODEL = Build.MODEL; - /** - * Value returned by {@link #inferContentType(String)} for DASH manifests. - */ - public static final int TYPE_DASH = 0; - - /** - * Value returned by {@link #inferContentType(String)} for Smooth Streaming manifests. - */ - public static final int TYPE_SS = 1; - - /** - * Value returned by {@link #inferContentType(String)} for HLS manifests. - */ - public static final int TYPE_HLS = 2; - - /** - * Value returned by {@link #inferContentType(String)} for files other than DASH, HLS or Smooth - * Streaming manifests. - */ - public static final int TYPE_OTHER = 3; - private static final String TAG = "Util"; private static final Pattern XS_DATE_TIME_PATTERN = Pattern.compile( "(\\d\\d\\d\\d)\\-(\\d\\d)\\-(\\d\\d)[Tt]" @@ -712,6 +691,7 @@ public final class Util { * {@link C#ENCODING_PCM_32BIT}. If the bit depth is unsupported then * {@link C#ENCODING_INVALID} is returned. */ + @C.PcmEncoding public static int getPcmEncoding(int bitDepth) { switch (bitDepth) { case 8: @@ -731,19 +711,20 @@ public final class Util { * Makes a best guess to infer the type from a file name. * * @param fileName Name of the file. It can include the path of the file. - * @return One of {@link #TYPE_DASH}, {@link #TYPE_SS}, {@link #TYPE_HLS} or {@link #TYPE_OTHER}. + * @return The content type. */ + @C.ContentType public static int inferContentType(String fileName) { if (fileName == null) { - return TYPE_OTHER; + return C.TYPE_OTHER; } else if (fileName.endsWith(".mpd")) { - return TYPE_DASH; + return C.TYPE_DASH; } else if (fileName.endsWith(".ism") || fileName.endsWith(".isml")) { - return TYPE_SS; + return C.TYPE_SS; } else if (fileName.endsWith(".m3u8")) { - return TYPE_HLS; + return C.TYPE_HLS; } else { - return TYPE_OTHER; + return C.TYPE_OTHER; } } diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeTrackOutput.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeTrackOutput.java index a66e13cfd6..b399d79e8d 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeTrackOutput.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeTrackOutput.java @@ -88,7 +88,8 @@ public final class FakeTrackOutput implements TrackOutput, Dumper.Dumpable { } @Override - public void sampleMetadata(long timeUs, int flags, int size, int offset, byte[] encryptionKey) { + public void sampleMetadata(long timeUs, @C.BufferFlags int flags, int size, int offset, + byte[] encryptionKey) { sampleTimesUs.add(timeUs); sampleFlags.add(flags); sampleStartOffsets.add(sampleData.length - offset - size); From 57e486a44afd4d029fa9f9f2e360e1f6f9fca6ca Mon Sep 17 00:00:00 2001 From: "[]inger" <[]inger@google.com> Date: Thu, 22 Sep 2016 03:19:40 -0700 Subject: [PATCH 09/36] Reorganize test vectors ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=133935070 --- .../google/android/exoplayer2/demo/SampleChooserActivity.java | 1 + 1 file changed, 1 insertion(+) diff --git a/demo/src/main/java/com/google/android/exoplayer2/demo/SampleChooserActivity.java b/demo/src/main/java/com/google/android/exoplayer2/demo/SampleChooserActivity.java index 95d42e0532..c6feddd346 100644 --- a/demo/src/main/java/com/google/android/exoplayer2/demo/SampleChooserActivity.java +++ b/demo/src/main/java/com/google/android/exoplayer2/demo/SampleChooserActivity.java @@ -65,6 +65,7 @@ public class SampleChooserActivity extends Activity { } else { uris = new String[] { "asset:///media.exolist.json", + "https://raw.githubusercontent.com/Axinom/dash-test-vectors/master/axinom.exolist.json", }; } SampleListLoader loaderTask = new SampleListLoader(); From 06b96bfa8e32ed57ba6c7eac4c23ce63a8b69ccd Mon Sep 17 00:00:00 2001 From: olly Date: Thu, 22 Sep 2016 04:17:36 -0700 Subject: [PATCH 10/36] Upgrade gradle to 2.14.1 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=133938559 --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 04363c87a9..d0950de9ad 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME 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 From a7d5f4b0694a07bd5c5d0f9e78ead86d3270f795 Mon Sep 17 00:00:00 2001 From: olly Date: Thu, 22 Sep 2016 04:32:11 -0700 Subject: [PATCH 11/36] Automated rollback of "Upgrade gradle to 2.14.1" *** Reason for rollback *** 2.14.1 not compatible with 2.1.2. Oops. *** Original change description *** Upgrade gradle to 2.14.1 *** ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=133939282 --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index d0950de9ad..04363c87a9 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip From e1ab1e3256c0c26ae2ea95b796a4f251dbf4826a Mon Sep 17 00:00:00 2001 From: "[]inger" <[]inger@google.com> Date: Thu, 22 Sep 2016 05:50:39 -0700 Subject: [PATCH 12/36] Update test vectors ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=133944105 --- .../google/android/exoplayer2/demo/SampleChooserActivity.java | 1 - 1 file changed, 1 deletion(-) diff --git a/demo/src/main/java/com/google/android/exoplayer2/demo/SampleChooserActivity.java b/demo/src/main/java/com/google/android/exoplayer2/demo/SampleChooserActivity.java index c6feddd346..95d42e0532 100644 --- a/demo/src/main/java/com/google/android/exoplayer2/demo/SampleChooserActivity.java +++ b/demo/src/main/java/com/google/android/exoplayer2/demo/SampleChooserActivity.java @@ -65,7 +65,6 @@ public class SampleChooserActivity extends Activity { } else { uris = new String[] { "asset:///media.exolist.json", - "https://raw.githubusercontent.com/Axinom/dash-test-vectors/master/axinom.exolist.json", }; } SampleListLoader loaderTask = new SampleListLoader(); From 57a2749a9d78c2c88a0b4da5e62dca8ff1c504d4 Mon Sep 17 00:00:00 2001 From: olly Date: Thu, 22 Sep 2016 05:57:00 -0700 Subject: [PATCH 13/36] Fix playback of short duration content Issue #1837 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=133944552 --- .../exoplayer2/ExoPlayerImplInternal.java | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/library/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java b/library/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java index 00520b2400..fe555a87ec 100644 --- a/library/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java +++ b/library/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java @@ -458,12 +458,11 @@ import java.io.IOException; startRenderers(); } } - } else if (state == ExoPlayer.STATE_READY) { - if (enabledRenderers.length > 0 ? !allRenderersReadyOrEnded : !isTimelineReady) { - rebuffering = playWhenReady; - setState(ExoPlayer.STATE_BUFFERING); - stopRenderers(); - } + } else if (state == ExoPlayer.STATE_READY + && (enabledRenderers.length > 0 ? !allRenderersReadyOrEnded : !isTimelineReady)) { + rebuffering = playWhenReady; + setState(ExoPlayer.STATE_BUFFERING); + stopRenderers(); } if (state == ExoPlayer.STATE_BUFFERING) { @@ -995,10 +994,18 @@ import java.io.IOException; eventHandler.obtainMessage(MSG_POSITION_DISCONTINUITY, playbackInfo).sendToTarget(); } updateTimelineState(); + + if (readingPeriodHolder != null && readingPeriodHolder.isLast) { + readingPeriodHolder = null; + for (Renderer renderer : enabledRenderers) { + renderer.setCurrentStreamIsFinal(); + } + } if (readingPeriodHolder == null) { // The renderers have their final SampleStreams. return; } + for (Renderer renderer : enabledRenderers) { if (!renderer.hasReadStreamToEnd()) { return; @@ -1029,11 +1036,6 @@ import java.io.IOException; } } } - } else if (readingPeriodHolder.isLast) { - readingPeriodHolder = null; - for (Renderer renderer : enabledRenderers) { - renderer.setCurrentStreamIsFinal(); - } } } From f2cf086d760dfc3c0ccdc41edb628c97110075d7 Mon Sep 17 00:00:00 2001 From: falhassen Date: Thu, 22 Sep 2016 16:00:43 -0700 Subject: [PATCH 14/36] Fix content length calculation for gzipped files ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=134011959 --- .../ext/cronet/CronetDataSourceTest.java | 76 +++++++++++++------ .../ext/cronet/CronetDataSource.java | 40 +++++++--- 2 files changed, 85 insertions(+), 31 deletions(-) diff --git a/extensions/cronet/src/androidTest/java/com/google/android/exoplayer2/ext/cronet/CronetDataSourceTest.java b/extensions/cronet/src/androidTest/java/com/google/android/exoplayer2/ext/cronet/CronetDataSourceTest.java index f2364ac257..ccd4dec191 100644 --- a/extensions/cronet/src/androidTest/java/com/google/android/exoplayer2/ext/cronet/CronetDataSourceTest.java +++ b/extensions/cronet/src/androidTest/java/com/google/android/exoplayer2/ext/cronet/CronetDataSourceTest.java @@ -109,7 +109,7 @@ public final class CronetDataSourceTest { @Mock private Predicate mockContentTypePredicate; @Mock - private TransferListener mockTransferListener; + private TransferListener mockTransferListener; @Mock private Clock mockClock; @Mock @@ -172,8 +172,8 @@ public final class CronetDataSourceTest { } @Test(expected = IllegalStateException.class) - public void testOpeningTwiceThrows() throws HttpDataSourceException, IllegalStateException { - mockResponesStartSuccess(); + public void testOpeningTwiceThrows() throws HttpDataSourceException { + mockResponseStartSuccess(); assertConnectionState(CronetDataSource.IDLE_CONNECTION); dataSourceUnderTest.open(testDataSpec); @@ -183,7 +183,7 @@ public final class CronetDataSourceTest { @Test public void testCallbackFromPreviousRequest() throws HttpDataSourceException { - mockResponesStartSuccess(); + mockResponseStartSuccess(); dataSourceUnderTest.open(testDataSpec); dataSourceUnderTest.close(); @@ -217,7 +217,7 @@ public final class CronetDataSourceTest { @Test public void testRequestStartCalled() throws HttpDataSourceException { - mockResponesStartSuccess(); + mockResponseStartSuccess(); dataSourceUnderTest.open(testDataSpec); verify(mockCronetEngine).createRequest( @@ -234,7 +234,7 @@ public final class CronetDataSourceTest { @Test public void testRequestHeadersSet() throws HttpDataSourceException { - mockResponesStartSuccess(); + mockResponseStartSuccess(); testDataSpec = new DataSpec(Uri.parse(TEST_URL), 1000, 5000, null); testResponseHeader.put("Content-Length", Long.toString(5000L)); @@ -252,13 +252,29 @@ public final class CronetDataSourceTest { @Test public void testRequestOpen() throws HttpDataSourceException { - mockResponesStartSuccess(); + mockResponseStartSuccess(); assertEquals(TEST_CONTENT_LENGTH, dataSourceUnderTest.open(testDataSpec)); assertConnectionState(CronetDataSource.OPEN_CONNECTION); verify(mockTransferListener).onTransferStart(dataSourceUnderTest, testDataSpec); } + + @Test + public void testRequestOpenGzippedCompressedReturnsDataSpecLength() + throws HttpDataSourceException { + testResponseHeader.put("Content-Encoding", "gzip"); + testUrlResponseInfo = createUrlResponseInfo(200); // statusCode + mockResponseStartSuccess(); + + // Data spec's requested length, 5000. Test response's length, 16,000. + testDataSpec = new DataSpec(Uri.parse(TEST_URL), 1000, 5000, null); + + assertEquals(5000 /* contentLength */, dataSourceUnderTest.open(testDataSpec)); + assertConnectionState(CronetDataSource.OPEN_CONNECTION); + verify(mockTransferListener).onTransferStart(dataSourceUnderTest, testDataSpec); + } + @Test public void testRequestOpenFail() { mockResponseStartFailure(); @@ -295,7 +311,7 @@ public final class CronetDataSourceTest { @Test public void testRequestOpenValidatesStatusCode() { - mockResponesStartSuccess(); + mockResponseStartSuccess(); testUrlResponseInfo = createUrlResponseInfo(500); // statusCode try { @@ -312,7 +328,7 @@ public final class CronetDataSourceTest { @Test public void testRequestOpenValidatesContentTypePredicate() { - mockResponesStartSuccess(); + mockResponseStartSuccess(); when(mockContentTypePredicate.evaluate(anyString())).thenReturn(false); try { @@ -329,7 +345,7 @@ public final class CronetDataSourceTest { @Test public void testRequestOpenValidatesContentLength() { - mockResponesStartSuccess(); + mockResponseStartSuccess(); // Data spec's requested length, 5000. Test response's length, 16,000. testDataSpec = new DataSpec(Uri.parse(TEST_URL), 1000, 5000, null); @@ -348,7 +364,7 @@ public final class CronetDataSourceTest { @Test public void testPostRequestOpen() throws HttpDataSourceException { - mockResponesStartSuccess(); + mockResponseStartSuccess(); dataSourceUnderTest.setRequestProperty("Content-Type", TEST_CONTENT_TYPE); assertEquals(TEST_CONTENT_LENGTH, dataSourceUnderTest.open(testPostDataSpec)); @@ -358,7 +374,7 @@ public final class CronetDataSourceTest { @Test public void testPostRequestOpenValidatesContentType() { - mockResponesStartSuccess(); + mockResponseStartSuccess(); try { dataSourceUnderTest.open(testPostDataSpec); @@ -370,7 +386,7 @@ public final class CronetDataSourceTest { @Test public void testPostRequestOpenRejects307Redirects() { - mockResponesStartSuccess(); + mockResponseStartSuccess(); mockResponseStartRedirect(); try { @@ -384,7 +400,7 @@ public final class CronetDataSourceTest { @Test public void testRequestReadTwice() throws HttpDataSourceException { - mockResponesStartSuccess(); + mockResponseStartSuccess(); mockReadSuccess(); dataSourceUnderTest.open(testDataSpec); @@ -406,7 +422,7 @@ public final class CronetDataSourceTest { @Test public void testSecondRequestNoContentLength() throws HttpDataSourceException { - mockResponesStartSuccess(); + mockResponseStartSuccess(); mockReadSuccess(); byte[] returnedBuffer = new byte[8]; @@ -437,7 +453,23 @@ public final class CronetDataSourceTest { @Test public void testReadWithOffset() throws HttpDataSourceException { - mockResponesStartSuccess(); + mockResponseStartSuccess(); + mockReadSuccess(); + + dataSourceUnderTest.open(testDataSpec); + + byte[] returnedBuffer = new byte[16]; + int bytesRead = dataSourceUnderTest.read(returnedBuffer, 8, 8); + assertArrayEquals(prefixZeros(buildTestDataArray(0, 8), 16), returnedBuffer); + assertEquals(8, bytesRead); + verify(mockTransferListener).onBytesTransferred(dataSourceUnderTest, 8); + } + + @Test + public void testReadWithUnsetLength() throws HttpDataSourceException { + testResponseHeader.remove("Content-Length"); + testUrlResponseInfo = createUrlResponseInfo(200); // statusCode + mockResponseStartSuccess(); mockReadSuccess(); dataSourceUnderTest.open(testDataSpec); @@ -451,7 +483,7 @@ public final class CronetDataSourceTest { @Test public void testReadReturnsWhatItCan() throws HttpDataSourceException { - mockResponesStartSuccess(); + mockResponseStartSuccess(); mockReadSuccess(); dataSourceUnderTest.open(testDataSpec); @@ -465,7 +497,7 @@ public final class CronetDataSourceTest { @Test public void testClosedMeansClosed() throws HttpDataSourceException { - mockResponesStartSuccess(); + mockResponseStartSuccess(); mockReadSuccess(); int bytesRead = 0; @@ -493,7 +525,7 @@ public final class CronetDataSourceTest { @Test public void testOverread() throws HttpDataSourceException { - mockResponesStartSuccess(); + mockResponseStartSuccess(); mockReadSuccess(); // Ask for 16 bytes @@ -680,7 +712,7 @@ public final class CronetDataSourceTest { @Test public void testExceptionFromTransferListener() throws HttpDataSourceException { - mockResponesStartSuccess(); + mockResponseStartSuccess(); // Make mockTransferListener throw an exception in CronetDataSource.close(). Ensure that // the subsequent open() call succeeds. @@ -699,7 +731,7 @@ public final class CronetDataSourceTest { @Test public void testReadFailure() throws HttpDataSourceException { - mockResponesStartSuccess(); + mockResponseStartSuccess(); mockReadFailure(); dataSourceUnderTest.open(testDataSpec); @@ -726,7 +758,7 @@ public final class CronetDataSourceTest { }).when(mockUrlRequest).getStatus(any(UrlRequest.StatusListener.class)); } - private void mockResponesStartSuccess() { + private void mockResponseStartSuccess() { doAnswer(new Answer() { @Override public Object answer(InvocationOnMock invocation) throws Throwable { diff --git a/extensions/cronet/src/main/java/com/google/android/exoplayer2/ext/cronet/CronetDataSource.java b/extensions/cronet/src/main/java/com/google/android/exoplayer2/ext/cronet/CronetDataSource.java index 401941addc..fe8fd13abe 100644 --- a/extensions/cronet/src/main/java/com/google/android/exoplayer2/ext/cronet/CronetDataSource.java +++ b/extensions/cronet/src/main/java/com/google/android/exoplayer2/ext/cronet/CronetDataSource.java @@ -300,15 +300,20 @@ public class CronetDataSource extends UrlRequest.Callback implements HttpDataSou try { validateResponse(info); responseInfo = info; - // Check content length. - contentLength = getContentLength(info.getAllHeaders()); - // If a specific length is requested and a specific length is returned but the 2 don't match - // it's an error. - if (currentDataSpec.length != C.LENGTH_UNSET - && contentLength != C.LENGTH_UNSET - && currentDataSpec.length != contentLength) { - throw new OpenException("Content length did not match requested length", currentDataSpec, - getCurrentRequestStatus()); + + if (isCompressed(info)) { + contentLength = currentDataSpec.length; + } else { + // Check content length. + contentLength = getContentLength(info.getAllHeaders()); + // If a specific length is requested and a specific length is returned but the 2 don't match + // it's an error. + if (currentDataSpec.length != C.LENGTH_UNSET + && contentLength != C.LENGTH_UNSET + && currentDataSpec.length != contentLength) { + throw new OpenException("Content length did not match requested length", currentDataSpec, + getCurrentRequestStatus()); + } } if (contentLength > 0) { @@ -326,6 +331,23 @@ public class CronetDataSource extends UrlRequest.Callback implements HttpDataSou } } + /** + * Returns {@code true} iff the content is compressed. + * + *

If {@code true}, clients cannot use the value of content length from the request headers to + * read the data, since Cronet returns the uncompressed data and this content length reflects the + * compressed content length. + */ + private boolean isCompressed(UrlResponseInfo info) { + for (Map.Entry entry : info.getAllHeadersAsList()) { + if (entry.getKey().equalsIgnoreCase("Content-Encoding")) { + return !entry.getValue().equalsIgnoreCase("identity"); + } + } + + return false; + } + private void validateResponse(UrlResponseInfo info) throws HttpDataSourceException { // Check for a valid response code. int responseCode = info.getHttpStatusCode(); From 669cf6804a8c44455bfb9b0899c5be3273e5081b Mon Sep 17 00:00:00 2001 From: "[]inger" <[]inger@google.com> Date: Fri, 23 Sep 2016 03:15:38 -0700 Subject: [PATCH 15/36] Mention jcenter and gradle dependency in dev guide and Github readme. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=134056007 --- README.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 970d482e16..f9c422db5e 100644 --- a/README.md +++ b/README.md @@ -22,8 +22,17 @@ and extend, and can be updated through Play Store application updates. #### Via jCenter #### -The easiest way to get started using ExoPlayer is by including the following in -your project's `build.gradle` file: +The easiest way to get started using ExoPlayer is to add it as a gradle +dependency. You need to make sure you have the jcenter repository included in +the `build.gradle` file in the root of your project: + +```gradle +repositories { + jcenter() +} +``` + +Next, include the following in your module's `build.gradle` file: ```gradle compile 'com.google.android.exoplayer:exoplayer:rX.X.X' From 654d914bb65c8458d0c9713ac94f77164f7bee65 Mon Sep 17 00:00:00 2001 From: olly Date: Fri, 23 Sep 2016 05:35:42 -0700 Subject: [PATCH 16/36] Fix DefaultHttpDataSource read when requested length is 0 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=134063990 --- .../android/exoplayer2/upstream/DefaultHttpDataSource.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/library/src/main/java/com/google/android/exoplayer2/upstream/DefaultHttpDataSource.java b/library/src/main/java/com/google/android/exoplayer2/upstream/DefaultHttpDataSource.java index bcb4b9d2bd..e00f93aa19 100644 --- a/library/src/main/java/com/google/android/exoplayer2/upstream/DefaultHttpDataSource.java +++ b/library/src/main/java/com/google/android/exoplayer2/upstream/DefaultHttpDataSource.java @@ -547,11 +547,8 @@ public class DefaultHttpDataSource implements HttpDataSource { * @throws IOException If an error occurs reading from the source. */ private int readInternal(byte[] buffer, int offset, int readLength) throws IOException { - readLength = bytesToRead == C.LENGTH_UNSET ? readLength - : (int) Math.min(readLength, bytesToRead - bytesRead); if (readLength == 0) { - // We've read all of the requested data. - return C.RESULT_END_OF_INPUT; + return 0; } int read = inputStream.read(buffer, offset, readLength); From 8cf107408d2b6d15373dc8a6b938bfbec03e8b83 Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Fri, 23 Sep 2016 07:49:27 -0700 Subject: [PATCH 17/36] Replace stbl consistency assertions with a warning. Issue: #1850 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=134073096 --- .../exoplayer2/extractor/mp4/AtomParsers.java | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java b/library/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java index 87be7029c9..be26e7f9d4 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java @@ -15,6 +15,7 @@ */ package com.google.android.exoplayer2.extractor.mp4; +import android.util.Log; import android.util.Pair; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; @@ -38,6 +39,8 @@ import java.util.List; */ /* package */ final class AtomParsers { + private static final String TAG = "AtomParsers"; + private static final int TYPE_vide = Util.getIntegerCodeForString("vide"); private static final int TYPE_soun = Util.getIntegerCodeForString("soun"); private static final int TYPE_text = Util.getIntegerCodeForString("text"); @@ -248,11 +251,16 @@ import java.util.List; remainingTimestampOffsetChanges--; } - // Check all the expected samples have been seen. - Assertions.checkArgument(remainingSynchronizationSamples == 0); - Assertions.checkArgument(remainingSamplesAtTimestampDelta == 0); - Assertions.checkArgument(remainingSamplesInChunk == 0); - Assertions.checkArgument(remainingTimestampDeltaChanges == 0); + // If the stbl's child boxes are not consistent the container is malformed, but the stream may + // still be playable. + if (remainingSynchronizationSamples != 0 || remainingSamplesAtTimestampDelta != 0 + || remainingSamplesInChunk != 0 || remainingTimestampDeltaChanges != 0) { + Log.w(TAG, "Inconsistent stbl box for track " + track.id + + ": remainingSynchronizationSamples " + remainingSynchronizationSamples + + ", remainingSamplesAtTimestampDelta " + remainingSamplesAtTimestampDelta + + ", remainingSamplesInChunk " + remainingSamplesInChunk + + ", remainingTimestampDeltaChanges " + remainingTimestampDeltaChanges); + } } else { long[] chunkOffsetsBytes = new long[chunkIterator.length]; int[] chunkSampleCounts = new int[chunkIterator.length]; From 94cc606091799b4a03593582528a600494979b15 Mon Sep 17 00:00:00 2001 From: aquilescanta Date: Fri, 23 Sep 2016 07:56:25 -0700 Subject: [PATCH 18/36] Export variant codecs in HLS Issue:#1772 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=134073672 --- .../com/google/android/exoplayer2/Format.java | 2 +- .../source/hls/HlsSampleStreamWrapper.java | 49 ++++++++++++++++--- .../android/exoplayer2/util/MimeTypes.java | 41 ++++++++++++++++ 3 files changed, 83 insertions(+), 9 deletions(-) diff --git a/library/src/main/java/com/google/android/exoplayer2/Format.java b/library/src/main/java/com/google/android/exoplayer2/Format.java index 50112d5a0a..550e6ab1d8 100644 --- a/library/src/main/java/com/google/android/exoplayer2/Format.java +++ b/library/src/main/java/com/google/android/exoplayer2/Format.java @@ -380,7 +380,7 @@ public final class Format implements Parcelable { selectionFlags, language, subsampleOffsetUs, initializationData, drmInitData); } - public Format copyWithContainerInfo(String id, int bitrate, int width, int height, + public Format copyWithContainerInfo(String id, String codecs, int bitrate, int width, int height, @C.SelectionFlags int selectionFlags, String language) { return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, maxInputSize, width, height, frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData, diff --git a/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java b/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java index b67d6538dd..bf10480262 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java @@ -15,6 +15,7 @@ */ package com.google.android.exoplayer2.source.hls; +import android.text.TextUtils; import android.util.SparseArray; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; @@ -567,7 +568,7 @@ import java.util.LinkedList; if (i == primaryExtractorTrackIndex) { Format[] formats = new Format[chunkSourceTrackCount]; for (int j = 0; j < chunkSourceTrackCount; j++) { - formats[j] = getSampleFormat(chunkSourceTrackGroup.getFormat(j), sampleFormat); + formats[j] = deriveFormat(chunkSourceTrackGroup.getFormat(j), sampleFormat); } trackGroups[i] = new TrackGroup(formats); primaryTrackGroupIndex = i; @@ -580,7 +581,7 @@ import java.util.LinkedList; trackFormat = muxedCaptionFormat; } } - trackGroups[i] = new TrackGroup(getSampleFormat(trackFormat, sampleFormat)); + trackGroups[i] = new TrackGroup(deriveFormat(trackFormat, sampleFormat)); } } this.trackGroups = new TrackGroupArray(trackGroups); @@ -599,18 +600,25 @@ import java.util.LinkedList; } /** - * Derives a sample format corresponding to a given container format, by combining it with sample - * level information obtained from a second sample format. + * Derives a track format corresponding to a given container format, by combining it with sample + * level information obtained from the samples. * - * @param containerFormat The container format for which the sample format should be derived. + * @param containerFormat The container format for which the track format should be derived. * @param sampleFormat A sample format from which to obtain sample level information. - * @return The derived sample format. + * @return The derived track format. */ - private static Format getSampleFormat(Format containerFormat, Format sampleFormat) { + private static Format deriveFormat(Format containerFormat, Format sampleFormat) { if (containerFormat == null) { return sampleFormat; } - return sampleFormat.copyWithContainerInfo(containerFormat.id, containerFormat.bitrate, + String codecs = null; + int sampleTrackType = MimeTypes.getTrackType(sampleFormat.sampleMimeType); + if (sampleTrackType == C.TRACK_TYPE_AUDIO) { + codecs = getAudioCodecs(containerFormat.codecs); + } else if (sampleTrackType == C.TRACK_TYPE_VIDEO) { + codecs = getVideoCodecs(containerFormat.codecs); + } + return sampleFormat.copyWithContainerInfo(containerFormat.id, codecs, containerFormat.bitrate, containerFormat.width, containerFormat.height, containerFormat.selectionFlags, containerFormat.language); } @@ -623,4 +631,29 @@ import java.util.LinkedList; return pendingResetPositionUs != C.TIME_UNSET; } + private static String getAudioCodecs(String codecs) { + return getCodecsOfType(codecs, C.TRACK_TYPE_AUDIO); + } + + private static String getVideoCodecs(String codecs) { + return getCodecsOfType(codecs, C.TRACK_TYPE_VIDEO); + } + + private static String getCodecsOfType(String codecs, int trackType) { + if (TextUtils.isEmpty(codecs)) { + return null; + } + String[] codecArray = codecs.split("(\\s*,\\s*)|(\\s*$)"); + StringBuilder builder = new StringBuilder(); + for (String codec : codecArray) { + if (trackType == MimeTypes.getTrackTypeOfCodec(codec)) { + if (builder.length() > 0) { + builder.append(","); + } + builder.append(codec); + } + } + return builder.length() > 0 ? builder.toString() : null; + } + } diff --git a/library/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java b/library/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java index a678d9487b..f3f63b1a76 100644 --- a/library/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java +++ b/library/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java @@ -15,6 +15,9 @@ */ package com.google.android.exoplayer2.util; +import android.text.TextUtils; +import com.google.android.exoplayer2.C; + /** * Defines common MIME types and helper methods. */ @@ -191,6 +194,44 @@ public final class MimeTypes { return null; } + /** + * Returns the {@link C}{@code .TRACK_TYPE_*} constant that corresponds to a specified mime type. + * {@link C#TRACK_TYPE_UNKNOWN} if the mime type is not known or the mapping cannot be + * established. + * + * @param mimeType The mimeType. + * @return The {@link C}{@code .TRACK_TYPE_*} constant that corresponds to a specified mime type. + */ + public static int getTrackType(String mimeType) { + if (TextUtils.isEmpty(mimeType)) { + return C.TRACK_TYPE_UNKNOWN; + } else if (isAudio(mimeType)) { + return C.TRACK_TYPE_AUDIO; + } else if (isVideo(mimeType)) { + return C.TRACK_TYPE_VIDEO; + } else if (isText(mimeType) || APPLICATION_EIA608.equals(mimeType) + || APPLICATION_SUBRIP.equals(mimeType) || APPLICATION_TTML.equals(mimeType) + || APPLICATION_TX3G.equals(mimeType) || APPLICATION_MP4VTT.equals(mimeType) + || APPLICATION_RAWCC.equals(mimeType) || APPLICATION_VOBSUB.equals(mimeType) + || APPLICATION_PGS.equals(mimeType)) { + return C.TRACK_TYPE_TEXT; + } else if (APPLICATION_ID3.equals(mimeType)) { + return C.TRACK_TYPE_METADATA; + } else { + return C.TRACK_TYPE_UNKNOWN; + } + } + + /** + * Equivalent to {@code getTrackType(getMediaMimeType(codec))}. + * + * @param codec The codec. + * @return The {@link C}{@code .TRACK_TYPE_*} constant that corresponds to a specified codec. + */ + public static int getTrackTypeOfCodec(String codec) { + return getTrackType(getMediaMimeType(codec)); + } + /** * Returns the top-level type of {@code mimeType}. * From 88b9e8c615914d96e639c372115e7025ba641104 Mon Sep 17 00:00:00 2001 From: olly Date: Fri, 23 Sep 2016 08:49:04 -0700 Subject: [PATCH 19/36] Clear the selection override even if renderer is disabled ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=134078455 --- .../android/exoplayer2/demo/TrackSelectionHelper.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/demo/src/main/java/com/google/android/exoplayer2/demo/TrackSelectionHelper.java b/demo/src/main/java/com/google/android/exoplayer2/demo/TrackSelectionHelper.java index 1bbd620660..8102cd7034 100644 --- a/demo/src/main/java/com/google/android/exoplayer2/demo/TrackSelectionHelper.java +++ b/demo/src/main/java/com/google/android/exoplayer2/demo/TrackSelectionHelper.java @@ -203,11 +203,7 @@ import java.util.Locale; @Override public void onClick(DialogInterface dialog, int which) { - if (isDisabled) { - selector.setRendererDisabled(rendererIndex, true); - return; - } - selector.setRendererDisabled(rendererIndex, false); + selector.setRendererDisabled(rendererIndex, isDisabled); if (override != null) { selector.setSelectionOverride(rendererIndex, trackGroups, override); } else { From 65eda1d105c894b6d359702587943dce9f35b77f Mon Sep 17 00:00:00 2001 From: olly Date: Fri, 23 Sep 2016 11:43:07 -0700 Subject: [PATCH 20/36] Add missing generic types ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=134099118 --- .../com/google/android/exoplayer2/drm/DrmSessionManager.java | 2 +- .../android/exoplayer2/drm/StreamingDrmSessionManager.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/library/src/main/java/com/google/android/exoplayer2/drm/DrmSessionManager.java b/library/src/main/java/com/google/android/exoplayer2/drm/DrmSessionManager.java index 375ffc2733..8e63fbfaae 100644 --- a/library/src/main/java/com/google/android/exoplayer2/drm/DrmSessionManager.java +++ b/library/src/main/java/com/google/android/exoplayer2/drm/DrmSessionManager.java @@ -37,6 +37,6 @@ public interface DrmSessionManager { /** * Releases a {@link DrmSession}. */ - void releaseSession(DrmSession drmSession); + void releaseSession(DrmSession drmSession); } diff --git a/library/src/main/java/com/google/android/exoplayer2/drm/StreamingDrmSessionManager.java b/library/src/main/java/com/google/android/exoplayer2/drm/StreamingDrmSessionManager.java index 2882111ac9..f3c6595736 100644 --- a/library/src/main/java/com/google/android/exoplayer2/drm/StreamingDrmSessionManager.java +++ b/library/src/main/java/com/google/android/exoplayer2/drm/StreamingDrmSessionManager.java @@ -40,7 +40,7 @@ import java.util.UUID; * A {@link DrmSessionManager} that supports streaming playbacks using {@link MediaDrm}. */ @TargetApi(18) -public class StreamingDrmSessionManager implements DrmSessionManager, +public class StreamingDrmSessionManager implements DrmSessionManager, DrmSession { /** @@ -268,7 +268,7 @@ public class StreamingDrmSessionManager implements Drm } @Override - public void releaseSession(DrmSession session) { + public void releaseSession(DrmSession session) { if (--openCount != 0) { return; } From 7273237b28e17cacc9fe9e58fff5ce9783026a65 Mon Sep 17 00:00:00 2001 From: aquilescanta Date: Mon, 26 Sep 2016 09:43:40 -0700 Subject: [PATCH 21/36] Add an immutable holder for track selection parameters This solves the thread unsafety issue of the default track selector and allows atomic configuration changes. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=134288525 --- .../trackselection/DefaultTrackSelector.java | 352 ++++++++++++++---- 1 file changed, 287 insertions(+), 65 deletions(-) diff --git a/library/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java b/library/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java index 2351da716c..61f46bdadd 100644 --- a/library/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java +++ b/library/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java @@ -18,22 +18,274 @@ package com.google.android.exoplayer2.trackselection; import android.content.Context; import android.graphics.Point; import android.os.Handler; +import android.text.TextUtils; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.RendererCapabilities; import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.TrackGroupArray; +import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Util; import java.util.ArrayList; import java.util.HashSet; import java.util.List; +import java.util.concurrent.atomic.AtomicReference; /** - * A {@link MappingTrackSelector} that allows configuration of common parameters. + * A {@link MappingTrackSelector} that allows configuration of common parameters. It is safe to call + * the methods of this class from the application thread. See {@link Parameters#Parameters()} for + * default selection parameters. */ public class DefaultTrackSelector extends MappingTrackSelector { + /** + * Holder for available configurations for the {@link DefaultTrackSelector}. + */ + public static final class Parameters { + + // Audio. + public final String preferredAudioLanguage; + + // Text. + public final String preferredTextLanguage; + + // Video. + public final boolean allowMixedMimeAdaptiveness; + public final boolean allowNonSeamlessAdaptiveness; + public final int maxVideoWidth; + public final int maxVideoHeight; + public final boolean exceedVideoConstraintsIfNecessary; + public final int viewportWidth; + public final int viewportHeight; + public final boolean orientationMayChange; + + /** + * Constructor with default selection parameters: + *

    + *
  • No preferred audio language is set.
  • + *
  • No preferred text language is set.
  • + *
  • Adaptation between different mime types is not allowed.
  • + *
  • Non seamless adaptation is allowed.
  • + *
  • No max limit for video width/height.
  • + *
  • Video constrains are ignored if no supported selection can be made otherwise.
  • + *
  • No viewport width/height constrains are set.
  • + *
+ */ + public Parameters() { + this(null, null, false, true, Integer.MAX_VALUE, Integer.MAX_VALUE, true, Integer.MAX_VALUE, + Integer.MAX_VALUE, true); + } + + /** + * @param preferredAudioLanguage The preferred language for audio, as well as for forced text + * tracks as defined by RFC 5646. {@code null} to select the default track, or first track + * if there's no default. + * @param preferredTextLanguage The preferred language for text tracks as defined by RFC 5646. + * {@code null} to select the default track, or first track if there's no default. + * @param allowMixedMimeAdaptiveness Whether to allow selections to contain mixed mime types. + * @param allowNonSeamlessAdaptiveness Whether non-seamless adaptation is allowed. + * @param maxVideoWidth Maximum allowed video width. + * @param maxVideoHeight Maximum allowed video height. + * @param exceedVideoConstraintsIfNecessary True to ignore video constraints when no selections + * can be made otherwise. False to force constraints anyway. + * @param viewportWidth Viewport width in pixels. + * @param viewportHeight Viewport height in pixels. + * @param orientationMayChange Whether orientation may change during playback. + */ + public Parameters(String preferredAudioLanguage, String preferredTextLanguage, + boolean allowMixedMimeAdaptiveness, boolean allowNonSeamlessAdaptiveness, + int maxVideoWidth, int maxVideoHeight, boolean exceedVideoConstraintsIfNecessary, + int viewportWidth, int viewportHeight, boolean orientationMayChange) { + this.preferredAudioLanguage = preferredAudioLanguage; + this.preferredTextLanguage = preferredTextLanguage; + this.allowMixedMimeAdaptiveness = allowMixedMimeAdaptiveness; + this.allowNonSeamlessAdaptiveness = allowNonSeamlessAdaptiveness; + this.maxVideoWidth = maxVideoWidth; + this.maxVideoHeight = maxVideoHeight; + this.exceedVideoConstraintsIfNecessary = exceedVideoConstraintsIfNecessary; + this.viewportWidth = viewportWidth; + this.viewportHeight = viewportHeight; + this.orientationMayChange = orientationMayChange; + } + + /** + * Returns a {@link Parameters} instance with the provided preferred language for audio and + * forced text tracks. + * + * @param preferredAudioLanguage The preferred language as defined by RFC 5646. {@code null} to + * select the default track, or first track if there's no default. + * @return A {@link Parameters} instance with the provided preferred language for audio and + * forced text tracks. + */ + public Parameters withPreferredAudioLanguage(String preferredAudioLanguage) { + preferredAudioLanguage = Util.normalizeLanguageCode(preferredAudioLanguage); + if (TextUtils.equals(preferredAudioLanguage, this.preferredAudioLanguage)) { + return this; + } + return new Parameters(preferredAudioLanguage, preferredTextLanguage, + allowMixedMimeAdaptiveness, allowNonSeamlessAdaptiveness, maxVideoWidth, maxVideoHeight, + exceedVideoConstraintsIfNecessary, viewportWidth, viewportHeight, orientationMayChange); + } + + /** + * Returns a {@link Parameters} instance with the provided preferred language for text tracks. + * + * @param preferredTextLanguage The preferred language as defined by RFC 5646. {@code null} to + * select the default track, or no track if there's no default. + * @return A {@link Parameters} instance with the provided preferred language for text tracks. + */ + public Parameters withPreferredTextLanguage(String preferredTextLanguage) { + preferredTextLanguage = Util.normalizeLanguageCode(preferredTextLanguage); + if (TextUtils.equals(preferredTextLanguage, this.preferredTextLanguage)) { + return this; + } + return new Parameters(preferredAudioLanguage, preferredTextLanguage, + allowMixedMimeAdaptiveness, allowNonSeamlessAdaptiveness, maxVideoWidth, + maxVideoHeight, exceedVideoConstraintsIfNecessary, viewportWidth, viewportHeight, + orientationMayChange); + } + + /** + * Returns a {@link Parameters} instance with the provided mixed mime adaptiveness allowance. + * + * @param allowMixedMimeAdaptiveness Whether to allow selections to contain mixed mime types. + * @return A {@link Parameters} instance with the provided mixed mime adaptiveness allowance. + */ + public Parameters withAllowMixedMimeAdaptiveness(boolean allowMixedMimeAdaptiveness) { + if (allowMixedMimeAdaptiveness == this.allowMixedMimeAdaptiveness) { + return this; + } + return new Parameters(preferredAudioLanguage, preferredTextLanguage, + allowMixedMimeAdaptiveness, allowNonSeamlessAdaptiveness, maxVideoWidth, + maxVideoHeight, exceedVideoConstraintsIfNecessary, viewportWidth, viewportHeight, + orientationMayChange); + } + + /** + * Returns a {@link Parameters} instance with the provided seamless adaptiveness allowance. + * + * @param allowNonSeamlessAdaptiveness Whether non-seamless adaptation is allowed. + * @return A {@link Parameters} instance with the provided seamless adaptiveness allowance. + */ + public Parameters withAllowNonSeamlessAdaptiveness(boolean allowNonSeamlessAdaptiveness) { + if (allowNonSeamlessAdaptiveness == this.allowNonSeamlessAdaptiveness) { + return this; + } + return new Parameters(preferredAudioLanguage, preferredTextLanguage, + allowMixedMimeAdaptiveness, allowNonSeamlessAdaptiveness, maxVideoWidth, + maxVideoHeight, exceedVideoConstraintsIfNecessary, viewportWidth, viewportHeight, + orientationMayChange); + } + + /** + * Returns a {@link Parameters} instance with the provided max video size. + * + * @param maxVideoWidth The max video width. + * @param maxVideoHeight The max video width. + * @return A {@link Parameters} instance with the provided max video size. + */ + public Parameters withMaxVideoSize(int maxVideoWidth, int maxVideoHeight) { + if (maxVideoWidth == this.maxVideoWidth && maxVideoHeight == this.maxVideoHeight) { + return this; + } + return new Parameters(preferredAudioLanguage, preferredTextLanguage, + allowMixedMimeAdaptiveness, allowNonSeamlessAdaptiveness, maxVideoWidth, + maxVideoHeight, exceedVideoConstraintsIfNecessary, viewportWidth, viewportHeight, + orientationMayChange); + } + + /** + * Returns a {@link Parameters} instance with the provided + * {@code exceedVideoConstraintsIfNecessary} value. + * + * @param exceedVideoConstraintsIfNecessary True to ignore video constraints when no selections + * can be made otherwise. False to force constraints anyway. + * @return A {@link Parameters} instance with the provided + * {@code exceedVideoConstraintsIfNecessary} value. + */ + public Parameters withExceedVideoConstraintsIfNecessary( + boolean exceedVideoConstraintsIfNecessary) { + if (exceedVideoConstraintsIfNecessary == this.exceedVideoConstraintsIfNecessary) { + return this; + } + return new Parameters(preferredAudioLanguage, preferredTextLanguage, + allowMixedMimeAdaptiveness, allowNonSeamlessAdaptiveness, maxVideoWidth, + maxVideoHeight, exceedVideoConstraintsIfNecessary, viewportWidth, viewportHeight, + orientationMayChange); + } + + /** + * Returns a {@link Parameters} instance with the provided viewport size. + * + * @param viewportWidth Viewport width in pixels. + * @param viewportHeight Viewport height in pixels. + * @param orientationMayChange Whether orientation may change during playback. + * @return A {@link Parameters} instance with the provided viewport size. + */ + public Parameters withViewportSize(int viewportWidth, int viewportHeight, + boolean orientationMayChange) { + if (viewportWidth == this.viewportWidth && viewportHeight == this.viewportHeight + && orientationMayChange == this.orientationMayChange) { + return this; + } + return new Parameters(preferredAudioLanguage, preferredTextLanguage, + allowMixedMimeAdaptiveness, allowNonSeamlessAdaptiveness, maxVideoWidth, + maxVideoHeight, exceedVideoConstraintsIfNecessary, viewportWidth, viewportHeight, + orientationMayChange); + } + + /** + * Returns a {@link Parameters} instance where the viewport size is obtained from the provided + * {@link Context}. + * + * @param context The context to obtain the viewport size from. + * @param orientationMayChange Whether orientation may change during playback. + * @return A {@link Parameters} instance where the viewport size is obtained from the provided + * {@link Context}. + */ + public Parameters withViewportSizeFromContext(Context context, boolean orientationMayChange) { + // Assume the viewport is fullscreen. + Point viewportSize = Util.getPhysicalDisplaySize(context); + return withViewportSize(viewportSize.x, viewportSize.y, orientationMayChange); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + Parameters other = (Parameters) obj; + return allowMixedMimeAdaptiveness == other.allowMixedMimeAdaptiveness + && allowNonSeamlessAdaptiveness == other.allowNonSeamlessAdaptiveness + && maxVideoWidth == other.maxVideoWidth && maxVideoHeight == other.maxVideoHeight + && exceedVideoConstraintsIfNecessary == other.exceedVideoConstraintsIfNecessary + && orientationMayChange == other.orientationMayChange + && viewportWidth == other.viewportWidth && viewportHeight == other.viewportHeight + && TextUtils.equals(preferredAudioLanguage, other.preferredAudioLanguage) + && TextUtils.equals(preferredTextLanguage, other.preferredTextLanguage); + } + + @Override + public int hashCode() { + int result = preferredAudioLanguage.hashCode(); + result = 31 * result + preferredTextLanguage.hashCode(); + result = 31 * result + (allowMixedMimeAdaptiveness ? 1 : 0); + result = 31 * result + (allowNonSeamlessAdaptiveness ? 1 : 0); + result = 31 * result + maxVideoWidth; + result = 31 * result + maxVideoHeight; + result = 31 * result + (exceedVideoConstraintsIfNecessary ? 1 : 0); + result = 31 * result + (orientationMayChange ? 1 : 0); + result = 31 * result + viewportWidth; + result = 31 * result + viewportHeight; + return result; + } + + } + /** * If a dimension (i.e. width or height) of a video is greater or equal to this fraction of the * corresponding viewport dimension, then the video is considered as filling the viewport (in that @@ -43,22 +295,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { private static final int[] NO_TRACKS = new int[0]; private final TrackSelection.Factory adaptiveVideoTrackSelectionFactory; - - // Audio. - private String preferredAudioLanguage; - - // Text. - private String preferredTextLanguage; - - // Video. - private boolean allowMixedMimeAdaptiveness; - private boolean allowNonSeamlessAdaptiveness; - private int maxVideoWidth; - private int maxVideoHeight; - private boolean exceedVideoConstraintsIfNecessary; - private boolean orientationMayChange; - private int viewportWidth; - private int viewportHeight; + private final AtomicReference params; /** * Constructs an instance that does not support adaptive video. @@ -82,13 +319,19 @@ public class DefaultTrackSelector extends MappingTrackSelector { TrackSelection.Factory adaptiveVideoTrackSelectionFactory) { super(eventHandler); this.adaptiveVideoTrackSelectionFactory = adaptiveVideoTrackSelectionFactory; - allowNonSeamlessAdaptiveness = true; - exceedVideoConstraintsIfNecessary = true; - maxVideoWidth = Integer.MAX_VALUE; - maxVideoHeight = Integer.MAX_VALUE; - viewportWidth = Integer.MAX_VALUE; - viewportHeight = Integer.MAX_VALUE; - orientationMayChange = true; + params = new AtomicReference<>(new Parameters()); + } + + /** + * Atomically sets the provided parameters for track selection. + * + * @param params The parameters for track selection. + */ + public void setParameters(Parameters params) { + if (!this.params.get().equals(params)) { + this.params.set(Assertions.checkNotNull(params)); + invalidate(); + } } /** @@ -97,12 +340,9 @@ public class DefaultTrackSelector extends MappingTrackSelector { * @param preferredAudioLanguage The preferred language as defined by RFC 5646. {@code null} to * select the default track, or first track if there's no default. */ - public void setPreferredLanguages(String preferredAudioLanguage) { + public void setPreferredLanguage(String preferredAudioLanguage) { preferredAudioLanguage = Util.normalizeLanguageCode(preferredAudioLanguage); - if (!Util.areEqual(this.preferredAudioLanguage, preferredAudioLanguage)) { - this.preferredAudioLanguage = preferredAudioLanguage; - invalidate(); - } + setParameters(params.get().withPreferredAudioLanguage(preferredAudioLanguage)); } /** @@ -113,10 +353,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { */ public void setPreferredTextLanguage(String preferredTextLanguage) { preferredTextLanguage = Util.normalizeLanguageCode(preferredTextLanguage); - if (!Util.areEqual(this.preferredTextLanguage, preferredTextLanguage)) { - this.preferredTextLanguage = preferredTextLanguage; - invalidate(); - } + setParameters(params.get().withPreferredTextLanguage(preferredTextLanguage)); } /** @@ -125,10 +362,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { * @param allowMixedMimeAdaptiveness Whether to allow selections to contain mixed mime types. */ public void allowMixedMimeAdaptiveness(boolean allowMixedMimeAdaptiveness) { - if (this.allowMixedMimeAdaptiveness != allowMixedMimeAdaptiveness) { - this.allowMixedMimeAdaptiveness = allowMixedMimeAdaptiveness; - invalidate(); - } + setParameters(params.get().withAllowMixedMimeAdaptiveness(allowMixedMimeAdaptiveness)); } /** @@ -137,10 +371,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { * @param allowNonSeamlessAdaptiveness Whether non-seamless adaptation is allowed. */ public void allowNonSeamlessAdaptiveness(boolean allowNonSeamlessAdaptiveness) { - if (this.allowNonSeamlessAdaptiveness != allowNonSeamlessAdaptiveness) { - this.allowNonSeamlessAdaptiveness = allowNonSeamlessAdaptiveness; - invalidate(); - } + setParameters(params.get().withAllowNonSeamlessAdaptiveness(allowNonSeamlessAdaptiveness)); } /** @@ -150,11 +381,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { * @param maxVideoHeight Maximum allowed height. */ public void setMaxVideoSize(int maxVideoWidth, int maxVideoHeight) { - if (this.maxVideoWidth != maxVideoWidth || this.maxVideoHeight != maxVideoHeight) { - this.maxVideoWidth = maxVideoWidth; - this.maxVideoHeight = maxVideoHeight; - invalidate(); - } + setParameters(params.get().withMaxVideoSize(maxVideoWidth, maxVideoHeight)); } /** @@ -178,10 +405,8 @@ public class DefaultTrackSelector extends MappingTrackSelector { * can be made otherwise. False to force constraints anyway. */ public void setExceedVideoConstraintsIfNecessary(boolean exceedVideoConstraintsIfNecessary) { - if (this.exceedVideoConstraintsIfNecessary != exceedVideoConstraintsIfNecessary) { - this.exceedVideoConstraintsIfNecessary = exceedVideoConstraintsIfNecessary; - invalidate(); - } + setParameters( + params.get().withExceedVideoConstraintsIfNecessary(exceedVideoConstraintsIfNecessary)); } /** @@ -192,13 +417,8 @@ public class DefaultTrackSelector extends MappingTrackSelector { * @param orientationMayChange Whether orientation may change during playback. */ public void setViewportSize(int viewportWidth, int viewportHeight, boolean orientationMayChange) { - if (this.viewportWidth != viewportWidth || this.viewportHeight != viewportHeight - || this.orientationMayChange != orientationMayChange) { - this.viewportWidth = viewportWidth; - this.viewportHeight = viewportHeight; - this.orientationMayChange = orientationMayChange; - invalidate(); - } + setParameters( + params.get().withViewportSize(viewportWidth, viewportHeight, orientationMayChange)); } /** @@ -209,8 +429,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { * @param orientationMayChange Whether orientation may change during playback. */ public void setViewportSizeFromContext(Context context, boolean orientationMayChange) { - Point viewportSize = Util.getPhysicalDisplaySize(context); // Assume the viewport is fullscreen. - setViewportSize(viewportSize.x, viewportSize.y, orientationMayChange); + setParameters(params.get().withViewportSizeFromContext(context, orientationMayChange)); } /** @@ -228,22 +447,25 @@ public class DefaultTrackSelector extends MappingTrackSelector { throws ExoPlaybackException { // Make a track selection for each renderer. TrackSelection[] rendererTrackSelections = new TrackSelection[rendererCapabilities.length]; + Parameters params = this.params.get(); for (int i = 0; i < rendererCapabilities.length; i++) { switch (rendererCapabilities[i].getTrackType()) { case C.TRACK_TYPE_VIDEO: rendererTrackSelections[i] = selectVideoTrack(rendererCapabilities[i], - rendererTrackGroupArrays[i], rendererFormatSupports[i], maxVideoWidth, maxVideoHeight, - allowNonSeamlessAdaptiveness, allowMixedMimeAdaptiveness, viewportWidth, - viewportHeight, orientationMayChange, adaptiveVideoTrackSelectionFactory, - exceedVideoConstraintsIfNecessary); + rendererTrackGroupArrays[i], rendererFormatSupports[i], params.maxVideoWidth, + params.maxVideoHeight, + params.allowNonSeamlessAdaptiveness, params.allowMixedMimeAdaptiveness, + params.viewportWidth, params.viewportHeight, params.orientationMayChange, + adaptiveVideoTrackSelectionFactory, params.exceedVideoConstraintsIfNecessary); break; case C.TRACK_TYPE_AUDIO: rendererTrackSelections[i] = selectAudioTrack(rendererTrackGroupArrays[i], - rendererFormatSupports[i], preferredAudioLanguage); + rendererFormatSupports[i], params.preferredAudioLanguage); break; case C.TRACK_TYPE_TEXT: rendererTrackSelections[i] = selectTextTrack(rendererTrackGroupArrays[i], - rendererFormatSupports[i], preferredTextLanguage, preferredAudioLanguage); + rendererFormatSupports[i], params.preferredTextLanguage, + params.preferredAudioLanguage); break; default: rendererTrackSelections[i] = selectOtherTrack(rendererCapabilities[i].getTrackType(), From f5ebcb8d043292e3e9aad190900e9bcfc2f52296 Mon Sep 17 00:00:00 2001 From: cdrolle Date: Mon, 26 Sep 2016 11:03:37 -0700 Subject: [PATCH 22/36] Modified the default position parameters of the Cue objects created by Eia608Decoder. Full preamble positioning will be provided in a subsequent CL. This CL also contains some minor cleanup in Eia608Decoder and adds some TODOs to handle the second channel. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=134299337 --- .../exoplayer2/text/eia608/Eia608Decoder.java | 68 +++++++++++++------ .../text/eia608/Eia608Subtitle.java | 20 +++--- 2 files changed, 56 insertions(+), 32 deletions(-) diff --git a/library/src/main/java/com/google/android/exoplayer2/text/eia608/Eia608Decoder.java b/library/src/main/java/com/google/android/exoplayer2/text/eia608/Eia608Decoder.java index b5249cde78..50c6d4f6b0 100644 --- a/library/src/main/java/com/google/android/exoplayer2/text/eia608/Eia608Decoder.java +++ b/library/src/main/java/com/google/android/exoplayer2/text/eia608/Eia608Decoder.java @@ -15,8 +15,10 @@ */ package com.google.android.exoplayer2.text.eia608; +import android.text.Layout.Alignment; import android.text.TextUtils; import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.text.Cue; import com.google.android.exoplayer2.text.SubtitleDecoder; import com.google.android.exoplayer2.text.SubtitleDecoderException; import com.google.android.exoplayer2.text.SubtitleInputBuffer; @@ -159,6 +161,14 @@ public final class Eia608Decoder implements SubtitleDecoder { 0xC5, 0xE5, 0xD8, 0xF8, 0x250C, 0x2510, 0x2514, 0x2518 }; + private static final Alignment CUE_TEXT_ALIGNMENT = Alignment.ALIGN_NORMAL; + private static final float CUE_LINE = Cue.DIMEN_UNSET; + private static final int CUE_LINE_TYPE = Cue.TYPE_UNSET; + private static final int CUE_LINE_ANCHOR = Cue.TYPE_UNSET; + private static final float CUE_POSITION = 0.1f; + private static final int CUE_POSITION_ANCHOR = Cue.TYPE_UNSET; + private static final float CUE_SIZE = 0.8f; + private final LinkedList availableInputBuffers; private final LinkedList availableOutputBuffers; private final TreeSet queuedInputBuffers; @@ -256,8 +266,13 @@ public final class Eia608Decoder implements SubtitleDecoder { if (!TextUtils.equals(captionString, lastCaptionString)) { lastCaptionString = captionString; if (!inputBuffer.isDecodeOnly()) { + Cue cue = null; + if (!TextUtils.isEmpty(captionString)) { + cue = new Cue(captionString, CUE_TEXT_ALIGNMENT, CUE_LINE, CUE_LINE_TYPE, + CUE_LINE_ANCHOR, CUE_POSITION, CUE_POSITION_ANCHOR, CUE_SIZE); + } SubtitleOutputBuffer outputBuffer = availableOutputBuffers.pollFirst(); - outputBuffer.setContent(inputBuffer.timeUs, new Eia608Subtitle(captionString), 0); + outputBuffer.setContent(inputBuffer.timeUs, new Eia608Subtitle(cue), 0); releaseInputBuffer(inputBuffer); return outputBuffer; } @@ -320,26 +335,33 @@ public final class Eia608Decoder implements SubtitleDecoder { captionDataProcessed = true; // Special North American character set. + // ccData1 - P|0|0|1|C|0|0|1 // ccData2 - P|0|1|1|X|X|X|X if ((ccData1 == 0x11 || ccData1 == 0x19) && ((ccData2 & 0x70) == 0x30)) { + // TODO: Make use of the channel bit captionStringBuilder.append(getSpecialChar(ccData2)); continue; } - // Extended Spanish/Miscellaneous and French character set. + // Extended Western European character set. + // ccData1 - P|0|0|1|C|0|1|S // ccData2 - P|0|1|X|X|X|X|X - if ((ccData1 == 0x12 || ccData1 == 0x1A) && ((ccData2 & 0x60) == 0x20)) { - backspace(); // Remove standard equivalent of the special extended char. - captionStringBuilder.append(getExtendedEsFrChar(ccData2)); - continue; - } + if ((ccData2 & 0x60) == 0x20) { + // Extended Spanish/Miscellaneous and French character set (S = 0). + if (ccData1 == 0x12 || ccData1 == 0x1A) { + // TODO: Make use of the channel bit + backspace(); // Remove standard equivalent of the special extended char. + captionStringBuilder.append(getExtendedEsFrChar(ccData2)); + continue; + } - // Extended Portuguese and German/Danish character set. - // ccData2 - P|0|1|X|X|X|X|X - if ((ccData1 == 0x13 || ccData1 == 0x1B) && ((ccData2 & 0x60) == 0x20)) { - backspace(); // Remove standard equivalent of the special extended char. - captionStringBuilder.append(getExtendedPtDeChar(ccData2)); - continue; + // Extended Portuguese and German/Danish character set (S = 1). + if (ccData1 == 0x13 || ccData1 == 0x1B) { + // TODO: Make use of the channel bit + backspace(); // Remove standard equivalent of the special extended char. + captionStringBuilder.append(getExtendedPtDeChar(ccData2)); + continue; + } } // Control character. @@ -367,15 +389,17 @@ public final class Eia608Decoder implements SubtitleDecoder { private boolean handleCtrl(byte cc1, byte cc2) { boolean isRepeatableControl = isRepeatable(cc1); - if (isRepeatableControl && repeatableControlSet - && repeatableControlCc1 == cc1 - && repeatableControlCc2 == cc2) { - repeatableControlSet = false; - return true; - } else if (isRepeatableControl) { - repeatableControlSet = true; - repeatableControlCc1 = cc1; - repeatableControlCc2 = cc2; + if (isRepeatableControl) { + if (repeatableControlSet + && repeatableControlCc1 == cc1 + && repeatableControlCc2 == cc2) { + repeatableControlSet = false; + return true; + } else { + repeatableControlSet = true; + repeatableControlCc1 = cc1; + repeatableControlCc2 = cc2; + } } if (isMiscCode(cc1, cc2)) { handleMiscCode(cc2); diff --git a/library/src/main/java/com/google/android/exoplayer2/text/eia608/Eia608Subtitle.java b/library/src/main/java/com/google/android/exoplayer2/text/eia608/Eia608Subtitle.java index 6b27004174..5ec8ca6d95 100644 --- a/library/src/main/java/com/google/android/exoplayer2/text/eia608/Eia608Subtitle.java +++ b/library/src/main/java/com/google/android/exoplayer2/text/eia608/Eia608Subtitle.java @@ -15,7 +15,6 @@ */ package com.google.android.exoplayer2.text.eia608; -import android.text.TextUtils; import com.google.android.exoplayer2.text.Cue; import com.google.android.exoplayer2.text.Subtitle; import java.util.Collections; @@ -26,13 +25,17 @@ import java.util.List; */ /* package */ final class Eia608Subtitle implements Subtitle { - private final String text; + private final List cues; /** - * @param text The subtitle text. + * @param cue The subtitle cue. */ - public Eia608Subtitle(String text) { - this.text = text; + public Eia608Subtitle(Cue cue) { + if (cue == null) { + cues = Collections.emptyList(); + } else { + cues = Collections.singletonList(cue); + } } @Override @@ -52,11 +55,8 @@ import java.util.List; @Override public List getCues(long timeUs) { - if (TextUtils.isEmpty(text)) { - return Collections.emptyList(); - } else { - return Collections.singletonList(new Cue(text)); - } + return cues; + } } From d74166cd355a6ae3b2d71382b234cf40606e984c Mon Sep 17 00:00:00 2001 From: anjalibh Date: Mon, 26 Sep 2016 11:20:58 -0700 Subject: [PATCH 23/36] Parse Projection, ProjectionPrivate As described in https://github.com/google/spatial-media/blob/master/docs/spherical-video-v2-rfc.md. Also parse StereoMode as per standard matroska spec. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=134302208 --- .../extractor/mkv/MatroskaExtractor.java | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/mkv/MatroskaExtractor.java b/library/src/main/java/com/google/android/exoplayer2/extractor/mkv/MatroskaExtractor.java index 2d84e73c1e..bc2d891dab 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/mkv/MatroskaExtractor.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/mkv/MatroskaExtractor.java @@ -163,6 +163,9 @@ public final class MatroskaExtractor implements Extractor { private static final int ID_CUE_TRACK_POSITIONS = 0xB7; private static final int ID_CUE_CLUSTER_POSITION = 0xF1; private static final int ID_LANGUAGE = 0x22B59C; + private static final int ID_PROJECTION = 0x7670; + private static final int ID_PROJECTION_PRIVATE = 0x7672; + private static final int ID_STEREO_MODE = 0x53B8; private static final int LACING_NONE = 0; private static final int LACING_XIPH = 1; @@ -362,6 +365,7 @@ public final class MatroskaExtractor implements Extractor { case ID_CUE_POINT: case ID_CUE_TRACK_POSITIONS: case ID_BLOCK_GROUP: + case ID_PROJECTION: return EbmlReader.TYPE_MASTER; case ID_EBML_READ_VERSION: case ID_DOC_TYPE_READ_VERSION: @@ -391,6 +395,7 @@ public final class MatroskaExtractor implements Extractor { case ID_CUE_TIME: case ID_CUE_CLUSTER_POSITION: case ID_REFERENCE_BLOCK: + case ID_STEREO_MODE: return EbmlReader.TYPE_UNSIGNED_INT; case ID_DOC_TYPE: case ID_CODEC_ID: @@ -402,6 +407,7 @@ public final class MatroskaExtractor implements Extractor { case ID_SIMPLE_BLOCK: case ID_BLOCK: case ID_CODEC_PRIVATE: + case ID_PROJECTION_PRIVATE: return EbmlReader.TYPE_BINARY; case ID_DURATION: case ID_SAMPLING_FREQUENCY: @@ -656,6 +662,22 @@ public final class MatroskaExtractor implements Extractor { case ID_BLOCK_DURATION: blockDurationUs = scaleTimecodeToUs(value); return; + case ID_STEREO_MODE: + int layout = (int) value; + switch (layout) { + case 0: + currentTrack.stereoMode = C.STEREO_MODE_MONO; + break; + case 1: + currentTrack.stereoMode = C.STEREO_MODE_LEFT_RIGHT; + break; + case 3: + currentTrack.stereoMode = C.STEREO_MODE_TOP_BOTTOM; + break; + default: + break; + } + return; default: return; } @@ -706,6 +728,10 @@ public final class MatroskaExtractor implements Extractor { currentTrack.codecPrivate = new byte[contentSize]; input.readFully(currentTrack.codecPrivate, 0, contentSize); return; + case ID_PROJECTION_PRIVATE: + currentTrack.projectionData = new byte[contentSize]; + input.readFully(currentTrack.projectionData, 0, contentSize); + return; case ID_CONTENT_COMPRESSION_SETTINGS: // This extractor only supports header stripping, so the payload is the stripped bytes. currentTrack.sampleStrippedBytes = new byte[contentSize]; @@ -1292,6 +1318,9 @@ public final class MatroskaExtractor implements Extractor { public int displayWidth = Format.NO_VALUE; public int displayHeight = Format.NO_VALUE; public int displayUnit = DISPLAY_UNIT_PIXELS; + public byte[] projectionData = null; + @C.StereoMode + public int stereoMode = Format.NO_VALUE; // Audio elements. Initially set to their default values. public int channelCount = 1; @@ -1450,7 +1479,7 @@ public final class MatroskaExtractor implements Extractor { } format = Format.createVideoSampleFormat(Integer.toString(trackId), mimeType, null, Format.NO_VALUE, maxInputSize, width, height, Format.NO_VALUE, initializationData, - Format.NO_VALUE, pixelWidthHeightRatio, drmInitData); + Format.NO_VALUE, pixelWidthHeightRatio, projectionData, stereoMode, drmInitData); } else if (MimeTypes.APPLICATION_SUBRIP.equals(mimeType)) { format = Format.createTextSampleFormat(Integer.toString(trackId), mimeType, null, Format.NO_VALUE, selectionFlags, language, drmInitData); From eaf8218855940ea636212859ae6c7c5c9d493841 Mon Sep 17 00:00:00 2001 From: olly Date: Tue, 27 Sep 2016 04:12:23 -0700 Subject: [PATCH 24/36] Fix shutter open/close behavior ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=134389619 --- .../android/exoplayer2/demo/EventLogger.java | 22 ++-- .../exoplayer2/demo/PlayerActivity.java | 27 +++-- .../exoplayer2/demo/TrackSelectionHelper.java | 6 +- .../exoplayer2/ext/flac/FlacPlaybackTest.java | 3 +- .../exoplayer2/ext/opus/OpusPlaybackTest.java | 3 +- .../exoplayer2/ext/vp9/VpxPlaybackTest.java | 3 +- .../exoplayer2/DefaultLoadControl.java | 4 +- .../exoplayer2/ExoPlayerImplInternal.java | 67 ++++++----- .../android/exoplayer2/LoadControl.java | 7 +- .../android/exoplayer2/SimpleExoPlayer.java | 54 ++++++--- .../trackselection/DefaultTrackSelector.java | 8 +- .../trackselection/MappingTrackSelector.java | 109 ++---------------- ...lectionArray.java => TrackSelections.java} | 16 ++- .../trackselection/TrackSelector.java | 99 ++++++++++++---- .../exoplayer2/ui/SimpleExoPlayerView.java | 11 +- 15 files changed, 220 insertions(+), 219 deletions(-) rename library/src/main/java/com/google/android/exoplayer2/trackselection/{TrackSelectionArray.java => TrackSelections.java} (80%) diff --git a/demo/src/main/java/com/google/android/exoplayer2/demo/EventLogger.java b/demo/src/main/java/com/google/android/exoplayer2/demo/EventLogger.java index 7fd32ddbe1..8c12290d35 100644 --- a/demo/src/main/java/com/google/android/exoplayer2/demo/EventLogger.java +++ b/demo/src/main/java/com/google/android/exoplayer2/demo/EventLogger.java @@ -38,9 +38,10 @@ import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener; import com.google.android.exoplayer2.source.ExtractorMediaSource; import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.TrackGroupArray; -import com.google.android.exoplayer2.trackselection.MappingTrackSelector; -import com.google.android.exoplayer2.trackselection.MappingTrackSelector.TrackInfo; +import com.google.android.exoplayer2.trackselection.MappingTrackSelector.MappedTrackInfo; import com.google.android.exoplayer2.trackselection.TrackSelection; +import com.google.android.exoplayer2.trackselection.TrackSelections; +import com.google.android.exoplayer2.trackselection.TrackSelector; import com.google.android.exoplayer2.upstream.DataSpec; import com.google.android.exoplayer2.video.VideoRendererEventListener; import java.io.IOException; @@ -54,7 +55,7 @@ import java.util.Locale; /* package */ final class EventLogger implements ExoPlayer.EventListener, AudioRendererEventListener, VideoRendererEventListener, AdaptiveMediaSourceEventListener, ExtractorMediaSource.EventListener, StreamingDrmSessionManager.EventListener, - MappingTrackSelector.EventListener, MetadataRenderer.Output> { + TrackSelector.EventListener, MetadataRenderer.Output> { private static final String TAG = "EventLogger"; private static final int MAX_TIMELINE_ITEM_LINES = 3; @@ -125,23 +126,24 @@ import java.util.Locale; // MappingTrackSelector.EventListener @Override - public void onTracksChanged(TrackInfo trackInfo) { + public void onTrackSelectionsChanged(TrackSelections trackSelections) { Log.d(TAG, "Tracks ["); // Log tracks associated to renderers. - for (int rendererIndex = 0; rendererIndex < trackInfo.rendererCount; rendererIndex++) { - TrackGroupArray trackGroups = trackInfo.getTrackGroups(rendererIndex); - TrackSelection trackSelection = trackInfo.getTrackSelection(rendererIndex); + MappedTrackInfo info = trackSelections.info; + for (int rendererIndex = 0; rendererIndex < trackSelections.length; rendererIndex++) { + TrackGroupArray trackGroups = info.getTrackGroups(rendererIndex); + TrackSelection trackSelection = trackSelections.get(rendererIndex); if (trackGroups.length > 0) { Log.d(TAG, " Renderer:" + rendererIndex + " ["); for (int groupIndex = 0; groupIndex < trackGroups.length; groupIndex++) { TrackGroup trackGroup = trackGroups.get(groupIndex); String adaptiveSupport = getAdaptiveSupportString( - trackGroup.length, trackInfo.getAdaptiveSupport(rendererIndex, groupIndex, false)); + trackGroup.length, info.getAdaptiveSupport(rendererIndex, groupIndex, false)); Log.d(TAG, " Group:" + groupIndex + ", adaptive_supported=" + adaptiveSupport + " ["); for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) { String status = getTrackStatusString(trackSelection, trackGroup, trackIndex); String formatSupport = getFormatSupportString( - trackInfo.getTrackFormatSupport(rendererIndex, groupIndex, trackIndex)); + info.getTrackFormatSupport(rendererIndex, groupIndex, trackIndex)); Log.d(TAG, " " + status + " Track:" + trackIndex + ", " + getFormatString(trackGroup.getFormat(trackIndex)) + ", supported=" + formatSupport); @@ -152,7 +154,7 @@ import java.util.Locale; } } // Log tracks not associated with a renderer. - TrackGroupArray trackGroups = trackInfo.getUnassociatedTrackGroups(); + TrackGroupArray trackGroups = info.getUnassociatedTrackGroups(); if (trackGroups.length > 0) { Log.d(TAG, " Renderer:None ["); for (int groupIndex = 0; groupIndex < trackGroups.length; groupIndex++) { diff --git a/demo/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java b/demo/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java index 0607c296c6..481db5b71c 100644 --- a/demo/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java +++ b/demo/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java @@ -36,6 +36,7 @@ import com.google.android.exoplayer2.ExoPlayerFactory; import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.drm.DrmSessionManager; +import com.google.android.exoplayer2.drm.FrameworkMediaCrypto; import com.google.android.exoplayer2.drm.FrameworkMediaDrm; import com.google.android.exoplayer2.drm.HttpMediaDrmCallback; import com.google.android.exoplayer2.drm.StreamingDrmSessionManager; @@ -55,8 +56,10 @@ import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource; import com.google.android.exoplayer2.trackselection.AdaptiveVideoTrackSelection; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.trackselection.MappingTrackSelector; -import com.google.android.exoplayer2.trackselection.MappingTrackSelector.TrackInfo; +import com.google.android.exoplayer2.trackselection.MappingTrackSelector.MappedTrackInfo; import com.google.android.exoplayer2.trackselection.TrackSelection; +import com.google.android.exoplayer2.trackselection.TrackSelections; +import com.google.android.exoplayer2.trackselection.TrackSelector; import com.google.android.exoplayer2.ui.DebugTextViewHelper; import com.google.android.exoplayer2.ui.PlaybackControlView; import com.google.android.exoplayer2.ui.SimpleExoPlayerView; @@ -77,7 +80,7 @@ import java.util.UUID; * An activity that plays media using {@link SimpleExoPlayer}. */ public class PlayerActivity extends Activity implements OnClickListener, ExoPlayer.EventListener, - MappingTrackSelector.EventListener, PlaybackControlView.VisibilityListener { + TrackSelector.EventListener, PlaybackControlView.VisibilityListener { public static final String DRM_SCHEME_UUID_EXTRA = "drm_scheme_uuid"; public static final String DRM_LICENSE_URL = "drm_license_url"; @@ -203,7 +206,7 @@ public class PlayerActivity extends Activity implements OnClickListener, ExoPlay initializePlayer(); } else if (view.getParent() == debugRootView) { trackSelectionHelper.showSelectionDialog(this, ((Button) view).getText(), - trackSelector.getTrackInfo(), (int) view.getTag()); + trackSelector.getCurrentSelections().info, (int) view.getTag()); } } @@ -222,7 +225,7 @@ public class PlayerActivity extends Activity implements OnClickListener, ExoPlay boolean preferExtensionDecoders = intent.getBooleanExtra(PREFER_EXTENSION_DECODERS, false); UUID drmSchemeUuid = intent.hasExtra(DRM_SCHEME_UUID_EXTRA) ? UUID.fromString(intent.getStringExtra(DRM_SCHEME_UUID_EXTRA)) : null; - DrmSessionManager drmSessionManager = null; + DrmSessionManager drmSessionManager = null; if (drmSchemeUuid != null) { String drmLicenseUrl = intent.getStringExtra(DRM_LICENSE_URL); String[] keyRequestPropertiesArray = intent.getStringArrayExtra(DRM_KEY_REQUEST_PROPERTIES); @@ -333,9 +336,8 @@ public class PlayerActivity extends Activity implements OnClickListener, ExoPlay } } - private DrmSessionManager buildDrmSessionManager(UUID uuid, String licenseUrl, - Map keyRequestProperties) - throws UnsupportedDrmException { + private DrmSessionManager buildDrmSessionManager(UUID uuid, + String licenseUrl, Map keyRequestProperties) throws UnsupportedDrmException { if (Util.SDK_INT < 18) { return null; } @@ -452,8 +454,9 @@ public class PlayerActivity extends Activity implements OnClickListener, ExoPlay // MappingTrackSelector.EventListener implementation @Override - public void onTracksChanged(TrackInfo trackInfo) { + public void onTrackSelectionsChanged(TrackSelections trackSelections) { updateButtonVisibilities(); + MappedTrackInfo trackInfo = trackSelections.info; if (trackInfo.hasOnlyUnplayableTracks(C.TRACK_TYPE_VIDEO)) { showToast(R.string.error_unsupported_video); } @@ -474,14 +477,14 @@ public class PlayerActivity extends Activity implements OnClickListener, ExoPlay return; } - TrackInfo trackInfo = trackSelector.getTrackInfo(); - if (trackInfo == null) { + TrackSelections trackSelections = trackSelector.getCurrentSelections(); + if (trackSelections == null) { return; } - int rendererCount = trackInfo.rendererCount; + int rendererCount = trackSelections.length; for (int i = 0; i < rendererCount; i++) { - TrackGroupArray trackGroups = trackInfo.getTrackGroups(i); + TrackGroupArray trackGroups = trackSelections.info.getTrackGroups(i); if (trackGroups.length != 0) { Button button = new Button(this); int label; diff --git a/demo/src/main/java/com/google/android/exoplayer2/demo/TrackSelectionHelper.java b/demo/src/main/java/com/google/android/exoplayer2/demo/TrackSelectionHelper.java index 8102cd7034..8892c138d0 100644 --- a/demo/src/main/java/com/google/android/exoplayer2/demo/TrackSelectionHelper.java +++ b/demo/src/main/java/com/google/android/exoplayer2/demo/TrackSelectionHelper.java @@ -31,8 +31,8 @@ import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.trackselection.FixedTrackSelection; import com.google.android.exoplayer2.trackselection.MappingTrackSelector; +import com.google.android.exoplayer2.trackselection.MappingTrackSelector.MappedTrackInfo; import com.google.android.exoplayer2.trackselection.MappingTrackSelector.SelectionOverride; -import com.google.android.exoplayer2.trackselection.MappingTrackSelector.TrackInfo; import com.google.android.exoplayer2.trackselection.RandomTrackSelection; import com.google.android.exoplayer2.trackselection.TrackSelection; import com.google.android.exoplayer2.util.MimeTypes; @@ -51,7 +51,7 @@ import java.util.Locale; private final MappingTrackSelector selector; private final TrackSelection.Factory adaptiveVideoTrackSelectionFactory; - private TrackInfo trackInfo; + private MappedTrackInfo trackInfo; private int rendererIndex; private TrackGroupArray trackGroups; private boolean[] trackGroupsAdaptive; @@ -82,7 +82,7 @@ import java.util.Locale; * @param trackInfo The current track information. * @param rendererIndex The index of the renderer. */ - public void showSelectionDialog(Activity activity, CharSequence title, TrackInfo trackInfo, + public void showSelectionDialog(Activity activity, CharSequence title, MappedTrackInfo trackInfo, int rendererIndex) { this.trackInfo = trackInfo; this.rendererIndex = rendererIndex; diff --git a/extensions/flac/src/androidTest/java/com/google/android/exoplayer2/ext/flac/FlacPlaybackTest.java b/extensions/flac/src/androidTest/java/com/google/android/exoplayer2/ext/flac/FlacPlaybackTest.java index 5df912ace2..29a22f380a 100644 --- a/extensions/flac/src/androidTest/java/com/google/android/exoplayer2/ext/flac/FlacPlaybackTest.java +++ b/extensions/flac/src/androidTest/java/com/google/android/exoplayer2/ext/flac/FlacPlaybackTest.java @@ -17,6 +17,7 @@ package com.google.android.exoplayer2.ext.flac; import android.content.Context; import android.net.Uri; +import android.os.Handler; import android.os.Looper; import android.test.InstrumentationTestCase; import com.google.android.exoplayer2.ExoPlaybackException; @@ -71,7 +72,7 @@ public class FlacPlaybackTest extends InstrumentationTestCase { public void run() { Looper.prepare(); LibflacAudioRenderer audioRenderer = new LibflacAudioRenderer(); - DefaultTrackSelector trackSelector = new DefaultTrackSelector(null); + DefaultTrackSelector trackSelector = new DefaultTrackSelector(new Handler()); player = ExoPlayerFactory.newInstance(new Renderer[] {audioRenderer}, trackSelector); player.addListener(this); ExtractorMediaSource mediaSource = new ExtractorMediaSource( diff --git a/extensions/opus/src/androidTest/java/com/google/android/exoplayer2/ext/opus/OpusPlaybackTest.java b/extensions/opus/src/androidTest/java/com/google/android/exoplayer2/ext/opus/OpusPlaybackTest.java index 1f554cbebc..4f723698a4 100644 --- a/extensions/opus/src/androidTest/java/com/google/android/exoplayer2/ext/opus/OpusPlaybackTest.java +++ b/extensions/opus/src/androidTest/java/com/google/android/exoplayer2/ext/opus/OpusPlaybackTest.java @@ -17,6 +17,7 @@ package com.google.android.exoplayer2.ext.opus; import android.content.Context; import android.net.Uri; +import android.os.Handler; import android.os.Looper; import android.test.InstrumentationTestCase; import com.google.android.exoplayer2.ExoPlaybackException; @@ -71,7 +72,7 @@ public class OpusPlaybackTest extends InstrumentationTestCase { public void run() { Looper.prepare(); LibopusAudioRenderer audioRenderer = new LibopusAudioRenderer(); - DefaultTrackSelector trackSelector = new DefaultTrackSelector(null); + DefaultTrackSelector trackSelector = new DefaultTrackSelector(new Handler()); player = ExoPlayerFactory.newInstance(new Renderer[] {audioRenderer}, trackSelector); player.addListener(this); ExtractorMediaSource mediaSource = new ExtractorMediaSource( diff --git a/extensions/vp9/src/androidTest/java/com/google/android/exoplayer2/ext/vp9/VpxPlaybackTest.java b/extensions/vp9/src/androidTest/java/com/google/android/exoplayer2/ext/vp9/VpxPlaybackTest.java index 553157605e..c5f61cf231 100644 --- a/extensions/vp9/src/androidTest/java/com/google/android/exoplayer2/ext/vp9/VpxPlaybackTest.java +++ b/extensions/vp9/src/androidTest/java/com/google/android/exoplayer2/ext/vp9/VpxPlaybackTest.java @@ -17,6 +17,7 @@ package com.google.android.exoplayer2.ext.vp9; import android.content.Context; import android.net.Uri; +import android.os.Handler; import android.os.Looper; import android.test.InstrumentationTestCase; import com.google.android.exoplayer2.ExoPlaybackException; @@ -87,7 +88,7 @@ public class VpxPlaybackTest extends InstrumentationTestCase { public void run() { Looper.prepare(); LibvpxVideoRenderer videoRenderer = new LibvpxVideoRenderer(true, 0); - DefaultTrackSelector trackSelector = new DefaultTrackSelector(null); + DefaultTrackSelector trackSelector = new DefaultTrackSelector(new Handler()); player = ExoPlayerFactory.newInstance(new Renderer[] {videoRenderer}, trackSelector); player.addListener(this); ExtractorMediaSource mediaSource = new ExtractorMediaSource( diff --git a/library/src/main/java/com/google/android/exoplayer2/DefaultLoadControl.java b/library/src/main/java/com/google/android/exoplayer2/DefaultLoadControl.java index a24a2363a2..1416dd0ae4 100644 --- a/library/src/main/java/com/google/android/exoplayer2/DefaultLoadControl.java +++ b/library/src/main/java/com/google/android/exoplayer2/DefaultLoadControl.java @@ -16,7 +16,7 @@ package com.google.android.exoplayer2; import com.google.android.exoplayer2.source.TrackGroupArray; -import com.google.android.exoplayer2.trackselection.TrackSelectionArray; +import com.google.android.exoplayer2.trackselection.TrackSelections; import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.upstream.DefaultAllocator; import com.google.android.exoplayer2.util.Util; @@ -106,7 +106,7 @@ public final class DefaultLoadControl implements LoadControl { @Override public void onTracksSelected(Renderer[] renderers, TrackGroupArray trackGroups, - TrackSelectionArray trackSelections) { + TrackSelections trackSelections) { targetBufferSize = 0; for (int i = 0; i < renderers.length; i++) { if (trackSelections.get(i) != null) { diff --git a/library/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java b/library/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java index fe555a87ec..ed19afa29c 100644 --- a/library/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java +++ b/library/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java @@ -27,7 +27,7 @@ import com.google.android.exoplayer2.source.MediaPeriod; import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.SampleStream; import com.google.android.exoplayer2.trackselection.TrackSelection; -import com.google.android.exoplayer2.trackselection.TrackSelectionArray; +import com.google.android.exoplayer2.trackselection.TrackSelections; import com.google.android.exoplayer2.trackselection.TrackSelector; import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.MediaClock; @@ -40,8 +40,8 @@ import java.io.IOException; /** * Implements the internal behavior of {@link ExoPlayerImpl}. */ -/* package */ final class ExoPlayerImplInternal implements Handler.Callback, MediaPeriod.Callback, - TrackSelector.InvalidationListener, MediaSource.Listener { +/* package */ final class ExoPlayerImplInternal implements Handler.Callback, + MediaPeriod.Callback, TrackSelector.InvalidationListener, MediaSource.Listener { /** * Playback position information which is read on the application's thread by @@ -100,7 +100,7 @@ import java.io.IOException; private final Renderer[] renderers; private final RendererCapabilities[] rendererCapabilities; - private final TrackSelector trackSelector; + private final TrackSelector trackSelector; private final LoadControl loadControl; private final StandaloneMediaClock standaloneMediaClock; private final Handler handler; @@ -128,13 +128,13 @@ import java.io.IOException; private boolean isTimelineReady; private boolean isTimelineEnded; private int bufferAheadPeriodCount; - private MediaPeriodHolder playingPeriodHolder; - private MediaPeriodHolder readingPeriodHolder; - private MediaPeriodHolder loadingPeriodHolder; + private MediaPeriodHolder playingPeriodHolder; + private MediaPeriodHolder readingPeriodHolder; + private MediaPeriodHolder loadingPeriodHolder; private Timeline timeline; - public ExoPlayerImplInternal(Renderer[] renderers, TrackSelector trackSelector, + public ExoPlayerImplInternal(Renderer[] renderers, TrackSelector trackSelector, LoadControl loadControl, boolean playWhenReady, Handler eventHandler, PlaybackInfo playbackInfo) { this.renderers = renderers; @@ -538,8 +538,8 @@ import java.io.IOException; } // Clear the timeline, but keep the requested period if it is already prepared. - MediaPeriodHolder periodHolder = playingPeriodHolder; - MediaPeriodHolder newPlayingPeriodHolder = null; + MediaPeriodHolder periodHolder = playingPeriodHolder; + MediaPeriodHolder newPlayingPeriodHolder = null; while (periodHolder != null) { if (periodHolder.index == periodIndex && periodHolder.prepared) { newPlayingPeriodHolder = periodHolder; @@ -671,7 +671,7 @@ import java.io.IOException; return; } // Reselect tracks on each period in turn, until the selection changes. - MediaPeriodHolder periodHolder = playingPeriodHolder; + MediaPeriodHolder periodHolder = playingPeriodHolder; boolean selectionsChangedForReadPeriod = true; while (true) { if (periodHolder == null || !periodHolder.prepared) { @@ -738,7 +738,7 @@ import java.io.IOException; } } } - trackSelector.onSelectionActivated(playingPeriodHolder.trackSelectionData); + trackSelector.onSelectionActivated(playingPeriodHolder.trackSelections); enableRenderers(rendererWasEnabledFlags, enabledRendererCount); } else { // Release and re-prepare/buffer periods after the one whose selection changed. @@ -810,11 +810,11 @@ import java.io.IOException; playingPeriodHolder.setIndex(timeline, timeline.getWindow(period.windowIndex, window), index); - MediaPeriodHolder previousPeriod = playingPeriodHolder; + MediaPeriodHolder previousPeriod = playingPeriodHolder; boolean seenReadingPeriod = false; bufferAheadPeriodCount = 0; while (previousPeriod.next != null) { - MediaPeriodHolder periodHolder = previousPeriod.next; + MediaPeriodHolder periodHolder = previousPeriod.next; index++; timeline.getPeriod(index, period, true); if (!periodHolder.uid.equals(period.uid)) { @@ -954,8 +954,9 @@ import java.io.IOException; Object newPeriodUid = timeline.getPeriod(newLoadingPeriodIndex, period, true).uid; MediaPeriod newMediaPeriod = mediaSource.createPeriod(newLoadingPeriodIndex, this, loadControl.getAllocator(), periodStartPositionUs); - MediaPeriodHolder newPeriodHolder = new MediaPeriodHolder(renderers, rendererCapabilities, - trackSelector, mediaSource, newMediaPeriod, newPeriodUid, periodStartPositionUs); + MediaPeriodHolder newPeriodHolder = new MediaPeriodHolder<>(renderers, + rendererCapabilities, trackSelector, mediaSource, newMediaPeriod, newPeriodUid, + periodStartPositionUs); timeline.getWindow(windowIndex, window); newPeriodHolder.setIndex(timeline, window, newLoadingPeriodIndex); if (loadingPeriodHolder != null) { @@ -1012,9 +1013,9 @@ import java.io.IOException; } } if (readingPeriodHolder.next != null && readingPeriodHolder.next.prepared) { - TrackSelectionArray oldTrackSelections = readingPeriodHolder.trackSelections; + TrackSelections oldTrackSelections = readingPeriodHolder.trackSelections; readingPeriodHolder = readingPeriodHolder.next; - TrackSelectionArray newTrackSelections = readingPeriodHolder.trackSelections; + TrackSelections newTrackSelections = readingPeriodHolder.trackSelections; for (int i = 0; i < renderers.length; i++) { Renderer renderer = renderers[i]; TrackSelection oldSelection = oldTrackSelections.get(i); @@ -1088,14 +1089,15 @@ import java.io.IOException; } } - private void releasePeriodHoldersFrom(MediaPeriodHolder periodHolder) { + private void releasePeriodHoldersFrom(MediaPeriodHolder periodHolder) { while (periodHolder != null) { periodHolder.release(); periodHolder = periodHolder.next; } } - private void setPlayingPeriodHolder(MediaPeriodHolder periodHolder) throws ExoPlaybackException { + private void setPlayingPeriodHolder(MediaPeriodHolder periodHolder) + throws ExoPlaybackException { int enabledRendererCount = 0; boolean[] rendererWasEnabledFlags = new boolean[renderers.length]; for (int i = 0; i < renderers.length; i++) { @@ -1118,7 +1120,7 @@ import java.io.IOException; } } - trackSelector.onSelectionActivated(periodHolder.trackSelectionData); + trackSelector.onSelectionActivated(periodHolder.trackSelections); playingPeriodHolder = periodHolder; enableRenderers(rendererWasEnabledFlags, enabledRendererCount); } @@ -1175,7 +1177,7 @@ import java.io.IOException; /** * Holds a {@link MediaPeriod} with information required to play it as part of a timeline. */ - private static final class MediaPeriodHolder { + private static final class MediaPeriodHolder { public final MediaPeriod mediaPeriod; public final Object uid; @@ -1189,21 +1191,20 @@ import java.io.IOException; public boolean prepared; public boolean hasEnabledTracks; public long rendererPositionOffsetUs; - public MediaPeriodHolder next; + public MediaPeriodHolder next; public boolean needsContinueLoading; private final Renderer[] renderers; private final RendererCapabilities[] rendererCapabilities; - private final TrackSelector trackSelector; + private final TrackSelector trackSelector; private final MediaSource mediaSource; - private Object trackSelectionData; - private TrackSelectionArray trackSelections; - private TrackSelectionArray periodTrackSelections; + private TrackSelections trackSelections; + private TrackSelections periodTrackSelections; public MediaPeriodHolder(Renderer[] renderers, RendererCapabilities[] rendererCapabilities, - TrackSelector trackSelector, MediaSource mediaSource, MediaPeriod mediaPeriod, Object uid, - long positionUs) { + TrackSelector trackSelector, MediaSource mediaSource, MediaPeriod mediaPeriod, + Object uid, long positionUs) { this.renderers = renderers; this.rendererCapabilities = rendererCapabilities; this.trackSelector = trackSelector; @@ -1215,7 +1216,7 @@ import java.io.IOException; startPositionUs = positionUs; } - public void setNext(MediaPeriodHolder next) { + public void setNext(MediaPeriodHolder next) { this.next = next; } @@ -1237,14 +1238,12 @@ import java.io.IOException; } public boolean selectTracks() throws ExoPlaybackException { - Pair result = - trackSelector.selectTracks(rendererCapabilities, mediaPeriod.getTrackGroups()); - TrackSelectionArray newTrackSelections = result.first; + TrackSelections newTrackSelections = trackSelector.selectTracks(rendererCapabilities, + mediaPeriod.getTrackGroups()); if (newTrackSelections.equals(periodTrackSelections)) { return false; } trackSelections = newTrackSelections; - trackSelectionData = result.second; return true; } diff --git a/library/src/main/java/com/google/android/exoplayer2/LoadControl.java b/library/src/main/java/com/google/android/exoplayer2/LoadControl.java index 213d5e5297..7842ada276 100644 --- a/library/src/main/java/com/google/android/exoplayer2/LoadControl.java +++ b/library/src/main/java/com/google/android/exoplayer2/LoadControl.java @@ -17,8 +17,7 @@ package com.google.android.exoplayer2; import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.TrackGroupArray; -import com.google.android.exoplayer2.trackselection.TrackSelection; -import com.google.android.exoplayer2.trackselection.TrackSelectionArray; +import com.google.android.exoplayer2.trackselection.TrackSelections; import com.google.android.exoplayer2.upstream.Allocator; /** @@ -31,10 +30,10 @@ public interface LoadControl { * * @param renderers The renderers. * @param trackGroups The {@link TrackGroup}s from which the selection was made. - * @param trackSelections The {@link TrackSelection}s that were made. + * @param trackSelections The track selections that were made. */ void onTracksSelected(Renderer[] renderers, TrackGroupArray trackGroups, - TrackSelectionArray trackSelections); + TrackSelections trackSelections); /** * Called by the player when all tracks are disabled. diff --git a/library/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java b/library/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java index 07702c8f8c..5eda502da5 100644 --- a/library/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java +++ b/library/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java @@ -33,6 +33,7 @@ import com.google.android.exoplayer2.audio.AudioTrack; import com.google.android.exoplayer2.audio.MediaCodecAudioRenderer; import com.google.android.exoplayer2.decoder.DecoderCounters; import com.google.android.exoplayer2.drm.DrmSessionManager; +import com.google.android.exoplayer2.drm.FrameworkMediaCrypto; import com.google.android.exoplayer2.mediacodec.MediaCodecSelector; import com.google.android.exoplayer2.metadata.MetadataRenderer; import com.google.android.exoplayer2.metadata.id3.Id3Decoder; @@ -40,6 +41,7 @@ import com.google.android.exoplayer2.metadata.id3.Id3Frame; import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.text.Cue; import com.google.android.exoplayer2.text.TextRenderer; +import com.google.android.exoplayer2.trackselection.TrackSelections; import com.google.android.exoplayer2.trackselection.TrackSelector; import com.google.android.exoplayer2.video.MediaCodecVideoRenderer; import com.google.android.exoplayer2.video.VideoRendererEventListener; @@ -80,18 +82,14 @@ public final class SimpleExoPlayer implements ExoPlayer { /** * Called when a frame is rendered for the first time since setting the surface, and when a - * frame is rendered for the first time since the renderer was reset. - * - * @param surface The {@link Surface} to which a first frame has been rendered. + * frame is rendered for the first time since a video track was selected. */ - void onRenderedFirstFrame(Surface surface); + void onRenderedFirstFrame(); /** - * Called when the renderer is disabled. - * - * @param counters {@link DecoderCounters} that were updated by the renderer. + * Called when a video track is no longer selected. */ - void onVideoDisabled(DecoderCounters counters); + void onVideoTracksDisabled(); } @@ -105,9 +103,11 @@ public final class SimpleExoPlayer implements ExoPlayer { private final int videoRendererCount; private final int audioRendererCount; + private boolean videoTracksEnabled; private Format videoFormat; private Format audioFormat; + private Surface surface; private SurfaceHolder surfaceHolder; private TextureView textureView; private TextRenderer.Output textOutput; @@ -121,11 +121,12 @@ public final class SimpleExoPlayer implements ExoPlayer { private float volume; private PlaybackParamsHolder playbackParamsHolder; - /* package */ SimpleExoPlayer(Context context, TrackSelector trackSelector, - LoadControl loadControl, DrmSessionManager drmSessionManager, + /* package */ SimpleExoPlayer(Context context, TrackSelector trackSelector, + LoadControl loadControl, DrmSessionManager drmSessionManager, boolean preferExtensionDecoders, long allowedVideoJoiningTimeMs) { mainHandler = new Handler(); componentListener = new ComponentListener(); + trackSelector.addListener(componentListener); // Build the renderers. ArrayList renderersList = new ArrayList<>(); @@ -509,8 +510,9 @@ public final class SimpleExoPlayer implements ExoPlayer { // Internal methods. - private void buildRenderers(Context context, DrmSessionManager drmSessionManager, - ArrayList renderersList, long allowedVideoJoiningTimeMs) { + private void buildRenderers(Context context, + DrmSessionManager drmSessionManager, ArrayList renderersList, + long allowedVideoJoiningTimeMs) { MediaCodecVideoRenderer videoRenderer = new MediaCodecVideoRenderer(context, MediaCodecSelector.DEFAULT, MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT, allowedVideoJoiningTimeMs, drmSessionManager, false, mainHandler, componentListener, @@ -601,6 +603,7 @@ public final class SimpleExoPlayer implements ExoPlayer { } private void setVideoSurfaceInternal(Surface surface) { + this.surface = surface; ExoPlayerMessage[] messages = new ExoPlayerMessage[videoRendererCount]; int count = 0; for (Renderer renderer : renderers) { @@ -618,7 +621,8 @@ public final class SimpleExoPlayer implements ExoPlayer { private final class ComponentListener implements VideoRendererEventListener, AudioRendererEventListener, TextRenderer.Output, MetadataRenderer.Output>, - SurfaceHolder.Callback, TextureView.SurfaceTextureListener { + SurfaceHolder.Callback, TextureView.SurfaceTextureListener, + TrackSelector.EventListener { // VideoRendererEventListener implementation @@ -669,8 +673,8 @@ public final class SimpleExoPlayer implements ExoPlayer { @Override public void onRenderedFirstFrame(Surface surface) { - if (videoListener != null) { - videoListener.onRenderedFirstFrame(surface); + if (videoListener != null && SimpleExoPlayer.this.surface == surface) { + videoListener.onRenderedFirstFrame(); } if (videoDebugListener != null) { videoDebugListener.onRenderedFirstFrame(surface); @@ -679,9 +683,6 @@ public final class SimpleExoPlayer implements ExoPlayer { @Override public void onVideoDisabled(DecoderCounters counters) { - if (videoListener != null) { - videoListener.onVideoDisabled(counters); - } if (videoDebugListener != null) { videoDebugListener.onVideoDisabled(counters); } @@ -800,6 +801,23 @@ public final class SimpleExoPlayer implements ExoPlayer { // Do nothing. } + // TrackSelector.EventListener implementation + + @Override + public void onTrackSelectionsChanged(TrackSelections trackSelections) { + boolean videoTracksEnabled = false; + for (int i = 0; i < renderers.length; i++) { + if (renderers[i].getTrackType() == C.TRACK_TYPE_VIDEO && trackSelections.get(i) != null) { + videoTracksEnabled = true; + break; + } + } + if (videoListener != null && SimpleExoPlayer.this.videoTracksEnabled && !videoTracksEnabled) { + videoListener.onVideoTracksDisabled(); + } + SimpleExoPlayer.this.videoTracksEnabled = videoTracksEnabled; + } + } @TargetApi(23) diff --git a/library/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java b/library/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java index 61f46bdadd..02b2ef5de9 100644 --- a/library/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java +++ b/library/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java @@ -453,10 +453,10 @@ public class DefaultTrackSelector extends MappingTrackSelector { case C.TRACK_TYPE_VIDEO: rendererTrackSelections[i] = selectVideoTrack(rendererCapabilities[i], rendererTrackGroupArrays[i], rendererFormatSupports[i], params.maxVideoWidth, - params.maxVideoHeight, - params.allowNonSeamlessAdaptiveness, params.allowMixedMimeAdaptiveness, - params.viewportWidth, params.viewportHeight, params.orientationMayChange, - adaptiveVideoTrackSelectionFactory, params.exceedVideoConstraintsIfNecessary); + params.maxVideoHeight, params.allowNonSeamlessAdaptiveness, + params.allowMixedMimeAdaptiveness, params.viewportWidth, params.viewportHeight, + params.orientationMayChange, adaptiveVideoTrackSelectionFactory, + params.exceedVideoConstraintsIfNecessary); break; case C.TRACK_TYPE_AUDIO: rendererTrackSelections[i] = selectAudioTrack(rendererTrackGroupArrays[i], diff --git a/library/src/main/java/com/google/android/exoplayer2/trackselection/MappingTrackSelector.java b/library/src/main/java/com/google/android/exoplayer2/trackselection/MappingTrackSelector.java index 5097079874..3826ee4668 100644 --- a/library/src/main/java/com/google/android/exoplayer2/trackselection/MappingTrackSelector.java +++ b/library/src/main/java/com/google/android/exoplayer2/trackselection/MappingTrackSelector.java @@ -16,39 +16,23 @@ package com.google.android.exoplayer2.trackselection; import android.os.Handler; -import android.util.Pair; import android.util.SparseArray; import android.util.SparseBooleanArray; import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.RendererCapabilities; import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.TrackGroupArray; -import com.google.android.exoplayer2.util.Assertions; +import com.google.android.exoplayer2.trackselection.MappingTrackSelector.MappedTrackInfo; import com.google.android.exoplayer2.util.Util; import java.util.Arrays; import java.util.HashMap; import java.util.Map; -import java.util.concurrent.CopyOnWriteArraySet; /** * Base class for {@link TrackSelector}s that first establish a mapping between {@link TrackGroup}s * and renderers, and then from that mapping create a {@link TrackSelection} for each renderer. */ -public abstract class MappingTrackSelector extends TrackSelector { - - /** - * Listener of {@link MappingTrackSelector} events. - */ - public interface EventListener { - - /** - * Called when the track information has changed. - * - * @param trackInfo Contains the new track and track selection information. - */ - void onTracksChanged(TrackInfo trackInfo); - - } +public abstract class MappingTrackSelector extends TrackSelector { /** * A track selection override. @@ -96,51 +80,19 @@ public abstract class MappingTrackSelector extends TrackSelector { } - private final Handler eventHandler; - private final CopyOnWriteArraySet listeners; private final SparseArray> selectionOverrides; private final SparseBooleanArray rendererDisabledFlags; - private TrackInfo activeTrackInfo; - /** * @param eventHandler A handler to use when delivering events to listeners added via * {@link #addListener(EventListener)}. */ public MappingTrackSelector(Handler eventHandler) { - this.eventHandler = eventHandler; - this.listeners = new CopyOnWriteArraySet<>(); + super(eventHandler); selectionOverrides = new SparseArray<>(); rendererDisabledFlags = new SparseBooleanArray(); } - /** - * Register a listener to receive events from the selector. The listener's methods will be called - * using the {@link Handler} that was passed to the constructor. - * - * @param listener The listener to register. - */ - public final void addListener(EventListener listener) { - Assertions.checkState(eventHandler != null); - listeners.add(listener); - } - - /** - * Unregister a listener. The listener will no longer receive events from the selector. - * - * @param listener The listener to unregister. - */ - public final void removeListener(EventListener listener) { - listeners.remove(listener); - } - - /** - * Returns information about the current tracks and track selection for each renderer. - */ - public final TrackInfo getTrackInfo() { - return activeTrackInfo; - } - /** * Sets whether the renderer at the specified index is disabled. * @@ -272,13 +224,7 @@ public abstract class MappingTrackSelector extends TrackSelector { // TrackSelector implementation. @Override - public final void onSelectionActivated(Object selectionInfo) { - activeTrackInfo = (TrackInfo) selectionInfo; - notifyTrackInfoChanged(activeTrackInfo); - } - - @Override - public final Pair selectTracks( + public final TrackSelections selectTracks( RendererCapabilities[] rendererCapabilities, TrackGroupArray trackGroups) throws ExoPlaybackException { // Structures into which data will be written during the selection. The extra item at the end @@ -345,11 +291,10 @@ public abstract class MappingTrackSelector extends TrackSelector { } // Package up the track information and selections. - TrackSelectionArray trackSelectionArray = new TrackSelectionArray(trackSelections); - TrackInfo trackInfo = new TrackInfo(rendererTrackTypes, rendererTrackGroupArrays, - trackSelections, mixedMimeTypeAdaptationSupport, rendererFormatSupports, + MappedTrackInfo mappedTrackInfo = new MappedTrackInfo(rendererTrackTypes, + rendererTrackGroupArrays, mixedMimeTypeAdaptationSupport, rendererFormatSupports, unassociatedTrackGroupArray); - return Pair.create(trackSelectionArray, trackInfo); + return new TrackSelections<>(mappedTrackInfo, trackSelections); } /** @@ -446,23 +391,10 @@ public abstract class MappingTrackSelector extends TrackSelector { return mixedMimeTypeAdaptationSupport; } - private void notifyTrackInfoChanged(final TrackInfo trackInfo) { - if (eventHandler != null) { - eventHandler.post(new Runnable() { - @Override - public void run() { - for (EventListener listener : listeners) { - listener.onTracksChanged(trackInfo); - } - } - }); - } - } - /** * Provides track information for each renderer. */ - public static final class TrackInfo { + public static final class MappedTrackInfo { /** * The renderer does not have any associated tracks. @@ -477,34 +409,27 @@ public abstract class MappingTrackSelector extends TrackSelector { */ public static final int RENDERER_SUPPORT_PLAYABLE_TRACKS = 2; - /** - * The number of renderers. - */ - public final int rendererCount; - private final int[] rendererTrackTypes; private final TrackGroupArray[] trackGroups; - private final TrackSelection[] trackSelections; private final int[] mixedMimeTypeAdaptiveSupport; private final int[][][] formatSupport; private final TrackGroupArray unassociatedTrackGroups; + private final int rendererCount; /** * @param rendererTrackTypes The track type supported by each renderer. * @param trackGroups The {@link TrackGroupArray}s for each renderer. - * @param trackSelections The current {@link TrackSelection}s for each renderer. * @param mixedMimeTypeAdaptiveSupport The result of * {@link RendererCapabilities#supportsMixedMimeTypeAdaptation()} for each renderer. * @param formatSupport The result of {@link RendererCapabilities#supportsFormat} for each * track, indexed by renderer index, group index and track index (in that order). * @param unassociatedTrackGroups Contains {@link TrackGroup}s not associated with any renderer. */ - /* package */ TrackInfo(int[] rendererTrackTypes, TrackGroupArray[] trackGroups, - TrackSelection[] trackSelections, int[] mixedMimeTypeAdaptiveSupport, + /* package */ MappedTrackInfo(int[] rendererTrackTypes, + TrackGroupArray[] trackGroups, int[] mixedMimeTypeAdaptiveSupport, int[][][] formatSupport, TrackGroupArray unassociatedTrackGroups) { this.rendererTrackTypes = rendererTrackTypes; this.trackGroups = trackGroups; - this.trackSelections = trackSelections; this.formatSupport = formatSupport; this.mixedMimeTypeAdaptiveSupport = mixedMimeTypeAdaptiveSupport; this.unassociatedTrackGroups = unassociatedTrackGroups; @@ -521,16 +446,6 @@ public abstract class MappingTrackSelector extends TrackSelector { return trackGroups[rendererIndex]; } - /** - * Returns the current {@link TrackSelection} for the renderer at a specified index. - * - * @param rendererIndex The renderer index. - * @return The corresponding {@link TrackSelection}, or null if the renderer is disabled. - */ - public TrackSelection getTrackSelection(int rendererIndex) { - return trackSelections[rendererIndex]; - } - /** * Returns the extent to which a renderer can support playback of the tracks associated to it. * @@ -657,7 +572,7 @@ public abstract class MappingTrackSelector extends TrackSelector { * unplayable. False in all other cases. */ public boolean hasOnlyUnplayableTracks(int trackType) { - int rendererSupport = TrackInfo.RENDERER_SUPPORT_NO_TRACKS; + int rendererSupport = RENDERER_SUPPORT_NO_TRACKS; for (int i = 0; i < rendererCount; i++) { if (rendererTrackTypes[i] == trackType) { rendererSupport = Math.max(rendererSupport, getRendererSupport(i)); diff --git a/library/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelectionArray.java b/library/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelections.java similarity index 80% rename from library/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelectionArray.java rename to library/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelections.java index 4d4b4c614e..31ed838310 100644 --- a/library/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelectionArray.java +++ b/library/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelections.java @@ -18,12 +18,16 @@ package com.google.android.exoplayer2.trackselection; import java.util.Arrays; /** - * An array of {@link TrackSelection}s generated by a {@link TrackSelector}. + * The result of a {@link TrackSelector} operation. */ -public final class TrackSelectionArray { +public final class TrackSelections { /** - * The number of selections in the array. Greater than or equal to zero. + * Opaque information associated with the result. + */ + public final T info; + /** + * The number of selections in the result. Greater than or equal to zero. */ public final int length; @@ -33,9 +37,11 @@ public final class TrackSelectionArray { private int hashCode; /** + * @param info Opaque information associated with the result. * @param trackSelections The selections. Must not be null, but may contain null elements. */ - public TrackSelectionArray(TrackSelection... trackSelections) { + public TrackSelections(T info, TrackSelection... trackSelections) { + this.info = info; this.trackSelections = trackSelections; this.length = trackSelections.length; } @@ -75,7 +81,7 @@ public final class TrackSelectionArray { if (obj == null || getClass() != obj.getClass()) { return false; } - TrackSelectionArray other = (TrackSelectionArray) obj; + TrackSelections other = (TrackSelections) obj; return Arrays.equals(trackSelections, other.trackSelections); } diff --git a/library/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelector.java b/library/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelector.java index b18184ab3e..41c62f6e0e 100644 --- a/library/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelector.java +++ b/library/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelector.java @@ -15,15 +15,15 @@ */ package com.google.android.exoplayer2.trackselection; -import android.util.Pair; +import android.os.Handler; import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.RendererCapabilities; import com.google.android.exoplayer2.source.TrackGroupArray; +import com.google.android.exoplayer2.util.Assertions; +import java.util.concurrent.CopyOnWriteArraySet; -/** - * Selects tracks to be consumed by available renderers. - */ -public abstract class TrackSelector { +/** Selects tracks to be consumed by available renderers. */ +public abstract class TrackSelector { /** * Notified when previous selections by a {@link TrackSelector} are no longer valid. @@ -37,7 +37,55 @@ public abstract class TrackSelector { } + /** Listener of {@link TrackSelector} events. */ + public interface EventListener { + + /** + * Called when the track selections have changed. + * + * @param trackSelections The new track selections. + */ + void onTrackSelectionsChanged(TrackSelections trackSelections); + } + + private final Handler eventHandler; + private final CopyOnWriteArraySet> listeners; + private InvalidationListener listener; + private TrackSelections activeSelections; + + /** + * @param eventHandler A handler to use when delivering events to listeners added via {@link + * #addListener(EventListener)}. + */ + public TrackSelector(Handler eventHandler) { + this.eventHandler = Assertions.checkNotNull(eventHandler); + this.listeners = new CopyOnWriteArraySet<>(); + } + + /** + * Registers a listener to receive events from the selector. The listener's methods will be called + * using the {@link Handler} that was passed to the constructor. + * + * @param listener The listener to register. + */ + public final void addListener(EventListener listener) { + listeners.add(listener); + } + + /** + * Unregister a listener. The listener will no longer receive events from the selector. + * + * @param listener The listener to unregister. + */ + public final void removeListener(EventListener listener) { + listeners.remove(listener); + } + + /** Returns the current track selections. */ + public final TrackSelections getCurrentSelections() { + return activeSelections; + } /** * Initializes the selector. @@ -49,31 +97,28 @@ public abstract class TrackSelector { } /** - * Generates a {@link TrackSelection} for each renderer. - *

- * The selections are returned in a {@link TrackSelectionArray}, together with an opaque object - * that the selector wishes to receive in an invocation of {@link #onSelectionActivated(Object)} - * should the selection be activated. + * Generates {@link TrackSelections} for the renderers. * - * @param rendererCapabilities The {@link RendererCapabilities} of the renderers for which - * {@link TrackSelection}s are to be generated. + * @param rendererCapabilities The {@link RendererCapabilities} of the renderers for which {@link + * TrackSelection}s are to be generated. * @param trackGroups The available track groups. - * @return A {@link TrackSelectionArray} containing a {@link TrackSelection} for each renderer, - * together with an opaque object that will be passed to {@link #onSelectionActivated(Object)} - * if the selection is activated. + * @return The track selections. * @throws ExoPlaybackException If an error occurs selecting tracks. */ - public abstract Pair selectTracks( + public abstract TrackSelections selectTracks( RendererCapabilities[] rendererCapabilities, TrackGroupArray trackGroups) throws ExoPlaybackException; /** - * Called when a selection previously generated by - * {@link #selectTracks(RendererCapabilities[], TrackGroupArray)} is activated. + * Called when {@link TrackSelections} previously generated by {@link + * #selectTracks(RendererCapabilities[], TrackGroupArray)} are activated. * - * @param selectionInfo The opaque object associated with the selection. + * @param activeSelections The activated {@link TrackSelections}. */ - public abstract void onSelectionActivated(Object selectionInfo); + public final void onSelectionActivated(TrackSelections activeSelections) { + this.activeSelections = activeSelections; + notifyTrackSelectionsChanged(activeSelections); + } /** * Invalidates all previously generated track selections. @@ -84,4 +129,18 @@ public abstract class TrackSelector { } } + private void notifyTrackSelectionsChanged(final TrackSelections activeSelections) { + if (eventHandler != null) { + eventHandler.post( + new Runnable() { + @Override + public void run() { + for (EventListener listener : listeners) { + listener.onTrackSelectionsChanged(activeSelections); + } + } + }); + } + } + } diff --git a/library/src/main/java/com/google/android/exoplayer2/ui/SimpleExoPlayerView.java b/library/src/main/java/com/google/android/exoplayer2/ui/SimpleExoPlayerView.java index aff9e2c968..9263918958 100644 --- a/library/src/main/java/com/google/android/exoplayer2/ui/SimpleExoPlayerView.java +++ b/library/src/main/java/com/google/android/exoplayer2/ui/SimpleExoPlayerView.java @@ -22,22 +22,18 @@ import android.util.AttributeSet; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; -import android.view.Surface; import android.view.SurfaceView; import android.view.TextureView; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; - import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.R; import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.Timeline; -import com.google.android.exoplayer2.decoder.DecoderCounters; import com.google.android.exoplayer2.text.Cue; import com.google.android.exoplayer2.text.TextRenderer; - import java.util.List; /** @@ -112,7 +108,6 @@ public final class SimpleExoPlayerView extends FrameLayout { this.player.setVideoSurface(null); } this.player = player; - if (player != null) { if (surfaceView instanceof TextureView) { player.setVideoTextureView((TextureView) surfaceView); @@ -122,6 +117,8 @@ public final class SimpleExoPlayerView extends FrameLayout { player.setVideoListener(componentListener); player.addListener(componentListener); player.setTextOutput(componentListener); + } else { + shutterView.setVisibility(VISIBLE); } setUseController(useController); } @@ -233,12 +230,12 @@ public final class SimpleExoPlayerView extends FrameLayout { } @Override - public void onRenderedFirstFrame(Surface surface) { + public void onRenderedFirstFrame() { shutterView.setVisibility(GONE); } @Override - public void onVideoDisabled(DecoderCounters counters) { + public void onVideoTracksDisabled() { shutterView.setVisibility(VISIBLE); } From 85b61adb32cfc9a5b4cae83ce924994d0e9c020d Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Tue, 27 Sep 2016 08:05:22 -0700 Subject: [PATCH 25/36] Call onPrepared/onSourceInfoRefreshed on the playback thread. HlsSampleStreamWrapper and ExtractorMediaPeriod would call onPrepared/onSourceInfoRefreshed from their loading threads. That was problematic for ConcatenatingMediaSource and MergingMediaSource, which assume that their callbacks are called on the same thread (iterating through timelines from all sources and updating pendingTimelineSources respectively). This change makes them post calls to the callbacks on the playback thread. Generally, implementing a composite MediaSource is easier if MediaPeriod.Callback's methods are all called on the same (playback) thread, so this change makes that part of its contract. Also post onContinueLoadingRequested from ExtractingLoadable because MergingMediaPeriod.onContinueLoadingRequested reads trackGroups written on the playback thread. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=134407280 --- .../source/ExtractorMediaPeriod.java | 43 ++++++++++++++----- .../exoplayer2/source/MediaPeriod.java | 2 +- .../exoplayer2/source/SequenceableLoader.java | 2 +- .../exoplayer2/source/hls/HlsMediaPeriod.java | 2 +- .../source/hls/HlsSampleStreamWrapper.java | 24 ++++++++--- 5 files changed, 53 insertions(+), 20 deletions(-) diff --git a/library/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaPeriod.java b/library/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaPeriod.java index a9d4c18db6..8512167adb 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaPeriod.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaPeriod.java @@ -66,6 +66,9 @@ import java.util.Arrays; private final Loader loader; private final ExtractorHolder extractorHolder; private final ConditionVariable loadCondition; + private final Runnable maybeFinishPrepareRunnable; + private final Runnable onContinueLoadingRequestedRunnable; + private final Handler handler; private SeekMap seekMap; private boolean tracksBuilt; @@ -85,6 +88,7 @@ import java.util.Arrays; private int extractedSamplesCountAtStartOfLoad; private boolean loadingFinished; + private boolean released; /** * @param uri The {@link Uri} of the media stream. @@ -100,7 +104,7 @@ import java.util.Arrays; public ExtractorMediaPeriod(Uri uri, DataSource dataSource, Extractor[] extractors, int minLoadableRetryCount, Handler eventHandler, ExtractorMediaSource.EventListener eventListener, MediaSource.Listener sourceListener, - Callback callback, Allocator allocator) { + final Callback callback, Allocator allocator) { this.uri = uri; this.dataSource = dataSource; this.minLoadableRetryCount = minLoadableRetryCount; @@ -112,6 +116,21 @@ import java.util.Arrays; loader = new Loader("Loader:ExtractorMediaPeriod"); extractorHolder = new ExtractorHolder(extractors, this); loadCondition = new ConditionVariable(); + maybeFinishPrepareRunnable = new Runnable() { + @Override + public void run() { + maybeFinishPrepare(); + } + }; + onContinueLoadingRequestedRunnable = new Runnable() { + @Override + public void run() { + if (!released) { + callback.onContinueLoadingRequested(ExtractorMediaPeriod.this); + } + } + }; + handler = new Handler(); pendingResetPositionUs = C.TIME_UNSET; sampleQueues = new DefaultTrackOutput[0]; @@ -126,11 +145,13 @@ import java.util.Arrays; @Override public void run() { extractorHolder.release(); + for (DefaultTrackOutput sampleQueue : sampleQueues) { + sampleQueue.disable(); + } } }); - for (DefaultTrackOutput sampleQueue : sampleQueues) { - sampleQueue.disable(); - } + handler.removeCallbacksAndMessages(null); + released = true; } @Override @@ -330,7 +351,7 @@ import java.util.Arrays; return madeProgress ? Loader.RETRY_RESET_ERROR_COUNT : Loader.RETRY; } - // ExtractorOutput implementation. + // ExtractorOutput implementation. Called by the loading thread. @Override public TrackOutput track(int id) { @@ -344,26 +365,26 @@ import java.util.Arrays; @Override public void endTracks() { tracksBuilt = true; - maybeFinishPrepare(); + handler.post(maybeFinishPrepareRunnable); } @Override public void seekMap(SeekMap seekMap) { this.seekMap = seekMap; - maybeFinishPrepare(); + handler.post(maybeFinishPrepareRunnable); } - // UpstreamFormatChangedListener implementation + // UpstreamFormatChangedListener implementation. Called by the loading thread. @Override public void onUpstreamFormatChanged(Format format) { - maybeFinishPrepare(); + handler.post(maybeFinishPrepareRunnable); } // Internal methods. private void maybeFinishPrepare() { - if (prepared || seekMap == null || !tracksBuilt) { + if (released || prepared || seekMap == null || !tracksBuilt) { return; } for (DefaultTrackOutput sampleQueue : sampleQueues) { @@ -576,7 +597,7 @@ import java.util.Arrays; if (input.getPosition() > position + CONTINUE_LOADING_CHECK_INTERVAL_BYTES) { position = input.getPosition(); loadCondition.close(); - callback.onContinueLoadingRequested(ExtractorMediaPeriod.this); + handler.post(onContinueLoadingRequestedRunnable); } } } finally { diff --git a/library/src/main/java/com/google/android/exoplayer2/source/MediaPeriod.java b/library/src/main/java/com/google/android/exoplayer2/source/MediaPeriod.java index 80424d445e..b274b0400d 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/MediaPeriod.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/MediaPeriod.java @@ -32,7 +32,7 @@ public interface MediaPeriod extends SequenceableLoader { /** * Called when preparation completes. *

- * May be called from any thread. After invoking this method, the {@link MediaPeriod} can expect + * Called on the playback thread. After invoking this method, the {@link MediaPeriod} can expect * for {@link #selectTracks(TrackSelection[], boolean[], SampleStream[], boolean[], long)} to be * called with the initial track selection. * diff --git a/library/src/main/java/com/google/android/exoplayer2/source/SequenceableLoader.java b/library/src/main/java/com/google/android/exoplayer2/source/SequenceableLoader.java index 5ade51376f..9aebcece9e 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/SequenceableLoader.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/SequenceableLoader.java @@ -29,7 +29,7 @@ public interface SequenceableLoader { /** * Called by the loader to indicate that it wishes for its {@link #continueLoading(long)} method - * to be called when it can continue to load data. + * to be called when it can continue to load data. Called on the playback thread. */ void onContinueLoadingRequested(T source); diff --git a/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriod.java b/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriod.java index 09b21c688f..9597e4be35 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriod.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriod.java @@ -62,6 +62,7 @@ import java.util.List; private final Handler continueLoadingHandler; private final Loader manifestFetcher; private final long preparePositionUs; + private final Runnable continueLoadingRunnable; private int pendingPrepareCount; private HlsPlaylist playlist; @@ -72,7 +73,6 @@ import java.util.List; private HlsSampleStreamWrapper[] sampleStreamWrappers; private HlsSampleStreamWrapper[] enabledSampleStreamWrappers; private CompositeSequenceableLoader sequenceableLoader; - private Runnable continueLoadingRunnable; public HlsMediaPeriod(Uri manifestUri, DataSource.Factory dataSourceFactory, int minLoadableRetryCount, EventDispatcher eventDispatcher, diff --git a/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java b/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java index bf10480262..979878a259 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java @@ -15,6 +15,7 @@ */ package com.google.android.exoplayer2.source.hls; +import android.os.Handler; import android.text.TextUtils; import android.util.SparseArray; import com.google.android.exoplayer2.C; @@ -81,13 +82,15 @@ import java.util.LinkedList; private final HlsChunkSource.HlsChunkHolder nextChunkHolder; private final SparseArray sampleQueues; private final LinkedList mediaChunks; + private final Runnable maybeFinishPrepareRunnable; + private final Handler handler; - private volatile boolean sampleQueuesBuilt; - + private boolean sampleQueuesBuilt; private boolean prepared; private int enabledTrackCount; private Format downstreamTrackFormat; private int upstreamChunkUid; + private boolean released; // Tracks are complicated in HLS. See documentation of buildTracks for details. // Indexed by track (as exposed by this source). @@ -130,6 +133,13 @@ import java.util.LinkedList; nextChunkHolder = new HlsChunkSource.HlsChunkHolder(); sampleQueues = new SparseArray<>(); mediaChunks = new LinkedList<>(); + maybeFinishPrepareRunnable = new Runnable() { + @Override + public void run() { + maybeFinishPrepare(); + } + }; + handler = new Handler(); lastSeekPositionUs = positionUs; pendingResetPositionUs = positionUs; } @@ -255,6 +265,8 @@ import java.util.LinkedList; sampleQueues.valueAt(i).disable(); } loader.release(); + handler.removeCallbacksAndMessages(null); + released = true; } public long getLargestQueuedTimestampUs() { @@ -464,7 +476,7 @@ import java.util.LinkedList; @Override public void endTracks() { sampleQueuesBuilt = true; - maybeFinishPrepare(); + handler.post(maybeFinishPrepareRunnable); } @Override @@ -472,17 +484,17 @@ import java.util.LinkedList; // Do nothing. } - // UpstreamFormatChangedListener implementation. + // UpstreamFormatChangedListener implementation. Called by the loading thread. @Override public void onUpstreamFormatChanged(Format format) { - maybeFinishPrepare(); + handler.post(maybeFinishPrepareRunnable); } // Internal methods. private void maybeFinishPrepare() { - if (prepared || !sampleQueuesBuilt) { + if (released || prepared || !sampleQueuesBuilt) { return; } int sampleQueueCount = sampleQueues.size(); From 4a62b2688cf8c22545e54088c520bedcf4ba6764 Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Tue, 27 Sep 2016 08:32:41 -0700 Subject: [PATCH 26/36] Post onPrepared so it runs after createPeriod has finished. Issue: #1853 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=134409897 --- .../google/android/exoplayer2/source/MediaSource.java | 6 +++--- .../android/exoplayer2/source/MergingMediaSource.java | 2 -- .../exoplayer2/source/SingleSampleMediaPeriod.java | 11 ++++++++++- .../exoplayer2/source/SingleSampleMediaSource.java | 6 ++---- .../exoplayer2/source/dash/DashMediaPeriod.java | 11 ++++++++++- .../source/smoothstreaming/SsMediaPeriod.java | 11 ++++++++++- 6 files changed, 35 insertions(+), 12 deletions(-) diff --git a/library/src/main/java/com/google/android/exoplayer2/source/MediaSource.java b/library/src/main/java/com/google/android/exoplayer2/source/MediaSource.java index 4c0686eec1..8f180ad69e 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/MediaSource.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/MediaSource.java @@ -55,9 +55,9 @@ public interface MediaSource { /** * Returns a {@link MediaPeriod} corresponding to the period at the specified index. *

- * {@link Callback#onPrepared(MediaPeriod)} is called when the new period is prepared. If - * preparation fails, {@link MediaPeriod#maybeThrowPrepareError()} will throw an - * {@link IOException} if called on the returned instance. + * {@link Callback#onPrepared(MediaPeriod)} is called after this method has returned, when the new + * period is prepared. If preparation fails, {@link MediaPeriod#maybeThrowPrepareError()} will + * throw an {@link IOException} if called on the returned instance. * * @param index The index of the period. * @param callback A callback to receive updates from the period. diff --git a/library/src/main/java/com/google/android/exoplayer2/source/MergingMediaSource.java b/library/src/main/java/com/google/android/exoplayer2/source/MergingMediaSource.java index 8653a91a6d..edd9a1f48c 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/MergingMediaSource.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/MergingMediaSource.java @@ -19,7 +19,6 @@ import android.support.annotation.IntDef; import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.source.MediaPeriod.Callback; import com.google.android.exoplayer2.upstream.Allocator; -import com.google.android.exoplayer2.util.Assertions; import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -125,7 +124,6 @@ public final class MergingMediaSource implements MediaSource { MergingMediaPeriod mergingPeriod = new MergingMediaPeriod(callback, periods); for (int i = 0; i < periods.length; i++) { periods[i] = mediaSources[i].createPeriod(index, mergingPeriod, allocator, positionUs); - Assertions.checkState(periods[i] != null, "Child source must not return null period"); } return mergingPeriod; } diff --git a/library/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaPeriod.java b/library/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaPeriod.java index 2e6b22e05c..7981067d83 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaPeriod.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaPeriod.java @@ -51,6 +51,7 @@ import java.util.Arrays; private final int eventSourceId; private final TrackGroupArray tracks; private final ArrayList sampleStreams; + private final Handler handler; /* package */ final Loader loader; /* package */ final Format format; @@ -60,7 +61,7 @@ import java.util.Arrays; public SingleSampleMediaPeriod(Uri uri, DataSource.Factory dataSourceFactory, Format format, int minLoadableRetryCount, Handler eventHandler, EventListener eventListener, - int eventSourceId) { + int eventSourceId, final Callback callback) { this.uri = uri; this.dataSourceFactory = dataSourceFactory; this.format = format; @@ -70,12 +71,20 @@ import java.util.Arrays; this.eventSourceId = eventSourceId; tracks = new TrackGroupArray(new TrackGroup(format)); sampleStreams = new ArrayList<>(); + handler = new Handler(); loader = new Loader("Loader:SingleSampleMediaPeriod"); sampleData = new byte[INITIAL_SAMPLE_SIZE]; + handler.post(new Runnable() { + @Override + public void run() { + callback.onPrepared(SingleSampleMediaPeriod.this); + } + }); } public void release() { loader.release(); + handler.removeCallbacksAndMessages(null); } @Override diff --git a/library/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaSource.java b/library/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaSource.java index c0902f5a84..e1acfb42cd 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaSource.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaSource.java @@ -98,10 +98,8 @@ public final class SingleSampleMediaSource implements MediaSource { public MediaPeriod createPeriod(int index, Callback callback, Allocator allocator, long positionUs) { Assertions.checkArgument(index == 0); - MediaPeriod mediaPeriod = new SingleSampleMediaPeriod(uri, dataSourceFactory, format, - minLoadableRetryCount, eventHandler, eventListener, eventSourceId); - callback.onPrepared(mediaPeriod); - return mediaPeriod; + return new SingleSampleMediaPeriod(uri, dataSourceFactory, format, minLoadableRetryCount, + eventHandler, eventListener, eventSourceId, callback); } @Override diff --git a/library/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaPeriod.java b/library/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaPeriod.java index c5a18b9d2b..fbc5b8abf1 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaPeriod.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaPeriod.java @@ -15,6 +15,7 @@ */ package com.google.android.exoplayer2.source.dash; +import android.os.Handler; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener.EventDispatcher; @@ -51,6 +52,7 @@ import java.util.List; private final Callback callback; private final Allocator allocator; private final TrackGroupArray trackGroups; + private final Handler handler; private ChunkSampleStream[] sampleStreams; private CompositeSequenceableLoader sequenceableLoader; @@ -76,7 +78,13 @@ import java.util.List; sequenceableLoader = new CompositeSequenceableLoader(sampleStreams); period = manifest.getPeriod(index); trackGroups = buildTrackGroups(period); - callback.onPrepared(this); + handler = new Handler(); + handler.post(new Runnable() { + @Override + public void run() { + DashMediaPeriod.this.callback.onPrepared(DashMediaPeriod.this); + } + }); } public void updateManifest(DashManifest manifest, int index) { @@ -95,6 +103,7 @@ import java.util.List; for (ChunkSampleStream sampleStream : sampleStreams) { sampleStream.release(); } + handler.removeCallbacksAndMessages(null); } @Override diff --git a/library/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaPeriod.java b/library/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaPeriod.java index 9512d60f7d..7dd1cda6d8 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaPeriod.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaPeriod.java @@ -15,6 +15,7 @@ */ package com.google.android.exoplayer2.source.smoothstreaming; +import android.os.Handler; import android.util.Base64; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.extractor.mp4.TrackEncryptionBox; @@ -50,6 +51,7 @@ import java.util.ArrayList; private final Allocator allocator; private final TrackGroupArray trackGroups; private final TrackEncryptionBox[] trackEncryptionBoxes; + private final Handler handler; private SsManifest manifest; private ChunkSampleStream[] sampleStreams; @@ -74,10 +76,16 @@ import java.util.ArrayList; } else { trackEncryptionBoxes = null; } + handler = new Handler(); this.manifest = manifest; sampleStreams = newSampleStreamArray(0); sequenceableLoader = new CompositeSequenceableLoader(sampleStreams); - callback.onPrepared(this); + handler.post(new Runnable() { + @Override + public void run() { + SsMediaPeriod.this.callback.onPrepared(SsMediaPeriod.this); + } + }); } public void updateManifest(SsManifest manifest) { @@ -92,6 +100,7 @@ import java.util.ArrayList; for (ChunkSampleStream sampleStream : sampleStreams) { sampleStream.release(); } + handler.removeCallbacksAndMessages(null); } @Override From c82fd859becef278df2397eaf72551daa4f6010e Mon Sep 17 00:00:00 2001 From: eguven Date: Tue, 27 Sep 2016 09:23:53 -0700 Subject: [PATCH 27/36] Move build*DataSourceFactory methods to the DemoApplication class for easy customization. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=134414869 --- demo/build.gradle | 10 ++++---- .../exoplayer2/demo/DemoApplication.java | 24 +++++++++++++++++++ .../exoplayer2/demo/PlayerActivity.java | 11 ++++----- 3 files changed, 33 insertions(+), 12 deletions(-) diff --git a/demo/build.gradle b/demo/build.gradle index 6aaedf05c5..593796bda5 100644 --- a/demo/build.gradle +++ b/demo/build.gradle @@ -39,14 +39,14 @@ android { productFlavors { demo - demo_ext + demoExt } } dependencies { compile project(':library') - demo_extCompile project(path: ':extension-ffmpeg') - demo_extCompile project(path: ':extension-flac') - demo_extCompile project(path: ':extension-opus') - demo_extCompile project(path: ':extension-vp9') + demoExtCompile project(path: ':extension-ffmpeg') + demoExtCompile project(path: ':extension-flac') + demoExtCompile project(path: ':extension-opus') + demoExtCompile project(path: ':extension-vp9') } diff --git a/demo/src/main/java/com/google/android/exoplayer2/demo/DemoApplication.java b/demo/src/main/java/com/google/android/exoplayer2/demo/DemoApplication.java index 9e927488a7..92dc08597f 100644 --- a/demo/src/main/java/com/google/android/exoplayer2/demo/DemoApplication.java +++ b/demo/src/main/java/com/google/android/exoplayer2/demo/DemoApplication.java @@ -16,9 +16,33 @@ package com.google.android.exoplayer2.demo; import android.app.Application; +import com.google.android.exoplayer2.upstream.DataSource; +import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter; +import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; +import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory; +import com.google.android.exoplayer2.upstream.HttpDataSource; +import com.google.android.exoplayer2.util.Util; /** * Placeholder application to facilitate overriding Application methods for debugging and testing. */ public class DemoApplication extends Application { + + protected String userAgent; + + @Override + public void onCreate() { + super.onCreate(); + userAgent = Util.getUserAgent(this, "ExoPlayerDemo"); + } + + DataSource.Factory buildDataSourceFactory(DefaultBandwidthMeter bandwidthMeter) { + return new DefaultDataSourceFactory(this, bandwidthMeter, + buildHttpDataSourceFactory(bandwidthMeter)); + } + + HttpDataSource.Factory buildHttpDataSourceFactory(DefaultBandwidthMeter bandwidthMeter) { + return new DefaultHttpDataSourceFactory(userAgent, bandwidthMeter); + } + } diff --git a/demo/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java b/demo/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java index 481db5b71c..667ccd5bab 100644 --- a/demo/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java +++ b/demo/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java @@ -65,8 +65,6 @@ import com.google.android.exoplayer2.ui.PlaybackControlView; import com.google.android.exoplayer2.ui.SimpleExoPlayerView; import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter; -import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; -import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory; import com.google.android.exoplayer2.upstream.HttpDataSource; import com.google.android.exoplayer2.util.Util; import java.net.CookieHandler; @@ -109,7 +107,6 @@ public class PlayerActivity extends Activity implements OnClickListener, ExoPlay private TextView debugTextView; private Button retryButton; - private String userAgent; private DataSource.Factory mediaDataSourceFactory; private SimpleExoPlayer player; private MappingTrackSelector trackSelector; @@ -128,7 +125,6 @@ public class PlayerActivity extends Activity implements OnClickListener, ExoPlay public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); shouldAutoPlay = true; - userAgent = Util.getUserAgent(this, "ExoPlayerDemo"); mediaDataSourceFactory = buildDataSourceFactory(true); mainHandler = new Handler(); if (CookieHandler.getDefault() != DEFAULT_COOKIE_MANAGER) { @@ -378,8 +374,8 @@ public class PlayerActivity extends Activity implements OnClickListener, ExoPlay * @return A new DataSource factory. */ private DataSource.Factory buildDataSourceFactory(boolean useBandwidthMeter) { - return new DefaultDataSourceFactory(this, useBandwidthMeter ? BANDWIDTH_METER : null, - buildHttpDataSourceFactory(useBandwidthMeter)); + return ((DemoApplication) getApplication()) + .buildDataSourceFactory(useBandwidthMeter ? BANDWIDTH_METER : null); } /** @@ -390,7 +386,8 @@ public class PlayerActivity extends Activity implements OnClickListener, ExoPlay * @return A new HttpDataSource factory. */ private HttpDataSource.Factory buildHttpDataSourceFactory(boolean useBandwidthMeter) { - return new DefaultHttpDataSourceFactory(userAgent, useBandwidthMeter ? BANDWIDTH_METER : null); + return ((DemoApplication) getApplication()) + .buildHttpDataSourceFactory(useBandwidthMeter ? BANDWIDTH_METER : null); } // ExoPlayer.EventListener implementation From ed5decfafc2a88216d0e05fbf71960a00c0e8698 Mon Sep 17 00:00:00 2001 From: aquilescanta Date: Tue, 27 Sep 2016 11:36:54 -0700 Subject: [PATCH 28/36] Add ElementaryStreamReader's factory to inject custom readers in TSExtractor Issue:#726 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=134433012 --- .../extractor/ts/TsExtractorTest.java | 91 ++++++++++ .../ts/DefaultStreamReaderFactory.java | 110 ++++++++++++ .../extractor/ts/ElementaryStreamReader.java | 49 +++++- .../exoplayer2/extractor/ts/TsExtractor.java | 157 +++++------------- .../exoplayer2/source/hls/HlsChunkSource.java | 11 +- 5 files changed, 293 insertions(+), 125 deletions(-) create mode 100644 library/src/main/java/com/google/android/exoplayer2/extractor/ts/DefaultStreamReaderFactory.java diff --git a/library/src/androidTest/java/com/google/android/exoplayer2/extractor/ts/TsExtractorTest.java b/library/src/androidTest/java/com/google/android/exoplayer2/extractor/ts/TsExtractorTest.java index 083ade6ac5..b149b66feb 100644 --- a/library/src/androidTest/java/com/google/android/exoplayer2/extractor/ts/TsExtractorTest.java +++ b/library/src/androidTest/java/com/google/android/exoplayer2/extractor/ts/TsExtractorTest.java @@ -16,8 +16,16 @@ package com.google.android.exoplayer2.extractor.ts; import android.test.InstrumentationTestCase; +import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.extractor.Extractor; +import com.google.android.exoplayer2.extractor.ExtractorOutput; +import com.google.android.exoplayer2.extractor.PositionHolder; +import com.google.android.exoplayer2.extractor.TrackOutput; +import com.google.android.exoplayer2.testutil.FakeExtractorInput; +import com.google.android.exoplayer2.testutil.FakeExtractorOutput; +import com.google.android.exoplayer2.testutil.FakeTrackOutput; import com.google.android.exoplayer2.testutil.TestUtil; +import com.google.android.exoplayer2.util.ParsableByteArray; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Random; @@ -61,6 +69,31 @@ public final class TsExtractorTest extends InstrumentationTestCase { }, "ts/sample.ts", fileData, getInstrumentation()); } + public void testCustomPesReader() throws Exception { + CustomEsReaderFactory factory = new CustomEsReaderFactory(); + TsExtractor tsExtractor = new TsExtractor(new TimestampAdjuster(0), factory); + FakeExtractorInput input = new FakeExtractorInput.Builder() + .setData(TestUtil.getByteArray(getInstrumentation(), "ts/sample.ts")) + .setSimulateIOErrors(false) + .setSimulateUnknownLength(false) + .setSimulatePartialReads(false).build(); + FakeExtractorOutput output = new FakeExtractorOutput(); + tsExtractor.init(output); + tsExtractor.seek(input.getPosition()); + PositionHolder seekPositionHolder = new PositionHolder(); + int readResult = Extractor.RESULT_CONTINUE; + while (readResult != Extractor.RESULT_END_OF_INPUT) { + readResult = tsExtractor.read(input, seekPositionHolder); + } + CustomEsReader reader = factory.reader; + assertEquals(2, reader.packetsRead); + TrackOutput trackOutput = reader.getTrackOutput(); + assertTrue(trackOutput == output.trackOutputs.get(257 /* PID of audio track. */)); + assertEquals( + Format.createTextSampleFormat("Overriding format", "mime", null, 0, 0, "und", null, 0), + ((FakeTrackOutput) trackOutput).format); + } + private static void writeJunkData(ByteArrayOutputStream out, int length) throws IOException { for (int i = 0; i < length; i++) { if (((byte) i) == TS_SYNC_BYTE) { @@ -71,4 +104,62 @@ public final class TsExtractorTest extends InstrumentationTestCase { } } + private static final class CustomEsReader extends ElementaryStreamReader { + + public int packetsRead = 0; + + public CustomEsReader(TrackOutput output, String language) { + super(output); + output.format(Format.createTextSampleFormat("Overriding format", "mime", null, 0, 0, + language, null, 0)); + } + + @Override + public void seek() { + } + + @Override + public void packetStarted(long pesTimeUs, boolean dataAlignmentIndicator) { + } + + @Override + public void consume(ParsableByteArray data) { + } + + @Override + public void packetFinished() { + packetsRead++; + } + + public TrackOutput getTrackOutput() { + return output; + } + + } + + private static final class CustomEsReaderFactory implements ElementaryStreamReader.Factory { + + private final ElementaryStreamReader.Factory defaultFactory; + private CustomEsReader reader; + + public CustomEsReaderFactory() { + defaultFactory = new DefaultStreamReaderFactory(); + } + + @Override + public ElementaryStreamReader onPmtEntry(int pid, int streamType, + ElementaryStreamReader.EsInfo esInfo, ExtractorOutput output) { + if (streamType == 3) { + // We need to manually avoid a duplicate custom reader creation. + if (reader == null) { + reader = new CustomEsReader(output.track(pid), esInfo.language); + } + return reader; + } else { + return defaultFactory.onPmtEntry(pid, streamType, esInfo, output); + } + } + + } + } diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/DefaultStreamReaderFactory.java b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/DefaultStreamReaderFactory.java new file mode 100644 index 0000000000..d5e3b78cfd --- /dev/null +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/DefaultStreamReaderFactory.java @@ -0,0 +1,110 @@ +/* + * 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.support.annotation.IntDef; +import android.util.SparseBooleanArray; +import com.google.android.exoplayer2.extractor.DummyTrackOutput; +import com.google.android.exoplayer2.extractor.ExtractorOutput; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Default implementation for {@link ElementaryStreamReader.Factory}. + */ +public final class DefaultStreamReaderFactory implements ElementaryStreamReader.Factory { + + /** + * Flags controlling what workarounds are enabled for elementary stream readers. + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, value = {WORKAROUND_ALLOW_NON_IDR_KEYFRAMES, WORKAROUND_IGNORE_AAC_STREAM, + WORKAROUND_IGNORE_H264_STREAM, WORKAROUND_DETECT_ACCESS_UNITS, WORKAROUND_MAP_BY_TYPE}) + public @interface WorkaroundFlags { + } + public static final int WORKAROUND_ALLOW_NON_IDR_KEYFRAMES = 1; + public static final int WORKAROUND_IGNORE_AAC_STREAM = 2; + public static final int WORKAROUND_IGNORE_H264_STREAM = 4; + public static final int WORKAROUND_DETECT_ACCESS_UNITS = 8; + public static final int WORKAROUND_MAP_BY_TYPE = 16; + + private static final int BASE_EMBEDDED_TRACK_ID = 0x2000; // 0xFF + 1. + + private final SparseBooleanArray trackIds; + @WorkaroundFlags + private final int workaroundFlags; + private Id3Reader id3Reader; + private int nextEmbeddedTrackId = BASE_EMBEDDED_TRACK_ID; + + public DefaultStreamReaderFactory() { + this(0); + } + + public DefaultStreamReaderFactory(int workaroundFlags) { + trackIds = new SparseBooleanArray(); + this.workaroundFlags = workaroundFlags; + } + + @Override + public ElementaryStreamReader onPmtEntry(int pid, int streamType, + ElementaryStreamReader.EsInfo esInfo, ExtractorOutput output) { + + if ((workaroundFlags & WORKAROUND_MAP_BY_TYPE) != 0 && id3Reader == null) { + // Setup an ID3 track regardless of whether there's a corresponding entry, in case one + // appears intermittently during playback. See b/20261500. + id3Reader = new Id3Reader(output.track(TsExtractor.TS_STREAM_TYPE_ID3)); + } + int trackId = (workaroundFlags & WORKAROUND_MAP_BY_TYPE) != 0 ? streamType : pid; + if (trackIds.get(trackId)) { + return null; + } + trackIds.put(trackId, true); + switch (streamType) { + case TsExtractor.TS_STREAM_TYPE_MPA: + case TsExtractor.TS_STREAM_TYPE_MPA_LSF: + return new MpegAudioReader(output.track(trackId), esInfo.language); + case TsExtractor.TS_STREAM_TYPE_AAC: + return (workaroundFlags & WORKAROUND_IGNORE_AAC_STREAM) != 0 ? null + : new AdtsReader(output.track(trackId), new DummyTrackOutput(), esInfo.language); + case TsExtractor.TS_STREAM_TYPE_AC3: + case TsExtractor.TS_STREAM_TYPE_E_AC3: + return new Ac3Reader(output.track(trackId), esInfo.language); + case TsExtractor.TS_STREAM_TYPE_DTS: + case TsExtractor.TS_STREAM_TYPE_HDMV_DTS: + return new DtsReader(output.track(trackId), esInfo.language); + case TsExtractor.TS_STREAM_TYPE_H262: + return new H262Reader(output.track(trackId)); + case TsExtractor.TS_STREAM_TYPE_H264: + return (workaroundFlags & WORKAROUND_IGNORE_H264_STREAM) != 0 + ? null : new H264Reader(output.track(trackId), + new SeiReader(output.track(nextEmbeddedTrackId++)), + (workaroundFlags & WORKAROUND_ALLOW_NON_IDR_KEYFRAMES) != 0, + (workaroundFlags & WORKAROUND_DETECT_ACCESS_UNITS) != 0); + case TsExtractor.TS_STREAM_TYPE_H265: + return new H265Reader(output.track(trackId), + new SeiReader(output.track(nextEmbeddedTrackId++))); + case TsExtractor.TS_STREAM_TYPE_ID3: + if ((workaroundFlags & WORKAROUND_MAP_BY_TYPE) != 0) { + return id3Reader; + } else { + return new Id3Reader(output.track(nextEmbeddedTrackId++)); + } + default: + return null; + } + } + +} diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/ElementaryStreamReader.java b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/ElementaryStreamReader.java index 13029ca04e..7a220c98b3 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/ElementaryStreamReader.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/ElementaryStreamReader.java @@ -15,13 +15,60 @@ */ package com.google.android.exoplayer2.extractor.ts; +import com.google.android.exoplayer2.extractor.ExtractorOutput; import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.util.ParsableByteArray; /** * Extracts individual samples from an elementary media stream, preserving original order. */ -/* package */ abstract class ElementaryStreamReader { +public abstract class 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 pid The pid for the PMT entry. + * @param streamType One of the {@link TsExtractor}{@code .TS_STREAM_TYPE_*} constants defining + * the type of the stream. + * @param esInfo The descriptor information linked to the elementary stream. + * @param output The {@link ExtractorOutput} that provides the {@link TrackOutput}s for the + * created readers. + * @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 onPmtEntry(int pid, int streamType, EsInfo esInfo, + ExtractorOutput output); + + } + + /** + * 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; + } + + } protected final TrackOutput output; diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/TsExtractor.java b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/TsExtractor.java index 7c6a13bbff..897603bc13 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/TsExtractor.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/TsExtractor.java @@ -15,13 +15,10 @@ */ package com.google.android.exoplayer2.extractor.ts; -import android.support.annotation.IntDef; import android.util.Log; import android.util.SparseArray; -import android.util.SparseBooleanArray; import android.util.SparseIntArray; import com.google.android.exoplayer2.C; -import com.google.android.exoplayer2.extractor.DummyTrackOutput; import com.google.android.exoplayer2.extractor.Extractor; import com.google.android.exoplayer2.extractor.ExtractorInput; import com.google.android.exoplayer2.extractor.ExtractorOutput; @@ -33,8 +30,7 @@ import com.google.android.exoplayer2.util.ParsableBitArray; import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.Util; import java.io.IOException; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; +import java.util.Arrays; /** * Facilitates the extraction of data from the MPEG-2 TS container format. @@ -53,37 +49,24 @@ public final class TsExtractor implements Extractor { }; - /** - * Flags controlling what workarounds are enabled for the extractor. - */ - @Retention(RetentionPolicy.SOURCE) - @IntDef(flag = true, value = {WORKAROUND_ALLOW_NON_IDR_KEYFRAMES, WORKAROUND_IGNORE_AAC_STREAM, - WORKAROUND_IGNORE_H264_STREAM, WORKAROUND_DETECT_ACCESS_UNITS, WORKAROUND_MAP_BY_TYPE}) - public @interface WorkaroundFlags {} - public static final int WORKAROUND_ALLOW_NON_IDR_KEYFRAMES = 1; - public static final int WORKAROUND_IGNORE_AAC_STREAM = 2; - public static final int WORKAROUND_IGNORE_H264_STREAM = 4; - public static final int WORKAROUND_DETECT_ACCESS_UNITS = 8; - public static final int WORKAROUND_MAP_BY_TYPE = 16; - private static final String TAG = "TsExtractor"; private static final int TS_PACKET_SIZE = 188; private static final int TS_SYNC_BYTE = 0x47; // First byte of each TS packet. private static final int TS_PAT_PID = 0; - private static final int TS_STREAM_TYPE_MPA = 0x03; - private static final int TS_STREAM_TYPE_MPA_LSF = 0x04; - private static final int TS_STREAM_TYPE_AAC = 0x0F; - private static final int TS_STREAM_TYPE_AC3 = 0x81; - private static final int TS_STREAM_TYPE_DTS = 0x8A; - private static final int TS_STREAM_TYPE_HDMV_DTS = 0x82; - private static final int TS_STREAM_TYPE_E_AC3 = 0x87; - private static final int TS_STREAM_TYPE_H262 = 0x02; - private static final int TS_STREAM_TYPE_H264 = 0x1B; - private static final int TS_STREAM_TYPE_H265 = 0x24; - private static final int TS_STREAM_TYPE_ID3 = 0x15; - private static final int BASE_EMBEDDED_TRACK_ID = 0x2000; // 0xFF + 1 + public static final int TS_STREAM_TYPE_MPA = 0x03; + public static final int TS_STREAM_TYPE_MPA_LSF = 0x04; + public static final int TS_STREAM_TYPE_AAC = 0x0F; + public static final int TS_STREAM_TYPE_AC3 = 0x81; + public static final int TS_STREAM_TYPE_DTS = 0x8A; + public static final int TS_STREAM_TYPE_HDMV_DTS = 0x82; + public static final int TS_STREAM_TYPE_E_AC3 = 0x87; + public static final int TS_STREAM_TYPE_H262 = 0x02; + public static final int TS_STREAM_TYPE_H264 = 0x1B; + public static final int TS_STREAM_TYPE_H265 = 0x24; + public static final int TS_STREAM_TYPE_ID3 = 0x15; + private static final long AC3_FORMAT_IDENTIFIER = Util.getIntegerCodeForString("AC-3"); private static final long E_AC3_FORMAT_IDENTIFIER = Util.getIntegerCodeForString("EAC3"); @@ -93,36 +76,38 @@ public final class TsExtractor implements Extractor { private static final int BUFFER_SIZE = TS_PACKET_SIZE * BUFFER_PACKET_COUNT; private final TimestampAdjuster timestampAdjuster; - @WorkaroundFlags - private final int workaroundFlags; private final ParsableByteArray tsPacketBuffer; private final ParsableBitArray tsScratch; private final SparseIntArray continuityCounters; + private final ElementaryStreamReader.Factory streamReaderFactory; /* package */ final SparseArray tsPayloadReaders; // Indexed by pid - /* package */ final SparseBooleanArray trackIds; // Accessed only by the loading thread. private ExtractorOutput output; - private int nextEmbeddedTrackId; - /* package */ Id3Reader id3Reader; public TsExtractor() { this(new TimestampAdjuster(0)); } + /** + * @param timestampAdjuster A timestamp adjuster for offsetting and scaling sample timestamps. + */ public TsExtractor(TimestampAdjuster timestampAdjuster) { - this(timestampAdjuster, 0); + this(timestampAdjuster, new DefaultStreamReaderFactory()); } - public TsExtractor(TimestampAdjuster timestampAdjuster, @WorkaroundFlags int workaroundFlags) { + /** + * @param timestampAdjuster A timestamp adjuster for offsetting and scaling sample timestamps. + * @param customReaderFactory Factory for injecting a custom set of elementary stream readers. + */ + public TsExtractor(TimestampAdjuster timestampAdjuster, + ElementaryStreamReader.Factory customReaderFactory) { this.timestampAdjuster = timestampAdjuster; - this.workaroundFlags = workaroundFlags; + this.streamReaderFactory = Assertions.checkNotNull(customReaderFactory); tsPacketBuffer = new ParsableByteArray(BUFFER_SIZE); tsScratch = new ParsableBitArray(new byte[3]); tsPayloadReaders = new SparseArray<>(); tsPayloadReaders.put(TS_PAT_PID, new PatReader()); - trackIds = new SparseBooleanArray(); - nextEmbeddedTrackId = BASE_EMBEDDED_TRACK_ID; continuityCounters = new SparseIntArray(); } @@ -427,12 +412,6 @@ public final class TsExtractor implements Extractor { // Skip the descriptors. sectionData.skipBytes(programInfoLength); - if ((workaroundFlags & WORKAROUND_MAP_BY_TYPE) != 0 && id3Reader == null) { - // Setup an ID3 track regardless of whether there's a corresponding entry, in case one - // appears intermittently during playback. See b/20261500. - id3Reader = new Id3Reader(output.track(TS_STREAM_TYPE_ID3)); - } - int remainingEntriesLength = sectionLength - 9 /* Length of fields before descriptors */ - programInfoLength - 4 /* CRC length */; while (remainingEntriesLength > 0) { @@ -442,63 +421,15 @@ public final class TsExtractor implements Extractor { int elementaryPid = pmtScratch.readBits(13); pmtScratch.skipBits(4); // reserved int esInfoLength = pmtScratch.readBits(12); // ES_info_length. - EsInfo esInfo = readEsInfo(sectionData, esInfoLength); + ElementaryStreamReader.EsInfo esInfo = readEsInfo(sectionData, esInfoLength); if (streamType == 0x06) { streamType = esInfo.streamType; } remainingEntriesLength -= esInfoLength + 5; - int trackId = (workaroundFlags & WORKAROUND_MAP_BY_TYPE) != 0 ? streamType : elementaryPid; - if (trackIds.get(trackId)) { - continue; - } - ElementaryStreamReader pesPayloadReader; - switch (streamType) { - case TS_STREAM_TYPE_MPA: - pesPayloadReader = new MpegAudioReader(output.track(trackId), esInfo.language); - break; - case TS_STREAM_TYPE_MPA_LSF: - pesPayloadReader = new MpegAudioReader(output.track(trackId), esInfo.language); - break; - case TS_STREAM_TYPE_AAC: - pesPayloadReader = (workaroundFlags & WORKAROUND_IGNORE_AAC_STREAM) != 0 ? null - : new AdtsReader(output.track(trackId), new DummyTrackOutput(), esInfo.language); - break; - case TS_STREAM_TYPE_AC3: - case TS_STREAM_TYPE_E_AC3: - pesPayloadReader = new Ac3Reader(output.track(trackId), esInfo.language); - break; - case TS_STREAM_TYPE_DTS: - case TS_STREAM_TYPE_HDMV_DTS: - pesPayloadReader = new DtsReader(output.track(trackId), esInfo.language); - break; - case TS_STREAM_TYPE_H262: - pesPayloadReader = new H262Reader(output.track(trackId)); - break; - case TS_STREAM_TYPE_H264: - pesPayloadReader = (workaroundFlags & WORKAROUND_IGNORE_H264_STREAM) != 0 ? null - : new H264Reader(output.track(trackId), - new SeiReader(output.track(nextEmbeddedTrackId++)), - (workaroundFlags & WORKAROUND_ALLOW_NON_IDR_KEYFRAMES) != 0, - (workaroundFlags & WORKAROUND_DETECT_ACCESS_UNITS) != 0); - break; - case TS_STREAM_TYPE_H265: - pesPayloadReader = new H265Reader(output.track(trackId), - new SeiReader(output.track(nextEmbeddedTrackId++))); - break; - case TS_STREAM_TYPE_ID3: - if ((workaroundFlags & WORKAROUND_MAP_BY_TYPE) != 0) { - pesPayloadReader = id3Reader; - } else { - pesPayloadReader = new Id3Reader(output.track(nextEmbeddedTrackId++)); - } - break; - default: - pesPayloadReader = null; - break; - } + ElementaryStreamReader pesPayloadReader = streamReaderFactory.onPmtEntry(elementaryPid, + streamType, esInfo, output); if (pesPayloadReader != null) { - trackIds.put(trackId, true); tsPayloadReaders.put(elementaryPid, new PesReader(pesPayloadReader, timestampAdjuster)); } @@ -508,18 +439,17 @@ public final class TsExtractor implements Extractor { } /** - * Returns the stream info read from the available descriptors, or -1 if no - * descriptors are present. Sets {@code data}'s position to the end of the descriptors. + * Returns the stream info read from the available descriptors. Sets {@code data}'s position to + * the end of the descriptors. * * @param data A buffer with its position set to the start of the first descriptor. * @param length The length of descriptors to read from the current position in {@code data}. - * @return The stream info read from the available descriptors, or -1 if no - * descriptors are present. + * @return The stream info read from the available descriptors. */ - private EsInfo readEsInfo(ParsableByteArray data, int length) { - int descriptorsEndPosition = data.getPosition() + length; + private ElementaryStreamReader.EsInfo readEsInfo(ParsableByteArray data, int length) { + int descriptorsStartPosition = data.getPosition(); + int descriptorsEndPosition = descriptorsStartPosition + length; int streamType = -1; - int audioType = -1; String language = null; while (data.getPosition() < descriptorsEndPosition) { int descriptorTag = data.readUnsignedByte(); @@ -542,27 +472,14 @@ public final class TsExtractor implements Extractor { streamType = TS_STREAM_TYPE_DTS; } else if (descriptorTag == TS_PMT_DESC_ISO639_LANG) { language = new String(data.data, data.getPosition(), 3).trim(); - audioType = data.data[data.getPosition() + 3]; + // Audio type is ignored. } // Skip unused bytes of current descriptor. data.skipBytes(positionOfNextDescriptor - data.getPosition()); } data.setPosition(descriptorsEndPosition); - return new EsInfo(streamType, audioType, language); - } - - private final class EsInfo { - - final int streamType; - final int audioType; - final String language; - - public EsInfo(int streamType, int audioType, String language) { - this.streamType = streamType; - this.audioType = audioType; - this.language = language; - } - + return new ElementaryStreamReader.EsInfo(streamType, language, + Arrays.copyOfRange(sectionData.data, descriptorsStartPosition, descriptorsEndPosition)); } } diff --git a/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java b/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java index 8f2fc5a99b..9be42e413f 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java @@ -24,6 +24,7 @@ import com.google.android.exoplayer2.extractor.Extractor; import com.google.android.exoplayer2.extractor.mp3.Mp3Extractor; import com.google.android.exoplayer2.extractor.ts.Ac3Extractor; import com.google.android.exoplayer2.extractor.ts.AdtsExtractor; +import com.google.android.exoplayer2.extractor.ts.DefaultStreamReaderFactory; import com.google.android.exoplayer2.extractor.ts.TimestampAdjuster; import com.google.android.exoplayer2.extractor.ts.TsExtractor; import com.google.android.exoplayer2.source.BehindLiveWindowException; @@ -355,20 +356,22 @@ import java.util.Locale; timestampAdjuster = timestampAdjusterProvider.getAdjuster(segment.discontinuitySequenceNumber, startTimeUs); // This flag ensures the change of pid between streams does not affect the sample queues. - @TsExtractor.WorkaroundFlags int workaroundFlags = TsExtractor.WORKAROUND_MAP_BY_TYPE; + @DefaultStreamReaderFactory.WorkaroundFlags + int workaroundFlags = DefaultStreamReaderFactory.WORKAROUND_MAP_BY_TYPE; String codecs = variants[newVariantIndex].format.codecs; if (!TextUtils.isEmpty(codecs)) { // Sometimes AAC and H264 streams are declared in TS chunks even though they don't really // exist. If we know from the codec attribute that they don't exist, then we can explicitly // ignore them even if they're declared. if (!MimeTypes.AUDIO_AAC.equals(MimeTypes.getAudioMediaMimeType(codecs))) { - workaroundFlags |= TsExtractor.WORKAROUND_IGNORE_AAC_STREAM; + workaroundFlags |= DefaultStreamReaderFactory.WORKAROUND_IGNORE_AAC_STREAM; } if (!MimeTypes.VIDEO_H264.equals(MimeTypes.getVideoMediaMimeType(codecs))) { - workaroundFlags |= TsExtractor.WORKAROUND_IGNORE_H264_STREAM; + workaroundFlags |= DefaultStreamReaderFactory.WORKAROUND_IGNORE_H264_STREAM; } } - extractor = new TsExtractor(timestampAdjuster, workaroundFlags); + extractor = new TsExtractor(timestampAdjuster, + new DefaultStreamReaderFactory(workaroundFlags)); } else { // MPEG-2 TS segments, and we need to continue using the same extractor. extractor = previous.extractor; From c7b5c967ff44e1c39b22a3509479c3431cdff5ac Mon Sep 17 00:00:00 2001 From: aquilescanta Date: Tue, 27 Sep 2016 12:00:42 -0700 Subject: [PATCH 29/36] Remove duplicate methods in DefaultTrackSelector This encourages a single invalidation when setting different parameters. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=134436136 --- .../trackselection/DefaultTrackSelector.java | 135 +++++------------- 1 file changed, 33 insertions(+), 102 deletions(-) diff --git a/library/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java b/library/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java index 02b2ef5de9..81d79ac055 100644 --- a/library/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java +++ b/library/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java @@ -68,8 +68,8 @@ public class DefaultTrackSelector extends MappingTrackSelector { *

  • Adaptation between different mime types is not allowed.
  • *
  • Non seamless adaptation is allowed.
  • *
  • No max limit for video width/height.
  • - *
  • Video constrains are ignored if no supported selection can be made otherwise.
  • - *
  • No viewport width/height constrains are set.
  • + *
  • Video constraints are ignored if no supported selection can be made otherwise.
  • + *
  • No viewport width/height constraints are set.
  • * */ public Parameters() { @@ -195,6 +195,24 @@ public class DefaultTrackSelector extends MappingTrackSelector { orientationMayChange); } + /** + * Equivalent to {@code withMaxVideoSize(1279, 719)}. + * + * @return A {@link Parameters} instance with maximum standard definition as maximum video size. + */ + public Parameters withMaxVideoSizeSd() { + return withMaxVideoSize(1279, 719); + } + + /** + * Equivalent to {@code withMaxVideoSize(Integer.MAX_VALUE, Integer.MAX_VALUE)}. + * + * @return A {@link Parameters} instance without video size constraints. + */ + public Parameters withoutVideoSizeConstraints() { + return withMaxVideoSize(Integer.MAX_VALUE, Integer.MAX_VALUE); + } + /** * Returns a {@link Parameters} instance with the provided * {@code exceedVideoConstraintsIfNecessary} value. @@ -250,6 +268,15 @@ public class DefaultTrackSelector extends MappingTrackSelector { return withViewportSize(viewportSize.x, viewportSize.y, orientationMayChange); } + /** + * Equivalent to {@code withViewportSize(Integer.MAX_VALUE, Integer.MAX_VALUE, true)}. + * + * @return A {@link Parameters} instance without viewport size constraints. + */ + public Parameters withoutViewportSizeConstraints() { + return withViewportSize(Integer.MAX_VALUE, Integer.MAX_VALUE, true); + } + @Override public boolean equals(Object obj) { if (this == obj) { @@ -335,108 +362,12 @@ public class DefaultTrackSelector extends MappingTrackSelector { } /** - * Sets the preferred language for audio, as well as for forced text tracks. + * Gets the current selection parameters. * - * @param preferredAudioLanguage The preferred language as defined by RFC 5646. {@code null} to - * select the default track, or first track if there's no default. + * @return The current selection parameters. */ - public void setPreferredLanguage(String preferredAudioLanguage) { - preferredAudioLanguage = Util.normalizeLanguageCode(preferredAudioLanguage); - setParameters(params.get().withPreferredAudioLanguage(preferredAudioLanguage)); - } - - /** - * Sets the preferred language for text tracks. - * - * @param preferredTextLanguage The preferred language as defined by RFC 5646. {@code null} to - * select the default track, or no track if there's no default. - */ - public void setPreferredTextLanguage(String preferredTextLanguage) { - preferredTextLanguage = Util.normalizeLanguageCode(preferredTextLanguage); - setParameters(params.get().withPreferredTextLanguage(preferredTextLanguage)); - } - - /** - * Sets whether to allow selections to contain mixed mime types. - * - * @param allowMixedMimeAdaptiveness Whether to allow selections to contain mixed mime types. - */ - public void allowMixedMimeAdaptiveness(boolean allowMixedMimeAdaptiveness) { - setParameters(params.get().withAllowMixedMimeAdaptiveness(allowMixedMimeAdaptiveness)); - } - - /** - * Sets whether non-seamless adaptation is allowed. - * - * @param allowNonSeamlessAdaptiveness Whether non-seamless adaptation is allowed. - */ - public void allowNonSeamlessAdaptiveness(boolean allowNonSeamlessAdaptiveness) { - setParameters(params.get().withAllowNonSeamlessAdaptiveness(allowNonSeamlessAdaptiveness)); - } - - /** - * Sets the maximum allowed size for video tracks. - * - * @param maxVideoWidth Maximum allowed width. - * @param maxVideoHeight Maximum allowed height. - */ - public void setMaxVideoSize(int maxVideoWidth, int maxVideoHeight) { - setParameters(params.get().withMaxVideoSize(maxVideoWidth, maxVideoHeight)); - } - - /** - * Equivalent to {@code setMaxVideoSize(1279, 719)}. - */ - public void setMaxVideoSizeSd() { - setMaxVideoSize(1279, 719); - } - - /** - * Equivalent to {@code setMaxVideoSize(Integer.MAX_VALUE, Integer.MAX_VALUE)}. - */ - public void clearMaxVideoSize() { - setMaxVideoSize(Integer.MAX_VALUE, Integer.MAX_VALUE); - } - - /** - * Sets whether video constraints should be ignored when no selection can be made otherwise. - * - * @param exceedVideoConstraintsIfNecessary True to ignore video constraints when no selections - * can be made otherwise. False to force constraints anyway. - */ - public void setExceedVideoConstraintsIfNecessary(boolean exceedVideoConstraintsIfNecessary) { - setParameters( - params.get().withExceedVideoConstraintsIfNecessary(exceedVideoConstraintsIfNecessary)); - } - - /** - * Sets the target viewport size for selecting video tracks. - * - * @param viewportWidth Viewport width in pixels. - * @param viewportHeight Viewport height in pixels. - * @param orientationMayChange Whether orientation may change during playback. - */ - public void setViewportSize(int viewportWidth, int viewportHeight, boolean orientationMayChange) { - setParameters( - params.get().withViewportSize(viewportWidth, viewportHeight, orientationMayChange)); - } - - /** - * Retrieves the viewport size from the provided {@link Context} and calls - * {@link #setViewportSize(int, int, boolean)} with this information. - * - * @param context The context to obtain the viewport size from. - * @param orientationMayChange Whether orientation may change during playback. - */ - public void setViewportSizeFromContext(Context context, boolean orientationMayChange) { - setParameters(params.get().withViewportSizeFromContext(context, orientationMayChange)); - } - - /** - * Equivalent to {@code setViewportSize(Integer.MAX_VALUE, Integer.MAX_VALUE, true)}. - */ - public void clearViewportConstraints() { - setViewportSize(Integer.MAX_VALUE, Integer.MAX_VALUE, true); + public Parameters getParameters() { + return params.get(); } // MappingTrackSelector implementation. From 825ec70d319b9aba7dbe488f8139eb5392859cc9 Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Wed, 28 Sep 2016 00:37:42 -0700 Subject: [PATCH 30/36] Move preparation from MediaPeriod constructors to prepare(). ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=134504088 --- .../exoplayer2/ExoPlayerImplInternal.java | 3 ++- .../source/ConcatenatingMediaSource.java | 8 +++---- .../source/ExtractorMediaPeriod.java | 15 +++++++------ .../source/ExtractorMediaSource.java | 6 ++---- .../exoplayer2/source/LoopingMediaSource.java | 6 ++---- .../exoplayer2/source/MediaPeriod.java | 11 ++++++++++ .../exoplayer2/source/MediaSource.java | 8 +------ .../exoplayer2/source/MergingMediaPeriod.java | 13 +++++++++--- .../exoplayer2/source/MergingMediaSource.java | 10 +++------ .../source/SingleSampleMediaPeriod.java | 16 ++++++-------- .../source/SingleSampleMediaSource.java | 6 ++---- .../source/dash/DashMediaPeriod.java | 21 +++++++------------ .../source/dash/DashMediaSource.java | 6 ++---- .../exoplayer2/source/hls/HlsMediaPeriod.java | 21 ++++++++++++------- .../exoplayer2/source/hls/HlsMediaSource.java | 6 ++---- .../source/smoothstreaming/SsMediaPeriod.java | 21 +++++++------------ .../source/smoothstreaming/SsMediaSource.java | 6 ++---- 17 files changed, 86 insertions(+), 97 deletions(-) diff --git a/library/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java b/library/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java index ed19afa29c..84ed37af8b 100644 --- a/library/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java +++ b/library/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java @@ -952,8 +952,9 @@ import java.io.IOException; periodStartPositionUs = defaultPosition.second; } Object newPeriodUid = timeline.getPeriod(newLoadingPeriodIndex, period, true).uid; - MediaPeriod newMediaPeriod = mediaSource.createPeriod(newLoadingPeriodIndex, this, + MediaPeriod newMediaPeriod = mediaSource.createPeriod(newLoadingPeriodIndex, loadControl.getAllocator(), periodStartPositionUs); + newMediaPeriod.prepare(this); MediaPeriodHolder newPeriodHolder = new MediaPeriodHolder<>(renderers, rendererCapabilities, trackSelector, mediaSource, newMediaPeriod, newPeriodUid, periodStartPositionUs); diff --git a/library/src/main/java/com/google/android/exoplayer2/source/ConcatenatingMediaSource.java b/library/src/main/java/com/google/android/exoplayer2/source/ConcatenatingMediaSource.java index 90af3936a4..3b743c5fda 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/ConcatenatingMediaSource.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/ConcatenatingMediaSource.java @@ -18,7 +18,6 @@ package com.google.android.exoplayer2.source; import android.util.Pair; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Timeline; -import com.google.android.exoplayer2.source.MediaPeriod.Callback; import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.util.Util; import java.io.IOException; @@ -79,12 +78,11 @@ public final class ConcatenatingMediaSource implements MediaSource { } @Override - public MediaPeriod createPeriod(int index, Callback callback, Allocator allocator, - long positionUs) { + public MediaPeriod createPeriod(int index, Allocator allocator, long positionUs) { int sourceIndex = timeline.getSourceIndexForPeriod(index); int periodIndexInSource = index - timeline.getFirstPeriodIndexInSource(sourceIndex); - MediaPeriod mediaPeriod = mediaSources[sourceIndex].createPeriod(periodIndexInSource, callback, - allocator, positionUs); + MediaPeriod mediaPeriod = mediaSources[sourceIndex].createPeriod(periodIndexInSource, allocator, + positionUs); sourceIndexByMediaPeriod.put(mediaPeriod, sourceIndex); return mediaPeriod; } diff --git a/library/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaPeriod.java b/library/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaPeriod.java index 8512167adb..e1945c1a79 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaPeriod.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaPeriod.java @@ -61,7 +61,6 @@ import java.util.Arrays; private final Handler eventHandler; private final ExtractorMediaSource.EventListener eventListener; private final MediaSource.Listener sourceListener; - private final Callback callback; private final Allocator allocator; private final Loader loader; private final ExtractorHolder extractorHolder; @@ -70,6 +69,7 @@ import java.util.Arrays; private final Runnable onContinueLoadingRequestedRunnable; private final Handler handler; + private Callback callback; private SeekMap seekMap; private boolean tracksBuilt; private boolean prepared; @@ -98,20 +98,18 @@ import java.util.Arrays; * @param eventHandler A handler for events. May be null if delivery of events is not required. * @param eventListener A listener of events. May be null if delivery of events is not required. * @param sourceListener A listener to notify when the timeline has been loaded. - * @param callback A callback to receive updates from the period. * @param allocator An {@link Allocator} from which to obtain media buffer allocations. */ public ExtractorMediaPeriod(Uri uri, DataSource dataSource, Extractor[] extractors, int minLoadableRetryCount, Handler eventHandler, ExtractorMediaSource.EventListener eventListener, MediaSource.Listener sourceListener, - final Callback callback, Allocator allocator) { + Allocator allocator) { this.uri = uri; this.dataSource = dataSource; this.minLoadableRetryCount = minLoadableRetryCount; this.eventHandler = eventHandler; this.eventListener = eventListener; this.sourceListener = sourceListener; - this.callback = callback; this.allocator = allocator; loader = new Loader("Loader:ExtractorMediaPeriod"); extractorHolder = new ExtractorHolder(extractors, this); @@ -135,8 +133,6 @@ import java.util.Arrays; pendingResetPositionUs = C.TIME_UNSET; sampleQueues = new DefaultTrackOutput[0]; length = C.LENGTH_UNSET; - loadCondition.open(); - startLoading(); } public void release() { @@ -154,6 +150,13 @@ import java.util.Arrays; released = true; } + @Override + public void prepare(Callback callback) { + this.callback = callback; + loadCondition.open(); + startLoading(); + } + @Override public void maybeThrowPrepareError() throws IOException { maybeThrowError(); diff --git a/library/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaSource.java b/library/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaSource.java index 936358352e..bf795241bc 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaSource.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaSource.java @@ -23,7 +23,6 @@ import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory; import com.google.android.exoplayer2.extractor.Extractor; import com.google.android.exoplayer2.extractor.ExtractorsFactory; -import com.google.android.exoplayer2.source.MediaPeriod.Callback; import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.util.Assertions; @@ -148,12 +147,11 @@ public final class ExtractorMediaSource implements MediaSource, MediaSource.List } @Override - public MediaPeriod createPeriod(int index, Callback callback, Allocator allocator, - long positionUs) { + public MediaPeriod createPeriod(int index, Allocator allocator, long positionUs) { Assertions.checkArgument(index == 0); return new ExtractorMediaPeriod(uri, dataSourceFactory.createDataSource(), extractorsFactory.createExtractors(), minLoadableRetryCount, eventHandler, eventListener, - this, callback, allocator); + this, allocator); } @Override diff --git a/library/src/main/java/com/google/android/exoplayer2/source/LoopingMediaSource.java b/library/src/main/java/com/google/android/exoplayer2/source/LoopingMediaSource.java index e41f5cdc1e..21455ed89d 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/LoopingMediaSource.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/LoopingMediaSource.java @@ -19,7 +19,6 @@ import android.util.Log; import android.util.Pair; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Timeline; -import com.google.android.exoplayer2.source.MediaPeriod.Callback; import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.util.Assertions; import java.io.IOException; @@ -76,9 +75,8 @@ public final class LoopingMediaSource implements MediaSource { } @Override - public MediaPeriod createPeriod(int index, Callback callback, Allocator allocator, - long positionUs) { - return childSource.createPeriod(index % childPeriodCount, callback, allocator, positionUs); + public MediaPeriod createPeriod(int index, Allocator allocator, long positionUs) { + return childSource.createPeriod(index % childPeriodCount, allocator, positionUs); } @Override diff --git a/library/src/main/java/com/google/android/exoplayer2/source/MediaPeriod.java b/library/src/main/java/com/google/android/exoplayer2/source/MediaPeriod.java index b274b0400d..a3c1c88df4 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/MediaPeriod.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/MediaPeriod.java @@ -42,6 +42,17 @@ public interface MediaPeriod extends SequenceableLoader { } + /** + * Prepares this media period asynchronously. + *

    + * {@code callback.onPrepared} is called when preparation completes. If preparation fails, + * {@link #maybeThrowPrepareError()} will throw an {@link IOException}. + * + * @param callback Callback to receive updates from this period, including being notified when + * preparation completes. + */ + void prepare(Callback callback); + /** * Throws an error that's preventing the period from becoming prepared. Does nothing if no such * error exists. diff --git a/library/src/main/java/com/google/android/exoplayer2/source/MediaSource.java b/library/src/main/java/com/google/android/exoplayer2/source/MediaSource.java index 8f180ad69e..709a92cbf5 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/MediaSource.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/MediaSource.java @@ -16,7 +16,6 @@ package com.google.android.exoplayer2.source; import com.google.android.exoplayer2.Timeline; -import com.google.android.exoplayer2.source.MediaPeriod.Callback; import com.google.android.exoplayer2.upstream.Allocator; import java.io.IOException; @@ -54,18 +53,13 @@ public interface MediaSource { /** * Returns a {@link MediaPeriod} corresponding to the period at the specified index. - *

    - * {@link Callback#onPrepared(MediaPeriod)} is called after this method has returned, when the new - * period is prepared. If preparation fails, {@link MediaPeriod#maybeThrowPrepareError()} will - * throw an {@link IOException} if called on the returned instance. * * @param index The index of the period. - * @param callback A callback to receive updates from the period. * @param allocator An {@link Allocator} from which to obtain media buffer allocations. * @param positionUs The player's current playback position. * @return A new {@link MediaPeriod}. */ - MediaPeriod createPeriod(int index, Callback callback, Allocator allocator, long positionUs); + MediaPeriod createPeriod(int index, Allocator allocator, long positionUs); /** * Releases the period. diff --git a/library/src/main/java/com/google/android/exoplayer2/source/MergingMediaPeriod.java b/library/src/main/java/com/google/android/exoplayer2/source/MergingMediaPeriod.java index 4df6cdfebf..227dc29366 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/MergingMediaPeriod.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/MergingMediaPeriod.java @@ -28,20 +28,27 @@ import java.util.IdentityHashMap; public final MediaPeriod[] periods; - private final Callback callback; private final IdentityHashMap streamPeriodIndices; + private Callback callback; private int pendingChildPrepareCount; private TrackGroupArray trackGroups; private MediaPeriod[] enabledPeriods; private SequenceableLoader sequenceableLoader; - public MergingMediaPeriod(Callback callback, MediaPeriod... periods) { + public MergingMediaPeriod(MediaPeriod... periods) { this.periods = periods; - this.callback = callback; streamPeriodIndices = new IdentityHashMap<>(); + } + + @Override + public void prepare(Callback callback) { + this.callback = callback; pendingChildPrepareCount = periods.length; + for (MediaPeriod period : periods) { + period.prepare(this); + } } @Override diff --git a/library/src/main/java/com/google/android/exoplayer2/source/MergingMediaSource.java b/library/src/main/java/com/google/android/exoplayer2/source/MergingMediaSource.java index edd9a1f48c..7d5f78c1cd 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/MergingMediaSource.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/MergingMediaSource.java @@ -17,7 +17,6 @@ package com.google.android.exoplayer2.source; import android.support.annotation.IntDef; import com.google.android.exoplayer2.Timeline; -import com.google.android.exoplayer2.source.MediaPeriod.Callback; import com.google.android.exoplayer2.upstream.Allocator; import java.io.IOException; import java.lang.annotation.Retention; @@ -117,15 +116,12 @@ public final class MergingMediaSource implements MediaSource { } @Override - public MediaPeriod createPeriod(int index, Callback callback, Allocator allocator, - long positionUs) { + public MediaPeriod createPeriod(int index, Allocator allocator, long positionUs) { MediaPeriod[] periods = new MediaPeriod[mediaSources.length]; - // The periods are only referenced after they have all been prepared. - MergingMediaPeriod mergingPeriod = new MergingMediaPeriod(callback, periods); for (int i = 0; i < periods.length; i++) { - periods[i] = mediaSources[i].createPeriod(index, mergingPeriod, allocator, positionUs); + periods[i] = mediaSources[i].createPeriod(index, allocator, positionUs); } - return mergingPeriod; + return new MergingMediaPeriod(periods); } @Override diff --git a/library/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaPeriod.java b/library/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaPeriod.java index 7981067d83..2888f267ce 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaPeriod.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaPeriod.java @@ -51,7 +51,6 @@ import java.util.Arrays; private final int eventSourceId; private final TrackGroupArray tracks; private final ArrayList sampleStreams; - private final Handler handler; /* package */ final Loader loader; /* package */ final Format format; @@ -61,7 +60,7 @@ import java.util.Arrays; public SingleSampleMediaPeriod(Uri uri, DataSource.Factory dataSourceFactory, Format format, int minLoadableRetryCount, Handler eventHandler, EventListener eventListener, - int eventSourceId, final Callback callback) { + int eventSourceId) { this.uri = uri; this.dataSourceFactory = dataSourceFactory; this.format = format; @@ -71,20 +70,17 @@ import java.util.Arrays; this.eventSourceId = eventSourceId; tracks = new TrackGroupArray(new TrackGroup(format)); sampleStreams = new ArrayList<>(); - handler = new Handler(); loader = new Loader("Loader:SingleSampleMediaPeriod"); sampleData = new byte[INITIAL_SAMPLE_SIZE]; - handler.post(new Runnable() { - @Override - public void run() { - callback.onPrepared(SingleSampleMediaPeriod.this); - } - }); } public void release() { loader.release(); - handler.removeCallbacksAndMessages(null); + } + + @Override + public void prepare(Callback callback) { + callback.onPrepared(this); } @Override diff --git a/library/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaSource.java b/library/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaSource.java index e1acfb42cd..385bebfb88 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaSource.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaSource.java @@ -19,7 +19,6 @@ import android.net.Uri; import android.os.Handler; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Timeline; -import com.google.android.exoplayer2.source.MediaPeriod.Callback; import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.util.Assertions; @@ -95,11 +94,10 @@ public final class SingleSampleMediaSource implements MediaSource { } @Override - public MediaPeriod createPeriod(int index, Callback callback, Allocator allocator, - long positionUs) { + public MediaPeriod createPeriod(int index, Allocator allocator, long positionUs) { Assertions.checkArgument(index == 0); return new SingleSampleMediaPeriod(uri, dataSourceFactory, format, minLoadableRetryCount, - eventHandler, eventListener, eventSourceId, callback); + eventHandler, eventListener, eventSourceId); } @Override diff --git a/library/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaPeriod.java b/library/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaPeriod.java index fbc5b8abf1..4a24c7c176 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaPeriod.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaPeriod.java @@ -15,7 +15,6 @@ */ package com.google.android.exoplayer2.source.dash; -import android.os.Handler; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener.EventDispatcher; @@ -49,11 +48,10 @@ import java.util.List; private final EventDispatcher eventDispatcher; private final long elapsedRealtimeOffset; private final LoaderErrorThrower manifestLoaderErrorThrower; - private final Callback callback; private final Allocator allocator; private final TrackGroupArray trackGroups; - private final Handler handler; + private Callback callback; private ChunkSampleStream[] sampleStreams; private CompositeSequenceableLoader sequenceableLoader; private DashManifest manifest; @@ -63,7 +61,7 @@ import java.util.List; public DashMediaPeriod(int id, DashManifest manifest, int index, DashChunkSource.Factory chunkSourceFactory, int minLoadableRetryCount, EventDispatcher eventDispatcher, long elapsedRealtimeOffset, - LoaderErrorThrower manifestLoaderErrorThrower, Callback callback, Allocator allocator) { + LoaderErrorThrower manifestLoaderErrorThrower, Allocator allocator) { this.id = id; this.manifest = manifest; this.index = index; @@ -72,19 +70,11 @@ import java.util.List; this.eventDispatcher = eventDispatcher; this.elapsedRealtimeOffset = elapsedRealtimeOffset; this.manifestLoaderErrorThrower = manifestLoaderErrorThrower; - this.callback = callback; this.allocator = allocator; sampleStreams = newSampleStreamArray(0); sequenceableLoader = new CompositeSequenceableLoader(sampleStreams); period = manifest.getPeriod(index); trackGroups = buildTrackGroups(period); - handler = new Handler(); - handler.post(new Runnable() { - @Override - public void run() { - DashMediaPeriod.this.callback.onPrepared(DashMediaPeriod.this); - } - }); } public void updateManifest(DashManifest manifest, int index) { @@ -103,7 +93,12 @@ import java.util.List; for (ChunkSampleStream sampleStream : sampleStreams) { sampleStream.release(); } - handler.removeCallbacksAndMessages(null); + } + + @Override + public void prepare(Callback callback) { + this.callback = callback; + callback.onPrepared(this); } @Override diff --git a/library/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java b/library/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java index a6e54ef4a1..766f1e0ebf 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java @@ -26,7 +26,6 @@ import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener; import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener.EventDispatcher; import com.google.android.exoplayer2.source.MediaPeriod; -import com.google.android.exoplayer2.source.MediaPeriod.Callback; import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.dash.manifest.DashManifest; import com.google.android.exoplayer2.source.dash.manifest.DashManifestParser; @@ -171,11 +170,10 @@ public final class DashMediaSource implements MediaSource { } @Override - public MediaPeriod createPeriod(int index, Callback callback, Allocator allocator, - long positionUs) { + public MediaPeriod createPeriod(int index, Allocator allocator, long positionUs) { DashMediaPeriod mediaPeriod = new DashMediaPeriod(firstPeriodId + index, manifest, index, chunkSourceFactory, minLoadableRetryCount, eventDispatcher, elapsedRealtimeOffsetMs, loader, - callback, allocator); + allocator); periodsById.put(mediaPeriod.id, mediaPeriod); return mediaPeriod; } diff --git a/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriod.java b/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriod.java index 9597e4be35..ceb47771a6 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriod.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriod.java @@ -50,11 +50,11 @@ import java.util.List; /* package */ final class HlsMediaPeriod implements MediaPeriod, Loader.Callback>, HlsSampleStreamWrapper.Callback { + private final Uri manifestUri; private final DataSource.Factory dataSourceFactory; private final int minLoadableRetryCount; private final EventDispatcher eventDispatcher; private final MediaSource.Listener sourceListener; - private final Callback callback; private final Allocator allocator; private final IdentityHashMap streamWrapperIndices; private final TimestampAdjusterProvider timestampAdjusterProvider; @@ -64,6 +64,7 @@ import java.util.List; private final long preparePositionUs; private final Runnable continueLoadingRunnable; + private Callback callback; private int pendingPrepareCount; private HlsPlaylist playlist; private boolean seenFirstTrackSelection; @@ -76,13 +77,13 @@ import java.util.List; public HlsMediaPeriod(Uri manifestUri, DataSource.Factory dataSourceFactory, int minLoadableRetryCount, EventDispatcher eventDispatcher, - MediaSource.Listener sourceListener, final Callback callback, Allocator allocator, + MediaSource.Listener sourceListener, Allocator allocator, long positionUs) { + this.manifestUri = manifestUri; this.dataSourceFactory = dataSourceFactory; this.minLoadableRetryCount = minLoadableRetryCount; this.eventDispatcher = eventDispatcher; this.sourceListener = sourceListener; - this.callback = callback; this.allocator = allocator; streamWrapperIndices = new IdentityHashMap<>(); timestampAdjusterProvider = new TimestampAdjusterProvider(); @@ -96,11 +97,6 @@ import java.util.List; callback.onContinueLoadingRequested(HlsMediaPeriod.this); } }; - - ParsingLoadable loadable = new ParsingLoadable<>( - dataSourceFactory.createDataSource(), manifestUri, C.DATA_TYPE_MANIFEST, manifestParser); - long elapsedRealtimeMs = manifestFetcher.startLoading(loadable, this, minLoadableRetryCount); - eventDispatcher.loadStarted(loadable.dataSpec, loadable.type, elapsedRealtimeMs); } public void release() { @@ -111,6 +107,15 @@ import java.util.List; } } + @Override + public void prepare(Callback callback) { + this.callback = callback; + ParsingLoadable loadable = new ParsingLoadable<>( + dataSourceFactory.createDataSource(), manifestUri, C.DATA_TYPE_MANIFEST, manifestParser); + long elapsedRealtimeMs = manifestFetcher.startLoading(loadable, this, minLoadableRetryCount); + eventDispatcher.loadStarted(loadable.dataSpec, loadable.type, elapsedRealtimeMs); + } + @Override public void maybeThrowPrepareError() throws IOException { if (sampleStreamWrappers == null) { diff --git a/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java b/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java index 72a9fd6c0c..b8b6c033b3 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java @@ -21,7 +21,6 @@ import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener; import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener.EventDispatcher; import com.google.android.exoplayer2.source.MediaPeriod; -import com.google.android.exoplayer2.source.MediaPeriod.Callback; import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.SinglePeriodTimeline; import com.google.android.exoplayer2.upstream.Allocator; @@ -73,11 +72,10 @@ public final class HlsMediaSource implements MediaSource { } @Override - public MediaPeriod createPeriod(int index, Callback callback, Allocator allocator, - long positionUs) { + public MediaPeriod createPeriod(int index, Allocator allocator, long positionUs) { Assertions.checkArgument(index == 0); return new HlsMediaPeriod(manifestUri, dataSourceFactory, minLoadableRetryCount, - eventDispatcher, sourceListener, callback, allocator, positionUs); + eventDispatcher, sourceListener, allocator, positionUs); } @Override diff --git a/library/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaPeriod.java b/library/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaPeriod.java index 7dd1cda6d8..fef2480fd6 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaPeriod.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaPeriod.java @@ -15,7 +15,6 @@ */ package com.google.android.exoplayer2.source.smoothstreaming; -import android.os.Handler; import android.util.Base64; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.extractor.mp4.TrackEncryptionBox; @@ -47,24 +46,22 @@ import java.util.ArrayList; private final LoaderErrorThrower manifestLoaderErrorThrower; private final int minLoadableRetryCount; private final EventDispatcher eventDispatcher; - private final Callback callback; private final Allocator allocator; private final TrackGroupArray trackGroups; private final TrackEncryptionBox[] trackEncryptionBoxes; - private final Handler handler; + private Callback callback; private SsManifest manifest; private ChunkSampleStream[] sampleStreams; private CompositeSequenceableLoader sequenceableLoader; public SsMediaPeriod(SsManifest manifest, SsChunkSource.Factory chunkSourceFactory, int minLoadableRetryCount, EventDispatcher eventDispatcher, - LoaderErrorThrower manifestLoaderErrorThrower, Callback callback, Allocator allocator) { + LoaderErrorThrower manifestLoaderErrorThrower, Allocator allocator) { this.chunkSourceFactory = chunkSourceFactory; this.manifestLoaderErrorThrower = manifestLoaderErrorThrower; this.minLoadableRetryCount = minLoadableRetryCount; this.eventDispatcher = eventDispatcher; - this.callback = callback; this.allocator = allocator; trackGroups = buildTrackGroups(manifest); @@ -76,16 +73,9 @@ import java.util.ArrayList; } else { trackEncryptionBoxes = null; } - handler = new Handler(); this.manifest = manifest; sampleStreams = newSampleStreamArray(0); sequenceableLoader = new CompositeSequenceableLoader(sampleStreams); - handler.post(new Runnable() { - @Override - public void run() { - SsMediaPeriod.this.callback.onPrepared(SsMediaPeriod.this); - } - }); } public void updateManifest(SsManifest manifest) { @@ -100,7 +90,12 @@ import java.util.ArrayList; for (ChunkSampleStream sampleStream : sampleStreams) { sampleStream.release(); } - handler.removeCallbacksAndMessages(null); + } + + @Override + public void prepare(Callback callback) { + this.callback = callback; + callback.onPrepared(this); } @Override diff --git a/library/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaSource.java b/library/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaSource.java index ef8129e228..d328e5ecf2 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaSource.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaSource.java @@ -24,7 +24,6 @@ import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener; import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener.EventDispatcher; import com.google.android.exoplayer2.source.MediaPeriod; -import com.google.android.exoplayer2.source.MediaPeriod.Callback; import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.SinglePeriodTimeline; import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifest; @@ -122,11 +121,10 @@ public final class SsMediaSource implements MediaSource, } @Override - public MediaPeriod createPeriod(int index, Callback callback, Allocator allocator, - long positionUs) { + public MediaPeriod createPeriod(int index, Allocator allocator, long positionUs) { Assertions.checkArgument(index == 0); SsMediaPeriod period = new SsMediaPeriod(manifest, chunkSourceFactory, minLoadableRetryCount, - eventDispatcher, manifestLoader, callback, allocator); + eventDispatcher, manifestLoader, allocator); mediaPeriods.add(period); return period; } From 040fe3b194572f680081fce3effb6c87d06ba9f8 Mon Sep 17 00:00:00 2001 From: cdrolle Date: Wed, 28 Sep 2016 08:30:50 -0700 Subject: [PATCH 31/36] Refactored the text.eia608 package to text.cea. All of the classes in the text.eia608 package have been moved to text.cea, and renamed with the "cea" prefix instead of "eia". All of the buffering logic has been extracted from Cea608Decoder (formerly Eia608Decoder) into the abstract CeaDecoder, which Cea608Decoder extends. Cea608Decoder also now expects a 3-byte sample (i.e. the entire cc_data_pkt instead of just the cc_data_1 and cc_data_2 bytes). Classes like RawCcExtractor and SeiReader, responsible for creating these samples, have also been updated accordingly. This change is a necessary precursor to adding support for multi -channel CEA-608 and CEA-708 captions. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=134537482 --- .../assets/rawcc/sample.rawcc.0.dump | 600 +++++++++++++----- .../exoplayer2/extractor/mp4/AtomParsers.java | 2 +- .../extractor/rawcc/RawCcExtractor.java | 13 +- .../exoplayer2/extractor/ts/H264Reader.java | 2 +- .../exoplayer2/extractor/ts/H265Reader.java | 2 +- .../exoplayer2/extractor/ts/SeiReader.java | 18 +- .../dash/manifest/DashManifestParser.java | 7 +- .../source/hls/HlsSampleStreamWrapper.java | 2 +- .../hls/playlist/HlsPlaylistParser.java | 2 +- .../text/SubtitleDecoderFactory.java | 8 +- .../Cea608Decoder.java} | 155 +---- .../exoplayer2/text/cea/CeaDecoder.java | 167 +++++ .../CeaOutputBuffer.java} | 11 +- .../CeaSubtitle.java} | 8 +- .../android/exoplayer2/util/MimeTypes.java | 4 +- .../exoplayer2/util/ParsableByteArray.java | 7 + 16 files changed, 694 insertions(+), 314 deletions(-) rename library/src/main/java/com/google/android/exoplayer2/text/{eia608/Eia608Decoder.java => cea/Cea608Decoder.java} (78%) create mode 100644 library/src/main/java/com/google/android/exoplayer2/text/cea/CeaDecoder.java rename library/src/main/java/com/google/android/exoplayer2/text/{eia608/Eia608SubtitleOutputBuffer.java => cea/CeaOutputBuffer.java} (72%) rename library/src/main/java/com/google/android/exoplayer2/text/{eia608/Eia608Subtitle.java => cea/CeaSubtitle.java} (86%) diff --git a/library/src/androidTest/assets/rawcc/sample.rawcc.0.dump b/library/src/androidTest/assets/rawcc/sample.rawcc.0.dump index 13e7a93f19..3e84813162 100644 --- a/library/src/androidTest/assets/rawcc/sample.rawcc.0.dump +++ b/library/src/androidTest/assets/rawcc/sample.rawcc.0.dump @@ -8,7 +8,7 @@ track 0: bitrate = -1 id = null containerMimeType = null - sampleMimeType = application/eia-608 + sampleMimeType = application/cea-608 maxInputSize = -1 width = -1 height = -1 @@ -25,305 +25,605 @@ track 0: language = null drmInitData = - initializationData: - sample count = 75 + sample count = 150 sample 0: time = 37657512133 flags = 1 - data = length 2, hash FFFFF3C1 + data = length 3, hash 7363 sample 1: + time = 37657528822 + flags = 1 + data = length 3, hash 7724 + sample 2: time = 37657545511 flags = 1 - data = length 2, hash FFFFF6CD - sample 2: + data = length 3, hash 766F + sample 3: + time = 37657562177 + flags = 1 + data = length 3, hash 7724 + sample 4: time = 37657578866 flags = 1 - data = length 2, hash FFFFF6DC - sample 3: + data = length 3, hash 767E + sample 5: + time = 37657595555 + flags = 1 + data = length 3, hash 7724 + sample 6: time = 37657612244 flags = 1 - data = length 2, hash FFFFF65B - sample 4: + data = length 15, hash E4359178 + sample 7: + time = 37657628911 + flags = 1 + data = length 3, hash 7724 + sample 8: time = 37657645600 flags = 1 - data = length 2, hash FFFFF6CD - sample 5: + data = length 12, hash 15EBEB66 + sample 9: + time = 37657662288 + flags = 1 + data = length 3, hash 7724 + sample 10: time = 37657678977 flags = 1 - data = length 2, hash FFFFF67B - sample 6: + data = length 3, hash 761D + sample 11: + time = 37657695644 + flags = 1 + data = length 3, hash 7724 + sample 12: time = 37657712333 flags = 1 - data = length 2, hash 2B5 - sample 7: + data = length 30, hash E181418F + sample 13: + time = 37657729022 + flags = 1 + data = length 6, hash 36289CE2 + sample 14: time = 37657745711 flags = 1 - data = length 2, hash F5 - sample 8: + data = length 12, hash 3C304F5B + sample 15: + time = 37657762377 + flags = 1 + data = length 3, hash 7724 + sample 16: time = 37657779066 flags = 1 - data = length 2, hash FFFFF87A - sample 9: + data = length 12, hash 88DD8EF6 + sample 17: + time = 37657795755 + flags = 1 + data = length 3, hash 7724 + sample 18: time = 37657812444 flags = 1 - data = length 2, hash FFFFF698 - sample 10: + data = length 12, hash 8B411833 + sample 19: + time = 37657829111 + flags = 1 + data = length 3, hash 7724 + sample 20: time = 37657845800 flags = 1 - data = length 2, hash 1F4 - sample 11: + data = length 12, hash 742A2DF1 + sample 21: + time = 37657862488 + flags = 1 + data = length 3, hash 7724 + sample 22: time = 37657879177 flags = 1 - data = length 2, hash 803 - sample 12: + data = length 12, hash 9A2ECBEE + sample 23: + time = 37657895844 + flags = 1 + data = length 3, hash 7724 + sample 24: time = 37657912533 flags = 1 - data = length 2, hash 1F8 - sample 13: + data = length 12, hash 562688EA + sample 25: + time = 37657929222 + flags = 1 + data = length 3, hash 7724 + sample 26: time = 37657945911 flags = 1 - data = length 2, hash 117A - sample 14: + data = length 12, hash ADE4B953 + sample 27: + time = 37657962577 + flags = 1 + data = length 3, hash 7724 + sample 28: time = 37657979266 flags = 1 - data = length 2, hash 166 - sample 15: + data = length 12, hash F927E3E5 + sample 29: + time = 37657995955 + flags = 1 + data = length 3, hash 7724 + sample 30: time = 37658012644 flags = 1 - data = length 2, hash 105A - sample 16: + data = length 12, hash EA327945 + sample 31: + time = 37658029311 + flags = 1 + data = length 3, hash 7724 + sample 32: time = 37658046000 flags = 1 - data = length 2, hash FCF - sample 17: + data = length 12, hash 3E5DA13C + sample 33: + time = 37658062688 + flags = 1 + data = length 3, hash 7724 + sample 34: time = 37658079377 flags = 1 - data = length 2, hash 1253 - sample 18: + data = length 12, hash BF646AE3 + sample 35: + time = 37658096044 + flags = 1 + data = length 3, hash 7724 + sample 36: time = 37658112733 flags = 1 - data = length 2, hash 11DA - sample 19: + data = length 12, hash 41E3BA78 + sample 37: + time = 37658129422 + flags = 1 + data = length 3, hash 7724 + sample 38: time = 37658146111 flags = 1 - data = length 2, hash 795 - sample 20: + data = length 12, hash A2945EF6 + sample 39: + time = 37658162777 + flags = 1 + data = length 3, hash 7724 + sample 40: time = 37658179466 flags = 1 - data = length 2, hash 103E - sample 21: + data = length 12, hash 26735812 + sample 41: + time = 37658196155 + flags = 1 + data = length 3, hash 7724 + sample 42: time = 37658212844 flags = 1 - data = length 2, hash 120F - sample 22: + data = length 12, hash DC14D3D8 + sample 43: + time = 37658229511 + flags = 1 + data = length 3, hash 7724 + sample 44: time = 37658246200 flags = 1 - data = length 2, hash FFFFF698 - sample 23: + data = length 12, hash 882191BE + sample 45: + time = 37658262888 + flags = 1 + data = length 3, hash 7724 + sample 46: time = 37658279577 flags = 1 - data = length 2, hash 1F4 - sample 24: + data = length 12, hash 8B4886B1 + sample 47: + time = 37658296244 + flags = 1 + data = length 3, hash 7724 + sample 48: time = 37658312933 flags = 1 - data = length 2, hash FFFFF71B - sample 25: + data = length 12, hash 98D98F96 + sample 49: + time = 37658329622 + flags = 1 + data = length 3, hash 7724 + sample 50: time = 37658346311 flags = 1 - data = length 2, hash F91 - sample 26: + data = length 30, hash CF8E53E3 + sample 51: + time = 37658362977 + flags = 1 + data = length 6, hash 36289CE2 + sample 52: time = 37658379666 flags = 1 - data = length 2, hash 166 - sample 27: + data = length 12, hash F883C9EE + sample 53: + time = 37658396355 + flags = 1 + data = length 3, hash 7724 + sample 54: time = 37658413044 flags = 1 - data = length 2, hash 1023 - sample 28: + data = length 12, hash 6E6B2B9C + sample 55: + time = 37658429711 + flags = 1 + data = length 3, hash 7724 + sample 56: time = 37658446400 flags = 1 - data = length 2, hash 117A - sample 29: + data = length 12, hash B4FE7F08 + sample 57: + time = 37658463088 + flags = 1 + data = length 3, hash 7724 + sample 58: time = 37658479777 flags = 1 - data = length 2, hash 784 - sample 30: + data = length 12, hash 5A1EA7C7 + sample 59: + time = 37658496444 + flags = 1 + data = length 3, hash 7724 + sample 60: time = 37658513133 flags = 1 - data = length 2, hash 1F8 - sample 31: + data = length 12, hash 46BD6CC9 + sample 61: + time = 37658529822 + flags = 1 + data = length 3, hash 7724 + sample 62: time = 37658546511 flags = 1 - data = length 2, hash 10D9 - sample 32: + data = length 12, hash 1B1E2554 + sample 63: + time = 37658563177 + flags = 1 + data = length 3, hash 7724 + sample 64: time = 37658579866 flags = 1 - data = length 2, hash 935 - sample 33: + data = length 12, hash 91FCC537 + sample 65: + time = 37658596555 + flags = 1 + data = length 3, hash 7724 + sample 66: time = 37658613244 flags = 1 - data = length 2, hash 2B5 - sample 34: + data = length 12, hash A9355E1B + sample 67: + time = 37658629911 + flags = 1 + data = length 3, hash 7724 + sample 68: time = 37658646600 flags = 1 - data = length 2, hash F5 - sample 35: + data = length 12, hash 2511F69B + sample 69: + time = 37658663288 + flags = 1 + data = length 3, hash 7724 + sample 70: time = 37658679977 flags = 1 - data = length 2, hash FFFFF87A - sample 36: + data = length 12, hash 90925736 + sample 71: + time = 37658696644 + flags = 1 + data = length 3, hash 7724 + sample 72: time = 37658713333 flags = 1 - data = length 2, hash FFFFF698 - sample 37: + data = length 21, hash 431EEE30 + sample 73: + time = 37658730022 + flags = 1 + data = length 3, hash 7724 + sample 74: time = 37658746711 flags = 1 - data = length 2, hash 1F4 - sample 38: + data = length 12, hash 7BDEF631 + sample 75: + time = 37658763377 + flags = 1 + data = length 3, hash 7724 + sample 76: time = 37658780066 flags = 1 - data = length 2, hash 793 - sample 39: + data = length 12, hash A2EEF59E + sample 77: + time = 37658796755 + flags = 1 + data = length 3, hash 7724 + sample 78: time = 37658813444 flags = 1 - data = length 2, hash FF0 - sample 40: + data = length 12, hash BFC6C022 + sample 79: + time = 37658830111 + flags = 1 + data = length 3, hash 7724 + sample 80: time = 37658846800 flags = 1 - data = length 2, hash 16B - sample 41: + data = length 12, hash CD4D8FCA + sample 81: + time = 37658863488 + flags = 1 + data = length 3, hash 7724 + sample 82: time = 37658880177 flags = 1 - data = length 2, hash 2C0 - sample 42: + data = length 12, hash 2BDE8EFA + sample 83: + time = 37658896844 + flags = 1 + data = length 3, hash 7724 + sample 84: time = 37658913533 flags = 1 - data = length 2, hash FFFFF953 - sample 43: + data = length 12, hash 8C858812 + sample 85: + time = 37658930222 + flags = 1 + data = length 3, hash 7724 + sample 86: time = 37658946911 flags = 1 - data = length 2, hash FFFFF3C1 - sample 44: + data = length 12, hash DE7D0E31 + sample 87: + time = 37658963577 + flags = 1 + data = length 3, hash 7724 + sample 88: time = 37658980266 flags = 1 - data = length 2, hash FFFFF3C1 - sample 45: + data = length 3, hash 7363 + sample 89: + time = 37658996955 + flags = 1 + data = length 3, hash 7724 + sample 90: time = 37659013644 flags = 1 - data = length 2, hash FFFFF3C1 - sample 46: + data = length 3, hash 7363 + sample 91: + time = 37659030311 + flags = 1 + data = length 3, hash 7724 + sample 92: time = 37659047000 flags = 1 - data = length 2, hash FFFFF3C1 - sample 47: + data = length 3, hash 7363 + sample 93: + time = 37659063688 + flags = 1 + data = length 3, hash 7724 + sample 94: time = 37659080377 flags = 1 - data = length 2, hash FFFFF3C1 - sample 48: + data = length 3, hash 7363 + sample 95: + time = 37659097044 + flags = 1 + data = length 3, hash 7724 + sample 96: time = 37659113733 flags = 1 - data = length 2, hash FFFFF3C1 - sample 49: + data = length 3, hash 7363 + sample 97: + time = 37659130422 + flags = 1 + data = length 3, hash 7724 + sample 98: time = 37659147111 flags = 1 - data = length 2, hash FFFFF3C1 - sample 50: + data = length 3, hash 7363 + sample 99: + time = 37659163777 + flags = 1 + data = length 3, hash 7724 + sample 100: time = 37659180466 flags = 1 - data = length 2, hash FFFFF3C1 - sample 51: + data = length 3, hash 7363 + sample 101: + time = 37659197155 + flags = 1 + data = length 3, hash 7724 + sample 102: time = 37659213844 flags = 1 - data = length 2, hash FFFFF3C1 - sample 52: + data = length 3, hash 7363 + sample 103: + time = 37659230511 + flags = 1 + data = length 3, hash 7724 + sample 104: time = 37659247200 flags = 1 - data = length 2, hash FFFFF3C1 - sample 53: + data = length 3, hash 7363 + sample 105: + time = 37659263888 + flags = 1 + data = length 3, hash 7724 + sample 106: time = 37659280577 flags = 1 - data = length 2, hash FFFFF3C1 - sample 54: + data = length 3, hash 7363 + sample 107: + time = 37659297244 + flags = 1 + data = length 3, hash 7724 + sample 108: time = 37659313933 flags = 1 - data = length 2, hash FFFFF3C1 - sample 55: + data = length 3, hash 7363 + sample 109: + time = 37659330622 + flags = 1 + data = length 3, hash 7724 + sample 110: time = 37659347311 flags = 1 - data = length 2, hash FFFFF3C1 - sample 56: + data = length 3, hash 7363 + sample 111: + time = 37659363977 + flags = 1 + data = length 3, hash 7724 + sample 112: time = 37659380666 flags = 1 - data = length 2, hash FFFFF3C1 - sample 57: + data = length 3, hash 7363 + sample 113: + time = 37659397355 + flags = 1 + data = length 3, hash 7724 + sample 114: time = 37659414044 flags = 1 - data = length 2, hash FFFFF3C1 - sample 58: + data = length 3, hash 7363 + sample 115: + time = 37659430711 + flags = 1 + data = length 3, hash 7724 + sample 116: time = 37659447400 flags = 1 - data = length 2, hash FFFFF3C1 - sample 59: + data = length 3, hash 7363 + sample 117: + time = 37659464088 + flags = 1 + data = length 3, hash 7724 + sample 118: time = 37659480777 flags = 1 - data = length 2, hash FFFFF3C1 - sample 60: + data = length 3, hash 7363 + sample 119: + time = 37659497444 + flags = 1 + data = length 3, hash 7724 + sample 120: time = 37659514133 flags = 1 - data = length 2, hash FFFFF3C1 - sample 61: + data = length 3, hash 7363 + sample 121: + time = 37659530822 + flags = 1 + data = length 3, hash 7724 + sample 122: time = 37659547511 flags = 1 - data = length 2, hash FFFFF3C1 - sample 62: + data = length 3, hash 7363 + sample 123: + time = 37659564177 + flags = 1 + data = length 3, hash 7724 + sample 124: time = 37659580866 flags = 1 - data = length 2, hash FFFFF3C1 - sample 63: + data = length 3, hash 7363 + sample 125: + time = 37659597555 + flags = 1 + data = length 3, hash 7724 + sample 126: time = 37659614244 flags = 1 - data = length 2, hash FFFFF6CD - sample 64: + data = length 3, hash 766F + sample 127: + time = 37659630911 + flags = 1 + data = length 3, hash 7724 + sample 128: time = 37659647600 flags = 1 - data = length 2, hash FFFFF6DC - sample 65: + data = length 3, hash 767E + sample 129: + time = 37659664288 + flags = 1 + data = length 3, hash 7724 + sample 130: time = 37659680977 flags = 1 - data = length 2, hash FFFFF65B - sample 66: + data = length 15, hash 191B585A + sample 131: + time = 37659697644 + flags = 1 + data = length 3, hash 7724 + sample 132: time = 37659714333 flags = 1 - data = length 2, hash FFFFF6CD - sample 67: + data = length 12, hash 15EC5FC5 + sample 133: + time = 37659731022 + flags = 1 + data = length 3, hash 7724 + sample 134: time = 37659747711 flags = 1 - data = length 2, hash FFFFF6FF - sample 68: + data = length 3, hash 76A1 + sample 135: + time = 37659764377 + flags = 1 + data = length 3, hash 7724 + sample 136: time = 37659781066 flags = 1 - data = length 2, hash FFFFF6AC - sample 69: + data = length 30, hash E8012479 + sample 137: + time = 37659797755 + flags = 1 + data = length 6, hash 36289D5E + sample 138: time = 37659814444 flags = 1 - data = length 2, hash FFFFF5FE - sample 70: + data = length 12, hash D32F29F3 + sample 139: + time = 37659831111 + flags = 1 + data = length 3, hash 7724 + sample 140: time = 37659847800 flags = 1 - data = length 2, hash FFFFFEF7 - sample 71: + data = length 21, hash 6258623 + sample 141: + time = 37659864488 + flags = 1 + data = length 3, hash 7724 + sample 142: time = 37659881177 flags = 1 - data = length 2, hash 120C - sample 72: + data = length 12, hash FE69ABA2 + sample 143: + time = 37659897844 + flags = 1 + data = length 3, hash 7724 + sample 144: time = 37659914533 flags = 1 - data = length 2, hash 1124 - sample 73: + data = length 12, hash 958D0815 + sample 145: + time = 37659931222 + flags = 1 + data = length 3, hash 7724 + sample 146: time = 37659947911 flags = 1 - data = length 2, hash 1A9 - sample 74: + data = length 12, hash FF57BFD8 + sample 147: + time = 37659964577 + flags = 1 + data = length 3, hash 7724 + sample 148: time = 37659981266 flags = 1 - data = length 2, hash 935 + data = length 12, hash 922122E7 + sample 149: + time = 37659997955 + flags = 1 + data = length 3, hash 7724 tracksEnded = true diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java b/library/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java index be26e7f9d4..0ca0f216c6 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java @@ -644,7 +644,7 @@ import java.util.List; 0 /* subsample timing is absolute */); } else if (childAtomType == Atom.TYPE_c608) { out.format = Format.createTextSampleFormat(Integer.toString(trackId), - MimeTypes.APPLICATION_EIA608, null, Format.NO_VALUE, 0, language, drmInitData); + MimeTypes.APPLICATION_CEA608, null, Format.NO_VALUE, 0, language, drmInitData); out.requiredSampleTransformation = Track.TRANSFORMATION_CEA608_CDAT; } stsd.setPosition(childStartPosition + childAtomSize); diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/rawcc/RawCcExtractor.java b/library/src/main/java/com/google/android/exoplayer2/extractor/rawcc/RawCcExtractor.java index 8a63c5557b..ea9458a657 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/rawcc/RawCcExtractor.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/rawcc/RawCcExtractor.java @@ -30,7 +30,7 @@ import com.google.android.exoplayer2.util.Util; import java.io.IOException; /** - * Extracts EIA-608 data from a RawCC file + * Extracts CEA data from a RawCC file. */ public final class RawCcExtractor implements Extractor { @@ -68,7 +68,7 @@ public final class RawCcExtractor implements Extractor { trackOutput = extractorOutput.track(0); extractorOutput.endTracks(); - trackOutput.format(Format.createTextSampleFormat(null, MimeTypes.APPLICATION_EIA608, + trackOutput.format(Format.createTextSampleFormat(null, MimeTypes.APPLICATION_CEA608, null, Format.NO_VALUE, 0, null, null)); } @@ -154,13 +154,8 @@ public final class RawCcExtractor implements Extractor { dataScratch.reset(); input.readFully(dataScratch.data, 0, 3); - // only accept EIA-608 packets which have validity (6th bit) == 1 and - // type (7-8th bits) == 0; i.e. ccDataPkt[0] == 0bXXXXX100 - int ccValidityAndType = dataScratch.readUnsignedByte() & 0x07; - if (ccValidityAndType == 0x04) { - trackOutput.sampleData(dataScratch, 2); - sampleBytesWritten += 2; - } + trackOutput.sampleData(dataScratch, 3); + sampleBytesWritten += 3; } if (sampleBytesWritten > 0) { diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/H264Reader.java b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/H264Reader.java index 993ec4f872..ce7b7e6383 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/H264Reader.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/H264Reader.java @@ -57,7 +57,7 @@ import java.util.List; /** * @param output A {@link TrackOutput} to which H.264 samples should be written. - * @param seiReader A reader for EIA-608 samples in SEI NAL units. + * @param seiReader A reader for CEA-608 samples in SEI NAL units. * @param allowNonIdrKeyframes Whether to treat samples consisting of non-IDR I slices as * synchronization samples (key-frames). * @param detectAccessUnits Whether to split the input stream into access units (samples) based on diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/H265Reader.java b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/H265Reader.java index e7ca3cc625..c8828cefa6 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/H265Reader.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/H265Reader.java @@ -64,7 +64,7 @@ import java.util.Collections; /** * @param output A {@link TrackOutput} to which H.265 samples should be written. - * @param seiReader A reader for EIA-608 samples in SEI NAL units. + * @param seiReader A reader for CEA-608 samples in SEI NAL units. */ public H265Reader(TrackOutput output, SeiReader seiReader) { super(output); diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/SeiReader.java b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/SeiReader.java index a921e4c38f..4971c0c2b1 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/SeiReader.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/SeiReader.java @@ -18,12 +18,12 @@ package com.google.android.exoplayer2.extractor.ts; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.extractor.TrackOutput; -import com.google.android.exoplayer2.text.eia608.Eia608Decoder; +import com.google.android.exoplayer2.text.cea.Cea608Decoder; import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.ParsableByteArray; /** - * Consumes SEI buffers, outputting contained EIA608 messages to a {@link TrackOutput}. + * Consumes SEI buffers, outputting contained CEA-608 messages to a {@link TrackOutput}. */ /* package */ final class SeiReader { @@ -31,7 +31,7 @@ import com.google.android.exoplayer2.util.ParsableByteArray; public SeiReader(TrackOutput output) { this.output = output; - output.format(Format.createTextSampleFormat(null, MimeTypes.APPLICATION_EIA608, null, + output.format(Format.createTextSampleFormat(null, MimeTypes.APPLICATION_CEA608, null, Format.NO_VALUE, 0, null, null)); } @@ -51,7 +51,7 @@ import com.google.android.exoplayer2.util.ParsableByteArray; payloadSize += b; } while (b == 0xFF); // Process the payload. - if (Eia608Decoder.isSeiMessageEia608(payloadType, payloadSize, seiBuffer)) { + if (Cea608Decoder.isSeiMessageCea608(payloadType, payloadSize, seiBuffer)) { // Ignore country_code (1) + provider_code (2) + user_identifier (4) // + user_data_type_code (1). seiBuffer.skipBytes(8); @@ -60,13 +60,13 @@ import com.google.android.exoplayer2.util.ParsableByteArray; seiBuffer.skipBytes(1); int sampleBytes = 0; for (int i = 0; i < ccCount; i++) { - int ccValidityAndType = seiBuffer.readUnsignedByte() & 0x07; - // Check that validity == 1 and type == 0. + int ccValidityAndType = seiBuffer.peekUnsignedByte() & 0x07; + // Check that validity == 1 and type == 0 (i.e. NTSC_CC_FIELD_1). if (ccValidityAndType != 0x04) { - seiBuffer.skipBytes(2); + seiBuffer.skipBytes(3); } else { - sampleBytes += 2; - output.sampleData(seiBuffer, 2); + sampleBytes += 3; + output.sampleData(seiBuffer, 3); } } output.sampleMetadata(pesTimeUs, C.BUFFER_FLAG_KEY_FRAME, sampleBytes, 0, null); diff --git a/library/src/main/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParser.java b/library/src/main/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParser.java index e0bf7d00e6..143a0e995b 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParser.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParser.java @@ -654,9 +654,10 @@ public class DashManifestParser extends DefaultHandler } else if (MimeTypes.isVideo(containerMimeType)) { return MimeTypes.getVideoMediaMimeType(codecs); } else if (MimeTypes.APPLICATION_RAWCC.equals(containerMimeType)) { - // We currently only support EIA-608 through RawCC - if (codecs != null && codecs.contains("eia608")) { - return MimeTypes.APPLICATION_EIA608; + // We currently only support CEA-608 through RawCC + if (codecs != null + && (codecs.contains("eia608") || codecs.contains("cea608"))) { + return MimeTypes.APPLICATION_CEA608; } return null; } else if (mimeTypeIsRawText(containerMimeType)) { diff --git a/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java b/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java index 979878a259..1f9fd11c3f 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java @@ -589,7 +589,7 @@ import java.util.LinkedList; if (primaryExtractorTrackType == PRIMARY_TYPE_VIDEO) { if (MimeTypes.isAudio(sampleFormat.sampleMimeType)) { trackFormat = muxedAudioFormat; - } else if (MimeTypes.APPLICATION_EIA608.equals(sampleFormat.sampleMimeType)) { + } else if (MimeTypes.APPLICATION_CEA608.equals(sampleFormat.sampleMimeType)) { trackFormat = muxedCaptionFormat; } } diff --git a/library/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java b/library/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java index a0757c364a..d5e5267820 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java @@ -161,7 +161,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.ParserTTML ({@link TtmlDecoder}) *

  • SubRip ({@link SubripDecoder})
  • *
  • TX3G ({@link Tx3gDecoder})
  • - *
  • Eia608 ({@link Eia608Decoder})
  • + *
  • Cea608 ({@link Cea608Decoder})
  • * */ SubtitleDecoderFactory DEFAULT = new SubtitleDecoderFactory() { @@ -93,8 +93,8 @@ public interface SubtitleDecoderFactory { return Class.forName("com.google.android.exoplayer2.text.subrip.SubripDecoder"); case MimeTypes.APPLICATION_TX3G: return Class.forName("com.google.android.exoplayer2.text.tx3g.Tx3gDecoder"); - case MimeTypes.APPLICATION_EIA608: - return Class.forName("com.google.android.exoplayer2.text.eia608.Eia608Decoder"); + case MimeTypes.APPLICATION_CEA608: + return Class.forName("com.google.android.exoplayer2.text.cea.Cea608Decoder"); default: return null; } diff --git a/library/src/main/java/com/google/android/exoplayer2/text/eia608/Eia608Decoder.java b/library/src/main/java/com/google/android/exoplayer2/text/cea/Cea608Decoder.java similarity index 78% rename from library/src/main/java/com/google/android/exoplayer2/text/eia608/Eia608Decoder.java rename to library/src/main/java/com/google/android/exoplayer2/text/cea/Cea608Decoder.java index 50c6d4f6b0..0b497d3646 100644 --- a/library/src/main/java/com/google/android/exoplayer2/text/eia608/Eia608Decoder.java +++ b/library/src/main/java/com/google/android/exoplayer2/text/cea/Cea608Decoder.java @@ -13,28 +13,23 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.google.android.exoplayer2.text.eia608; +package com.google.android.exoplayer2.text.cea; import android.text.Layout.Alignment; import android.text.TextUtils; -import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.text.Cue; +import com.google.android.exoplayer2.text.Subtitle; import com.google.android.exoplayer2.text.SubtitleDecoder; -import com.google.android.exoplayer2.text.SubtitleDecoderException; import com.google.android.exoplayer2.text.SubtitleInputBuffer; -import com.google.android.exoplayer2.text.SubtitleOutputBuffer; -import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.ParsableByteArray; -import java.util.LinkedList; -import java.util.TreeSet; /** - * A {@link SubtitleDecoder} for EIA-608 (also known as "line 21 captions" and "CEA-608"). + * A {@link SubtitleDecoder} for CEA-608 (also known as "line 21 captions" and "EIA-608"). */ -public final class Eia608Decoder implements SubtitleDecoder { +public final class Cea608Decoder extends CeaDecoder { - private static final int NUM_INPUT_BUFFERS = 10; - private static final int NUM_OUTPUT_BUFFERS = 2; + private static final int NTSC_CC_FIELD_1 = 0x00; + private static final int CC_VALID_FLAG = 0x04; private static final int PAYLOAD_TYPE_CC = 4; private static final int COUNTRY_CODE = 0xB5; @@ -169,18 +164,10 @@ public final class Eia608Decoder implements SubtitleDecoder { private static final int CUE_POSITION_ANCHOR = Cue.TYPE_UNSET; private static final float CUE_SIZE = 0.8f; - private final LinkedList availableInputBuffers; - private final LinkedList availableOutputBuffers; - private final TreeSet queuedInputBuffers; - private final ParsableByteArray ccData; private final StringBuilder captionStringBuilder; - private long playbackPositionUs; - - private SubtitleInputBuffer dequeuedInputBuffer; - private int captionMode; private int captionRowCount; private String captionString; @@ -191,17 +178,7 @@ public final class Eia608Decoder implements SubtitleDecoder { private byte repeatableControlCc1; private byte repeatableControlCc2; - public Eia608Decoder() { - availableInputBuffers = new LinkedList<>(); - for (int i = 0; i < NUM_INPUT_BUFFERS; i++) { - availableInputBuffers.add(new SubtitleInputBuffer()); - } - availableOutputBuffers = new LinkedList<>(); - for (int i = 0; i < NUM_OUTPUT_BUFFERS; i++) { - availableOutputBuffers.add(new Eia608SubtitleOutputBuffer(this)); - } - queuedInputBuffers = new TreeSet<>(); - + public Cea608Decoder() { ccData = new ParsableByteArray(); captionStringBuilder = new StringBuilder(); @@ -212,106 +189,20 @@ public final class Eia608Decoder implements SubtitleDecoder { @Override public String getName() { - return "Eia608Decoder"; - } - - @Override - public void setPositionUs(long positionUs) { - playbackPositionUs = positionUs; - } - - @Override - public SubtitleInputBuffer dequeueInputBuffer() throws SubtitleDecoderException { - Assertions.checkState(dequeuedInputBuffer == null); - if (availableInputBuffers.isEmpty()) { - return null; - } - dequeuedInputBuffer = availableInputBuffers.pollFirst(); - return dequeuedInputBuffer; - } - - @Override - public void queueInputBuffer(SubtitleInputBuffer inputBuffer) throws SubtitleDecoderException { - Assertions.checkArgument(inputBuffer != null); - Assertions.checkArgument(inputBuffer == dequeuedInputBuffer); - queuedInputBuffers.add(inputBuffer); - dequeuedInputBuffer = null; - } - - @Override - public SubtitleOutputBuffer dequeueOutputBuffer() throws SubtitleDecoderException { - if (availableOutputBuffers.isEmpty()) { - return null; - } - - // iterate through all available input buffers whose timestamps are less than or equal - // to the current playback position; processing input buffers for future content should - // be deferred until they would be applicable - while (!queuedInputBuffers.isEmpty() - && queuedInputBuffers.first().timeUs <= playbackPositionUs) { - SubtitleInputBuffer inputBuffer = queuedInputBuffers.pollFirst(); - - // If the input buffer indicates we've reached the end of the stream, we can - // return immediately with an output buffer propagating that - if (inputBuffer.isEndOfStream()) { - SubtitleOutputBuffer outputBuffer = availableOutputBuffers.pollFirst(); - outputBuffer.addFlag(C.BUFFER_FLAG_END_OF_STREAM); - releaseInputBuffer(inputBuffer); - return outputBuffer; - } - - decode(inputBuffer); - - // check if we have any caption updates to report - if (!TextUtils.equals(captionString, lastCaptionString)) { - lastCaptionString = captionString; - if (!inputBuffer.isDecodeOnly()) { - Cue cue = null; - if (!TextUtils.isEmpty(captionString)) { - cue = new Cue(captionString, CUE_TEXT_ALIGNMENT, CUE_LINE, CUE_LINE_TYPE, - CUE_LINE_ANCHOR, CUE_POSITION, CUE_POSITION_ANCHOR, CUE_SIZE); - } - SubtitleOutputBuffer outputBuffer = availableOutputBuffers.pollFirst(); - outputBuffer.setContent(inputBuffer.timeUs, new Eia608Subtitle(cue), 0); - releaseInputBuffer(inputBuffer); - return outputBuffer; - } - } - - releaseInputBuffer(inputBuffer); - } - - return null; - } - - private void releaseInputBuffer(SubtitleInputBuffer inputBuffer) { - inputBuffer.clear(); - availableInputBuffers.add(inputBuffer); - } - - protected void releaseOutputBuffer(SubtitleOutputBuffer outputBuffer) { - outputBuffer.clear(); - availableOutputBuffers.add(outputBuffer); + return "Cea608Decoder"; } @Override public void flush() { + super.flush(); setCaptionMode(CC_MODE_UNKNOWN); captionRowCount = DEFAULT_CAPTIONS_ROW_COUNT; - playbackPositionUs = 0; captionStringBuilder.setLength(0); captionString = null; lastCaptionString = null; repeatableControlSet = false; repeatableControlCc1 = 0; repeatableControlCc2 = 0; - while (!queuedInputBuffers.isEmpty()) { - releaseInputBuffer(queuedInputBuffers.pollFirst()); - } - if (dequeuedInputBuffer != null) { - releaseInputBuffer(dequeuedInputBuffer); - dequeuedInputBuffer = null; - } } @Override @@ -319,14 +210,34 @@ public final class Eia608Decoder implements SubtitleDecoder { // Do nothing } - private void decode(SubtitleInputBuffer inputBuffer) { + @Override + protected boolean isNewSubtitleDataAvailable() { + return !TextUtils.equals(captionString, lastCaptionString); + } + + @Override + protected Subtitle createSubtitle() { + lastCaptionString = captionString; + return new CeaSubtitle(new Cue(captionString, CUE_TEXT_ALIGNMENT, CUE_LINE, CUE_LINE_TYPE, + CUE_LINE_ANCHOR, CUE_POSITION, CUE_POSITION_ANCHOR, CUE_SIZE)); + } + + @Override + protected void decode(SubtitleInputBuffer inputBuffer) { ccData.reset(inputBuffer.data.array(), inputBuffer.data.limit()); boolean captionDataProcessed = false; boolean isRepeatableControl = false; while (ccData.bytesLeft() > 0) { + byte ccTypeAndValid = (byte) (ccData.readUnsignedByte() & 0x07); byte ccData1 = (byte) (ccData.readUnsignedByte() & 0x7F); byte ccData2 = (byte) (ccData.readUnsignedByte() & 0x7F); + // Only examine valid NTSC_CC_FIELD_1 packets + if (ccTypeAndValid != (CC_VALID_FLAG | NTSC_CC_FIELD_1)) { + // TODO: Add support for NTSC_CC_FIELD_2 packets + continue; + } + // Ignore empty captions. if (ccData1 == 0 && ccData2 == 0) { continue; @@ -550,16 +461,16 @@ public final class Eia608Decoder implements SubtitleDecoder { } /** - * Inspects an sei message to determine whether it contains EIA-608. + * Inspects an sei message to determine whether it contains CEA-608. *

    * The position of {@code payload} is left unchanged. * * @param payloadType The payload type of the message. * @param payloadLength The length of the payload. * @param payload A {@link ParsableByteArray} containing the payload. - * @return Whether the sei message contains EIA-608. + * @return Whether the sei message contains CEA-608. */ - public static boolean isSeiMessageEia608(int payloadType, int payloadLength, + public static boolean isSeiMessageCea608(int payloadType, int payloadLength, ParsableByteArray payload) { if (payloadType != PAYLOAD_TYPE_CC || payloadLength < 8) { return false; diff --git a/library/src/main/java/com/google/android/exoplayer2/text/cea/CeaDecoder.java b/library/src/main/java/com/google/android/exoplayer2/text/cea/CeaDecoder.java new file mode 100644 index 0000000000..ae92d7fab8 --- /dev/null +++ b/library/src/main/java/com/google/android/exoplayer2/text/cea/CeaDecoder.java @@ -0,0 +1,167 @@ +/* + * 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.text.cea; + +import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.text.Subtitle; +import com.google.android.exoplayer2.text.SubtitleDecoder; +import com.google.android.exoplayer2.text.SubtitleDecoderException; +import com.google.android.exoplayer2.text.SubtitleInputBuffer; +import com.google.android.exoplayer2.text.SubtitleOutputBuffer; +import com.google.android.exoplayer2.util.Assertions; +import java.util.LinkedList; +import java.util.TreeSet; + +/** + * Base class for subtitle parsers for CEA captions. + */ +/* package */ abstract class CeaDecoder implements SubtitleDecoder { + + private static final int NUM_INPUT_BUFFERS = 10; + private static final int NUM_OUTPUT_BUFFERS = 2; + + private final LinkedList availableInputBuffers; + private final LinkedList availableOutputBuffers; + private final TreeSet queuedInputBuffers; + + private SubtitleInputBuffer dequeuedInputBuffer; + private long playbackPositionUs; + + public CeaDecoder() { + availableInputBuffers = new LinkedList<>(); + for (int i = 0; i < NUM_INPUT_BUFFERS; i++) { + availableInputBuffers.add(new SubtitleInputBuffer()); + } + availableOutputBuffers = new LinkedList<>(); + for (int i = 0; i < NUM_OUTPUT_BUFFERS; i++) { + availableOutputBuffers.add(new CeaOutputBuffer(this)); + } + queuedInputBuffers = new TreeSet<>(); + } + + @Override + public abstract String getName(); + + @Override + public void setPositionUs(long positionUs) { + playbackPositionUs = positionUs; + } + + @Override + public SubtitleInputBuffer dequeueInputBuffer() throws SubtitleDecoderException { + Assertions.checkState(dequeuedInputBuffer == null); + if (availableInputBuffers.isEmpty()) { + return null; + } + dequeuedInputBuffer = availableInputBuffers.pollFirst(); + return dequeuedInputBuffer; + } + + @Override + public void queueInputBuffer(SubtitleInputBuffer inputBuffer) throws SubtitleDecoderException { + Assertions.checkArgument(inputBuffer != null); + Assertions.checkArgument(inputBuffer == dequeuedInputBuffer); + queuedInputBuffers.add(inputBuffer); + dequeuedInputBuffer = null; + } + + @Override + public SubtitleOutputBuffer dequeueOutputBuffer() throws SubtitleDecoderException { + if (availableOutputBuffers.isEmpty()) { + return null; + } + + // iterate through all available input buffers whose timestamps are less than or equal + // to the current playback position; processing input buffers for future content should + // be deferred until they would be applicable + while (!queuedInputBuffers.isEmpty() + && queuedInputBuffers.first().timeUs <= playbackPositionUs) { + SubtitleInputBuffer inputBuffer = queuedInputBuffers.pollFirst(); + + // If the input buffer indicates we've reached the end of the stream, we can + // return immediately with an output buffer propagating that + if (inputBuffer.isEndOfStream()) { + SubtitleOutputBuffer outputBuffer = availableOutputBuffers.pollFirst(); + outputBuffer.addFlag(C.BUFFER_FLAG_END_OF_STREAM); + releaseInputBuffer(inputBuffer); + return outputBuffer; + } + + decode(inputBuffer); + + // check if we have any caption updates to report + if (isNewSubtitleDataAvailable()) { + // Even if the subtitle is decode-only; we need to generate it to consume the data so it + // isn't accidentally prepended to the next subtitle + Subtitle subtitle = createSubtitle(); + if (!inputBuffer.isDecodeOnly()) { + SubtitleOutputBuffer outputBuffer = availableOutputBuffers.pollFirst(); + outputBuffer.setContent(inputBuffer.timeUs, subtitle, 0); + releaseInputBuffer(inputBuffer); + return outputBuffer; + } + } + + releaseInputBuffer(inputBuffer); + } + + return null; + } + + private void releaseInputBuffer(SubtitleInputBuffer inputBuffer) { + inputBuffer.clear(); + availableInputBuffers.add(inputBuffer); + } + + protected void releaseOutputBuffer(SubtitleOutputBuffer outputBuffer) { + outputBuffer.clear(); + availableOutputBuffers.add(outputBuffer); + } + + @Override + public void flush() { + playbackPositionUs = 0; + while (!queuedInputBuffers.isEmpty()) { + releaseInputBuffer(queuedInputBuffers.pollFirst()); + } + if (dequeuedInputBuffer != null) { + releaseInputBuffer(dequeuedInputBuffer); + dequeuedInputBuffer = null; + } + } + + @Override + public void release() { + // Do nothing + } + + /** + * Returns whether there is data available to create a new {@link Subtitle}. + */ + protected abstract boolean isNewSubtitleDataAvailable(); + + /** + * Creates a {@link Subtitle} from the available data. + */ + protected abstract Subtitle createSubtitle(); + + /** + * Filters and processes the raw data, providing {@link Subtitle}s via {@link #createSubtitle()} + * when sufficient data has been processed. + */ + protected abstract void decode(SubtitleInputBuffer inputBuffer); + +} diff --git a/library/src/main/java/com/google/android/exoplayer2/text/eia608/Eia608SubtitleOutputBuffer.java b/library/src/main/java/com/google/android/exoplayer2/text/cea/CeaOutputBuffer.java similarity index 72% rename from library/src/main/java/com/google/android/exoplayer2/text/eia608/Eia608SubtitleOutputBuffer.java rename to library/src/main/java/com/google/android/exoplayer2/text/cea/CeaOutputBuffer.java index 7e5ba062ab..4cc32bb9e4 100644 --- a/library/src/main/java/com/google/android/exoplayer2/text/eia608/Eia608SubtitleOutputBuffer.java +++ b/library/src/main/java/com/google/android/exoplayer2/text/cea/CeaOutputBuffer.java @@ -13,22 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.google.android.exoplayer2.text.eia608; +package com.google.android.exoplayer2.text.cea; -import com.google.android.exoplayer2.text.Subtitle; import com.google.android.exoplayer2.text.SubtitleOutputBuffer; /** - * A {@link Subtitle} output from an {@link Eia608Decoder}. + * A {@link SubtitleOutputBuffer} for {@link CeaDecoder}s. */ -/* package */ final class Eia608SubtitleOutputBuffer extends SubtitleOutputBuffer { +public final class CeaOutputBuffer extends SubtitleOutputBuffer { - private Eia608Decoder owner; + private final CeaDecoder owner; /** * @param owner The decoder that owns this buffer. */ - public Eia608SubtitleOutputBuffer(Eia608Decoder owner) { + public CeaOutputBuffer(CeaDecoder owner) { super(); this.owner = owner; } diff --git a/library/src/main/java/com/google/android/exoplayer2/text/eia608/Eia608Subtitle.java b/library/src/main/java/com/google/android/exoplayer2/text/cea/CeaSubtitle.java similarity index 86% rename from library/src/main/java/com/google/android/exoplayer2/text/eia608/Eia608Subtitle.java rename to library/src/main/java/com/google/android/exoplayer2/text/cea/CeaSubtitle.java index 5ec8ca6d95..5becefe106 100644 --- a/library/src/main/java/com/google/android/exoplayer2/text/eia608/Eia608Subtitle.java +++ b/library/src/main/java/com/google/android/exoplayer2/text/cea/CeaSubtitle.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.google.android.exoplayer2.text.eia608; +package com.google.android.exoplayer2.text.cea; import com.google.android.exoplayer2.text.Cue; import com.google.android.exoplayer2.text.Subtitle; @@ -21,16 +21,16 @@ import java.util.Collections; import java.util.List; /** - * A representation of an EIA-608 subtitle. + * A representation of a CEA subtitle. */ -/* package */ final class Eia608Subtitle implements Subtitle { +/* package */ final class CeaSubtitle implements Subtitle { private final List cues; /** * @param cue The subtitle cue. */ - public Eia608Subtitle(Cue cue) { + public CeaSubtitle(Cue cue) { if (cue == null) { cues = Collections.emptyList(); } else { diff --git a/library/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java b/library/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java index f3f63b1a76..561aba0146 100644 --- a/library/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java +++ b/library/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java @@ -64,7 +64,7 @@ public final class MimeTypes { public static final String APPLICATION_MP4 = BASE_TYPE_APPLICATION + "/mp4"; public static final String APPLICATION_WEBM = BASE_TYPE_APPLICATION + "/webm"; public static final String APPLICATION_ID3 = BASE_TYPE_APPLICATION + "/id3"; - public static final String APPLICATION_EIA608 = BASE_TYPE_APPLICATION + "/eia-608"; + public static final String APPLICATION_CEA608 = BASE_TYPE_APPLICATION + "/cea-608"; public static final String APPLICATION_SUBRIP = BASE_TYPE_APPLICATION + "/x-subrip"; public static final String APPLICATION_TTML = BASE_TYPE_APPLICATION + "/ttml+xml"; public static final String APPLICATION_M3U8 = BASE_TYPE_APPLICATION + "/x-mpegURL"; @@ -209,7 +209,7 @@ public final class MimeTypes { return C.TRACK_TYPE_AUDIO; } else if (isVideo(mimeType)) { return C.TRACK_TYPE_VIDEO; - } else if (isText(mimeType) || APPLICATION_EIA608.equals(mimeType) + } else if (isText(mimeType) || APPLICATION_CEA608.equals(mimeType) || APPLICATION_SUBRIP.equals(mimeType) || APPLICATION_TTML.equals(mimeType) || APPLICATION_TX3G.equals(mimeType) || APPLICATION_MP4VTT.equals(mimeType) || APPLICATION_RAWCC.equals(mimeType) || APPLICATION_VOBSUB.equals(mimeType) diff --git a/library/src/main/java/com/google/android/exoplayer2/util/ParsableByteArray.java b/library/src/main/java/com/google/android/exoplayer2/util/ParsableByteArray.java index e140484531..b306fbf76e 100644 --- a/library/src/main/java/com/google/android/exoplayer2/util/ParsableByteArray.java +++ b/library/src/main/java/com/google/android/exoplayer2/util/ParsableByteArray.java @@ -194,6 +194,13 @@ public final class ParsableByteArray { position += length; } + /** + * Peeks at the next byte as an unsigned value. + */ + public int peekUnsignedByte() { + return (data[position] & 0xFF); + } + /** * Reads the next byte as an unsigned value. */ From 8bf0243daf3f20d421cc17d87f5fb9b51d30ef4b Mon Sep 17 00:00:00 2001 From: olly Date: Thu, 29 Sep 2016 07:42:48 -0700 Subject: [PATCH 32/36] Fix mp3 extraction failure for short frames ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=134660133 --- .../exoplayer2/extractor/mp3/Mp3Extractor.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/mp3/Mp3Extractor.java b/library/src/main/java/com/google/android/exoplayer2/extractor/mp3/Mp3Extractor.java index d201b3ace2..ab501af1cb 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/mp3/Mp3Extractor.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/mp3/Mp3Extractor.java @@ -272,14 +272,17 @@ public final class Mp3Extractor implements Extractor { long position = input.getPosition(); long length = input.getLength(); + int headerData = 0; + Seeker seeker = null; // Check if there is a Xing header. int xingBase = (synchronizedHeader.version & 1) != 0 ? (synchronizedHeader.channels != 1 ? 36 : 21) // MPEG 1 : (synchronizedHeader.channels != 1 ? 21 : 13); // MPEG 2 or 2.5 - frame.setPosition(xingBase); - int headerData = frame.readInt(); - Seeker seeker = null; + if (frame.limit() >= xingBase + 4) { + frame.setPosition(xingBase); + headerData = frame.readInt(); + } if (headerData == XING_HEADER || headerData == INFO_HEADER) { seeker = XingSeeker.create(synchronizedHeader, frame, position, length); if (seeker != null && !gaplessInfoHolder.hasGaplessInfo()) { @@ -291,7 +294,7 @@ public final class Mp3Extractor implements Extractor { gaplessInfoHolder.setFromXingHeaderValue(scratch.readUnsignedInt24()); } input.skipFully(synchronizedHeader.frameSize); - } else { + } else if (frame.limit() >= 40) { // Check if there is a VBRI header. frame.setPosition(36); // MPEG audio header (4 bytes) + 32 bytes. headerData = frame.readInt(); From 21626c417e3545d767d4fac8661589489f87097b Mon Sep 17 00:00:00 2001 From: olly Date: Thu, 29 Sep 2016 08:08:51 -0700 Subject: [PATCH 33/36] Bump version to 2.0.1 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=134662367 --- RELEASENOTES.md | 9 +++++++++ demo/src/main/AndroidManifest.xml | 4 ++-- library/build.gradle | 2 +- .../google/android/exoplayer2/ExoPlayerLibraryInfo.java | 4 ++-- playbacktests/src/main/AndroidManifest.xml | 4 ++-- 5 files changed, 16 insertions(+), 7 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index fb3b33d696..d53d153912 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,5 +1,14 @@ # Release notes # +### r2.0.1 ### + +* Fix playback of short duration content + ([#1837](https://github.com/google/ExoPlayer/issues/1837)). +* Fix MergingMediaSource preparation issue + ([#1853](https://github.com/google/ExoPlayer/issues/1853)). +* Fix live stream buffering (out of memory) issue + ([#1825](https://github.com/google/ExoPlayer/issues/1825)). + ### r2.0.0 ### ExoPlayer 2.x is a major iteration of the library. It includes significant API diff --git a/demo/src/main/AndroidManifest.xml b/demo/src/main/AndroidManifest.xml index 6cf98d2aca..b270b1410f 100644 --- a/demo/src/main/AndroidManifest.xml +++ b/demo/src/main/AndroidManifest.xml @@ -16,8 +16,8 @@ + android:versionCode="2001" + android:versionName="2.0.1"> diff --git a/library/build.gradle b/library/build.gradle index 3c7a633f49..c0fdfc1d14 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -96,7 +96,7 @@ publish { userOrg = 'google' groupId = 'com.google.android.exoplayer' artifactId = 'exoplayer' - version = 'r2.0.0' + version = 'r2.0.1' description = 'The ExoPlayer library.' website = 'https://github.com/google/ExoPlayer' } diff --git a/library/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java b/library/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java index e2e17c425c..1e95372730 100644 --- a/library/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java +++ b/library/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java @@ -23,7 +23,7 @@ public interface ExoPlayerLibraryInfo { /** * The version of the library, expressed as a string. */ - String VERSION = "2.0.0"; + String VERSION = "2.0.1"; /** * The version of the library, expressed as an integer. @@ -32,7 +32,7 @@ public interface ExoPlayerLibraryInfo { * corresponding integer version 1002003 (001-002-003), and "123.45.6" has the corresponding * integer version 123045006 (123-045-006). */ - int VERSION_INT = 2000000; + int VERSION_INT = 2000001; /** * Whether the library was compiled with {@link com.google.android.exoplayer2.util.Assertions} diff --git a/playbacktests/src/main/AndroidManifest.xml b/playbacktests/src/main/AndroidManifest.xml index 38e8491b31..7dde5b65f8 100644 --- a/playbacktests/src/main/AndroidManifest.xml +++ b/playbacktests/src/main/AndroidManifest.xml @@ -17,8 +17,8 @@ + android:versionCode="2001" + android:versionName="2.0.1"> From 7971a18a836fe6bbb95fcd4f0ff994fd31836048 Mon Sep 17 00:00:00 2001 From: olly Date: Thu, 29 Sep 2016 09:57:45 -0700 Subject: [PATCH 34/36] Revert back to default cue parameters until we support positioning ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=134673236 --- .../android/exoplayer2/text/cea/Cea608Decoder.java | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/library/src/main/java/com/google/android/exoplayer2/text/cea/Cea608Decoder.java b/library/src/main/java/com/google/android/exoplayer2/text/cea/Cea608Decoder.java index 0b497d3646..2715b0cbe0 100644 --- a/library/src/main/java/com/google/android/exoplayer2/text/cea/Cea608Decoder.java +++ b/library/src/main/java/com/google/android/exoplayer2/text/cea/Cea608Decoder.java @@ -15,7 +15,6 @@ */ package com.google.android.exoplayer2.text.cea; -import android.text.Layout.Alignment; import android.text.TextUtils; import com.google.android.exoplayer2.text.Cue; import com.google.android.exoplayer2.text.Subtitle; @@ -156,14 +155,6 @@ public final class Cea608Decoder extends CeaDecoder { 0xC5, 0xE5, 0xD8, 0xF8, 0x250C, 0x2510, 0x2514, 0x2518 }; - private static final Alignment CUE_TEXT_ALIGNMENT = Alignment.ALIGN_NORMAL; - private static final float CUE_LINE = Cue.DIMEN_UNSET; - private static final int CUE_LINE_TYPE = Cue.TYPE_UNSET; - private static final int CUE_LINE_ANCHOR = Cue.TYPE_UNSET; - private static final float CUE_POSITION = 0.1f; - private static final int CUE_POSITION_ANCHOR = Cue.TYPE_UNSET; - private static final float CUE_SIZE = 0.8f; - private final ParsableByteArray ccData; private final StringBuilder captionStringBuilder; @@ -218,8 +209,7 @@ public final class Cea608Decoder extends CeaDecoder { @Override protected Subtitle createSubtitle() { lastCaptionString = captionString; - return new CeaSubtitle(new Cue(captionString, CUE_TEXT_ALIGNMENT, CUE_LINE, CUE_LINE_TYPE, - CUE_LINE_ANCHOR, CUE_POSITION, CUE_POSITION_ANCHOR, CUE_SIZE)); + return new CeaSubtitle(new Cue(captionString)); } @Override From ee4d297f22a3b6775a13a64cf9a97e229be21f00 Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Fri, 30 Sep 2016 05:46:14 -0700 Subject: [PATCH 35/36] Fix seeking in the last period. When reading the last period, the readingPeriodHolder was set to null in updatePeriods if it was the last period. (This would occur almost immediately when playing a single-period source.) seekToPeriodPosition suppresses reusing a loaded/prepared period if the reading period and playing period did not match, which meant that the whole timeline was recreated when seeking in the last period. Leave readingPeriodHolder non-null. This means that at all times either playingPeriodHolder == readingPeriodHolder (and they could be null or non-null), or playingPeriodHolder and readingPeriodHolder differ and are both non-null. Also fix an issue where streams were never forced to be recreated during track reselection when reading ahead. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=134774238 --- .../exoplayer2/ExoPlayerImplInternal.java | 29 ++++++++----------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/library/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java b/library/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java index 84ed37af8b..2a9360337a 100644 --- a/library/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java +++ b/library/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java @@ -529,9 +529,9 @@ import java.io.IOException; rebuffering = false; setState(ExoPlayer.STATE_BUFFERING); - if (periodPositionUs == C.TIME_UNSET - || (readingPeriodHolder != playingPeriodHolder && (periodIndex == playingPeriodHolder.index - || (readingPeriodHolder != null && periodIndex == readingPeriodHolder.index)))) { + if (periodPositionUs == C.TIME_UNSET || (readingPeriodHolder != playingPeriodHolder + && (periodIndex == playingPeriodHolder.index + || periodIndex == readingPeriodHolder.index))) { // Clear the timeline because either the seek position is not known, or a renderer is reading // ahead to the next period and the seek is to either the playing or reading period. periodIndex = C.INDEX_UNSET; @@ -690,16 +690,14 @@ import java.io.IOException; } if (selectionsChangedForReadPeriod) { - // Release everything after the playing period because a renderer may have read data from a - // track whose selection has now changed. + // Update streams and rebuffer for the new selection, recreating all streams if reading ahead. + boolean recreateStreams = readingPeriodHolder != playingPeriodHolder; releasePeriodHoldersFrom(playingPeriodHolder.next); playingPeriodHolder.next = null; readingPeriodHolder = playingPeriodHolder; loadingPeriodHolder = playingPeriodHolder; bufferAheadPeriodCount = 0; - // Update streams for the new selection, recreating all streams if reading ahead. - boolean recreateStreams = readingPeriodHolder != playingPeriodHolder; boolean[] streamResetFlags = new boolean[renderers.length]; long periodPositionUs = playingPeriodHolder.updatePeriodTrackSelection( playbackInfo.positionUs, loadControl, recreateStreams, streamResetFlags); @@ -810,11 +808,11 @@ import java.io.IOException; playingPeriodHolder.setIndex(timeline, timeline.getWindow(period.windowIndex, window), index); - MediaPeriodHolder previousPeriod = playingPeriodHolder; + MediaPeriodHolder previousPeriodHolder = playingPeriodHolder; boolean seenReadingPeriod = false; bufferAheadPeriodCount = 0; - while (previousPeriod.next != null) { - MediaPeriodHolder periodHolder = previousPeriod.next; + while (previousPeriodHolder.next != null) { + MediaPeriodHolder periodHolder = previousPeriodHolder.next; index++; timeline.getPeriod(index, period, true); if (!periodHolder.uid.equals(period.uid)) { @@ -835,7 +833,7 @@ import java.io.IOException; } // Update the loading period to be the latest period that is still valid. - loadingPeriodHolder = previousPeriod; + loadingPeriodHolder = previousPeriodHolder; loadingPeriodHolder.next = null; // Release the rest of the timeline. @@ -849,7 +847,7 @@ import java.io.IOException; if (periodHolder == readingPeriodHolder) { seenReadingPeriod = true; } - previousPeriod = periodHolder; + previousPeriodHolder = periodHolder; } } else if (loadingPeriodHolder != null) { Object uid = loadingPeriodHolder.uid; @@ -997,14 +995,11 @@ import java.io.IOException; } updateTimelineState(); - if (readingPeriodHolder != null && readingPeriodHolder.isLast) { - readingPeriodHolder = null; + if (readingPeriodHolder.isLast) { + // The renderers have their final SampleStreams. for (Renderer renderer : enabledRenderers) { renderer.setCurrentStreamIsFinal(); } - } - if (readingPeriodHolder == null) { - // The renderers have their final SampleStreams. return; } From c381093a485d956562bb824fa55ef67f38646929 Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Fri, 30 Sep 2016 06:21:26 -0700 Subject: [PATCH 36/36] Update variable name in comment. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=134776852 --- .../com/google/android/exoplayer2/extractor/ts/H262Reader.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/H262Reader.java b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/H262Reader.java index f74f771bb7..cdbd8e391d 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/H262Reader.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/H262Reader.java @@ -136,7 +136,7 @@ import java.util.Collections; if (startCodeValue == START_GROUP) { foundFirstFrameInGroup = false; isKeyframe = true; - } else /* startCode == START_PICTURE */ { + } else /* startCodeValue == START_PICTURE */ { frameTimeUs = pesPtsUsAvailable ? pesTimeUs : (frameTimeUs + frameDurationUs); framePosition = totalBytesWritten - bytesWrittenPastStartCode; pesPtsUsAvailable = false;