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