• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.google.android.exoplayer2.source;
17 
18 import android.os.Looper;
19 import androidx.annotation.CallSuper;
20 import androidx.annotation.Nullable;
21 import androidx.annotation.VisibleForTesting;
22 import com.google.android.exoplayer2.C;
23 import com.google.android.exoplayer2.Format;
24 import com.google.android.exoplayer2.FormatHolder;
25 import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
26 import com.google.android.exoplayer2.drm.DrmInitData;
27 import com.google.android.exoplayer2.drm.DrmSession;
28 import com.google.android.exoplayer2.drm.DrmSessionManager;
29 import com.google.android.exoplayer2.extractor.TrackOutput;
30 import com.google.android.exoplayer2.upstream.Allocator;
31 import com.google.android.exoplayer2.upstream.DataReader;
32 import com.google.android.exoplayer2.util.Assertions;
33 import com.google.android.exoplayer2.util.MediaSourceEventDispatcher;
34 import com.google.android.exoplayer2.util.MimeTypes;
35 import com.google.android.exoplayer2.util.ParsableByteArray;
36 import com.google.android.exoplayer2.util.Util;
37 import java.io.IOException;
38 import org.checkerframework.checker.nullness.compatqual.NullableType;
39 
40 /** A queue of media samples. */
41 public class SampleQueue implements TrackOutput {
42 
43   /** A listener for changes to the upstream format. */
44   public interface UpstreamFormatChangedListener {
45 
46     /**
47      * Called on the loading thread when an upstream format change occurs.
48      *
49      * @param format The new upstream format.
50      */
onUpstreamFormatChanged(Format format)51     void onUpstreamFormatChanged(Format format);
52   }
53 
54   @VisibleForTesting /* package */ static final int SAMPLE_CAPACITY_INCREMENT = 1000;
55 
56   private final SampleDataQueue sampleDataQueue;
57   private final SampleExtrasHolder extrasHolder;
58   private final Looper playbackLooper;
59   private final DrmSessionManager drmSessionManager;
60   private final MediaSourceEventDispatcher eventDispatcher;
61   @Nullable private UpstreamFormatChangedListener upstreamFormatChangeListener;
62 
63   @Nullable private Format downstreamFormat;
64   @Nullable private DrmSession currentDrmSession;
65 
66   private int capacity;
67   private int[] sourceIds;
68   private long[] offsets;
69   private int[] sizes;
70   private int[] flags;
71   private long[] timesUs;
72   private @NullableType CryptoData[] cryptoDatas;
73   private Format[] formats;
74 
75   private int length;
76   private int absoluteFirstIndex;
77   private int relativeFirstIndex;
78   private int readPosition;
79 
80   private long largestDiscardedTimestampUs;
81   private long largestQueuedTimestampUs;
82   private boolean isLastSampleQueued;
83   private boolean upstreamKeyframeRequired;
84   private boolean upstreamFormatRequired;
85   private boolean upstreamFormatAdjustmentRequired;
86   @Nullable private Format unadjustedUpstreamFormat;
87   @Nullable private Format upstreamFormat;
88   @Nullable private Format upstreamCommittedFormat;
89   private int upstreamSourceId;
90 
91   private long sampleOffsetUs;
92   private boolean pendingSplice;
93 
94   /**
95    * Creates a sample queue.
96    *
97    * @param allocator An {@link Allocator} from which allocations for sample data can be obtained.
98    * @param playbackLooper The looper associated with the media playback thread.
99    * @param drmSessionManager The {@link DrmSessionManager} to obtain {@link DrmSession DrmSessions}
100    *     from. The created instance does not take ownership of this {@link DrmSessionManager}.
101    * @param eventDispatcher A {@link MediaSourceEventDispatcher} to notify of events related to this
102    *     SampleQueue.
103    */
SampleQueue( Allocator allocator, Looper playbackLooper, DrmSessionManager drmSessionManager, MediaSourceEventDispatcher eventDispatcher)104   public SampleQueue(
105       Allocator allocator,
106       Looper playbackLooper,
107       DrmSessionManager drmSessionManager,
108       MediaSourceEventDispatcher eventDispatcher) {
109     this.playbackLooper = playbackLooper;
110     this.drmSessionManager = drmSessionManager;
111     this.eventDispatcher = eventDispatcher;
112     sampleDataQueue = new SampleDataQueue(allocator);
113     extrasHolder = new SampleExtrasHolder();
114     capacity = SAMPLE_CAPACITY_INCREMENT;
115     sourceIds = new int[capacity];
116     offsets = new long[capacity];
117     timesUs = new long[capacity];
118     flags = new int[capacity];
119     sizes = new int[capacity];
120     cryptoDatas = new CryptoData[capacity];
121     formats = new Format[capacity];
122     largestDiscardedTimestampUs = Long.MIN_VALUE;
123     largestQueuedTimestampUs = Long.MIN_VALUE;
124     upstreamFormatRequired = true;
125     upstreamKeyframeRequired = true;
126   }
127 
128   // Called by the consuming thread when there is no loading thread.
129 
130   /** Calls {@link #reset(boolean) reset(true)} and releases any resources owned by the queue. */
131   @CallSuper
release()132   public void release() {
133     reset(/* resetUpstreamFormat= */ true);
134     releaseDrmSessionReferences();
135   }
136 
137   /** Convenience method for {@code reset(false)}. */
reset()138   public final void reset() {
139     reset(/* resetUpstreamFormat= */ false);
140   }
141 
142   /**
143    * Clears all samples from the queue.
144    *
145    * @param resetUpstreamFormat Whether the upstream format should be cleared. If set to false,
146    *     samples queued after the reset (and before a subsequent call to {@link #format(Format)})
147    *     are assumed to have the current upstream format. If set to true, {@link #format(Format)}
148    *     must be called after the reset before any more samples can be queued.
149    */
150   @CallSuper
reset(boolean resetUpstreamFormat)151   public void reset(boolean resetUpstreamFormat) {
152     sampleDataQueue.reset();
153     length = 0;
154     absoluteFirstIndex = 0;
155     relativeFirstIndex = 0;
156     readPosition = 0;
157     upstreamKeyframeRequired = true;
158     largestDiscardedTimestampUs = Long.MIN_VALUE;
159     largestQueuedTimestampUs = Long.MIN_VALUE;
160     isLastSampleQueued = false;
161     upstreamCommittedFormat = null;
162     if (resetUpstreamFormat) {
163       unadjustedUpstreamFormat = null;
164       upstreamFormat = null;
165       upstreamFormatRequired = true;
166     }
167   }
168 
169   /**
170    * Sets a source identifier for subsequent samples.
171    *
172    * @param sourceId The source identifier.
173    */
sourceId(int sourceId)174   public final void sourceId(int sourceId) {
175     upstreamSourceId = sourceId;
176   }
177 
178   /** Indicates samples that are subsequently queued should be spliced into those already queued. */
splice()179   public final void splice() {
180     pendingSplice = true;
181   }
182 
183   /** Returns the current absolute write index. */
getWriteIndex()184   public final int getWriteIndex() {
185     return absoluteFirstIndex + length;
186   }
187 
188   /**
189    * Discards samples from the write side of the queue.
190    *
191    * @param discardFromIndex The absolute index of the first sample to be discarded. Must be in the
192    *     range [{@link #getReadIndex()}, {@link #getWriteIndex()}].
193    */
discardUpstreamSamples(int discardFromIndex)194   public final void discardUpstreamSamples(int discardFromIndex) {
195     sampleDataQueue.discardUpstreamSampleBytes(discardUpstreamSampleMetadata(discardFromIndex));
196   }
197 
198   // Called by the consuming thread.
199 
200   /** Calls {@link #discardToEnd()} and releases any resources owned by the queue. */
201   @CallSuper
preRelease()202   public void preRelease() {
203     discardToEnd();
204     releaseDrmSessionReferences();
205   }
206 
207   /**
208    * Throws an error that's preventing data from being read. Does nothing if no such error exists.
209    *
210    * @throws IOException The underlying error.
211    */
212   @CallSuper
maybeThrowError()213   public void maybeThrowError() throws IOException {
214     // TODO: Avoid throwing if the DRM error is not preventing a read operation.
215     if (currentDrmSession != null && currentDrmSession.getState() == DrmSession.STATE_ERROR) {
216       throw Assertions.checkNotNull(currentDrmSession.getError());
217     }
218   }
219 
220   /** Returns the current absolute start index. */
getFirstIndex()221   public final int getFirstIndex() {
222     return absoluteFirstIndex;
223   }
224 
225   /** Returns the current absolute read index. */
getReadIndex()226   public final int getReadIndex() {
227     return absoluteFirstIndex + readPosition;
228   }
229 
230   /**
231    * Peeks the source id of the next sample to be read, or the current upstream source id if the
232    * queue is empty or if the read position is at the end of the queue.
233    *
234    * @return The source id.
235    */
peekSourceId()236   public final synchronized int peekSourceId() {
237     int relativeReadIndex = getRelativeIndex(readPosition);
238     return hasNextSample() ? sourceIds[relativeReadIndex] : upstreamSourceId;
239   }
240 
241   /** Returns the upstream {@link Format} in which samples are being queued. */
242   @Nullable
getUpstreamFormat()243   public final synchronized Format getUpstreamFormat() {
244     return upstreamFormatRequired ? null : upstreamFormat;
245   }
246 
247   /**
248    * Returns the largest sample timestamp that has been queued since the last {@link #reset}.
249    *
250    * <p>Samples that were discarded by calling {@link #discardUpstreamSamples(int)} are not
251    * considered as having been queued. Samples that were dequeued from the front of the queue are
252    * considered as having been queued.
253    *
254    * @return The largest sample timestamp that has been queued, or {@link Long#MIN_VALUE} if no
255    *     samples have been queued.
256    */
getLargestQueuedTimestampUs()257   public final synchronized long getLargestQueuedTimestampUs() {
258     return largestQueuedTimestampUs;
259   }
260 
261   /**
262    * Returns whether the last sample of the stream has knowingly been queued. A return value of
263    * {@code false} means that the last sample had not been queued or that it's unknown whether the
264    * last sample has been queued.
265    *
266    * <p>Samples that were discarded by calling {@link #discardUpstreamSamples(int)} are not
267    * considered as having been queued. Samples that were dequeued from the front of the queue are
268    * considered as having been queued.
269    */
isLastSampleQueued()270   public final synchronized boolean isLastSampleQueued() {
271     return isLastSampleQueued;
272   }
273 
274   /** Returns the timestamp of the first sample, or {@link Long#MIN_VALUE} if the queue is empty. */
getFirstTimestampUs()275   public final synchronized long getFirstTimestampUs() {
276     return length == 0 ? Long.MIN_VALUE : timesUs[relativeFirstIndex];
277   }
278 
279   /**
280    * Returns whether there is data available for reading.
281    *
282    * <p>Note: If the stream has ended then a buffer with the end of stream flag can always be read
283    * from {@link #read}. Hence an ended stream is always ready.
284    *
285    * @param loadingFinished Whether no more samples will be written to the sample queue. When true,
286    *     this method returns true if the sample queue is empty, because an empty sample queue means
287    *     the end of stream has been reached. When false, this method returns false if the sample
288    *     queue is empty.
289    */
290   @SuppressWarnings("ReferenceEquality") // See comments in setUpstreamFormat
291   @CallSuper
isReady(boolean loadingFinished)292   public synchronized boolean isReady(boolean loadingFinished) {
293     if (!hasNextSample()) {
294       return loadingFinished
295           || isLastSampleQueued
296           || (upstreamFormat != null && upstreamFormat != downstreamFormat);
297     }
298     int relativeReadIndex = getRelativeIndex(readPosition);
299     if (formats[relativeReadIndex] != downstreamFormat) {
300       // A format can be read.
301       return true;
302     }
303     return mayReadSample(relativeReadIndex);
304   }
305 
306   /**
307    * Attempts to read from the queue.
308    *
309    * <p>{@link Format Formats} read from this method may be associated to a {@link DrmSession}
310    * through {@link FormatHolder#drmSession}, which is populated in two scenarios:
311    *
312    * <ul>
313    *   <li>The {@link Format} has a non-null {@link Format#drmInitData}.
314    *   <li>The {@link DrmSessionManager} provides placeholder sessions for this queue's track type.
315    *       See {@link DrmSessionManager#acquirePlaceholderSession(Looper, int)}.
316    * </ul>
317    *
318    * @param formatHolder A {@link FormatHolder} to populate in the case of reading a format.
319    * @param buffer A {@link DecoderInputBuffer} to populate in the case of reading a sample or the
320    *     end of the stream. If the end of the stream has been reached, the {@link
321    *     C#BUFFER_FLAG_END_OF_STREAM} flag will be set on the buffer. If a {@link
322    *     DecoderInputBuffer#isFlagsOnly() flags-only} buffer is passed, only the buffer flags may be
323    *     populated by this method and the read position of the queue will not change.
324    * @param formatRequired Whether the caller requires that the format of the stream be read even if
325    *     it's not changing. A sample will never be read if set to true, however it is still possible
326    *     for the end of stream or nothing to be read.
327    * @param loadingFinished True if an empty queue should be considered the end of the stream.
328    * @param decodeOnlyUntilUs If a buffer is read, the {@link C#BUFFER_FLAG_DECODE_ONLY} flag will
329    *     be set if the buffer's timestamp is less than this value.
330    * @return The result, which can be {@link C#RESULT_NOTHING_READ}, {@link C#RESULT_FORMAT_READ} or
331    *     {@link C#RESULT_BUFFER_READ}.
332    */
333   @CallSuper
read( FormatHolder formatHolder, DecoderInputBuffer buffer, boolean formatRequired, boolean loadingFinished, long decodeOnlyUntilUs)334   public int read(
335       FormatHolder formatHolder,
336       DecoderInputBuffer buffer,
337       boolean formatRequired,
338       boolean loadingFinished,
339       long decodeOnlyUntilUs) {
340     int result =
341         readSampleMetadata(
342             formatHolder, buffer, formatRequired, loadingFinished, decodeOnlyUntilUs, extrasHolder);
343     if (result == C.RESULT_BUFFER_READ && !buffer.isEndOfStream() && !buffer.isFlagsOnly()) {
344       sampleDataQueue.readToBuffer(buffer, extrasHolder);
345     }
346     return result;
347   }
348 
349   /**
350    * Attempts to seek the read position to the specified sample index.
351    *
352    * @param sampleIndex The sample index.
353    * @return Whether the seek was successful.
354    */
seekTo(int sampleIndex)355   public final synchronized boolean seekTo(int sampleIndex) {
356     rewind();
357     if (sampleIndex < absoluteFirstIndex || sampleIndex > absoluteFirstIndex + length) {
358       return false;
359     }
360     readPosition = sampleIndex - absoluteFirstIndex;
361     return true;
362   }
363 
364   /**
365    * Attempts to seek the read position to the keyframe before or at the specified time.
366    *
367    * @param timeUs The time to seek to.
368    * @param allowTimeBeyondBuffer Whether the operation can succeed if {@code timeUs} is beyond the
369    *     end of the queue, by seeking to the last sample (or keyframe).
370    * @return Whether the seek was successful.
371    */
seekTo(long timeUs, boolean allowTimeBeyondBuffer)372   public final synchronized boolean seekTo(long timeUs, boolean allowTimeBeyondBuffer) {
373     rewind();
374     int relativeReadIndex = getRelativeIndex(readPosition);
375     if (!hasNextSample()
376         || timeUs < timesUs[relativeReadIndex]
377         || (timeUs > largestQueuedTimestampUs && !allowTimeBeyondBuffer)) {
378       return false;
379     }
380     int offset =
381         findSampleBefore(relativeReadIndex, length - readPosition, timeUs, /* keyframe= */ true);
382     if (offset == -1) {
383       return false;
384     }
385     readPosition += offset;
386     return true;
387   }
388 
389   /**
390    * Advances the read position to the keyframe before or at the specified time.
391    *
392    * @param timeUs The time to advance to.
393    * @return The number of samples that were skipped, which may be equal to 0.
394    */
advanceTo(long timeUs)395   public final synchronized int advanceTo(long timeUs) {
396     int relativeReadIndex = getRelativeIndex(readPosition);
397     if (!hasNextSample() || timeUs < timesUs[relativeReadIndex]) {
398       return 0;
399     }
400     int offset =
401         findSampleBefore(relativeReadIndex, length - readPosition, timeUs, /* keyframe= */ true);
402     if (offset == -1) {
403       return 0;
404     }
405     readPosition += offset;
406     return offset;
407   }
408 
409   /**
410    * Advances the read position to the end of the queue.
411    *
412    * @return The number of samples that were skipped.
413    */
advanceToEnd()414   public final synchronized int advanceToEnd() {
415     int skipCount = length - readPosition;
416     readPosition = length;
417     return skipCount;
418   }
419 
420   /**
421    * Discards up to but not including the sample immediately before or at the specified time.
422    *
423    * @param timeUs The time to discard up to.
424    * @param toKeyframe If true then discards samples up to the keyframe before or at the specified
425    *     time, rather than any sample before or at that time.
426    * @param stopAtReadPosition If true then samples are only discarded if they're before the read
427    *     position. If false then samples at and beyond the read position may be discarded, in which
428    *     case the read position is advanced to the first remaining sample.
429    */
discardTo(long timeUs, boolean toKeyframe, boolean stopAtReadPosition)430   public final void discardTo(long timeUs, boolean toKeyframe, boolean stopAtReadPosition) {
431     sampleDataQueue.discardDownstreamTo(
432         discardSampleMetadataTo(timeUs, toKeyframe, stopAtReadPosition));
433   }
434 
435   /** Discards up to but not including the read position. */
discardToRead()436   public final void discardToRead() {
437     sampleDataQueue.discardDownstreamTo(discardSampleMetadataToRead());
438   }
439 
440   /** Discards all samples in the queue and advances the read position. */
discardToEnd()441   public final void discardToEnd() {
442     sampleDataQueue.discardDownstreamTo(discardSampleMetadataToEnd());
443   }
444 
445   // Called by the loading thread.
446 
447   /**
448    * Sets an offset that will be added to the timestamps (and sub-sample timestamps) of samples that
449    * are subsequently queued.
450    *
451    * @param sampleOffsetUs The timestamp offset in microseconds.
452    */
setSampleOffsetUs(long sampleOffsetUs)453   public final void setSampleOffsetUs(long sampleOffsetUs) {
454     if (this.sampleOffsetUs != sampleOffsetUs) {
455       this.sampleOffsetUs = sampleOffsetUs;
456       invalidateUpstreamFormatAdjustment();
457     }
458   }
459 
460   /**
461    * Sets a listener to be notified of changes to the upstream format.
462    *
463    * @param listener The listener.
464    */
setUpstreamFormatChangeListener( @ullable UpstreamFormatChangedListener listener)465   public final void setUpstreamFormatChangeListener(
466       @Nullable UpstreamFormatChangedListener listener) {
467     upstreamFormatChangeListener = listener;
468   }
469 
470   // TrackOutput implementation. Called by the loading thread.
471 
472   @Override
format(Format unadjustedUpstreamFormat)473   public final void format(Format unadjustedUpstreamFormat) {
474     Format adjustedUpstreamFormat = getAdjustedUpstreamFormat(unadjustedUpstreamFormat);
475     upstreamFormatAdjustmentRequired = false;
476     this.unadjustedUpstreamFormat = unadjustedUpstreamFormat;
477     boolean upstreamFormatChanged = setUpstreamFormat(adjustedUpstreamFormat);
478     if (upstreamFormatChangeListener != null && upstreamFormatChanged) {
479       upstreamFormatChangeListener.onUpstreamFormatChanged(adjustedUpstreamFormat);
480     }
481   }
482 
483   @Override
sampleData( DataReader input, int length, boolean allowEndOfInput, @SampleDataPart int sampleDataPart)484   public final int sampleData(
485       DataReader input, int length, boolean allowEndOfInput, @SampleDataPart int sampleDataPart)
486       throws IOException {
487     return sampleDataQueue.sampleData(input, length, allowEndOfInput);
488   }
489 
490   @Override
sampleData( ParsableByteArray buffer, int length, @SampleDataPart int sampleDataPart)491   public final void sampleData(
492       ParsableByteArray buffer, int length, @SampleDataPart int sampleDataPart) {
493     sampleDataQueue.sampleData(buffer, length);
494   }
495 
496   @Override
sampleMetadata( long timeUs, @C.BufferFlags int flags, int size, int offset, @Nullable CryptoData cryptoData)497   public void sampleMetadata(
498       long timeUs,
499       @C.BufferFlags int flags,
500       int size,
501       int offset,
502       @Nullable CryptoData cryptoData) {
503     if (upstreamFormatAdjustmentRequired) {
504       format(Assertions.checkStateNotNull(unadjustedUpstreamFormat));
505     }
506     timeUs += sampleOffsetUs;
507     if (pendingSplice) {
508       if ((flags & C.BUFFER_FLAG_KEY_FRAME) == 0 || !attemptSplice(timeUs)) {
509         return;
510       }
511       pendingSplice = false;
512     }
513     long absoluteOffset = sampleDataQueue.getTotalBytesWritten() - size - offset;
514     commitSample(timeUs, flags, absoluteOffset, size, cryptoData);
515   }
516 
517   /**
518    * Invalidates the last upstream format adjustment. {@link #getAdjustedUpstreamFormat(Format)}
519    * will be called to adjust the upstream {@link Format} again before the next sample is queued.
520    */
invalidateUpstreamFormatAdjustment()521   protected final void invalidateUpstreamFormatAdjustment() {
522     upstreamFormatAdjustmentRequired = true;
523   }
524 
525   /**
526    * Adjusts the upstream {@link Format} (i.e., the {@link Format} that was most recently passed to
527    * {@link #format(Format)}).
528    *
529    * <p>The default implementation incorporates the sample offset passed to {@link
530    * #setSampleOffsetUs(long)} into {@link Format#subsampleOffsetUs}.
531    *
532    * @param format The {@link Format} to adjust.
533    * @return The adjusted {@link Format}.
534    */
535   @CallSuper
getAdjustedUpstreamFormat(Format format)536   protected Format getAdjustedUpstreamFormat(Format format) {
537     if (sampleOffsetUs != 0 && format.subsampleOffsetUs != Format.OFFSET_SAMPLE_RELATIVE) {
538       format =
539           format
540               .buildUpon()
541               .setSubsampleOffsetUs(format.subsampleOffsetUs + sampleOffsetUs)
542               .build();
543     }
544     return format;
545   }
546 
547   // Internal methods.
548 
549   /** Rewinds the read position to the first sample in the queue. */
rewind()550   private synchronized void rewind() {
551     readPosition = 0;
552     sampleDataQueue.rewind();
553   }
554 
555   @SuppressWarnings("ReferenceEquality") // See comments in setUpstreamFormat
readSampleMetadata( FormatHolder formatHolder, DecoderInputBuffer buffer, boolean formatRequired, boolean loadingFinished, long decodeOnlyUntilUs, SampleExtrasHolder extrasHolder)556   private synchronized int readSampleMetadata(
557       FormatHolder formatHolder,
558       DecoderInputBuffer buffer,
559       boolean formatRequired,
560       boolean loadingFinished,
561       long decodeOnlyUntilUs,
562       SampleExtrasHolder extrasHolder) {
563     buffer.waitingForKeys = false;
564     // This is a temporary fix for https://github.com/google/ExoPlayer/issues/6155.
565     // TODO: Remove it and replace it with a fix that discards samples when writing to the queue.
566     boolean hasNextSample;
567     int relativeReadIndex = C.INDEX_UNSET;
568     while ((hasNextSample = hasNextSample())) {
569       relativeReadIndex = getRelativeIndex(readPosition);
570       long timeUs = timesUs[relativeReadIndex];
571       if (timeUs < decodeOnlyUntilUs
572           && MimeTypes.allSamplesAreSyncSamples(formats[relativeReadIndex].sampleMimeType)) {
573         readPosition++;
574       } else {
575         break;
576       }
577     }
578 
579     if (!hasNextSample) {
580       if (loadingFinished || isLastSampleQueued) {
581         buffer.setFlags(C.BUFFER_FLAG_END_OF_STREAM);
582         return C.RESULT_BUFFER_READ;
583       } else if (upstreamFormat != null && (formatRequired || upstreamFormat != downstreamFormat)) {
584         onFormatResult(Assertions.checkNotNull(upstreamFormat), formatHolder);
585         return C.RESULT_FORMAT_READ;
586       } else {
587         return C.RESULT_NOTHING_READ;
588       }
589     }
590 
591     if (formatRequired || formats[relativeReadIndex] != downstreamFormat) {
592       onFormatResult(formats[relativeReadIndex], formatHolder);
593       return C.RESULT_FORMAT_READ;
594     }
595 
596     if (!mayReadSample(relativeReadIndex)) {
597       buffer.waitingForKeys = true;
598       return C.RESULT_NOTHING_READ;
599     }
600 
601     buffer.setFlags(flags[relativeReadIndex]);
602     buffer.timeUs = timesUs[relativeReadIndex];
603     if (buffer.timeUs < decodeOnlyUntilUs) {
604       buffer.addFlag(C.BUFFER_FLAG_DECODE_ONLY);
605     }
606     if (buffer.isFlagsOnly()) {
607       return C.RESULT_BUFFER_READ;
608     }
609     extrasHolder.size = sizes[relativeReadIndex];
610     extrasHolder.offset = offsets[relativeReadIndex];
611     extrasHolder.cryptoData = cryptoDatas[relativeReadIndex];
612 
613     readPosition++;
614     return C.RESULT_BUFFER_READ;
615   }
616 
setUpstreamFormat(Format format)617   private synchronized boolean setUpstreamFormat(Format format) {
618     upstreamFormatRequired = false;
619     if (Util.areEqual(format, upstreamFormat)) {
620       // The format is unchanged. If format and upstreamFormat are different objects, we keep the
621       // current upstreamFormat so we can detect format changes on the read side using cheap
622       // referential quality.
623       return false;
624     } else if (Util.areEqual(format, upstreamCommittedFormat)) {
625       // The format has changed back to the format of the last committed sample. If they are
626       // different objects, we revert back to using upstreamCommittedFormat as the upstreamFormat
627       // so we can detect format changes on the read side using cheap referential equality.
628       upstreamFormat = upstreamCommittedFormat;
629       return true;
630     } else {
631       upstreamFormat = format;
632       return true;
633     }
634   }
635 
discardSampleMetadataTo( long timeUs, boolean toKeyframe, boolean stopAtReadPosition)636   private synchronized long discardSampleMetadataTo(
637       long timeUs, boolean toKeyframe, boolean stopAtReadPosition) {
638     if (length == 0 || timeUs < timesUs[relativeFirstIndex]) {
639       return C.POSITION_UNSET;
640     }
641     int searchLength = stopAtReadPosition && readPosition != length ? readPosition + 1 : length;
642     int discardCount = findSampleBefore(relativeFirstIndex, searchLength, timeUs, toKeyframe);
643     if (discardCount == -1) {
644       return C.POSITION_UNSET;
645     }
646     return discardSamples(discardCount);
647   }
648 
discardSampleMetadataToRead()649   public synchronized long discardSampleMetadataToRead() {
650     if (readPosition == 0) {
651       return C.POSITION_UNSET;
652     }
653     return discardSamples(readPosition);
654   }
655 
discardSampleMetadataToEnd()656   private synchronized long discardSampleMetadataToEnd() {
657     if (length == 0) {
658       return C.POSITION_UNSET;
659     }
660     return discardSamples(length);
661   }
662 
releaseDrmSessionReferences()663   private void releaseDrmSessionReferences() {
664     if (currentDrmSession != null) {
665       currentDrmSession.release(eventDispatcher);
666       currentDrmSession = null;
667       // Clear downstream format to avoid violating the assumption that downstreamFormat.drmInitData
668       // != null implies currentSession != null
669       downstreamFormat = null;
670     }
671   }
672 
commitSample( long timeUs, @C.BufferFlags int sampleFlags, long offset, int size, @Nullable CryptoData cryptoData)673   private synchronized void commitSample(
674       long timeUs,
675       @C.BufferFlags int sampleFlags,
676       long offset,
677       int size,
678       @Nullable CryptoData cryptoData) {
679     if (upstreamKeyframeRequired) {
680       if ((sampleFlags & C.BUFFER_FLAG_KEY_FRAME) == 0) {
681         return;
682       }
683       upstreamKeyframeRequired = false;
684     }
685     Assertions.checkState(!upstreamFormatRequired);
686 
687     isLastSampleQueued = (sampleFlags & C.BUFFER_FLAG_LAST_SAMPLE) != 0;
688     largestQueuedTimestampUs = Math.max(largestQueuedTimestampUs, timeUs);
689 
690     int relativeEndIndex = getRelativeIndex(length);
691     timesUs[relativeEndIndex] = timeUs;
692     offsets[relativeEndIndex] = offset;
693     sizes[relativeEndIndex] = size;
694     flags[relativeEndIndex] = sampleFlags;
695     cryptoDatas[relativeEndIndex] = cryptoData;
696     formats[relativeEndIndex] = upstreamFormat;
697     sourceIds[relativeEndIndex] = upstreamSourceId;
698     upstreamCommittedFormat = upstreamFormat;
699 
700     length++;
701     if (length == capacity) {
702       // Increase the capacity.
703       int newCapacity = capacity + SAMPLE_CAPACITY_INCREMENT;
704       int[] newSourceIds = new int[newCapacity];
705       long[] newOffsets = new long[newCapacity];
706       long[] newTimesUs = new long[newCapacity];
707       int[] newFlags = new int[newCapacity];
708       int[] newSizes = new int[newCapacity];
709       CryptoData[] newCryptoDatas = new CryptoData[newCapacity];
710       Format[] newFormats = new Format[newCapacity];
711       int beforeWrap = capacity - relativeFirstIndex;
712       System.arraycopy(offsets, relativeFirstIndex, newOffsets, 0, beforeWrap);
713       System.arraycopy(timesUs, relativeFirstIndex, newTimesUs, 0, beforeWrap);
714       System.arraycopy(flags, relativeFirstIndex, newFlags, 0, beforeWrap);
715       System.arraycopy(sizes, relativeFirstIndex, newSizes, 0, beforeWrap);
716       System.arraycopy(cryptoDatas, relativeFirstIndex, newCryptoDatas, 0, beforeWrap);
717       System.arraycopy(formats, relativeFirstIndex, newFormats, 0, beforeWrap);
718       System.arraycopy(sourceIds, relativeFirstIndex, newSourceIds, 0, beforeWrap);
719       int afterWrap = relativeFirstIndex;
720       System.arraycopy(offsets, 0, newOffsets, beforeWrap, afterWrap);
721       System.arraycopy(timesUs, 0, newTimesUs, beforeWrap, afterWrap);
722       System.arraycopy(flags, 0, newFlags, beforeWrap, afterWrap);
723       System.arraycopy(sizes, 0, newSizes, beforeWrap, afterWrap);
724       System.arraycopy(cryptoDatas, 0, newCryptoDatas, beforeWrap, afterWrap);
725       System.arraycopy(formats, 0, newFormats, beforeWrap, afterWrap);
726       System.arraycopy(sourceIds, 0, newSourceIds, beforeWrap, afterWrap);
727       offsets = newOffsets;
728       timesUs = newTimesUs;
729       flags = newFlags;
730       sizes = newSizes;
731       cryptoDatas = newCryptoDatas;
732       formats = newFormats;
733       sourceIds = newSourceIds;
734       relativeFirstIndex = 0;
735       capacity = newCapacity;
736     }
737   }
738 
739   /**
740    * Attempts to discard samples from the end of the queue to allow samples starting from the
741    * specified timestamp to be spliced in. Samples will not be discarded prior to the read position.
742    *
743    * @param timeUs The timestamp at which the splice occurs.
744    * @return Whether the splice was successful.
745    */
attemptSplice(long timeUs)746   private synchronized boolean attemptSplice(long timeUs) {
747     if (length == 0) {
748       return timeUs > largestDiscardedTimestampUs;
749     }
750     long largestReadTimestampUs =
751         Math.max(largestDiscardedTimestampUs, getLargestTimestamp(readPosition));
752     if (largestReadTimestampUs >= timeUs) {
753       return false;
754     }
755     int retainCount = length;
756     int relativeSampleIndex = getRelativeIndex(length - 1);
757     while (retainCount > readPosition && timesUs[relativeSampleIndex] >= timeUs) {
758       retainCount--;
759       relativeSampleIndex--;
760       if (relativeSampleIndex == -1) {
761         relativeSampleIndex = capacity - 1;
762       }
763     }
764     discardUpstreamSampleMetadata(absoluteFirstIndex + retainCount);
765     return true;
766   }
767 
discardUpstreamSampleMetadata(int discardFromIndex)768   private long discardUpstreamSampleMetadata(int discardFromIndex) {
769     int discardCount = getWriteIndex() - discardFromIndex;
770     Assertions.checkArgument(0 <= discardCount && discardCount <= (length - readPosition));
771     length -= discardCount;
772     largestQueuedTimestampUs = Math.max(largestDiscardedTimestampUs, getLargestTimestamp(length));
773     isLastSampleQueued = discardCount == 0 && isLastSampleQueued;
774     if (length != 0) {
775       int relativeLastWriteIndex = getRelativeIndex(length - 1);
776       return offsets[relativeLastWriteIndex] + sizes[relativeLastWriteIndex];
777     }
778     return 0;
779   }
780 
hasNextSample()781   private boolean hasNextSample() {
782     return readPosition != length;
783   }
784 
785   /**
786    * Sets the downstream format, performs DRM resource management, and populates the {@code
787    * outputFormatHolder}.
788    *
789    * @param newFormat The new downstream format.
790    * @param outputFormatHolder The output {@link FormatHolder}.
791    */
onFormatResult(Format newFormat, FormatHolder outputFormatHolder)792   private void onFormatResult(Format newFormat, FormatHolder outputFormatHolder) {
793     outputFormatHolder.format = newFormat;
794     boolean isFirstFormat = downstreamFormat == null;
795     @Nullable DrmInitData oldDrmInitData = isFirstFormat ? null : downstreamFormat.drmInitData;
796     downstreamFormat = newFormat;
797     @Nullable DrmInitData newDrmInitData = newFormat.drmInitData;
798     outputFormatHolder.drmSession = currentDrmSession;
799     if (!isFirstFormat && Util.areEqual(oldDrmInitData, newDrmInitData)) {
800       // Nothing to do.
801       return;
802     }
803     // Ensure we acquire the new session before releasing the previous one in case the same session
804     // is being used for both DrmInitData.
805     @Nullable DrmSession previousSession = currentDrmSession;
806     currentDrmSession =
807         newDrmInitData != null
808             ? drmSessionManager.acquireSession(playbackLooper, eventDispatcher, newDrmInitData)
809             : drmSessionManager.acquirePlaceholderSession(
810                 playbackLooper, MimeTypes.getTrackType(newFormat.sampleMimeType));
811     outputFormatHolder.drmSession = currentDrmSession;
812 
813     if (previousSession != null) {
814       previousSession.release(eventDispatcher);
815     }
816   }
817 
818   /**
819    * Returns whether it's possible to read the next sample.
820    *
821    * @param relativeReadIndex The relative read index of the next sample.
822    * @return Whether it's possible to read the next sample.
823    */
mayReadSample(int relativeReadIndex)824   private boolean mayReadSample(int relativeReadIndex) {
825     return currentDrmSession == null
826         || currentDrmSession.getState() == DrmSession.STATE_OPENED_WITH_KEYS
827         || ((flags[relativeReadIndex] & C.BUFFER_FLAG_ENCRYPTED) == 0
828             && currentDrmSession.playClearSamplesWithoutKeys());
829   }
830 
831   /**
832    * Finds the sample in the specified range that's before or at the specified time. If {@code
833    * keyframe} is {@code true} then the sample is additionally required to be a keyframe.
834    *
835    * @param relativeStartIndex The relative index from which to start searching.
836    * @param length The length of the range being searched.
837    * @param timeUs The specified time.
838    * @param keyframe Whether only keyframes should be considered.
839    * @return The offset from {@code relativeFirstIndex} to the found sample, or -1 if no matching
840    *     sample was found.
841    */
findSampleBefore(int relativeStartIndex, int length, long timeUs, boolean keyframe)842   private int findSampleBefore(int relativeStartIndex, int length, long timeUs, boolean keyframe) {
843     // This could be optimized to use a binary search, however in practice callers to this method
844     // normally pass times near to the start of the search region. Hence it's unclear whether
845     // switching to a binary search would yield any real benefit.
846     int sampleCountToTarget = -1;
847     int searchIndex = relativeStartIndex;
848     for (int i = 0; i < length && timesUs[searchIndex] <= timeUs; i++) {
849       if (!keyframe || (flags[searchIndex] & C.BUFFER_FLAG_KEY_FRAME) != 0) {
850         // We've found a suitable sample.
851         sampleCountToTarget = i;
852       }
853       searchIndex++;
854       if (searchIndex == capacity) {
855         searchIndex = 0;
856       }
857     }
858     return sampleCountToTarget;
859   }
860 
861   /**
862    * Discards the specified number of samples.
863    *
864    * @param discardCount The number of samples to discard.
865    * @return The corresponding offset up to which data should be discarded.
866    */
discardSamples(int discardCount)867   private long discardSamples(int discardCount) {
868     largestDiscardedTimestampUs =
869         Math.max(largestDiscardedTimestampUs, getLargestTimestamp(discardCount));
870     length -= discardCount;
871     absoluteFirstIndex += discardCount;
872     relativeFirstIndex += discardCount;
873     if (relativeFirstIndex >= capacity) {
874       relativeFirstIndex -= capacity;
875     }
876     readPosition -= discardCount;
877     if (readPosition < 0) {
878       readPosition = 0;
879     }
880     if (length == 0) {
881       int relativeLastDiscardIndex = (relativeFirstIndex == 0 ? capacity : relativeFirstIndex) - 1;
882       return offsets[relativeLastDiscardIndex] + sizes[relativeLastDiscardIndex];
883     } else {
884       return offsets[relativeFirstIndex];
885     }
886   }
887 
888   /**
889    * Finds the largest timestamp of any sample from the start of the queue up to the specified
890    * length, assuming that the timestamps prior to a keyframe are always less than the timestamp of
891    * the keyframe itself, and of subsequent frames.
892    *
893    * @param length The length of the range being searched.
894    * @return The largest timestamp, or {@link Long#MIN_VALUE} if {@code length == 0}.
895    */
getLargestTimestamp(int length)896   private long getLargestTimestamp(int length) {
897     if (length == 0) {
898       return Long.MIN_VALUE;
899     }
900     long largestTimestampUs = Long.MIN_VALUE;
901     int relativeSampleIndex = getRelativeIndex(length - 1);
902     for (int i = 0; i < length; i++) {
903       largestTimestampUs = Math.max(largestTimestampUs, timesUs[relativeSampleIndex]);
904       if ((flags[relativeSampleIndex] & C.BUFFER_FLAG_KEY_FRAME) != 0) {
905         break;
906       }
907       relativeSampleIndex--;
908       if (relativeSampleIndex == -1) {
909         relativeSampleIndex = capacity - 1;
910       }
911     }
912     return largestTimestampUs;
913   }
914 
915   /**
916    * Returns the relative index for a given offset from the start of the queue.
917    *
918    * @param offset The offset, which must be in the range [0, length].
919    */
getRelativeIndex(int offset)920   private int getRelativeIndex(int offset) {
921     int relativeIndex = relativeFirstIndex + offset;
922     return relativeIndex < capacity ? relativeIndex : relativeIndex - capacity;
923   }
924 
925   /** A holder for sample metadata not held by {@link DecoderInputBuffer}. */
926   /* package */ static final class SampleExtrasHolder {
927 
928     public int size;
929     public long offset;
930     @Nullable public CryptoData cryptoData;
931   }
932 }
933