mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Implement secondary renderer feature for pre-warming
`RenderersFactory#createSecondaryRenderer` can be implemented to provide secondary renderers for pre-warming. These renderers must match their primaries in terms of reported track type support and `RendererCapabilities`. If a secondary renderer is provided, ExoPlayer will enable it for a subsequent media item as soon as its `MediaPeriod` is prepared. This will cause the renderer to start decoding and processing content so that it is ready to play as soon as playback transitions to that media item. PiperOrigin-RevId: 704326302
This commit is contained in:
parent
fc3f096888
commit
987869e456
@ -24,6 +24,9 @@
|
||||
functionality to platform `MediaExtractor`.
|
||||
* Move `BasePreloadManager.Listener` to a top level
|
||||
`PreloadManagerListener`.
|
||||
* `RenderersFactory.createSecondaryRenderer` can be implemented to provide
|
||||
secondary renderers for pre-warming. Pre-warming enables quicker media
|
||||
item transitions during playback.
|
||||
* Transformer:
|
||||
* Update parameters of `VideoFrameProcessor.registerInputStream` and
|
||||
`VideoFrameProcessor.Listener.onInputStreamRegistered` to use `Format`.
|
||||
|
@ -1481,6 +1481,19 @@ public interface ExoPlayer extends Player {
|
||||
@UnstableApi
|
||||
Renderer getRenderer(int index);
|
||||
|
||||
/**
|
||||
* Returns the secondary renderer at the given index.
|
||||
*
|
||||
* @param index The index of the secondary renderer.
|
||||
* @return The secondary renderer at this index, or null if there is no secondary renderer at this
|
||||
* index.
|
||||
*/
|
||||
@UnstableApi
|
||||
@Nullable
|
||||
default Renderer getSecondaryRenderer(int index) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the track selector that this player uses, or null if track selection is not supported.
|
||||
*/
|
||||
|
@ -87,6 +87,7 @@ import androidx.media3.common.util.ConditionVariable;
|
||||
import androidx.media3.common.util.HandlerWrapper;
|
||||
import androidx.media3.common.util.ListenerSet;
|
||||
import androidx.media3.common.util.Log;
|
||||
import androidx.media3.common.util.NullableType;
|
||||
import androidx.media3.common.util.Size;
|
||||
import androidx.media3.common.util.Util;
|
||||
import androidx.media3.exoplayer.PlayerMessage.Target;
|
||||
@ -153,6 +154,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
||||
private final Context applicationContext;
|
||||
private final Player wrappingPlayer;
|
||||
private final Renderer[] renderers;
|
||||
private final @NullableType Renderer[] secondaryRenderers;
|
||||
private final TrackSelector trackSelector;
|
||||
private final HandlerWrapper playbackInfoUpdateHandler;
|
||||
private final ExoPlayerImplInternal.PlaybackInfoUpdateListener playbackInfoUpdateListener;
|
||||
@ -263,17 +265,27 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
||||
componentListener = new ComponentListener();
|
||||
frameMetadataListener = new FrameMetadataListener();
|
||||
Handler eventHandler = new Handler(builder.looper);
|
||||
RenderersFactory renderersFactory = builder.renderersFactorySupplier.get();
|
||||
renderers =
|
||||
builder
|
||||
.renderersFactorySupplier
|
||||
.get()
|
||||
.createRenderers(
|
||||
renderersFactory.createRenderers(
|
||||
eventHandler,
|
||||
componentListener,
|
||||
componentListener,
|
||||
componentListener,
|
||||
componentListener);
|
||||
checkState(renderers.length > 0);
|
||||
secondaryRenderers = new Renderer[renderers.length];
|
||||
for (int i = 0; i < secondaryRenderers.length; i++) {
|
||||
// TODO(b/377671489): Fix DefaultAnalyticsCollector logic to still work with pre-warming.
|
||||
secondaryRenderers[i] =
|
||||
renderersFactory.createSecondaryRenderer(
|
||||
renderers[i],
|
||||
eventHandler,
|
||||
componentListener,
|
||||
componentListener,
|
||||
componentListener,
|
||||
componentListener);
|
||||
}
|
||||
this.trackSelector = builder.trackSelectorSupplier.get();
|
||||
this.mediaSourceFactory = builder.mediaSourceFactorySupplier.get();
|
||||
this.bandwidthMeter = builder.bandwidthMeterSupplier.get();
|
||||
@ -357,6 +369,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
||||
internalPlayer =
|
||||
new ExoPlayerImplInternal(
|
||||
renderers,
|
||||
secondaryRenderers,
|
||||
trackSelector,
|
||||
emptyTrackSelectorResult,
|
||||
builder.loadControlSupplier.get(),
|
||||
@ -1225,6 +1238,13 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
||||
return renderers[index];
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Renderer getSecondaryRenderer(int index) {
|
||||
verifyApplicationThread();
|
||||
return secondaryRenderers[index];
|
||||
}
|
||||
|
||||
@Override
|
||||
public TrackSelector getTrackSelector() {
|
||||
verifyApplicationThread();
|
||||
|
@ -19,6 +19,9 @@ import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||
import static androidx.media3.common.util.Assertions.checkState;
|
||||
import static androidx.media3.common.util.Util.castNonNull;
|
||||
import static androidx.media3.common.util.Util.msToUs;
|
||||
import static androidx.media3.exoplayer.MediaPeriodQueue.REMOVE_AFTER_REMOVED_READING_PERIOD;
|
||||
import static androidx.media3.exoplayer.RendererHolder.REPLACE_STREAMS_DISABLE_RENDERERS_COMPLETED;
|
||||
import static androidx.media3.exoplayer.RendererHolder.REPLACE_STREAMS_DISABLE_RENDERERS_DISABLE_OFFLOAD_SCHEDULING;
|
||||
import static androidx.media3.exoplayer.audio.AudioSink.OFFLOAD_MODE_DISABLED;
|
||||
import static java.lang.Math.max;
|
||||
import static java.lang.Math.min;
|
||||
@ -35,6 +38,7 @@ import androidx.media3.common.Format;
|
||||
import androidx.media3.common.IllegalSeekPositionException;
|
||||
import androidx.media3.common.MediaItem;
|
||||
import androidx.media3.common.Metadata;
|
||||
import androidx.media3.common.MimeTypes;
|
||||
import androidx.media3.common.ParserException;
|
||||
import androidx.media3.common.PlaybackException;
|
||||
import androidx.media3.common.PlaybackException.ErrorCode;
|
||||
@ -200,6 +204,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
private final boolean dynamicSchedulingEnabled;
|
||||
private final AnalyticsCollector analyticsCollector;
|
||||
private final HandlerWrapper applicationLooperHandler;
|
||||
private final boolean hasSecondaryRenderers;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private SeekParameters seekParameters;
|
||||
@ -228,9 +233,11 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
private long playbackMaybeBecameStuckAtMs;
|
||||
private PreloadConfiguration preloadConfiguration;
|
||||
private Timeline lastPreloadPoolInvalidationTimeline;
|
||||
private long prewarmingMediaPeriodDiscontinuity = C.TIME_UNSET;
|
||||
|
||||
public ExoPlayerImplInternal(
|
||||
Renderer[] renderers,
|
||||
Renderer[] secondaryRenderers,
|
||||
TrackSelector trackSelector,
|
||||
TrackSelectorResult emptyTrackSelectorResult,
|
||||
LoadControl loadControl,
|
||||
@ -281,6 +288,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
RendererCapabilities.Listener rendererCapabilitiesListener =
|
||||
trackSelector.getRendererCapabilitiesListener();
|
||||
|
||||
boolean hasSecondaryRenderers = false;
|
||||
this.renderers = new RendererHolder[renderers.length];
|
||||
for (int i = 0; i < renderers.length; i++) {
|
||||
renderers[i].init(/* index= */ i, playerId, clock);
|
||||
@ -288,8 +296,14 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
if (rendererCapabilitiesListener != null) {
|
||||
rendererCapabilities[i].setListener(rendererCapabilitiesListener);
|
||||
}
|
||||
this.renderers[i] = new RendererHolder(renderers[i], /* index= */ i);
|
||||
if (secondaryRenderers[i] != null) {
|
||||
secondaryRenderers[i].init(/* index= */ i, playerId, clock);
|
||||
hasSecondaryRenderers = true;
|
||||
}
|
||||
this.renderers[i] = new RendererHolder(renderers[i], secondaryRenderers[i], /* index= */ i);
|
||||
}
|
||||
this.hasSecondaryRenderers = hasSecondaryRenderers;
|
||||
|
||||
mediaClock = new DefaultMediaClock(this, clock);
|
||||
pendingMessages = new ArrayList<>();
|
||||
window = new Timeline.Window();
|
||||
@ -1477,6 +1491,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
newPlayingPeriodHolder.setRendererOffset(
|
||||
MediaPeriodQueue.INITIAL_RENDERER_POSITION_OFFSET_US);
|
||||
enableRenderers();
|
||||
newPlayingPeriodHolder.allRenderersInCorrectState = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1512,7 +1527,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
: playingMediaPeriod.toRendererTime(periodPositionUs);
|
||||
mediaClock.resetPosition(rendererPositionUs);
|
||||
for (RendererHolder rendererHolder : renderers) {
|
||||
rendererHolder.resetPosition(rendererPositionUs);
|
||||
rendererHolder.resetPosition(playingMediaPeriod, rendererPositionUs);
|
||||
}
|
||||
notifyTrackSelectionDiscontinuity();
|
||||
}
|
||||
@ -1533,12 +1548,10 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
this.foregroundMode = foregroundMode;
|
||||
if (!foregroundMode) {
|
||||
for (RendererHolder rendererHolder : renderers) {
|
||||
if (rendererHolder.getEnabledRendererCount() == 0) {
|
||||
rendererHolder.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (processedFlag != null) {
|
||||
synchronized (this) {
|
||||
processedFlag.set(true);
|
||||
@ -1873,15 +1886,16 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
private void disableRenderers() {
|
||||
for (int i = 0; i < renderers.length; i++) {
|
||||
disableRenderer(i);
|
||||
disableRenderer(/* rendererIndex= */ i);
|
||||
}
|
||||
prewarmingMediaPeriodDiscontinuity = C.TIME_UNSET;
|
||||
}
|
||||
|
||||
private void disableRenderer(int rendererIndex) {
|
||||
int holderEnabledRendererCount = renderers[rendererIndex].getEnabledRendererCount();
|
||||
int enabledRendererCountBeforeDisabling = renderers[rendererIndex].getEnabledRendererCount();
|
||||
renderers[rendererIndex].disable(mediaClock);
|
||||
maybeTriggerOnRendererReadyChanged(rendererIndex, /* allowsPlayback= */ false);
|
||||
enabledRendererCount -= holderEnabledRendererCount;
|
||||
enabledRendererCount -= enabledRendererCountBeforeDisabling;
|
||||
}
|
||||
|
||||
private void reselectTracksInternalAndSeek() throws ExoPlaybackException {
|
||||
@ -1923,7 +1937,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
if (selectionsChangedForReadPeriod) {
|
||||
// Update streams and rebuffer for the new selection, recreating all streams if reading ahead.
|
||||
MediaPeriodHolder playingPeriodHolder = queue.getPlayingPeriod();
|
||||
boolean recreateStreams = queue.removeAfter(playingPeriodHolder);
|
||||
int removeAfterResult = queue.removeAfter(playingPeriodHolder);
|
||||
boolean recreateStreams = (removeAfterResult & REMOVE_AFTER_REMOVED_READING_PERIOD) != 0;
|
||||
|
||||
boolean[] streamResetFlags = new boolean[renderers.length];
|
||||
long periodPositionUs =
|
||||
@ -1954,7 +1969,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
if (!renderers[i].isReadingFromPeriod(playingPeriodHolder)) {
|
||||
disableRenderer(i);
|
||||
} else if (streamResetFlags[i]) {
|
||||
renderers[i].resetPosition(rendererPositionUs);
|
||||
renderers[i].resetPosition(playingPeriodHolder, rendererPositionUs);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2020,7 +2035,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
? livePlaybackSpeedControl.getTargetLiveOffsetUs()
|
||||
: C.TIME_UNSET;
|
||||
MediaPeriodHolder loadingHolder = queue.getLoadingPeriod();
|
||||
boolean isBufferedToEnd = (loadingHolder.isFullyBuffered() && loadingHolder.info.isFinal);
|
||||
boolean isBufferedToEnd = loadingHolder.isFullyBuffered() && loadingHolder.info.isFinal;
|
||||
// Ad loader implementations may only load ad media once playback has nearly reached the ad, but
|
||||
// it is possible for playback to be stuck buffering waiting for this. Therefore, we start
|
||||
// playback regardless of buffered duration if we are waiting for an ad media period to prepare.
|
||||
@ -2221,6 +2236,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
return;
|
||||
}
|
||||
boolean loadingPeriodChanged = maybeUpdateLoadingPeriod();
|
||||
maybeUpdatePrewarmingPeriod();
|
||||
maybeUpdateReadingPeriod();
|
||||
maybeUpdateReadingRenderers();
|
||||
maybeUpdatePlayingPeriod();
|
||||
@ -2258,6 +2274,54 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
return loadingPeriodChanged;
|
||||
}
|
||||
|
||||
private void maybeUpdatePrewarmingPeriod() throws ExoPlaybackException {
|
||||
// TODO: Add limit as to not enable waiting renderer too early
|
||||
if (pendingPauseAtEndOfPeriod || !hasSecondaryRenderers || areRenderersPrewarming()) {
|
||||
return;
|
||||
}
|
||||
@Nullable MediaPeriodHolder prewarmingPeriodHolder = queue.getPrewarmingPeriod();
|
||||
if (prewarmingPeriodHolder == null
|
||||
|| prewarmingPeriodHolder != queue.getReadingPeriod()
|
||||
|| prewarmingPeriodHolder.getNext() == null
|
||||
|| !prewarmingPeriodHolder.getNext().prepared) {
|
||||
return;
|
||||
}
|
||||
|
||||
queue.advancePrewarmingPeriod();
|
||||
maybePrewarmRenderers();
|
||||
}
|
||||
|
||||
private void maybePrewarmRenderers() throws ExoPlaybackException {
|
||||
@Nullable MediaPeriodHolder prewarmingPeriod = queue.getPrewarmingPeriod();
|
||||
if (prewarmingPeriod == null) {
|
||||
return;
|
||||
}
|
||||
TrackSelectorResult trackSelectorResult = prewarmingPeriod.getTrackSelectorResult();
|
||||
for (int i = 0; i < renderers.length; i++) {
|
||||
if (trackSelectorResult.isRendererEnabled(i)
|
||||
&& renderers[i].hasSecondary()
|
||||
&& !renderers[i].isPrewarming()) {
|
||||
renderers[i].startPrewarming();
|
||||
enableRenderer(
|
||||
prewarmingPeriod,
|
||||
/* rendererIndex= */ i,
|
||||
/* wasRendererEnabled= */ false,
|
||||
prewarmingPeriod.getStartPositionRendererTime());
|
||||
}
|
||||
}
|
||||
// Handle any media period discontinuities.
|
||||
if (areRenderersPrewarming()) {
|
||||
prewarmingMediaPeriodDiscontinuity = prewarmingPeriod.mediaPeriod.readDiscontinuity();
|
||||
if (!prewarmingPeriod.isFullyBuffered()) {
|
||||
// The discontinuity caused the period to not be fully buffered. Continue loading from
|
||||
// this period again and discard all other periods we already started loading.
|
||||
queue.removeAfter(prewarmingPeriod);
|
||||
handleLoadingMediaPeriodChanged(/* loadingTrackSelectionChanged= */ false);
|
||||
maybeContinueLoading();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void maybeUpdateReadingPeriod() throws ExoPlaybackException {
|
||||
@Nullable MediaPeriodHolder readingPeriodHolder = queue.getReadingPeriod();
|
||||
if (readingPeriodHolder == null) {
|
||||
@ -2268,20 +2332,19 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
// We don't have a successor to advance the reading period to or we want to let them end
|
||||
// intentionally to pause at the end of the period.
|
||||
if (readingPeriodHolder.info.isFinal || pendingPauseAtEndOfPeriod) {
|
||||
for (int i = 0; i < renderers.length; i++) {
|
||||
RendererHolder renderer = renderers[i];
|
||||
for (RendererHolder renderer : renderers) {
|
||||
if (!renderer.isReadingFromPeriod(readingPeriodHolder)) {
|
||||
continue;
|
||||
}
|
||||
// Defer setting the stream as final until the renderer has actually consumed the whole
|
||||
// stream in case of playlist changes that cause the stream to be no longer final.
|
||||
if (renderer.hasReadStreamToEnd()) {
|
||||
if (renderer.hasReadPeriodToEnd(readingPeriodHolder)) {
|
||||
long streamEndPositionUs =
|
||||
readingPeriodHolder.info.durationUs != C.TIME_UNSET
|
||||
&& readingPeriodHolder.info.durationUs != C.TIME_END_OF_SOURCE
|
||||
? readingPeriodHolder.getRendererOffset() + readingPeriodHolder.info.durationUs
|
||||
: C.TIME_UNSET;
|
||||
renderer.setCurrentStreamFinal(streamEndPositionUs);
|
||||
renderer.setCurrentStreamFinal(readingPeriodHolder, streamEndPositionUs);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2292,6 +2355,11 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
return;
|
||||
}
|
||||
|
||||
if (areRenderersPrewarming() && queue.getPrewarmingPeriod() == queue.getReadingPeriod()) {
|
||||
// Reading period has already advanced to pre-warming period.
|
||||
return;
|
||||
}
|
||||
|
||||
if (!readingPeriodHolder.getNext().prepared
|
||||
&& rendererPositionUs < readingPeriodHolder.getNext().getStartPositionRendererTime()) {
|
||||
// The successor is not prepared yet and playback hasn't reached the transition point.
|
||||
@ -2312,37 +2380,48 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
/* forceSetTargetOffsetOverride= */ false);
|
||||
|
||||
if (readingPeriodHolder.prepared
|
||||
&& readingPeriodHolder.mediaPeriod.readDiscontinuity() != C.TIME_UNSET) {
|
||||
// The new period starts with a discontinuity, so the renderers will play out all data, then
|
||||
&& ((hasSecondaryRenderers && prewarmingMediaPeriodDiscontinuity != C.TIME_UNSET)
|
||||
|| readingPeriodHolder.mediaPeriod.readDiscontinuity() != C.TIME_UNSET)) {
|
||||
prewarmingMediaPeriodDiscontinuity = C.TIME_UNSET;
|
||||
// The new period starts with a discontinuity, so unless a pre-warming renderer is handling
|
||||
// the discontinuity, the renderers will play out all data, then
|
||||
// be disabled and re-enabled when they start playing the next period.
|
||||
setAllRendererStreamsFinal(
|
||||
boolean arePrewarmingRenderersHandlingDiscontinuity = hasSecondaryRenderers;
|
||||
if (arePrewarmingRenderersHandlingDiscontinuity) {
|
||||
for (int i = 0; i < renderers.length; i++) {
|
||||
if (!newTrackSelectorResult.isRendererEnabled(i)) {
|
||||
continue;
|
||||
}
|
||||
// TODO: This check should ideally be replaced by a per-stream discontinuity check
|
||||
// done by the MediaPeriod itself.
|
||||
if (!MimeTypes.allSamplesAreSyncSamples(
|
||||
newTrackSelectorResult.selections[i].getSelectedFormat().sampleMimeType,
|
||||
newTrackSelectorResult.selections[i].getSelectedFormat().codecs)
|
||||
&& !renderers[i].isPrewarming()) {
|
||||
arePrewarmingRenderersHandlingDiscontinuity = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!arePrewarmingRenderersHandlingDiscontinuity) {
|
||||
setAllNonPrewarmingRendererStreamsFinal(
|
||||
/* streamEndPositionUs= */ readingPeriodHolder.getStartPositionRendererTime());
|
||||
if (!readingPeriodHolder.isFullyBuffered()) {
|
||||
// The discontinuity caused the period to not be fully buffered. Continue loading from this
|
||||
// period again and discard all other periods we already started loading.
|
||||
// The discontinuity caused the period to not be fully buffered. Continue loading from
|
||||
// this period again and discard all other periods we already started loading.
|
||||
queue.removeAfter(readingPeriodHolder);
|
||||
handleLoadingMediaPeriodChanged(/* loadingTrackSelectionChanged= */ false);
|
||||
maybeContinueLoading();
|
||||
}
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < renderers.length; i++) {
|
||||
boolean oldRendererEnabled = oldTrackSelectorResult.isRendererEnabled(i);
|
||||
boolean newRendererEnabled = newTrackSelectorResult.isRendererEnabled(i);
|
||||
if (oldRendererEnabled && !renderers[i].isCurrentStreamFinal()) {
|
||||
boolean isNoSampleRenderer = rendererCapabilities[i].getTrackType() == C.TRACK_TYPE_NONE;
|
||||
RendererConfiguration oldConfig = oldTrackSelectorResult.rendererConfigurations[i];
|
||||
RendererConfiguration newConfig = newTrackSelectorResult.rendererConfigurations[i];
|
||||
if (!newRendererEnabled || !newConfig.equals(oldConfig) || isNoSampleRenderer) {
|
||||
// The renderer will be disabled when transitioning to playing the next period, because
|
||||
// there's no new selection, or because a configuration change is required, or because
|
||||
// it's a no-sample renderer for which rendererOffsetUs should be updated only when
|
||||
// starting to play the next period. Mark the SampleStream as final to play out any
|
||||
// remaining data.
|
||||
renderers[i].setCurrentStreamFinal(
|
||||
/* streamEndPositionUs= */ readingPeriodHolder.getStartPositionRendererTime());
|
||||
}
|
||||
}
|
||||
|
||||
for (RendererHolder renderer : renderers) {
|
||||
renderer.maybeSetOldStreamToFinal(
|
||||
oldTrackSelectorResult,
|
||||
newTrackSelectorResult,
|
||||
readingPeriodHolder.getStartPositionRendererTime());
|
||||
}
|
||||
}
|
||||
|
||||
@ -2354,11 +2433,48 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
// Not reading ahead or all renderers updated.
|
||||
return;
|
||||
}
|
||||
if (replaceStreamsOrDisableRendererForTransition()) {
|
||||
enableRenderers();
|
||||
boolean allUpdated = updateRenderersForTransition();
|
||||
if (allUpdated) {
|
||||
queue.getReadingPeriod().allRenderersInCorrectState = true;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean updateRenderersForTransition() throws ExoPlaybackException {
|
||||
MediaPeriodHolder readingMediaPeriod = queue.getReadingPeriod();
|
||||
TrackSelectorResult newTrackSelectorResult = readingMediaPeriod.getTrackSelectorResult();
|
||||
boolean allUpdated = true;
|
||||
for (int i = 0; i < renderers.length; i++) {
|
||||
int enabledRendererCountPreTransition = renderers[i].getEnabledRendererCount();
|
||||
int result =
|
||||
renderers[i].replaceStreamsOrDisableRendererForTransition(
|
||||
readingMediaPeriod, newTrackSelectorResult, mediaClock);
|
||||
if ((result & REPLACE_STREAMS_DISABLE_RENDERERS_DISABLE_OFFLOAD_SCHEDULING) != 0
|
||||
&& offloadSchedulingEnabled) {
|
||||
// Prevent sleeping across offload track transition else position won't get updated.
|
||||
// TODO: (b/183635183) Optimize Offload End-Of-Stream: Sleep to just before end of track
|
||||
setOffloadSchedulingEnabled(false);
|
||||
}
|
||||
enabledRendererCount -=
|
||||
enabledRendererCountPreTransition - renderers[i].getEnabledRendererCount();
|
||||
|
||||
boolean completedUpdate = (result & REPLACE_STREAMS_DISABLE_RENDERERS_COMPLETED) != 0;
|
||||
allUpdated &= completedUpdate;
|
||||
}
|
||||
if (allUpdated) {
|
||||
for (int i = 0; i < renderers.length; i++) {
|
||||
if (newTrackSelectorResult.isRendererEnabled(i)
|
||||
&& !renderers[i].isReadingFromPeriod(readingMediaPeriod)) {
|
||||
enableRenderer(
|
||||
readingMediaPeriod,
|
||||
/* rendererIndex= */ i,
|
||||
/* wasRendererEnabled= */ false,
|
||||
readingMediaPeriod.getStartPositionRendererTime());
|
||||
}
|
||||
}
|
||||
}
|
||||
return allUpdated;
|
||||
}
|
||||
|
||||
private void maybeUpdatePreloadPeriods(boolean loadingPeriodChanged) {
|
||||
if (preloadConfiguration.targetPreloadDurationUs == C.TIME_UNSET) {
|
||||
// Do nothing if preloading disabled.
|
||||
@ -2397,46 +2513,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean replaceStreamsOrDisableRendererForTransition() throws ExoPlaybackException {
|
||||
MediaPeriodHolder readingPeriodHolder = queue.getReadingPeriod();
|
||||
TrackSelectorResult newTrackSelectorResult = readingPeriodHolder.getTrackSelectorResult();
|
||||
boolean needsToWaitForRendererToEnd = false;
|
||||
for (int i = 0; i < renderers.length; i++) {
|
||||
RendererHolder renderer = renderers[i];
|
||||
if (renderer.getEnabledRendererCount() == 0) {
|
||||
continue;
|
||||
}
|
||||
boolean rendererIsReadingOldStream = !renderer.isReadingFromPeriod(readingPeriodHolder);
|
||||
boolean rendererShouldBeEnabled = newTrackSelectorResult.isRendererEnabled(i);
|
||||
if (rendererShouldBeEnabled && !rendererIsReadingOldStream) {
|
||||
// All done.
|
||||
continue;
|
||||
}
|
||||
if (!renderer.isCurrentStreamFinal()) {
|
||||
// The renderer stream is not final, so we can replace the sample streams immediately.
|
||||
Format[] formats = getFormats(newTrackSelectorResult.selections[i]);
|
||||
renderer.replaceStream(
|
||||
formats,
|
||||
readingPeriodHolder.sampleStreams[i],
|
||||
readingPeriodHolder.getStartPositionRendererTime(),
|
||||
readingPeriodHolder.getRendererOffset(),
|
||||
readingPeriodHolder.info.id);
|
||||
if (offloadSchedulingEnabled) {
|
||||
// Prevent sleeping across offload track transition else position won't get updated.
|
||||
// TODO: (b/183635183) Optimize Offload End-Of-Stream: Sleep to just before end of track
|
||||
setOffloadSchedulingEnabled(false);
|
||||
}
|
||||
} else if (renderer.isEnded()) {
|
||||
// The renderer has finished playback, so we can disable it now.
|
||||
disableRenderer(i);
|
||||
} else {
|
||||
// We need to wait until rendering finished before disabling the renderer.
|
||||
needsToWaitForRendererToEnd = true;
|
||||
}
|
||||
}
|
||||
return !needsToWaitForRendererToEnd;
|
||||
}
|
||||
|
||||
private void maybeUpdatePlayingPeriod() throws ExoPlaybackException {
|
||||
boolean advancedPlayingPeriod = false;
|
||||
while (shouldAdvancePlayingPeriod()) {
|
||||
@ -2461,6 +2537,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
Player.DISCONTINUITY_REASON_AUTO_TRANSITION);
|
||||
resetPendingPauseAtEndOfPeriod();
|
||||
updatePlaybackPositions();
|
||||
if (areRenderersPrewarming() && newPlayingPeriodHolder == queue.getPrewarmingPeriod()) {
|
||||
maybeHandlePrewarmingTransition();
|
||||
}
|
||||
if (playbackInfo.playbackState == Player.STATE_READY) {
|
||||
startRenderers();
|
||||
}
|
||||
@ -2469,6 +2548,12 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
}
|
||||
}
|
||||
|
||||
private void maybeHandlePrewarmingTransition() {
|
||||
for (RendererHolder renderer : renderers) {
|
||||
renderer.maybeHandlePrewarmingTransition();
|
||||
}
|
||||
}
|
||||
|
||||
private void maybeUpdateOffloadScheduling() {
|
||||
// If playing period is audio-only with offload mode preference to enable, then offload
|
||||
// scheduling should be enabled.
|
||||
@ -2539,9 +2624,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
return true;
|
||||
}
|
||||
|
||||
private void setAllRendererStreamsFinal(long streamEndPositionUs) {
|
||||
private void setAllNonPrewarmingRendererStreamsFinal(long streamEndPositionUs) {
|
||||
for (RendererHolder renderer : renderers) {
|
||||
renderer.setCurrentStreamFinal(streamEndPositionUs);
|
||||
renderer.setAllNonPrewarmingRendererStreamsFinal(streamEndPositionUs);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2579,6 +2664,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
// This is the first prepared period, so update the position and the renderers.
|
||||
resetRendererPosition(loadingPeriodHolder.info.startPositionUs);
|
||||
enableRenderers();
|
||||
loadingPeriodHolder.allRenderersInCorrectState = true;
|
||||
playbackInfo =
|
||||
handlePositionDiscontinuity(
|
||||
playbackInfo.periodId,
|
||||
@ -2789,20 +2875,26 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
renderers[i].reset();
|
||||
}
|
||||
}
|
||||
// Enable the renderers.
|
||||
for (int i = 0; i < renderers.length; i++) {
|
||||
if (trackSelectorResult.isRendererEnabled(i)) {
|
||||
enableRenderer(i, rendererWasEnabledFlags[i], startPositionUs);
|
||||
if (trackSelectorResult.isRendererEnabled(i)
|
||||
&& !renderers[i].isReadingFromPeriod(readingMediaPeriod)) {
|
||||
enableRenderer(
|
||||
readingMediaPeriod,
|
||||
/* rendererIndex= */ i,
|
||||
rendererWasEnabledFlags[i],
|
||||
startPositionUs);
|
||||
}
|
||||
}
|
||||
readingMediaPeriod.allRenderersInCorrectState = true;
|
||||
}
|
||||
|
||||
private void enableRenderer(int rendererIndex, boolean wasRendererEnabled, long startPositionUs)
|
||||
private void enableRenderer(
|
||||
MediaPeriodHolder periodHolder,
|
||||
int rendererIndex,
|
||||
boolean wasRendererEnabled,
|
||||
long startPositionUs)
|
||||
throws ExoPlaybackException {
|
||||
MediaPeriodHolder periodHolder = queue.getReadingPeriod();
|
||||
RendererHolder renderer = renderers[rendererIndex];
|
||||
if (renderer.getEnabledRendererCount() > 0) {
|
||||
if (renderer.isRendererEnabled()) {
|
||||
return;
|
||||
}
|
||||
boolean arePlayingAndReadingTheSamePeriod = periodHolder == queue.getPlayingPeriod();
|
||||
@ -2810,16 +2902,16 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
RendererConfiguration rendererConfiguration =
|
||||
trackSelectorResult.rendererConfigurations[rendererIndex];
|
||||
ExoTrackSelection newSelection = trackSelectorResult.selections[rendererIndex];
|
||||
Format[] formats = getFormats(newSelection);
|
||||
// The renderer needs enabling with its new track selection.
|
||||
boolean playing = shouldPlayWhenReady() && playbackInfo.playbackState == Player.STATE_READY;
|
||||
// Consider as joining only if the renderer was previously disabled.
|
||||
// Consider as joining only if the renderer was previously disabled and being enabled on the
|
||||
// playing period.
|
||||
boolean joining = !wasRendererEnabled && playing;
|
||||
// Enable the renderer.
|
||||
enabledRendererCount++;
|
||||
renderer.enable(
|
||||
rendererConfiguration,
|
||||
formats,
|
||||
newSelection,
|
||||
periodHolder.sampleStreams[rendererIndex],
|
||||
rendererPositionUs,
|
||||
joining,
|
||||
@ -2842,7 +2934,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
handler.sendEmptyMessage(MSG_DO_SOME_WORK);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
/* mediaPeriod= */ periodHolder);
|
||||
// Start the renderer if playing and the Playing and Reading periods are the same.
|
||||
if (playing && arePlayingAndReadingTheSamePeriod) {
|
||||
renderer.start();
|
||||
@ -2934,7 +3027,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
throws IOException, ExoPlaybackException {
|
||||
RendererHolder renderer = renderers[rendererIndex];
|
||||
try {
|
||||
renderer.maybeThrowStreamError();
|
||||
renderer.maybeThrowStreamError(checkNotNull(queue.getPlayingPeriod()));
|
||||
} catch (IOException | RuntimeException e) {
|
||||
switch (renderer.getTrackType()) {
|
||||
case C.TRACK_TYPE_TEXT:
|
||||
@ -2970,6 +3063,18 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean areRenderersPrewarming() {
|
||||
if (!hasSecondaryRenderers) {
|
||||
return false;
|
||||
}
|
||||
for (RendererHolder renderer : renderers) {
|
||||
if (renderer.isPrewarming()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static PositionUpdateForPlaylistChange resolvePositionForPlaylistChange(
|
||||
Timeline timeline,
|
||||
PlaybackInfo playbackInfo,
|
||||
@ -3416,16 +3521,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
: newTimeline.getPeriod(newPeriodIndex, period).windowIndex;
|
||||
}
|
||||
|
||||
private static Format[] getFormats(ExoTrackSelection newSelection) {
|
||||
// Build an array of formats contained by the selection.
|
||||
int length = newSelection != null ? newSelection.length() : 0;
|
||||
Format[] formats = new Format[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
formats[i] = newSelection.getFormat(i);
|
||||
}
|
||||
return formats;
|
||||
}
|
||||
|
||||
private static final class SeekPosition {
|
||||
|
||||
public final Timeline timeline;
|
||||
|
@ -18,9 +18,11 @@ package androidx.media3.exoplayer;
|
||||
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||
import static androidx.media3.common.util.Assertions.checkStateNotNull;
|
||||
import static java.lang.Math.max;
|
||||
import static java.lang.annotation.ElementType.TYPE_USE;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.util.Pair;
|
||||
import androidx.annotation.IntDef;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.media3.common.AdPlaybackState;
|
||||
import androidx.media3.common.C;
|
||||
@ -33,6 +35,10 @@ import androidx.media3.exoplayer.analytics.AnalyticsCollector;
|
||||
import androidx.media3.exoplayer.source.MediaPeriod;
|
||||
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@ -82,6 +88,7 @@ import java.util.List;
|
||||
private PreloadConfiguration preloadConfiguration;
|
||||
@Nullable private MediaPeriodHolder playing;
|
||||
@Nullable private MediaPeriodHolder reading;
|
||||
@Nullable private MediaPeriodHolder prewarming;
|
||||
@Nullable private MediaPeriodHolder loading;
|
||||
@Nullable private MediaPeriodHolder preloading;
|
||||
private int length;
|
||||
@ -218,6 +225,7 @@ import java.util.List;
|
||||
} else {
|
||||
playing = newPeriodHolder;
|
||||
reading = newPeriodHolder;
|
||||
prewarming = newPeriodHolder;
|
||||
}
|
||||
oldFrontPeriodUid = null;
|
||||
loading = newPeriodHolder;
|
||||
@ -362,17 +370,37 @@ import java.util.List;
|
||||
return reading;
|
||||
}
|
||||
|
||||
/** Returns the prewarming period holder, or null if the queue is empty. */
|
||||
@Nullable
|
||||
public MediaPeriodHolder getPrewarmingPeriod() {
|
||||
return prewarming;
|
||||
}
|
||||
|
||||
/**
|
||||
* Continues reading from the next period holder in the queue.
|
||||
*
|
||||
* @return The updated reading period holder.
|
||||
*/
|
||||
public MediaPeriodHolder advanceReadingPeriod() {
|
||||
if (prewarming == reading) {
|
||||
prewarming = checkStateNotNull(reading).getNext();
|
||||
}
|
||||
reading = checkStateNotNull(reading).getNext();
|
||||
notifyQueueUpdate();
|
||||
return checkStateNotNull(reading);
|
||||
}
|
||||
|
||||
/**
|
||||
* Continues pre-warming from the next period holder in the queue.
|
||||
*
|
||||
* @return The updated pre-warming period holder.
|
||||
*/
|
||||
public MediaPeriodHolder advancePrewarmingPeriod() {
|
||||
prewarming = checkStateNotNull(prewarming).getNext();
|
||||
notifyQueueUpdate();
|
||||
return checkStateNotNull(prewarming);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dequeues the playing period holder from the front of the queue and advances the playing period
|
||||
* holder to be the next item in the queue.
|
||||
@ -387,6 +415,9 @@ import java.util.List;
|
||||
if (playing == reading) {
|
||||
reading = playing.getNext();
|
||||
}
|
||||
if (playing == prewarming) {
|
||||
prewarming = playing.getNext();
|
||||
}
|
||||
playing.release();
|
||||
length--;
|
||||
if (length == 0) {
|
||||
@ -405,27 +436,33 @@ import java.util.List;
|
||||
* the same as the playing period holder at the front of the queue.
|
||||
*
|
||||
* @param mediaPeriodHolder The media period holder that shall be the new end of the queue.
|
||||
* @return Whether the reading period has been removed.
|
||||
* @return {@link RemoveAfterResult} with flags denoting if the reading or pre-warming periods
|
||||
* were removed.
|
||||
*/
|
||||
public boolean removeAfter(MediaPeriodHolder mediaPeriodHolder) {
|
||||
public int removeAfter(MediaPeriodHolder mediaPeriodHolder) {
|
||||
checkStateNotNull(mediaPeriodHolder);
|
||||
if (mediaPeriodHolder.equals(loading)) {
|
||||
return false;
|
||||
return 0;
|
||||
}
|
||||
boolean removedReading = false;
|
||||
int removedResult = 0;
|
||||
loading = mediaPeriodHolder;
|
||||
while (mediaPeriodHolder.getNext() != null) {
|
||||
mediaPeriodHolder = checkNotNull(mediaPeriodHolder.getNext());
|
||||
if (mediaPeriodHolder == reading) {
|
||||
reading = playing;
|
||||
removedReading = true;
|
||||
prewarming = playing;
|
||||
removedResult |= REMOVE_AFTER_REMOVED_READING_PERIOD;
|
||||
}
|
||||
if (mediaPeriodHolder == prewarming) {
|
||||
prewarming = reading;
|
||||
removedResult |= REMOVE_AFTER_REMOVED_PREWARMING_PERIOD;
|
||||
}
|
||||
mediaPeriodHolder.release();
|
||||
length--;
|
||||
}
|
||||
checkNotNull(loading).setNext(null);
|
||||
notifyQueueUpdate();
|
||||
return removedReading;
|
||||
return removedResult;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -472,6 +509,7 @@ import java.util.List;
|
||||
playing = null;
|
||||
loading = null;
|
||||
reading = null;
|
||||
prewarming = null;
|
||||
length = 0;
|
||||
notifyQueueUpdate();
|
||||
}
|
||||
@ -511,11 +549,13 @@ import java.util.List;
|
||||
getFollowingMediaPeriodInfo(timeline, previousPeriodHolder, rendererPositionUs);
|
||||
if (newPeriodInfo == null) {
|
||||
// We've loaded a next media period that is not in the new timeline.
|
||||
return !removeAfter(previousPeriodHolder);
|
||||
int removeAfterResult = removeAfter(previousPeriodHolder);
|
||||
return (removeAfterResult & REMOVE_AFTER_REMOVED_READING_PERIOD) == 0;
|
||||
}
|
||||
if (!canKeepMediaPeriodHolder(oldPeriodInfo, newPeriodInfo)) {
|
||||
// The new media period has a different id or start position.
|
||||
return !removeAfter(previousPeriodHolder);
|
||||
int removeAfterResult = removeAfter(previousPeriodHolder);
|
||||
return (removeAfterResult & REMOVE_AFTER_REMOVED_READING_PERIOD) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -538,7 +578,9 @@ import java.util.List;
|
||||
&& !periodHolder.info.isFollowedByTransitionToSameStream
|
||||
&& (maxRendererReadPositionUs == C.TIME_END_OF_SOURCE
|
||||
|| maxRendererReadPositionUs >= newDurationInRendererTime);
|
||||
boolean readingPeriodRemoved = removeAfter(periodHolder);
|
||||
int removeAfterResult = removeAfter(periodHolder);
|
||||
boolean readingPeriodRemoved =
|
||||
(removeAfterResult & REMOVE_AFTER_REMOVED_READING_PERIOD) != 0;
|
||||
return !readingPeriodRemoved && !isReadingAndReadBeyondNewDuration;
|
||||
}
|
||||
|
||||
@ -834,7 +876,8 @@ import java.util.List;
|
||||
}
|
||||
|
||||
// Release any period holders that don't match the new period order.
|
||||
boolean readingPeriodRemoved = removeAfter(lastValidPeriodHolder);
|
||||
int removeAfterResult = removeAfter(lastValidPeriodHolder);
|
||||
boolean readingPeriodRemoved = (removeAfterResult & REMOVE_AFTER_REMOVED_READING_PERIOD) != 0;
|
||||
|
||||
// Update the period info for the last holder, as it may now be the last period in the timeline.
|
||||
lastValidPeriodHolder.info = getUpdatedMediaPeriodInfo(timeline, lastValidPeriodHolder.info);
|
||||
@ -1211,4 +1254,22 @@ import java.util.List;
|
||||
}
|
||||
return startPositionUs + period.getContentResumeOffsetUs(adGroupIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Result for {@link #removeAfter} that signifies whether the reading or pre-warming periods were
|
||||
* removed during the process.
|
||||
*/
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@Target(TYPE_USE)
|
||||
@IntDef(
|
||||
flag = true,
|
||||
value = {REMOVE_AFTER_REMOVED_READING_PERIOD, REMOVE_AFTER_REMOVED_PREWARMING_PERIOD})
|
||||
/* package */ @interface RemoveAfterResult {}
|
||||
|
||||
/** The call to {@link #removeAfter} removed the reading period. */
|
||||
/* package */ static final int REMOVE_AFTER_REMOVED_READING_PERIOD = 1;
|
||||
|
||||
/** The call to {@link #removeAfter} removed the pre-warming period. */
|
||||
/* package */ static final int REMOVE_AFTER_REMOVED_PREWARMING_PERIOD = 1 << 1;
|
||||
}
|
||||
|
@ -16,37 +16,84 @@
|
||||
package androidx.media3.exoplayer;
|
||||
|
||||
import static androidx.media3.common.C.TRACK_TYPE_VIDEO;
|
||||
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||
import static androidx.media3.common.util.Assertions.checkState;
|
||||
import static androidx.media3.exoplayer.Renderer.STATE_DISABLED;
|
||||
import static androidx.media3.exoplayer.Renderer.STATE_ENABLED;
|
||||
import static androidx.media3.exoplayer.Renderer.STATE_STARTED;
|
||||
import static java.lang.Math.min;
|
||||
import static java.lang.annotation.ElementType.TYPE_USE;
|
||||
|
||||
import androidx.annotation.IntDef;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.Format;
|
||||
import androidx.media3.common.Timeline;
|
||||
import androidx.media3.common.util.Assertions;
|
||||
import androidx.media3.exoplayer.metadata.MetadataRenderer;
|
||||
import androidx.media3.exoplayer.source.MediaPeriod;
|
||||
import androidx.media3.exoplayer.source.MediaSource;
|
||||
import androidx.media3.exoplayer.source.SampleStream;
|
||||
import androidx.media3.exoplayer.text.TextRenderer;
|
||||
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
|
||||
import androidx.media3.exoplayer.trackselection.TrackSelectorResult;
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.Objects;
|
||||
|
||||
/** Holds a {@link Renderer renderer}. */
|
||||
/* package */ class RendererHolder {
|
||||
private final Renderer renderer;
|
||||
private final Renderer primaryRenderer;
|
||||
// Index of renderer in renderer list held by the {@link Player}.
|
||||
private final int index;
|
||||
private boolean requiresReset;
|
||||
@Nullable private final Renderer secondaryRenderer;
|
||||
private @RendererPrewarmingState int prewarmingState;
|
||||
private boolean primaryRequiresReset;
|
||||
private boolean secondaryRequiresReset;
|
||||
|
||||
public RendererHolder(Renderer renderer, int index) {
|
||||
this.renderer = renderer;
|
||||
public RendererHolder(Renderer renderer, @Nullable Renderer secondaryRenderer, int index) {
|
||||
this.primaryRenderer = renderer;
|
||||
this.index = index;
|
||||
requiresReset = false;
|
||||
this.secondaryRenderer = secondaryRenderer;
|
||||
prewarmingState = RENDERER_PREWARMING_STATE_NOT_PREWARMING_USING_PRIMARY;
|
||||
primaryRequiresReset = false;
|
||||
secondaryRequiresReset = false;
|
||||
}
|
||||
|
||||
public boolean hasSecondary() {
|
||||
return secondaryRenderer != null;
|
||||
}
|
||||
|
||||
public void startPrewarming() {
|
||||
checkState(!isPrewarming());
|
||||
prewarmingState =
|
||||
isRendererEnabled(primaryRenderer)
|
||||
? RENDERER_PREWARMING_STATE_TRANSITIONING_TO_SECONDARY
|
||||
: secondaryRenderer != null && isRendererEnabled(secondaryRenderer)
|
||||
? RENDERER_PREWARMING_STATE_TRANSITIONING_TO_PRIMARY
|
||||
: RENDERER_PREWARMING_STATE_PREWARMING_PRIMARY;
|
||||
}
|
||||
|
||||
public boolean isPrewarming() {
|
||||
return isPrimaryRendererPrewarming() || isSecondaryRendererPrewarming();
|
||||
}
|
||||
|
||||
private boolean isPrimaryRendererPrewarming() {
|
||||
return prewarmingState == RENDERER_PREWARMING_STATE_PREWARMING_PRIMARY
|
||||
|| prewarmingState == RENDERER_PREWARMING_STATE_TRANSITIONING_TO_PRIMARY;
|
||||
}
|
||||
|
||||
private boolean isSecondaryRendererPrewarming() {
|
||||
return prewarmingState == RENDERER_PREWARMING_STATE_TRANSITIONING_TO_SECONDARY;
|
||||
}
|
||||
|
||||
public int getEnabledRendererCount() {
|
||||
return isRendererEnabled(renderer) ? 1 : 0;
|
||||
int result = 0;
|
||||
result += isRendererEnabled(primaryRenderer) ? 1 : 0;
|
||||
result += secondaryRenderer != null && isRendererEnabled(secondaryRenderer) ? 1 : 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -55,7 +102,7 @@ import java.io.IOException;
|
||||
* @see Renderer#getTrackType()
|
||||
*/
|
||||
public @C.TrackType int getTrackType() {
|
||||
return renderer.getTrackType();
|
||||
return primaryRenderer.getTrackType();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -70,8 +117,7 @@ import java.io.IOException;
|
||||
* {@link MediaPeriodHolder media period}.
|
||||
*/
|
||||
public long getReadingPositionUs(@Nullable MediaPeriodHolder period) {
|
||||
Assertions.checkState(isReadingFromPeriod(period));
|
||||
return renderer.getReadingPositionUs();
|
||||
return Objects.requireNonNull(getRendererReadingFromPeriod(period)).getReadingPositionUs();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -79,7 +125,8 @@ import java.io.IOException;
|
||||
*
|
||||
* @see Renderer#hasReadStreamToEnd()
|
||||
*/
|
||||
public boolean hasReadStreamToEnd() {
|
||||
public boolean hasReadPeriodToEnd(MediaPeriodHolder mediaPeriodHolder) {
|
||||
Renderer renderer = checkNotNull(getRendererReadingFromPeriod(mediaPeriodHolder));
|
||||
return renderer.hasReadStreamToEnd();
|
||||
}
|
||||
|
||||
@ -88,47 +135,84 @@ import java.io.IOException;
|
||||
* before it is next disabled or reset.
|
||||
*
|
||||
* @see Renderer#setCurrentStreamFinal()
|
||||
* @param mediaPeriodHolder The {@link MediaPeriodHolder media period} containing the current
|
||||
* stream.
|
||||
* @param streamEndPositionUs The position to stop rendering at or {@link C#LENGTH_UNSET} to
|
||||
* render until the end of the current stream.
|
||||
*/
|
||||
public void setCurrentStreamFinal(long streamEndPositionUs) {
|
||||
if (renderer.getStream() != null) {
|
||||
setCurrentStreamFinal(renderer, streamEndPositionUs);
|
||||
public void setCurrentStreamFinal(MediaPeriodHolder mediaPeriodHolder, long streamEndPositionUs) {
|
||||
Renderer renderer = checkNotNull(getRendererReadingFromPeriod(mediaPeriodHolder));
|
||||
setCurrentStreamFinalInternal(renderer, streamEndPositionUs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Maybe signal to the renderer that the old {@link SampleStream} will be the final one supplied
|
||||
* before it is next disabled or reset.
|
||||
*
|
||||
* @param oldTrackSelectorResult {@link TrackSelectorResult} containing the previous {@link
|
||||
* SampleStream}.
|
||||
* @param newTrackSelectorResult {@link TrackSelectorResult} containing the next {@link
|
||||
* SampleStream}.
|
||||
* @param streamEndPositionUs The position to stop rendering at or {@link C#LENGTH_UNSET} to
|
||||
* render until the end of the current stream.
|
||||
*/
|
||||
public void maybeSetOldStreamToFinal(
|
||||
TrackSelectorResult oldTrackSelectorResult,
|
||||
TrackSelectorResult newTrackSelectorResult,
|
||||
long streamEndPositionUs) {
|
||||
boolean oldRendererEnabled = oldTrackSelectorResult.isRendererEnabled(index);
|
||||
boolean newRendererEnabled = newTrackSelectorResult.isRendererEnabled(index);
|
||||
boolean isPrimaryOldRenderer =
|
||||
secondaryRenderer == null
|
||||
|| prewarmingState == RENDERER_PREWARMING_STATE_TRANSITIONING_TO_SECONDARY
|
||||
|| (prewarmingState == RENDERER_PREWARMING_STATE_NOT_PREWARMING_USING_PRIMARY
|
||||
&& isRendererEnabled(primaryRenderer));
|
||||
Renderer oldRenderer = isPrimaryOldRenderer ? primaryRenderer : checkNotNull(secondaryRenderer);
|
||||
if (oldRendererEnabled && !oldRenderer.isCurrentStreamFinal()) {
|
||||
boolean isNoSampleRenderer = getTrackType() == C.TRACK_TYPE_NONE;
|
||||
RendererConfiguration oldConfig = oldTrackSelectorResult.rendererConfigurations[index];
|
||||
RendererConfiguration newConfig = newTrackSelectorResult.rendererConfigurations[index];
|
||||
if (!newRendererEnabled
|
||||
|| !Objects.equals(newConfig, oldConfig)
|
||||
|| isNoSampleRenderer
|
||||
|| isPrewarming()) {
|
||||
// The renderer will be disabled when transitioning to playing the next period, because
|
||||
// there's no new selection, or because a configuration change is required, or because
|
||||
// it's a no-sample renderer for which rendererOffsetUs should be updated only when
|
||||
// starting to play the next period, or there is a backup renderer that has already been
|
||||
// enabled for the following media item. Mark the SampleStream as final to play out any
|
||||
// remaining data.
|
||||
setCurrentStreamFinalInternal(oldRenderer, streamEndPositionUs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setCurrentStreamFinal(Renderer renderer, long streamEndPositionUs) {
|
||||
/**
|
||||
* Calls {@link Renderer#setCurrentStreamFinal} on enabled {@link Renderer renderers} that are not
|
||||
* pre-warming.
|
||||
*
|
||||
* @see Renderer#setCurrentStreamFinal
|
||||
*/
|
||||
public void setAllNonPrewarmingRendererStreamsFinal(long streamEndPositionUs) {
|
||||
if (isRendererEnabled(primaryRenderer)
|
||||
&& prewarmingState != RENDERER_PREWARMING_STATE_TRANSITIONING_TO_PRIMARY
|
||||
&& prewarmingState != RENDERER_PREWARMING_STATE_PREWARMING_PRIMARY) {
|
||||
setCurrentStreamFinalInternal(primaryRenderer, streamEndPositionUs);
|
||||
}
|
||||
if (secondaryRenderer != null
|
||||
&& isRendererEnabled(secondaryRenderer)
|
||||
&& prewarmingState != RENDERER_PREWARMING_STATE_TRANSITIONING_TO_SECONDARY) {
|
||||
setCurrentStreamFinalInternal(secondaryRenderer, streamEndPositionUs);
|
||||
}
|
||||
}
|
||||
|
||||
private void setCurrentStreamFinalInternal(Renderer renderer, long streamEndPositionUs) {
|
||||
renderer.setCurrentStreamFinal();
|
||||
if (renderer instanceof TextRenderer) {
|
||||
((TextRenderer) renderer).setFinalStreamEndPositionUs(streamEndPositionUs);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the current {@link SampleStream} will be the final one supplied before the
|
||||
* renderer is next disabled or reset.
|
||||
*
|
||||
* @see Renderer#isCurrentStreamFinal()
|
||||
*/
|
||||
public boolean isCurrentStreamFinal() {
|
||||
return renderer.isCurrentStreamFinal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes {@link Renderer#replaceStream}.
|
||||
*
|
||||
* @see Renderer#replaceStream
|
||||
*/
|
||||
public void replaceStream(
|
||||
Format[] formats,
|
||||
SampleStream stream,
|
||||
long startPositionUs,
|
||||
long offsetUs,
|
||||
MediaSource.MediaPeriodId mediaPeriodId)
|
||||
throws ExoPlaybackException {
|
||||
renderer.replaceStream(formats, stream, startPositionUs, offsetUs, mediaPeriodId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns minimum amount of playback clock time that must pass in order for the {@link #render}
|
||||
* call to make progress.
|
||||
@ -145,9 +229,19 @@ import java.io.IOException;
|
||||
*/
|
||||
public long getMinDurationToProgressUs(
|
||||
long rendererPositionUs, long rendererPositionElapsedRealtimeUs) {
|
||||
return isRendererEnabled(renderer)
|
||||
? renderer.getDurationToProgressUs(rendererPositionUs, rendererPositionElapsedRealtimeUs)
|
||||
long minDurationToProgress =
|
||||
isRendererEnabled(primaryRenderer)
|
||||
? primaryRenderer.getDurationToProgressUs(
|
||||
rendererPositionUs, rendererPositionElapsedRealtimeUs)
|
||||
: Long.MAX_VALUE;
|
||||
if (secondaryRenderer != null && isRendererEnabled(secondaryRenderer)) {
|
||||
minDurationToProgress =
|
||||
min(
|
||||
minDurationToProgress,
|
||||
secondaryRenderer.getDurationToProgressUs(
|
||||
rendererPositionUs, rendererPositionElapsedRealtimeUs));
|
||||
}
|
||||
return minDurationToProgress;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -156,8 +250,10 @@ import java.io.IOException;
|
||||
* @see Renderer#enableMayRenderStartOfStream
|
||||
*/
|
||||
public void enableMayRenderStartOfStream() {
|
||||
if (isRendererEnabled(renderer)) {
|
||||
renderer.enableMayRenderStartOfStream();
|
||||
if (isRendererEnabled(primaryRenderer)) {
|
||||
primaryRenderer.enableMayRenderStartOfStream();
|
||||
} else if (secondaryRenderer != null && isRendererEnabled(secondaryRenderer)) {
|
||||
secondaryRenderer.enableMayRenderStartOfStream();
|
||||
}
|
||||
}
|
||||
|
||||
@ -168,7 +264,10 @@ import java.io.IOException;
|
||||
*/
|
||||
public void setPlaybackSpeed(float currentPlaybackSpeed, float targetPlaybackSpeed)
|
||||
throws ExoPlaybackException {
|
||||
renderer.setPlaybackSpeed(currentPlaybackSpeed, targetPlaybackSpeed);
|
||||
primaryRenderer.setPlaybackSpeed(currentPlaybackSpeed, targetPlaybackSpeed);
|
||||
if (secondaryRenderer != null) {
|
||||
secondaryRenderer.setPlaybackSpeed(currentPlaybackSpeed, targetPlaybackSpeed);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -177,7 +276,10 @@ import java.io.IOException;
|
||||
* @see Renderer#setTimeline
|
||||
*/
|
||||
public void setTimeline(Timeline timeline) {
|
||||
renderer.setTimeline(timeline);
|
||||
primaryRenderer.setTimeline(timeline);
|
||||
if (secondaryRenderer != null) {
|
||||
secondaryRenderer.setTimeline(timeline);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -187,7 +289,14 @@ import java.io.IOException;
|
||||
* @return if all renderers have {@link Renderer#isEnded() ended}.
|
||||
*/
|
||||
public boolean isEnded() {
|
||||
return renderer.isEnded();
|
||||
boolean renderersEnded = true;
|
||||
if (isRendererEnabled(primaryRenderer)) {
|
||||
renderersEnded &= primaryRenderer.isEnded();
|
||||
}
|
||||
if (secondaryRenderer != null && isRendererEnabled(secondaryRenderer)) {
|
||||
renderersEnded &= secondaryRenderer.isEnded();
|
||||
}
|
||||
return renderersEnded;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -200,28 +309,6 @@ import java.io.IOException;
|
||||
return getRendererReadingFromPeriod(period) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Renderer} that is enabled on the provided media {@link MediaPeriodHolder
|
||||
* period}.
|
||||
*
|
||||
* <p>Returns null if the renderer is not enabled on the requested period.
|
||||
*
|
||||
* @param period The {@link MediaPeriodHolder period} with which to retrieve the linked {@link
|
||||
* Renderer}
|
||||
* @return {@link Renderer} enabled on the {@link MediaPeriodHolder period} or {@code null} if the
|
||||
* renderer is not enabled on the provided period.
|
||||
*/
|
||||
@Nullable
|
||||
private Renderer getRendererReadingFromPeriod(@Nullable MediaPeriodHolder period) {
|
||||
if (period == null || period.sampleStreams[index] == null) {
|
||||
return null;
|
||||
}
|
||||
if (renderer.getStream() == period.sampleStreams[index]) {
|
||||
return renderer;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the {@link Renderer renderers} are still reading a {@link MediaPeriodHolder
|
||||
* media period}.
|
||||
@ -230,17 +317,27 @@ import java.io.IOException;
|
||||
* @return true if {@link Renderer renderers} are reading the current reading period.
|
||||
*/
|
||||
public boolean hasFinishedReadingFromPeriod(MediaPeriodHolder periodHolder) {
|
||||
return hasFinishedReadingFromPeriodInternal(periodHolder);
|
||||
return hasFinishedReadingFromPeriodInternal(periodHolder, primaryRenderer)
|
||||
&& hasFinishedReadingFromPeriodInternal(periodHolder, secondaryRenderer);
|
||||
}
|
||||
|
||||
private boolean hasFinishedReadingFromPeriodInternal(MediaPeriodHolder readingPeriodHolder) {
|
||||
private boolean hasFinishedReadingFromPeriodInternal(
|
||||
MediaPeriodHolder readingPeriodHolder, @Nullable Renderer renderer) {
|
||||
if (renderer == null) {
|
||||
return true;
|
||||
}
|
||||
SampleStream sampleStream = readingPeriodHolder.sampleStreams[index];
|
||||
if (renderer.getStream() != sampleStream
|
||||
if (renderer.getStream() != null
|
||||
&& (renderer.getStream() != sampleStream
|
||||
|| (sampleStream != null
|
||||
&& !renderer.hasReadStreamToEnd()
|
||||
&& !hasReachedServerSideInsertedAdsTransition(renderer, readingPeriodHolder))) {
|
||||
&& !hasReachedServerSideInsertedAdsTransition(renderer, readingPeriodHolder)))) {
|
||||
// The current reading period is still being read by at least one renderer.
|
||||
return false;
|
||||
MediaPeriodHolder followingPeriod = readingPeriodHolder.getNext();
|
||||
// If renderer is reading ahead as it was enabled early, then it is not 'reading' the
|
||||
// current reading period.
|
||||
return followingPeriod != null
|
||||
&& followingPeriod.sampleStreams[index] == renderer.getStream();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -272,8 +369,11 @@ import java.io.IOException;
|
||||
*/
|
||||
public void render(long rendererPositionUs, long rendererPositionElapsedRealtimeUs)
|
||||
throws ExoPlaybackException {
|
||||
if (isRendererEnabled(renderer)) {
|
||||
renderer.render(rendererPositionUs, rendererPositionElapsedRealtimeUs);
|
||||
if (isRendererEnabled(primaryRenderer)) {
|
||||
primaryRenderer.render(rendererPositionUs, rendererPositionElapsedRealtimeUs);
|
||||
}
|
||||
if (secondaryRenderer != null && isRendererEnabled(secondaryRenderer)) {
|
||||
secondaryRenderer.render(rendererPositionUs, rendererPositionElapsedRealtimeUs);
|
||||
}
|
||||
}
|
||||
|
||||
@ -289,23 +389,22 @@ import java.io.IOException;
|
||||
* @param playingPeriodHolder The currently playing media {@link MediaPeriodHolder period}.
|
||||
* @return whether renderer allows playback.
|
||||
*/
|
||||
public boolean allowsPlayback(MediaPeriodHolder playingPeriodHolder) throws IOException {
|
||||
return allowsPlayback(renderer, playingPeriodHolder);
|
||||
}
|
||||
|
||||
private boolean allowsPlayback(Renderer renderer, MediaPeriodHolder playingPeriodHolder) {
|
||||
boolean isReadingAhead = playingPeriodHolder.sampleStreams[index] != renderer.getStream();
|
||||
boolean isWaitingForNextStream = !isReadingAhead && renderer.hasReadStreamToEnd();
|
||||
return isReadingAhead || isWaitingForNextStream || renderer.isReady() || renderer.isEnded();
|
||||
public boolean allowsPlayback(MediaPeriodHolder playingPeriodHolder) {
|
||||
Renderer renderer = getRendererReadingFromPeriod(playingPeriodHolder);
|
||||
return renderer == null
|
||||
|| renderer.hasReadStreamToEnd()
|
||||
|| renderer.isReady()
|
||||
|| renderer.isEnded();
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes {Renderer#maybeThrowStreamError}.
|
||||
* Invokes {@link Renderer#maybeThrowStreamError()} for {@link Renderer} enabled on {@link
|
||||
* MediaPeriodHolder media period}.
|
||||
*
|
||||
* @see Renderer#maybeThrowStreamError()
|
||||
*/
|
||||
public void maybeThrowStreamError() throws IOException {
|
||||
renderer.maybeThrowStreamError();
|
||||
public void maybeThrowStreamError(MediaPeriodHolder mediaPeriodHolder) throws IOException {
|
||||
checkNotNull(getRendererReadingFromPeriod(mediaPeriodHolder)).maybeThrowStreamError();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -314,15 +413,23 @@ import java.io.IOException;
|
||||
* @throws ExoPlaybackException If an error occurs.
|
||||
*/
|
||||
public void start() throws ExoPlaybackException {
|
||||
if (renderer.getState() == STATE_ENABLED) {
|
||||
renderer.start();
|
||||
if (primaryRenderer.getState() == STATE_ENABLED
|
||||
&& (prewarmingState != RENDERER_PREWARMING_STATE_TRANSITIONING_TO_PRIMARY)) {
|
||||
primaryRenderer.start();
|
||||
} else if (secondaryRenderer != null
|
||||
&& secondaryRenderer.getState() == STATE_ENABLED
|
||||
&& prewarmingState != RENDERER_PREWARMING_STATE_TRANSITIONING_TO_SECONDARY) {
|
||||
secondaryRenderer.start();
|
||||
}
|
||||
}
|
||||
|
||||
/** Calls {@link Renderer#stop()} on all enabled {@link Renderer renderers}. */
|
||||
public void stop() {
|
||||
if (isRendererEnabled(renderer)) {
|
||||
ensureStopped(renderer);
|
||||
if (isRendererEnabled(primaryRenderer)) {
|
||||
ensureStopped(primaryRenderer);
|
||||
}
|
||||
if (secondaryRenderer != null && isRendererEnabled(secondaryRenderer)) {
|
||||
ensureStopped(secondaryRenderer);
|
||||
}
|
||||
}
|
||||
|
||||
@ -337,7 +444,7 @@ import java.io.IOException;
|
||||
*
|
||||
* @see Renderer#enable
|
||||
* @param configuration The renderer configuration.
|
||||
* @param formats The enabled formats.
|
||||
* @param trackSelection The track selection for the {@link Renderer}.
|
||||
* @param stream The {@link SampleStream} from which the renderer should consume.
|
||||
* @param positionUs The player's current position.
|
||||
* @param joining Whether this renderer is being enabled to join an ongoing playback.
|
||||
@ -354,7 +461,7 @@ import java.io.IOException;
|
||||
*/
|
||||
public void enable(
|
||||
RendererConfiguration configuration,
|
||||
Format[] formats,
|
||||
ExoTrackSelection trackSelection,
|
||||
SampleStream stream,
|
||||
long positionUs,
|
||||
boolean joining,
|
||||
@ -364,8 +471,14 @@ import java.io.IOException;
|
||||
MediaSource.MediaPeriodId mediaPeriodId,
|
||||
DefaultMediaClock mediaClock)
|
||||
throws ExoPlaybackException {
|
||||
requiresReset = true;
|
||||
renderer.enable(
|
||||
Format[] formats = getFormats(trackSelection);
|
||||
boolean enablePrimary =
|
||||
prewarmingState == RENDERER_PREWARMING_STATE_NOT_PREWARMING_USING_PRIMARY
|
||||
|| prewarmingState == RENDERER_PREWARMING_STATE_PREWARMING_PRIMARY
|
||||
|| prewarmingState == RENDERER_PREWARMING_STATE_TRANSITIONING_TO_PRIMARY;
|
||||
if (enablePrimary) {
|
||||
primaryRequiresReset = true;
|
||||
primaryRenderer.enable(
|
||||
configuration,
|
||||
formats,
|
||||
stream,
|
||||
@ -375,16 +488,36 @@ import java.io.IOException;
|
||||
startPositionUs,
|
||||
offsetUs,
|
||||
mediaPeriodId);
|
||||
mediaClock.onRendererEnabled(renderer);
|
||||
mediaClock.onRendererEnabled(primaryRenderer);
|
||||
} else {
|
||||
secondaryRequiresReset = true;
|
||||
checkNotNull(secondaryRenderer)
|
||||
.enable(
|
||||
configuration,
|
||||
formats,
|
||||
stream,
|
||||
positionUs,
|
||||
joining,
|
||||
mayRenderStartOfStream,
|
||||
startPositionUs,
|
||||
offsetUs,
|
||||
mediaPeriodId);
|
||||
mediaClock.onRendererEnabled(secondaryRenderer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes {@link Renderer#handleMessage} on the {@link Renderer}.
|
||||
* Invokes {@link Renderer#handleMessage} on the {@link Renderer} enabled on the {@link
|
||||
* MediaPeriodHolder media period}.
|
||||
*
|
||||
* @see Renderer#handleMessage(int, Object)
|
||||
*/
|
||||
public void handleMessage(@Renderer.MessageType int messageType, @Nullable Object message)
|
||||
public void handleMessage(
|
||||
@Renderer.MessageType int messageType,
|
||||
@Nullable Object message,
|
||||
MediaPeriodHolder mediaPeriod)
|
||||
throws ExoPlaybackException {
|
||||
Renderer renderer = checkNotNull(getRendererReadingFromPeriod(mediaPeriod));
|
||||
renderer.handleMessage(messageType, message);
|
||||
}
|
||||
|
||||
@ -395,7 +528,22 @@ import java.io.IOException;
|
||||
* Renderer}.
|
||||
*/
|
||||
public void disable(DefaultMediaClock mediaClock) {
|
||||
disableRenderer(renderer, mediaClock);
|
||||
disableRenderer(primaryRenderer, mediaClock);
|
||||
if (secondaryRenderer != null) {
|
||||
disableRenderer(secondaryRenderer, mediaClock);
|
||||
// Release resources for other renderer
|
||||
maybeResetRenderer(/* resetPrimary= */ false);
|
||||
}
|
||||
prewarmingState = RENDERER_PREWARMING_STATE_NOT_PREWARMING_USING_PRIMARY;
|
||||
}
|
||||
|
||||
public void maybeHandlePrewarmingTransition() {
|
||||
if (prewarmingState == RENDERER_PREWARMING_STATE_TRANSITIONING_TO_PRIMARY
|
||||
|| prewarmingState == RENDERER_PREWARMING_STATE_PREWARMING_PRIMARY) {
|
||||
prewarmingState = RENDERER_PREWARMING_STATE_NOT_PREWARMING_USING_PRIMARY;
|
||||
} else if (prewarmingState == RENDERER_PREWARMING_STATE_TRANSITIONING_TO_SECONDARY) {
|
||||
prewarmingState = RENDERER_PREWARMING_STATE_NOT_PREWARMING_USING_SECONDARY;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -410,6 +558,7 @@ import java.io.IOException;
|
||||
* renderer}.
|
||||
*/
|
||||
private void disableRenderer(Renderer renderer, DefaultMediaClock mediaClock) {
|
||||
checkState(primaryRenderer == renderer || secondaryRenderer == renderer);
|
||||
if (!isRendererEnabled(renderer)) {
|
||||
return;
|
||||
}
|
||||
@ -419,37 +568,241 @@ import java.io.IOException;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link Renderer#resetPosition} on the {@link Renderer} if its enabled.
|
||||
* Invokes {@link Renderer#resetPosition} on the {@link Renderer} that is enabled on the provided
|
||||
* {@link MediaPeriodHolder media period}.
|
||||
*
|
||||
* @see Renderer#resetPosition
|
||||
*/
|
||||
public void resetPosition(long positionUs) throws ExoPlaybackException {
|
||||
if (isRendererEnabled(renderer)) {
|
||||
public void resetPosition(MediaPeriodHolder playingPeriod, long positionUs)
|
||||
throws ExoPlaybackException {
|
||||
Renderer renderer = getRendererReadingFromPeriod(playingPeriod);
|
||||
if (renderer != null) {
|
||||
renderer.resetPosition(positionUs);
|
||||
}
|
||||
}
|
||||
|
||||
/** Calls {@link Renderer#reset()} on all renderers that must be reset. */
|
||||
/**
|
||||
* Calls {@link Renderer#reset()} on all disabled {@link Renderer renderers} that must be reset.
|
||||
*/
|
||||
public void reset() {
|
||||
if (requiresReset) {
|
||||
renderer.reset();
|
||||
requiresReset = false;
|
||||
if (!isRendererEnabled(primaryRenderer)) {
|
||||
maybeResetRenderer(/* resetPrimary= */ true);
|
||||
}
|
||||
if (secondaryRenderer != null && !isRendererEnabled(secondaryRenderer)) {
|
||||
maybeResetRenderer(/* resetPrimary= */ false);
|
||||
}
|
||||
}
|
||||
|
||||
private void maybeResetRenderer(boolean resetPrimary) {
|
||||
if (resetPrimary) {
|
||||
if (primaryRequiresReset) {
|
||||
primaryRenderer.reset();
|
||||
primaryRequiresReset = false;
|
||||
}
|
||||
} else if (secondaryRequiresReset) {
|
||||
checkNotNull(secondaryRenderer).reset();
|
||||
secondaryRequiresReset = false;
|
||||
}
|
||||
}
|
||||
|
||||
public int replaceStreamsOrDisableRendererForTransition(
|
||||
MediaPeriodHolder readingPeriodHolder,
|
||||
TrackSelectorResult newTrackSelectorResult,
|
||||
DefaultMediaClock mediaClock)
|
||||
throws ExoPlaybackException {
|
||||
int primaryRendererResult =
|
||||
replaceStreamsOrDisableRendererForTransitionInternal(
|
||||
primaryRenderer, readingPeriodHolder, newTrackSelectorResult, mediaClock);
|
||||
int secondaryRendererResult =
|
||||
replaceStreamsOrDisableRendererForTransitionInternal(
|
||||
secondaryRenderer, readingPeriodHolder, newTrackSelectorResult, mediaClock);
|
||||
return primaryRendererResult == REPLACE_STREAMS_DISABLE_RENDERERS_COMPLETED
|
||||
? secondaryRendererResult
|
||||
: primaryRendererResult;
|
||||
}
|
||||
|
||||
private int replaceStreamsOrDisableRendererForTransitionInternal(
|
||||
@Nullable Renderer renderer,
|
||||
MediaPeriodHolder readingPeriodHolder,
|
||||
TrackSelectorResult newTrackSelectorResult,
|
||||
DefaultMediaClock mediaClock)
|
||||
throws ExoPlaybackException {
|
||||
if (renderer == null
|
||||
|| !isRendererEnabled(renderer)
|
||||
|| (renderer == primaryRenderer && isPrimaryRendererPrewarming())
|
||||
|| (renderer == secondaryRenderer && isSecondaryRendererPrewarming())) {
|
||||
return REPLACE_STREAMS_DISABLE_RENDERERS_COMPLETED;
|
||||
}
|
||||
boolean rendererIsReadingOldStream =
|
||||
renderer.getStream() != readingPeriodHolder.sampleStreams[index];
|
||||
boolean rendererShouldBeEnabled = newTrackSelectorResult.isRendererEnabled(index);
|
||||
if (rendererShouldBeEnabled && !rendererIsReadingOldStream) {
|
||||
// All done.
|
||||
return REPLACE_STREAMS_DISABLE_RENDERERS_COMPLETED;
|
||||
}
|
||||
if (!renderer.isCurrentStreamFinal()) {
|
||||
// The renderer stream is not final, so we can replace the sample streams immediately.
|
||||
Format[] formats = getFormats(newTrackSelectorResult.selections[index]);
|
||||
renderer.replaceStream(
|
||||
formats,
|
||||
checkNotNull(readingPeriodHolder.sampleStreams[index]),
|
||||
readingPeriodHolder.getStartPositionRendererTime(),
|
||||
readingPeriodHolder.getRendererOffset(),
|
||||
readingPeriodHolder.info.id);
|
||||
// Prevent sleeping across offload track transition else position won't get updated.
|
||||
return REPLACE_STREAMS_DISABLE_RENDERERS_COMPLETED
|
||||
| REPLACE_STREAMS_DISABLE_RENDERERS_DISABLE_OFFLOAD_SCHEDULING;
|
||||
} else if (renderer.isEnded()) {
|
||||
// The renderer has finished playback, so we can disable it now.
|
||||
disableRenderer(renderer, mediaClock);
|
||||
if (!rendererShouldBeEnabled || isPrewarming()) {
|
||||
maybeResetRenderer(/* resetPrimary= */ renderer == primaryRenderer);
|
||||
}
|
||||
return REPLACE_STREAMS_DISABLE_RENDERERS_COMPLETED;
|
||||
} else {
|
||||
// Need to wait until rendering of current item is finished.
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static Format[] getFormats(@Nullable ExoTrackSelection newSelection) {
|
||||
// Build an array of formats contained by the selection.
|
||||
int length = newSelection != null ? newSelection.length() : 0;
|
||||
Format[] formats = new Format[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
formats[i] = checkNotNull(newSelection).getFormat(i);
|
||||
}
|
||||
return formats;
|
||||
}
|
||||
|
||||
/** Calls {@link Renderer#release()} on all {@link Renderer renderers}. */
|
||||
public void release() {
|
||||
renderer.release();
|
||||
requiresReset = false;
|
||||
primaryRenderer.release();
|
||||
primaryRequiresReset = false;
|
||||
if (secondaryRenderer != null) {
|
||||
secondaryRenderer.release();
|
||||
secondaryRequiresReset = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void setVideoOutput(@Nullable Object videoOutput) throws ExoPlaybackException {
|
||||
if (renderer.getTrackType() == TRACK_TYPE_VIDEO) {
|
||||
renderer.handleMessage(Renderer.MSG_SET_VIDEO_OUTPUT, videoOutput);
|
||||
if (getTrackType() != TRACK_TYPE_VIDEO) {
|
||||
return;
|
||||
}
|
||||
if (prewarmingState == RENDERER_PREWARMING_STATE_TRANSITIONING_TO_PRIMARY
|
||||
|| prewarmingState == RENDERER_PREWARMING_STATE_NOT_PREWARMING_USING_SECONDARY) {
|
||||
checkNotNull(secondaryRenderer).handleMessage(Renderer.MSG_SET_VIDEO_OUTPUT, videoOutput);
|
||||
} else {
|
||||
primaryRenderer.handleMessage(Renderer.MSG_SET_VIDEO_OUTPUT, videoOutput);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isRendererEnabled() {
|
||||
boolean checkPrimary =
|
||||
prewarmingState == RENDERER_PREWARMING_STATE_NOT_PREWARMING_USING_PRIMARY
|
||||
|| prewarmingState == RENDERER_PREWARMING_STATE_PREWARMING_PRIMARY
|
||||
|| prewarmingState == RENDERER_PREWARMING_STATE_TRANSITIONING_TO_PRIMARY;
|
||||
return checkPrimary
|
||||
? isRendererEnabled(primaryRenderer)
|
||||
: isRendererEnabled(checkNotNull(secondaryRenderer));
|
||||
}
|
||||
|
||||
private static boolean isRendererEnabled(Renderer renderer) {
|
||||
return renderer.getState() != STATE_DISABLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Renderer} that is enabled on the provided media {@link MediaPeriodHolder
|
||||
* period}.
|
||||
*
|
||||
* <p>Returns null if the renderer is not enabled on the requested period.
|
||||
*
|
||||
* @param period The {@link MediaPeriodHolder period} with which to retrieve the linked {@link
|
||||
* Renderer}
|
||||
* @return {@link Renderer} enabled on the {@link MediaPeriodHolder period} or {@code null} if the
|
||||
* renderer is not enabled on the provided period.
|
||||
*/
|
||||
@Nullable
|
||||
private Renderer getRendererReadingFromPeriod(@Nullable MediaPeriodHolder period) {
|
||||
if (period == null || period.sampleStreams[index] == null) {
|
||||
return null;
|
||||
}
|
||||
if (primaryRenderer.getStream() == period.sampleStreams[index]) {
|
||||
return primaryRenderer;
|
||||
} else if (secondaryRenderer != null
|
||||
&& secondaryRenderer.getStream() == period.sampleStreams[index]) {
|
||||
return secondaryRenderer;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Possible pre-warming states for primary and secondary renderers. */
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@Target(TYPE_USE)
|
||||
@IntDef({
|
||||
RENDERER_PREWARMING_STATE_NOT_PREWARMING_USING_PRIMARY,
|
||||
RENDERER_PREWARMING_STATE_NOT_PREWARMING_USING_SECONDARY,
|
||||
RENDERER_PREWARMING_STATE_PREWARMING_PRIMARY,
|
||||
RENDERER_PREWARMING_STATE_TRANSITIONING_TO_SECONDARY,
|
||||
RENDERER_PREWARMING_STATE_TRANSITIONING_TO_PRIMARY
|
||||
})
|
||||
@interface RendererPrewarmingState {}
|
||||
|
||||
/**
|
||||
* ExoPlayer is not currently transitioning between two enabled renderers for subsequent media
|
||||
* items and is using the primary renderer.
|
||||
*/
|
||||
/* package */ static final int RENDERER_PREWARMING_STATE_NOT_PREWARMING_USING_PRIMARY = 0;
|
||||
|
||||
/**
|
||||
* ExoPlayer is not currently transitioning between two enabled renderers for subsequent media
|
||||
* items and is using the secondary renderer.
|
||||
*/
|
||||
/* package */ static final int RENDERER_PREWARMING_STATE_NOT_PREWARMING_USING_SECONDARY = 1;
|
||||
|
||||
/**
|
||||
* ExoPlayer is currently pre-warming the primary renderer that is not being used for the current
|
||||
* media item for a subsequent media item.
|
||||
*/
|
||||
/* package */ static final int RENDERER_PREWARMING_STATE_PREWARMING_PRIMARY = 2;
|
||||
|
||||
/**
|
||||
* Both a primary and secondary renderer are enabled and ExoPlayer is transitioning to a media
|
||||
* item using the secondary renderer.
|
||||
*/
|
||||
/* package */ static final int RENDERER_PREWARMING_STATE_TRANSITIONING_TO_SECONDARY = 3;
|
||||
|
||||
/**
|
||||
* Both a primary and secondary renderer are enabled and ExoPlayer is transitioning to a media
|
||||
* item using the primary renderer.
|
||||
*/
|
||||
/* package */ static final int RENDERER_PREWARMING_STATE_TRANSITIONING_TO_PRIMARY = 4;
|
||||
|
||||
/** Results for calls to {@link #replaceStreamsOrDisableRendererForTransition}. */
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@Target(TYPE_USE)
|
||||
@IntDef(
|
||||
flag = true,
|
||||
value = {
|
||||
REPLACE_STREAMS_DISABLE_RENDERERS_COMPLETED,
|
||||
REPLACE_STREAMS_DISABLE_RENDERERS_DISABLE_OFFLOAD_SCHEDULING
|
||||
})
|
||||
/* package */ @interface ReplaceStreamsOrDisableRendererResult {}
|
||||
|
||||
/**
|
||||
* The call to {@link #replaceStreamsOrDisableRendererForTransition} has completed processing
|
||||
* {@link Renderer#replaceStream} or {@link Renderer#disable()} on all renderers enabled on the
|
||||
* current playing period.
|
||||
*/
|
||||
/* package */ static final int REPLACE_STREAMS_DISABLE_RENDERERS_COMPLETED = 1;
|
||||
|
||||
/**
|
||||
* The call to {@link #replaceStreamsOrDisableRendererForTransition} invoked {@link
|
||||
* Renderer#replaceStream} and so therefore offload should be disabled until after the media
|
||||
* transition.
|
||||
*/
|
||||
/* package */ static final int REPLACE_STREAMS_DISABLE_RENDERERS_DISABLE_OFFLOAD_SCHEDULING =
|
||||
1 << 1;
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
package androidx.media3.exoplayer;
|
||||
|
||||
import android.os.Handler;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.media3.exoplayer.audio.AudioRendererEventListener;
|
||||
import androidx.media3.exoplayer.metadata.MetadataOutput;
|
||||
@ -42,4 +43,29 @@ public interface RenderersFactory {
|
||||
AudioRendererEventListener audioRendererEventListener,
|
||||
TextOutput textRendererOutput,
|
||||
MetadataOutput metadataRendererOutput);
|
||||
|
||||
/**
|
||||
* Provides a secondary {@link Renderer} instance for an {@link ExoPlayer} to use for pre-warming.
|
||||
*
|
||||
* <p>The created secondary {@code Renderer} should match its primary in its reported track type
|
||||
* support and {@link RendererCapabilities}.
|
||||
*
|
||||
* @param renderer The primary {@code Renderer} for which to create the backup.
|
||||
* @param eventHandler A handler to use when invoking event listeners and outputs.
|
||||
* @param videoRendererEventListener An event listener for video renderers.
|
||||
* @param audioRendererEventListener An event listener for audio renderers.
|
||||
* @param textRendererOutput An output for text renderers.
|
||||
* @param metadataRendererOutput An output for metadata renderers.
|
||||
* @return The {@link Renderer instances}.
|
||||
*/
|
||||
@Nullable
|
||||
default Renderer createSecondaryRenderer(
|
||||
Renderer renderer,
|
||||
Handler eventHandler,
|
||||
VideoRendererEventListener videoRendererEventListener,
|
||||
AudioRendererEventListener audioRendererEventListener,
|
||||
TextOutput textRendererOutput,
|
||||
MetadataOutput metadataRendererOutput) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,331 @@
|
||||
/*
|
||||
* Copyright (C) 2024 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 androidx.media3.exoplayer;
|
||||
|
||||
import static androidx.media3.test.utils.robolectric.TestPlayerRunHelper.run;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Handler;
|
||||
import androidx.media3.common.Player;
|
||||
import androidx.media3.common.util.Clock;
|
||||
import androidx.media3.common.util.HandlerWrapper;
|
||||
import androidx.media3.exoplayer.audio.AudioRendererEventListener;
|
||||
import androidx.media3.exoplayer.metadata.MetadataOutput;
|
||||
import androidx.media3.exoplayer.text.TextOutput;
|
||||
import androidx.media3.exoplayer.video.VideoRendererEventListener;
|
||||
import androidx.media3.test.utils.ExoPlayerTestRunner;
|
||||
import androidx.media3.test.utils.FakeAudioRenderer;
|
||||
import androidx.media3.test.utils.FakeClock;
|
||||
import androidx.media3.test.utils.FakeMediaSource;
|
||||
import androidx.media3.test.utils.FakeTimeline;
|
||||
import androidx.media3.test.utils.FakeVideoRenderer;
|
||||
import androidx.media3.test.utils.TestExoPlayerBuilder;
|
||||
import androidx.media3.test.utils.robolectric.ShadowMediaCodecConfig;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/** Unit test for {@link ExoPlayer} with the pre-warming render feature. */
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ExoPlayerWithPrewarmingRenderersTest {
|
||||
|
||||
private Context context;
|
||||
|
||||
@Rule
|
||||
public ShadowMediaCodecConfig mediaCodecConfig =
|
||||
ShadowMediaCodecConfig.forAllSupportedMimeTypes();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
context = ApplicationProvider.getApplicationContext();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void play_withTwoItemsAndPrewarming_secondaryRendererisEnabledButNotStarted()
|
||||
throws Exception {
|
||||
Clock fakeClock = new FakeClock(/* isAutoAdvancing= */ true);
|
||||
ExoPlayer player =
|
||||
new TestExoPlayerBuilder(context)
|
||||
.setClock(fakeClock)
|
||||
.setRenderersFactory(
|
||||
new FakeRenderersFactorySupportingSecondaryVideoRenderer(fakeClock))
|
||||
.build();
|
||||
Renderer videoRenderer = player.getRenderer(/* index= */ 0);
|
||||
Renderer secondaryVideoRenderer = player.getSecondaryRenderer(/* index= */ 0);
|
||||
// Set a playlist that allows a new renderer to be enabled early.
|
||||
player.setMediaSources(
|
||||
ImmutableList.of(
|
||||
new FakeMediaSource(new FakeTimeline(), ExoPlayerTestRunner.VIDEO_FORMAT),
|
||||
new FakeMediaSource(
|
||||
new FakeTimeline(),
|
||||
ExoPlayerTestRunner.VIDEO_FORMAT,
|
||||
ExoPlayerTestRunner.AUDIO_FORMAT)));
|
||||
player.prepare();
|
||||
|
||||
// Play a bit until the second renderer is being pre-warmed.
|
||||
player.play();
|
||||
run(player)
|
||||
.untilBackgroundThreadCondition(
|
||||
() -> secondaryVideoRenderer.getState() == Renderer.STATE_ENABLED);
|
||||
@Renderer.State int videoState1 = videoRenderer.getState();
|
||||
@Renderer.State int secondaryVideoState1 = secondaryVideoRenderer.getState();
|
||||
// Play until second item is started.
|
||||
run(player)
|
||||
.untilBackgroundThreadCondition(
|
||||
() -> secondaryVideoRenderer.getState() == Renderer.STATE_STARTED);
|
||||
@Renderer.State int videoState2 = videoRenderer.getState();
|
||||
@Renderer.State int secondaryVideoState2 = secondaryVideoRenderer.getState();
|
||||
player.release();
|
||||
|
||||
assertThat(videoState1).isEqualTo(Renderer.STATE_STARTED);
|
||||
assertThat(secondaryVideoState1).isEqualTo(Renderer.STATE_ENABLED);
|
||||
assertThat(videoState2).isEqualTo(Renderer.STATE_DISABLED);
|
||||
assertThat(secondaryVideoState2).isEqualTo(Renderer.STATE_STARTED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
play_withThreeItemsAndPrewarming_playerSuccessfullyPrewarmsAndSwapsBackToPrimaryRenderer()
|
||||
throws Exception {
|
||||
Clock fakeClock = new FakeClock(/* isAutoAdvancing= */ true);
|
||||
ExoPlayer player =
|
||||
new TestExoPlayerBuilder(context)
|
||||
.setClock(fakeClock)
|
||||
.setRenderersFactory(
|
||||
new FakeRenderersFactorySupportingSecondaryVideoRenderer(fakeClock))
|
||||
.build();
|
||||
Renderer videoRenderer = player.getRenderer(/* index= */ 0);
|
||||
Renderer secondaryVideoRenderer = player.getSecondaryRenderer(/* index= */ 0);
|
||||
// Set a playlist that allows a new renderer to be enabled early.
|
||||
player.setMediaSources(
|
||||
ImmutableList.of(
|
||||
new FakeMediaSource(new FakeTimeline(), ExoPlayerTestRunner.VIDEO_FORMAT),
|
||||
new FakeMediaSource(
|
||||
new FakeTimeline(),
|
||||
ExoPlayerTestRunner.VIDEO_FORMAT,
|
||||
ExoPlayerTestRunner.AUDIO_FORMAT),
|
||||
new FakeMediaSource(
|
||||
new FakeTimeline(),
|
||||
ExoPlayerTestRunner.VIDEO_FORMAT,
|
||||
ExoPlayerTestRunner.AUDIO_FORMAT)));
|
||||
player.prepare();
|
||||
|
||||
// Play a bit until the secondary renderer is being pre-warmed.
|
||||
player.play();
|
||||
run(player)
|
||||
.untilBackgroundThreadCondition(
|
||||
() -> secondaryVideoRenderer.getState() == Renderer.STATE_ENABLED);
|
||||
@Renderer.State int videoState1 = videoRenderer.getState();
|
||||
@Renderer.State int secondaryVideoState1 = secondaryVideoRenderer.getState();
|
||||
// Play until until the primary renderer is being pre-warmed.
|
||||
run(player)
|
||||
.untilBackgroundThreadCondition(() -> videoRenderer.getState() == Renderer.STATE_ENABLED);
|
||||
@Renderer.State int videoState2 = videoRenderer.getState();
|
||||
@Renderer.State int secondaryVideoState2 = secondaryVideoRenderer.getState();
|
||||
// Play until past transition back to primary renderer for third media item.
|
||||
run(player)
|
||||
.untilBackgroundThreadCondition(() -> videoRenderer.getState() == Renderer.STATE_STARTED);
|
||||
@Renderer.State int videoState3 = videoRenderer.getState();
|
||||
@Renderer.State int secondaryVideoState3 = secondaryVideoRenderer.getState();
|
||||
player.release();
|
||||
|
||||
assertThat(videoState1).isEqualTo(Renderer.STATE_STARTED);
|
||||
assertThat(videoState2).isEqualTo(Renderer.STATE_ENABLED);
|
||||
assertThat(videoState3).isEqualTo(Renderer.STATE_STARTED);
|
||||
assertThat(secondaryVideoState1).isEqualTo(Renderer.STATE_ENABLED);
|
||||
assertThat(secondaryVideoState2).isEqualTo(Renderer.STATE_STARTED);
|
||||
assertThat(secondaryVideoState3).isEqualTo(Renderer.STATE_DISABLED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void prepare_withPeriodBetweenPlayingAndPrewarmingPeriods_playerSuccessfullyPrewarms()
|
||||
throws Exception {
|
||||
Clock fakeClock = new FakeClock(/* isAutoAdvancing= */ true);
|
||||
ExoPlayer player =
|
||||
new TestExoPlayerBuilder(context)
|
||||
.setClock(fakeClock)
|
||||
.setRenderersFactory(
|
||||
new FakeRenderersFactorySupportingSecondaryVideoRenderer(fakeClock))
|
||||
.build();
|
||||
Renderer secondaryVideoRenderer = player.getSecondaryRenderer(/* index= */ 0);
|
||||
// Set a playlist that allows a new renderer to be enabled early.
|
||||
player.setMediaSources(
|
||||
ImmutableList.of(
|
||||
new FakeMediaSource(new FakeTimeline(), ExoPlayerTestRunner.VIDEO_FORMAT),
|
||||
new FakeMediaSource(new FakeTimeline(), ExoPlayerTestRunner.AUDIO_FORMAT),
|
||||
new FakeMediaSource(
|
||||
new FakeTimeline(),
|
||||
ExoPlayerTestRunner.VIDEO_FORMAT,
|
||||
ExoPlayerTestRunner.AUDIO_FORMAT)));
|
||||
player.prepare();
|
||||
|
||||
// Advance media periods until secondary renderer is being pre-warmed.
|
||||
run(player)
|
||||
.untilBackgroundThreadCondition(
|
||||
() -> secondaryVideoRenderer.getState() == Renderer.STATE_ENABLED);
|
||||
@Renderer.State int secondaryVideoState = secondaryVideoRenderer.getState();
|
||||
player.release();
|
||||
|
||||
assertThat(secondaryVideoState).isEqualTo(Renderer.STATE_ENABLED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
setPlayWhenReady_playFromPauseWithPrewarmingPrimaryRenderer_primaryRendererIsEnabledButNotStarted()
|
||||
throws Exception {
|
||||
Clock fakeClock = new FakeClock(/* isAutoAdvancing= */ true);
|
||||
ExoPlayer player =
|
||||
new TestExoPlayerBuilder(context)
|
||||
.setClock(fakeClock)
|
||||
.setRenderersFactory(
|
||||
new FakeRenderersFactorySupportingSecondaryVideoRenderer(fakeClock))
|
||||
.build();
|
||||
Renderer videoRenderer = player.getRenderer(/* index= */ 0);
|
||||
Renderer secondaryVideoRenderer = player.getSecondaryRenderer(/* index= */ 0);
|
||||
// Set a playlist that allows a new renderer to be enabled early.
|
||||
player.setMediaSources(
|
||||
ImmutableList.of(
|
||||
new FakeMediaSource(
|
||||
new FakeTimeline(),
|
||||
ExoPlayerTestRunner.VIDEO_FORMAT,
|
||||
ExoPlayerTestRunner.AUDIO_FORMAT),
|
||||
new FakeMediaSource(
|
||||
new FakeTimeline(),
|
||||
ExoPlayerTestRunner.VIDEO_FORMAT,
|
||||
ExoPlayerTestRunner.AUDIO_FORMAT),
|
||||
new FakeMediaSource(
|
||||
new FakeTimeline(),
|
||||
ExoPlayerTestRunner.VIDEO_FORMAT,
|
||||
ExoPlayerTestRunner.AUDIO_FORMAT)));
|
||||
player.prepare();
|
||||
|
||||
// Play a bit until the primary renderer has been enabled, but not yet started.
|
||||
player.play();
|
||||
run(player)
|
||||
.untilBackgroundThreadCondition(
|
||||
() -> secondaryVideoRenderer.getState() == Renderer.STATE_STARTED);
|
||||
run(player)
|
||||
.untilBackgroundThreadCondition(() -> videoRenderer.getState() == Renderer.STATE_ENABLED);
|
||||
@Renderer.State int videoState1 = videoRenderer.getState();
|
||||
@Renderer.State int secondaryVideoState1 = secondaryVideoRenderer.getState();
|
||||
player.pause();
|
||||
run(player).untilPendingCommandsAreFullyHandled();
|
||||
@Renderer.State int videoState2 = videoRenderer.getState();
|
||||
@Renderer.State int secondaryVideoState2 = secondaryVideoRenderer.getState();
|
||||
player.play();
|
||||
run(player)
|
||||
.untilBackgroundThreadCondition(
|
||||
() -> secondaryVideoRenderer.getState() == Renderer.STATE_STARTED);
|
||||
@Renderer.State int videoState3 = videoRenderer.getState();
|
||||
@Renderer.State int secondaryVideoState3 = secondaryVideoRenderer.getState();
|
||||
player.release();
|
||||
|
||||
assertThat(videoState1).isEqualTo(Renderer.STATE_ENABLED);
|
||||
assertThat(secondaryVideoState1).isEqualTo(Renderer.STATE_STARTED);
|
||||
assertThat(videoState2).isEqualTo(Renderer.STATE_ENABLED);
|
||||
assertThat(secondaryVideoState2).isEqualTo(Renderer.STATE_ENABLED);
|
||||
assertThat(videoState3).isEqualTo(Renderer.STATE_ENABLED);
|
||||
assertThat(secondaryVideoState3).isEqualTo(Renderer.STATE_STARTED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
setPlayWhenReady_playFromPauseWithPrewarmingNonTransitioningRenderer_rendererIsEnabledButNotStarted()
|
||||
throws Exception {
|
||||
Clock fakeClock = new FakeClock(/* isAutoAdvancing= */ true);
|
||||
ExoPlayer player =
|
||||
new TestExoPlayerBuilder(context)
|
||||
.setClock(fakeClock)
|
||||
.setRenderersFactory(
|
||||
new FakeRenderersFactorySupportingSecondaryVideoRenderer(fakeClock))
|
||||
.build();
|
||||
Renderer videoRenderer = player.getRenderer(/* index= */ 0);
|
||||
Renderer secondaryVideoRenderer = player.getSecondaryRenderer(/* index= */ 0);
|
||||
// Set a playlist that allows a new renderer to be enabled early.
|
||||
player.setMediaSources(
|
||||
ImmutableList.of(
|
||||
new FakeMediaSource(new FakeTimeline(), ExoPlayerTestRunner.AUDIO_FORMAT),
|
||||
new FakeMediaSource(
|
||||
new FakeTimeline(),
|
||||
ExoPlayerTestRunner.VIDEO_FORMAT,
|
||||
ExoPlayerTestRunner.AUDIO_FORMAT)));
|
||||
player.prepare();
|
||||
|
||||
// Play a bit until the primary renderer has been enabled, but not yet started.
|
||||
run(player).untilState(Player.STATE_READY);
|
||||
player.play();
|
||||
run(player).untilPendingCommandsAreFullyHandled();
|
||||
@Renderer.State int videoState1 = videoRenderer.getState();
|
||||
@Renderer.State int secondaryVideoState1 = secondaryVideoRenderer.getState();
|
||||
player.pause();
|
||||
run(player).untilPendingCommandsAreFullyHandled();
|
||||
@Renderer.State int videoState2 = videoRenderer.getState();
|
||||
player.play();
|
||||
run(player).untilPendingCommandsAreFullyHandled();
|
||||
@Renderer.State int videoState3 = videoRenderer.getState();
|
||||
player.release();
|
||||
|
||||
assertThat(videoState1).isEqualTo(Renderer.STATE_ENABLED);
|
||||
assertThat(secondaryVideoState1).isEqualTo(Renderer.STATE_DISABLED);
|
||||
assertThat(videoState2).isEqualTo(Renderer.STATE_ENABLED);
|
||||
assertThat(videoState3).isEqualTo(Renderer.STATE_ENABLED);
|
||||
}
|
||||
|
||||
private static class FakeRenderersFactorySupportingSecondaryVideoRenderer
|
||||
implements RenderersFactory {
|
||||
protected final Clock clock;
|
||||
|
||||
public FakeRenderersFactorySupportingSecondaryVideoRenderer(Clock clock) {
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Renderer[] createRenderers(
|
||||
Handler eventHandler,
|
||||
VideoRendererEventListener videoRendererEventListener,
|
||||
AudioRendererEventListener audioRendererEventListener,
|
||||
TextOutput textRendererOutput,
|
||||
MetadataOutput metadataRendererOutput) {
|
||||
HandlerWrapper clockAwareHandler =
|
||||
clock.createHandler(eventHandler.getLooper(), /* callback= */ null);
|
||||
return new Renderer[] {
|
||||
new FakeVideoRenderer(clockAwareHandler, videoRendererEventListener),
|
||||
new FakeAudioRenderer(clockAwareHandler, audioRendererEventListener)
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Renderer createSecondaryRenderer(
|
||||
Renderer renderer,
|
||||
Handler eventHandler,
|
||||
VideoRendererEventListener videoRendererEventListener,
|
||||
AudioRendererEventListener audioRendererEventListener,
|
||||
TextOutput textRendererOutput,
|
||||
MetadataOutput metadataRendererOutput) {
|
||||
if (renderer instanceof FakeVideoRenderer) {
|
||||
return new FakeVideoRenderer(
|
||||
clock.createHandler(eventHandler.getLooper(), /* callback= */ null),
|
||||
videoRendererEventListener);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,315 @@
|
||||
/*
|
||||
* Copyright (C) 2024 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 androidx.media3.exoplayer.e2etest;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.SurfaceTexture;
|
||||
import android.os.Handler;
|
||||
import android.view.Surface;
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.MediaItem;
|
||||
import androidx.media3.common.Player;
|
||||
import androidx.media3.exoplayer.ExoPlayer;
|
||||
import androidx.media3.exoplayer.Renderer;
|
||||
import androidx.media3.exoplayer.audio.AudioRendererEventListener;
|
||||
import androidx.media3.exoplayer.image.ImageDecoder;
|
||||
import androidx.media3.exoplayer.image.ImageRenderer;
|
||||
import androidx.media3.exoplayer.metadata.MetadataOutput;
|
||||
import androidx.media3.exoplayer.text.TextOutput;
|
||||
import androidx.media3.exoplayer.video.MediaCodecVideoRenderer;
|
||||
import androidx.media3.exoplayer.video.VideoRendererEventListener;
|
||||
import androidx.media3.test.utils.CapturingRenderersFactory;
|
||||
import androidx.media3.test.utils.DumpFileAsserts;
|
||||
import androidx.media3.test.utils.FakeClock;
|
||||
import androidx.media3.test.utils.robolectric.PlaybackOutput;
|
||||
import androidx.media3.test.utils.robolectric.ShadowMediaCodecConfig;
|
||||
import androidx.media3.test.utils.robolectric.TestPlayerRunHelper;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/** End-to-end playback tests using secondary renderers. */
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class PrewarmingRendererPlaybackTest {
|
||||
|
||||
private static final String TEST_AUDIO_MP4_URI = "asset:///media/mp4/sample_ac3.mp4";
|
||||
private static final String TEST_MP4_URI = "asset:///media/mp4/sample.mp4";
|
||||
private static final String TEST_IMAGE_URI = "asset:///media/jpeg/tokyo.jpg";
|
||||
|
||||
@Rule
|
||||
public ShadowMediaCodecConfig mediaCodecConfig =
|
||||
ShadowMediaCodecConfig.forAllSupportedMimeTypes();
|
||||
|
||||
@Test
|
||||
public void playback_withTwoMediaItemsAndSecondaryVideoRenderer_dumpsCorrectOutput()
|
||||
throws Exception {
|
||||
Context applicationContext = ApplicationProvider.getApplicationContext();
|
||||
CapturingRenderersWithSecondaryVideoRendererFactory capturingRenderersFactory =
|
||||
new CapturingRenderersWithSecondaryVideoRendererFactory(applicationContext);
|
||||
ExoPlayer player =
|
||||
new ExoPlayer.Builder(applicationContext, capturingRenderersFactory)
|
||||
.setClock(new FakeClock(/* isAutoAdvancing= */ true))
|
||||
.build();
|
||||
Surface surface = new Surface(new SurfaceTexture(/* texName= */ 1));
|
||||
player.setVideoSurface(surface);
|
||||
PlaybackOutput playbackOutput = PlaybackOutput.register(player, capturingRenderersFactory);
|
||||
player.addMediaItems(
|
||||
ImmutableList.of(
|
||||
new MediaItem.Builder().setUri(TEST_MP4_URI).build(),
|
||||
new MediaItem.Builder().setUri(TEST_MP4_URI).build()));
|
||||
|
||||
player.prepare();
|
||||
player.play();
|
||||
TestPlayerRunHelper.runUntilPlaybackState(player, Player.STATE_ENDED);
|
||||
player.release();
|
||||
surface.release();
|
||||
|
||||
DumpFileAsserts.assertOutput(
|
||||
applicationContext,
|
||||
playbackOutput,
|
||||
/* dumpFile= */ "playbackdumps/prewarmingRenderer/"
|
||||
+ "twoItemsPlaylist-withSecondaryVideoRenderer"
|
||||
+ ".dump");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void playback_withThreeItemsAndSecondaryVideoRenderer_dumpsCorrectOutput()
|
||||
throws Exception {
|
||||
Context applicationContext = ApplicationProvider.getApplicationContext();
|
||||
CapturingRenderersWithSecondaryVideoRendererFactory capturingRenderersFactory =
|
||||
new CapturingRenderersWithSecondaryVideoRendererFactory(applicationContext);
|
||||
ExoPlayer player =
|
||||
new ExoPlayer.Builder(applicationContext, capturingRenderersFactory)
|
||||
.setClock(new FakeClock(/* isAutoAdvancing= */ true))
|
||||
.build();
|
||||
Surface surface = new Surface(new SurfaceTexture(/* texName= */ 1));
|
||||
player.setVideoSurface(surface);
|
||||
PlaybackOutput playbackOutput = PlaybackOutput.register(player, capturingRenderersFactory);
|
||||
player.addMediaItems(
|
||||
ImmutableList.of(
|
||||
new MediaItem.Builder().setUri(TEST_MP4_URI).build(),
|
||||
new MediaItem.Builder().setUri(TEST_MP4_URI).build(),
|
||||
new MediaItem.Builder().setUri(TEST_MP4_URI).build()));
|
||||
// Disable audio renderer for simpler dump file.
|
||||
player.setTrackSelectionParameters(
|
||||
player
|
||||
.getTrackSelectionParameters()
|
||||
.buildUpon()
|
||||
.setTrackTypeDisabled(C.TRACK_TYPE_AUDIO, true)
|
||||
.build());
|
||||
player.prepare();
|
||||
player.play();
|
||||
|
||||
TestPlayerRunHelper.runUntilPlaybackState(player, Player.STATE_ENDED);
|
||||
|
||||
player.release();
|
||||
surface.release();
|
||||
|
||||
DumpFileAsserts.assertOutput(
|
||||
applicationContext,
|
||||
playbackOutput,
|
||||
"playbackdumps/prewarmingRenderer/"
|
||||
+ "threeItemPlaylist-withSecondaryVideoRenderer"
|
||||
+ ".dump");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void playback_withMultipleMediaItemsWithClippingConfigurations_dumpsCorrectOutput()
|
||||
throws Exception {
|
||||
Context applicationContext = ApplicationProvider.getApplicationContext();
|
||||
CapturingRenderersWithSecondaryVideoRendererFactory capturingRenderersFactory =
|
||||
new CapturingRenderersWithSecondaryVideoRendererFactory(applicationContext);
|
||||
ExoPlayer player =
|
||||
new ExoPlayer.Builder(applicationContext, capturingRenderersFactory)
|
||||
.setClock(new FakeClock(/* isAutoAdvancing= */ true))
|
||||
.build();
|
||||
Surface surface = new Surface(new SurfaceTexture(/* texName= */ 1));
|
||||
player.setVideoSurface(surface);
|
||||
PlaybackOutput playbackOutput = PlaybackOutput.register(player, capturingRenderersFactory);
|
||||
player.addMediaItem(
|
||||
new MediaItem.Builder()
|
||||
.setUri(TEST_MP4_URI)
|
||||
.setClippingConfiguration(
|
||||
new MediaItem.ClippingConfiguration.Builder()
|
||||
.setStartPositionMs(0)
|
||||
.setEndPositionMs(250)
|
||||
.build())
|
||||
.build());
|
||||
player.addMediaItem(
|
||||
new MediaItem.Builder()
|
||||
.setUri(TEST_MP4_URI)
|
||||
.setClippingConfiguration(
|
||||
new MediaItem.ClippingConfiguration.Builder()
|
||||
.setStartPositionMs(250)
|
||||
.setEndPositionMs(600)
|
||||
.build())
|
||||
.build());
|
||||
player.addMediaItem(
|
||||
new MediaItem.Builder()
|
||||
.setUri(TEST_MP4_URI)
|
||||
.setClippingConfiguration(
|
||||
new MediaItem.ClippingConfiguration.Builder()
|
||||
.setStartPositionMs(600)
|
||||
.setEndPositionMs(C.TIME_END_OF_SOURCE)
|
||||
.build())
|
||||
.build());
|
||||
|
||||
player.prepare();
|
||||
player.play();
|
||||
TestPlayerRunHelper.runUntilPlaybackState(player, Player.STATE_ENDED);
|
||||
player.release();
|
||||
surface.release();
|
||||
|
||||
DumpFileAsserts.assertOutput(
|
||||
applicationContext,
|
||||
playbackOutput,
|
||||
"playbackdumps/prewarmingRenderer/"
|
||||
+ "threeItemPlaylist-withClippingConfigurations"
|
||||
+ ".dump");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void playback_withPrewarmingNonTransitioningRenderer_dumpsCorrectOutput()
|
||||
throws Exception {
|
||||
Context applicationContext = ApplicationProvider.getApplicationContext();
|
||||
CapturingRenderersWithSecondaryVideoRendererFactory capturingRenderersFactory =
|
||||
new CapturingRenderersWithSecondaryVideoRendererFactory(applicationContext);
|
||||
ExoPlayer player =
|
||||
new ExoPlayer.Builder(applicationContext, capturingRenderersFactory)
|
||||
.setClock(new FakeClock(/* isAutoAdvancing= */ true))
|
||||
.build();
|
||||
Surface surface = new Surface(new SurfaceTexture(/* texName= */ 1));
|
||||
player.setVideoSurface(surface);
|
||||
PlaybackOutput playbackOutput = PlaybackOutput.register(player, capturingRenderersFactory);
|
||||
player.addMediaItem(new MediaItem.Builder().setUri(TEST_AUDIO_MP4_URI).build());
|
||||
player.addMediaItem(
|
||||
new MediaItem.Builder()
|
||||
.setUri(TEST_MP4_URI)
|
||||
.setClippingConfiguration(
|
||||
new MediaItem.ClippingConfiguration.Builder()
|
||||
.setStartPositionMs(300)
|
||||
.setEndPositionMs(600)
|
||||
.build())
|
||||
.build());
|
||||
|
||||
player.prepare();
|
||||
player.play();
|
||||
TestPlayerRunHelper.runUntilPlaybackState(player, Player.STATE_ENDED);
|
||||
player.release();
|
||||
surface.release();
|
||||
|
||||
DumpFileAsserts.assertOutput(
|
||||
applicationContext,
|
||||
playbackOutput,
|
||||
"playbackdumps/prewarmingRenderer/"
|
||||
+ "twoItemPlaylist-withPrewarmingNonTransitioningRenderer"
|
||||
+ ".dump");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void playback_withImageVideoPlaylistAndSecondaryVideoRendererOnly_dumpsCorrectOutput()
|
||||
throws Exception {
|
||||
Context applicationContext = ApplicationProvider.getApplicationContext();
|
||||
CapturingRenderersWithSecondaryVideoAndImageRenderersFactory capturingRenderersFactory =
|
||||
new CapturingRenderersWithSecondaryVideoAndImageRenderersFactory(applicationContext);
|
||||
ExoPlayer player =
|
||||
new ExoPlayer.Builder(applicationContext, capturingRenderersFactory)
|
||||
.setClock(new FakeClock(/* isAutoAdvancing= */ true))
|
||||
.build();
|
||||
Surface surface = new Surface(new SurfaceTexture(/* texName= */ 1));
|
||||
player.setVideoSurface(surface);
|
||||
PlaybackOutput playbackOutput = PlaybackOutput.register(player, capturingRenderersFactory);
|
||||
player.addMediaItems(
|
||||
ImmutableList.of(
|
||||
new MediaItem.Builder().setUri(TEST_IMAGE_URI).setImageDurationMs(1000).build(),
|
||||
new MediaItem.Builder().setUri(TEST_MP4_URI).build(),
|
||||
new MediaItem.Builder().setUri(TEST_IMAGE_URI).setImageDurationMs(1000).build()));
|
||||
|
||||
player.prepare();
|
||||
player.play();
|
||||
TestPlayerRunHelper.runUntilPlaybackState(player, Player.STATE_ENDED);
|
||||
player.release();
|
||||
surface.release();
|
||||
|
||||
DumpFileAsserts.assertOutput(
|
||||
applicationContext,
|
||||
playbackOutput,
|
||||
/* dumpFile= */ "playbackdumps/prewarmingRenderer/"
|
||||
+ "imageVideoPlaylist-withSecondaryRenderers"
|
||||
+ ".dump");
|
||||
}
|
||||
|
||||
/** This class extends {@link CapturingRenderersFactory} to provide a secondary video renderer. */
|
||||
private static final class CapturingRenderersWithSecondaryVideoRendererFactory
|
||||
extends CapturingRenderersFactory {
|
||||
|
||||
/**
|
||||
* Creates an instance.
|
||||
*
|
||||
* @param context The {@link Context}.
|
||||
*/
|
||||
public CapturingRenderersWithSecondaryVideoRendererFactory(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Renderer createSecondaryRenderer(
|
||||
Renderer renderer,
|
||||
Handler eventHandler,
|
||||
VideoRendererEventListener videoRendererEventListener,
|
||||
AudioRendererEventListener audioRendererEventListener,
|
||||
TextOutput textRendererOutput,
|
||||
MetadataOutput metadataRendererOutput) {
|
||||
if (renderer.getClass().getSuperclass() == MediaCodecVideoRenderer.class) {
|
||||
return createMediaCodecVideoRenderer(eventHandler, videoRendererEventListener);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** This class extends {@link CapturingRenderersFactory} to provide a secondary video renderer. */
|
||||
private static final class CapturingRenderersWithSecondaryVideoAndImageRenderersFactory
|
||||
extends CapturingRenderersFactory {
|
||||
|
||||
/**
|
||||
* Creates an instance.
|
||||
*
|
||||
* @param context The {@link Context}.
|
||||
*/
|
||||
public CapturingRenderersWithSecondaryVideoAndImageRenderersFactory(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Renderer createSecondaryRenderer(
|
||||
Renderer renderer,
|
||||
Handler eventHandler,
|
||||
VideoRendererEventListener videoRendererEventListener,
|
||||
AudioRendererEventListener audioRendererEventListener,
|
||||
TextOutput textRendererOutput,
|
||||
MetadataOutput metadataRendererOutput) {
|
||||
if (renderer.getClass().getSuperclass() == MediaCodecVideoRenderer.class) {
|
||||
return createMediaCodecVideoRenderer(eventHandler, videoRendererEventListener);
|
||||
} else if (renderer.getClass() == ImageRenderer.class) {
|
||||
return new ImageRenderer(ImageDecoder.Factory.DEFAULT, /* imageOutput= */ null);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,692 @@
|
||||
MediaCodecAdapter (exotest.audio.aac):
|
||||
inputBuffers:
|
||||
count = 46
|
||||
input buffer #0:
|
||||
timeUs = 1000001044000
|
||||
contents = length 23, hash 47DE9131
|
||||
input buffer #1:
|
||||
timeUs = 1000001067219
|
||||
contents = length 6, hash 31EC5206
|
||||
input buffer #2:
|
||||
timeUs = 1000001090439
|
||||
contents = length 148, hash 894A176B
|
||||
input buffer #3:
|
||||
timeUs = 1000001113659
|
||||
contents = length 189, hash CEF235A1
|
||||
input buffer #4:
|
||||
timeUs = 1000001136879
|
||||
contents = length 205, hash BBF5F7B0
|
||||
input buffer #5:
|
||||
timeUs = 1000001160099
|
||||
contents = length 210, hash F278B193
|
||||
input buffer #6:
|
||||
timeUs = 1000001183319
|
||||
contents = length 210, hash 82DA1589
|
||||
input buffer #7:
|
||||
timeUs = 1000001206539
|
||||
contents = length 207, hash 5BE231DF
|
||||
input buffer #8:
|
||||
timeUs = 1000001229759
|
||||
contents = length 225, hash 18819EE1
|
||||
input buffer #9:
|
||||
timeUs = 1000001252979
|
||||
contents = length 215, hash CA7FA67B
|
||||
input buffer #10:
|
||||
timeUs = 1000001276199
|
||||
contents = length 211, hash 581A1C18
|
||||
input buffer #11:
|
||||
timeUs = 1000001299419
|
||||
contents = length 216, hash ADB88187
|
||||
input buffer #12:
|
||||
timeUs = 1000001322639
|
||||
contents = length 229, hash 2E8BA4DC
|
||||
input buffer #13:
|
||||
timeUs = 1000001345859
|
||||
contents = length 232, hash 22F0C510
|
||||
input buffer #14:
|
||||
timeUs = 1000001369079
|
||||
contents = length 235, hash 867AD0DC
|
||||
input buffer #15:
|
||||
timeUs = 1000001392299
|
||||
contents = length 231, hash 84E823A8
|
||||
input buffer #16:
|
||||
timeUs = 1000001415519
|
||||
contents = length 226, hash 1BEF3A95
|
||||
input buffer #17:
|
||||
timeUs = 1000001438739
|
||||
contents = length 216, hash EAA345AE
|
||||
input buffer #18:
|
||||
timeUs = 1000001461959
|
||||
contents = length 229, hash 6957411F
|
||||
input buffer #19:
|
||||
timeUs = 1000001485179
|
||||
contents = length 219, hash 41275022
|
||||
input buffer #20:
|
||||
timeUs = 1000001508399
|
||||
contents = length 241, hash 6495DF96
|
||||
input buffer #21:
|
||||
timeUs = 1000001531619
|
||||
contents = length 228, hash 63D95906
|
||||
input buffer #22:
|
||||
timeUs = 1000001554839
|
||||
contents = length 238, hash 34F676F9
|
||||
input buffer #23:
|
||||
timeUs = 1000001578058
|
||||
contents = length 234, hash E5CBC045
|
||||
input buffer #24:
|
||||
timeUs = 1000001601278
|
||||
contents = length 231, hash 5FC43661
|
||||
input buffer #25:
|
||||
timeUs = 1000001624498
|
||||
contents = length 217, hash 682708ED
|
||||
input buffer #26:
|
||||
timeUs = 1000001647718
|
||||
contents = length 239, hash D43780FC
|
||||
input buffer #27:
|
||||
timeUs = 1000001670938
|
||||
contents = length 243, hash C5E17980
|
||||
input buffer #28:
|
||||
timeUs = 1000001694158
|
||||
contents = length 231, hash AC5837BA
|
||||
input buffer #29:
|
||||
timeUs = 1000001717378
|
||||
contents = length 230, hash 169EE895
|
||||
input buffer #30:
|
||||
timeUs = 1000001740598
|
||||
contents = length 238, hash C48FF3F1
|
||||
input buffer #31:
|
||||
timeUs = 1000001763818
|
||||
contents = length 225, hash 531E4599
|
||||
input buffer #32:
|
||||
timeUs = 1000001787038
|
||||
contents = length 232, hash CB3C6B8D
|
||||
input buffer #33:
|
||||
timeUs = 1000001810258
|
||||
contents = length 243, hash F8C94C7
|
||||
input buffer #34:
|
||||
timeUs = 1000001833478
|
||||
contents = length 232, hash A646A7D0
|
||||
input buffer #35:
|
||||
timeUs = 1000001856698
|
||||
contents = length 237, hash E8B787A5
|
||||
input buffer #36:
|
||||
timeUs = 1000001879918
|
||||
contents = length 228, hash 3FA7A29F
|
||||
input buffer #37:
|
||||
timeUs = 1000001903138
|
||||
contents = length 235, hash B9B33B0A
|
||||
input buffer #38:
|
||||
timeUs = 1000001926358
|
||||
contents = length 264, hash 71A4869E
|
||||
input buffer #39:
|
||||
timeUs = 1000001949578
|
||||
contents = length 257, hash D049B54C
|
||||
input buffer #40:
|
||||
timeUs = 1000001972798
|
||||
contents = length 227, hash 66757231
|
||||
input buffer #41:
|
||||
timeUs = 1000001996018
|
||||
contents = length 227, hash BD374F1B
|
||||
input buffer #42:
|
||||
timeUs = 1000002019238
|
||||
contents = length 235, hash 999477F6
|
||||
input buffer #43:
|
||||
timeUs = 1000002042458
|
||||
contents = length 229, hash FFF98DF0
|
||||
input buffer #44:
|
||||
timeUs = 1000002065678
|
||||
contents = length 6, hash 31B22286
|
||||
input buffer #45:
|
||||
timeUs = 0
|
||||
flags = 4
|
||||
contents = length 0, hash 1
|
||||
outputBuffers:
|
||||
count = 45
|
||||
output buffer #0:
|
||||
timeUs = 1000001044000
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #1:
|
||||
timeUs = 1000001067219
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #2:
|
||||
timeUs = 1000001090439
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #3:
|
||||
timeUs = 1000001113659
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #4:
|
||||
timeUs = 1000001136879
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #5:
|
||||
timeUs = 1000001160099
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #6:
|
||||
timeUs = 1000001183319
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #7:
|
||||
timeUs = 1000001206539
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #8:
|
||||
timeUs = 1000001229759
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #9:
|
||||
timeUs = 1000001252979
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #10:
|
||||
timeUs = 1000001276199
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #11:
|
||||
timeUs = 1000001299419
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #12:
|
||||
timeUs = 1000001322639
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #13:
|
||||
timeUs = 1000001345859
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #14:
|
||||
timeUs = 1000001369079
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #15:
|
||||
timeUs = 1000001392299
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #16:
|
||||
timeUs = 1000001415519
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #17:
|
||||
timeUs = 1000001438739
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #18:
|
||||
timeUs = 1000001461959
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #19:
|
||||
timeUs = 1000001485179
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #20:
|
||||
timeUs = 1000001508399
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #21:
|
||||
timeUs = 1000001531619
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #22:
|
||||
timeUs = 1000001554839
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #23:
|
||||
timeUs = 1000001578058
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #24:
|
||||
timeUs = 1000001601278
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #25:
|
||||
timeUs = 1000001624498
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #26:
|
||||
timeUs = 1000001647718
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #27:
|
||||
timeUs = 1000001670938
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #28:
|
||||
timeUs = 1000001694158
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #29:
|
||||
timeUs = 1000001717378
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #30:
|
||||
timeUs = 1000001740598
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #31:
|
||||
timeUs = 1000001763818
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #32:
|
||||
timeUs = 1000001787038
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #33:
|
||||
timeUs = 1000001810258
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #34:
|
||||
timeUs = 1000001833478
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #35:
|
||||
timeUs = 1000001856698
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #36:
|
||||
timeUs = 1000001879918
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #37:
|
||||
timeUs = 1000001903138
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #38:
|
||||
timeUs = 1000001926358
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #39:
|
||||
timeUs = 1000001949578
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #40:
|
||||
timeUs = 1000001972798
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #41:
|
||||
timeUs = 1000001996018
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #42:
|
||||
timeUs = 1000002019238
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #43:
|
||||
timeUs = 1000002042458
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #44:
|
||||
timeUs = 1000002065678
|
||||
size = 0
|
||||
rendered = false
|
||||
MediaCodecAdapter (exotest.video.avc):
|
||||
inputBuffers:
|
||||
count = 31
|
||||
input buffer #0:
|
||||
timeUs = 1000001000000
|
||||
contents = length 36692, hash D216076E
|
||||
input buffer #1:
|
||||
timeUs = 1000001066733
|
||||
contents = length 5312, hash D45D3CA0
|
||||
input buffer #2:
|
||||
timeUs = 1000001033366
|
||||
contents = length 599, hash 1BE7812D
|
||||
input buffer #3:
|
||||
timeUs = 1000001200200
|
||||
contents = length 7735, hash 4490F110
|
||||
input buffer #4:
|
||||
timeUs = 1000001133466
|
||||
contents = length 987, hash 560B5036
|
||||
input buffer #5:
|
||||
timeUs = 1000001100100
|
||||
contents = length 673, hash ED7CD8C7
|
||||
input buffer #6:
|
||||
timeUs = 1000001166833
|
||||
contents = length 523, hash 3020DF50
|
||||
input buffer #7:
|
||||
timeUs = 1000001333666
|
||||
contents = length 6061, hash 736C72B2
|
||||
input buffer #8:
|
||||
timeUs = 1000001266933
|
||||
contents = length 992, hash FE132F23
|
||||
input buffer #9:
|
||||
timeUs = 1000001233566
|
||||
contents = length 623, hash 5B2C1816
|
||||
input buffer #10:
|
||||
timeUs = 1000001300300
|
||||
contents = length 421, hash 742E69C1
|
||||
input buffer #11:
|
||||
timeUs = 1000001433766
|
||||
contents = length 4899, hash F72F86A1
|
||||
input buffer #12:
|
||||
timeUs = 1000001400400
|
||||
contents = length 568, hash 519A8E50
|
||||
input buffer #13:
|
||||
timeUs = 1000001367033
|
||||
contents = length 620, hash 3990AA39
|
||||
input buffer #14:
|
||||
timeUs = 1000001567233
|
||||
contents = length 5450, hash F06EC4AA
|
||||
input buffer #15:
|
||||
timeUs = 1000001500500
|
||||
contents = length 1051, hash 92DFA63A
|
||||
input buffer #16:
|
||||
timeUs = 1000001467133
|
||||
contents = length 874, hash 69587FB4
|
||||
input buffer #17:
|
||||
timeUs = 1000001533866
|
||||
contents = length 781, hash 36BE495B
|
||||
input buffer #18:
|
||||
timeUs = 1000001700700
|
||||
contents = length 4725, hash AC0C8CD3
|
||||
input buffer #19:
|
||||
timeUs = 1000001633966
|
||||
contents = length 1022, hash 5D8BFF34
|
||||
input buffer #20:
|
||||
timeUs = 1000001600600
|
||||
contents = length 790, hash 99413A99
|
||||
input buffer #21:
|
||||
timeUs = 1000001667333
|
||||
contents = length 610, hash 5E129290
|
||||
input buffer #22:
|
||||
timeUs = 1000001834166
|
||||
contents = length 2751, hash 769974CB
|
||||
input buffer #23:
|
||||
timeUs = 1000001767433
|
||||
contents = length 745, hash B78A477A
|
||||
input buffer #24:
|
||||
timeUs = 1000001734066
|
||||
contents = length 621, hash CF741E7A
|
||||
input buffer #25:
|
||||
timeUs = 1000001800800
|
||||
contents = length 505, hash 1DB4894E
|
||||
input buffer #26:
|
||||
timeUs = 1000001967633
|
||||
contents = length 1268, hash C15348DC
|
||||
input buffer #27:
|
||||
timeUs = 1000001900900
|
||||
contents = length 880, hash C2DE85D0
|
||||
input buffer #28:
|
||||
timeUs = 1000001867533
|
||||
contents = length 530, hash C98BC6A8
|
||||
input buffer #29:
|
||||
timeUs = 1000001934266
|
||||
contents = length 568, hash 4FE5C8EA
|
||||
input buffer #30:
|
||||
timeUs = 0
|
||||
flags = 4
|
||||
contents = length 0, hash 1
|
||||
outputBuffers:
|
||||
count = 30
|
||||
output buffer #0:
|
||||
timeUs = 1000001000000
|
||||
size = 36692
|
||||
rendered = true
|
||||
output buffer #1:
|
||||
timeUs = 1000001066733
|
||||
size = 5312
|
||||
rendered = true
|
||||
output buffer #2:
|
||||
timeUs = 1000001033366
|
||||
size = 599
|
||||
rendered = true
|
||||
output buffer #3:
|
||||
timeUs = 1000001200200
|
||||
size = 7735
|
||||
rendered = true
|
||||
output buffer #4:
|
||||
timeUs = 1000001133466
|
||||
size = 987
|
||||
rendered = true
|
||||
output buffer #5:
|
||||
timeUs = 1000001100100
|
||||
size = 673
|
||||
rendered = true
|
||||
output buffer #6:
|
||||
timeUs = 1000001166833
|
||||
size = 523
|
||||
rendered = true
|
||||
output buffer #7:
|
||||
timeUs = 1000001333666
|
||||
size = 6061
|
||||
rendered = true
|
||||
output buffer #8:
|
||||
timeUs = 1000001266933
|
||||
size = 992
|
||||
rendered = true
|
||||
output buffer #9:
|
||||
timeUs = 1000001233566
|
||||
size = 623
|
||||
rendered = true
|
||||
output buffer #10:
|
||||
timeUs = 1000001300300
|
||||
size = 421
|
||||
rendered = true
|
||||
output buffer #11:
|
||||
timeUs = 1000001433766
|
||||
size = 4899
|
||||
rendered = true
|
||||
output buffer #12:
|
||||
timeUs = 1000001400400
|
||||
size = 568
|
||||
rendered = true
|
||||
output buffer #13:
|
||||
timeUs = 1000001367033
|
||||
size = 620
|
||||
rendered = true
|
||||
output buffer #14:
|
||||
timeUs = 1000001567233
|
||||
size = 5450
|
||||
rendered = true
|
||||
output buffer #15:
|
||||
timeUs = 1000001500500
|
||||
size = 1051
|
||||
rendered = true
|
||||
output buffer #16:
|
||||
timeUs = 1000001467133
|
||||
size = 874
|
||||
rendered = true
|
||||
output buffer #17:
|
||||
timeUs = 1000001533866
|
||||
size = 781
|
||||
rendered = true
|
||||
output buffer #18:
|
||||
timeUs = 1000001700700
|
||||
size = 4725
|
||||
rendered = true
|
||||
output buffer #19:
|
||||
timeUs = 1000001633966
|
||||
size = 1022
|
||||
rendered = true
|
||||
output buffer #20:
|
||||
timeUs = 1000001600600
|
||||
size = 790
|
||||
rendered = true
|
||||
output buffer #21:
|
||||
timeUs = 1000001667333
|
||||
size = 610
|
||||
rendered = true
|
||||
output buffer #22:
|
||||
timeUs = 1000001834166
|
||||
size = 2751
|
||||
rendered = true
|
||||
output buffer #23:
|
||||
timeUs = 1000001767433
|
||||
size = 745
|
||||
rendered = true
|
||||
output buffer #24:
|
||||
timeUs = 1000001734066
|
||||
size = 621
|
||||
rendered = true
|
||||
output buffer #25:
|
||||
timeUs = 1000001800800
|
||||
size = 505
|
||||
rendered = true
|
||||
output buffer #26:
|
||||
timeUs = 1000001967633
|
||||
size = 1268
|
||||
rendered = true
|
||||
output buffer #27:
|
||||
timeUs = 1000001900900
|
||||
size = 880
|
||||
rendered = true
|
||||
output buffer #28:
|
||||
timeUs = 1000001867533
|
||||
size = 530
|
||||
rendered = true
|
||||
output buffer #29:
|
||||
timeUs = 1000001934266
|
||||
size = 568
|
||||
rendered = true
|
||||
AudioSink:
|
||||
buffer count = 45
|
||||
config:
|
||||
pcmEncoding = 2
|
||||
channelCount = 1
|
||||
sampleRate = 44100
|
||||
buffer #0:
|
||||
time = 1000001044000
|
||||
data = 1
|
||||
buffer #1:
|
||||
time = 1000001067219
|
||||
data = 1
|
||||
buffer #2:
|
||||
time = 1000001090439
|
||||
data = 1
|
||||
buffer #3:
|
||||
time = 1000001113659
|
||||
data = 1
|
||||
buffer #4:
|
||||
time = 1000001136879
|
||||
data = 1
|
||||
buffer #5:
|
||||
time = 1000001160099
|
||||
data = 1
|
||||
buffer #6:
|
||||
time = 1000001183319
|
||||
data = 1
|
||||
buffer #7:
|
||||
time = 1000001206539
|
||||
data = 1
|
||||
buffer #8:
|
||||
time = 1000001229759
|
||||
data = 1
|
||||
buffer #9:
|
||||
time = 1000001252979
|
||||
data = 1
|
||||
buffer #10:
|
||||
time = 1000001276199
|
||||
data = 1
|
||||
buffer #11:
|
||||
time = 1000001299419
|
||||
data = 1
|
||||
buffer #12:
|
||||
time = 1000001322639
|
||||
data = 1
|
||||
buffer #13:
|
||||
time = 1000001345859
|
||||
data = 1
|
||||
buffer #14:
|
||||
time = 1000001369079
|
||||
data = 1
|
||||
buffer #15:
|
||||
time = 1000001392299
|
||||
data = 1
|
||||
buffer #16:
|
||||
time = 1000001415519
|
||||
data = 1
|
||||
buffer #17:
|
||||
time = 1000001438739
|
||||
data = 1
|
||||
buffer #18:
|
||||
time = 1000001461959
|
||||
data = 1
|
||||
buffer #19:
|
||||
time = 1000001485179
|
||||
data = 1
|
||||
buffer #20:
|
||||
time = 1000001508399
|
||||
data = 1
|
||||
buffer #21:
|
||||
time = 1000001531619
|
||||
data = 1
|
||||
buffer #22:
|
||||
time = 1000001554839
|
||||
data = 1
|
||||
buffer #23:
|
||||
time = 1000001578058
|
||||
data = 1
|
||||
buffer #24:
|
||||
time = 1000001601278
|
||||
data = 1
|
||||
buffer #25:
|
||||
time = 1000001624498
|
||||
data = 1
|
||||
buffer #26:
|
||||
time = 1000001647718
|
||||
data = 1
|
||||
buffer #27:
|
||||
time = 1000001670938
|
||||
data = 1
|
||||
buffer #28:
|
||||
time = 1000001694158
|
||||
data = 1
|
||||
buffer #29:
|
||||
time = 1000001717378
|
||||
data = 1
|
||||
buffer #30:
|
||||
time = 1000001740598
|
||||
data = 1
|
||||
buffer #31:
|
||||
time = 1000001763818
|
||||
data = 1
|
||||
buffer #32:
|
||||
time = 1000001787038
|
||||
data = 1
|
||||
buffer #33:
|
||||
time = 1000001810258
|
||||
data = 1
|
||||
buffer #34:
|
||||
time = 1000001833478
|
||||
data = 1
|
||||
buffer #35:
|
||||
time = 1000001856698
|
||||
data = 1
|
||||
buffer #36:
|
||||
time = 1000001879918
|
||||
data = 1
|
||||
buffer #37:
|
||||
time = 1000001903138
|
||||
data = 1
|
||||
buffer #38:
|
||||
time = 1000001926358
|
||||
data = 1
|
||||
buffer #39:
|
||||
time = 1000001949578
|
||||
data = 1
|
||||
buffer #40:
|
||||
time = 1000001972798
|
||||
data = 1
|
||||
buffer #41:
|
||||
time = 1000001996018
|
||||
data = 1
|
||||
buffer #42:
|
||||
time = 1000002019238
|
||||
data = 1
|
||||
buffer #43:
|
||||
time = 1000002042458
|
||||
data = 1
|
||||
buffer #44:
|
||||
time = 1000002065678
|
||||
data = 1
|
||||
ImageOutput:
|
||||
rendered image count = 2
|
||||
image output #1:
|
||||
presentationTimeUs = 0
|
||||
bitmap hash = -1041353260
|
||||
image output #2:
|
||||
presentationTimeUs = 0
|
||||
bitmap hash = -1041353260
|
@ -0,0 +1,895 @@
|
||||
MediaCodecAdapter (exotest.audio.aac):
|
||||
inputBuffers:
|
||||
count = 48
|
||||
input buffer #0:
|
||||
timeUs = 1000000044000
|
||||
contents = length 23, hash 47DE9131
|
||||
input buffer #1:
|
||||
timeUs = 1000000067219
|
||||
contents = length 6, hash 31EC5206
|
||||
input buffer #2:
|
||||
timeUs = 1000000090439
|
||||
contents = length 148, hash 894A176B
|
||||
input buffer #3:
|
||||
timeUs = 1000000113659
|
||||
contents = length 189, hash CEF235A1
|
||||
input buffer #4:
|
||||
timeUs = 1000000136879
|
||||
contents = length 205, hash BBF5F7B0
|
||||
input buffer #5:
|
||||
timeUs = 1000000160099
|
||||
contents = length 210, hash F278B193
|
||||
input buffer #6:
|
||||
timeUs = 1000000183319
|
||||
contents = length 210, hash 82DA1589
|
||||
input buffer #7:
|
||||
timeUs = 1000000206539
|
||||
contents = length 207, hash 5BE231DF
|
||||
input buffer #8:
|
||||
timeUs = 1000000229759
|
||||
contents = length 225, hash 18819EE1
|
||||
input buffer #9:
|
||||
timeUs = 0
|
||||
flags = 4
|
||||
contents = length 0, hash 1
|
||||
input buffer #10:
|
||||
timeUs = 1000000252979
|
||||
contents = length 215, hash CA7FA67B
|
||||
input buffer #11:
|
||||
timeUs = 1000000276199
|
||||
contents = length 211, hash 581A1C18
|
||||
input buffer #12:
|
||||
timeUs = 1000000299419
|
||||
contents = length 216, hash ADB88187
|
||||
input buffer #13:
|
||||
timeUs = 1000000322639
|
||||
contents = length 229, hash 2E8BA4DC
|
||||
input buffer #14:
|
||||
timeUs = 1000000345859
|
||||
contents = length 232, hash 22F0C510
|
||||
input buffer #15:
|
||||
timeUs = 1000000369079
|
||||
contents = length 235, hash 867AD0DC
|
||||
input buffer #16:
|
||||
timeUs = 1000000392299
|
||||
contents = length 231, hash 84E823A8
|
||||
input buffer #17:
|
||||
timeUs = 1000000415519
|
||||
contents = length 226, hash 1BEF3A95
|
||||
input buffer #18:
|
||||
timeUs = 1000000438739
|
||||
contents = length 216, hash EAA345AE
|
||||
input buffer #19:
|
||||
timeUs = 1000000461959
|
||||
contents = length 229, hash 6957411F
|
||||
input buffer #20:
|
||||
timeUs = 1000000485179
|
||||
contents = length 219, hash 41275022
|
||||
input buffer #21:
|
||||
timeUs = 1000000508399
|
||||
contents = length 241, hash 6495DF96
|
||||
input buffer #22:
|
||||
timeUs = 1000000531619
|
||||
contents = length 228, hash 63D95906
|
||||
input buffer #23:
|
||||
timeUs = 1000000554839
|
||||
contents = length 238, hash 34F676F9
|
||||
input buffer #24:
|
||||
timeUs = 1000000578058
|
||||
contents = length 234, hash E5CBC045
|
||||
input buffer #25:
|
||||
timeUs = 0
|
||||
flags = 4
|
||||
contents = length 0, hash 1
|
||||
input buffer #26:
|
||||
timeUs = 1000000601278
|
||||
contents = length 231, hash 5FC43661
|
||||
input buffer #27:
|
||||
timeUs = 1000000624498
|
||||
contents = length 217, hash 682708ED
|
||||
input buffer #28:
|
||||
timeUs = 1000000647718
|
||||
contents = length 239, hash D43780FC
|
||||
input buffer #29:
|
||||
timeUs = 1000000670938
|
||||
contents = length 243, hash C5E17980
|
||||
input buffer #30:
|
||||
timeUs = 1000000694158
|
||||
contents = length 231, hash AC5837BA
|
||||
input buffer #31:
|
||||
timeUs = 1000000717378
|
||||
contents = length 230, hash 169EE895
|
||||
input buffer #32:
|
||||
timeUs = 1000000740598
|
||||
contents = length 238, hash C48FF3F1
|
||||
input buffer #33:
|
||||
timeUs = 1000000763818
|
||||
contents = length 225, hash 531E4599
|
||||
input buffer #34:
|
||||
timeUs = 1000000787038
|
||||
contents = length 232, hash CB3C6B8D
|
||||
input buffer #35:
|
||||
timeUs = 1000000810258
|
||||
contents = length 243, hash F8C94C7
|
||||
input buffer #36:
|
||||
timeUs = 1000000833478
|
||||
contents = length 232, hash A646A7D0
|
||||
input buffer #37:
|
||||
timeUs = 1000000856698
|
||||
contents = length 237, hash E8B787A5
|
||||
input buffer #38:
|
||||
timeUs = 1000000879918
|
||||
contents = length 228, hash 3FA7A29F
|
||||
input buffer #39:
|
||||
timeUs = 1000000903138
|
||||
contents = length 235, hash B9B33B0A
|
||||
input buffer #40:
|
||||
timeUs = 1000000926358
|
||||
contents = length 264, hash 71A4869E
|
||||
input buffer #41:
|
||||
timeUs = 1000000949578
|
||||
contents = length 257, hash D049B54C
|
||||
input buffer #42:
|
||||
timeUs = 1000000972798
|
||||
contents = length 227, hash 66757231
|
||||
input buffer #43:
|
||||
timeUs = 1000000996018
|
||||
contents = length 227, hash BD374F1B
|
||||
input buffer #44:
|
||||
timeUs = 1000001019238
|
||||
contents = length 235, hash 999477F6
|
||||
input buffer #45:
|
||||
timeUs = 1000001042458
|
||||
contents = length 229, hash FFF98DF0
|
||||
input buffer #46:
|
||||
timeUs = 1000001065678
|
||||
contents = length 6, hash 31B22286
|
||||
input buffer #47:
|
||||
timeUs = 0
|
||||
flags = 4
|
||||
contents = length 0, hash 1
|
||||
outputBuffers:
|
||||
count = 45
|
||||
output buffer #0:
|
||||
timeUs = 1000000044000
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #1:
|
||||
timeUs = 1000000067219
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #2:
|
||||
timeUs = 1000000090439
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #3:
|
||||
timeUs = 1000000113659
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #4:
|
||||
timeUs = 1000000136879
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #5:
|
||||
timeUs = 1000000160099
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #6:
|
||||
timeUs = 1000000183319
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #7:
|
||||
timeUs = 1000000206539
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #8:
|
||||
timeUs = 1000000229759
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #9:
|
||||
timeUs = 1000000252979
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #10:
|
||||
timeUs = 1000000276199
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #11:
|
||||
timeUs = 1000000299419
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #12:
|
||||
timeUs = 1000000322639
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #13:
|
||||
timeUs = 1000000345859
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #14:
|
||||
timeUs = 1000000369079
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #15:
|
||||
timeUs = 1000000392299
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #16:
|
||||
timeUs = 1000000415519
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #17:
|
||||
timeUs = 1000000438739
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #18:
|
||||
timeUs = 1000000461959
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #19:
|
||||
timeUs = 1000000485179
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #20:
|
||||
timeUs = 1000000508399
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #21:
|
||||
timeUs = 1000000531619
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #22:
|
||||
timeUs = 1000000554839
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #23:
|
||||
timeUs = 1000000578058
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #24:
|
||||
timeUs = 1000000601278
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #25:
|
||||
timeUs = 1000000624498
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #26:
|
||||
timeUs = 1000000647718
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #27:
|
||||
timeUs = 1000000670938
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #28:
|
||||
timeUs = 1000000694158
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #29:
|
||||
timeUs = 1000000717378
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #30:
|
||||
timeUs = 1000000740598
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #31:
|
||||
timeUs = 1000000763818
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #32:
|
||||
timeUs = 1000000787038
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #33:
|
||||
timeUs = 1000000810258
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #34:
|
||||
timeUs = 1000000833478
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #35:
|
||||
timeUs = 1000000856698
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #36:
|
||||
timeUs = 1000000879918
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #37:
|
||||
timeUs = 1000000903138
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #38:
|
||||
timeUs = 1000000926358
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #39:
|
||||
timeUs = 1000000949578
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #40:
|
||||
timeUs = 1000000972798
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #41:
|
||||
timeUs = 1000000996018
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #42:
|
||||
timeUs = 1000001019238
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #43:
|
||||
timeUs = 1000001042458
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #44:
|
||||
timeUs = 1000001065678
|
||||
size = 0
|
||||
rendered = false
|
||||
MediaCodecAdapter (exotest.video.avc):
|
||||
inputBuffers:
|
||||
count = 8
|
||||
input buffer #0:
|
||||
timeUs = 1000000000000
|
||||
contents = length 36692, hash D216076E
|
||||
input buffer #1:
|
||||
timeUs = 1000000066733
|
||||
contents = length 5312, hash D45D3CA0
|
||||
input buffer #2:
|
||||
timeUs = 1000000033366
|
||||
contents = length 599, hash 1BE7812D
|
||||
input buffer #3:
|
||||
timeUs = 1000000200200
|
||||
contents = length 7735, hash 4490F110
|
||||
input buffer #4:
|
||||
timeUs = 1000000133466
|
||||
contents = length 987, hash 560B5036
|
||||
input buffer #5:
|
||||
timeUs = 1000000100100
|
||||
contents = length 673, hash ED7CD8C7
|
||||
input buffer #6:
|
||||
timeUs = 1000000166833
|
||||
contents = length 523, hash 3020DF50
|
||||
input buffer #7:
|
||||
timeUs = 0
|
||||
flags = 4
|
||||
contents = length 0, hash 1
|
||||
outputBuffers:
|
||||
count = 7
|
||||
output buffer #0:
|
||||
timeUs = 1000000000000
|
||||
size = 36692
|
||||
rendered = true
|
||||
output buffer #1:
|
||||
timeUs = 1000000066733
|
||||
size = 5312
|
||||
rendered = true
|
||||
output buffer #2:
|
||||
timeUs = 1000000033366
|
||||
size = 599
|
||||
rendered = true
|
||||
output buffer #3:
|
||||
timeUs = 1000000200200
|
||||
size = 7735
|
||||
rendered = true
|
||||
output buffer #4:
|
||||
timeUs = 1000000133466
|
||||
size = 987
|
||||
rendered = true
|
||||
output buffer #5:
|
||||
timeUs = 1000000100100
|
||||
size = 673
|
||||
rendered = true
|
||||
output buffer #6:
|
||||
timeUs = 1000000166833
|
||||
size = 523
|
||||
rendered = true
|
||||
MediaCodecAdapter (exotest.video.avc):
|
||||
inputBuffers:
|
||||
count = 19
|
||||
input buffer #0:
|
||||
timeUs = 1000000000000
|
||||
contents = length 36692, hash D216076E
|
||||
input buffer #1:
|
||||
timeUs = 1000000066733
|
||||
contents = length 5312, hash D45D3CA0
|
||||
input buffer #2:
|
||||
timeUs = 1000000033366
|
||||
contents = length 599, hash 1BE7812D
|
||||
input buffer #3:
|
||||
timeUs = 1000000200200
|
||||
contents = length 7735, hash 4490F110
|
||||
input buffer #4:
|
||||
timeUs = 1000000133466
|
||||
contents = length 987, hash 560B5036
|
||||
input buffer #5:
|
||||
timeUs = 1000000100100
|
||||
contents = length 673, hash ED7CD8C7
|
||||
input buffer #6:
|
||||
timeUs = 1000000166833
|
||||
contents = length 523, hash 3020DF50
|
||||
input buffer #7:
|
||||
timeUs = 1000000333666
|
||||
contents = length 6061, hash 736C72B2
|
||||
input buffer #8:
|
||||
timeUs = 1000000266933
|
||||
contents = length 992, hash FE132F23
|
||||
input buffer #9:
|
||||
timeUs = 1000000233566
|
||||
contents = length 623, hash 5B2C1816
|
||||
input buffer #10:
|
||||
timeUs = 1000000300300
|
||||
contents = length 421, hash 742E69C1
|
||||
input buffer #11:
|
||||
timeUs = 1000000433766
|
||||
contents = length 4899, hash F72F86A1
|
||||
input buffer #12:
|
||||
timeUs = 1000000400400
|
||||
contents = length 568, hash 519A8E50
|
||||
input buffer #13:
|
||||
timeUs = 1000000367033
|
||||
contents = length 620, hash 3990AA39
|
||||
input buffer #14:
|
||||
timeUs = 1000000567233
|
||||
contents = length 5450, hash F06EC4AA
|
||||
input buffer #15:
|
||||
timeUs = 1000000500500
|
||||
contents = length 1051, hash 92DFA63A
|
||||
input buffer #16:
|
||||
timeUs = 1000000467133
|
||||
contents = length 874, hash 69587FB4
|
||||
input buffer #17:
|
||||
timeUs = 1000000533866
|
||||
contents = length 781, hash 36BE495B
|
||||
input buffer #18:
|
||||
timeUs = 0
|
||||
flags = 4
|
||||
contents = length 0, hash 1
|
||||
outputBuffers:
|
||||
count = 18
|
||||
output buffer #0:
|
||||
timeUs = 1000000000000
|
||||
size = 36692
|
||||
rendered = false
|
||||
output buffer #1:
|
||||
timeUs = 1000000066733
|
||||
size = 5312
|
||||
rendered = false
|
||||
output buffer #2:
|
||||
timeUs = 1000000033366
|
||||
size = 599
|
||||
rendered = false
|
||||
output buffer #3:
|
||||
timeUs = 1000000200200
|
||||
size = 7735
|
||||
rendered = false
|
||||
output buffer #4:
|
||||
timeUs = 1000000133466
|
||||
size = 987
|
||||
rendered = false
|
||||
output buffer #5:
|
||||
timeUs = 1000000100100
|
||||
size = 673
|
||||
rendered = false
|
||||
output buffer #6:
|
||||
timeUs = 1000000166833
|
||||
size = 523
|
||||
rendered = false
|
||||
output buffer #7:
|
||||
timeUs = 1000000333666
|
||||
size = 6061
|
||||
rendered = false
|
||||
output buffer #8:
|
||||
timeUs = 1000000266933
|
||||
size = 992
|
||||
rendered = false
|
||||
output buffer #9:
|
||||
timeUs = 1000000233566
|
||||
size = 623
|
||||
rendered = false
|
||||
output buffer #10:
|
||||
timeUs = 1000000300300
|
||||
size = 421
|
||||
rendered = false
|
||||
output buffer #11:
|
||||
timeUs = 1000000433766
|
||||
size = 4899
|
||||
rendered = false
|
||||
output buffer #12:
|
||||
timeUs = 1000000400400
|
||||
size = 568
|
||||
rendered = false
|
||||
output buffer #13:
|
||||
timeUs = 1000000367033
|
||||
size = 620
|
||||
rendered = false
|
||||
output buffer #14:
|
||||
timeUs = 1000000567233
|
||||
size = 5450
|
||||
rendered = false
|
||||
output buffer #15:
|
||||
timeUs = 1000000500500
|
||||
size = 1051
|
||||
rendered = false
|
||||
output buffer #16:
|
||||
timeUs = 1000000467133
|
||||
size = 874
|
||||
rendered = false
|
||||
output buffer #17:
|
||||
timeUs = 1000000533866
|
||||
size = 781
|
||||
rendered = false
|
||||
MediaCodecAdapter (exotest.video.avc):
|
||||
inputBuffers:
|
||||
count = 31
|
||||
input buffer #0:
|
||||
timeUs = 1000000000000
|
||||
contents = length 36692, hash D216076E
|
||||
input buffer #1:
|
||||
timeUs = 1000000066733
|
||||
contents = length 5312, hash D45D3CA0
|
||||
input buffer #2:
|
||||
timeUs = 1000000033366
|
||||
contents = length 599, hash 1BE7812D
|
||||
input buffer #3:
|
||||
timeUs = 1000000200200
|
||||
contents = length 7735, hash 4490F110
|
||||
input buffer #4:
|
||||
timeUs = 1000000133466
|
||||
contents = length 987, hash 560B5036
|
||||
input buffer #5:
|
||||
timeUs = 1000000100100
|
||||
contents = length 673, hash ED7CD8C7
|
||||
input buffer #6:
|
||||
timeUs = 1000000166833
|
||||
contents = length 523, hash 3020DF50
|
||||
input buffer #7:
|
||||
timeUs = 1000000333666
|
||||
contents = length 6061, hash 736C72B2
|
||||
input buffer #8:
|
||||
timeUs = 1000000266933
|
||||
contents = length 992, hash FE132F23
|
||||
input buffer #9:
|
||||
timeUs = 1000000233566
|
||||
contents = length 623, hash 5B2C1816
|
||||
input buffer #10:
|
||||
timeUs = 1000000300300
|
||||
contents = length 421, hash 742E69C1
|
||||
input buffer #11:
|
||||
timeUs = 1000000433766
|
||||
contents = length 4899, hash F72F86A1
|
||||
input buffer #12:
|
||||
timeUs = 1000000400400
|
||||
contents = length 568, hash 519A8E50
|
||||
input buffer #13:
|
||||
timeUs = 1000000367033
|
||||
contents = length 620, hash 3990AA39
|
||||
input buffer #14:
|
||||
timeUs = 1000000567233
|
||||
contents = length 5450, hash F06EC4AA
|
||||
input buffer #15:
|
||||
timeUs = 1000000500500
|
||||
contents = length 1051, hash 92DFA63A
|
||||
input buffer #16:
|
||||
timeUs = 1000000467133
|
||||
contents = length 874, hash 69587FB4
|
||||
input buffer #17:
|
||||
timeUs = 1000000533866
|
||||
contents = length 781, hash 36BE495B
|
||||
input buffer #18:
|
||||
timeUs = 1000000700700
|
||||
contents = length 4725, hash AC0C8CD3
|
||||
input buffer #19:
|
||||
timeUs = 1000000633966
|
||||
contents = length 1022, hash 5D8BFF34
|
||||
input buffer #20:
|
||||
timeUs = 1000000600600
|
||||
contents = length 790, hash 99413A99
|
||||
input buffer #21:
|
||||
timeUs = 1000000667333
|
||||
contents = length 610, hash 5E129290
|
||||
input buffer #22:
|
||||
timeUs = 1000000834166
|
||||
contents = length 2751, hash 769974CB
|
||||
input buffer #23:
|
||||
timeUs = 1000000767433
|
||||
contents = length 745, hash B78A477A
|
||||
input buffer #24:
|
||||
timeUs = 1000000734066
|
||||
contents = length 621, hash CF741E7A
|
||||
input buffer #25:
|
||||
timeUs = 1000000800800
|
||||
contents = length 505, hash 1DB4894E
|
||||
input buffer #26:
|
||||
timeUs = 1000000967633
|
||||
contents = length 1268, hash C15348DC
|
||||
input buffer #27:
|
||||
timeUs = 1000000900900
|
||||
contents = length 880, hash C2DE85D0
|
||||
input buffer #28:
|
||||
timeUs = 1000000867533
|
||||
contents = length 530, hash C98BC6A8
|
||||
input buffer #29:
|
||||
timeUs = 1000000934266
|
||||
contents = length 568, hash 4FE5C8EA
|
||||
input buffer #30:
|
||||
timeUs = 0
|
||||
flags = 4
|
||||
contents = length 0, hash 1
|
||||
outputBuffers:
|
||||
count = 30
|
||||
output buffer #0:
|
||||
timeUs = 1000000000000
|
||||
size = 36692
|
||||
rendered = false
|
||||
output buffer #1:
|
||||
timeUs = 1000000066733
|
||||
size = 5312
|
||||
rendered = false
|
||||
output buffer #2:
|
||||
timeUs = 1000000033366
|
||||
size = 599
|
||||
rendered = false
|
||||
output buffer #3:
|
||||
timeUs = 1000000200200
|
||||
size = 7735
|
||||
rendered = false
|
||||
output buffer #4:
|
||||
timeUs = 1000000133466
|
||||
size = 987
|
||||
rendered = false
|
||||
output buffer #5:
|
||||
timeUs = 1000000100100
|
||||
size = 673
|
||||
rendered = false
|
||||
output buffer #6:
|
||||
timeUs = 1000000166833
|
||||
size = 523
|
||||
rendered = false
|
||||
output buffer #7:
|
||||
timeUs = 1000000333666
|
||||
size = 6061
|
||||
rendered = false
|
||||
output buffer #8:
|
||||
timeUs = 1000000266933
|
||||
size = 992
|
||||
rendered = false
|
||||
output buffer #9:
|
||||
timeUs = 1000000233566
|
||||
size = 623
|
||||
rendered = false
|
||||
output buffer #10:
|
||||
timeUs = 1000000300300
|
||||
size = 421
|
||||
rendered = false
|
||||
output buffer #11:
|
||||
timeUs = 1000000433766
|
||||
size = 4899
|
||||
rendered = false
|
||||
output buffer #12:
|
||||
timeUs = 1000000400400
|
||||
size = 568
|
||||
rendered = false
|
||||
output buffer #13:
|
||||
timeUs = 1000000367033
|
||||
size = 620
|
||||
rendered = false
|
||||
output buffer #14:
|
||||
timeUs = 1000000567233
|
||||
size = 5450
|
||||
rendered = false
|
||||
output buffer #15:
|
||||
timeUs = 1000000500500
|
||||
size = 1051
|
||||
rendered = false
|
||||
output buffer #16:
|
||||
timeUs = 1000000467133
|
||||
size = 874
|
||||
rendered = false
|
||||
output buffer #17:
|
||||
timeUs = 1000000533866
|
||||
size = 781
|
||||
rendered = false
|
||||
output buffer #18:
|
||||
timeUs = 1000000700700
|
||||
size = 4725
|
||||
rendered = true
|
||||
output buffer #19:
|
||||
timeUs = 1000000633966
|
||||
size = 1022
|
||||
rendered = true
|
||||
output buffer #20:
|
||||
timeUs = 1000000600600
|
||||
size = 790
|
||||
rendered = true
|
||||
output buffer #21:
|
||||
timeUs = 1000000667333
|
||||
size = 610
|
||||
rendered = true
|
||||
output buffer #22:
|
||||
timeUs = 1000000834166
|
||||
size = 2751
|
||||
rendered = true
|
||||
output buffer #23:
|
||||
timeUs = 1000000767433
|
||||
size = 745
|
||||
rendered = true
|
||||
output buffer #24:
|
||||
timeUs = 1000000734066
|
||||
size = 621
|
||||
rendered = true
|
||||
output buffer #25:
|
||||
timeUs = 1000000800800
|
||||
size = 505
|
||||
rendered = true
|
||||
output buffer #26:
|
||||
timeUs = 1000000967633
|
||||
size = 1268
|
||||
rendered = true
|
||||
output buffer #27:
|
||||
timeUs = 1000000900900
|
||||
size = 880
|
||||
rendered = true
|
||||
output buffer #28:
|
||||
timeUs = 1000000867533
|
||||
size = 530
|
||||
rendered = true
|
||||
output buffer #29:
|
||||
timeUs = 1000000934266
|
||||
size = 568
|
||||
rendered = true
|
||||
AudioSink:
|
||||
buffer count = 45
|
||||
config:
|
||||
pcmEncoding = 2
|
||||
channelCount = 1
|
||||
sampleRate = 44100
|
||||
buffer #0:
|
||||
time = 1000000044000
|
||||
data = 1
|
||||
buffer #1:
|
||||
time = 1000000067219
|
||||
data = 1
|
||||
buffer #2:
|
||||
time = 1000000090439
|
||||
data = 1
|
||||
buffer #3:
|
||||
time = 1000000113659
|
||||
data = 1
|
||||
buffer #4:
|
||||
time = 1000000136879
|
||||
data = 1
|
||||
buffer #5:
|
||||
time = 1000000160099
|
||||
data = 1
|
||||
buffer #6:
|
||||
time = 1000000183319
|
||||
data = 1
|
||||
buffer #7:
|
||||
time = 1000000206539
|
||||
data = 1
|
||||
buffer #8:
|
||||
time = 1000000229759
|
||||
data = 1
|
||||
discontinuity:
|
||||
config:
|
||||
pcmEncoding = 2
|
||||
channelCount = 1
|
||||
sampleRate = 44100
|
||||
buffer #9:
|
||||
time = 1000000252979
|
||||
data = 1
|
||||
buffer #10:
|
||||
time = 1000000276199
|
||||
data = 1
|
||||
buffer #11:
|
||||
time = 1000000299419
|
||||
data = 1
|
||||
buffer #12:
|
||||
time = 1000000322639
|
||||
data = 1
|
||||
buffer #13:
|
||||
time = 1000000345859
|
||||
data = 1
|
||||
buffer #14:
|
||||
time = 1000000369079
|
||||
data = 1
|
||||
buffer #15:
|
||||
time = 1000000392299
|
||||
data = 1
|
||||
buffer #16:
|
||||
time = 1000000415519
|
||||
data = 1
|
||||
buffer #17:
|
||||
time = 1000000438739
|
||||
data = 1
|
||||
buffer #18:
|
||||
time = 1000000461959
|
||||
data = 1
|
||||
buffer #19:
|
||||
time = 1000000485179
|
||||
data = 1
|
||||
buffer #20:
|
||||
time = 1000000508399
|
||||
data = 1
|
||||
buffer #21:
|
||||
time = 1000000531619
|
||||
data = 1
|
||||
buffer #22:
|
||||
time = 1000000554839
|
||||
data = 1
|
||||
buffer #23:
|
||||
time = 1000000578058
|
||||
data = 1
|
||||
discontinuity:
|
||||
config:
|
||||
pcmEncoding = 2
|
||||
channelCount = 1
|
||||
sampleRate = 44100
|
||||
buffer #24:
|
||||
time = 1000000601278
|
||||
data = 1
|
||||
buffer #25:
|
||||
time = 1000000624498
|
||||
data = 1
|
||||
buffer #26:
|
||||
time = 1000000647718
|
||||
data = 1
|
||||
buffer #27:
|
||||
time = 1000000670938
|
||||
data = 1
|
||||
buffer #28:
|
||||
time = 1000000694158
|
||||
data = 1
|
||||
buffer #29:
|
||||
time = 1000000717378
|
||||
data = 1
|
||||
buffer #30:
|
||||
time = 1000000740598
|
||||
data = 1
|
||||
buffer #31:
|
||||
time = 1000000763818
|
||||
data = 1
|
||||
buffer #32:
|
||||
time = 1000000787038
|
||||
data = 1
|
||||
buffer #33:
|
||||
time = 1000000810258
|
||||
data = 1
|
||||
buffer #34:
|
||||
time = 1000000833478
|
||||
data = 1
|
||||
buffer #35:
|
||||
time = 1000000856698
|
||||
data = 1
|
||||
buffer #36:
|
||||
time = 1000000879918
|
||||
data = 1
|
||||
buffer #37:
|
||||
time = 1000000903138
|
||||
data = 1
|
||||
buffer #38:
|
||||
time = 1000000926358
|
||||
data = 1
|
||||
buffer #39:
|
||||
time = 1000000949578
|
||||
data = 1
|
||||
buffer #40:
|
||||
time = 1000000972798
|
||||
data = 1
|
||||
buffer #41:
|
||||
time = 1000000996018
|
||||
data = 1
|
||||
buffer #42:
|
||||
time = 1000001019238
|
||||
data = 1
|
||||
buffer #43:
|
||||
time = 1000001042458
|
||||
data = 1
|
||||
buffer #44:
|
||||
time = 1000001065678
|
||||
data = 1
|
@ -0,0 +1,657 @@
|
||||
MediaCodecAdapter (exotest.video.avc):
|
||||
inputBuffers:
|
||||
count = 31
|
||||
input buffer #0:
|
||||
timeUs = 1000000000000
|
||||
contents = length 36692, hash D216076E
|
||||
input buffer #1:
|
||||
timeUs = 1000000066733
|
||||
contents = length 5312, hash D45D3CA0
|
||||
input buffer #2:
|
||||
timeUs = 1000000033366
|
||||
contents = length 599, hash 1BE7812D
|
||||
input buffer #3:
|
||||
timeUs = 1000000200200
|
||||
contents = length 7735, hash 4490F110
|
||||
input buffer #4:
|
||||
timeUs = 1000000133466
|
||||
contents = length 987, hash 560B5036
|
||||
input buffer #5:
|
||||
timeUs = 1000000100100
|
||||
contents = length 673, hash ED7CD8C7
|
||||
input buffer #6:
|
||||
timeUs = 1000000166833
|
||||
contents = length 523, hash 3020DF50
|
||||
input buffer #7:
|
||||
timeUs = 1000000333666
|
||||
contents = length 6061, hash 736C72B2
|
||||
input buffer #8:
|
||||
timeUs = 1000000266933
|
||||
contents = length 992, hash FE132F23
|
||||
input buffer #9:
|
||||
timeUs = 1000000233566
|
||||
contents = length 623, hash 5B2C1816
|
||||
input buffer #10:
|
||||
timeUs = 1000000300300
|
||||
contents = length 421, hash 742E69C1
|
||||
input buffer #11:
|
||||
timeUs = 1000000433766
|
||||
contents = length 4899, hash F72F86A1
|
||||
input buffer #12:
|
||||
timeUs = 1000000400400
|
||||
contents = length 568, hash 519A8E50
|
||||
input buffer #13:
|
||||
timeUs = 1000000367033
|
||||
contents = length 620, hash 3990AA39
|
||||
input buffer #14:
|
||||
timeUs = 1000000567233
|
||||
contents = length 5450, hash F06EC4AA
|
||||
input buffer #15:
|
||||
timeUs = 1000000500500
|
||||
contents = length 1051, hash 92DFA63A
|
||||
input buffer #16:
|
||||
timeUs = 1000000467133
|
||||
contents = length 874, hash 69587FB4
|
||||
input buffer #17:
|
||||
timeUs = 1000000533866
|
||||
contents = length 781, hash 36BE495B
|
||||
input buffer #18:
|
||||
timeUs = 1000000700700
|
||||
contents = length 4725, hash AC0C8CD3
|
||||
input buffer #19:
|
||||
timeUs = 1000000633966
|
||||
contents = length 1022, hash 5D8BFF34
|
||||
input buffer #20:
|
||||
timeUs = 1000000600600
|
||||
contents = length 790, hash 99413A99
|
||||
input buffer #21:
|
||||
timeUs = 1000000667333
|
||||
contents = length 610, hash 5E129290
|
||||
input buffer #22:
|
||||
timeUs = 1000000834166
|
||||
contents = length 2751, hash 769974CB
|
||||
input buffer #23:
|
||||
timeUs = 1000000767433
|
||||
contents = length 745, hash B78A477A
|
||||
input buffer #24:
|
||||
timeUs = 1000000734066
|
||||
contents = length 621, hash CF741E7A
|
||||
input buffer #25:
|
||||
timeUs = 1000000800800
|
||||
contents = length 505, hash 1DB4894E
|
||||
input buffer #26:
|
||||
timeUs = 1000000967633
|
||||
contents = length 1268, hash C15348DC
|
||||
input buffer #27:
|
||||
timeUs = 1000000900900
|
||||
contents = length 880, hash C2DE85D0
|
||||
input buffer #28:
|
||||
timeUs = 1000000867533
|
||||
contents = length 530, hash C98BC6A8
|
||||
input buffer #29:
|
||||
timeUs = 1000000934266
|
||||
contents = length 568, hash 4FE5C8EA
|
||||
input buffer #30:
|
||||
timeUs = 0
|
||||
flags = 4
|
||||
contents = length 0, hash 1
|
||||
outputBuffers:
|
||||
count = 30
|
||||
output buffer #0:
|
||||
timeUs = 1000000000000
|
||||
size = 36692
|
||||
rendered = true
|
||||
output buffer #1:
|
||||
timeUs = 1000000066733
|
||||
size = 5312
|
||||
rendered = true
|
||||
output buffer #2:
|
||||
timeUs = 1000000033366
|
||||
size = 599
|
||||
rendered = true
|
||||
output buffer #3:
|
||||
timeUs = 1000000200200
|
||||
size = 7735
|
||||
rendered = true
|
||||
output buffer #4:
|
||||
timeUs = 1000000133466
|
||||
size = 987
|
||||
rendered = true
|
||||
output buffer #5:
|
||||
timeUs = 1000000100100
|
||||
size = 673
|
||||
rendered = true
|
||||
output buffer #6:
|
||||
timeUs = 1000000166833
|
||||
size = 523
|
||||
rendered = true
|
||||
output buffer #7:
|
||||
timeUs = 1000000333666
|
||||
size = 6061
|
||||
rendered = true
|
||||
output buffer #8:
|
||||
timeUs = 1000000266933
|
||||
size = 992
|
||||
rendered = true
|
||||
output buffer #9:
|
||||
timeUs = 1000000233566
|
||||
size = 623
|
||||
rendered = true
|
||||
output buffer #10:
|
||||
timeUs = 1000000300300
|
||||
size = 421
|
||||
rendered = true
|
||||
output buffer #11:
|
||||
timeUs = 1000000433766
|
||||
size = 4899
|
||||
rendered = true
|
||||
output buffer #12:
|
||||
timeUs = 1000000400400
|
||||
size = 568
|
||||
rendered = true
|
||||
output buffer #13:
|
||||
timeUs = 1000000367033
|
||||
size = 620
|
||||
rendered = true
|
||||
output buffer #14:
|
||||
timeUs = 1000000567233
|
||||
size = 5450
|
||||
rendered = true
|
||||
output buffer #15:
|
||||
timeUs = 1000000500500
|
||||
size = 1051
|
||||
rendered = true
|
||||
output buffer #16:
|
||||
timeUs = 1000000467133
|
||||
size = 874
|
||||
rendered = true
|
||||
output buffer #17:
|
||||
timeUs = 1000000533866
|
||||
size = 781
|
||||
rendered = true
|
||||
output buffer #18:
|
||||
timeUs = 1000000700700
|
||||
size = 4725
|
||||
rendered = true
|
||||
output buffer #19:
|
||||
timeUs = 1000000633966
|
||||
size = 1022
|
||||
rendered = true
|
||||
output buffer #20:
|
||||
timeUs = 1000000600600
|
||||
size = 790
|
||||
rendered = true
|
||||
output buffer #21:
|
||||
timeUs = 1000000667333
|
||||
size = 610
|
||||
rendered = true
|
||||
output buffer #22:
|
||||
timeUs = 1000000834166
|
||||
size = 2751
|
||||
rendered = true
|
||||
output buffer #23:
|
||||
timeUs = 1000000767433
|
||||
size = 745
|
||||
rendered = true
|
||||
output buffer #24:
|
||||
timeUs = 1000000734066
|
||||
size = 621
|
||||
rendered = true
|
||||
output buffer #25:
|
||||
timeUs = 1000000800800
|
||||
size = 505
|
||||
rendered = true
|
||||
output buffer #26:
|
||||
timeUs = 1000000967633
|
||||
size = 1268
|
||||
rendered = true
|
||||
output buffer #27:
|
||||
timeUs = 1000000900900
|
||||
size = 880
|
||||
rendered = true
|
||||
output buffer #28:
|
||||
timeUs = 1000000867533
|
||||
size = 530
|
||||
rendered = true
|
||||
output buffer #29:
|
||||
timeUs = 1000000934266
|
||||
size = 568
|
||||
rendered = true
|
||||
MediaCodecAdapter (exotest.video.avc):
|
||||
inputBuffers:
|
||||
count = 31
|
||||
input buffer #0:
|
||||
timeUs = 1000001024000
|
||||
contents = length 36692, hash D216076E
|
||||
input buffer #1:
|
||||
timeUs = 1000001090733
|
||||
contents = length 5312, hash D45D3CA0
|
||||
input buffer #2:
|
||||
timeUs = 1000001057366
|
||||
contents = length 599, hash 1BE7812D
|
||||
input buffer #3:
|
||||
timeUs = 1000001224200
|
||||
contents = length 7735, hash 4490F110
|
||||
input buffer #4:
|
||||
timeUs = 1000001157466
|
||||
contents = length 987, hash 560B5036
|
||||
input buffer #5:
|
||||
timeUs = 1000001124100
|
||||
contents = length 673, hash ED7CD8C7
|
||||
input buffer #6:
|
||||
timeUs = 1000001190833
|
||||
contents = length 523, hash 3020DF50
|
||||
input buffer #7:
|
||||
timeUs = 1000001357666
|
||||
contents = length 6061, hash 736C72B2
|
||||
input buffer #8:
|
||||
timeUs = 1000001290933
|
||||
contents = length 992, hash FE132F23
|
||||
input buffer #9:
|
||||
timeUs = 1000001257566
|
||||
contents = length 623, hash 5B2C1816
|
||||
input buffer #10:
|
||||
timeUs = 1000001324300
|
||||
contents = length 421, hash 742E69C1
|
||||
input buffer #11:
|
||||
timeUs = 1000001457766
|
||||
contents = length 4899, hash F72F86A1
|
||||
input buffer #12:
|
||||
timeUs = 1000001424400
|
||||
contents = length 568, hash 519A8E50
|
||||
input buffer #13:
|
||||
timeUs = 1000001391033
|
||||
contents = length 620, hash 3990AA39
|
||||
input buffer #14:
|
||||
timeUs = 1000001591233
|
||||
contents = length 5450, hash F06EC4AA
|
||||
input buffer #15:
|
||||
timeUs = 1000001524500
|
||||
contents = length 1051, hash 92DFA63A
|
||||
input buffer #16:
|
||||
timeUs = 1000001491133
|
||||
contents = length 874, hash 69587FB4
|
||||
input buffer #17:
|
||||
timeUs = 1000001557866
|
||||
contents = length 781, hash 36BE495B
|
||||
input buffer #18:
|
||||
timeUs = 1000001724700
|
||||
contents = length 4725, hash AC0C8CD3
|
||||
input buffer #19:
|
||||
timeUs = 1000001657966
|
||||
contents = length 1022, hash 5D8BFF34
|
||||
input buffer #20:
|
||||
timeUs = 1000001624600
|
||||
contents = length 790, hash 99413A99
|
||||
input buffer #21:
|
||||
timeUs = 1000001691333
|
||||
contents = length 610, hash 5E129290
|
||||
input buffer #22:
|
||||
timeUs = 1000001858166
|
||||
contents = length 2751, hash 769974CB
|
||||
input buffer #23:
|
||||
timeUs = 1000001791433
|
||||
contents = length 745, hash B78A477A
|
||||
input buffer #24:
|
||||
timeUs = 1000001758066
|
||||
contents = length 621, hash CF741E7A
|
||||
input buffer #25:
|
||||
timeUs = 1000001824800
|
||||
contents = length 505, hash 1DB4894E
|
||||
input buffer #26:
|
||||
timeUs = 1000001991633
|
||||
contents = length 1268, hash C15348DC
|
||||
input buffer #27:
|
||||
timeUs = 1000001924900
|
||||
contents = length 880, hash C2DE85D0
|
||||
input buffer #28:
|
||||
timeUs = 1000001891533
|
||||
contents = length 530, hash C98BC6A8
|
||||
input buffer #29:
|
||||
timeUs = 1000001958266
|
||||
contents = length 568, hash 4FE5C8EA
|
||||
input buffer #30:
|
||||
timeUs = 0
|
||||
flags = 4
|
||||
contents = length 0, hash 1
|
||||
outputBuffers:
|
||||
count = 30
|
||||
output buffer #0:
|
||||
timeUs = 1000001024000
|
||||
size = 36692
|
||||
rendered = false
|
||||
output buffer #1:
|
||||
timeUs = 1000001090733
|
||||
size = 5312
|
||||
rendered = false
|
||||
output buffer #2:
|
||||
timeUs = 1000001057366
|
||||
size = 599
|
||||
rendered = false
|
||||
output buffer #3:
|
||||
timeUs = 1000001224200
|
||||
size = 7735
|
||||
rendered = false
|
||||
output buffer #4:
|
||||
timeUs = 1000001157466
|
||||
size = 987
|
||||
rendered = false
|
||||
output buffer #5:
|
||||
timeUs = 1000001124100
|
||||
size = 673
|
||||
rendered = false
|
||||
output buffer #6:
|
||||
timeUs = 1000001190833
|
||||
size = 523
|
||||
rendered = false
|
||||
output buffer #7:
|
||||
timeUs = 1000001357666
|
||||
size = 6061
|
||||
rendered = false
|
||||
output buffer #8:
|
||||
timeUs = 1000001290933
|
||||
size = 992
|
||||
rendered = false
|
||||
output buffer #9:
|
||||
timeUs = 1000001257566
|
||||
size = 623
|
||||
rendered = false
|
||||
output buffer #10:
|
||||
timeUs = 1000001324300
|
||||
size = 421
|
||||
rendered = false
|
||||
output buffer #11:
|
||||
timeUs = 1000001457766
|
||||
size = 4899
|
||||
rendered = false
|
||||
output buffer #12:
|
||||
timeUs = 1000001424400
|
||||
size = 568
|
||||
rendered = false
|
||||
output buffer #13:
|
||||
timeUs = 1000001391033
|
||||
size = 620
|
||||
rendered = false
|
||||
output buffer #14:
|
||||
timeUs = 1000001591233
|
||||
size = 5450
|
||||
rendered = false
|
||||
output buffer #15:
|
||||
timeUs = 1000001524500
|
||||
size = 1051
|
||||
rendered = false
|
||||
output buffer #16:
|
||||
timeUs = 1000001491133
|
||||
size = 874
|
||||
rendered = false
|
||||
output buffer #17:
|
||||
timeUs = 1000001557866
|
||||
size = 781
|
||||
rendered = false
|
||||
output buffer #18:
|
||||
timeUs = 1000001724700
|
||||
size = 4725
|
||||
rendered = false
|
||||
output buffer #19:
|
||||
timeUs = 1000001657966
|
||||
size = 1022
|
||||
rendered = false
|
||||
output buffer #20:
|
||||
timeUs = 1000001624600
|
||||
size = 790
|
||||
rendered = false
|
||||
output buffer #21:
|
||||
timeUs = 1000001691333
|
||||
size = 610
|
||||
rendered = false
|
||||
output buffer #22:
|
||||
timeUs = 1000001858166
|
||||
size = 2751
|
||||
rendered = false
|
||||
output buffer #23:
|
||||
timeUs = 1000001791433
|
||||
size = 745
|
||||
rendered = false
|
||||
output buffer #24:
|
||||
timeUs = 1000001758066
|
||||
size = 621
|
||||
rendered = false
|
||||
output buffer #25:
|
||||
timeUs = 1000001824800
|
||||
size = 505
|
||||
rendered = false
|
||||
output buffer #26:
|
||||
timeUs = 1000001991633
|
||||
size = 1268
|
||||
rendered = false
|
||||
output buffer #27:
|
||||
timeUs = 1000001924900
|
||||
size = 880
|
||||
rendered = false
|
||||
output buffer #28:
|
||||
timeUs = 1000001891533
|
||||
size = 530
|
||||
rendered = false
|
||||
output buffer #29:
|
||||
timeUs = 1000001958266
|
||||
size = 568
|
||||
rendered = false
|
||||
MediaCodecAdapter (exotest.video.avc):
|
||||
inputBuffers:
|
||||
count = 31
|
||||
input buffer #0:
|
||||
timeUs = 1000002048000
|
||||
contents = length 36692, hash D216076E
|
||||
input buffer #1:
|
||||
timeUs = 1000002114733
|
||||
contents = length 5312, hash D45D3CA0
|
||||
input buffer #2:
|
||||
timeUs = 1000002081366
|
||||
contents = length 599, hash 1BE7812D
|
||||
input buffer #3:
|
||||
timeUs = 1000002248200
|
||||
contents = length 7735, hash 4490F110
|
||||
input buffer #4:
|
||||
timeUs = 1000002181466
|
||||
contents = length 987, hash 560B5036
|
||||
input buffer #5:
|
||||
timeUs = 1000002148100
|
||||
contents = length 673, hash ED7CD8C7
|
||||
input buffer #6:
|
||||
timeUs = 1000002214833
|
||||
contents = length 523, hash 3020DF50
|
||||
input buffer #7:
|
||||
timeUs = 1000002381666
|
||||
contents = length 6061, hash 736C72B2
|
||||
input buffer #8:
|
||||
timeUs = 1000002314933
|
||||
contents = length 992, hash FE132F23
|
||||
input buffer #9:
|
||||
timeUs = 1000002281566
|
||||
contents = length 623, hash 5B2C1816
|
||||
input buffer #10:
|
||||
timeUs = 1000002348300
|
||||
contents = length 421, hash 742E69C1
|
||||
input buffer #11:
|
||||
timeUs = 1000002481766
|
||||
contents = length 4899, hash F72F86A1
|
||||
input buffer #12:
|
||||
timeUs = 1000002448400
|
||||
contents = length 568, hash 519A8E50
|
||||
input buffer #13:
|
||||
timeUs = 1000002415033
|
||||
contents = length 620, hash 3990AA39
|
||||
input buffer #14:
|
||||
timeUs = 1000002615233
|
||||
contents = length 5450, hash F06EC4AA
|
||||
input buffer #15:
|
||||
timeUs = 1000002548500
|
||||
contents = length 1051, hash 92DFA63A
|
||||
input buffer #16:
|
||||
timeUs = 1000002515133
|
||||
contents = length 874, hash 69587FB4
|
||||
input buffer #17:
|
||||
timeUs = 1000002581866
|
||||
contents = length 781, hash 36BE495B
|
||||
input buffer #18:
|
||||
timeUs = 1000002748700
|
||||
contents = length 4725, hash AC0C8CD3
|
||||
input buffer #19:
|
||||
timeUs = 1000002681966
|
||||
contents = length 1022, hash 5D8BFF34
|
||||
input buffer #20:
|
||||
timeUs = 1000002648600
|
||||
contents = length 790, hash 99413A99
|
||||
input buffer #21:
|
||||
timeUs = 1000002715333
|
||||
contents = length 610, hash 5E129290
|
||||
input buffer #22:
|
||||
timeUs = 1000002882166
|
||||
contents = length 2751, hash 769974CB
|
||||
input buffer #23:
|
||||
timeUs = 1000002815433
|
||||
contents = length 745, hash B78A477A
|
||||
input buffer #24:
|
||||
timeUs = 1000002782066
|
||||
contents = length 621, hash CF741E7A
|
||||
input buffer #25:
|
||||
timeUs = 1000002848800
|
||||
contents = length 505, hash 1DB4894E
|
||||
input buffer #26:
|
||||
timeUs = 1000003015633
|
||||
contents = length 1268, hash C15348DC
|
||||
input buffer #27:
|
||||
timeUs = 1000002948900
|
||||
contents = length 880, hash C2DE85D0
|
||||
input buffer #28:
|
||||
timeUs = 1000002915533
|
||||
contents = length 530, hash C98BC6A8
|
||||
input buffer #29:
|
||||
timeUs = 1000002982266
|
||||
contents = length 568, hash 4FE5C8EA
|
||||
input buffer #30:
|
||||
timeUs = 0
|
||||
flags = 4
|
||||
contents = length 0, hash 1
|
||||
outputBuffers:
|
||||
count = 30
|
||||
output buffer #0:
|
||||
timeUs = 1000002048000
|
||||
size = 36692
|
||||
rendered = true
|
||||
output buffer #1:
|
||||
timeUs = 1000002114733
|
||||
size = 5312
|
||||
rendered = true
|
||||
output buffer #2:
|
||||
timeUs = 1000002081366
|
||||
size = 599
|
||||
rendered = true
|
||||
output buffer #3:
|
||||
timeUs = 1000002248200
|
||||
size = 7735
|
||||
rendered = true
|
||||
output buffer #4:
|
||||
timeUs = 1000002181466
|
||||
size = 987
|
||||
rendered = true
|
||||
output buffer #5:
|
||||
timeUs = 1000002148100
|
||||
size = 673
|
||||
rendered = true
|
||||
output buffer #6:
|
||||
timeUs = 1000002214833
|
||||
size = 523
|
||||
rendered = true
|
||||
output buffer #7:
|
||||
timeUs = 1000002381666
|
||||
size = 6061
|
||||
rendered = true
|
||||
output buffer #8:
|
||||
timeUs = 1000002314933
|
||||
size = 992
|
||||
rendered = true
|
||||
output buffer #9:
|
||||
timeUs = 1000002281566
|
||||
size = 623
|
||||
rendered = true
|
||||
output buffer #10:
|
||||
timeUs = 1000002348300
|
||||
size = 421
|
||||
rendered = true
|
||||
output buffer #11:
|
||||
timeUs = 1000002481766
|
||||
size = 4899
|
||||
rendered = true
|
||||
output buffer #12:
|
||||
timeUs = 1000002448400
|
||||
size = 568
|
||||
rendered = true
|
||||
output buffer #13:
|
||||
timeUs = 1000002415033
|
||||
size = 620
|
||||
rendered = true
|
||||
output buffer #14:
|
||||
timeUs = 1000002615233
|
||||
size = 5450
|
||||
rendered = true
|
||||
output buffer #15:
|
||||
timeUs = 1000002548500
|
||||
size = 1051
|
||||
rendered = true
|
||||
output buffer #16:
|
||||
timeUs = 1000002515133
|
||||
size = 874
|
||||
rendered = true
|
||||
output buffer #17:
|
||||
timeUs = 1000002581866
|
||||
size = 781
|
||||
rendered = true
|
||||
output buffer #18:
|
||||
timeUs = 1000002748700
|
||||
size = 4725
|
||||
rendered = true
|
||||
output buffer #19:
|
||||
timeUs = 1000002681966
|
||||
size = 1022
|
||||
rendered = true
|
||||
output buffer #20:
|
||||
timeUs = 1000002648600
|
||||
size = 790
|
||||
rendered = true
|
||||
output buffer #21:
|
||||
timeUs = 1000002715333
|
||||
size = 610
|
||||
rendered = true
|
||||
output buffer #22:
|
||||
timeUs = 1000002882166
|
||||
size = 2751
|
||||
rendered = true
|
||||
output buffer #23:
|
||||
timeUs = 1000002815433
|
||||
size = 745
|
||||
rendered = true
|
||||
output buffer #24:
|
||||
timeUs = 1000002782066
|
||||
size = 621
|
||||
rendered = true
|
||||
output buffer #25:
|
||||
timeUs = 1000002848800
|
||||
size = 505
|
||||
rendered = true
|
||||
output buffer #26:
|
||||
timeUs = 1000003015633
|
||||
size = 1268
|
||||
rendered = true
|
||||
output buffer #27:
|
||||
timeUs = 1000002948900
|
||||
size = 880
|
||||
rendered = true
|
||||
output buffer #28:
|
||||
timeUs = 1000002915533
|
||||
size = 530
|
||||
rendered = true
|
||||
output buffer #29:
|
||||
timeUs = 1000002982266
|
||||
size = 568
|
||||
rendered = true
|
@ -0,0 +1,374 @@
|
||||
MediaCodecAdapter (exotest.audio.aac):
|
||||
inputBuffers:
|
||||
count = 13
|
||||
input buffer #0:
|
||||
timeUs = 1000000310972
|
||||
contents = length 229, hash 2E8BA4DC
|
||||
input buffer #1:
|
||||
timeUs = 1000000334192
|
||||
contents = length 232, hash 22F0C510
|
||||
input buffer #2:
|
||||
timeUs = 1000000357412
|
||||
contents = length 235, hash 867AD0DC
|
||||
input buffer #3:
|
||||
timeUs = 1000000380632
|
||||
contents = length 231, hash 84E823A8
|
||||
input buffer #4:
|
||||
timeUs = 1000000403852
|
||||
contents = length 226, hash 1BEF3A95
|
||||
input buffer #5:
|
||||
timeUs = 1000000427072
|
||||
contents = length 216, hash EAA345AE
|
||||
input buffer #6:
|
||||
timeUs = 1000000450292
|
||||
contents = length 229, hash 6957411F
|
||||
input buffer #7:
|
||||
timeUs = 1000000473512
|
||||
contents = length 219, hash 41275022
|
||||
input buffer #8:
|
||||
timeUs = 1000000496732
|
||||
contents = length 241, hash 6495DF96
|
||||
input buffer #9:
|
||||
timeUs = 1000000519952
|
||||
contents = length 228, hash 63D95906
|
||||
input buffer #10:
|
||||
timeUs = 1000000543172
|
||||
contents = length 238, hash 34F676F9
|
||||
input buffer #11:
|
||||
timeUs = 1000000566391
|
||||
contents = length 234, hash E5CBC045
|
||||
input buffer #12:
|
||||
timeUs = 0
|
||||
flags = 4
|
||||
contents = length 0, hash 1
|
||||
outputBuffers:
|
||||
count = 12
|
||||
output buffer #0:
|
||||
timeUs = 1000000310972
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #1:
|
||||
timeUs = 1000000334192
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #2:
|
||||
timeUs = 1000000357412
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #3:
|
||||
timeUs = 1000000380632
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #4:
|
||||
timeUs = 1000000403852
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #5:
|
||||
timeUs = 1000000427072
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #6:
|
||||
timeUs = 1000000450292
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #7:
|
||||
timeUs = 1000000473512
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #8:
|
||||
timeUs = 1000000496732
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #9:
|
||||
timeUs = 1000000519952
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #10:
|
||||
timeUs = 1000000543172
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #11:
|
||||
timeUs = 1000000566391
|
||||
size = 0
|
||||
rendered = false
|
||||
MediaCodecAdapter (exotest.audio.ac3):
|
||||
inputBuffers:
|
||||
count = 10
|
||||
input buffer #0:
|
||||
timeUs = 1000000000000
|
||||
contents = length 1536, hash 7108D5C2
|
||||
input buffer #1:
|
||||
timeUs = 1000000032000
|
||||
contents = length 1536, hash 80BF3B34
|
||||
input buffer #2:
|
||||
timeUs = 1000000064000
|
||||
contents = length 1536, hash 5D09685
|
||||
input buffer #3:
|
||||
timeUs = 1000000096000
|
||||
contents = length 1536, hash A9A24E44
|
||||
input buffer #4:
|
||||
timeUs = 1000000128000
|
||||
contents = length 1536, hash 6F856273
|
||||
input buffer #5:
|
||||
timeUs = 1000000160000
|
||||
contents = length 1536, hash B1737D3C
|
||||
input buffer #6:
|
||||
timeUs = 1000000192000
|
||||
contents = length 1536, hash 98FDEB9D
|
||||
input buffer #7:
|
||||
timeUs = 1000000224000
|
||||
contents = length 1536, hash 99B9B943
|
||||
input buffer #8:
|
||||
timeUs = 1000000256000
|
||||
contents = length 1536, hash AAD9FCD2
|
||||
input buffer #9:
|
||||
timeUs = 0
|
||||
flags = 4
|
||||
contents = length 0, hash 1
|
||||
outputBuffers:
|
||||
count = 9
|
||||
output buffer #0:
|
||||
timeUs = 1000000000000
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #1:
|
||||
timeUs = 1000000032000
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #2:
|
||||
timeUs = 1000000064000
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #3:
|
||||
timeUs = 1000000096000
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #4:
|
||||
timeUs = 1000000128000
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #5:
|
||||
timeUs = 1000000160000
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #6:
|
||||
timeUs = 1000000192000
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #7:
|
||||
timeUs = 1000000224000
|
||||
size = 0
|
||||
rendered = false
|
||||
output buffer #8:
|
||||
timeUs = 1000000256000
|
||||
size = 0
|
||||
rendered = false
|
||||
MediaCodecAdapter (exotest.video.avc):
|
||||
inputBuffers:
|
||||
count = 19
|
||||
input buffer #0:
|
||||
timeUs = 999999988333
|
||||
contents = length 36692, hash D216076E
|
||||
input buffer #1:
|
||||
timeUs = 1000000055066
|
||||
contents = length 5312, hash D45D3CA0
|
||||
input buffer #2:
|
||||
timeUs = 1000000021699
|
||||
contents = length 599, hash 1BE7812D
|
||||
input buffer #3:
|
||||
timeUs = 1000000188533
|
||||
contents = length 7735, hash 4490F110
|
||||
input buffer #4:
|
||||
timeUs = 1000000121799
|
||||
contents = length 987, hash 560B5036
|
||||
input buffer #5:
|
||||
timeUs = 1000000088433
|
||||
contents = length 673, hash ED7CD8C7
|
||||
input buffer #6:
|
||||
timeUs = 1000000155166
|
||||
contents = length 523, hash 3020DF50
|
||||
input buffer #7:
|
||||
timeUs = 1000000321999
|
||||
contents = length 6061, hash 736C72B2
|
||||
input buffer #8:
|
||||
timeUs = 1000000255266
|
||||
contents = length 992, hash FE132F23
|
||||
input buffer #9:
|
||||
timeUs = 1000000221899
|
||||
contents = length 623, hash 5B2C1816
|
||||
input buffer #10:
|
||||
timeUs = 1000000288633
|
||||
contents = length 421, hash 742E69C1
|
||||
input buffer #11:
|
||||
timeUs = 1000000422099
|
||||
contents = length 4899, hash F72F86A1
|
||||
input buffer #12:
|
||||
timeUs = 1000000388733
|
||||
contents = length 568, hash 519A8E50
|
||||
input buffer #13:
|
||||
timeUs = 1000000355366
|
||||
contents = length 620, hash 3990AA39
|
||||
input buffer #14:
|
||||
timeUs = 1000000555566
|
||||
contents = length 5450, hash F06EC4AA
|
||||
input buffer #15:
|
||||
timeUs = 1000000488833
|
||||
contents = length 1051, hash 92DFA63A
|
||||
input buffer #16:
|
||||
timeUs = 1000000455466
|
||||
contents = length 874, hash 69587FB4
|
||||
input buffer #17:
|
||||
timeUs = 1000000522199
|
||||
contents = length 781, hash 36BE495B
|
||||
input buffer #18:
|
||||
timeUs = 0
|
||||
flags = 4
|
||||
contents = length 0, hash 1
|
||||
outputBuffers:
|
||||
count = 18
|
||||
output buffer #0:
|
||||
timeUs = 999999988333
|
||||
size = 36692
|
||||
rendered = false
|
||||
output buffer #1:
|
||||
timeUs = 1000000055066
|
||||
size = 5312
|
||||
rendered = false
|
||||
output buffer #2:
|
||||
timeUs = 1000000021699
|
||||
size = 599
|
||||
rendered = false
|
||||
output buffer #3:
|
||||
timeUs = 1000000188533
|
||||
size = 7735
|
||||
rendered = false
|
||||
output buffer #4:
|
||||
timeUs = 1000000121799
|
||||
size = 987
|
||||
rendered = false
|
||||
output buffer #5:
|
||||
timeUs = 1000000088433
|
||||
size = 673
|
||||
rendered = false
|
||||
output buffer #6:
|
||||
timeUs = 1000000155166
|
||||
size = 523
|
||||
rendered = false
|
||||
output buffer #7:
|
||||
timeUs = 1000000321999
|
||||
size = 6061
|
||||
rendered = true
|
||||
output buffer #8:
|
||||
timeUs = 1000000255266
|
||||
size = 992
|
||||
rendered = false
|
||||
output buffer #9:
|
||||
timeUs = 1000000221899
|
||||
size = 623
|
||||
rendered = false
|
||||
output buffer #10:
|
||||
timeUs = 1000000288633
|
||||
size = 421
|
||||
rendered = true
|
||||
output buffer #11:
|
||||
timeUs = 1000000422099
|
||||
size = 4899
|
||||
rendered = true
|
||||
output buffer #12:
|
||||
timeUs = 1000000388733
|
||||
size = 568
|
||||
rendered = true
|
||||
output buffer #13:
|
||||
timeUs = 1000000355366
|
||||
size = 620
|
||||
rendered = true
|
||||
output buffer #14:
|
||||
timeUs = 1000000555566
|
||||
size = 5450
|
||||
rendered = true
|
||||
output buffer #15:
|
||||
timeUs = 1000000488833
|
||||
size = 1051
|
||||
rendered = true
|
||||
output buffer #16:
|
||||
timeUs = 1000000455466
|
||||
size = 874
|
||||
rendered = true
|
||||
output buffer #17:
|
||||
timeUs = 1000000522199
|
||||
size = 781
|
||||
rendered = true
|
||||
AudioSink:
|
||||
buffer count = 21
|
||||
config:
|
||||
pcmEncoding = 2
|
||||
channelCount = 6
|
||||
sampleRate = 48000
|
||||
buffer #0:
|
||||
time = 1000000000000
|
||||
data = 1
|
||||
buffer #1:
|
||||
time = 1000000032000
|
||||
data = 1
|
||||
buffer #2:
|
||||
time = 1000000064000
|
||||
data = 1
|
||||
buffer #3:
|
||||
time = 1000000096000
|
||||
data = 1
|
||||
buffer #4:
|
||||
time = 1000000128000
|
||||
data = 1
|
||||
buffer #5:
|
||||
time = 1000000160000
|
||||
data = 1
|
||||
buffer #6:
|
||||
time = 1000000192000
|
||||
data = 1
|
||||
buffer #7:
|
||||
time = 1000000224000
|
||||
data = 1
|
||||
buffer #8:
|
||||
time = 1000000256000
|
||||
data = 1
|
||||
discontinuity:
|
||||
config:
|
||||
pcmEncoding = 2
|
||||
channelCount = 1
|
||||
sampleRate = 44100
|
||||
buffer #9:
|
||||
time = 1000000310972
|
||||
data = 1
|
||||
buffer #10:
|
||||
time = 1000000334192
|
||||
data = 1
|
||||
buffer #11:
|
||||
time = 1000000357412
|
||||
data = 1
|
||||
buffer #12:
|
||||
time = 1000000380632
|
||||
data = 1
|
||||
buffer #13:
|
||||
time = 1000000403852
|
||||
data = 1
|
||||
buffer #14:
|
||||
time = 1000000427072
|
||||
data = 1
|
||||
buffer #15:
|
||||
time = 1000000450292
|
||||
data = 1
|
||||
buffer #16:
|
||||
time = 1000000473512
|
||||
data = 1
|
||||
buffer #17:
|
||||
time = 1000000496732
|
||||
data = 1
|
||||
buffer #18:
|
||||
time = 1000000519952
|
||||
data = 1
|
||||
buffer #19:
|
||||
time = 1000000543172
|
||||
data = 1
|
||||
buffer #20:
|
||||
time = 1000000566391
|
||||
data = 1
|
File diff suppressed because it is too large
Load Diff
@ -126,7 +126,7 @@ public class CapturingRenderersFactory implements RenderersFactory, Dumper.Dumpa
|
||||
MetadataOutput metadataRendererOutput) {
|
||||
ArrayList<Renderer> renderers = new ArrayList<>();
|
||||
renderers.add(
|
||||
new MediaCodecVideoRenderer(
|
||||
new CapturingMediaCodecVideoRenderer(
|
||||
context,
|
||||
mediaCodecAdapterFactory,
|
||||
MediaCodecSelector.DEFAULT,
|
||||
@ -134,27 +134,7 @@ public class CapturingRenderersFactory implements RenderersFactory, Dumper.Dumpa
|
||||
/* enableDecoderFallback= */ false,
|
||||
eventHandler,
|
||||
videoRendererEventListener,
|
||||
DefaultRenderersFactory.MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY) {
|
||||
@Override
|
||||
protected boolean shouldDropOutputBuffer(
|
||||
long earlyUs, long elapsedRealtimeUs, boolean isLastBuffer) {
|
||||
// Do not drop output buffers due to slow processing.
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldDropBuffersToKeyframe(
|
||||
long earlyUs, long elapsedRealtimeUs, boolean isLastBuffer) {
|
||||
// Do not drop output buffers due to slow processing.
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldSkipBuffersWithIdenticalReleaseTime() {
|
||||
// Do not skip buffers with identical vsync times as we can't control this from tests.
|
||||
return false;
|
||||
}
|
||||
});
|
||||
DefaultRenderersFactory.MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY));
|
||||
renderers.add(
|
||||
new MediaCodecAudioRenderer(
|
||||
context,
|
||||
@ -190,6 +170,72 @@ public class CapturingRenderersFactory implements RenderersFactory, Dumper.Dumpa
|
||||
Renderer create(TextOutput textOutput, Looper outputLooper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns new instance of a specialized {@link MediaCodecVideoRenderer} that will not drop or
|
||||
* skip buffers due to slow processing.
|
||||
*
|
||||
* @param eventHandler A handler to use when invoking event listeners and outputs.
|
||||
* @param videoRendererEventListener An event listener for video renderers.
|
||||
* @return a new instance of a specialized {@link MediaCodecVideoRenderer}.
|
||||
*/
|
||||
protected MediaCodecVideoRenderer createMediaCodecVideoRenderer(
|
||||
Handler eventHandler, VideoRendererEventListener videoRendererEventListener) {
|
||||
return new CapturingMediaCodecVideoRenderer(
|
||||
context,
|
||||
mediaCodecAdapterFactory,
|
||||
MediaCodecSelector.DEFAULT,
|
||||
DefaultRenderersFactory.DEFAULT_ALLOWED_VIDEO_JOINING_TIME_MS,
|
||||
/* enableDecoderFallback= */ false,
|
||||
eventHandler,
|
||||
videoRendererEventListener,
|
||||
DefaultRenderersFactory.MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY);
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link MediaCodecVideoRenderer} that will not skip or drop buffers due to slow processing.
|
||||
*/
|
||||
private static class CapturingMediaCodecVideoRenderer extends MediaCodecVideoRenderer {
|
||||
private CapturingMediaCodecVideoRenderer(
|
||||
Context context,
|
||||
MediaCodecAdapter.Factory codecAdapterFactory,
|
||||
MediaCodecSelector mediaCodecSelector,
|
||||
long allowedJoiningTimeMs,
|
||||
boolean enableDecoderFallback,
|
||||
@Nullable Handler eventHandler,
|
||||
@Nullable VideoRendererEventListener eventListener,
|
||||
int maxDroppedFramesToNotify) {
|
||||
super(
|
||||
context,
|
||||
codecAdapterFactory,
|
||||
mediaCodecSelector,
|
||||
allowedJoiningTimeMs,
|
||||
enableDecoderFallback,
|
||||
eventHandler,
|
||||
eventListener,
|
||||
maxDroppedFramesToNotify);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldDropOutputBuffer(
|
||||
long earlyUs, long elapsedRealtimeUs, boolean isLastBuffer) {
|
||||
// Do not drop output buffers due to slow processing.
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldDropBuffersToKeyframe(
|
||||
long earlyUs, long elapsedRealtimeUs, boolean isLastBuffer) {
|
||||
// Do not drop output buffers due to slow processing.
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldSkipBuffersWithIdenticalReleaseTime() {
|
||||
// Do not skip buffers with identical vsync times as we can't control this from tests.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link MediaCodecAdapter} that captures interactions and exposes them for test assertions via
|
||||
* {@link Dumper.Dumpable}.
|
||||
|
Loading…
x
Reference in New Issue
Block a user