Video processing offset in AnalyticsListener

PiperOrigin-RevId: 295146481
This commit is contained in:
christosts 2020-02-14 15:56:21 +00:00 committed by Ian Baker
parent 5104548204
commit 0a612ce34a
12 changed files with 146 additions and 119 deletions

View File

@ -3,6 +3,8 @@
### dev-v2 (not yet released) ### ### dev-v2 (not yet released) ###
* Core library: * Core library:
* Add API in `AnalyticsListener` to report video frame processing offset.
`MediaCodecVideoRenderer` reports the event.
* Add fields `videoFrameProcessingOffsetUsSum` and * Add fields `videoFrameProcessingOffsetUsSum` and
`videoFrameProcessingOffsetUsCount` in `DecoderCounters` to compute `videoFrameProcessingOffsetUsCount` in `DecoderCounters` to compute
the average video frame processing offset. the average video frame processing offset.

View File

@ -1775,6 +1775,15 @@ public class SimpleExoPlayer extends BasePlayer
videoDecoderCounters = null; videoDecoderCounters = null;
} }
@Override
public void onVideoFrameProcessingOffset(
long totalProcessingOffsetUs, int frameCount, Format format) {
for (VideoRendererEventListener videoDebugListener : videoDebugListeners) {
videoDebugListener.onVideoFrameProcessingOffset(
totalProcessingOffsetUs, frameCount, format);
}
}
// AudioRendererEventListener implementation // AudioRendererEventListener implementation
@Override @Override

View File

@ -281,6 +281,15 @@ public class AnalyticsCollector
} }
} }
@Override
public final void onVideoFrameProcessingOffset(
long totalProcessingOffsetUs, int frameCount, Format format) {
EventTime eventTime = generatePlayingMediaPeriodEventTime();
for (AnalyticsListener listener : listeners) {
listener.onVideoFrameProcessingOffset(eventTime, totalProcessingOffsetUs, frameCount, format);
}
}
// VideoListener implementation. // VideoListener implementation.
@Override @Override

View File

@ -461,6 +461,30 @@ public interface AnalyticsListener {
*/ */
default void onDroppedVideoFrames(EventTime eventTime, int droppedFrames, long elapsedMs) {} default void onDroppedVideoFrames(EventTime eventTime, int droppedFrames, long elapsedMs) {}
/**
* Called when there is an update to the video frame processing offset reported by a video
* renderer.
*
* <p>Video processing offset represents how early a video frame is processed compared to the
* player's current position. For each video frame, the offset is calculated as <em>P<sub>vf</sub>
* - P<sub>pl</sub></em> where <em>P<sub>vf</sub></em> is the presentation timestamp of the video
* frame and <em>P<sub>pl</sub></em> is the current position of the player. Positive values
* indicate the frame was processed early enough whereas negative values indicate that the
* player's position had progressed beyond the frame's timestamp when the frame was processed (and
* the frame was probably dropped).
*
* <p>The renderer reports the sum of video processing offset samples (one sample per processed
* video frame: dropped, skipped or rendered) and the total number of samples (frames).
*
* @param eventTime The event time.
* @param totalProcessingOffsetUs The sum of video frame processing offset samples for all video
* frames processed by the renderer in microseconds.
* @param frameCount The number to samples included in the {@code totalProcessingOffsetUs}.
* @param format The current output {@link Format} rendered by the video renderer.
*/
default void onVideoFrameProcessingOffset(
EventTime eventTime, long totalProcessingOffsetUs, int frameCount, Format format) {}
/** /**
* Called before a frame is rendered for the first time since setting the surface, and each time * Called before a frame is rendered for the first time since setting the surface, and each time
* there's a change in the size or pixel aspect ratio of the video being rendered. * there's a change in the size or pixel aspect ratio of the video being rendered.

View File

@ -15,8 +15,6 @@
*/ */
package com.google.android.exoplayer2.decoder; package com.google.android.exoplayer2.decoder;
import com.google.android.exoplayer2.util.Util;
/** /**
* Maintains decoder event counts, for debugging purposes only. * Maintains decoder event counts, for debugging purposes only.
* <p> * <p>
@ -84,14 +82,14 @@ public final class DecoderCounters {
* <p>Note: Use {@link #addVideoFrameProcessingOffsetSample(long)} to update this field instead of * <p>Note: Use {@link #addVideoFrameProcessingOffsetSample(long)} to update this field instead of
* updating it directly. * updating it directly.
*/ */
public long videoFrameProcessingOffsetUsSum; public long totalVideoFrameProcessingOffsetUs;
/** /**
* The number of video frame processing offset samples added. * The number of video frame processing offset samples added.
* *
* <p>Note: Use {@link #addVideoFrameProcessingOffsetSample(long)} to update this field instead of * <p>Note: Use {@link #addVideoFrameProcessingOffsetSample(long)} to update this field instead of
* updating it directly. * updating it directly.
*/ */
public int videoFrameProcessingOffsetUsCount; public int videoFrameProcessingOffsetCount;
/** /**
* Should be called to ensure counter values are made visible across threads. The playback thread * Should be called to ensure counter values are made visible across threads. The playback thread
@ -121,15 +119,14 @@ public final class DecoderCounters {
droppedToKeyframeCount += other.droppedToKeyframeCount; droppedToKeyframeCount += other.droppedToKeyframeCount;
addVideoFrameProcessingOffsetSamples( addVideoFrameProcessingOffsetSamples(
other.videoFrameProcessingOffsetUsSum, other.videoFrameProcessingOffsetUsCount); other.totalVideoFrameProcessingOffsetUs, other.videoFrameProcessingOffsetCount);
} }
/** /**
* Adds a video frame processing offset sample to {@link #videoFrameProcessingOffsetUsSum} and * Adds a video frame processing offset sample to {@link #totalVideoFrameProcessingOffsetUs} and
* increases {@link #videoFrameProcessingOffsetUsCount} by one. * increases {@link #videoFrameProcessingOffsetCount} by one.
* *
* <p>This method checks if adding {@code sampleUs} to {@link #videoFrameProcessingOffsetUsSum} * <p>Convenience method to ensure both fields are updated when adding a sample.
* will cause an overflow, in which case this method is a no-op.
* *
* @param sampleUs The sample in microseconds. * @param sampleUs The sample in microseconds.
*/ */
@ -138,12 +135,7 @@ public final class DecoderCounters {
} }
private void addVideoFrameProcessingOffsetSamples(long sampleUs, int count) { private void addVideoFrameProcessingOffsetSamples(long sampleUs, int count) {
long overflowFlag = videoFrameProcessingOffsetUsSum > 0 ? Long.MIN_VALUE : Long.MAX_VALUE; totalVideoFrameProcessingOffsetUs += sampleUs;
long newSampleSum = videoFrameProcessingOffsetCount += count;
Util.addWithOverflowDefault(videoFrameProcessingOffsetUsSum, sampleUs, overflowFlag);
if (newSampleSum != overflowFlag) {
videoFrameProcessingOffsetUsCount += count;
videoFrameProcessingOffsetUsSum = newSampleSum;
}
} }
} }

View File

@ -679,6 +679,11 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
return format; return format;
} }
@Nullable
protected final Format getCurrentOutputFormat() {
return outputFormat;
}
@Nullable @Nullable
protected final MediaCodec getCodec() { protected final MediaCodec getCodec() {
return codec; return codec;

View File

@ -138,6 +138,8 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
private int consecutiveDroppedFrameCount; private int consecutiveDroppedFrameCount;
private int buffersInCodecCount; private int buffersInCodecCount;
private long lastRenderTimeUs; private long lastRenderTimeUs;
private long totalVideoFrameProcessingOffsetUs;
private int videoFrameProcessingOffsetCount;
private int pendingRotationDegrees; private int pendingRotationDegrees;
private float pendingPixelWidthHeightRatio; private float pendingPixelWidthHeightRatio;
@ -510,12 +512,15 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
droppedFrames = 0; droppedFrames = 0;
droppedFrameAccumulationStartTimeMs = SystemClock.elapsedRealtime(); droppedFrameAccumulationStartTimeMs = SystemClock.elapsedRealtime();
lastRenderTimeUs = SystemClock.elapsedRealtime() * 1000; lastRenderTimeUs = SystemClock.elapsedRealtime() * 1000;
totalVideoFrameProcessingOffsetUs = 0;
videoFrameProcessingOffsetCount = 0;
} }
@Override @Override
protected void onStopped() { protected void onStopped() {
joiningDeadlineMs = C.TIME_UNSET; joiningDeadlineMs = C.TIME_UNSET;
maybeNotifyDroppedFrames(); maybeNotifyDroppedFrames();
maybeNotifyVideoFrameProcessingOffset();
super.onStopped(); super.onStopped();
} }
@ -751,6 +756,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
+ 1 + 1
: outputMediaFormat.getInteger(MediaFormat.KEY_HEIGHT); : outputMediaFormat.getInteger(MediaFormat.KEY_HEIGHT);
processOutputFormat(codec, mediaFormatWidth, mediaFormatHeight); processOutputFormat(codec, mediaFormatWidth, mediaFormatHeight);
maybeNotifyVideoFrameProcessingOffset();
} }
@Override @Override
@ -1216,6 +1222,22 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
} }
} }
private void maybeNotifyVideoFrameProcessingOffset() {
Format outputFormat = getCurrentOutputFormat();
if (outputFormat != null) {
long totalOffsetDelta =
decoderCounters.totalVideoFrameProcessingOffsetUs - totalVideoFrameProcessingOffsetUs;
int countDelta =
decoderCounters.videoFrameProcessingOffsetCount - videoFrameProcessingOffsetCount;
if (countDelta != 0) {
eventDispatcher.reportVideoFrameProcessingOffset(
totalOffsetDelta, countDelta, outputFormat);
totalVideoFrameProcessingOffsetUs = decoderCounters.totalVideoFrameProcessingOffsetUs;
videoFrameProcessingOffsetCount = decoderCounters.videoFrameProcessingOffsetCount;
}
}
}
private static boolean isBufferLate(long earlyUs) { private static boolean isBufferLate(long earlyUs) {
// Class a buffer as late if it should have been presented more than 30 ms ago. // Class a buffer as late if it should have been presented more than 30 ms ago.
return earlyUs < -30000; return earlyUs < -30000;

View File

@ -71,6 +71,28 @@ public interface VideoRendererEventListener {
*/ */
default void onDroppedFrames(int count, long elapsedMs) {} default void onDroppedFrames(int count, long elapsedMs) {}
/**
* Called to report the video processing offset of video frames processed by the video renderer.
*
* <p>Video processing offset represents how early a video frame is processed compared to the
* player's current position. For each video frame, the offset is calculated as <em>P<sub>vf</sub>
* - P<sub>pl</sub></em> where <em>P<sub>vf</sub></em> is the presentation timestamp of the video
* frame and <em>P<sub>pl</sub></em> is the current position of the player. Positive values
* indicate the frame was processed early enough whereas negative values indicate that the
* player's position had progressed beyond the frame's timestamp when the frame was processed (and
* the frame was probably dropped).
*
* <p>The renderer reports the sum of video processing offset samples (one sample per processed
* video frame: dropped, skipped or rendered) and the total number of samples.
*
* @param totalProcessingOffsetUs The sum of all video frame processing offset samples for the
* video frames processed by the renderer in microseconds.
* @param frameCount The number of samples included in the {@code totalProcessingOffsetUs}.
* @param format The {@link Format} that is currently output.
*/
default void onVideoFrameProcessingOffset(
long totalProcessingOffsetUs, int frameCount, Format format) {}
/** /**
* Called before a frame is rendered for the first time since setting the surface, and each time * Called before a frame is rendered for the first time since setting the surface, and each time
* there's a change in the size, rotation or pixel aspect ratio of the video being rendered. * there's a change in the size, rotation or pixel aspect ratio of the video being rendered.
@ -159,6 +181,17 @@ public interface VideoRendererEventListener {
} }
} }
/** Invokes {@link VideoRendererEventListener#onVideoFrameProcessingOffset}. */
public void reportVideoFrameProcessingOffset(
long totalProcessingOffsetUs, int frameCount, Format format) {
if (handler != null) {
handler.post(
() ->
castNonNull(listener)
.onVideoFrameProcessingOffset(totalProcessingOffsetUs, frameCount, format));
}
}
/** Invokes {@link VideoRendererEventListener#onVideoSizeChanged(int, int, int, float)}. */ /** Invokes {@link VideoRendererEventListener#onVideoSizeChanged(int, int, int, float)}. */
public void videoSizeChanged( public void videoSizeChanged(
int width, int width,

View File

@ -107,6 +107,7 @@ public final class AnalyticsCollectorTest {
private static final int EVENT_DRM_KEYS_REMOVED = 36; private static final int EVENT_DRM_KEYS_REMOVED = 36;
private static final int EVENT_DRM_SESSION_ACQUIRED = 37; private static final int EVENT_DRM_SESSION_ACQUIRED = 37;
private static final int EVENT_DRM_SESSION_RELEASED = 38; private static final int EVENT_DRM_SESSION_RELEASED = 38;
private static final int EVENT_VIDEO_FRAME_PROCESSING_OFFSET = 39;
private static final int TIMEOUT_MS = 10000; private static final int TIMEOUT_MS = 10000;
private static final Timeline SINGLE_PERIOD_TIMELINE = new FakeTimeline(/* windowCount= */ 1); private static final Timeline SINGLE_PERIOD_TIMELINE = new FakeTimeline(/* windowCount= */ 1);
@ -181,6 +182,7 @@ public final class AnalyticsCollectorTest {
assertThat(listener.getEvents(EVENT_DROPPED_VIDEO_FRAMES)).containsExactly(period0); assertThat(listener.getEvents(EVENT_DROPPED_VIDEO_FRAMES)).containsExactly(period0);
assertThat(listener.getEvents(EVENT_VIDEO_SIZE_CHANGED)).containsExactly(period0); assertThat(listener.getEvents(EVENT_VIDEO_SIZE_CHANGED)).containsExactly(period0);
assertThat(listener.getEvents(EVENT_RENDERED_FIRST_FRAME)).containsExactly(period0); assertThat(listener.getEvents(EVENT_RENDERED_FIRST_FRAME)).containsExactly(period0);
assertThat(listener.getEvents(EVENT_VIDEO_FRAME_PROCESSING_OFFSET)).containsExactly(period0);
listener.assertNoMoreEvents(); listener.assertNoMoreEvents();
} }
@ -241,6 +243,7 @@ public final class AnalyticsCollectorTest {
assertThat(listener.getEvents(EVENT_DROPPED_VIDEO_FRAMES)).containsExactly(period1); assertThat(listener.getEvents(EVENT_DROPPED_VIDEO_FRAMES)).containsExactly(period1);
assertThat(listener.getEvents(EVENT_VIDEO_SIZE_CHANGED)).containsExactly(period0); assertThat(listener.getEvents(EVENT_VIDEO_SIZE_CHANGED)).containsExactly(period0);
assertThat(listener.getEvents(EVENT_RENDERED_FIRST_FRAME)).containsExactly(period0); assertThat(listener.getEvents(EVENT_RENDERED_FIRST_FRAME)).containsExactly(period0);
assertThat(listener.getEvents(EVENT_VIDEO_FRAME_PROCESSING_OFFSET)).containsExactly(period1);
listener.assertNoMoreEvents(); listener.assertNoMoreEvents();
} }
@ -295,6 +298,7 @@ public final class AnalyticsCollectorTest {
assertThat(listener.getEvents(EVENT_DROPPED_VIDEO_FRAMES)).containsExactly(period0); assertThat(listener.getEvents(EVENT_DROPPED_VIDEO_FRAMES)).containsExactly(period0);
assertThat(listener.getEvents(EVENT_VIDEO_SIZE_CHANGED)).containsExactly(period0); assertThat(listener.getEvents(EVENT_VIDEO_SIZE_CHANGED)).containsExactly(period0);
assertThat(listener.getEvents(EVENT_RENDERED_FIRST_FRAME)).containsExactly(period0); assertThat(listener.getEvents(EVENT_RENDERED_FIRST_FRAME)).containsExactly(period0);
assertThat(listener.getEvents(EVENT_VIDEO_FRAME_PROCESSING_OFFSET)).containsExactly(period0);
listener.assertNoMoreEvents(); listener.assertNoMoreEvents();
} }
@ -442,6 +446,8 @@ public final class AnalyticsCollectorTest {
.containsExactly(period0, period1Seq2, period1Seq2); .containsExactly(period0, period1Seq2, period1Seq2);
assertThat(listener.getEvents(EVENT_VIDEO_SIZE_CHANGED)).containsExactly(period0, period0); assertThat(listener.getEvents(EVENT_VIDEO_SIZE_CHANGED)).containsExactly(period0, period0);
assertThat(listener.getEvents(EVENT_RENDERED_FIRST_FRAME)).containsExactly(period0, period0); assertThat(listener.getEvents(EVENT_RENDERED_FIRST_FRAME)).containsExactly(period0, period0);
assertThat(listener.getEvents(EVENT_VIDEO_FRAME_PROCESSING_OFFSET))
.containsExactly(period0, period1Seq2, period1Seq2);
listener.assertNoMoreEvents(); listener.assertNoMoreEvents();
} }
@ -518,6 +524,8 @@ public final class AnalyticsCollectorTest {
.containsExactly(period0Seq0, period0Seq1); .containsExactly(period0Seq0, period0Seq1);
assertThat(listener.getEvents(EVENT_RENDERED_FIRST_FRAME)) assertThat(listener.getEvents(EVENT_RENDERED_FIRST_FRAME))
.containsExactly(period0Seq0, period0Seq1); .containsExactly(period0Seq0, period0Seq1);
assertThat(listener.getEvents(EVENT_VIDEO_FRAME_PROCESSING_OFFSET))
.containsExactly(period0Seq1);
listener.assertNoMoreEvents(); listener.assertNoMoreEvents();
} }
@ -587,6 +595,8 @@ public final class AnalyticsCollectorTest {
.containsExactly(period0Seq0, period0Seq0); .containsExactly(period0Seq0, period0Seq0);
assertThat(listener.getEvents(EVENT_RENDERED_FIRST_FRAME)) assertThat(listener.getEvents(EVENT_RENDERED_FIRST_FRAME))
.containsExactly(period0Seq0, period0Seq0); .containsExactly(period0Seq0, period0Seq0);
assertThat(listener.getEvents(EVENT_VIDEO_FRAME_PROCESSING_OFFSET))
.containsExactly(period0Seq0);
listener.assertNoMoreEvents(); listener.assertNoMoreEvents();
} }
@ -665,6 +675,8 @@ public final class AnalyticsCollectorTest {
.containsExactly(window0Period1Seq0, period1Seq0); .containsExactly(window0Period1Seq0, period1Seq0);
assertThat(listener.getEvents(EVENT_RENDERED_FIRST_FRAME)) assertThat(listener.getEvents(EVENT_RENDERED_FIRST_FRAME))
.containsExactly(window0Period1Seq0, period1Seq0); .containsExactly(window0Period1Seq0, period1Seq0);
assertThat(listener.getEvents(EVENT_VIDEO_FRAME_PROCESSING_OFFSET))
.containsExactly(window0Period1Seq0);
listener.assertNoMoreEvents(); listener.assertNoMoreEvents();
} }
@ -735,6 +747,8 @@ public final class AnalyticsCollectorTest {
.containsExactly(period0Seq0, period0Seq1); .containsExactly(period0Seq0, period0Seq1);
assertThat(listener.getEvents(EVENT_RENDERED_FIRST_FRAME)) assertThat(listener.getEvents(EVENT_RENDERED_FIRST_FRAME))
.containsExactly(period0Seq0, period0Seq1); .containsExactly(period0Seq0, period0Seq1);
assertThat(listener.getEvents(EVENT_VIDEO_FRAME_PROCESSING_OFFSET))
.containsExactly(period0Seq1);
listener.assertNoMoreEvents(); listener.assertNoMoreEvents();
} }
@ -952,6 +966,8 @@ public final class AnalyticsCollectorTest {
.containsExactly(contentAfterPreroll, contentAfterMidroll, contentAfterPostroll); .containsExactly(contentAfterPreroll, contentAfterMidroll, contentAfterPostroll);
assertThat(listener.getEvents(EVENT_VIDEO_SIZE_CHANGED)).containsExactly(prerollAd); assertThat(listener.getEvents(EVENT_VIDEO_SIZE_CHANGED)).containsExactly(prerollAd);
assertThat(listener.getEvents(EVENT_RENDERED_FIRST_FRAME)).containsExactly(prerollAd); assertThat(listener.getEvents(EVENT_RENDERED_FIRST_FRAME)).containsExactly(prerollAd);
assertThat(listener.getEvents(EVENT_VIDEO_FRAME_PROCESSING_OFFSET))
.containsExactly(contentAfterPreroll, contentAfterMidroll, contentAfterPostroll);
listener.assertNoMoreEvents(); listener.assertNoMoreEvents();
} }
@ -1069,6 +1085,8 @@ public final class AnalyticsCollectorTest {
.containsExactly(contentBeforeMidroll, midrollAd); .containsExactly(contentBeforeMidroll, midrollAd);
assertThat(listener.getEvents(EVENT_RENDERED_FIRST_FRAME)) assertThat(listener.getEvents(EVENT_RENDERED_FIRST_FRAME))
.containsExactly(contentBeforeMidroll, midrollAd); .containsExactly(contentBeforeMidroll, midrollAd);
assertThat(listener.getEvents(EVENT_VIDEO_FRAME_PROCESSING_OFFSET))
.containsExactly(contentAfterMidroll);
listener.assertNoMoreEvents(); listener.assertNoMoreEvents();
} }
@ -1196,6 +1214,8 @@ public final class AnalyticsCollectorTest {
protected void onStopped() throws ExoPlaybackException { protected void onStopped() throws ExoPlaybackException {
super.onStopped(); super.onStopped();
eventDispatcher.droppedFrames(/* droppedFrameCount= */ 0, /* elapsedMs= */ 0); eventDispatcher.droppedFrames(/* droppedFrameCount= */ 0, /* elapsedMs= */ 0);
eventDispatcher.reportVideoFrameProcessingOffset(
/* totalProcessingOffsetUs= */ 400000, /* frameCount= */ 10, this.format);
} }
@Override @Override
@ -1561,6 +1581,12 @@ public final class AnalyticsCollectorTest {
reportedEvents.add(new ReportedEvent(EVENT_DRM_SESSION_RELEASED, eventTime)); reportedEvents.add(new ReportedEvent(EVENT_DRM_SESSION_RELEASED, eventTime));
} }
@Override
public void onVideoFrameProcessingOffset(
EventTime eventTime, long totalProcessingOffsetUs, int frameCount, Format format) {
reportedEvents.add(new ReportedEvent(EVENT_VIDEO_FRAME_PROCESSING_OFFSET, eventTime));
}
private static final class ReportedEvent { private static final class ReportedEvent {
public final int eventType; public final int eventType;

View File

@ -1,96 +0,0 @@
/*
* Copyright (C) 2020 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 com.google.android.exoplayer2.decoder;
import static com.google.common.truth.Truth.assertThat;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
/** Unit tests for {@link DecoderCounters}. */
@RunWith(AndroidJUnit4.class)
public class DecoderCountersTest {
private DecoderCounters decoderCounters;
@Before
public void setUp() {
decoderCounters = new DecoderCounters();
}
@Test
public void maybeAddVideoFrameProcessingOffsetSample_addsSamples() {
long sampleSum = 0;
for (int i = 0; i < 100; i++) {
long sample = (i + 10) * 10L;
sampleSum += sample;
decoderCounters.addVideoFrameProcessingOffsetSample(sample);
}
assertThat(decoderCounters.videoFrameProcessingOffsetUsSum).isEqualTo(sampleSum);
assertThat(decoderCounters.videoFrameProcessingOffsetUsCount).isEqualTo(100);
}
@Test
public void addVideoFrameProcessingOffsetSample_sumReachesMaxLong_addsValues() {
long highSampleValue = Long.MAX_VALUE - 10;
long additionalSample = Long.MAX_VALUE - highSampleValue;
decoderCounters.addVideoFrameProcessingOffsetSample(highSampleValue);
decoderCounters.addVideoFrameProcessingOffsetSample(additionalSample);
assertThat(decoderCounters.videoFrameProcessingOffsetUsSum).isEqualTo(Long.MAX_VALUE);
assertThat(decoderCounters.videoFrameProcessingOffsetUsCount).isEqualTo(2);
}
@Test
public void addVideoFrameProcessingOffsetSample_sumOverflows_isNoOp() {
long highSampleValue = Long.MAX_VALUE - 10;
long additionalSample = Long.MAX_VALUE - highSampleValue + 10;
decoderCounters.addVideoFrameProcessingOffsetSample(highSampleValue);
decoderCounters.addVideoFrameProcessingOffsetSample(additionalSample);
assertThat(decoderCounters.videoFrameProcessingOffsetUsSum).isEqualTo(highSampleValue);
assertThat(decoderCounters.videoFrameProcessingOffsetUsCount).isEqualTo(1);
}
@Test
public void addVideoFrameProcessingOffsetSample_sumReachesMinLong_addsValues() {
long lowSampleValue = Long.MIN_VALUE + 10;
long additionalSample = Long.MIN_VALUE - lowSampleValue;
decoderCounters.addVideoFrameProcessingOffsetSample(lowSampleValue);
decoderCounters.addVideoFrameProcessingOffsetSample(additionalSample);
assertThat(decoderCounters.videoFrameProcessingOffsetUsSum).isEqualTo(Long.MIN_VALUE);
assertThat(decoderCounters.videoFrameProcessingOffsetUsCount).isEqualTo(2);
}
@Test
public void addVideoFrameProcessingOffsetSample_sumUnderflows_isNoOp() {
long lowSampleValue = Long.MIN_VALUE + 10;
long additionalSample = Long.MIN_VALUE - lowSampleValue - 10;
decoderCounters.addVideoFrameProcessingOffsetSample(lowSampleValue);
decoderCounters.addVideoFrameProcessingOffsetSample(additionalSample);
assertThat(decoderCounters.videoFrameProcessingOffsetUsSum).isEqualTo(lowSampleValue);
assertThat(decoderCounters.videoFrameProcessingOffsetUsCount).isEqualTo(1);
}
}

View File

@ -158,7 +158,9 @@ public class DebugTextViewHelper implements Player.EventListener, Runnable {
+ getPixelAspectRatioString(format.pixelWidthHeightRatio) + getPixelAspectRatioString(format.pixelWidthHeightRatio)
+ getDecoderCountersBufferCountString(decoderCounters) + getDecoderCountersBufferCountString(decoderCounters)
+ " vfpo: " + " vfpo: "
+ getVideoFrameProcessingOffsetAverageString(decoderCounters) + getVideoFrameProcessingOffsetAverageString(
decoderCounters.totalVideoFrameProcessingOffsetUs,
decoderCounters.videoFrameProcessingOffsetCount)
+ ")"; + ")";
} }
@ -199,13 +201,12 @@ public class DebugTextViewHelper implements Player.EventListener, Runnable {
: (" par:" + String.format(Locale.US, "%.02f", pixelAspectRatio)); : (" par:" + String.format(Locale.US, "%.02f", pixelAspectRatio));
} }
private static String getVideoFrameProcessingOffsetAverageString(DecoderCounters counters) { private static String getVideoFrameProcessingOffsetAverageString(
counters.ensureUpdated(); long totalOffsetUs, int frameCount) {
int sampleCount = counters.videoFrameProcessingOffsetUsCount; if (frameCount == 0) {
if (sampleCount == 0) {
return "N/A"; return "N/A";
} else { } else {
long averageUs = (long) ((double) counters.videoFrameProcessingOffsetUsSum / sampleCount); long averageUs = (long) ((double) totalOffsetUs / frameCount);
return String.valueOf(averageUs); return String.valueOf(averageUs);
} }
} }

View File

@ -100,7 +100,7 @@ public final class DecoderCountersUtil {
public static void assertVideoFrameProcessingOffsetSampleCount( public static void assertVideoFrameProcessingOffsetSampleCount(
String name, DecoderCounters counters, int minCount, int maxCount) { String name, DecoderCounters counters, int minCount, int maxCount) {
int actual = counters.videoFrameProcessingOffsetUsCount; int actual = counters.videoFrameProcessingOffsetCount;
assertWithMessage( assertWithMessage(
"Codec(" "Codec("
+ name + name