Add 'Player.getVideoSurfaceSize' that returns the size of the surface

on which the video is rendered.

Design Doc: go/aaos-mu-media-dd

PiperOrigin-RevId: 485884772
This commit is contained in:
Googler 2022-11-03 15:50:19 +00:00 committed by microkatz
parent 5ac4700c9a
commit b780635c0b
13 changed files with 206 additions and 10 deletions

View File

@ -28,6 +28,8 @@ Release notes
([#10667](https://github.com/google/ExoPlayer/issues/10667)).
* Enforce minimum `compileSdkVersion` to avoid compilation errors
([#10684](https://github.com/google/ExoPlayer/issues/10684)).
* Add `Player.getVideoSurfaceSize` that returns the size of the surface on
which the video is rendered.
* Downloads:
* Fix potential infinite loop in `ProgressiveDownloader` caused by
simultaneous download and playback with the same `PriorityTaskManager`

View File

@ -47,6 +47,7 @@ import androidx.media3.common.util.Assertions;
import androidx.media3.common.util.Clock;
import androidx.media3.common.util.ListenerSet;
import androidx.media3.common.util.Log;
import androidx.media3.common.util.Size;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.common.util.Util;
import com.google.android.gms.cast.CastStatusCodes;
@ -727,6 +728,12 @@ public final class CastPlayer extends BasePlayer {
return VideoSize.UNKNOWN;
}
/** This method is not supported and returns {@link Size#UNKNOWN}. */
@Override
public Size getVideoSurfaceSize() {
return Size.UNKNOWN;
}
/** This method is not supported and returns an empty {@link CueGroup}. */
@Override
public CueGroup getCurrentCues() {

View File

@ -23,6 +23,7 @@ import android.view.TextureView;
import androidx.annotation.Nullable;
import androidx.media3.common.text.Cue;
import androidx.media3.common.text.CueGroup;
import androidx.media3.common.util.Size;
import androidx.media3.common.util.UnstableApi;
import java.util.List;
@ -759,6 +760,12 @@ public class ForwardingPlayer implements Player {
return player.getVideoSize();
}
/** Calls {@link Player#getVideoSurfaceSize()} on the delegate and returns the result. */
@Override
public Size getVideoSurfaceSize() {
return player.getVideoSurfaceSize();
}
/** Calls {@link Player#clearVideoSurface()} on the delegate. */
@Override
public void clearVideoSurface() {

View File

@ -33,6 +33,7 @@ import androidx.annotation.IntRange;
import androidx.annotation.Nullable;
import androidx.media3.common.text.Cue;
import androidx.media3.common.text.CueGroup;
import androidx.media3.common.util.Size;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.common.util.Util;
import com.google.common.base.Objects;
@ -2498,6 +2499,14 @@ public interface Player {
*/
VideoSize getVideoSize();
/**
* Gets the size of the surface on which the video is rendered.
*
* @see Listener#onSurfaceSizeChanged(int, int)
*/
@UnstableApi
Size getVideoSurfaceSize();
/** Returns the current {@link CueGroup}. */
CueGroup getCurrentCues();

View File

@ -28,6 +28,7 @@ import androidx.media3.common.text.CueGroup;
import androidx.media3.common.util.Clock;
import androidx.media3.common.util.HandlerWrapper;
import androidx.media3.common.util.ListenerSet;
import androidx.media3.common.util.Size;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.common.util.Util;
import com.google.common.base.Supplier;
@ -589,6 +590,12 @@ public abstract class SimpleBasePlayer extends BasePlayer {
throw new IllegalStateException();
}
@Override
public final Size getVideoSurfaceSize() {
// TODO: implement.
throw new IllegalStateException();
}
@Override
public final CueGroup getCurrentCues() {
// TODO: implement.

View File

@ -0,0 +1,85 @@
/*
* Copyright (C) 2022 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.common.util;
import static androidx.media3.common.util.Assertions.checkArgument;
import androidx.annotation.Nullable;
import androidx.media3.common.C;
/** Immutable class for describing width and height dimensions in pixels. */
@UnstableApi
public final class Size {
/** A static instance to represent an unknown size value. */
public static final Size UNKNOWN =
new Size(/* width= */ C.LENGTH_UNSET, /* height= */ C.LENGTH_UNSET);
private final int width;
private final int height;
/**
* Creates a new immutable Size instance.
*
* @param width The width of the size, in pixels, or {@link C#LENGTH_UNSET} if unknown.
* @param height The height of the size, in pixels, or {@link C#LENGTH_UNSET} if unknown.
* @throws IllegalArgumentException if an invalid {@code width} or {@code height} is specified.
*/
public Size(int width, int height) {
checkArgument(
(width == C.LENGTH_UNSET || width >= 0) && (height == C.LENGTH_UNSET || height >= 0));
this.width = width;
this.height = height;
}
/** Returns the width of the size (in pixels), or {@link C#LENGTH_UNSET} if unknown. */
public int getWidth() {
return width;
}
/** Returns the height of the size (in pixels), or {@link C#LENGTH_UNSET} if unknown. */
public int getHeight() {
return height;
}
@Override
public boolean equals(@Nullable Object obj) {
if (obj == null) {
return false;
}
if (this == obj) {
return true;
}
if (obj instanceof Size) {
Size other = (Size) obj;
return width == other.width && height == other.height;
}
return false;
}
@Override
public String toString() {
return width + "x" + height;
}
@Override
public int hashCode() {
// assuming most sizes are <2^16, doing a rotate will give us perfect hashing
return height ^ ((width << (Integer.SIZE / 2)) | (width >>> (Integer.SIZE / 2)));
}
}

View File

@ -82,6 +82,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.Size;
import androidx.media3.common.util.Util;
import androidx.media3.exoplayer.PlayerMessage.Target;
import androidx.media3.exoplayer.Renderer.MessageType;
@ -192,8 +193,7 @@ import java.util.concurrent.TimeoutException;
@Nullable private TextureView textureView;
private @C.VideoScalingMode int videoScalingMode;
private @C.VideoChangeFrameRateStrategy int videoChangeFrameRateStrategy;
private int surfaceWidth;
private int surfaceHeight;
private Size surfaceSize;
@Nullable private DecoderCounters videoDecoderCounters;
@Nullable private DecoderCounters audioDecoderCounters;
private int audioSessionId;
@ -382,6 +382,7 @@ import java.util.concurrent.TimeoutException;
wifiLockManager.setEnabled(builder.wakeMode == C.WAKE_MODE_NETWORK);
deviceInfo = createDeviceInfo(streamVolumeManager);
videoSize = VideoSize.UNKNOWN;
surfaceSize = Size.UNKNOWN;
trackSelector.setAudioAttributes(audioAttributes);
sendRendererMessage(TRACK_TYPE_AUDIO, MSG_SET_AUDIO_SESSION_ID, audioSessionId);
@ -1229,6 +1230,12 @@ import java.util.concurrent.TimeoutException;
return videoSize;
}
@Override
public Size getVideoSurfaceSize() {
verifyApplicationThread();
return surfaceSize;
}
@Override
public void clearVideoSurface() {
verifyApplicationThread();
@ -2545,9 +2552,8 @@ import java.util.concurrent.TimeoutException;
}
private void maybeNotifySurfaceSizeChanged(int width, int height) {
if (width != surfaceWidth || height != surfaceHeight) {
surfaceWidth = width;
surfaceHeight = height;
if (width != surfaceSize.getWidth() || height != surfaceSize.getHeight()) {
surfaceSize = new Size(width, height);
listeners.sendEvent(
EVENT_SURFACE_SIZE_CHANGED, listener -> listener.onSurfaceSizeChanged(width, height));
}

View File

@ -44,6 +44,7 @@ import androidx.media3.common.VideoSize;
import androidx.media3.common.text.CueGroup;
import androidx.media3.common.util.Clock;
import androidx.media3.common.util.ConditionVariable;
import androidx.media3.common.util.Size;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.exoplayer.analytics.AnalyticsCollector;
import androidx.media3.exoplayer.analytics.AnalyticsListener;
@ -528,6 +529,12 @@ public class SimpleExoPlayer extends BasePlayer
return player.getVideoSize();
}
@Override
public Size getVideoSurfaceSize() {
blockUntilConstructorFinished();
return player.getVideoSurfaceSize();
}
@Override
public void clearVideoSurface() {
blockUntilConstructorFinished();

View File

@ -54,6 +54,7 @@ import androidx.media3.common.VideoSize;
import androidx.media3.common.text.CueGroup;
import androidx.media3.common.util.Consumer;
import androidx.media3.common.util.Log;
import androidx.media3.common.util.Size;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.common.util.Util;
import com.google.common.util.concurrent.Futures;
@ -1498,6 +1499,13 @@ public class MediaController implements Player {
return isConnected() ? impl.getVideoSize() : VideoSize.UNKNOWN;
}
@UnstableApi
@Override
public Size getVideoSurfaceSize() {
verifyApplicationThread();
return isConnected() ? impl.getVideoSurfaceSize() : Size.UNKNOWN;
}
@Override
public void clearVideoSurface() {
verifyApplicationThread();
@ -1968,6 +1976,8 @@ public class MediaController implements Player {
VideoSize getVideoSize();
Size getVideoSurfaceSize();
void clearVideoSurface();
void clearVideoSurface(@Nullable Surface surface);

View File

@ -75,6 +75,7 @@ import androidx.media3.common.util.BundleableUtil;
import androidx.media3.common.util.Clock;
import androidx.media3.common.util.ListenerSet;
import androidx.media3.common.util.Log;
import androidx.media3.common.util.Size;
import androidx.media3.common.util.Util;
import androidx.media3.session.MediaController.MediaControllerImpl;
import com.google.common.collect.ImmutableList;
@ -122,8 +123,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
@Nullable private Surface videoSurface;
@Nullable private SurfaceHolder videoSurfaceHolder;
@Nullable private TextureView videoTextureView;
private int surfaceWidth;
private int surfaceHeight;
private Size surfaceSize;
@Nullable private IMediaSession iSession;
private long lastReturnedContentPositionMs;
private long lastSetPlayWhenReadyCalledTimeMs;
@ -136,6 +136,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
Looper applicationLooper) {
// Initialize default values.
playerInfo = PlayerInfo.DEFAULT;
surfaceSize = Size.UNKNOWN;
sessionCommands = SessionCommands.EMPTY;
playerCommandsFromSession = Commands.EMPTY;
playerCommandsFromPlayer = Commands.EMPTY;
@ -1566,6 +1567,11 @@ import org.checkerframework.checker.nullness.qual.NonNull;
return playerInfo.videoSize;
}
@Override
public Size getVideoSurfaceSize() {
return surfaceSize;
}
@Override
public void clearVideoSurface() {
if (!isPlayerCommandAvailable(Player.COMMAND_SET_VIDEO_SURFACE)) {
@ -2189,9 +2195,8 @@ import org.checkerframework.checker.nullness.qual.NonNull;
}
private void maybeNotifySurfaceSizeChanged(int width, int height) {
if (surfaceWidth != width || surfaceHeight != height) {
surfaceWidth = width;
surfaceHeight = height;
if (surfaceSize.getWidth() != width || surfaceSize.getHeight() != height) {
surfaceSize = new Size(width, height);
listeners.sendEvent(
/* eventFlag= */ Player.EVENT_SURFACE_SIZE_CHANGED,
listener -> listener.onSurfaceSizeChanged(width, height));

View File

@ -67,6 +67,7 @@ import androidx.media3.common.text.CueGroup;
import androidx.media3.common.util.Clock;
import androidx.media3.common.util.ListenerSet;
import androidx.media3.common.util.Log;
import androidx.media3.common.util.Size;
import androidx.media3.common.util.Util;
import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.Futures;
@ -961,6 +962,12 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
return VideoSize.UNKNOWN;
}
@Override
public Size getVideoSurfaceSize() {
Log.w(TAG, "Session doesn't support getting VideoSurfaceSize");
return Size.UNKNOWN;
}
@Override
public void clearVideoSurface() {
Log.w(TAG, "Session doesn't support clearing Surface");

View File

@ -18,6 +18,7 @@ package androidx.media3.session;
import static androidx.media3.common.util.Assertions.checkNotNull;
import static java.lang.annotation.ElementType.TYPE_USE;
import android.graphics.Rect;
import android.os.Looper;
import android.view.Surface;
import android.view.SurfaceHolder;
@ -40,6 +41,7 @@ import androidx.media3.common.Tracks;
import androidx.media3.common.VideoSize;
import androidx.media3.common.text.CueGroup;
import androidx.media3.common.util.ConditionVariable;
import androidx.media3.common.util.Size;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.common.util.Util;
import com.google.common.collect.ImmutableList;
@ -230,6 +232,7 @@ public class MockPlayer implements Player {
public @RepeatMode int repeatMode;
public boolean shuffleModeEnabled;
public VideoSize videoSize;
public Size surfaceSize;
@Nullable public Surface surface;
@Nullable public SurfaceHolder surfaceHolder;
@Nullable public SurfaceView surfaceView;
@ -277,6 +280,7 @@ public class MockPlayer implements Player {
currentMediaItemIndex = 0;
repeatMode = Player.REPEAT_MODE_OFF;
videoSize = VideoSize.UNKNOWN;
surfaceSize = Size.UNKNOWN;
volume = 1.0f;
cueGroup = CueGroup.EMPTY_TIME_ZERO;
deviceInfo = DeviceInfo.UNKNOWN;
@ -1121,6 +1125,11 @@ public class MockPlayer implements Player {
return videoSize;
}
@Override
public Size getVideoSurfaceSize() {
return surfaceSize;
}
public void notifyVideoSizeChanged(VideoSize videoSize) {
for (Listener listener : listeners) {
listener.onVideoSizeChanged(videoSize);
@ -1136,47 +1145,70 @@ public class MockPlayer implements Player {
public void clearVideoSurface(@Nullable Surface surface) {
if (surface != null && surface == this.surface) {
this.surface = null;
maybeUpdateSurfaceSize(/* width= */ 0, /* height= */ 0);
}
}
@Override
public void setVideoSurface(@Nullable Surface surface) {
this.surface = surface;
int newSurfaceSize = surface == null ? 0 : C.LENGTH_UNSET;
maybeUpdateSurfaceSize(/* width= */ newSurfaceSize, /* height= */ newSurfaceSize);
}
@Override
public void setVideoSurfaceHolder(@Nullable SurfaceHolder surfaceHolder) {
this.surfaceHolder = surfaceHolder;
if (surfaceHolder == null || surfaceHolder.getSurfaceFrame() == null) {
maybeUpdateSurfaceSize(/* width= */ 0, /* height= */ 0);
} else {
Rect rect = surfaceHolder.getSurfaceFrame();
maybeUpdateSurfaceSize(rect.width(), rect.height());
}
}
@Override
public void clearVideoSurfaceHolder(@Nullable SurfaceHolder surfaceHolder) {
if (surfaceHolder != null && surfaceHolder == this.surfaceHolder) {
this.surfaceHolder = null;
maybeUpdateSurfaceSize(/* width= */ 0, /* height= */ 0);
}
}
@Override
public void setVideoSurfaceView(@Nullable SurfaceView surfaceView) {
this.surfaceView = surfaceView;
if (surfaceView == null
|| surfaceView.getHolder() == null
|| surfaceView.getHolder().getSurfaceFrame() == null) {
maybeUpdateSurfaceSize(/* width= */ 0, /* height= */ 0);
} else {
Rect rect = surfaceView.getHolder().getSurfaceFrame();
maybeUpdateSurfaceSize(rect.width(), rect.height());
}
}
@Override
public void clearVideoSurfaceView(@Nullable SurfaceView surfaceView) {
if (surfaceView != null && surfaceView == this.surfaceView) {
this.surfaceView = null;
maybeUpdateSurfaceSize(/* width= */ 0, /* height= */ 0);
}
}
@Override
public void setVideoTextureView(@Nullable TextureView textureView) {
this.textureView = textureView;
if (textureView != null) {
maybeUpdateSurfaceSize(textureView.getWidth(), textureView.getHeight());
}
}
@Override
public void clearVideoTextureView(@Nullable TextureView textureView) {
if (textureView != null && textureView == this.textureView) {
this.textureView = null;
maybeUpdateSurfaceSize(/* width= */ 0, /* height= */ 0);
}
}
@ -1278,6 +1310,12 @@ public class MockPlayer implements Player {
}
}
private void maybeUpdateSurfaceSize(int width, int height) {
if (width != surfaceSize.getWidth() || height != surfaceSize.getHeight()) {
surfaceSize = new Size(width, height);
}
}
private static ImmutableMap<@Method Integer, ConditionVariable> createMethodConditionVariables() {
return new ImmutableMap.Builder<@Method Integer, ConditionVariable>()
.put(METHOD_ADD_MEDIA_ITEM, new ConditionVariable())

View File

@ -34,6 +34,7 @@ import androidx.media3.common.TrackSelectionParameters;
import androidx.media3.common.Tracks;
import androidx.media3.common.VideoSize;
import androidx.media3.common.text.CueGroup;
import androidx.media3.common.util.Size;
import androidx.media3.common.util.UnstableApi;
import java.util.List;
@ -351,6 +352,11 @@ public class StubPlayer extends BasePlayer {
throw new UnsupportedOperationException();
}
@Override
public Size getVideoSurfaceSize() {
throw new UnsupportedOperationException();
}
@Override
public CueGroup getCurrentCues() {
throw new UnsupportedOperationException();