diff --git a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java index b0f834d37c..f2bd503737 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java @@ -631,6 +631,13 @@ public abstract class MediaCodecRenderer extends BaseRenderer { throws ExoPlaybackException { boolean outputFormatChanged = false; @Nullable Format format = formatQueue.pollFloor(presentationTimeUs); + if (format == null && codecOutputMediaFormatChanged) { + // If the codec's output MediaFormat has changed then there should be a corresponding Format + // change, which we've not found. Check the Format queue in case the corresponding + // presentation timestamp is greater than presentationTimeUs, which can happen for some codecs + // [Internal ref: b/162719047]. + format = formatQueue.pollFirst(); + } if (format != null) { outputFormat = format; outputFormatChanged = true; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/util/TimedValueQueue.java b/library/core/src/main/java/com/google/android/exoplayer2/util/TimedValueQueue.java index da5d9bafeb..d49b37224c 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/util/TimedValueQueue.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/util/TimedValueQueue.java @@ -62,6 +62,12 @@ public final class TimedValueQueue { return size; } + /** Removes and returns the first value in the queue, or null if the queue is empty. */ + @Nullable + public synchronized V pollFirst() { + return size == 0 ? null : popFirst(); + } + /** * Returns the value with the greatest timestamp which is less than or equal to the given * timestamp. Removes all older values and the returned one from the buffer. @@ -71,7 +77,8 @@ public final class TimedValueQueue { * timestamp or null if there is no such value. * @see #poll(long) */ - public synchronized @Nullable V pollFloor(long timestamp) { + @Nullable + public synchronized V pollFloor(long timestamp) { return poll(timestamp, /* onlyOlder= */ true); } @@ -83,7 +90,8 @@ public final class TimedValueQueue { * @return The value with the closest timestamp or null if the buffer is empty. * @see #pollFloor(long) */ - public synchronized @Nullable V poll(long timestamp) { + @Nullable + public synchronized V poll(long timestamp) { return poll(timestamp, /* onlyOlder= */ false); } @@ -99,7 +107,7 @@ public final class TimedValueQueue { */ @Nullable private V poll(long timestamp, boolean onlyOlder) { - V value = null; + @Nullable V value = null; long previousTimeDiff = Long.MAX_VALUE; while (size > 0) { long timeDiff = timestamp - timestamps[first]; @@ -107,14 +115,21 @@ public final class TimedValueQueue { break; } previousTimeDiff = timeDiff; - value = values[first]; - values[first] = null; - first = (first + 1) % values.length; - size--; + value = popFirst(); } return value; } + @Nullable + private V popFirst() { + Assertions.checkState(size > 0); + @Nullable V value = values[first]; + values[first] = null; + first = (first + 1) % values.length; + size--; + return value; + } + private void clearBufferOnTimeDiscontinuity(long timestamp) { if (size > 0) { int last = (first + size - 1) % values.length; @@ -131,7 +146,7 @@ public final class TimedValueQueue { } int newCapacity = capacity * 2; long[] newTimestamps = new long[newCapacity]; - V[] newValues = newArray(newCapacity); + @NullableType V[] newValues = newArray(newCapacity); // Reset the loop starting index to 0 while coping to the new buffer. // First copy the values from 'first' index to the end of original array. int length = capacity - first; @@ -155,7 +170,7 @@ public final class TimedValueQueue { } @SuppressWarnings("unchecked") - private static V[] newArray(int length) { + private static @NullableType V[] newArray(int length) { return (V[]) new Object[length]; } } diff --git a/library/core/src/test/java/com/google/android/exoplayer2/util/TimedValueQueueTest.java b/library/core/src/test/java/com/google/android/exoplayer2/util/TimedValueQueueTest.java index 8f1949f96e..0334027234 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/util/TimedValueQueueTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/util/TimedValueQueueTest.java @@ -33,23 +33,13 @@ public class TimedValueQueueTest { queue = new TimedValueQueue<>(); } - @Test - public void addAndPollValues() { - queue.add(0, "a"); - queue.add(1, "b"); - queue.add(2, "c"); - assertThat(queue.poll(0)).isEqualTo("a"); - assertThat(queue.poll(1)).isEqualTo("b"); - assertThat(queue.poll(2)).isEqualTo("c"); - } - @Test public void bufferCapacityIncreasesAutomatically() { queue = new TimedValueQueue<>(1); for (int i = 0; i < 20; i++) { queue.add(i, "" + i); if ((i & 1) == 1) { - assertThat(queue.poll(0)).isEqualTo("" + (i / 2)); + assertThat(queue.pollFirst()).isEqualTo("" + (i / 2)); } } assertThat(queue.size()).isEqualTo(10); @@ -61,7 +51,7 @@ public class TimedValueQueueTest { queue.add(2, "c"); queue.add(0, "a"); assertThat(queue.size()).isEqualTo(1); - assertThat(queue.poll(0)).isEqualTo("a"); + assertThat(queue.pollFirst()).isEqualTo("a"); } @Test @@ -71,7 +61,37 @@ public class TimedValueQueueTest { queue.add(3, "c"); queue.add(2, "a"); assertThat(queue.size()).isEqualTo(1); - assertThat(queue.poll(2)).isEqualTo("a"); + assertThat(queue.pollFirst()).isEqualTo("a"); + } + + @Test + public void pollFirstReturnsValues() { + queue.add(0, "a"); + queue.add(1, "b"); + queue.add(2, "c"); + assertThat(queue.pollFirst()).isEqualTo("a"); + assertThat(queue.size()).isEqualTo(2); + assertThat(queue.pollFirst()).isEqualTo("b"); + assertThat(queue.size()).isEqualTo(1); + assertThat(queue.pollFirst()).isEqualTo("c"); + assertThat(queue.size()).isEqualTo(0); + assertThat(queue.pollFirst()).isEqualTo(null); + assertThat(queue.size()).isEqualTo(0); + } + + @Test + public void pollReturnsValues() { + queue.add(0, "a"); + queue.add(1, "b"); + queue.add(2, "c"); + assertThat(queue.poll(0)).isEqualTo("a"); + assertThat(queue.size()).isEqualTo(2); + assertThat(queue.poll(1)).isEqualTo("b"); + assertThat(queue.size()).isEqualTo(1); + assertThat(queue.poll(2)).isEqualTo("c"); + assertThat(queue.size()).isEqualTo(0); + assertThat(queue.pollFirst()).isEqualTo(null); + assertThat(queue.size()).isEqualTo(0); } @Test