Commit dc813eca by andrewlewis Committed by Oliver Woodman

Fix stuck ad playbacks with DRM-protected content

When ClippingMediaPeriod first tried to read a buffer, if its end
position was before the end of the stream and it was buffered to its end
position, it would sometimes erroneously signal end-of-stream for
protected content because the sample queue might be waiting for DRM keys
at this point.

Work around the issue temporarily by signaling this specific case back
to ClippingMediaPeriod via the DecoderInputBuffer.

There will likely be a cleaner fix as a result of adding support for
dynamic clip end points in the future, at which point this can be
reverted.

issue:#7188
PiperOrigin-RevId: 305081757
parent 1f544b08
...@@ -63,6 +63,14 @@ public class DecoderInputBuffer extends Buffer { ...@@ -63,6 +63,14 @@ public class DecoderInputBuffer extends Buffer {
/** The buffer's data, or {@code null} if no data has been set. */ /** The buffer's data, or {@code null} if no data has been set. */
@Nullable public ByteBuffer data; @Nullable public ByteBuffer data;
// TODO: Remove this temporary signaling once end-of-stream propagation for clips using content
// protection is fixed. See [Internal: b/153326944] for details.
/**
* Whether the last attempt to read a sample into this buffer failed due to not yet having the DRM
* keys associated with the next sample.
*/
public boolean waitingForKeys;
/** /**
* The time at which the sample should be presented. * The time at which the sample should be presented.
*/ */
...@@ -183,6 +191,7 @@ public class DecoderInputBuffer extends Buffer { ...@@ -183,6 +191,7 @@ public class DecoderInputBuffer extends Buffer {
if (supplementalData != null) { if (supplementalData != null) {
supplementalData.clear(); supplementalData.clear();
} }
waitingForKeys = false;
} }
private ByteBuffer createReplacementByteBuffer(int requiredCapacity) { private ByteBuffer createReplacementByteBuffer(int requiredCapacity) {
......
...@@ -329,7 +329,8 @@ public final class ClippingMediaPeriod implements MediaPeriod, MediaPeriod.Callb ...@@ -329,7 +329,8 @@ public final class ClippingMediaPeriod implements MediaPeriod, MediaPeriod.Callb
if (endUs != C.TIME_END_OF_SOURCE if (endUs != C.TIME_END_OF_SOURCE
&& ((result == C.RESULT_BUFFER_READ && buffer.timeUs >= endUs) && ((result == C.RESULT_BUFFER_READ && buffer.timeUs >= endUs)
|| (result == C.RESULT_NOTHING_READ || (result == C.RESULT_NOTHING_READ
&& getBufferedPositionUs() == C.TIME_END_OF_SOURCE))) { && getBufferedPositionUs() == C.TIME_END_OF_SOURCE
&& !buffer.waitingForKeys))) {
buffer.clear(); buffer.clear();
buffer.setFlags(C.BUFFER_FLAG_END_OF_STREAM); buffer.setFlags(C.BUFFER_FLAG_END_OF_STREAM);
sentEos = true; sentEos = true;
......
...@@ -554,7 +554,7 @@ public class SampleQueue implements TrackOutput { ...@@ -554,7 +554,7 @@ public class SampleQueue implements TrackOutput {
boolean loadingFinished, boolean loadingFinished,
long decodeOnlyUntilUs, long decodeOnlyUntilUs,
SampleExtrasHolder extrasHolder) { SampleExtrasHolder extrasHolder) {
buffer.waitingForKeys = false;
// This is a temporary fix for https://github.com/google/ExoPlayer/issues/6155. // This is a temporary fix for https://github.com/google/ExoPlayer/issues/6155.
// TODO: Remove it and replace it with a fix that discards samples when writing to the queue. // TODO: Remove it and replace it with a fix that discards samples when writing to the queue.
boolean hasNextSample; boolean hasNextSample;
...@@ -588,6 +588,7 @@ public class SampleQueue implements TrackOutput { ...@@ -588,6 +588,7 @@ public class SampleQueue implements TrackOutput {
} }
if (!mayReadSample(relativeReadIndex)) { if (!mayReadSample(relativeReadIndex)) {
buffer.waitingForKeys = true;
return C.RESULT_NOTHING_READ; return C.RESULT_NOTHING_READ;
} }
......
...@@ -368,8 +368,10 @@ public final class SampleQueueTest { ...@@ -368,8 +368,10 @@ public final class SampleQueueTest {
assertReadFormat(/* formatRequired= */ false, FORMAT_ENCRYPTED); assertReadFormat(/* formatRequired= */ false, FORMAT_ENCRYPTED);
assertReadNothing(/* formatRequired= */ false); assertReadNothing(/* formatRequired= */ false);
assertThat(inputBuffer.waitingForKeys).isTrue();
when(mockDrmSession.getState()).thenReturn(DrmSession.STATE_OPENED_WITH_KEYS); when(mockDrmSession.getState()).thenReturn(DrmSession.STATE_OPENED_WITH_KEYS);
assertReadEncryptedSample(/* sampleIndex= */ 0); assertReadEncryptedSample(/* sampleIndex= */ 0);
assertThat(inputBuffer.waitingForKeys).isFalse();
} }
@Test @Test
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment