Add get video size

Move VideoSize in the common module and have the Player return it.

`Listener` and `AnalyticsListener` `onVideoSizeChanged` are updated
with the old method deprecated.

`VideoRendererEventListener.onVideoSizeChanged` was also migrated to
`VideoSize` but the old method is removed, not deprecated.
This is because:
 - apps calling/listening to this method is a rare and niche use-case.
 - it would introduce hard to diagnostic issues where if only the caller
   or the callee is updated to use the new method, the event will be lost.
   This doesn't occur with the other 2 listeners as the caller is always
   in ExoPlayer library and was updated to call both the old and new methods.

VideoSize is used everywhere except in `Format` as this would lead to
too much refactoring and backward compatibility breakage for little gain.

#minor-release

PiperOrigin-RevId: 371087419
This commit is contained in:
krocard 2021-04-29 11:35:29 +01:00 committed by bachinger
parent 79316aa5ac
commit 0f7ef1ea60
21 changed files with 383 additions and 130 deletions

View file

@ -61,6 +61,10 @@
* Allow forcing offload for gapless content even if gapless playback is
not supported.
* Allow fall back from DTS-HD to DTS when playing via passthrough.
* Video:
* Add `Player.getVideoSize()` to retrieve the current size of the video
stream. Add `Listener.onVideoSizeChanged(VideoSize)` and
deprecate `Listener.onVideoSizeChanged(int weight, int height...)`.
* Analytics:
* Add `onAudioCodecError` and `onVideoCodecError` to `AnalyticsListener`.
* Downloads and caching:

View file

@ -48,6 +48,7 @@ import com.google.android.exoplayer2.util.ListenerSet;
import com.google.android.exoplayer2.util.Log;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.Util;
import com.google.android.exoplayer2.video.VideoSize;
import com.google.android.gms.cast.CastStatusCodes;
import com.google.android.gms.cast.MediaInfo;
import com.google.android.gms.cast.MediaQueueItem;
@ -686,6 +687,12 @@ public final class CastPlayer extends BasePlayer {
@Override
public void clearVideoTextureView(@Nullable TextureView textureView) {}
/** This method is not supported and returns {@link VideoSize#UNKNOWN}. */
@Override
public VideoSize getVideoSize() {
return VideoSize.UNKNOWN;
}
/** This method is not supported and returns an empty list. */
@Override
public ImmutableList<Cue> getCurrentCues() {

View file

@ -27,6 +27,7 @@ import com.google.android.exoplayer2.metadata.Metadata;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.text.Cue;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.video.VideoSize;
import java.util.List;
/**
@ -274,6 +275,11 @@ public class ForwardingPlayer extends BasePlayer {
return player.getVolume();
}
@Override
public VideoSize getVideoSize() {
return player.getVideoSize();
}
@Override
public void clearVideoSurface() {
player.clearVideoSurface();

View file

@ -36,6 +36,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.util.ExoFlags;
import com.google.android.exoplayer2.util.Util;
import com.google.android.exoplayer2.video.VideoListener;
import com.google.android.exoplayer2.video.VideoSize;
import com.google.common.base.Objects;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
@ -1797,6 +1798,16 @@ public interface Player {
*/
void clearVideoTextureView(@Nullable TextureView textureView);
/**
* Gets the size of the video.
*
* <p>The video's width and height are {@code 0} if there is no video or its size has not been
* determined yet.
*
* @see Listener#onVideoSizeChanged(int, int, int, float)
*/
VideoSize getVideoSize();
/** Returns the current {@link Cue Cues}. This list may be empty. */
List<Cue> getCurrentCues();

View file

@ -23,19 +23,12 @@ public interface VideoListener {
/**
* Called each time there's a change in the size of the video being rendered.
*
* @param width The video width in pixels.
* @param height The video height in pixels.
* @param unappliedRotationDegrees For videos that require a rotation, this is the clockwise
* rotation in degrees that the application should apply for the video for it to be rendered
* in the correct orientation. This value will always be zero on API levels 21 and above,
* since the renderer will apply all necessary rotations internally. On earlier API levels
* this is not possible. Applications that use {@link android.view.TextureView} can apply the
* rotation by calling {@link android.view.TextureView#setTransform}. Applications that do not
* expect to encounter rotated videos can safely ignore this parameter.
* @param pixelWidthHeightRatio The width to height ratio of each pixel. For the normal case of
* square pixels this will be equal to 1.0. Different values are indicative of anamorphic
* content.
* @param videoSize The new size of the video.
*/
default void onVideoSizeChanged(VideoSize videoSize) {}
/** @deprecated Use {@link #onVideoSizeChanged(VideoSize videoSize)}. */
@Deprecated
default void onVideoSizeChanged(
int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) {}

View file

@ -0,0 +1,171 @@
/*
* Copyright 2021 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.video;
import android.os.Bundle;
import androidx.annotation.FloatRange;
import androidx.annotation.IntDef;
import androidx.annotation.IntRange;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.Bundleable;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/** Represents the video size. */
public final class VideoSize implements Bundleable {
private static final int DEFAULT_WIDTH = 0;
private static final int DEFAULT_HEIGHT = 0;
private static final int DEFAULT_UNAPPLIED_ROTATION_DEGREES = 0;
private static final float DEFAULT_PIXEL_WIDTH_HEIGHT_RATIO = 1F;
public static final VideoSize UNKNOWN = new VideoSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
/** The video width in pixels, 0 when unknown. */
@IntRange(from = 0)
public final int width;
/** The video height in pixels, 0 when unknown. */
@IntRange(from = 0)
public final int height;
/**
* Clockwise rotation in degrees that the application should apply for the video for it to be
* rendered in the correct orientation.
*
* <p>Is 0 if unknown or if no rotation is needed.
*
* <p>Player should apply video rotation internally, in which case unappliedRotationDegrees is 0.
* But when a player can't apply the rotation, for example before API level 21, the unapplied
* rotation is reported by this field for application to handle.
*
* <p>Applications that use {@link android.view.TextureView} can apply the rotation by calling
* {@link android.view.TextureView#setTransform}.
*/
@IntRange(from = 0, to = 359)
public final int unappliedRotationDegrees;
/**
* The width to height ratio of each pixel, 1 if unknown.
*
* <p>For the normal case of square pixels this will be equal to 1.0. Different values are
* indicative of anamorphic content.
*/
@FloatRange(from = 0, fromInclusive = false)
public final float pixelWidthHeightRatio;
/**
* Creates a VideoSize without unapplied rotation or anamorphic content.
*
* @param width The video width in pixels.
* @param height The video height in pixels.
*/
public VideoSize(@IntRange(from = 0) int width, @IntRange(from = 0) int height) {
this(width, height, DEFAULT_UNAPPLIED_ROTATION_DEGREES, DEFAULT_PIXEL_WIDTH_HEIGHT_RATIO);
}
/**
* Creates a VideoSize.
*
* @param width The video width in pixels.
* @param height The video height in pixels.
* @param unappliedRotationDegrees Clockwise rotation in degrees that the application should apply
* for the video for it to be rendered in the correct orientation. See {@link
* #unappliedRotationDegrees}.
* @param pixelWidthHeightRatio The width to height ratio of each pixel. For the normal case of
* square pixels this will be equal to 1.0. Different values are indicative of anamorphic
* content.
*/
public VideoSize(
@IntRange(from = 0) int width,
@IntRange(from = 0) int height,
@IntRange(from = 0, to = 359) int unappliedRotationDegrees,
@FloatRange(from = 0, fromInclusive = false) float pixelWidthHeightRatio) {
this.width = width;
this.height = height;
this.unappliedRotationDegrees = unappliedRotationDegrees;
this.pixelWidthHeightRatio = pixelWidthHeightRatio;
}
@Override
public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof VideoSize) {
VideoSize other = (VideoSize) obj;
return width == other.width
&& height == other.height
&& unappliedRotationDegrees == other.unappliedRotationDegrees
&& pixelWidthHeightRatio == other.pixelWidthHeightRatio;
}
return false;
}
@Override
public int hashCode() {
int result = 7;
result = 31 * result + width;
result = 31 * result + height;
result = 31 * result + unappliedRotationDegrees;
result = 31 * result + Float.floatToRawIntBits(pixelWidthHeightRatio);
return result;
}
// Bundleable implementation.
@Documented
@Retention(RetentionPolicy.SOURCE)
@IntDef({
FIELD_WIDTH,
FIELD_HEIGHT,
FIELD_UNAPPLIED_ROTATION_DEGREES,
FIELD_PIXEL_WIDTH_HEIGHT_RATIO,
})
private @interface FieldNumber {}
private static final int FIELD_WIDTH = 0;
private static final int FIELD_HEIGHT = 1;
private static final int FIELD_UNAPPLIED_ROTATION_DEGREES = 2;
private static final int FIELD_PIXEL_WIDTH_HEIGHT_RATIO = 3;
@Override
public Bundle toBundle() {
Bundle bundle = new Bundle();
bundle.putInt(keyForField(FIELD_WIDTH), width);
bundle.putInt(keyForField(FIELD_HEIGHT), height);
bundle.putInt(keyForField(FIELD_UNAPPLIED_ROTATION_DEGREES), unappliedRotationDegrees);
bundle.putFloat(keyForField(FIELD_PIXEL_WIDTH_HEIGHT_RATIO), pixelWidthHeightRatio);
return bundle;
}
public static final Creator<VideoSize> CREATOR =
bundle -> {
int width = bundle.getInt(keyForField(FIELD_WIDTH), DEFAULT_WIDTH);
int height = bundle.getInt(keyForField(FIELD_HEIGHT), DEFAULT_HEIGHT);
int unappliedRotationDegrees =
bundle.getInt(
keyForField(FIELD_UNAPPLIED_ROTATION_DEGREES), DEFAULT_UNAPPLIED_ROTATION_DEGREES);
float pixelWidthHeightRatio =
bundle.getFloat(
keyForField(FIELD_PIXEL_WIDTH_HEIGHT_RATIO), DEFAULT_PIXEL_WIDTH_HEIGHT_RATIO);
return new VideoSize(width, height, unappliedRotationDegrees, pixelWidthHeightRatio);
};
private static String keyForField(@FieldNumber int field) {
return Integer.toString(field, Character.MAX_RADIX);
}
}

View file

@ -0,0 +1,53 @@
/*
* Copyright 2021 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.video;
import static com.google.common.truth.Truth.assertThat;
import android.os.Bundle;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
/** Unit test for {@link VideoSize}. */
@RunWith(AndroidJUnit4.class)
public final class VideoSizeTest {
@Test
public void roundTripViaBundle_ofVideoSizeUnknown_yieldsEqualInstance() {
assertThat(roundTripViaBundle(VideoSize.UNKNOWN)).isEqualTo(VideoSize.UNKNOWN);
}
@Test
public void roundTripViaBundle_ofArbitraryVideoSize_yieldsEqualInstance() {
VideoSize videoSize =
new VideoSize(
/* width= */ 9,
/* height= */ 8,
/* unappliedRotationDegrees= */ 7,
/* pixelWidthHeightRatio= */ 6);
assertThat(roundTripViaBundle(videoSize)).isEqualTo(videoSize);
}
@Test
public void fromBundle_ofEmptyBundle_yieldsVideoSizeUnknown() {
assertThat(VideoSize.CREATOR.fromBundle(new Bundle())).isEqualTo(VideoSize.UNKNOWN);
}
private static VideoSize roundTripViaBundle(VideoSize videoSize) {
return VideoSize.CREATOR.fromBundle(videoSize.toBundle());
}
}

View file

@ -54,6 +54,7 @@ import com.google.android.exoplayer2.util.Util;
import com.google.android.exoplayer2.video.MediaCodecVideoRenderer;
import com.google.android.exoplayer2.video.VideoFrameMetadataListener;
import com.google.android.exoplayer2.video.VideoListener;
import com.google.android.exoplayer2.video.VideoSize;
import com.google.android.exoplayer2.video.spherical.CameraMotionListener;
import java.util.List;
@ -368,6 +369,16 @@ public interface ExoPlayer extends Player {
* @param textureView The texture view to clear.
*/
void clearVideoTextureView(@Nullable TextureView textureView);
/**
* Gets the size of the video.
*
* <p>The width and height of size could be 0 if there is no video or the size has not been
* determined yet.
*
* @see Listener#onVideoSizeChanged(int, int, int, float)
*/
VideoSize getVideoSize();
}
/** The text component of an {@link ExoPlayer}. */

View file

@ -52,6 +52,7 @@ import com.google.android.exoplayer2.util.HandlerWrapper;
import com.google.android.exoplayer2.util.ListenerSet;
import com.google.android.exoplayer2.util.Log;
import com.google.android.exoplayer2.util.Util;
import com.google.android.exoplayer2.video.VideoSize;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Collections;
@ -1056,6 +1057,12 @@ import java.util.concurrent.CopyOnWriteArraySet;
@Override
public void clearVideoTextureView(@Nullable TextureView textureView) {}
/** This method is not supported and returns {@link VideoSize#UNKNOWN}. */
@Override
public VideoSize getVideoSize() {
return VideoSize.UNKNOWN;
}
/** This method is not supported and returns an empty list. */
@Override
public ImmutableList<Cue> getCurrentCues() {

View file

@ -76,6 +76,7 @@ import com.google.android.exoplayer2.video.VideoDecoderOutputBufferRenderer;
import com.google.android.exoplayer2.video.VideoFrameMetadataListener;
import com.google.android.exoplayer2.video.VideoListener;
import com.google.android.exoplayer2.video.VideoRendererEventListener;
import com.google.android.exoplayer2.video.VideoSize;
import com.google.android.exoplayer2.video.spherical.CameraMotionListener;
import com.google.android.exoplayer2.video.spherical.SphericalGLSurfaceView;
import java.util.ArrayList;
@ -636,6 +637,7 @@ public class SimpleExoPlayer extends BasePlayer
private boolean isPriorityTaskManagerRegistered;
private boolean playerReleased;
private DeviceInfo deviceInfo;
private VideoSize videoSize;
/** @deprecated Use the {@link Builder} and pass it to {@link #SimpleExoPlayer(Builder)}. */
@Deprecated
@ -748,6 +750,7 @@ public class SimpleExoPlayer extends BasePlayer
wifiLockManager = new WifiLockManager(builder.context);
wifiLockManager.setEnabled(builder.wakeMode == C.WAKE_MODE_NETWORK);
deviceInfo = createDeviceInfo(streamVolumeManager);
videoSize = VideoSize.UNKNOWN;
sendRendererMessage(C.TRACK_TYPE_AUDIO, MSG_SET_AUDIO_SESSION_ID, audioSessionId);
sendRendererMessage(C.TRACK_TYPE_VIDEO, MSG_SET_AUDIO_SESSION_ID, audioSessionId);
@ -826,6 +829,11 @@ public class SimpleExoPlayer extends BasePlayer
return videoScalingMode;
}
@Override
public VideoSize getVideoSize() {
return videoSize;
}
@Override
public void clearVideoSurface() {
verifyApplicationThread();
@ -2122,13 +2130,16 @@ public class SimpleExoPlayer extends BasePlayer
}
@Override
public void onVideoSizeChanged(
int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) {
analyticsCollector.onVideoSizeChanged(
width, height, unappliedRotationDegrees, pixelWidthHeightRatio);
public void onVideoSizeChanged(VideoSize videoSize) {
SimpleExoPlayer.this.videoSize = videoSize;
analyticsCollector.onVideoSizeChanged(videoSize);
for (VideoListener videoListener : videoListeners) {
videoListener.onVideoSizeChanged(videoSize);
videoListener.onVideoSizeChanged(
width, height, unappliedRotationDegrees, pixelWidthHeightRatio);
videoSize.width,
videoSize.height,
videoSize.unappliedRotationDegrees,
videoSize.pixelWidthHeightRatio);
}
}

View file

@ -52,6 +52,7 @@ import com.google.android.exoplayer2.util.Clock;
import com.google.android.exoplayer2.util.ListenerSet;
import com.google.android.exoplayer2.util.Util;
import com.google.android.exoplayer2.video.VideoRendererEventListener;
import com.google.android.exoplayer2.video.VideoSize;
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@ -430,16 +431,22 @@ public class AnalyticsCollector
});
}
@SuppressWarnings("deprecation") // Calling deprecated listener method.
@Override
public final void onVideoSizeChanged(
int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) {
public final void onVideoSizeChanged(VideoSize videoSize) {
EventTime eventTime = generateReadingMediaPeriodEventTime();
sendEvent(
eventTime,
AnalyticsListener.EVENT_VIDEO_SIZE_CHANGED,
listener ->
listener.onVideoSizeChanged(
eventTime, width, height, unappliedRotationDegrees, pixelWidthHeightRatio));
listener -> {
listener.onVideoSizeChanged(eventTime, videoSize);
listener.onVideoSizeChanged(
eventTime,
videoSize.width,
videoSize.height,
videoSize.unappliedRotationDegrees,
videoSize.pixelWidthHeightRatio);
});
}
@Override

View file

@ -51,6 +51,7 @@ import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.util.ExoFlags;
import com.google.android.exoplayer2.video.VideoDecoderOutputBufferRenderer;
import com.google.android.exoplayer2.video.VideoSize;
import com.google.common.base.Objects;
import java.io.IOException;
import java.lang.annotation.Documented;
@ -1050,14 +1051,12 @@ public interface AnalyticsListener {
* there's a change in the size or pixel aspect ratio of the video being rendered.
*
* @param eventTime The event time.
* @param width The width of the video.
* @param height The height of the video.
* @param unappliedRotationDegrees For videos that require a rotation, this is the clockwise
* rotation in degrees that the application should apply for the video for it to be rendered
* in the correct orientation. This value will always be zero on API levels 21 and above,
* since the renderer will apply all necessary rotations internally.
* @param pixelWidthHeightRatio The width to height ratio of each pixel.
* @param videoSize The new size of the video.
*/
default void onVideoSizeChanged(EventTime eventTime, VideoSize videoSize) {}
/** @deprecated Implement {@link #onVideoSizeChanged(EventTime eventTime, VideoSize)} instead. */
@Deprecated
default void onVideoSizeChanged(
EventTime eventTime,
int width,

View file

@ -38,6 +38,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.Util;
import com.google.android.exoplayer2.video.VideoSize;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
@ -89,8 +90,7 @@ public final class PlaybackStatsListener
private long bandwidthBytes;
@Nullable private Format videoFormat;
@Nullable private Format audioFormat;
private int videoHeight;
private int videoWidth;
private VideoSize videoSize;
/**
* Creates listener for playback stats.
@ -107,6 +107,7 @@ public final class PlaybackStatsListener
sessionStartEventTimes = new HashMap<>();
finishedPlaybackStats = PlaybackStats.EMPTY;
period = new Period();
videoSize = VideoSize.UNKNOWN;
sessionManager.setListener(this);
}
@ -229,10 +230,8 @@ public final class PlaybackStatsListener
}
@Override
public void onVideoSizeChanged(
EventTime eventTime, int width, int height, int rotationDegrees, float pixelRatio) {
videoWidth = width;
videoHeight = height;
public void onVideoSizeChanged(EventTime eventTime, VideoSize videoSize) {
this.videoSize = videoSize;
}
@Override
@ -270,8 +269,7 @@ public final class PlaybackStatsListener
hasBandwidthData ? bandwidthBytes : 0,
hasFormatData ? videoFormat : null,
hasFormatData ? audioFormat : null,
hasVideoSize ? videoHeight : Format.NO_VALUE,
hasVideoSize ? videoWidth : Format.NO_VALUE);
hasVideoSize ? videoSize : null);
}
videoFormat = null;
audioFormat = null;
@ -480,8 +478,7 @@ public final class PlaybackStatsListener
* @param bandwidthBytes The number of bytes loaded for this playback.
* @param videoFormat A reported downstream video format for this playback, or null.
* @param audioFormat A reported downstream audio format for this playback, or null.
* @param videoHeight The reported video height for this playback, or {@link Format#NO_VALUE}.
* @param videoWidth The reported video width for this playback, or {@link Format#NO_VALUE}.
* @param videoSize The reported video size for this playback, or null.
*/
public void onEvents(
Player player,
@ -498,8 +495,7 @@ public final class PlaybackStatsListener
long bandwidthBytes,
@Nullable Format videoFormat,
@Nullable Format audioFormat,
int videoHeight,
int videoWidth) {
@Nullable VideoSize videoSize) {
if (discontinuityFromPositionMs != C.TIME_UNSET) {
maybeUpdateMediaTimeHistory(eventTime.realtimeMs, discontinuityFromPositionMs);
isSeeking = true;
@ -550,9 +546,13 @@ public final class PlaybackStatsListener
}
if (currentVideoFormat != null
&& currentVideoFormat.height == Format.NO_VALUE
&& videoHeight != Format.NO_VALUE) {
&& videoSize != null) {
Format formatWithHeightAndWidth =
currentVideoFormat.buildUpon().setWidth(videoWidth).setHeight(videoHeight).build();
currentVideoFormat
.buildUpon()
.setWidth(videoSize.width)
.setHeight(videoSize.height)
.build();
maybeUpdateVideoFormat(eventTime, formatWithHeightAndWidth);
}
if (startedLoading) {

View file

@ -44,6 +44,7 @@ import com.google.android.exoplayer2.trackselection.MappingTrackSelector;
import com.google.android.exoplayer2.trackselection.MappingTrackSelector.MappedTrackInfo;
import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.video.VideoSize;
import java.io.IOException;
import java.text.NumberFormat;
import java.util.List;
@ -457,13 +458,8 @@ public class EventLogger implements AnalyticsListener {
}
@Override
public void onVideoSizeChanged(
EventTime eventTime,
int width,
int height,
int unappliedRotationDegrees,
float pixelWidthHeightRatio) {
logd(eventTime, "videoSize", width + ", " + height);
public void onVideoSizeChanged(EventTime eventTime, VideoSize videoSize) {
logd(eventTime, "videoSize", videoSize.width + ", " + videoSize.height);
}
@Override

View file

@ -133,8 +133,7 @@ public abstract class DecoderVideoRenderer extends BaseRenderer {
private boolean inputStreamEnded;
private boolean outputStreamEnded;
private int reportedWidth;
private int reportedHeight;
@Nullable private VideoSize reportedVideoSize;
private long droppedFrameAccumulationStartTimeMs;
private int droppedFrames;
@ -914,26 +913,21 @@ public abstract class DecoderVideoRenderer extends BaseRenderer {
}
private void clearReportedVideoSize() {
reportedWidth = Format.NO_VALUE;
reportedHeight = Format.NO_VALUE;
reportedVideoSize = null;
}
private void maybeNotifyVideoSizeChanged(int width, int height) {
if (reportedWidth != width || reportedHeight != height) {
reportedWidth = width;
reportedHeight = height;
eventDispatcher.videoSizeChanged(
width, height, /* unappliedRotationDegrees= */ 0, /* pixelWidthHeightRatio= */ 1);
if (reportedVideoSize == null
|| reportedVideoSize.width != width
|| reportedVideoSize.height != height) {
reportedVideoSize = new VideoSize(width, height);
eventDispatcher.videoSizeChanged(reportedVideoSize);
}
}
private void maybeRenotifyVideoSizeChanged() {
if (reportedWidth != Format.NO_VALUE || reportedHeight != Format.NO_VALUE) {
eventDispatcher.videoSizeChanged(
reportedWidth,
reportedHeight,
/* unappliedRotationDegrees= */ 0,
/* pixelWidthHeightRatio= */ 1);
if (reportedVideoSize != null) {
eventDispatcher.videoSizeChanged(reportedVideoSize);
}
}

View file

@ -145,10 +145,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
private int currentHeight;
private int currentUnappliedRotationDegrees;
private float currentPixelWidthHeightRatio;
private int reportedWidth;
private int reportedHeight;
private int reportedUnappliedRotationDegrees;
private float reportedPixelWidthHeightRatio;
@Nullable private VideoSize reportedVideoSize;
private boolean tunneling;
private int tunnelingAudioSessionId;
@ -1200,30 +1197,29 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
}
private void clearReportedVideoSize() {
reportedWidth = Format.NO_VALUE;
reportedHeight = Format.NO_VALUE;
reportedPixelWidthHeightRatio = Format.NO_VALUE;
reportedUnappliedRotationDegrees = Format.NO_VALUE;
reportedVideoSize = null;
}
private void maybeNotifyVideoSizeChanged() {
if ((currentWidth != Format.NO_VALUE || currentHeight != Format.NO_VALUE)
&& (reportedWidth != currentWidth || reportedHeight != currentHeight
|| reportedUnappliedRotationDegrees != currentUnappliedRotationDegrees
|| reportedPixelWidthHeightRatio != currentPixelWidthHeightRatio)) {
eventDispatcher.videoSizeChanged(currentWidth, currentHeight, currentUnappliedRotationDegrees,
currentPixelWidthHeightRatio);
reportedWidth = currentWidth;
reportedHeight = currentHeight;
reportedUnappliedRotationDegrees = currentUnappliedRotationDegrees;
reportedPixelWidthHeightRatio = currentPixelWidthHeightRatio;
&& (reportedVideoSize == null
|| reportedVideoSize.width != currentWidth
|| reportedVideoSize.height != currentHeight
|| reportedVideoSize.unappliedRotationDegrees != currentUnappliedRotationDegrees
|| reportedVideoSize.pixelWidthHeightRatio != currentPixelWidthHeightRatio)) {
reportedVideoSize =
new VideoSize(
currentWidth,
currentHeight,
currentUnappliedRotationDegrees,
currentPixelWidthHeightRatio);
eventDispatcher.videoSizeChanged(reportedVideoSize);
}
}
private void maybeRenotifyVideoSizeChanged() {
if (reportedWidth != Format.NO_VALUE || reportedHeight != Format.NO_VALUE) {
eventDispatcher.videoSizeChanged(reportedWidth, reportedHeight,
reportedUnappliedRotationDegrees, reportedPixelWidthHeightRatio);
if (reportedVideoSize != null) {
eventDispatcher.videoSizeChanged(reportedVideoSize);
}
}

View file

@ -22,7 +22,6 @@ import android.media.MediaCodec.CodecException;
import android.os.Handler;
import android.os.SystemClock;
import android.view.Surface;
import android.view.TextureView;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.Player;
@ -108,21 +107,9 @@ public interface VideoRendererEventListener {
* 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.
*
* @param width The video width in pixels.
* @param height The video height in pixels.
* @param unappliedRotationDegrees For videos that require a rotation, this is the clockwise
* rotation in degrees that the application should apply for the video for it to be rendered
* in the correct orientation. This value will always be zero on API levels 21 and above,
* since the renderer will apply all necessary rotations internally. On earlier API levels
* this is not possible. Applications that use {@link TextureView} can apply the rotation by
* calling {@link TextureView#setTransform}. Applications that do not expect to encounter
* rotated videos can safely ignore this parameter.
* @param pixelWidthHeightRatio The width to height ratio of each pixel. For the normal case of
* square pixels this will be equal to 1.0. Different values are indicative of anamorphic
* content.
* @param videoSize The new size of the video.
*/
default void onVideoSizeChanged(
int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) {}
default void onVideoSizeChanged(VideoSize videoSize) {}
/**
* Called when a frame is rendered for the first time since setting the output, or since the
@ -232,18 +219,10 @@ public interface VideoRendererEventListener {
}
}
/** Invokes {@link VideoRendererEventListener#onVideoSizeChanged(int, int, int, float)}. */
public void videoSizeChanged(
int width,
int height,
final int unappliedRotationDegrees,
final float pixelWidthHeightRatio) {
/** Invokes {@link VideoRendererEventListener#onVideoSizeChanged(VideoSize)}. */
public void videoSizeChanged(VideoSize videoSize) {
if (handler != null) {
handler.post(
() ->
castNonNull(listener)
.onVideoSizeChanged(
width, height, unappliedRotationDegrees, pixelWidthHeightRatio));
handler.post(() -> castNonNull(listener).onVideoSizeChanged(videoSize));
}
}

View file

@ -113,6 +113,7 @@ import com.google.android.exoplayer2.util.Clock;
import com.google.android.exoplayer2.util.ConditionVariable;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.Util;
import com.google.android.exoplayer2.video.VideoSize;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.util.ArrayList;
@ -1769,6 +1770,8 @@ public final class AnalyticsCollectorTest {
.onRenderedFirstFrame(individualRenderedFirstFrameEventTimes.capture(), any(), anyLong());
ArgumentCaptor<AnalyticsListener.EventTime> individualVideoSizeChangedEventTimes =
ArgumentCaptor.forClass(AnalyticsListener.EventTime.class);
verify(listener, atLeastOnce())
.onVideoSizeChanged(individualVideoSizeChangedEventTimes.capture(), any());
verify(listener, atLeastOnce())
.onVideoSizeChanged(
individualVideoSizeChangedEventTimes.capture(),
@ -2314,12 +2317,7 @@ public final class AnalyticsCollectorTest {
}
@Override
public void onVideoSizeChanged(
EventTime eventTime,
int width,
int height,
int unappliedRotationDegrees,
float pixelWidthHeightRatio) {
public void onVideoSizeChanged(EventTime eventTime, VideoSize videoSize) {
reportedEvents.add(new ReportedEvent(EVENT_VIDEO_SIZE_CHANGED, eventTime));
}

View file

@ -19,10 +19,8 @@ import static com.google.android.exoplayer2.testutil.FakeSampleStream.FakeSample
import static com.google.android.exoplayer2.testutil.FakeSampleStream.FakeSampleStreamItem.format;
import static com.google.android.exoplayer2.testutil.FakeSampleStream.FakeSampleStreamItem.oneByteSample;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@ -51,12 +49,13 @@ import com.google.android.exoplayer2.upstream.DefaultAllocator;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.common.collect.ImmutableList;
import java.util.Collections;
import java.util.stream.Collectors;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InOrder;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@ -200,10 +199,11 @@ public class MediaCodecVideoRendererTest {
verify(eventListener)
.onVideoSizeChanged(
VIDEO_H264.width,
VIDEO_H264.height,
VIDEO_H264.rotationDegrees,
VIDEO_H264.pixelWidthHeightRatio);
new VideoSize(
VIDEO_H264.width,
VIDEO_H264.height,
VIDEO_H264.rotationDegrees,
VIDEO_H264.pixelWidthHeightRatio));
}
@Test
@ -256,11 +256,13 @@ public class MediaCodecVideoRendererTest {
} while (!mediaCodecVideoRenderer.isEnded());
shadowOf(testMainLooper).idle();
InOrder orderVerifier = inOrder(eventListener);
orderVerifier.verify(eventListener).onVideoSizeChanged(anyInt(), anyInt(), anyInt(), eq(1f));
orderVerifier.verify(eventListener).onVideoSizeChanged(anyInt(), anyInt(), anyInt(), eq(2f));
orderVerifier.verify(eventListener).onVideoSizeChanged(anyInt(), anyInt(), anyInt(), eq(3f));
orderVerifier.verifyNoMoreInteractions();
ArgumentCaptor<VideoSize> videoSizesCaptor = ArgumentCaptor.forClass(VideoSize.class);
verify(eventListener, times(3)).onVideoSizeChanged(videoSizesCaptor.capture());
assertThat(
videoSizesCaptor.getAllValues().stream()
.map(videoSize -> videoSize.pixelWidthHeightRatio)
.collect(Collectors.toList()))
.containsExactly(1f, 2f, 3f);
}
@Test

View file

@ -26,6 +26,7 @@ import com.google.android.exoplayer2.Renderer;
import com.google.android.exoplayer2.decoder.DecoderCounters;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.video.VideoRendererEventListener;
import com.google.android.exoplayer2.video.VideoSize;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/** A {@link FakeRenderer} that supports {@link C#TRACK_TYPE_VIDEO}. */
@ -119,7 +120,8 @@ public class FakeVideoRenderer extends FakeRenderer {
if (shouldProcess && !renderedFirstFrameAfterReset && output != null) {
@MonotonicNonNull Format format = Assertions.checkNotNull(this.format);
eventDispatcher.videoSizeChanged(
format.width, format.height, format.rotationDegrees, format.pixelWidthHeightRatio);
new VideoSize(
format.width, format.height, format.rotationDegrees, format.pixelWidthHeightRatio));
eventDispatcher.renderedFirstFrame(output);
renderedFirstFrameAfterReset = true;
renderedFirstFrameAfterEnable = true;

View file

@ -41,6 +41,7 @@ import com.google.android.exoplayer2.text.Cue;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.trackselection.TrackSelector;
import com.google.android.exoplayer2.util.Clock;
import com.google.android.exoplayer2.video.VideoSize;
import java.util.List;
/**
@ -489,6 +490,11 @@ public class StubExoPlayer extends BasePlayer implements ExoPlayer {
throw new UnsupportedOperationException();
}
@Override
public VideoSize getVideoSize() {
throw new UnsupportedOperationException();
}
@Override
public List<Cue> getCurrentCues() {
throw new UnsupportedOperationException();