Merge branch 'main' into rtp-mpeg4

This commit is contained in:
manisha_jajoo 2022-02-21 12:33:09 +05:30 committed by GitHub
commit 706d5ac252
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
778 changed files with 29367 additions and 15017 deletions

View File

@ -37,6 +37,7 @@ project.ext {
androidxAnnotationExperimentalVersion = '1.2.0' androidxAnnotationExperimentalVersion = '1.2.0'
androidxAppCompatVersion = '1.3.1' androidxAppCompatVersion = '1.3.1'
androidxCollectionVersion = '1.1.0' androidxCollectionVersion = '1.1.0'
androidxConstraintLayoutVersion = '2.0.4'
androidxCoreVersion = '1.7.0' androidxCoreVersion = '1.7.0'
androidxFuturesVersion = '1.1.0' androidxFuturesVersion = '1.1.0'
androidxMediaVersion = '1.4.3' androidxMediaVersion = '1.4.3'

View File

@ -36,7 +36,7 @@ import androidx.media3.common.MediaItem;
import androidx.media3.common.util.Assertions; import androidx.media3.common.util.Assertions;
import androidx.media3.common.util.Util; import androidx.media3.common.util.Util;
import androidx.media3.exoplayer.ExoPlayer; import androidx.media3.exoplayer.ExoPlayer;
import androidx.media3.ui.StyledPlayerView; import androidx.media3.ui.PlayerView;
import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
@ -52,7 +52,7 @@ import com.google.android.gms.dynamite.DynamiteModule;
public class MainActivity extends AppCompatActivity public class MainActivity extends AppCompatActivity
implements OnClickListener, PlayerManager.Listener { implements OnClickListener, PlayerManager.Listener {
private StyledPlayerView playerView; private PlayerView playerView;
private PlayerManager playerManager; private PlayerManager playerManager;
private RecyclerView mediaQueueList; private RecyclerView mediaQueueList;
private MediaQueueListAdapter mediaQueueListAdapter; private MediaQueueListAdapter mediaQueueListAdapter;

View File

@ -28,8 +28,8 @@ import androidx.media3.common.Player.TimelineChangeReason;
import androidx.media3.common.Timeline; import androidx.media3.common.Timeline;
import androidx.media3.common.TracksInfo; import androidx.media3.common.TracksInfo;
import androidx.media3.exoplayer.ExoPlayer; import androidx.media3.exoplayer.ExoPlayer;
import androidx.media3.ui.StyledPlayerControlView; import androidx.media3.ui.PlayerControlView;
import androidx.media3.ui.StyledPlayerView; import androidx.media3.ui.PlayerView;
import com.google.android.gms.cast.framework.CastContext; import com.google.android.gms.cast.framework.CastContext;
import java.util.ArrayList; import java.util.ArrayList;
@ -51,7 +51,7 @@ import java.util.ArrayList;
} }
private final Context context; private final Context context;
private final StyledPlayerView playerView; private final PlayerView playerView;
private final Player localPlayer; private final Player localPlayer;
private final CastPlayer castPlayer; private final CastPlayer castPlayer;
private final ArrayList<MediaItem> mediaQueue; private final ArrayList<MediaItem> mediaQueue;
@ -66,11 +66,11 @@ import java.util.ArrayList;
* *
* @param context A {@link Context}. * @param context A {@link Context}.
* @param listener A {@link Listener} for queue position changes. * @param listener A {@link Listener} for queue position changes.
* @param playerView The {@link StyledPlayerView} for playback. * @param playerView The {@link PlayerView} for playback.
* @param castContext The {@link CastContext}. * @param castContext The {@link CastContext}.
*/ */
public PlayerManager( public PlayerManager(
Context context, Listener listener, StyledPlayerView playerView, CastContext castContext) { Context context, Listener listener, PlayerView playerView, CastContext castContext) {
this.context = context; this.context = context;
this.listener = listener; this.listener = listener;
this.playerView = playerView; this.playerView = playerView;
@ -223,10 +223,12 @@ import java.util.ArrayList;
if (currentPlayer != localPlayer || tracksInfo == lastSeenTrackGroupInfo) { if (currentPlayer != localPlayer || tracksInfo == lastSeenTrackGroupInfo) {
return; return;
} }
if (!tracksInfo.isTypeSupportedOrEmpty(C.TRACK_TYPE_VIDEO)) { if (tracksInfo.containsType(C.TRACK_TYPE_VIDEO)
&& !tracksInfo.isTypeSupported(C.TRACK_TYPE_VIDEO, /* allowExceedsCapabilities= */ true)) {
listener.onUnsupportedTrack(C.TRACK_TYPE_VIDEO); listener.onUnsupportedTrack(C.TRACK_TYPE_VIDEO);
} }
if (!tracksInfo.isTypeSupportedOrEmpty(C.TRACK_TYPE_AUDIO)) { if (tracksInfo.containsType(C.TRACK_TYPE_AUDIO)
&& !tracksInfo.isTypeSupported(C.TRACK_TYPE_AUDIO, /* allowExceedsCapabilities= */ true)) {
listener.onUnsupportedTrack(C.TRACK_TYPE_AUDIO); listener.onUnsupportedTrack(C.TRACK_TYPE_AUDIO);
} }
lastSeenTrackGroupInfo = tracksInfo; lastSeenTrackGroupInfo = tracksInfo;
@ -270,7 +272,7 @@ import java.util.ArrayList;
R.drawable.ic_baseline_cast_connected_400, R.drawable.ic_baseline_cast_connected_400,
/* theme= */ null)); /* theme= */ null));
} else { // currentPlayer == localPlayer } else { // currentPlayer == localPlayer
playerView.setControllerShowTimeoutMs(StyledPlayerControlView.DEFAULT_SHOW_TIMEOUT_MS); playerView.setControllerShowTimeoutMs(PlayerControlView.DEFAULT_SHOW_TIMEOUT_MS);
playerView.setDefaultArtwork(null); playerView.setDefaultArtwork(null);
} }

View File

@ -20,7 +20,7 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:keepScreenOn="true"> android:keepScreenOn="true">
<androidx.media3.ui.StyledPlayerView android:id="@+id/player_view" <androidx.media3.ui.PlayerView android:id="@+id/player_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_weight="1" android:layout_weight="1"

View File

@ -15,19 +15,19 @@
#extension GL_OES_EGL_image_external : require #extension GL_OES_EGL_image_external : require
precision mediump float; precision mediump float;
// External texture containing video decoder output. // External texture containing video decoder output.
uniform samplerExternalOES tex_sampler_0; uniform samplerExternalOES uTexSampler0;
// Texture containing the overlap bitmap. // Texture containing the overlap bitmap.
uniform sampler2D tex_sampler_1; uniform sampler2D uTexSampler1;
// Horizontal scaling factor for the overlap bitmap. // Horizontal scaling factor for the overlap bitmap.
uniform float scaleX; uniform float uScaleX;
// Vertical scaling factory for the overlap bitmap. // Vertical scaling factory for the overlap bitmap.
uniform float scaleY; uniform float uScaleY;
varying vec2 v_texcoord; varying vec2 vTexCoords;
void main() { void main() {
vec4 videoColor = texture2D(tex_sampler_0, v_texcoord); vec4 videoColor = texture2D(uTexSampler0, vTexCoords);
vec4 overlayColor = texture2D(tex_sampler_1, vec4 overlayColor = texture2D(uTexSampler1,
vec2(v_texcoord.x * scaleX, vec2(vTexCoords.x * uScaleX,
v_texcoord.y * scaleY)); vTexCoords.y * uScaleY));
// Blend the video decoder output and the overlay bitmap. // Blend the video decoder output and the overlay bitmap.
gl_FragColor = videoColor * (1.0 - overlayColor.a) gl_FragColor = videoColor * (1.0 - overlayColor.a)
+ overlayColor * overlayColor.a; + overlayColor * overlayColor.a;

View File

@ -11,11 +11,11 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
attribute vec4 a_position; attribute vec4 aFramePosition;
attribute vec4 a_texcoord; attribute vec4 aTexCoords;
uniform mat4 tex_transform; uniform mat4 uTexTransform;
varying vec2 v_texcoord; varying vec2 vTexCoords;
void main() { void main() {
gl_Position = a_position; gl_Position = aFramePosition;
v_texcoord = (tex_transform * a_texcoord).xy; vTexCoords = (uTexTransform * aTexCoords).xy;
} }

View File

@ -27,6 +27,7 @@ import android.graphics.drawable.BitmapDrawable;
import android.opengl.GLES20; import android.opengl.GLES20;
import android.opengl.GLUtils; import android.opengl.GLUtils;
import androidx.media3.common.C; import androidx.media3.common.C;
import androidx.media3.common.util.GlProgram;
import androidx.media3.common.util.GlUtil; import androidx.media3.common.util.GlUtil;
import java.io.IOException; import java.io.IOException;
import java.util.Locale; import java.util.Locale;
@ -50,7 +51,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
private final Bitmap logoBitmap; private final Bitmap logoBitmap;
private final Canvas overlayCanvas; private final Canvas overlayCanvas;
private GlUtil.@MonotonicNonNull Program program; private @MonotonicNonNull GlProgram program;
private float bitmapScaleX; private float bitmapScaleX;
private float bitmapScaleY; private float bitmapScaleY;
@ -78,7 +79,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
public void initialize() { public void initialize() {
try { try {
program = program =
new GlUtil.Program( new GlProgram(
context, context,
/* vertexShaderFilePath= */ "bitmap_overlay_video_processor_vertex.glsl", /* vertexShaderFilePath= */ "bitmap_overlay_video_processor_vertex.glsl",
/* fragmentShaderFilePath= */ "bitmap_overlay_video_processor_fragment.glsl"); /* fragmentShaderFilePath= */ "bitmap_overlay_video_processor_fragment.glsl");
@ -86,23 +87,9 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
throw new IllegalStateException(e); throw new IllegalStateException(e);
} }
program.setBufferAttribute( program.setBufferAttribute(
"a_position", "aFramePosition", GlUtil.getNormalizedCoordinateBounds(), GlUtil.RECTANGLE_VERTICES_COUNT);
new float[] {
-1, -1, 0, 1,
1, -1, 0, 1,
-1, 1, 0, 1,
1, 1, 0, 1
},
4);
program.setBufferAttribute( program.setBufferAttribute(
"a_texcoord", "aTexCoords", GlUtil.getTextureCoordinateBounds(), GlUtil.RECTANGLE_VERTICES_COUNT);
new float[] {
0, 0, 0, 1,
1, 0, 0, 1,
0, 1, 0, 1,
1, 1, 0, 1
},
4);
GLES20.glGenTextures(1, textures, 0); GLES20.glGenTextures(1, textures, 0);
GLES20.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]); GLES20.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
GLES20.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST); GLES20.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
@ -131,12 +118,12 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
GlUtil.checkGlError(); GlUtil.checkGlError();
// Run the shader program. // Run the shader program.
GlUtil.Program program = checkNotNull(this.program); GlProgram program = checkNotNull(this.program);
program.setSamplerTexIdUniform("tex_sampler_0", frameTexture, /* unit= */ 0); program.setSamplerTexIdUniform("uTexSampler0", frameTexture, /* unit= */ 0);
program.setSamplerTexIdUniform("tex_sampler_1", textures[0], /* unit= */ 1); program.setSamplerTexIdUniform("uTexSampler1", textures[0], /* unit= */ 1);
program.setFloatUniform("scaleX", bitmapScaleX); program.setFloatUniform("uScaleX", bitmapScaleX);
program.setFloatUniform("scaleY", bitmapScaleY); program.setFloatUniform("uScaleY", bitmapScaleY);
program.setFloatsUniform("tex_transform", transformMatrix); program.setFloatsUniform("uTexTransform", transformMatrix);
program.bindAttributesAndUniforms(); program.bindAttributesAndUniforms();
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, /* first= */ 0, /* count= */ 4); GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, /* first= */ 0, /* count= */ 4);

View File

@ -42,7 +42,7 @@ import androidx.media3.exoplayer.drm.HttpMediaDrmCallback;
import androidx.media3.exoplayer.source.MediaSource; import androidx.media3.exoplayer.source.MediaSource;
import androidx.media3.exoplayer.source.ProgressiveMediaSource; import androidx.media3.exoplayer.source.ProgressiveMediaSource;
import androidx.media3.exoplayer.util.EventLogger; import androidx.media3.exoplayer.util.EventLogger;
import androidx.media3.ui.StyledPlayerView; import androidx.media3.ui.PlayerView;
import java.util.UUID; import java.util.UUID;
/** /**
@ -61,7 +61,7 @@ public final class MainActivity extends Activity {
private static final String DRM_SCHEME_EXTRA = "drm_scheme"; private static final String DRM_SCHEME_EXTRA = "drm_scheme";
private static final String DRM_LICENSE_URL_EXTRA = "drm_license_url"; private static final String DRM_LICENSE_URL_EXTRA = "drm_license_url";
@Nullable private StyledPlayerView playerView; @Nullable private PlayerView playerView;
@Nullable private VideoProcessingGLSurfaceView videoProcessingGLSurfaceView; @Nullable private VideoProcessingGLSurfaceView videoProcessingGLSurfaceView;
@Nullable private ExoPlayer player; @Nullable private ExoPlayer player;
@ -181,7 +181,7 @@ public final class MainActivity extends Activity {
Assertions.checkNotNull(this.videoProcessingGLSurfaceView); Assertions.checkNotNull(this.videoProcessingGLSurfaceView);
videoProcessingGLSurfaceView.setPlayer(player); videoProcessingGLSurfaceView.setPlayer(player);
Assertions.checkNotNull(playerView).setPlayer(player); Assertions.checkNotNull(playerView).setPlayer(player);
player.addAnalyticsListener(new EventLogger(/* trackSelector= */ null)); player.addAnalyticsListener(new EventLogger());
this.player = player; this.player = player;
} }

View File

@ -20,7 +20,7 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:keepScreenOn="true"> android:keepScreenOn="true">
<androidx.media3.ui.StyledPlayerView <androidx.media3.ui.PlayerView
android:id="@+id/player_view" android:id="@+id/player_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"

View File

@ -76,6 +76,7 @@
<data android:scheme="content"/> <data android:scheme="content"/>
<data android:scheme="asset"/> <data android:scheme="asset"/>
<data android:scheme="file"/> <data android:scheme="file"/>
<data android:scheme="ssai"/>
</intent-filter> </intent-filter>
<intent-filter> <intent-filter>
<action android:name="androidx.media3.demo.main.action.VIEW_LIST"/> <action android:name="androidx.media3.demo.main.action.VIEW_LIST"/>

View File

@ -35,31 +35,31 @@
"name": "HD (cenc)", "name": "HD (cenc)",
"uri": "https://storage.googleapis.com/wvmedia/cenc/h264/tears/tears.mpd", "uri": "https://storage.googleapis.com/wvmedia/cenc/h264/tears/tears.mpd",
"drm_scheme": "widevine", "drm_scheme": "widevine",
"drm_license_uri": "https://proxy.uat.widevine.com/proxy?provider=widevine_test" "drm_license_uri": "https://proxy.uat.widevine.com/proxy?video_id=2015_tears&provider=widevine_test"
}, },
{ {
"name": "UHD (cenc)", "name": "UHD (cenc)",
"uri": "https://storage.googleapis.com/wvmedia/cenc/h264/tears/tears_uhd.mpd", "uri": "https://storage.googleapis.com/wvmedia/cenc/h264/tears/tears_uhd.mpd",
"drm_scheme": "widevine", "drm_scheme": "widevine",
"drm_license_uri": "https://proxy.uat.widevine.com/proxy?provider=widevine_test" "drm_license_uri": "https://proxy.uat.widevine.com/proxy?video_id=2015_tears&provider=widevine_test"
}, },
{ {
"name": "HD (cbcs)", "name": "HD (cbcs)",
"uri": "https://storage.googleapis.com/wvmedia/cbcs/h264/tears/tears_aes_cbcs.mpd", "uri": "https://storage.googleapis.com/wvmedia/cbcs/h264/tears/tears_aes_cbcs.mpd",
"drm_scheme": "widevine", "drm_scheme": "widevine",
"drm_license_uri": "https://proxy.uat.widevine.com/proxy?provider=widevine_test" "drm_license_uri": "https://proxy.uat.widevine.com/proxy?video_id=2015_tears&provider=widevine_test"
}, },
{ {
"name": "UHD (cbcs)", "name": "UHD (cbcs)",
"uri": "https://storage.googleapis.com/wvmedia/cbcs/h264/tears/tears_aes_cbcs_uhd.mpd", "uri": "https://storage.googleapis.com/wvmedia/cbcs/h264/tears/tears_aes_cbcs_uhd.mpd",
"drm_scheme": "widevine", "drm_scheme": "widevine",
"drm_license_uri": "https://proxy.uat.widevine.com/proxy?provider=widevine_test" "drm_license_uri": "https://proxy.uat.widevine.com/proxy?video_id=2015_tears&provider=widevine_test"
}, },
{ {
"name": "Secure -> Clear -> Secure (cenc)", "name": "Secure -> Clear -> Secure (cenc)",
"uri": "https://storage.googleapis.com/exoplayer-test-media-1/widevine/tears_enc_clear_enc.mpd", "uri": "https://storage.googleapis.com/exoplayer-test-media-1/widevine/tears_enc_clear_enc.mpd",
"drm_scheme": "widevine", "drm_scheme": "widevine",
"drm_license_uri": "https://proxy.uat.widevine.com/proxy?provider=widevine_test", "drm_license_uri": "https://proxy.uat.widevine.com/proxy?video_id=2015_tears&provider=widevine_test",
"drm_session_for_clear_content": true "drm_session_for_clear_content": true
} }
] ]
@ -71,25 +71,25 @@
"name": "HD (cenc, full-sample)", "name": "HD (cenc, full-sample)",
"uri": "https://storage.googleapis.com/wvmedia/cenc/vp9/tears/tears.mpd", "uri": "https://storage.googleapis.com/wvmedia/cenc/vp9/tears/tears.mpd",
"drm_scheme": "widevine", "drm_scheme": "widevine",
"drm_license_uri": "https://proxy.uat.widevine.com/proxy?provider=widevine_test" "drm_license_uri": "https://proxy.uat.widevine.com/proxy?video_id=2015_tears&provider=widevine_test"
}, },
{ {
"name": "UHD (cenc, full-sample)", "name": "UHD (cenc, full-sample)",
"uri": "https://storage.googleapis.com/wvmedia/cenc/vp9/tears/tears_uhd.mpd", "uri": "https://storage.googleapis.com/wvmedia/cenc/vp9/tears/tears_uhd.mpd",
"drm_scheme": "widevine", "drm_scheme": "widevine",
"drm_license_uri": "https://proxy.uat.widevine.com/proxy?provider=widevine_test" "drm_license_uri": "https://proxy.uat.widevine.com/proxy?video_id=2015_tears&provider=widevine_test"
}, },
{ {
"name": "HD (cenc, sub-sample)", "name": "HD (cenc, sub-sample)",
"uri": "https://storage.googleapis.com/wvmedia/cenc/vp9/subsample/24fps/tears/tears.mpd", "uri": "https://storage.googleapis.com/wvmedia/cenc/vp9/subsample/24fps/tears/tears.mpd",
"drm_scheme": "widevine", "drm_scheme": "widevine",
"drm_license_uri": "https://proxy.uat.widevine.com/proxy?provider=widevine_test" "drm_license_uri": "https://proxy.uat.widevine.com/proxy?video_id=2015_tears&provider=widevine_test"
}, },
{ {
"name": "UHD (cenc, sub-sample)", "name": "UHD (cenc, sub-sample)",
"uri": "https://storage.googleapis.com/wvmedia/cenc/vp9/subsample/24fps/tears/tears_uhd.mpd", "uri": "https://storage.googleapis.com/wvmedia/cenc/vp9/subsample/24fps/tears/tears_uhd.mpd",
"drm_scheme": "widevine", "drm_scheme": "widevine",
"drm_license_uri": "https://proxy.uat.widevine.com/proxy?provider=widevine_test" "drm_license_uri": "https://proxy.uat.widevine.com/proxy?video_id=2015_tears&provider=widevine_test"
} }
] ]
}, },
@ -100,13 +100,13 @@
"name": "HD (cenc)", "name": "HD (cenc)",
"uri": "https://storage.googleapis.com/wvmedia/cenc/hevc/tears/tears.mpd", "uri": "https://storage.googleapis.com/wvmedia/cenc/hevc/tears/tears.mpd",
"drm_scheme": "widevine", "drm_scheme": "widevine",
"drm_license_uri": "https://proxy.uat.widevine.com/proxy?provider=widevine_test" "drm_license_uri": "https://proxy.uat.widevine.com/proxy?video_id=2015_tears&provider=widevine_test"
}, },
{ {
"name": "UHD (cenc)", "name": "UHD (cenc)",
"uri": "https://storage.googleapis.com/wvmedia/cenc/hevc/tears/tears_uhd.mpd", "uri": "https://storage.googleapis.com/wvmedia/cenc/hevc/tears/tears_uhd.mpd",
"drm_scheme": "widevine", "drm_scheme": "widevine",
"drm_license_uri": "https://proxy.uat.widevine.com/proxy?provider=widevine_test" "drm_license_uri": "https://proxy.uat.widevine.com/proxy?video_id=2015_tears&provider=widevine_test"
} }
] ]
}, },
@ -387,6 +387,98 @@
} }
] ]
}, },
{
"name": "IMA DAI streams",
"samples": [
{
"name": "HLS VOD: Demo (skippable pre/post), single ads [30 s]",
"uri": "ssai://dai.google.com/?contentSourceId=2483977&videoId=ima-vod-skippable-test&format=2&adsId=1"
},
{
"name": "HLS VOD: Tears of Steel (pre/mid/mid/mid/post), single ads [10s]",
"uri": "ssai://dai.google.com/?contentSourceId=2528370&videoId=tears-of-steel&format=2&adsId=1"
},
{
"name": "HLS Live: Big Buck Bunny (mid), 3 ads each [10 s]",
"uri": "ssai://dai.google.com/?assetKey=sN_IYUG8STe1ZzhIIE_ksA&format=2&adsId=3"
},
{
"name": "DASH VOD: Tears of Steel (11 periods, pre/mid/post), 2/5/2 ads [5/10s]",
"uri": "ssai://dai.google.com/?contentSourceId=2559737&videoId=tos-dash&format=0&adsId=1"
},
{
"name": "Playlist: No ads - HLS VOD: Demo (skippable pre/post) - No ads",
"playlist": [
{
"uri": "https://html5demos.com/assets/dizzy.mp4"
},
{
"uri": "ssai://dai.google.com/?contentSourceId=2483977&videoId=ima-vod-skippable-test&format=2&adsId=1"
},
{
"uri": "https://html5demos.com/assets/dizzy.mp4"
}
]
},
{
"name": "Playlist: No ads - HLS VOD: Tears of steel (pre/mid/mid/mid/post) - No ads",
"playlist": [
{
"uri": "https://html5demos.com/assets/dizzy.mp4"
},
{
"uri": "ssai://dai.google.com/?contentSourceId=2528370&videoId=tears-of-steel&format=2&adsId=1"
},
{
"uri": "https://html5demos.com/assets/dizzy.mp4"
}
]
},
{
"name": "Playlist: No ads - HLS Live: Big Buck Bunny (mid) - No ads",
"playlist": [
{
"uri": "https://html5demos.com/assets/dizzy.mp4"
},
{
"uri": "ssai://dai.google.com/?assetKey=sN_IYUG8STe1ZzhIIE_ksA&format=2&adsId=3"
},
{
"uri": "https://html5demos.com/assets/dizzy.mp4"
}
]
},
{
"name": "Playlist: No ads - DASH VOD: Tears of Steel (11 periods, pre/mid/post) - No ads",
"playlist": [
{
"uri": "https://html5demos.com/assets/dizzy.mp4"
},
{
"uri": "ssai://dai.google.com/?contentSourceId=2559737&videoId=tos-dash&format=0&adsId=1"
},
{
"uri": "https://html5demos.com/assets/dizzy.mp4"
}
]
},
{
"name": "Playlist: Client-side Ads - DASH VOD: Tears of Steel (11 periods, pre/mid/post) - No ads",
"playlist": [
{
"uri": "https://storage.googleapis.com/exoplayer-test-media-1/mkv/android-screens-lavf-56.36.100-aac-avc-main-1280x720.mkv",
"ad_tag_uri": "https://pubads.g.doubleclick.net/gampad/ads?sz=640x480&iu=/124319096/external/ad_rule_samples&ciu_szs=300x250&ad_rule=1&impl=s&gdfp_req=1&env=vp&output=vmap&unviewed_position_start=1&cust_params=deployment%3Ddevsite%26sample_ar%3Dpremidpost&cmsid=496&vid=short_onecue&correlator="
},
{
"uri": "ssai://dai.google.com/?contentSourceId=2559737&videoId=tos-dash&format=0&adsId=1"
},
{
"uri": "https://html5demos.com/assets/dizzy.mp4"
}
]
}
]
},
{ {
"name": "Playlists", "name": "Playlists",
"samples": [ "samples": [

View File

@ -16,7 +16,6 @@
package androidx.media3.demo.main; package androidx.media3.demo.main;
import android.content.Context; import android.content.Context;
import androidx.media3.common.util.Log;
import androidx.media3.database.DatabaseProvider; import androidx.media3.database.DatabaseProvider;
import androidx.media3.database.StandaloneDatabaseProvider; import androidx.media3.database.StandaloneDatabaseProvider;
import androidx.media3.datasource.DataSource; import androidx.media3.datasource.DataSource;
@ -31,12 +30,9 @@ import androidx.media3.datasource.cronet.CronetDataSource;
import androidx.media3.datasource.cronet.CronetUtil; import androidx.media3.datasource.cronet.CronetUtil;
import androidx.media3.exoplayer.DefaultRenderersFactory; import androidx.media3.exoplayer.DefaultRenderersFactory;
import androidx.media3.exoplayer.RenderersFactory; import androidx.media3.exoplayer.RenderersFactory;
import androidx.media3.exoplayer.offline.ActionFileUpgradeUtil;
import androidx.media3.exoplayer.offline.DefaultDownloadIndex;
import androidx.media3.exoplayer.offline.DownloadManager; import androidx.media3.exoplayer.offline.DownloadManager;
import androidx.media3.exoplayer.offline.DownloadNotificationHelper; import androidx.media3.exoplayer.offline.DownloadNotificationHelper;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.net.CookieHandler; import java.net.CookieHandler;
import java.net.CookieManager; import java.net.CookieManager;
import java.net.CookiePolicy; import java.net.CookiePolicy;
@ -60,8 +56,6 @@ public final class DemoUtil {
private static final boolean USE_CRONET_FOR_NETWORKING = true; private static final boolean USE_CRONET_FOR_NETWORKING = true;
private static final String TAG = "DemoUtil"; private static final String TAG = "DemoUtil";
private static final String DOWNLOAD_ACTION_FILE = "actions";
private static final String DOWNLOAD_TRACKER_ACTION_FILE = "tracked_actions";
private static final String DOWNLOAD_CONTENT_DIRECTORY = "downloads"; private static final String DOWNLOAD_CONTENT_DIRECTORY = "downloads";
private static DataSource.@MonotonicNonNull Factory dataSourceFactory; private static DataSource.@MonotonicNonNull Factory dataSourceFactory;
@ -155,14 +149,6 @@ public final class DemoUtil {
private static synchronized void ensureDownloadManagerInitialized(Context context) { private static synchronized void ensureDownloadManagerInitialized(Context context) {
if (downloadManager == null) { if (downloadManager == null) {
DefaultDownloadIndex downloadIndex = new DefaultDownloadIndex(getDatabaseProvider(context));
upgradeActionFile(
context, DOWNLOAD_ACTION_FILE, downloadIndex, /* addNewDownloadsAsCompleted= */ false);
upgradeActionFile(
context,
DOWNLOAD_TRACKER_ACTION_FILE,
downloadIndex,
/* addNewDownloadsAsCompleted= */ true);
downloadManager = downloadManager =
new DownloadManager( new DownloadManager(
context, context,
@ -175,23 +161,6 @@ public final class DemoUtil {
} }
} }
private static synchronized void upgradeActionFile(
Context context,
String fileName,
DefaultDownloadIndex downloadIndex,
boolean addNewDownloadsAsCompleted) {
try {
ActionFileUpgradeUtil.upgradeAndDelete(
new File(getDownloadDirectory(context), fileName),
/* downloadIdProvider= */ null,
downloadIndex,
/* deleteOnFailure= */ true,
addNewDownloadsAsCompleted);
} catch (IOException e) {
Log.e(TAG, "Failed to upgrade action file: " + fileName, e);
}
}
private static synchronized DatabaseProvider getDatabaseProvider(Context context) { private static synchronized DatabaseProvider getDatabaseProvider(Context context) {
if (databaseProvider == null) { if (databaseProvider == null) {
databaseProvider = new StandaloneDatabaseProvider(context); databaseProvider = new StandaloneDatabaseProvider(context);

View File

@ -15,8 +15,6 @@
*/ */
package androidx.media3.demo.main; package androidx.media3.demo.main;
import static androidx.media3.common.util.Assertions.checkNotNull;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.os.Bundle; import android.os.Bundle;
@ -43,6 +41,7 @@ import androidx.media3.exoplayer.ExoPlayer;
import androidx.media3.exoplayer.RenderersFactory; import androidx.media3.exoplayer.RenderersFactory;
import androidx.media3.exoplayer.drm.FrameworkMediaDrm; import androidx.media3.exoplayer.drm.FrameworkMediaDrm;
import androidx.media3.exoplayer.ima.ImaAdsLoader; import androidx.media3.exoplayer.ima.ImaAdsLoader;
import androidx.media3.exoplayer.ima.ImaServerSideAdInsertionMediaSource;
import androidx.media3.exoplayer.mediacodec.MediaCodecRenderer.DecoderInitializationException; import androidx.media3.exoplayer.mediacodec.MediaCodecRenderer.DecoderInitializationException;
import androidx.media3.exoplayer.mediacodec.MediaCodecUtil.DecoderQueryException; import androidx.media3.exoplayer.mediacodec.MediaCodecUtil.DecoderQueryException;
import androidx.media3.exoplayer.offline.DownloadRequest; import androidx.media3.exoplayer.offline.DownloadRequest;
@ -52,24 +51,26 @@ import androidx.media3.exoplayer.source.ads.AdsLoader;
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector; import androidx.media3.exoplayer.trackselection.DefaultTrackSelector;
import androidx.media3.exoplayer.util.DebugTextViewHelper; import androidx.media3.exoplayer.util.DebugTextViewHelper;
import androidx.media3.exoplayer.util.EventLogger; import androidx.media3.exoplayer.util.EventLogger;
import androidx.media3.ui.StyledPlayerControlView; import androidx.media3.ui.PlayerControlView;
import androidx.media3.ui.StyledPlayerView; import androidx.media3.ui.PlayerView;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/** An activity that plays media using {@link ExoPlayer}. */ /** An activity that plays media using {@link ExoPlayer}. */
public class PlayerActivity extends AppCompatActivity public class PlayerActivity extends AppCompatActivity
implements OnClickListener, StyledPlayerControlView.VisibilityListener { implements OnClickListener, PlayerControlView.VisibilityListener {
// Saved instance state keys. // Saved instance state keys.
private static final String KEY_TRACK_SELECTION_PARAMETERS = "track_selection_parameters"; private static final String KEY_TRACK_SELECTION_PARAMETERS = "track_selection_parameters";
private static final String KEY_SERVER_SIDE_ADS_LOADER_STATE = "server_side_ads_loader_state";
private static final String KEY_ITEM_INDEX = "item_index"; private static final String KEY_ITEM_INDEX = "item_index";
private static final String KEY_POSITION = "position"; private static final String KEY_POSITION = "position";
private static final String KEY_AUTO_PLAY = "auto_play"; private static final String KEY_AUTO_PLAY = "auto_play";
protected StyledPlayerView playerView; protected PlayerView playerView;
protected LinearLayout debugRootView; protected LinearLayout debugRootView;
protected TextView debugTextView; protected TextView debugTextView;
protected @Nullable ExoPlayer player; protected @Nullable ExoPlayer player;
@ -88,7 +89,10 @@ public class PlayerActivity extends AppCompatActivity
// For ad playback only. // For ad playback only.
private AdsLoader adsLoader; @Nullable private AdsLoader clientSideAdsLoader;
@Nullable private ImaServerSideAdInsertionMediaSource.AdsLoader serverSideAdsLoader;
private ImaServerSideAdInsertionMediaSource.AdsLoader.@MonotonicNonNull State
serverSideAdsLoaderState;
// Activity lifecycle. // Activity lifecycle.
@ -116,6 +120,12 @@ public class PlayerActivity extends AppCompatActivity
startAutoPlay = savedInstanceState.getBoolean(KEY_AUTO_PLAY); startAutoPlay = savedInstanceState.getBoolean(KEY_AUTO_PLAY);
startItemIndex = savedInstanceState.getInt(KEY_ITEM_INDEX); startItemIndex = savedInstanceState.getInt(KEY_ITEM_INDEX);
startPosition = savedInstanceState.getLong(KEY_POSITION); startPosition = savedInstanceState.getLong(KEY_POSITION);
Bundle adsLoaderStateBundle = savedInstanceState.getBundle(KEY_SERVER_SIDE_ADS_LOADER_STATE);
if (adsLoaderStateBundle != null) {
serverSideAdsLoaderState =
ImaServerSideAdInsertionMediaSource.AdsLoader.State.CREATOR.fromBundle(
adsLoaderStateBundle);
}
} else { } else {
trackSelectionParameters = trackSelectionParameters =
new DefaultTrackSelector.ParametersBuilder(/* context= */ this).build(); new DefaultTrackSelector.ParametersBuilder(/* context= */ this).build();
@ -127,7 +137,7 @@ public class PlayerActivity extends AppCompatActivity
public void onNewIntent(Intent intent) { public void onNewIntent(Intent intent) {
super.onNewIntent(intent); super.onNewIntent(intent);
releasePlayer(); releasePlayer();
releaseAdsLoader(); releaseClientSideAdsLoader();
clearStartPosition(); clearStartPosition();
setIntent(intent); setIntent(intent);
} }
@ -179,7 +189,7 @@ public class PlayerActivity extends AppCompatActivity
@Override @Override
public void onDestroy() { public void onDestroy() {
super.onDestroy(); super.onDestroy();
releaseAdsLoader(); releaseClientSideAdsLoader();
} }
@Override @Override
@ -208,6 +218,9 @@ public class PlayerActivity extends AppCompatActivity
outState.putBoolean(KEY_AUTO_PLAY, startAutoPlay); outState.putBoolean(KEY_AUTO_PLAY, startAutoPlay);
outState.putInt(KEY_ITEM_INDEX, startItemIndex); outState.putInt(KEY_ITEM_INDEX, startItemIndex);
outState.putLong(KEY_POSITION, startPosition); outState.putLong(KEY_POSITION, startPosition);
if (serverSideAdsLoaderState != null) {
outState.putBundle(KEY_SERVER_SIDE_ADS_LOADER_STATE, serverSideAdsLoaderState.toBundle());
}
} }
// Activity input // Activity input
@ -234,7 +247,7 @@ public class PlayerActivity extends AppCompatActivity
} }
} }
// StyledPlayerControlView.VisibilityListener implementation // PlayerControlView.VisibilityListener implementation
@Override @Override
public void onVisibilityChange(int visibility) { public void onVisibilityChange(int visibility) {
@ -247,7 +260,9 @@ public class PlayerActivity extends AppCompatActivity
setContentView(R.layout.player_activity); setContentView(R.layout.player_activity);
} }
/** @return Whether initialization was successful. */ /**
* @return Whether initialization was successful.
*/
protected boolean initializePlayer() { protected boolean initializePlayer() {
if (player == null) { if (player == null) {
Intent intent = getIntent(); Intent intent = getIntent();
@ -261,25 +276,22 @@ public class PlayerActivity extends AppCompatActivity
intent.getBooleanExtra(IntentUtil.PREFER_EXTENSION_DECODERS_EXTRA, false); intent.getBooleanExtra(IntentUtil.PREFER_EXTENSION_DECODERS_EXTRA, false);
RenderersFactory renderersFactory = RenderersFactory renderersFactory =
DemoUtil.buildRenderersFactory(/* context= */ this, preferExtensionDecoders); DemoUtil.buildRenderersFactory(/* context= */ this, preferExtensionDecoders);
MediaSource.Factory mediaSourceFactory =
new DefaultMediaSourceFactory(dataSourceFactory)
.setAdsLoaderProvider(this::getAdsLoader)
.setAdViewProvider(playerView);
trackSelector = new DefaultTrackSelector(/* context= */ this); trackSelector = new DefaultTrackSelector(/* context= */ this);
lastSeenTracksInfo = TracksInfo.EMPTY; lastSeenTracksInfo = TracksInfo.EMPTY;
player = player =
new ExoPlayer.Builder(/* context= */ this) new ExoPlayer.Builder(/* context= */ this)
.setRenderersFactory(renderersFactory) .setRenderersFactory(renderersFactory)
.setMediaSourceFactory(mediaSourceFactory) .setMediaSourceFactory(createMediaSourceFactory())
.setTrackSelector(trackSelector) .setTrackSelector(trackSelector)
.build(); .build();
player.setTrackSelectionParameters(trackSelectionParameters); player.setTrackSelectionParameters(trackSelectionParameters);
player.addListener(new PlayerEventListener()); player.addListener(new PlayerEventListener());
player.addAnalyticsListener(new EventLogger(trackSelector)); player.addAnalyticsListener(new EventLogger());
player.setAudioAttributes(AudioAttributes.DEFAULT, /* handleAudioFocus= */ true); player.setAudioAttributes(AudioAttributes.DEFAULT, /* handleAudioFocus= */ true);
player.setPlayWhenReady(startAutoPlay); player.setPlayWhenReady(startAutoPlay);
playerView.setPlayer(player); playerView.setPlayer(player);
serverSideAdsLoader.setPlayer(player);
debugViewHelper = new DebugTextViewHelper(player, debugTextView); debugViewHelper = new DebugTextViewHelper(player, debugTextView);
debugViewHelper.start(); debugViewHelper.start();
} }
@ -293,6 +305,22 @@ public class PlayerActivity extends AppCompatActivity
return true; return true;
} }
private MediaSource.Factory createMediaSourceFactory() {
ImaServerSideAdInsertionMediaSource.AdsLoader.Builder serverSideAdLoaderBuilder =
new ImaServerSideAdInsertionMediaSource.AdsLoader.Builder(/* context= */ this, playerView);
if (serverSideAdsLoaderState != null) {
serverSideAdLoaderBuilder.setAdsLoaderState(serverSideAdsLoaderState);
}
serverSideAdsLoader = serverSideAdLoaderBuilder.build();
ImaServerSideAdInsertionMediaSource.Factory imaServerSideAdInsertionMediaSourceFactory =
new ImaServerSideAdInsertionMediaSource.Factory(
serverSideAdsLoader, new DefaultMediaSourceFactory(dataSourceFactory));
return new DefaultMediaSourceFactory(dataSourceFactory)
.setAdsLoaderProvider(this::getClientSideAdsLoader)
.setAdViewProvider(playerView)
.setServerSideAdInsertionMediaSourceFactory(imaServerSideAdInsertionMediaSourceFactory);
}
private List<MediaItem> createMediaItems(Intent intent) { private List<MediaItem> createMediaItems(Intent intent) {
String action = intent.getAction(); String action = intent.getAction();
boolean actionIsListView = IntentUtil.ACTION_VIEW_LIST.equals(action); boolean actionIsListView = IntentUtil.ACTION_VIEW_LIST.equals(action);
@ -304,7 +332,6 @@ public class PlayerActivity extends AppCompatActivity
List<MediaItem> mediaItems = List<MediaItem> mediaItems =
createMediaItems(intent, DemoUtil.getDownloadTracker(/* context= */ this)); createMediaItems(intent, DemoUtil.getDownloadTracker(/* context= */ this));
boolean hasAds = false;
for (int i = 0; i < mediaItems.size(); i++) { for (int i = 0; i < mediaItems.size(); i++) {
MediaItem mediaItem = mediaItems.get(i); MediaItem mediaItem = mediaItems.get(i);
@ -318,8 +345,7 @@ public class PlayerActivity extends AppCompatActivity
return Collections.emptyList(); return Collections.emptyList();
} }
MediaItem.DrmConfiguration drmConfiguration = MediaItem.DrmConfiguration drmConfiguration = mediaItem.localConfiguration.drmConfiguration;
checkNotNull(mediaItem.localConfiguration).drmConfiguration;
if (drmConfiguration != null) { if (drmConfiguration != null) {
if (Util.SDK_INT < 18) { if (Util.SDK_INT < 18) {
showToast(R.string.error_drm_unsupported_before_api_18); showToast(R.string.error_drm_unsupported_before_api_18);
@ -331,43 +357,44 @@ public class PlayerActivity extends AppCompatActivity
return Collections.emptyList(); return Collections.emptyList();
} }
} }
hasAds |= mediaItem.localConfiguration.adsConfiguration != null;
}
if (!hasAds) {
releaseAdsLoader();
} }
return mediaItems; return mediaItems;
} }
private AdsLoader getAdsLoader(MediaItem.AdsConfiguration adsConfiguration) { private AdsLoader getClientSideAdsLoader(MediaItem.AdsConfiguration adsConfiguration) {
// The ads loader is reused for multiple playbacks, so that ad playback can resume. // The ads loader is reused for multiple playbacks, so that ad playback can resume.
if (adsLoader == null) { if (clientSideAdsLoader == null) {
adsLoader = new ImaAdsLoader.Builder(/* context= */ this).build(); clientSideAdsLoader = new ImaAdsLoader.Builder(/* context= */ this).build();
} }
adsLoader.setPlayer(player); clientSideAdsLoader.setPlayer(player);
return adsLoader; return clientSideAdsLoader;
} }
protected void releasePlayer() { protected void releasePlayer() {
if (player != null) { if (player != null) {
updateTrackSelectorParameters(); updateTrackSelectorParameters();
updateStartPosition(); updateStartPosition();
serverSideAdsLoaderState = serverSideAdsLoader.release();
serverSideAdsLoader = null;
debugViewHelper.stop(); debugViewHelper.stop();
debugViewHelper = null; debugViewHelper = null;
player.release(); player.release();
player = null; player = null;
playerView.setPlayer(/* player= */ null);
mediaItems = Collections.emptyList(); mediaItems = Collections.emptyList();
} }
if (adsLoader != null) { if (clientSideAdsLoader != null) {
adsLoader.setPlayer(null); clientSideAdsLoader.setPlayer(null);
} else {
playerView.getAdViewGroup().removeAllViews();
} }
} }
private void releaseAdsLoader() { private void releaseClientSideAdsLoader() {
if (adsLoader != null) { if (clientSideAdsLoader != null) {
adsLoader.release(); clientSideAdsLoader.release();
adsLoader = null; clientSideAdsLoader = null;
playerView.getOverlayFrameLayout().removeAllViews(); playerView.getAdViewGroup().removeAllViews();
} }
} }
@ -441,10 +468,14 @@ public class PlayerActivity extends AppCompatActivity
if (tracksInfo == lastSeenTracksInfo) { if (tracksInfo == lastSeenTracksInfo) {
return; return;
} }
if (!tracksInfo.isTypeSupportedOrEmpty(C.TRACK_TYPE_VIDEO)) { if (tracksInfo.containsType(C.TRACK_TYPE_VIDEO)
&& !tracksInfo.isTypeSupported(
C.TRACK_TYPE_VIDEO, /* allowExceedsCapabilities= */ true)) {
showToast(R.string.error_unsupported_video); showToast(R.string.error_unsupported_video);
} }
if (!tracksInfo.isTypeSupportedOrEmpty(C.TRACK_TYPE_AUDIO)) { if (tracksInfo.containsType(C.TRACK_TYPE_AUDIO)
&& !tracksInfo.isTypeSupported(
C.TRACK_TYPE_AUDIO, /* allowExceedsCapabilities= */ true)) {
showToast(R.string.error_unsupported_audio); showToast(R.string.error_unsupported_audio);
} }
lastSeenTracksInfo = tracksInfo; lastSeenTracksInfo = tracksInfo;
@ -488,7 +519,7 @@ public class PlayerActivity extends AppCompatActivity
for (MediaItem item : IntentUtil.createMediaItemsFromIntent(intent)) { for (MediaItem item : IntentUtil.createMediaItemsFromIntent(intent)) {
@Nullable @Nullable
DownloadRequest downloadRequest = DownloadRequest downloadRequest =
downloadTracker.getDownloadRequest(checkNotNull(item.localConfiguration).uri); downloadTracker.getDownloadRequest(item.localConfiguration.uri);
if (downloadRequest != null) { if (downloadRequest != null) {
MediaItem.Builder builder = item.buildUpon(); MediaItem.Builder builder = item.buildUpon();
builder builder

View File

@ -21,7 +21,7 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:keepScreenOn="true"> android:keepScreenOn="true">
<androidx.media3.ui.StyledPlayerView android:id="@+id/player_view" <androidx.media3.ui.PlayerView android:id="@+id/player_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
app:show_shuffle_button="true" app:show_shuffle_button="true"

View File

@ -61,9 +61,9 @@ dependencies {
implementation 'androidx.appcompat:appcompat:' + androidxAppCompatVersion implementation 'androidx.appcompat:appcompat:' + androidxAppCompatVersion
implementation 'androidx.multidex:multidex:' + androidxMultidexVersion implementation 'androidx.multidex:multidex:' + androidxMultidexVersion
implementation 'com.google.android.material:material:' + androidxMaterialVersion implementation 'com.google.android.material:material:' + androidxMaterialVersion
implementation project(modulePrefix + ':lib-exoplayer') implementation project(modulePrefix + 'lib-exoplayer')
implementation project(modulePrefix + ':lib-exoplayer-dash') implementation project(modulePrefix + 'lib-exoplayer-dash')
implementation project(modulePrefix + ':lib-exoplayer-hls') implementation project(modulePrefix + 'lib-exoplayer-hls')
implementation project(modulePrefix + ':lib-ui') implementation project(modulePrefix + 'lib-ui')
implementation project(modulePrefix + ':lib-session') implementation project(modulePrefix + 'lib-session')
} }

View File

@ -21,15 +21,14 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<application <application
android:allowBackup="true" android:allowBackup="false"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:label="@string/app_name" android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.Media3Demo"> android:theme="@style/Theme.Media3Demo">
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:exported="true"> android:exported="true">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
@ -37,18 +36,16 @@
</activity> </activity>
<activity <activity
android:name=".PlayerActivity" android:name=".PlayerActivity"
android:exported="true"> android:exported="true"/>
</activity>
<activity <activity
android:name=".PlayableFolderActivity" android:name=".PlayableFolderActivity"
android:exported="true"> android:exported="true"/>
</activity>
<service <service
android:name=".PlaybackService" android:name=".PlaybackService"
android:exported="true"> android:exported="true">
<intent-filter> <intent-filter>
<action android:name="androidx.media3.session.MediaSessionService"/> <action android:name="androidx.media3.session.MediaSessionService"/>
<action android:name="android.media.browse.MediaBrowserService"/> <action android:name="android.media.browse.MediaBrowserService"/>

View File

@ -2,7 +2,7 @@
"media": [ "media": [
{ {
"id": "video_01", "id": "video_01",
"title": "Future Scenerio", "title": "Future Scenario",
"album": "Mango Open Movie project", "album": "Mango Open Movie project",
"artist": "Blender Foundation", "artist": "Blender Foundation",
"genre": "Video", "genre": "Video",

View File

@ -34,7 +34,7 @@ import androidx.media3.common.MediaMetadata
import androidx.media3.common.Player import androidx.media3.common.Player
import androidx.media3.session.MediaController import androidx.media3.session.MediaController
import androidx.media3.session.SessionToken import androidx.media3.session.SessionToken
import androidx.media3.ui.StyledPlayerView import androidx.media3.ui.PlayerView
import com.google.common.util.concurrent.ListenableFuture import com.google.common.util.concurrent.ListenableFuture
import com.google.common.util.concurrent.MoreExecutors import com.google.common.util.concurrent.MoreExecutors
@ -43,7 +43,7 @@ class PlayerActivity : AppCompatActivity() {
private val controller: MediaController? private val controller: MediaController?
get() = if (controllerFuture.isDone) controllerFuture.get() else null get() = if (controllerFuture.isDone) controllerFuture.get() else null
private lateinit var playerView: StyledPlayerView private lateinit var playerView: PlayerView
private lateinit var mediaList: ListView private lateinit var mediaList: ListView
private lateinit var mediaListAdapter: PlayingMediaItemArrayAdapter private lateinit var mediaListAdapter: PlayingMediaItemArrayAdapter
private val subItemMediaList: MutableList<MediaItem> = mutableListOf() private val subItemMediaList: MutableList<MediaItem> = mutableListOf()

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 251 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 298 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 383 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 455 B

View File

@ -26,7 +26,7 @@
android:layout_height="300dp" android:layout_height="300dp"
android:layout_width="match_parent" android:layout_width="match_parent"
> >
<androidx.media3.ui.StyledPlayerView <androidx.media3.ui.PlayerView
android:id="@+id/player_view" android:id="@+id/player_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"

View File

@ -34,7 +34,7 @@
android:layout_width="50dp" android:layout_width="50dp"
android:layout_height="match_parent" android:layout_height="match_parent"
android:id="@+id/add_button" android:id="@+id/add_button"
android:background="@android:drawable/ic_input_add" android:background="@drawable/baseline_playlist_add_white_48"
/> />
</LinearLayout> </LinearLayout>

View File

@ -35,7 +35,7 @@
android:layout_width="50dp" android:layout_width="50dp"
android:layout_height="match_parent" android:layout_height="match_parent"
android:id="@+id/delete_button" android:id="@+id/delete_button"
android:background="@android:drawable/ic_menu_close_clear_cancel" android:background="@drawable/baseline_playlist_remove_white_48"
/> />
</LinearLayout> </LinearLayout>

View File

@ -22,12 +22,14 @@
<uses-sdk/> <uses-sdk/>
<application <application
android:allowBackup="false" android:allowBackup="false"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:label="@string/application_name" android:label="@string/application_name"
android:exported="true"> android:exported="true">
<activity android:name=".MainActivity"> <activity
android:name=".MainActivity"
android:exported="true">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/> <category android:name="android.intent.category.LAUNCHER"/>

View File

@ -0,0 +1,9 @@
# Transformer demo
This app demonstrates how to use the [Transformer][] API to modify videos, for
example by removing audio or video.
See the [demos README](../README.md) for instructions on how to build and run
this demo.
[Transformer]: https://exoplayer.dev/transforming-media.html

View File

@ -0,0 +1,61 @@
/*
* 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.
*/
apply from: '../../constants.gradle'
apply plugin: 'com.android.application'
android {
compileSdkVersion project.ext.compileSdkVersion
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
defaultConfig {
versionName project.ext.releaseVersion
versionCode project.ext.releaseVersionCode
minSdkVersion 21
targetSdkVersion project.ext.appTargetSdkVersion
multiDexEnabled true
}
buildTypes {
release {
shrinkResources true
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt')
signingConfig signingConfigs.debug
}
}
lintOptions {
// This demo app isn't indexed and doesn't have translations.
disable 'GoogleAppIndexingWarning','MissingTranslation'
}
}
dependencies {
implementation 'androidx.core:core:' + androidxCoreVersion
compileOnly 'org.checkerframework:checker-qual:' + checkerframeworkVersion
implementation 'androidx.annotation:annotation:' + androidxAnnotationVersion
implementation 'androidx.appcompat:appcompat:' + androidxAppCompatVersion
implementation 'androidx.constraintlayout:constraintlayout:' + androidxConstraintLayoutVersion
implementation 'androidx.multidex:multidex:' + androidxMultidexVersion
implementation 'com.google.android.material:material:' + androidxMaterialVersion
implementation project(modulePrefix + 'lib-exoplayer')
implementation project(modulePrefix + 'lib-transformer')
implementation project(modulePrefix + 'lib-ui')
}

View File

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ 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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="androidx.media3.demo.transformer">
<uses-sdk />
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<application
android:allowBackup="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/Theme.AppCompat"
android:taskAffinity=""
tools:targetApi="29">
<activity android:name=".ConfigurationActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|smallestScreenSize|uiMode"
android:launchMode="singleTop"
android:label="@string/app_name"
android:exported="true"
android:theme="@style/Theme.MaterialComponents.DayNight.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<intent-filter>
<action android:name="androidx.media3.demo.transformer.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="http"/>
<data android:scheme="https"/>
<data android:scheme="content"/>
<data android:scheme="asset"/>
<data android:scheme="file"/>
</intent-filter>
</activity>
<activity android:name=".TransformerActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|smallestScreenSize|uiMode"
android:launchMode="singleTop"
android:label="@string/app_name"
android:exported="true"
android:theme="@style/Theme.MaterialComponents.DayNight.NoActionBar"/>
</application>
</manifest>

View File

@ -0,0 +1,320 @@
/*
* 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 androidx.media3.demo.transformer;
import static androidx.media3.common.util.Assertions.checkNotNull;
import static androidx.media3.common.util.Assertions.checkState;
import android.app.Activity;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.Spinner;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.media3.common.MimeTypes;
import androidx.media3.common.util.Util;
import java.util.Arrays;
import java.util.List;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.RequiresNonNull;
/**
* An {@link Activity} that sets the configuration to use for transforming and playing media, using
* {@link TransformerActivity}.
*/
public final class ConfigurationActivity extends AppCompatActivity {
public static final String SHOULD_REMOVE_AUDIO = "should_remove_audio";
public static final String SHOULD_REMOVE_VIDEO = "should_remove_video";
public static final String SHOULD_FLATTEN_FOR_SLOW_MOTION = "should_flatten_for_slow_motion";
public static final String AUDIO_MIME_TYPE = "audio_mime_type";
public static final String VIDEO_MIME_TYPE = "video_mime_type";
public static final String RESOLUTION_HEIGHT = "resolution_height";
public static final String TRANSLATE_X = "translate_x";
public static final String TRANSLATE_Y = "translate_y";
public static final String SCALE_X = "scale_x";
public static final String SCALE_Y = "scale_y";
public static final String ROTATE_DEGREES = "rotate_degrees";
public static final String ENABLE_FALLBACK = "enable_fallback";
public static final String ENABLE_HDR_EDITING = "enable_hdr_editing";
private static final String[] INPUT_URIS = {
"https://html5demos.com/assets/dizzy.mp4",
"https://storage.googleapis.com/exoplayer-test-media-0/android-block-1080-hevc.mp4",
"https://storage.googleapis.com/exoplayer-test-media-0/BigBuckBunny_320x180.mp4",
"https://html5demos.com/assets/dizzy.webm",
"https://storage.googleapis.com/exoplayer-test-media-1/mp4/portrait_4k60.mp4",
};
private static final String[] URI_DESCRIPTIONS = { // same order as INPUT_URIS
"MP4 with H264 video and AAC audio",
"MP4 with H265 video and AAC audio",
"Long MP4 with H264 video and AAC audio",
"WebM with VP8 video and Vorbis audio",
"4K 60fps MP4 with H264 video and AAC audio (portrait, timestamps always increase)",
};
private static final String SAME_AS_INPUT_OPTION = "same as input";
private @MonotonicNonNull Button chooseFileButton;
private @MonotonicNonNull TextView chosenFileTextView;
private @MonotonicNonNull CheckBox removeAudioCheckbox;
private @MonotonicNonNull CheckBox removeVideoCheckbox;
private @MonotonicNonNull CheckBox flattenForSlowMotionCheckbox;
private @MonotonicNonNull Spinner audioMimeSpinner;
private @MonotonicNonNull Spinner videoMimeSpinner;
private @MonotonicNonNull Spinner resolutionHeightSpinner;
private @MonotonicNonNull Spinner translateSpinner;
private @MonotonicNonNull Spinner scaleSpinner;
private @MonotonicNonNull Spinner rotateSpinner;
private @MonotonicNonNull CheckBox enableFallbackCheckBox;
private @MonotonicNonNull CheckBox enableHdrEditingCheckBox;
private int inputUriPosition;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.configuration_activity);
findViewById(R.id.transform_button).setOnClickListener(this::startTransformation);
chooseFileButton = findViewById(R.id.choose_file_button);
chooseFileButton.setOnClickListener(this::chooseFile);
chosenFileTextView = findViewById(R.id.chosen_file_text_view);
chosenFileTextView.setText(URI_DESCRIPTIONS[inputUriPosition]);
removeAudioCheckbox = findViewById(R.id.remove_audio_checkbox);
removeAudioCheckbox.setOnClickListener(this::onRemoveAudio);
removeVideoCheckbox = findViewById(R.id.remove_video_checkbox);
removeVideoCheckbox.setOnClickListener(this::onRemoveVideo);
flattenForSlowMotionCheckbox = findViewById(R.id.flatten_for_slow_motion_checkbox);
ArrayAdapter<String> audioMimeAdapter =
new ArrayAdapter<>(/* context= */ this, R.layout.spinner_item);
audioMimeAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
audioMimeSpinner = findViewById(R.id.audio_mime_spinner);
audioMimeSpinner.setAdapter(audioMimeAdapter);
audioMimeAdapter.addAll(
SAME_AS_INPUT_OPTION, MimeTypes.AUDIO_AAC, MimeTypes.AUDIO_AMR_NB, MimeTypes.AUDIO_AMR_WB);
ArrayAdapter<String> videoMimeAdapter =
new ArrayAdapter<>(/* context= */ this, R.layout.spinner_item);
videoMimeAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
videoMimeSpinner = findViewById(R.id.video_mime_spinner);
videoMimeSpinner.setAdapter(videoMimeAdapter);
videoMimeAdapter.addAll(
SAME_AS_INPUT_OPTION, MimeTypes.VIDEO_H263, MimeTypes.VIDEO_H264, MimeTypes.VIDEO_MP4V);
if (Util.SDK_INT >= 24) {
videoMimeAdapter.add(MimeTypes.VIDEO_H265);
}
ArrayAdapter<String> resolutionHeightAdapter =
new ArrayAdapter<>(/* context= */ this, R.layout.spinner_item);
resolutionHeightAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
resolutionHeightSpinner = findViewById(R.id.resolution_height_spinner);
resolutionHeightSpinner.setAdapter(resolutionHeightAdapter);
resolutionHeightAdapter.addAll(
SAME_AS_INPUT_OPTION, "144", "240", "360", "480", "720", "1080", "1440", "2160");
ArrayAdapter<String> translateAdapter =
new ArrayAdapter<>(/* context= */ this, R.layout.spinner_item);
translateAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
translateSpinner = findViewById(R.id.translate_spinner);
translateSpinner.setAdapter(translateAdapter);
translateAdapter.addAll(
SAME_AS_INPUT_OPTION, "-.1, -.1", "0, 0", ".5, 0", "0, .5", "1, 1", "1.9, 0", "0, 1.9");
ArrayAdapter<String> scaleAdapter =
new ArrayAdapter<>(/* context= */ this, R.layout.spinner_item);
scaleAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
scaleSpinner = findViewById(R.id.scale_spinner);
scaleSpinner.setAdapter(scaleAdapter);
scaleAdapter.addAll(SAME_AS_INPUT_OPTION, "-1, -1", "-1, 1", "1, 1", ".5, 1", ".5, .5", "2, 2");
ArrayAdapter<String> rotateAdapter =
new ArrayAdapter<>(/* context= */ this, R.layout.spinner_item);
rotateAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
rotateSpinner = findViewById(R.id.rotate_spinner);
rotateSpinner.setAdapter(rotateAdapter);
rotateAdapter.addAll(SAME_AS_INPUT_OPTION, "0", "10", "45", "60", "90", "180");
enableFallbackCheckBox = findViewById(R.id.enable_fallback_checkbox);
enableHdrEditingCheckBox = findViewById(R.id.hdr_editing_checkbox);
}
@Override
protected void onResume() {
super.onResume();
@Nullable Uri intentUri = getIntent().getData();
if (intentUri != null) {
checkNotNull(chooseFileButton).setEnabled(false);
checkNotNull(chosenFileTextView).setText(intentUri.toString());
}
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
}
@RequiresNonNull({
"removeAudioCheckbox",
"removeVideoCheckbox",
"flattenForSlowMotionCheckbox",
"audioMimeSpinner",
"videoMimeSpinner",
"resolutionHeightSpinner",
"translateSpinner",
"scaleSpinner",
"rotateSpinner",
"enableFallbackCheckBox",
"enableHdrEditingCheckBox"
})
private void startTransformation(View view) {
Intent transformerIntent = new Intent(this, TransformerActivity.class);
Bundle bundle = new Bundle();
bundle.putBoolean(SHOULD_REMOVE_AUDIO, removeAudioCheckbox.isChecked());
bundle.putBoolean(SHOULD_REMOVE_VIDEO, removeVideoCheckbox.isChecked());
bundle.putBoolean(SHOULD_FLATTEN_FOR_SLOW_MOTION, flattenForSlowMotionCheckbox.isChecked());
String selectedAudioMimeType = String.valueOf(audioMimeSpinner.getSelectedItem());
if (!SAME_AS_INPUT_OPTION.equals(selectedAudioMimeType)) {
bundle.putString(AUDIO_MIME_TYPE, selectedAudioMimeType);
}
String selectedVideoMimeType = String.valueOf(videoMimeSpinner.getSelectedItem());
if (!SAME_AS_INPUT_OPTION.equals(selectedVideoMimeType)) {
bundle.putString(VIDEO_MIME_TYPE, selectedVideoMimeType);
}
String selectedResolutionHeight = String.valueOf(resolutionHeightSpinner.getSelectedItem());
if (!SAME_AS_INPUT_OPTION.equals(selectedResolutionHeight)) {
bundle.putInt(RESOLUTION_HEIGHT, Integer.parseInt(selectedResolutionHeight));
}
String selectedTranslate = String.valueOf(translateSpinner.getSelectedItem());
if (!SAME_AS_INPUT_OPTION.equals(selectedTranslate)) {
List<String> translateXY = Arrays.asList(selectedTranslate.split(", "));
checkState(translateXY.size() == 2);
bundle.putFloat(TRANSLATE_X, Float.parseFloat(translateXY.get(0)));
bundle.putFloat(TRANSLATE_Y, Float.parseFloat(translateXY.get(1)));
}
String selectedScale = String.valueOf(scaleSpinner.getSelectedItem());
if (!SAME_AS_INPUT_OPTION.equals(selectedScale)) {
List<String> scaleXY = Arrays.asList(selectedScale.split(", "));
checkState(scaleXY.size() == 2);
bundle.putFloat(SCALE_X, Float.parseFloat(scaleXY.get(0)));
bundle.putFloat(SCALE_Y, Float.parseFloat(scaleXY.get(1)));
}
String selectedRotate = String.valueOf(rotateSpinner.getSelectedItem());
if (!SAME_AS_INPUT_OPTION.equals(selectedRotate)) {
bundle.putFloat(ROTATE_DEGREES, Float.parseFloat(selectedRotate));
}
bundle.putBoolean(ENABLE_FALLBACK, enableFallbackCheckBox.isChecked());
bundle.putBoolean(ENABLE_HDR_EDITING, enableHdrEditingCheckBox.isChecked());
transformerIntent.putExtras(bundle);
@Nullable Uri intentUri = getIntent().getData();
transformerIntent.setData(
intentUri != null ? intentUri : Uri.parse(INPUT_URIS[inputUriPosition]));
startActivity(transformerIntent);
}
private void chooseFile(View view) {
new AlertDialog.Builder(/* context= */ this)
.setTitle(R.string.choose_file_title)
.setSingleChoiceItems(URI_DESCRIPTIONS, inputUriPosition, this::selectFileInDialog)
.setPositiveButton(android.R.string.ok, /* listener= */ null)
.create()
.show();
}
@RequiresNonNull("chosenFileTextView")
private void selectFileInDialog(DialogInterface dialog, int which) {
inputUriPosition = which;
chosenFileTextView.setText(URI_DESCRIPTIONS[inputUriPosition]);
}
@RequiresNonNull({
"removeVideoCheckbox",
"audioMimeSpinner",
"videoMimeSpinner",
"resolutionHeightSpinner",
"translateSpinner",
"scaleSpinner",
"rotateSpinner",
"enableHdrEditingCheckBox"
})
private void onRemoveAudio(View view) {
if (((CheckBox) view).isChecked()) {
removeVideoCheckbox.setChecked(false);
enableTrackSpecificOptions(/* isAudioEnabled= */ false, /* isVideoEnabled= */ true);
} else {
enableTrackSpecificOptions(/* isAudioEnabled= */ true, /* isVideoEnabled= */ true);
}
}
@RequiresNonNull({
"removeAudioCheckbox",
"audioMimeSpinner",
"videoMimeSpinner",
"resolutionHeightSpinner",
"translateSpinner",
"scaleSpinner",
"rotateSpinner",
"enableHdrEditingCheckBox"
})
private void onRemoveVideo(View view) {
if (((CheckBox) view).isChecked()) {
removeAudioCheckbox.setChecked(false);
enableTrackSpecificOptions(/* isAudioEnabled= */ true, /* isVideoEnabled= */ false);
} else {
enableTrackSpecificOptions(/* isAudioEnabled= */ true, /* isVideoEnabled= */ true);
}
}
@RequiresNonNull({
"audioMimeSpinner",
"videoMimeSpinner",
"resolutionHeightSpinner",
"translateSpinner",
"scaleSpinner",
"rotateSpinner",
"enableHdrEditingCheckBox"
})
private void enableTrackSpecificOptions(boolean isAudioEnabled, boolean isVideoEnabled) {
audioMimeSpinner.setEnabled(isAudioEnabled);
videoMimeSpinner.setEnabled(isVideoEnabled);
resolutionHeightSpinner.setEnabled(isVideoEnabled);
translateSpinner.setEnabled(isVideoEnabled);
scaleSpinner.setEnabled(isVideoEnabled);
rotateSpinner.setEnabled(isVideoEnabled);
enableHdrEditingCheckBox.setEnabled(isVideoEnabled);
findViewById(R.id.audio_mime_text_view).setEnabled(isAudioEnabled);
findViewById(R.id.video_mime_text_view).setEnabled(isVideoEnabled);
findViewById(R.id.resolution_height_text_view).setEnabled(isVideoEnabled);
findViewById(R.id.translate).setEnabled(isVideoEnabled);
findViewById(R.id.scale).setEnabled(isVideoEnabled);
findViewById(R.id.rotate).setEnabled(isVideoEnabled);
findViewById(R.id.hdr_editing).setEnabled(isVideoEnabled);
}
}

View File

@ -0,0 +1,391 @@
/*
* 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 androidx.media3.demo.transformer;
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
import static androidx.media3.common.util.Assertions.checkNotNull;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Matrix;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.media3.common.C;
import androidx.media3.common.MediaItem;
import androidx.media3.common.util.Log;
import androidx.media3.common.util.Util;
import androidx.media3.exoplayer.ExoPlayer;
import androidx.media3.exoplayer.util.DebugTextViewHelper;
import androidx.media3.transformer.DefaultEncoderFactory;
import androidx.media3.transformer.EncoderSelector;
import androidx.media3.transformer.ProgressHolder;
import androidx.media3.transformer.TransformationException;
import androidx.media3.transformer.TransformationRequest;
import androidx.media3.transformer.TransformationResult;
import androidx.media3.transformer.Transformer;
import androidx.media3.ui.AspectRatioFrameLayout;
import androidx.media3.ui.PlayerView;
import com.google.android.material.progressindicator.LinearProgressIndicator;
import com.google.common.base.Stopwatch;
import com.google.common.base.Ticker;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.RequiresNonNull;
/** An {@link Activity} that transforms and plays media using {@link Transformer}. */
public final class TransformerActivity extends AppCompatActivity {
private static final String TAG = "TransformerActivity";
private @MonotonicNonNull PlayerView playerView;
private @MonotonicNonNull TextView debugTextView;
private @MonotonicNonNull TextView informationTextView;
private @MonotonicNonNull ViewGroup progressViewGroup;
private @MonotonicNonNull LinearProgressIndicator progressIndicator;
private @MonotonicNonNull Stopwatch transformationStopwatch;
private @MonotonicNonNull AspectRatioFrameLayout debugFrame;
@Nullable private DebugTextViewHelper debugTextViewHelper;
@Nullable private ExoPlayer player;
@Nullable private Transformer transformer;
@Nullable private File externalCacheFile;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.transformer_activity);
playerView = findViewById(R.id.player_view);
debugTextView = findViewById(R.id.debug_text_view);
informationTextView = findViewById(R.id.information_text_view);
progressViewGroup = findViewById(R.id.progress_view_group);
progressIndicator = findViewById(R.id.progress_indicator);
debugFrame = findViewById(R.id.debug_aspect_ratio_frame_layout);
transformationStopwatch =
Stopwatch.createUnstarted(
new Ticker() {
public long read() {
return android.os.SystemClock.elapsedRealtimeNanos();
}
});
}
@Override
protected void onStart() {
super.onStart();
checkNotNull(progressIndicator);
checkNotNull(informationTextView);
checkNotNull(transformationStopwatch);
checkNotNull(playerView);
checkNotNull(debugTextView);
checkNotNull(progressViewGroup);
checkNotNull(debugFrame);
startTransformation();
playerView.onResume();
}
@Override
protected void onStop() {
super.onStop();
checkNotNull(transformer).cancel();
transformer = null;
// The stop watch is reset after cancelling the transformation, in case cancelling causes the
// stop watch to be stopped in a transformer callback.
checkNotNull(transformationStopwatch).reset();
checkNotNull(playerView).onPause();
releasePlayer();
checkNotNull(externalCacheFile).delete();
externalCacheFile = null;
}
@RequiresNonNull({
"playerView",
"debugTextView",
"informationTextView",
"progressIndicator",
"transformationStopwatch",
"progressViewGroup",
"debugFrame",
})
private void startTransformation() {
requestTransformerPermission();
Intent intent = getIntent();
Uri uri = checkNotNull(intent.getData());
try {
externalCacheFile = createExternalCacheFile("transformer-output.mp4");
String filePath = externalCacheFile.getAbsolutePath();
@Nullable Bundle bundle = intent.getExtras();
Transformer transformer = createTransformer(bundle, filePath);
transformationStopwatch.start();
transformer.startTransformation(MediaItem.fromUri(uri), filePath);
this.transformer = transformer;
} catch (IOException e) {
throw new IllegalStateException(e);
}
informationTextView.setText(R.string.transformation_started);
playerView.setVisibility(View.GONE);
Handler mainHandler = new Handler(getMainLooper());
ProgressHolder progressHolder = new ProgressHolder();
mainHandler.post(
new Runnable() {
@Override
public void run() {
if (transformer != null
&& transformer.getProgress(progressHolder)
!= Transformer.PROGRESS_STATE_NO_TRANSFORMATION) {
progressIndicator.setProgress(progressHolder.progress);
informationTextView.setText(
getString(
R.string.transformation_timer,
transformationStopwatch.elapsed(TimeUnit.SECONDS)));
mainHandler.postDelayed(/* r= */ this, /* delayMillis= */ 500);
}
}
});
}
// Create a cache file, resetting it if it already exists.
private File createExternalCacheFile(String fileName) throws IOException {
File file = new File(getExternalCacheDir(), fileName);
if (file.exists() && !file.delete()) {
throw new IllegalStateException("Could not delete the previous transformer output file");
}
if (!file.createNewFile()) {
throw new IllegalStateException("Could not create the transformer output file");
}
return file;
}
@RequiresNonNull({
"playerView",
"debugTextView",
"informationTextView",
"transformationStopwatch",
"progressViewGroup",
"debugFrame",
})
private Transformer createTransformer(@Nullable Bundle bundle, String filePath) {
Transformer.Builder transformerBuilder = new Transformer.Builder(/* context= */ this);
if (bundle != null) {
TransformationRequest.Builder requestBuilder = new TransformationRequest.Builder();
requestBuilder.setFlattenForSlowMotion(
bundle.getBoolean(ConfigurationActivity.SHOULD_FLATTEN_FOR_SLOW_MOTION));
@Nullable String audioMimeType = bundle.getString(ConfigurationActivity.AUDIO_MIME_TYPE);
if (audioMimeType != null) {
requestBuilder.setAudioMimeType(audioMimeType);
}
@Nullable String videoMimeType = bundle.getString(ConfigurationActivity.VIDEO_MIME_TYPE);
if (videoMimeType != null) {
requestBuilder.setVideoMimeType(videoMimeType);
}
int resolutionHeight =
bundle.getInt(
ConfigurationActivity.RESOLUTION_HEIGHT, /* defaultValue= */ C.LENGTH_UNSET);
if (resolutionHeight != C.LENGTH_UNSET) {
requestBuilder.setResolution(resolutionHeight);
}
Matrix transformationMatrix = getTransformationMatrix(bundle);
if (!transformationMatrix.isIdentity()) {
requestBuilder.setTransformationMatrix(transformationMatrix);
}
requestBuilder.experimental_setEnableHdrEditing(
bundle.getBoolean(ConfigurationActivity.ENABLE_HDR_EDITING));
transformerBuilder
.setTransformationRequest(requestBuilder.build())
.setRemoveAudio(bundle.getBoolean(ConfigurationActivity.SHOULD_REMOVE_AUDIO))
.setRemoveVideo(bundle.getBoolean(ConfigurationActivity.SHOULD_REMOVE_VIDEO))
.setEncoderFactory(
new DefaultEncoderFactory(
EncoderSelector.DEFAULT,
/* enableFallback= */ bundle.getBoolean(ConfigurationActivity.ENABLE_FALLBACK)));
}
return transformerBuilder
.addListener(
new Transformer.Listener() {
@Override
public void onTransformationCompleted(
MediaItem mediaItem, TransformationResult transformationResult) {
TransformerActivity.this.onTransformationCompleted(filePath);
}
@Override
public void onTransformationError(
MediaItem mediaItem, TransformationException exception) {
TransformerActivity.this.onTransformationError(exception);
}
})
.setDebugViewProvider(new DemoDebugViewProvider())
.build();
}
private static Matrix getTransformationMatrix(Bundle bundle) {
Matrix transformationMatrix = new Matrix();
float translateX = bundle.getFloat(ConfigurationActivity.TRANSLATE_X, /* defaultValue= */ 0);
float translateY = bundle.getFloat(ConfigurationActivity.TRANSLATE_Y, /* defaultValue= */ 0);
// TODO(b/201293185): Implement an AdvancedFrameEditor to handle translation, as the current
// transformationMatrix is automatically adjusted to focus on the original pixels and
// effectively undo translations.
transformationMatrix.postTranslate(translateX, translateY);
float scaleX = bundle.getFloat(ConfigurationActivity.SCALE_X, /* defaultValue= */ 1);
float scaleY = bundle.getFloat(ConfigurationActivity.SCALE_Y, /* defaultValue= */ 1);
transformationMatrix.postScale(scaleX, scaleY);
float rotateDegrees =
bundle.getFloat(ConfigurationActivity.ROTATE_DEGREES, /* defaultValue= */ 0);
transformationMatrix.postRotate(rotateDegrees);
return transformationMatrix;
}
@RequiresNonNull({
"informationTextView",
"progressViewGroup",
"debugFrame",
"transformationStopwatch",
})
private void onTransformationError(TransformationException exception) {
transformationStopwatch.stop();
informationTextView.setText(R.string.transformation_error);
progressViewGroup.setVisibility(View.GONE);
debugFrame.removeAllViews();
Toast.makeText(
TransformerActivity.this, "Transformation error: " + exception, Toast.LENGTH_LONG)
.show();
Log.e(TAG, "Transformation error", exception);
}
@RequiresNonNull({
"playerView",
"debugTextView",
"informationTextView",
"progressViewGroup",
"debugFrame",
"transformationStopwatch",
})
private void onTransformationCompleted(String filePath) {
transformationStopwatch.stop();
informationTextView.setText(
getString(
R.string.transformation_completed, transformationStopwatch.elapsed(TimeUnit.SECONDS)));
progressViewGroup.setVisibility(View.GONE);
debugFrame.removeAllViews();
playerView.setVisibility(View.VISIBLE);
playMediaItem(MediaItem.fromUri("file://" + filePath));
Log.d(TAG, "Output file path: file://" + filePath);
}
@RequiresNonNull({"playerView", "debugTextView"})
private void playMediaItem(MediaItem mediaItem) {
playerView.setPlayer(null);
releasePlayer();
ExoPlayer player = new ExoPlayer.Builder(/* context= */ this).build();
playerView.setPlayer(player);
player.setMediaItem(mediaItem);
player.play();
player.prepare();
this.player = player;
debugTextViewHelper = new DebugTextViewHelper(player, debugTextView);
debugTextViewHelper.start();
}
private void releasePlayer() {
if (debugTextViewHelper != null) {
debugTextViewHelper.stop();
debugTextViewHelper = null;
}
if (player != null) {
player.release();
player = null;
}
}
private void requestTransformerPermission() {
if (Util.SDK_INT < 23) {
return;
}
if (checkSelfPermission(READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[] {READ_EXTERNAL_STORAGE}, /* requestCode= */ 0);
}
}
private final class DemoDebugViewProvider implements Transformer.DebugViewProvider {
@Nullable
@Override
public SurfaceView getDebugPreviewSurfaceView(int width, int height) {
// Update the UI on the main thread and wait for the output surface to be available.
CountDownLatch surfaceCreatedCountDownLatch = new CountDownLatch(1);
SurfaceView surfaceView = new SurfaceView(/* context= */ TransformerActivity.this);
runOnUiThread(
() -> {
AspectRatioFrameLayout debugFrame = checkNotNull(TransformerActivity.this.debugFrame);
debugFrame.addView(surfaceView);
debugFrame.setAspectRatio((float) width / height);
surfaceView
.getHolder()
.addCallback(
new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
surfaceCreatedCountDownLatch.countDown();
}
@Override
public void surfaceChanged(
SurfaceHolder surfaceHolder, int format, int width, int height) {
// Do nothing.
}
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
// Do nothing.
}
});
});
try {
surfaceCreatedCountDownLatch.await();
} catch (InterruptedException e) {
Log.w(TAG, "Interrupted waiting for debug surface.");
Thread.currentThread().interrupt();
return null;
}
return surfaceView;
}
}
}

View File

@ -0,0 +1,19 @@
/*
* 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.
*/
@NonNullApi
package androidx.media3.demo.transformer;
import androidx.media3.common.util.NonNullApi;

View File

@ -0,0 +1,206 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ 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.
-->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ConfigurationActivity">
<TextView
android:id="@+id/configuration_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_marginStart="32dp"
android:layout_marginEnd="32dp"
android:text="@string/configuration"
android:textSize="24sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/choose_file_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:layout_marginStart="32dp"
android:layout_marginEnd="32dp"
android:text="@string/choose_file_title"
app:layout_constraintTop_toBottomOf="@+id/configuration_text_view"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="@+id/chosen_file_text_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:layout_marginStart="32dp"
android:layout_marginEnd="32dp"
android:paddingLeft="24dp"
android:paddingRight="24dp"
android:textSize="12sp"
android:gravity="center"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/choose_file_button" />
<androidx.core.widget.NestedScrollView
android:layout_width="fill_parent"
android:layout_height="0dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/chosen_file_text_view"
app:layout_constraintBottom_toTopOf="@+id/transform_button">
<TableLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:stretchColumns="1"
android:layout_marginTop="32dp"
android:measureWithLargestChild="true"
android:paddingLeft="24dp"
android:paddingRight="12dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent">
<TableRow
android:layout_weight="1"
android:gravity="center_vertical" >
<TextView
android:text="@string/remove_audio" />
<CheckBox
android:id="@+id/remove_audio_checkbox"
android:layout_gravity="right"/>
</TableRow>
<TableRow
android:layout_weight="1"
android:gravity="center_vertical" >
<TextView
android:text="@string/remove_video"/>
<CheckBox
android:id="@+id/remove_video_checkbox"
android:layout_gravity="right" />
</TableRow>
<TableRow
android:layout_weight="1"
android:gravity="center_vertical" >
<TextView
android:text="@string/flatten_for_slow_motion"/>
<CheckBox
android:id="@+id/flatten_for_slow_motion_checkbox"
android:layout_gravity="right" />
</TableRow>
<TableRow
android:layout_weight="1"
android:gravity="center_vertical" >
<TextView
android:id="@+id/audio_mime_text_view"
android:text="@string/audio_mime"/>
<Spinner
android:id="@+id/audio_mime_spinner"
android:layout_gravity="right|center_vertical"
android:gravity="right" />
</TableRow>
<TableRow
android:layout_weight="1"
android:gravity="center_vertical" >
<TextView
android:id="@+id/video_mime_text_view"
android:text="@string/video_mime"/>
<Spinner
android:id="@+id/video_mime_spinner"
android:layout_gravity="right|center_vertical"
android:gravity="right" />
</TableRow>
<TableRow
android:layout_weight="1"
android:gravity="center_vertical" >
<TextView
android:id="@+id/resolution_height_text_view"
android:text="@string/resolution_height"/>
<Spinner
android:id="@+id/resolution_height_spinner"
android:layout_gravity="right|center_vertical"
android:gravity="right" />
</TableRow>
<TableRow
android:layout_weight="1"
android:gravity="center_vertical" >
<TextView
android:id="@+id/translate"
android:text="@string/translate"/>
<Spinner
android:id="@+id/translate_spinner"
android:layout_gravity="right|center_vertical"
android:gravity="right" />
</TableRow>
<TableRow
android:layout_weight="1"
android:gravity="center_vertical" >
<TextView
android:id="@+id/scale"
android:text="@string/scale"/>
<Spinner
android:id="@+id/scale_spinner"
android:layout_gravity="right|center_vertical"
android:gravity="right" />
</TableRow>
<TableRow
android:layout_weight="1"
android:gravity="center_vertical" >
<TextView
android:id="@+id/rotate"
android:text="@string/rotate"/>
<Spinner
android:id="@+id/rotate_spinner"
android:layout_gravity="right|center_vertical"
android:gravity="right" />
</TableRow>
<TableRow
android:layout_weight="1"
android:gravity="center_vertical" >
<TextView
android:text="@string/enable_fallback" />
<CheckBox
android:id="@+id/enable_fallback_checkbox"
android:layout_gravity="right"
android:checked="true"/>
</TableRow>
<TableRow
android:layout_weight="1"
android:gravity="center_vertical" >
<TextView
android:id="@+id/hdr_editing"
android:text="@string/hdr_editing" />
<CheckBox
android:id="@+id/hdr_editing_checkbox"
android:layout_gravity="right" />
</TableRow>
</TableLayout>
</androidx.core.widget.NestedScrollView>
<Button
android:id="@+id/transform_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="28dp"
android:layout_marginStart="32dp"
android:layout_marginEnd="32dp"
android:text="@string/transform"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- <!--
~ Copyright 2018 The Android Open Source Project ~ Copyright 2021 The Android Open Source Project
~ ~
~ Licensed under the Apache License, Version 2.0 (the "License"); ~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License. ~ you may not use this file except in compliance with the License.
@ -13,11 +13,14 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and ~ See the License for the specific language governing permissions and
~ limitations under the License. ~ limitations under the License.
--> -->
<TextView
<resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> android:layout_width="wrap_content"
<string name="default_notification_channel_name" msgid="7213672915724563695">"Now playing"</string> android:layout_height="32dp"
<string name="play_button_content_description" msgid="963503759453979404">"Play"</string> android:gravity="left|center_vertical"
<string name="media3_controls_pause_description" msgid="3510124037191104584">"Pause"</string> android:paddingLeft="4dp"
</resources> android:paddingRight="4dp"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
android:textIsSelectable="false" />

View File

@ -0,0 +1,100 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ 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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:keepScreenOn="true"
android:orientation="vertical">
<com.google.android.material.card.MaterialCardView
android:layout_margin="8dp"
android:layout_height="wrap_content"
android:layout_width="match_parent"
app:cardCornerRadius="4dp"
app:cardElevation="2dp"
android:gravity="center_vertical" >
<TextView
android:id="@+id/information_text_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="8dp" />
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_margin="16dp"
app:cardCornerRadius="4dp"
app:cardElevation="2dp">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.media3.ui.PlayerView
android:id="@+id/player_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<TextView
android:id="@+id/debug_text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="10sp"
tools:ignore="SmallSp"/>
<LinearLayout
android:id="@+id/progress_view_group"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:padding="8dp"
android:orientation="vertical">
<com.google.android.material.progressindicator.LinearProgressIndicator
android:id="@+id/progress_indicator"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_gravity="center" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:text="@string/debug_preview" />
<androidx.media3.ui.AspectRatioFrameLayout
android:id="@+id/debug_aspect_ratio_frame_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/debug_preview_not_available" />
</androidx.media3.ui.AspectRatioFrameLayout>
</LinearLayout>
</FrameLayout>
</com.google.android.material.card.MaterialCardView>
</LinearLayout>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ 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.
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" translatable="false">Transformer Demo</string>
<string name="configuration" translatable="false">Configuration</string>
<string name="choose_file_title" translatable="false">Choose file</string>
<string name="remove_audio" translatable="false">Remove audio</string>
<string name="remove_video" translatable="false">Remove video</string>
<string name="flatten_for_slow_motion" translatable="false">Flatten for slow motion</string>
<string name="audio_mime" translatable="false">Output audio MIME type</string>
<string name="video_mime" translatable="false">Output video MIME type</string>
<string name="resolution_height" translatable="false">Output video resolution</string>
<string name="translate" translatable="false">Translate video</string>
<string name="scale" translatable="false">Scale video</string>
<string name="rotate" translatable="false">Rotate video (degrees)</string>
<string name="enable_fallback" translatable="false">Enable fallback</string>
<string name="transform" translatable="false">Transform</string>
<string name="hdr_editing" translatable="false">[Experimental] HDR editing</string>
<string name="debug_preview" translatable="false">Debug preview:</string>
<string name="debug_preview_not_available" translatable="false">No debug preview available.</string>
<string name="transformation_started" translatable="false">Transformation started</string>
<string name="transformation_timer" translatable="false">Transformation started %d seconds ago.</string>
<string name="transformation_completed" translatable="false">Transformation completed in %d seconds.</string>
<string name="transformation_error" translatable="false">Transformation error</string>
</resources>

View File

@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https://services.gradle.org/distributions/gradle-7.0.2-all.zip distributionUrl=https://services.gradle.org/distributions/gradle-7.3.3-all.zip

View File

@ -14,7 +14,7 @@
apply from: "$gradle.ext.androidxMediaSettingsDir/common_library_config.gradle" apply from: "$gradle.ext.androidxMediaSettingsDir/common_library_config.gradle"
dependencies { dependencies {
api 'com.google.android.gms:play-services-cast-framework:20.1.0' api 'com.google.android.gms:play-services-cast-framework:21.0.1'
implementation 'androidx.annotation:annotation:' + androidxAnnotationVersion implementation 'androidx.annotation:annotation:' + androidxAnnotationVersion
implementation project(modulePrefix + 'lib-common') implementation project(modulePrefix + 'lib-common')
compileOnly 'org.checkerframework:checker-qual:' + checkerframeworkVersion compileOnly 'org.checkerframework:checker-qual:' + checkerframeworkVersion

View File

@ -137,7 +137,7 @@ public final class CastPlayer extends BasePlayer {
private final SeekResultCallback seekResultCallback; private final SeekResultCallback seekResultCallback;
// Listeners and notification. // Listeners and notification.
private final ListenerSet<Player.EventListener> listeners; private final ListenerSet<Listener> listeners;
@Nullable private SessionAvailabilityListener sessionAvailabilityListener; @Nullable private SessionAvailabilityListener sessionAvailabilityListener;
// Internal state. // Internal state.
@ -150,7 +150,7 @@ public final class CastPlayer extends BasePlayer {
private TrackSelectionArray currentTrackSelection; private TrackSelectionArray currentTrackSelection;
private TracksInfo currentTracksInfo; private TracksInfo currentTracksInfo;
private Commands availableCommands; private Commands availableCommands;
@Player.State private int playbackState; private @Player.State int playbackState;
private int currentWindowIndex; private int currentWindowIndex;
private long lastReportedPositionMs; private long lastReportedPositionMs;
private int pendingSeekCount; private int pendingSeekCount;
@ -280,41 +280,11 @@ public final class CastPlayer extends BasePlayer {
@Override @Override
public void addListener(Listener listener) { public void addListener(Listener listener) {
EventListener eventListener = listener;
addListener(eventListener);
}
/**
* Registers a listener to receive events from the player.
*
* <p>The listener's methods will be called on the thread associated with {@link
* #getApplicationLooper()}.
*
* @param listener The listener to register.
* @deprecated Use {@link #addListener(Listener)} and {@link #removeListener(Listener)} instead.
*/
@Deprecated
@SuppressWarnings("deprecation")
public void addListener(EventListener listener) {
listeners.add(listener); listeners.add(listener);
} }
@Override @Override
public void removeListener(Listener listener) { public void removeListener(Listener listener) {
EventListener eventListener = listener;
removeListener(eventListener);
}
/**
* Unregister a listener registered through {@link #addListener(EventListener)}. The listener will
* no longer receive events from the player.
*
* @param listener The listener to unregister.
* @deprecated Use {@link #addListener(Listener)} and {@link #removeListener(Listener)} instead.
*/
@Deprecated
@SuppressWarnings("deprecation")
public void removeListener(EventListener listener) {
listeners.remove(listener); listeners.remove(listener);
} }
@ -387,14 +357,12 @@ public final class CastPlayer extends BasePlayer {
} }
@Override @Override
@Player.State public @Player.State int getPlaybackState() {
public int getPlaybackState() {
return playbackState; return playbackState;
} }
@Override @Override
@PlaybackSuppressionReason public @PlaybackSuppressionReason int getPlaybackSuppressionReason() {
public int getPlaybackSuppressionReason() {
return Player.PLAYBACK_SUPPRESSION_REASON_NONE; return Player.PLAYBACK_SUPPRESSION_REASON_NONE;
} }
@ -475,7 +443,7 @@ public final class CastPlayer extends BasePlayer {
} }
updateAvailableCommandsAndNotifyIfChanged(); updateAvailableCommandsAndNotifyIfChanged();
} else if (pendingSeekCount == 0) { } else if (pendingSeekCount == 0) {
listeners.queueEvent(/* eventFlag= */ C.INDEX_UNSET, EventListener::onSeekProcessed); listeners.queueEvent(/* eventFlag= */ C.INDEX_UNSET, Listener::onSeekProcessed);
} }
listeners.flushEvents(); listeners.flushEvents();
} }
@ -559,7 +527,7 @@ public final class CastPlayer extends BasePlayer {
setRepeatModeAndNotifyIfChanged(repeatMode); setRepeatModeAndNotifyIfChanged(repeatMode);
listeners.flushEvents(); listeners.flushEvents();
PendingResult<MediaChannelResult> pendingResult = PendingResult<MediaChannelResult> pendingResult =
remoteMediaClient.queueSetRepeatMode(getCastRepeatMode(repeatMode), /* jsonObject= */ null); remoteMediaClient.queueSetRepeatMode(getCastRepeatMode(repeatMode), /* customData= */ null);
this.repeatMode.pendingResultCallback = this.repeatMode.pendingResultCallback =
new ResultCallback<MediaChannelResult>() { new ResultCallback<MediaChannelResult>() {
@Override @Override
@ -574,8 +542,7 @@ public final class CastPlayer extends BasePlayer {
} }
@Override @Override
@RepeatMode public @RepeatMode int getRepeatMode() {
public int getRepeatMode() {
return repeatMode.value; return repeatMode.value;
} }
@ -1070,7 +1037,8 @@ public final class CastPlayer extends BasePlayer {
int[] trackSupport = new int[] {supported ? C.FORMAT_HANDLED : C.FORMAT_UNSUPPORTED_TYPE}; int[] trackSupport = new int[] {supported ? C.FORMAT_HANDLED : C.FORMAT_UNSUPPORTED_TYPE};
final boolean[] trackSelected = new boolean[] {selected}; final boolean[] trackSelected = new boolean[] {selected};
trackGroupInfos[i] = trackGroupInfos[i] =
new TracksInfo.TrackGroupInfo(trackGroups[i], trackSupport, trackType, trackSelected); new TracksInfo.TrackGroupInfo(
trackGroups[i], /* adaptiveSupported= */ false, trackSupport, trackSelected);
} }
TrackGroupArray newTrackGroups = new TrackGroupArray(trackGroups); TrackGroupArray newTrackGroups = new TrackGroupArray(trackGroups);
TrackSelectionArray newTrackSelections = new TrackSelectionArray(trackSelections); TrackSelectionArray newTrackSelections = new TrackSelectionArray(trackSelections);
@ -1292,8 +1260,7 @@ public final class CastPlayer extends BasePlayer {
* Retrieves the repeat mode from {@code remoteMediaClient} and maps it into a {@link * Retrieves the repeat mode from {@code remoteMediaClient} and maps it into a {@link
* Player.RepeatMode}. * Player.RepeatMode}.
*/ */
@RepeatMode private static @RepeatMode int fetchRepeatMode(RemoteMediaClient remoteMediaClient) {
private static int fetchRepeatMode(RemoteMediaClient remoteMediaClient) {
MediaStatus mediaStatus = remoteMediaClient.getMediaStatus(); MediaStatus mediaStatus = remoteMediaClient.getMediaStatus();
if (mediaStatus == null) { if (mediaStatus == null) {
// No media session active, yet. // No media session active, yet.
@ -1481,7 +1448,7 @@ public final class CastPlayer extends BasePlayer {
currentWindowIndex = pendingSeekWindowIndex; currentWindowIndex = pendingSeekWindowIndex;
pendingSeekWindowIndex = C.INDEX_UNSET; pendingSeekWindowIndex = C.INDEX_UNSET;
pendingSeekPositionMs = C.TIME_UNSET; pendingSeekPositionMs = C.TIME_UNSET;
listeners.sendEvent(/* eventFlag= */ C.INDEX_UNSET, EventListener::onSeekProcessed); listeners.sendEvent(/* eventFlag= */ C.INDEX_UNSET, Listener::onSeekProcessed);
} }
} }
} }

View File

@ -31,7 +31,9 @@ import androidx.media3.common.util.Assertions;
private final TrackGroup trackGroup; private final TrackGroup trackGroup;
/** @param trackGroup The {@link TrackGroup} from which the first track will only be selected. */ /**
* @param trackGroup The {@link TrackGroup} from which the first track will only be selected.
*/
public CastTrackSelection(TrackGroup trackGroup) { public CastTrackSelection(TrackGroup trackGroup) {
this.trackGroup = trackGroup; this.trackGroup = trackGroup;
} }

View File

@ -19,11 +19,13 @@ import android.net.Uri;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.media3.common.C; import androidx.media3.common.C;
import androidx.media3.common.MediaItem; import androidx.media3.common.MediaItem;
import androidx.media3.common.MimeTypes;
import androidx.media3.common.util.Assertions; import androidx.media3.common.util.Assertions;
import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.UnstableApi;
import com.google.android.gms.cast.MediaInfo; import com.google.android.gms.cast.MediaInfo;
import com.google.android.gms.cast.MediaMetadata; import com.google.android.gms.cast.MediaMetadata;
import com.google.android.gms.cast.MediaQueueItem; import com.google.android.gms.cast.MediaQueueItem;
import com.google.android.gms.common.images.WebImage;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.UUID; import java.util.UUID;
@ -47,10 +49,43 @@ public final class DefaultMediaItemConverter implements MediaItemConverter {
@Override @Override
public MediaItem toMediaItem(MediaQueueItem mediaQueueItem) { public MediaItem toMediaItem(MediaQueueItem mediaQueueItem) {
// `item` came from `toMediaQueueItem()` so the custom JSON data must be set. @Nullable MediaInfo mediaInfo = mediaQueueItem.getMedia();
MediaInfo mediaInfo = mediaQueueItem.getMedia();
Assertions.checkNotNull(mediaInfo); Assertions.checkNotNull(mediaInfo);
return getMediaItem(Assertions.checkNotNull(mediaInfo.getCustomData())); androidx.media3.common.MediaMetadata.Builder metadataBuilder =
new androidx.media3.common.MediaMetadata.Builder();
@Nullable MediaMetadata metadata = mediaInfo.getMetadata();
if (metadata != null) {
if (metadata.containsKey(MediaMetadata.KEY_TITLE)) {
metadataBuilder.setTitle(metadata.getString(MediaMetadata.KEY_TITLE));
}
if (metadata.containsKey(MediaMetadata.KEY_SUBTITLE)) {
metadataBuilder.setSubtitle(metadata.getString(MediaMetadata.KEY_SUBTITLE));
}
if (metadata.containsKey(MediaMetadata.KEY_ARTIST)) {
metadataBuilder.setArtist(metadata.getString(MediaMetadata.KEY_ARTIST));
}
if (metadata.containsKey(MediaMetadata.KEY_ALBUM_ARTIST)) {
metadataBuilder.setAlbumArtist(metadata.getString(MediaMetadata.KEY_ALBUM_ARTIST));
}
if (metadata.containsKey(MediaMetadata.KEY_ALBUM_TITLE)) {
metadataBuilder.setArtist(metadata.getString(MediaMetadata.KEY_ALBUM_TITLE));
}
if (!metadata.getImages().isEmpty()) {
metadataBuilder.setArtworkUri(metadata.getImages().get(0).getUrl());
}
if (metadata.containsKey(MediaMetadata.KEY_COMPOSER)) {
metadataBuilder.setComposer(metadata.getString(MediaMetadata.KEY_COMPOSER));
}
if (metadata.containsKey(MediaMetadata.KEY_DISC_NUMBER)) {
metadataBuilder.setDiscNumber(metadata.getInt(MediaMetadata.KEY_DISC_NUMBER));
}
if (metadata.containsKey(MediaMetadata.KEY_TRACK_NUMBER)) {
metadataBuilder.setTrackNumber(metadata.getInt(MediaMetadata.KEY_TRACK_NUMBER));
}
}
// `mediaQueueItem` came from `toMediaQueueItem()` so the custom JSON data must be set.
return getMediaItem(
Assertions.checkNotNull(mediaInfo.getCustomData()), metadataBuilder.build());
} }
@Override @Override
@ -59,10 +94,41 @@ public final class DefaultMediaItemConverter implements MediaItemConverter {
if (mediaItem.localConfiguration.mimeType == null) { if (mediaItem.localConfiguration.mimeType == null) {
throw new IllegalArgumentException("The item must specify its mimeType"); throw new IllegalArgumentException("The item must specify its mimeType");
} }
MediaMetadata metadata = new MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE); MediaMetadata metadata =
new MediaMetadata(
MimeTypes.isAudio(mediaItem.localConfiguration.mimeType)
? MediaMetadata.MEDIA_TYPE_MUSIC_TRACK
: MediaMetadata.MEDIA_TYPE_MOVIE);
if (mediaItem.mediaMetadata.title != null) { if (mediaItem.mediaMetadata.title != null) {
metadata.putString(MediaMetadata.KEY_TITLE, mediaItem.mediaMetadata.title.toString()); metadata.putString(MediaMetadata.KEY_TITLE, mediaItem.mediaMetadata.title.toString());
} }
if (mediaItem.mediaMetadata.subtitle != null) {
metadata.putString(MediaMetadata.KEY_SUBTITLE, mediaItem.mediaMetadata.subtitle.toString());
}
if (mediaItem.mediaMetadata.artist != null) {
metadata.putString(MediaMetadata.KEY_ARTIST, mediaItem.mediaMetadata.artist.toString());
}
if (mediaItem.mediaMetadata.albumArtist != null) {
metadata.putString(
MediaMetadata.KEY_ALBUM_ARTIST, mediaItem.mediaMetadata.albumArtist.toString());
}
if (mediaItem.mediaMetadata.albumTitle != null) {
metadata.putString(
MediaMetadata.KEY_ALBUM_TITLE, mediaItem.mediaMetadata.albumTitle.toString());
}
if (mediaItem.mediaMetadata.artworkUri != null) {
metadata.addImage(new WebImage(mediaItem.mediaMetadata.artworkUri));
}
if (mediaItem.mediaMetadata.composer != null) {
metadata.putString(MediaMetadata.KEY_COMPOSER, mediaItem.mediaMetadata.composer.toString());
}
if (mediaItem.mediaMetadata.discNumber != null) {
metadata.putInt(MediaMetadata.KEY_DISC_NUMBER, mediaItem.mediaMetadata.discNumber);
}
if (mediaItem.mediaMetadata.trackNumber != null) {
metadata.putInt(MediaMetadata.KEY_TRACK_NUMBER, mediaItem.mediaMetadata.trackNumber);
}
MediaInfo mediaInfo = MediaInfo mediaInfo =
new MediaInfo.Builder(mediaItem.localConfiguration.uri.toString()) new MediaInfo.Builder(mediaItem.localConfiguration.uri.toString())
.setStreamType(MediaInfo.STREAM_TYPE_BUFFERED) .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
@ -75,19 +141,15 @@ public final class DefaultMediaItemConverter implements MediaItemConverter {
// Deserialization. // Deserialization.
private static MediaItem getMediaItem(JSONObject customData) { private static MediaItem getMediaItem(
JSONObject customData, androidx.media3.common.MediaMetadata mediaMetadata) {
try { try {
JSONObject mediaItemJson = customData.getJSONObject(KEY_MEDIA_ITEM); JSONObject mediaItemJson = customData.getJSONObject(KEY_MEDIA_ITEM);
MediaItem.Builder builder = new MediaItem.Builder(); MediaItem.Builder builder =
builder.setUri(Uri.parse(mediaItemJson.getString(KEY_URI))); new MediaItem.Builder()
builder.setMediaId(mediaItemJson.getString(KEY_MEDIA_ID)); .setUri(Uri.parse(mediaItemJson.getString(KEY_URI)))
if (mediaItemJson.has(KEY_TITLE)) { .setMediaId(mediaItemJson.getString(KEY_MEDIA_ID))
androidx.media3.common.MediaMetadata mediaMetadata = .setMediaMetadata(mediaMetadata);
new androidx.media3.common.MediaMetadata.Builder()
.setTitle(mediaItemJson.getString(KEY_TITLE))
.build();
builder.setMediaMetadata(mediaMetadata);
}
if (mediaItemJson.has(KEY_MIME_TYPE)) { if (mediaItemJson.has(KEY_MIME_TYPE)) {
builder.setMimeType(mediaItemJson.getString(KEY_MIME_TYPE)); builder.setMimeType(mediaItemJson.getString(KEY_MIME_TYPE));
} }

View File

@ -17,9 +17,10 @@ apply from: "$gradle.ext.androidxMediaSettingsDir/common_library_config.gradle"
// the Gradle properties of each library are populated and we can automatically // the Gradle properties of each library are populated and we can automatically
// check if a 'releaseArtifactId' exists. // check if a 'releaseArtifactId' exists.
rootProject.allprojects.forEach { rootProject.allprojects.forEach {
if ((it.name.contains('lib-') || it.name.contains('test-')) if ((it.name.startsWith(modulePrefix.replace(':', '') + 'lib-')
|| it.name.startsWith(modulePrefix.replace(':', '') + 'test-'))
&& !it.name.endsWith('-common')) { && !it.name.endsWith('-common')) {
evaluationDependsOn(modulePrefix + it.name) evaluationDependsOn(':' + it.name)
} }
} }
// copybara:media3-only // copybara:media3-only
@ -36,8 +37,9 @@ dependencies {
// List all released targets as constraints. This ensures they are all // List all released targets as constraints. This ensures they are all
// resolved to the same version. // resolved to the same version.
rootProject.allprojects.forEach { rootProject.allprojects.forEach {
if (it.hasProperty('releaseArtifactId')) { if (it.hasProperty('releaseArtifactId')
implementation project(modulePrefix + it.name) && it.releaseArtifactId.startsWith('media3-')) {
implementation project(':' + it.name)
} }
} }
} }

View File

@ -37,6 +37,8 @@ public final class AdOverlayInfo {
* The purpose of the overlay. One of {@link #PURPOSE_CONTROLS}, {@link #PURPOSE_CLOSE_AD}, {@link * The purpose of the overlay. One of {@link #PURPOSE_CONTROLS}, {@link #PURPOSE_CLOSE_AD}, {@link
* #PURPOSE_OTHER} or {@link #PURPOSE_NOT_VISIBLE}. * #PURPOSE_OTHER} or {@link #PURPOSE_NOT_VISIBLE}.
*/ */
// @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility
// with Kotlin usages from before TYPE_USE was added.
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE})
@ -95,14 +97,18 @@ public final class AdOverlayInfo {
/** An optional, detailed reason that the overlay view is needed. */ /** An optional, detailed reason that the overlay view is needed. */
@Nullable public final String reasonDetail; @Nullable public final String reasonDetail;
/** @deprecated Use {@link Builder} instead. */ /**
* @deprecated Use {@link Builder} instead.
*/
@UnstableApi @UnstableApi
@Deprecated @Deprecated
public AdOverlayInfo(View view, @Purpose int purpose) { public AdOverlayInfo(View view, @Purpose int purpose) {
this(view, purpose, /* detailedReason= */ null); this(view, purpose, /* detailedReason= */ null);
} }
/** @deprecated Use {@link Builder} instead. */ /**
* @deprecated Use {@link Builder} instead.
*/
@UnstableApi @UnstableApi
@Deprecated @Deprecated
public AdOverlayInfo(View view, @Purpose int purpose, @Nullable String detailedReason) { public AdOverlayInfo(View view, @Purpose int purpose, @Nullable String detailedReason) {

View File

@ -18,6 +18,11 @@ package androidx.media3.common;
import static androidx.media3.common.util.Assertions.checkArgument; import static androidx.media3.common.util.Assertions.checkArgument;
import static androidx.media3.common.util.Assertions.checkState; import static androidx.media3.common.util.Assertions.checkState;
import static java.lang.Math.max; import static java.lang.Math.max;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.TYPE_USE;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
@ -30,6 +35,7 @@ import androidx.media3.common.util.Util;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import org.checkerframework.checker.nullness.compatqual.NullableType; import org.checkerframework.checker.nullness.compatqual.NullableType;
@ -61,7 +67,7 @@ public final class AdPlaybackState implements Bundleable {
/** The URI of each ad in the ad group. */ /** The URI of each ad in the ad group. */
public final @NullableType Uri[] uris; public final @NullableType Uri[] uris;
/** The state of each ad in the ad group. */ /** The state of each ad in the ad group. */
@AdState public final int[] states; public final @AdState int[] states;
/** The durations of each ad in the ad group, in microseconds. */ /** The durations of each ad in the ad group, in microseconds. */
public final long[] durationsUs; public final long[] durationsUs;
/** /**
@ -343,6 +349,7 @@ public final class AdPlaybackState implements Bundleable {
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@IntDef({ @IntDef({
FIELD_TIME_US, FIELD_TIME_US,
FIELD_COUNT, FIELD_COUNT,
@ -414,8 +421,11 @@ public final class AdPlaybackState implements Bundleable {
* #AD_STATE_AVAILABLE}, {@link #AD_STATE_SKIPPED}, {@link #AD_STATE_PLAYED} or {@link * #AD_STATE_AVAILABLE}, {@link #AD_STATE_SKIPPED}, {@link #AD_STATE_PLAYED} or {@link
* #AD_STATE_ERROR}. * #AD_STATE_ERROR}.
*/ */
// @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility
// with Kotlin usages from before TYPE_USE was added.
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE})
@IntDef({ @IntDef({
AD_STATE_UNAVAILABLE, AD_STATE_UNAVAILABLE,
AD_STATE_AVAILABLE, AD_STATE_AVAILABLE,
@ -913,6 +923,7 @@ public final class AdPlaybackState implements Bundleable {
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@IntDef({ @IntDef({
FIELD_AD_GROUPS, FIELD_AD_GROUPS,
FIELD_AD_RESUME_POSITION_US, FIELD_AD_RESUME_POSITION_US,

View File

@ -15,6 +15,8 @@
*/ */
package androidx.media3.common; package androidx.media3.common;
import static java.lang.annotation.ElementType.TYPE_USE;
import android.os.Bundle; import android.os.Bundle;
import androidx.annotation.DoNotInline; import androidx.annotation.DoNotInline;
import androidx.annotation.IntDef; import androidx.annotation.IntDef;
@ -25,6 +27,7 @@ import androidx.media3.common.util.Util;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method; import java.lang.reflect.Method;
/** /**
@ -40,6 +43,11 @@ import java.lang.reflect.Method;
*/ */
public final class AudioAttributes implements Bundleable { public final class AudioAttributes implements Bundleable {
/**
* The default audio attributes, where the content type is {@link C#CONTENT_TYPE_UNKNOWN}, usage
* is {@link C#USAGE_MEDIA}, capture policy is {@link C#ALLOW_CAPTURE_BY_ALL} and no flags are
* set.
*/
public static final AudioAttributes DEFAULT = new Builder().build(); public static final AudioAttributes DEFAULT = new Builder().build();
/** Builder for {@link AudioAttributes}. */ /** Builder for {@link AudioAttributes}. */
@ -65,19 +73,19 @@ public final class AudioAttributes implements Bundleable {
spatializationBehavior = C.SPATIALIZATION_BEHAVIOR_AUTO; spatializationBehavior = C.SPATIALIZATION_BEHAVIOR_AUTO;
} }
/** @see android.media.AudioAttributes.Builder#setContentType(int) */ /** See {@link android.media.AudioAttributes.Builder#setContentType(int)} */
public Builder setContentType(@C.AudioContentType int contentType) { public Builder setContentType(@C.AudioContentType int contentType) {
this.contentType = contentType; this.contentType = contentType;
return this; return this;
} }
/** @see android.media.AudioAttributes.Builder#setFlags(int) */ /** See {@link android.media.AudioAttributes.Builder#setFlags(int)} */
public Builder setFlags(@C.AudioFlags int flags) { public Builder setFlags(@C.AudioFlags int flags) {
this.flags = flags; this.flags = flags;
return this; return this;
} }
/** @see android.media.AudioAttributes.Builder#setUsage(int) */ /** See {@link android.media.AudioAttributes.Builder#setUsage(int)} */
public Builder setUsage(@C.AudioUsage int usage) { public Builder setUsage(@C.AudioUsage int usage) {
this.usage = usage; this.usage = usage;
return this; return this;
@ -91,7 +99,7 @@ public final class AudioAttributes implements Bundleable {
// TODO[b/190759307] Update javadoc to link to AudioAttributes.Builder#setSpatializationBehavior // TODO[b/190759307] Update javadoc to link to AudioAttributes.Builder#setSpatializationBehavior
// once compile SDK target is set to 32. // once compile SDK target is set to 32.
/** See AudioAttributes.Builder#setSpatializationBehavior(int). */ /** See {@code android.media.AudioAttributes.Builder.setSpatializationBehavior(int)}. */
public Builder setSpatializationBehavior(@C.SpatializationBehavior int spatializationBehavior) { public Builder setSpatializationBehavior(@C.SpatializationBehavior int spatializationBehavior) {
this.spatializationBehavior = spatializationBehavior; this.spatializationBehavior = spatializationBehavior;
return this; return this;
@ -104,10 +112,15 @@ public final class AudioAttributes implements Bundleable {
} }
} }
/** The {@link C.AudioContentType}. */
public final @C.AudioContentType int contentType; public final @C.AudioContentType int contentType;
/** The {@link C.AudioFlags}. */
public final @C.AudioFlags int flags; public final @C.AudioFlags int flags;
/** The {@link C.AudioUsage}. */
public final @C.AudioUsage int usage; public final @C.AudioUsage int usage;
/** The {@link C.AudioAllowedCapturePolicy}. */
public final @C.AudioAllowedCapturePolicy int allowedCapturePolicy; public final @C.AudioAllowedCapturePolicy int allowedCapturePolicy;
/** The {@link C.SpatializationBehavior}. */
public final @C.SpatializationBehavior int spatializationBehavior; public final @C.SpatializationBehavior int spatializationBehavior;
@Nullable private android.media.AudioAttributes audioAttributesV21; @Nullable private android.media.AudioAttributes audioAttributesV21;
@ -180,6 +193,7 @@ public final class AudioAttributes implements Bundleable {
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@IntDef({ @IntDef({
FIELD_CONTENT_TYPE, FIELD_CONTENT_TYPE,
FIELD_FLAGS, FIELD_FLAGS,

View File

@ -384,8 +384,7 @@ public abstract class BasePlayer implements Player {
: timeline.getWindow(getCurrentMediaItemIndex(), window).getDurationMs(); : timeline.getWindow(getCurrentMediaItemIndex(), window).getDurationMs();
} }
@RepeatMode private @RepeatMode int getRepeatModeForNavigation() {
private int getRepeatModeForNavigation() {
@RepeatMode int repeatMode = getRepeatMode(); @RepeatMode int repeatMode = getRepeatMode();
return repeatMode == REPEAT_MODE_ONE ? REPEAT_MODE_OFF : repeatMode; return repeatMode == REPEAT_MODE_ONE ? REPEAT_MODE_OFF : repeatMode;
} }

View File

@ -29,6 +29,7 @@ import android.media.AudioManager;
import android.media.MediaCodec; import android.media.MediaCodec;
import android.media.MediaCrypto; import android.media.MediaCrypto;
import android.media.MediaFormat; import android.media.MediaFormat;
import android.net.Uri;
import android.view.Surface; import android.view.Surface;
import androidx.annotation.IntDef; import androidx.annotation.IntDef;
import androidx.annotation.RequiresApi; import androidx.annotation.RequiresApi;
@ -68,6 +69,9 @@ public final class C {
/** Represents an unset or unknown rate. */ /** Represents an unset or unknown rate. */
public static final float RATE_UNSET = -Float.MAX_VALUE; public static final float RATE_UNSET = -Float.MAX_VALUE;
/** Represents an unset or unknown integer rate. */
@UnstableApi public static final int RATE_UNSET_INT = Integer.MIN_VALUE + 1;
/** Represents an unset or unknown length. */ /** Represents an unset or unknown length. */
public static final int LENGTH_UNSET = -1; public static final int LENGTH_UNSET = -1;
@ -125,6 +129,9 @@ public final class C {
/** The name of the sans-serif font family. */ /** The name of the sans-serif font family. */
@UnstableApi public static final String SANS_SERIF_NAME = "sans-serif"; @UnstableApi public static final String SANS_SERIF_NAME = "sans-serif";
/** The {@link Uri#getScheme() URI scheme} used for content with server side ad insertion. */
@UnstableApi public static final String SSAI_SCHEME = "ssai";
/** /**
* Types of crypto implementation. May be one of {@link #CRYPTO_TYPE_NONE}, {@link * Types of crypto implementation. May be one of {@link #CRYPTO_TYPE_NONE}, {@link
* #CRYPTO_TYPE_UNSUPPORTED} or {@link #CRYPTO_TYPE_FRAMEWORK}. May also be an app-defined value * #CRYPTO_TYPE_UNSUPPORTED} or {@link #CRYPTO_TYPE_FRAMEWORK}. May also be an app-defined value
@ -159,14 +166,21 @@ public final class C {
*/ */
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@IntDef({CRYPTO_MODE_UNENCRYPTED, CRYPTO_MODE_AES_CTR, CRYPTO_MODE_AES_CBC}) @IntDef({CRYPTO_MODE_UNENCRYPTED, CRYPTO_MODE_AES_CTR, CRYPTO_MODE_AES_CBC})
@UnstableApi @UnstableApi
public @interface CryptoMode {} public @interface CryptoMode {}
/** @see MediaCodec#CRYPTO_MODE_UNENCRYPTED */ /**
* @see MediaCodec#CRYPTO_MODE_UNENCRYPTED
*/
@UnstableApi public static final int CRYPTO_MODE_UNENCRYPTED = MediaCodec.CRYPTO_MODE_UNENCRYPTED; @UnstableApi public static final int CRYPTO_MODE_UNENCRYPTED = MediaCodec.CRYPTO_MODE_UNENCRYPTED;
/** @see MediaCodec#CRYPTO_MODE_AES_CTR */ /**
* @see MediaCodec#CRYPTO_MODE_AES_CTR
*/
@UnstableApi public static final int CRYPTO_MODE_AES_CTR = MediaCodec.CRYPTO_MODE_AES_CTR; @UnstableApi public static final int CRYPTO_MODE_AES_CTR = MediaCodec.CRYPTO_MODE_AES_CTR;
/** @see MediaCodec#CRYPTO_MODE_AES_CBC */ /**
* @see MediaCodec#CRYPTO_MODE_AES_CBC
*/
@UnstableApi public static final int CRYPTO_MODE_AES_CBC = MediaCodec.CRYPTO_MODE_AES_CBC; @UnstableApi public static final int CRYPTO_MODE_AES_CBC = MediaCodec.CRYPTO_MODE_AES_CBC;
/** /**
@ -187,6 +201,7 @@ public final class C {
@UnstableApi @UnstableApi
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@IntDef({ @IntDef({
Format.NO_VALUE, Format.NO_VALUE,
ENCODING_INVALID, ENCODING_INVALID,
@ -222,6 +237,7 @@ public final class C {
@UnstableApi @UnstableApi
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@IntDef({ @IntDef({
Format.NO_VALUE, Format.NO_VALUE,
ENCODING_INVALID, ENCODING_INVALID,
@ -233,11 +249,17 @@ public final class C {
ENCODING_PCM_FLOAT ENCODING_PCM_FLOAT
}) })
public @interface PcmEncoding {} public @interface PcmEncoding {}
/** @see AudioFormat#ENCODING_INVALID */ /**
* @see AudioFormat#ENCODING_INVALID
*/
@UnstableApi public static final int ENCODING_INVALID = AudioFormat.ENCODING_INVALID; @UnstableApi public static final int ENCODING_INVALID = AudioFormat.ENCODING_INVALID;
/** @see AudioFormat#ENCODING_PCM_8BIT */ /**
* @see AudioFormat#ENCODING_PCM_8BIT
*/
@UnstableApi public static final int ENCODING_PCM_8BIT = AudioFormat.ENCODING_PCM_8BIT; @UnstableApi public static final int ENCODING_PCM_8BIT = AudioFormat.ENCODING_PCM_8BIT;
/** @see AudioFormat#ENCODING_PCM_16BIT */ /**
* @see AudioFormat#ENCODING_PCM_16BIT
*/
@UnstableApi public static final int ENCODING_PCM_16BIT = AudioFormat.ENCODING_PCM_16BIT; @UnstableApi public static final int ENCODING_PCM_16BIT = AudioFormat.ENCODING_PCM_16BIT;
/** Like {@link #ENCODING_PCM_16BIT}, but with the bytes in big endian order. */ /** Like {@link #ENCODING_PCM_16BIT}, but with the bytes in big endian order. */
@UnstableApi public static final int ENCODING_PCM_16BIT_BIG_ENDIAN = 0x10000000; @UnstableApi public static final int ENCODING_PCM_16BIT_BIG_ENDIAN = 0x10000000;
@ -245,41 +267,69 @@ public final class C {
@UnstableApi public static final int ENCODING_PCM_24BIT = 0x20000000; @UnstableApi public static final int ENCODING_PCM_24BIT = 0x20000000;
/** PCM encoding with 32 bits per sample. */ /** PCM encoding with 32 bits per sample. */
@UnstableApi public static final int ENCODING_PCM_32BIT = 0x30000000; @UnstableApi public static final int ENCODING_PCM_32BIT = 0x30000000;
/** @see AudioFormat#ENCODING_PCM_FLOAT */ /**
* @see AudioFormat#ENCODING_PCM_FLOAT
*/
@UnstableApi public static final int ENCODING_PCM_FLOAT = AudioFormat.ENCODING_PCM_FLOAT; @UnstableApi public static final int ENCODING_PCM_FLOAT = AudioFormat.ENCODING_PCM_FLOAT;
/** @see AudioFormat#ENCODING_MP3 */ /**
* @see AudioFormat#ENCODING_MP3
*/
@UnstableApi public static final int ENCODING_MP3 = AudioFormat.ENCODING_MP3; @UnstableApi public static final int ENCODING_MP3 = AudioFormat.ENCODING_MP3;
/** @see AudioFormat#ENCODING_AAC_LC */ /**
* @see AudioFormat#ENCODING_AAC_LC
*/
@UnstableApi public static final int ENCODING_AAC_LC = AudioFormat.ENCODING_AAC_LC; @UnstableApi public static final int ENCODING_AAC_LC = AudioFormat.ENCODING_AAC_LC;
/** @see AudioFormat#ENCODING_AAC_HE_V1 */ /**
* @see AudioFormat#ENCODING_AAC_HE_V1
*/
@UnstableApi public static final int ENCODING_AAC_HE_V1 = AudioFormat.ENCODING_AAC_HE_V1; @UnstableApi public static final int ENCODING_AAC_HE_V1 = AudioFormat.ENCODING_AAC_HE_V1;
/** @see AudioFormat#ENCODING_AAC_HE_V2 */ /**
* @see AudioFormat#ENCODING_AAC_HE_V2
*/
@UnstableApi public static final int ENCODING_AAC_HE_V2 = AudioFormat.ENCODING_AAC_HE_V2; @UnstableApi public static final int ENCODING_AAC_HE_V2 = AudioFormat.ENCODING_AAC_HE_V2;
/** @see AudioFormat#ENCODING_AAC_XHE */ /**
* @see AudioFormat#ENCODING_AAC_XHE
*/
@UnstableApi public static final int ENCODING_AAC_XHE = AudioFormat.ENCODING_AAC_XHE; @UnstableApi public static final int ENCODING_AAC_XHE = AudioFormat.ENCODING_AAC_XHE;
/** @see AudioFormat#ENCODING_AAC_ELD */ /**
* @see AudioFormat#ENCODING_AAC_ELD
*/
@UnstableApi public static final int ENCODING_AAC_ELD = AudioFormat.ENCODING_AAC_ELD; @UnstableApi public static final int ENCODING_AAC_ELD = AudioFormat.ENCODING_AAC_ELD;
/** AAC Error Resilient Bit-Sliced Arithmetic Coding. */ /** AAC Error Resilient Bit-Sliced Arithmetic Coding. */
@UnstableApi public static final int ENCODING_AAC_ER_BSAC = 0x40000000; @UnstableApi public static final int ENCODING_AAC_ER_BSAC = 0x40000000;
/** @see AudioFormat#ENCODING_AC3 */ /**
* @see AudioFormat#ENCODING_AC3
*/
@UnstableApi public static final int ENCODING_AC3 = AudioFormat.ENCODING_AC3; @UnstableApi public static final int ENCODING_AC3 = AudioFormat.ENCODING_AC3;
/** @see AudioFormat#ENCODING_E_AC3 */ /**
* @see AudioFormat#ENCODING_E_AC3
*/
@UnstableApi public static final int ENCODING_E_AC3 = AudioFormat.ENCODING_E_AC3; @UnstableApi public static final int ENCODING_E_AC3 = AudioFormat.ENCODING_E_AC3;
/** @see AudioFormat#ENCODING_E_AC3_JOC */ /**
* @see AudioFormat#ENCODING_E_AC3_JOC
*/
@UnstableApi public static final int ENCODING_E_AC3_JOC = AudioFormat.ENCODING_E_AC3_JOC; @UnstableApi public static final int ENCODING_E_AC3_JOC = AudioFormat.ENCODING_E_AC3_JOC;
/** @see AudioFormat#ENCODING_AC4 */ /**
* @see AudioFormat#ENCODING_AC4
*/
@UnstableApi public static final int ENCODING_AC4 = AudioFormat.ENCODING_AC4; @UnstableApi public static final int ENCODING_AC4 = AudioFormat.ENCODING_AC4;
/** @see AudioFormat#ENCODING_DTS */ /**
* @see AudioFormat#ENCODING_DTS
*/
@UnstableApi public static final int ENCODING_DTS = AudioFormat.ENCODING_DTS; @UnstableApi public static final int ENCODING_DTS = AudioFormat.ENCODING_DTS;
/** @see AudioFormat#ENCODING_DTS_HD */ /**
* @see AudioFormat#ENCODING_DTS_HD
*/
@UnstableApi public static final int ENCODING_DTS_HD = AudioFormat.ENCODING_DTS_HD; @UnstableApi public static final int ENCODING_DTS_HD = AudioFormat.ENCODING_DTS_HD;
/** @see AudioFormat#ENCODING_DOLBY_TRUEHD */ /**
* @see AudioFormat#ENCODING_DOLBY_TRUEHD
*/
@UnstableApi public static final int ENCODING_DOLBY_TRUEHD = AudioFormat.ENCODING_DOLBY_TRUEHD; @UnstableApi public static final int ENCODING_DOLBY_TRUEHD = AudioFormat.ENCODING_DOLBY_TRUEHD;
/** Represents the behavior affecting whether spatialization will be used. */ /** Represents the behavior affecting whether spatialization will be used. */
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @Target(TYPE_USE)
@IntDef({SPATIALIZATION_BEHAVIOR_AUTO, SPATIALIZATION_BEHAVIOR_NEVER}) @IntDef({SPATIALIZATION_BEHAVIOR_AUTO, SPATIALIZATION_BEHAVIOR_NEVER})
public @interface SpatializationBehavior {} public @interface SpatializationBehavior {}
@ -296,10 +346,13 @@ public final class C {
* #STREAM_TYPE_RING}, {@link #STREAM_TYPE_SYSTEM}, {@link #STREAM_TYPE_VOICE_CALL} or {@link * #STREAM_TYPE_RING}, {@link #STREAM_TYPE_SYSTEM}, {@link #STREAM_TYPE_VOICE_CALL} or {@link
* #STREAM_TYPE_DEFAULT}. * #STREAM_TYPE_DEFAULT}.
*/ */
// @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility
// with Kotlin usages from before TYPE_USE was added.
@SuppressLint("UniqueConstants") // Intentional duplication to set STREAM_TYPE_DEFAULT. @SuppressLint("UniqueConstants") // Intentional duplication to set STREAM_TYPE_DEFAULT.
@UnstableApi @UnstableApi
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE})
@IntDef({ @IntDef({
STREAM_TYPE_ALARM, STREAM_TYPE_ALARM,
STREAM_TYPE_DTMF, STREAM_TYPE_DTMF,
@ -311,19 +364,33 @@ public final class C {
STREAM_TYPE_DEFAULT STREAM_TYPE_DEFAULT
}) })
public @interface StreamType {} public @interface StreamType {}
/** @see AudioManager#STREAM_ALARM */ /**
* @see AudioManager#STREAM_ALARM
*/
@UnstableApi public static final int STREAM_TYPE_ALARM = AudioManager.STREAM_ALARM; @UnstableApi public static final int STREAM_TYPE_ALARM = AudioManager.STREAM_ALARM;
/** @see AudioManager#STREAM_DTMF */ /**
* @see AudioManager#STREAM_DTMF
*/
@UnstableApi public static final int STREAM_TYPE_DTMF = AudioManager.STREAM_DTMF; @UnstableApi public static final int STREAM_TYPE_DTMF = AudioManager.STREAM_DTMF;
/** @see AudioManager#STREAM_MUSIC */ /**
* @see AudioManager#STREAM_MUSIC
*/
@UnstableApi public static final int STREAM_TYPE_MUSIC = AudioManager.STREAM_MUSIC; @UnstableApi public static final int STREAM_TYPE_MUSIC = AudioManager.STREAM_MUSIC;
/** @see AudioManager#STREAM_NOTIFICATION */ /**
* @see AudioManager#STREAM_NOTIFICATION
*/
@UnstableApi public static final int STREAM_TYPE_NOTIFICATION = AudioManager.STREAM_NOTIFICATION; @UnstableApi public static final int STREAM_TYPE_NOTIFICATION = AudioManager.STREAM_NOTIFICATION;
/** @see AudioManager#STREAM_RING */ /**
* @see AudioManager#STREAM_RING
*/
@UnstableApi public static final int STREAM_TYPE_RING = AudioManager.STREAM_RING; @UnstableApi public static final int STREAM_TYPE_RING = AudioManager.STREAM_RING;
/** @see AudioManager#STREAM_SYSTEM */ /**
* @see AudioManager#STREAM_SYSTEM
*/
@UnstableApi public static final int STREAM_TYPE_SYSTEM = AudioManager.STREAM_SYSTEM; @UnstableApi public static final int STREAM_TYPE_SYSTEM = AudioManager.STREAM_SYSTEM;
/** @see AudioManager#STREAM_VOICE_CALL */ /**
* @see AudioManager#STREAM_VOICE_CALL
*/
@UnstableApi public static final int STREAM_TYPE_VOICE_CALL = AudioManager.STREAM_VOICE_CALL; @UnstableApi public static final int STREAM_TYPE_VOICE_CALL = AudioManager.STREAM_VOICE_CALL;
/** The default stream type used by audio renderers. Equal to {@link #STREAM_TYPE_MUSIC}. */ /** The default stream type used by audio renderers. Equal to {@link #STREAM_TYPE_MUSIC}. */
@UnstableApi public static final int STREAM_TYPE_DEFAULT = STREAM_TYPE_MUSIC; @UnstableApi public static final int STREAM_TYPE_DEFAULT = STREAM_TYPE_MUSIC;
@ -333,6 +400,8 @@ public final class C {
* #CONTENT_TYPE_MUSIC}, {@link #CONTENT_TYPE_SONIFICATION}, {@link #CONTENT_TYPE_SPEECH} or * #CONTENT_TYPE_MUSIC}, {@link #CONTENT_TYPE_SONIFICATION}, {@link #CONTENT_TYPE_SPEECH} or
* {@link #CONTENT_TYPE_UNKNOWN}. * {@link #CONTENT_TYPE_UNKNOWN}.
*/ */
// @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility
// with Kotlin usages from before TYPE_USE was added.
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE})
@ -344,16 +413,26 @@ public final class C {
CONTENT_TYPE_UNKNOWN CONTENT_TYPE_UNKNOWN
}) })
public @interface AudioContentType {} public @interface AudioContentType {}
/** @see android.media.AudioAttributes#CONTENT_TYPE_MOVIE */ /**
* @see android.media.AudioAttributes#CONTENT_TYPE_MOVIE
*/
public static final int CONTENT_TYPE_MOVIE = android.media.AudioAttributes.CONTENT_TYPE_MOVIE; public static final int CONTENT_TYPE_MOVIE = android.media.AudioAttributes.CONTENT_TYPE_MOVIE;
/** @see android.media.AudioAttributes#CONTENT_TYPE_MUSIC */ /**
* @see android.media.AudioAttributes#CONTENT_TYPE_MUSIC
*/
public static final int CONTENT_TYPE_MUSIC = android.media.AudioAttributes.CONTENT_TYPE_MUSIC; public static final int CONTENT_TYPE_MUSIC = android.media.AudioAttributes.CONTENT_TYPE_MUSIC;
/** @see android.media.AudioAttributes#CONTENT_TYPE_SONIFICATION */ /**
* @see android.media.AudioAttributes#CONTENT_TYPE_SONIFICATION
*/
public static final int CONTENT_TYPE_SONIFICATION = public static final int CONTENT_TYPE_SONIFICATION =
android.media.AudioAttributes.CONTENT_TYPE_SONIFICATION; android.media.AudioAttributes.CONTENT_TYPE_SONIFICATION;
/** @see android.media.AudioAttributes#CONTENT_TYPE_SPEECH */ /**
* @see android.media.AudioAttributes#CONTENT_TYPE_SPEECH
*/
public static final int CONTENT_TYPE_SPEECH = android.media.AudioAttributes.CONTENT_TYPE_SPEECH; public static final int CONTENT_TYPE_SPEECH = android.media.AudioAttributes.CONTENT_TYPE_SPEECH;
/** @see android.media.AudioAttributes#CONTENT_TYPE_UNKNOWN */ /**
* @see android.media.AudioAttributes#CONTENT_TYPE_UNKNOWN
*/
public static final int CONTENT_TYPE_UNKNOWN = android.media.AudioAttributes.CONTENT_TYPE_UNKNOWN; public static final int CONTENT_TYPE_UNKNOWN = android.media.AudioAttributes.CONTENT_TYPE_UNKNOWN;
/** /**
@ -362,6 +441,8 @@ public final class C {
* <p>Note that {@code FLAG_HW_AV_SYNC} is not available because the player takes care of setting * <p>Note that {@code FLAG_HW_AV_SYNC} is not available because the player takes care of setting
* the flag when tunneling is enabled via a track selector. * the flag when tunneling is enabled via a track selector.
*/ */
// @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility
// with Kotlin usages from before TYPE_USE was added.
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE})
@ -369,7 +450,9 @@ public final class C {
flag = true, flag = true,
value = {FLAG_AUDIBILITY_ENFORCED}) value = {FLAG_AUDIBILITY_ENFORCED})
public @interface AudioFlags {} public @interface AudioFlags {}
/** @see android.media.AudioAttributes#FLAG_AUDIBILITY_ENFORCED */ /**
* @see android.media.AudioAttributes#FLAG_AUDIBILITY_ENFORCED
*/
public static final int FLAG_AUDIBILITY_ENFORCED = public static final int FLAG_AUDIBILITY_ENFORCED =
android.media.AudioAttributes.FLAG_AUDIBILITY_ENFORCED; android.media.AudioAttributes.FLAG_AUDIBILITY_ENFORCED;
@ -383,6 +466,8 @@ public final class C {
* #USAGE_NOTIFICATION_RINGTONE}, {@link #USAGE_UNKNOWN}, {@link #USAGE_VOICE_COMMUNICATION} or * #USAGE_NOTIFICATION_RINGTONE}, {@link #USAGE_UNKNOWN}, {@link #USAGE_VOICE_COMMUNICATION} or
* {@link #USAGE_VOICE_COMMUNICATION_SIGNALLING}. * {@link #USAGE_VOICE_COMMUNICATION_SIGNALLING}.
*/ */
// @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility
// with Kotlin usages from before TYPE_USE was added.
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE})
@ -405,46 +490,78 @@ public final class C {
USAGE_VOICE_COMMUNICATION_SIGNALLING USAGE_VOICE_COMMUNICATION_SIGNALLING
}) })
public @interface AudioUsage {} public @interface AudioUsage {}
/** @see android.media.AudioAttributes#USAGE_ALARM */ /**
* @see android.media.AudioAttributes#USAGE_ALARM
*/
public static final int USAGE_ALARM = android.media.AudioAttributes.USAGE_ALARM; public static final int USAGE_ALARM = android.media.AudioAttributes.USAGE_ALARM;
/** @see android.media.AudioAttributes#USAGE_ASSISTANCE_ACCESSIBILITY */ /**
* @see android.media.AudioAttributes#USAGE_ASSISTANCE_ACCESSIBILITY
*/
public static final int USAGE_ASSISTANCE_ACCESSIBILITY = public static final int USAGE_ASSISTANCE_ACCESSIBILITY =
android.media.AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY; android.media.AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY;
/** @see android.media.AudioAttributes#USAGE_ASSISTANCE_NAVIGATION_GUIDANCE */ /**
* @see android.media.AudioAttributes#USAGE_ASSISTANCE_NAVIGATION_GUIDANCE
*/
public static final int USAGE_ASSISTANCE_NAVIGATION_GUIDANCE = public static final int USAGE_ASSISTANCE_NAVIGATION_GUIDANCE =
android.media.AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE; android.media.AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE;
/** @see android.media.AudioAttributes#USAGE_ASSISTANCE_SONIFICATION */ /**
* @see android.media.AudioAttributes#USAGE_ASSISTANCE_SONIFICATION
*/
public static final int USAGE_ASSISTANCE_SONIFICATION = public static final int USAGE_ASSISTANCE_SONIFICATION =
android.media.AudioAttributes.USAGE_ASSISTANCE_SONIFICATION; android.media.AudioAttributes.USAGE_ASSISTANCE_SONIFICATION;
/** @see android.media.AudioAttributes#USAGE_ASSISTANT */ /**
* @see android.media.AudioAttributes#USAGE_ASSISTANT
*/
public static final int USAGE_ASSISTANT = android.media.AudioAttributes.USAGE_ASSISTANT; public static final int USAGE_ASSISTANT = android.media.AudioAttributes.USAGE_ASSISTANT;
/** @see android.media.AudioAttributes#USAGE_GAME */ /**
* @see android.media.AudioAttributes#USAGE_GAME
*/
public static final int USAGE_GAME = android.media.AudioAttributes.USAGE_GAME; public static final int USAGE_GAME = android.media.AudioAttributes.USAGE_GAME;
/** @see android.media.AudioAttributes#USAGE_MEDIA */ /**
* @see android.media.AudioAttributes#USAGE_MEDIA
*/
public static final int USAGE_MEDIA = android.media.AudioAttributes.USAGE_MEDIA; public static final int USAGE_MEDIA = android.media.AudioAttributes.USAGE_MEDIA;
/** @see android.media.AudioAttributes#USAGE_NOTIFICATION */ /**
* @see android.media.AudioAttributes#USAGE_NOTIFICATION
*/
public static final int USAGE_NOTIFICATION = android.media.AudioAttributes.USAGE_NOTIFICATION; public static final int USAGE_NOTIFICATION = android.media.AudioAttributes.USAGE_NOTIFICATION;
/** @see android.media.AudioAttributes#USAGE_NOTIFICATION_COMMUNICATION_DELAYED */ /**
* @see android.media.AudioAttributes#USAGE_NOTIFICATION_COMMUNICATION_DELAYED
*/
public static final int USAGE_NOTIFICATION_COMMUNICATION_DELAYED = public static final int USAGE_NOTIFICATION_COMMUNICATION_DELAYED =
android.media.AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED; android.media.AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED;
/** @see android.media.AudioAttributes#USAGE_NOTIFICATION_COMMUNICATION_INSTANT */ /**
* @see android.media.AudioAttributes#USAGE_NOTIFICATION_COMMUNICATION_INSTANT
*/
public static final int USAGE_NOTIFICATION_COMMUNICATION_INSTANT = public static final int USAGE_NOTIFICATION_COMMUNICATION_INSTANT =
android.media.AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT; android.media.AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT;
/** @see android.media.AudioAttributes#USAGE_NOTIFICATION_COMMUNICATION_REQUEST */ /**
* @see android.media.AudioAttributes#USAGE_NOTIFICATION_COMMUNICATION_REQUEST
*/
public static final int USAGE_NOTIFICATION_COMMUNICATION_REQUEST = public static final int USAGE_NOTIFICATION_COMMUNICATION_REQUEST =
android.media.AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST; android.media.AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST;
/** @see android.media.AudioAttributes#USAGE_NOTIFICATION_EVENT */ /**
* @see android.media.AudioAttributes#USAGE_NOTIFICATION_EVENT
*/
public static final int USAGE_NOTIFICATION_EVENT = public static final int USAGE_NOTIFICATION_EVENT =
android.media.AudioAttributes.USAGE_NOTIFICATION_EVENT; android.media.AudioAttributes.USAGE_NOTIFICATION_EVENT;
/** @see android.media.AudioAttributes#USAGE_NOTIFICATION_RINGTONE */ /**
* @see android.media.AudioAttributes#USAGE_NOTIFICATION_RINGTONE
*/
public static final int USAGE_NOTIFICATION_RINGTONE = public static final int USAGE_NOTIFICATION_RINGTONE =
android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE; android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
/** @see android.media.AudioAttributes#USAGE_UNKNOWN */ /**
* @see android.media.AudioAttributes#USAGE_UNKNOWN
*/
public static final int USAGE_UNKNOWN = android.media.AudioAttributes.USAGE_UNKNOWN; public static final int USAGE_UNKNOWN = android.media.AudioAttributes.USAGE_UNKNOWN;
/** @see android.media.AudioAttributes#USAGE_VOICE_COMMUNICATION */ /**
* @see android.media.AudioAttributes#USAGE_VOICE_COMMUNICATION
*/
public static final int USAGE_VOICE_COMMUNICATION = public static final int USAGE_VOICE_COMMUNICATION =
android.media.AudioAttributes.USAGE_VOICE_COMMUNICATION; android.media.AudioAttributes.USAGE_VOICE_COMMUNICATION;
/** @see android.media.AudioAttributes#USAGE_VOICE_COMMUNICATION_SIGNALLING */ /**
* @see android.media.AudioAttributes#USAGE_VOICE_COMMUNICATION_SIGNALLING
*/
public static final int USAGE_VOICE_COMMUNICATION_SIGNALLING = public static final int USAGE_VOICE_COMMUNICATION_SIGNALLING =
android.media.AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING; android.media.AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING;
@ -452,6 +569,8 @@ public final class C {
* Capture policies for audio attributes. One of {@link #ALLOW_CAPTURE_BY_ALL}, {@link * Capture policies for audio attributes. One of {@link #ALLOW_CAPTURE_BY_ALL}, {@link
* #ALLOW_CAPTURE_BY_NONE} or {@link #ALLOW_CAPTURE_BY_SYSTEM}. * #ALLOW_CAPTURE_BY_NONE} or {@link #ALLOW_CAPTURE_BY_SYSTEM}.
*/ */
// @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility
// with Kotlin usages from before TYPE_USE was added.
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE})
@ -464,62 +583,6 @@ public final class C {
/** See {@link android.media.AudioAttributes#ALLOW_CAPTURE_BY_SYSTEM}. */ /** See {@link android.media.AudioAttributes#ALLOW_CAPTURE_BY_SYSTEM}. */
public static final int ALLOW_CAPTURE_BY_SYSTEM = AudioAttributes.ALLOW_CAPTURE_BY_SYSTEM; public static final int ALLOW_CAPTURE_BY_SYSTEM = AudioAttributes.ALLOW_CAPTURE_BY_SYSTEM;
/**
* Audio focus types. One of {@link #AUDIOFOCUS_NONE}, {@link #AUDIOFOCUS_GAIN}, {@link
* #AUDIOFOCUS_GAIN_TRANSIENT}, {@link #AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK} or {@link
* #AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE}.
*/
@UnstableApi
@Documented
@Retention(RetentionPolicy.SOURCE)
@IntDef({
AUDIOFOCUS_NONE,
AUDIOFOCUS_GAIN,
AUDIOFOCUS_GAIN_TRANSIENT,
AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK,
AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE
})
public @interface AudioFocusGain {}
/** @see AudioManager#AUDIOFOCUS_NONE */
@UnstableApi public static final int AUDIOFOCUS_NONE = AudioManager.AUDIOFOCUS_NONE;
/** @see AudioManager#AUDIOFOCUS_GAIN */
@UnstableApi public static final int AUDIOFOCUS_GAIN = AudioManager.AUDIOFOCUS_GAIN;
/** @see AudioManager#AUDIOFOCUS_GAIN_TRANSIENT */
@UnstableApi
public static final int AUDIOFOCUS_GAIN_TRANSIENT = AudioManager.AUDIOFOCUS_GAIN_TRANSIENT;
/** @see AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK */
@UnstableApi
public static final int AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK =
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK;
/** @see AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE */
@UnstableApi
public static final int AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE =
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE;
/**
* Playback offload mode. One of {@link #PLAYBACK_OFFLOAD_NOT_SUPPORTED},{@link
* #PLAYBACK_OFFLOAD_SUPPORTED} or {@link #PLAYBACK_OFFLOAD_GAPLESS_SUPPORTED}.
*/
@UnstableApi
@IntDef({
PLAYBACK_OFFLOAD_NOT_SUPPORTED,
PLAYBACK_OFFLOAD_SUPPORTED,
PLAYBACK_OFFLOAD_GAPLESS_SUPPORTED
})
@Retention(RetentionPolicy.SOURCE)
public @interface AudioManagerOffloadMode {}
/** See AudioManager#PLAYBACK_OFFLOAD_NOT_SUPPORTED */
@UnstableApi
public static final int PLAYBACK_OFFLOAD_NOT_SUPPORTED =
AudioManager.PLAYBACK_OFFLOAD_NOT_SUPPORTED;
/** See AudioManager#PLAYBACK_OFFLOAD_SUPPORTED */
@UnstableApi
public static final int PLAYBACK_OFFLOAD_SUPPORTED = AudioManager.PLAYBACK_OFFLOAD_SUPPORTED;
/** See AudioManager#PLAYBACK_OFFLOAD_GAPLESS_SUPPORTED */
@UnstableApi
public static final int PLAYBACK_OFFLOAD_GAPLESS_SUPPORTED =
AudioManager.PLAYBACK_OFFLOAD_GAPLESS_SUPPORTED;
/** /**
* Flags which can apply to a buffer containing a media sample. Possible flag values are {@link * Flags which can apply to a buffer containing a media sample. Possible flag values are {@link
* #BUFFER_FLAG_KEY_FRAME}, {@link #BUFFER_FLAG_END_OF_STREAM}, {@link #BUFFER_FLAG_LAST_SAMPLE}, * #BUFFER_FLAG_KEY_FRAME}, {@link #BUFFER_FLAG_END_OF_STREAM}, {@link #BUFFER_FLAG_LAST_SAMPLE},
@ -528,6 +591,7 @@ public final class C {
@UnstableApi @UnstableApi
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@IntDef( @IntDef(
flag = true, flag = true,
value = { value = {
@ -560,6 +624,7 @@ public final class C {
@UnstableApi @UnstableApi
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@IntDef(value = {VIDEO_OUTPUT_MODE_NONE, VIDEO_OUTPUT_MODE_YUV, VIDEO_OUTPUT_MODE_SURFACE_YUV}) @IntDef(value = {VIDEO_OUTPUT_MODE_NONE, VIDEO_OUTPUT_MODE_YUV, VIDEO_OUTPUT_MODE_SURFACE_YUV})
public @interface VideoOutputMode {} public @interface VideoOutputMode {}
/** Video decoder output mode is not set. */ /** Video decoder output mode is not set. */
@ -574,10 +639,13 @@ public final class C {
* #VIDEO_SCALING_MODE_SCALE_TO_FIT}, {@link #VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING} or * #VIDEO_SCALING_MODE_SCALE_TO_FIT}, {@link #VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING} or
* {@link #VIDEO_SCALING_MODE_DEFAULT}. * {@link #VIDEO_SCALING_MODE_DEFAULT}.
*/ */
// @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility
// with Kotlin usages from before TYPE_USE was added.
@SuppressLint("UniqueConstants") // Intentional duplication to set VIDEO_SCALING_MODE_DEFAULT. @SuppressLint("UniqueConstants") // Intentional duplication to set VIDEO_SCALING_MODE_DEFAULT.
@UnstableApi @UnstableApi
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE})
@IntDef({ @IntDef({
VIDEO_SCALING_MODE_SCALE_TO_FIT, VIDEO_SCALING_MODE_SCALE_TO_FIT,
VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING, VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING,
@ -596,9 +664,12 @@ public final class C {
@UnstableApi public static final int VIDEO_SCALING_MODE_DEFAULT = VIDEO_SCALING_MODE_SCALE_TO_FIT; @UnstableApi public static final int VIDEO_SCALING_MODE_DEFAULT = VIDEO_SCALING_MODE_SCALE_TO_FIT;
/** Strategies for calling {@link Surface#setFrameRate}. */ /** Strategies for calling {@link Surface#setFrameRate}. */
// @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility
// with Kotlin usages from before TYPE_USE was added.
@UnstableApi @UnstableApi
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE})
@IntDef({VIDEO_CHANGE_FRAME_RATE_STRATEGY_OFF, VIDEO_CHANGE_FRAME_RATE_STRATEGY_ONLY_IF_SEAMLESS}) @IntDef({VIDEO_CHANGE_FRAME_RATE_STRATEGY_OFF, VIDEO_CHANGE_FRAME_RATE_STRATEGY_ONLY_IF_SEAMLESS})
public @interface VideoChangeFrameRateStrategy {} public @interface VideoChangeFrameRateStrategy {}
/** /**
@ -618,6 +689,8 @@ public final class C {
* Track selection flags. Possible flag values are {@link #SELECTION_FLAG_DEFAULT}, {@link * Track selection flags. Possible flag values are {@link #SELECTION_FLAG_DEFAULT}, {@link
* #SELECTION_FLAG_FORCED} and {@link #SELECTION_FLAG_AUTOSELECT}. * #SELECTION_FLAG_FORCED} and {@link #SELECTION_FLAG_AUTOSELECT}.
*/ */
// @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility
// with Kotlin usages from before TYPE_USE was added.
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE})
@ -651,9 +724,12 @@ public final class C {
* Represents a streaming or other media type. One of {@link #TYPE_DASH}, {@link #TYPE_SS}, {@link * Represents a streaming or other media type. One of {@link #TYPE_DASH}, {@link #TYPE_SS}, {@link
* #TYPE_HLS}, {@link #TYPE_RTSP} or {@link #TYPE_OTHER}. * #TYPE_HLS}, {@link #TYPE_RTSP} or {@link #TYPE_OTHER}.
*/ */
// @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility
// with Kotlin usages from before TYPE_USE was added.
@UnstableApi @UnstableApi
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE})
@IntDef({TYPE_DASH, TYPE_SS, TYPE_HLS, TYPE_RTSP, TYPE_OTHER}) @IntDef({TYPE_DASH, TYPE_SS, TYPE_HLS, TYPE_RTSP, TYPE_OTHER})
public @interface ContentType {} public @interface ContentType {}
/** Value returned by {@link Util#inferContentType(String)} for DASH manifests. */ /** Value returned by {@link Util#inferContentType(String)} for DASH manifests. */
@ -693,6 +769,7 @@ public final class C {
@UnstableApi @UnstableApi
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@IntDef( @IntDef(
open = true, open = true,
value = { value = {
@ -787,6 +864,7 @@ public final class C {
@UnstableApi @UnstableApi
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@IntDef( @IntDef(
open = true, open = true,
value = { value = {
@ -890,6 +968,7 @@ public final class C {
@UnstableApi @UnstableApi
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@IntDef({ @IntDef({
Format.NO_VALUE, Format.NO_VALUE,
STEREO_MODE_MONO, STEREO_MODE_MONO,
@ -917,13 +996,20 @@ public final class C {
@UnstableApi @UnstableApi
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@IntDef({Format.NO_VALUE, COLOR_SPACE_BT709, COLOR_SPACE_BT601, COLOR_SPACE_BT2020}) @IntDef({Format.NO_VALUE, COLOR_SPACE_BT709, COLOR_SPACE_BT601, COLOR_SPACE_BT2020})
public @interface ColorSpace {} public @interface ColorSpace {}
/** @see MediaFormat#COLOR_STANDARD_BT709 */ /**
* @see MediaFormat#COLOR_STANDARD_BT709
*/
@UnstableApi public static final int COLOR_SPACE_BT709 = MediaFormat.COLOR_STANDARD_BT709; @UnstableApi public static final int COLOR_SPACE_BT709 = MediaFormat.COLOR_STANDARD_BT709;
/** @see MediaFormat#COLOR_STANDARD_BT601_PAL */ /**
* @see MediaFormat#COLOR_STANDARD_BT601_PAL
*/
@UnstableApi public static final int COLOR_SPACE_BT601 = MediaFormat.COLOR_STANDARD_BT601_PAL; @UnstableApi public static final int COLOR_SPACE_BT601 = MediaFormat.COLOR_STANDARD_BT601_PAL;
/** @see MediaFormat#COLOR_STANDARD_BT2020 */ /**
* @see MediaFormat#COLOR_STANDARD_BT2020
*/
@UnstableApi public static final int COLOR_SPACE_BT2020 = MediaFormat.COLOR_STANDARD_BT2020; @UnstableApi public static final int COLOR_SPACE_BT2020 = MediaFormat.COLOR_STANDARD_BT2020;
/** /**
@ -933,13 +1019,20 @@ public final class C {
@UnstableApi @UnstableApi
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@IntDef({Format.NO_VALUE, COLOR_TRANSFER_SDR, COLOR_TRANSFER_ST2084, COLOR_TRANSFER_HLG}) @IntDef({Format.NO_VALUE, COLOR_TRANSFER_SDR, COLOR_TRANSFER_ST2084, COLOR_TRANSFER_HLG})
public @interface ColorTransfer {} public @interface ColorTransfer {}
/** @see MediaFormat#COLOR_TRANSFER_SDR_VIDEO */ /**
* @see MediaFormat#COLOR_TRANSFER_SDR_VIDEO
*/
@UnstableApi public static final int COLOR_TRANSFER_SDR = MediaFormat.COLOR_TRANSFER_SDR_VIDEO; @UnstableApi public static final int COLOR_TRANSFER_SDR = MediaFormat.COLOR_TRANSFER_SDR_VIDEO;
/** @see MediaFormat#COLOR_TRANSFER_ST2084 */ /**
* @see MediaFormat#COLOR_TRANSFER_ST2084
*/
@UnstableApi public static final int COLOR_TRANSFER_ST2084 = MediaFormat.COLOR_TRANSFER_ST2084; @UnstableApi public static final int COLOR_TRANSFER_ST2084 = MediaFormat.COLOR_TRANSFER_ST2084;
/** @see MediaFormat#COLOR_TRANSFER_HLG */ /**
* @see MediaFormat#COLOR_TRANSFER_HLG
*/
@UnstableApi public static final int COLOR_TRANSFER_HLG = MediaFormat.COLOR_TRANSFER_HLG; @UnstableApi public static final int COLOR_TRANSFER_HLG = MediaFormat.COLOR_TRANSFER_HLG;
/** /**
@ -949,17 +1042,23 @@ public final class C {
@UnstableApi @UnstableApi
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@IntDef({Format.NO_VALUE, COLOR_RANGE_LIMITED, COLOR_RANGE_FULL}) @IntDef({Format.NO_VALUE, COLOR_RANGE_LIMITED, COLOR_RANGE_FULL})
public @interface ColorRange {} public @interface ColorRange {}
/** @see MediaFormat#COLOR_RANGE_LIMITED */ /**
* @see MediaFormat#COLOR_RANGE_LIMITED
*/
@UnstableApi public static final int COLOR_RANGE_LIMITED = MediaFormat.COLOR_RANGE_LIMITED; @UnstableApi public static final int COLOR_RANGE_LIMITED = MediaFormat.COLOR_RANGE_LIMITED;
/** @see MediaFormat#COLOR_RANGE_FULL */ /**
* @see MediaFormat#COLOR_RANGE_FULL
*/
@UnstableApi public static final int COLOR_RANGE_FULL = MediaFormat.COLOR_RANGE_FULL; @UnstableApi public static final int COLOR_RANGE_FULL = MediaFormat.COLOR_RANGE_FULL;
/** Video projection types. */ /** Video projection types. */
@UnstableApi @UnstableApi
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@IntDef({ @IntDef({
Format.NO_VALUE, Format.NO_VALUE,
PROJECTION_RECTANGULAR, PROJECTION_RECTANGULAR,
@ -997,9 +1096,12 @@ public final class C {
* #NETWORK_TYPE_4G}, {@link #NETWORK_TYPE_5G_SA}, {@link #NETWORK_TYPE_5G_NSA}, {@link * #NETWORK_TYPE_4G}, {@link #NETWORK_TYPE_5G_SA}, {@link #NETWORK_TYPE_5G_NSA}, {@link
* #NETWORK_TYPE_CELLULAR_UNKNOWN}, {@link #NETWORK_TYPE_ETHERNET} or {@link #NETWORK_TYPE_OTHER}. * #NETWORK_TYPE_CELLULAR_UNKNOWN}, {@link #NETWORK_TYPE_ETHERNET} or {@link #NETWORK_TYPE_OTHER}.
*/ */
// @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility
// with Kotlin usages from before TYPE_USE was added.
@UnstableApi @UnstableApi
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE})
@IntDef({ @IntDef({
NETWORK_TYPE_UNKNOWN, NETWORK_TYPE_UNKNOWN,
NETWORK_TYPE_OFFLINE, NETWORK_TYPE_OFFLINE,
@ -1044,6 +1146,8 @@ public final class C {
* Mode specifying whether the player should hold a WakeLock and a WifiLock. One of {@link * Mode specifying whether the player should hold a WakeLock and a WifiLock. One of {@link
* #WAKE_MODE_NONE}, {@link #WAKE_MODE_LOCAL} or {@link #WAKE_MODE_NETWORK}. * #WAKE_MODE_NONE}, {@link #WAKE_MODE_LOCAL} or {@link #WAKE_MODE_NETWORK}.
*/ */
// @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility
// with Kotlin usages from before TYPE_USE was added.
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE})
@ -1081,6 +1185,8 @@ public final class C {
* {@link #ROLE_FLAG_TRANSCRIBES_DIALOG}, {@link #ROLE_FLAG_EASY_TO_READ} and {@link * {@link #ROLE_FLAG_TRANSCRIBES_DIALOG}, {@link #ROLE_FLAG_EASY_TO_READ} and {@link
* #ROLE_FLAG_TRICK_PLAY}. * #ROLE_FLAG_TRICK_PLAY}.
*/ */
// @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility
// with Kotlin usages from before TYPE_USE was added.
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE})
@ -1156,9 +1262,12 @@ public final class C {
* #FORMAT_EXCEEDS_CAPABILITIES}, {@link #FORMAT_UNSUPPORTED_DRM}, {@link * #FORMAT_EXCEEDS_CAPABILITIES}, {@link #FORMAT_UNSUPPORTED_DRM}, {@link
* #FORMAT_UNSUPPORTED_SUBTYPE} or {@link #FORMAT_UNSUPPORTED_TYPE}. * #FORMAT_UNSUPPORTED_SUBTYPE} or {@link #FORMAT_UNSUPPORTED_TYPE}.
*/ */
// @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility
// with Kotlin usages from before TYPE_USE was added.
@UnstableApi @UnstableApi
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE})
@IntDef({ @IntDef({
FORMAT_HANDLED, FORMAT_HANDLED,
FORMAT_EXCEEDS_CAPABILITIES, FORMAT_EXCEEDS_CAPABILITIES,
@ -1208,7 +1317,9 @@ public final class C {
*/ */
@UnstableApi public static final int FORMAT_UNSUPPORTED_TYPE = 0b000; @UnstableApi public static final int FORMAT_UNSUPPORTED_TYPE = 0b000;
/** @deprecated Use {@link Util#usToMs(long)}. */ /**
* @deprecated Use {@link Util#usToMs(long)}.
*/
@UnstableApi @UnstableApi
@InlineMe( @InlineMe(
replacement = "Util.usToMs(timeUs)", replacement = "Util.usToMs(timeUs)",
@ -1218,7 +1329,9 @@ public final class C {
return Util.usToMs(timeUs); return Util.usToMs(timeUs);
} }
/** @deprecated Use {@link Util#msToUs(long)}. */ /**
* @deprecated Use {@link Util#msToUs(long)}.
*/
@UnstableApi @UnstableApi
@InlineMe( @InlineMe(
replacement = "Util.msToUs(timeMs)", replacement = "Util.msToUs(timeMs)",
@ -1228,7 +1341,9 @@ public final class C {
return Util.msToUs(timeMs); return Util.msToUs(timeMs);
} }
/** @deprecated Use {@link Util#generateAudioSessionIdV21(Context)}. */ /**
* @deprecated Use {@link Util#generateAudioSessionIdV21(Context)}.
*/
@UnstableApi @UnstableApi
@InlineMe( @InlineMe(
replacement = "Util.generateAudioSessionIdV21(context)", replacement = "Util.generateAudioSessionIdV21(context)",
@ -1239,7 +1354,9 @@ public final class C {
return Util.generateAudioSessionIdV21(context); return Util.generateAudioSessionIdV21(context);
} }
/** @deprecated Use {@link Util#getFormatSupportString(int)}. */ /**
* @deprecated Use {@link Util#getFormatSupportString(int)}.
*/
@UnstableApi @UnstableApi
@InlineMe( @InlineMe(
replacement = "Util.getFormatSupportString(formatSupport)", replacement = "Util.getFormatSupportString(formatSupport)",
@ -1249,14 +1366,16 @@ public final class C {
return Util.getFormatSupportString(formatSupport); return Util.getFormatSupportString(formatSupport);
} }
/** @deprecated Use {@link Util#getErrorCodeForMediaDrmErrorCode(int)}. */ /**
* @deprecated Use {@link Util#getErrorCodeForMediaDrmErrorCode(int)}.
*/
@UnstableApi @UnstableApi
@InlineMe( @InlineMe(
replacement = "Util.getErrorCodeForMediaDrmErrorCode(mediaDrmErrorCode)", replacement = "Util.getErrorCodeForMediaDrmErrorCode(mediaDrmErrorCode)",
imports = {"androidx.media3.common.util.Util"}) imports = {"androidx.media3.common.util.Util"})
@Deprecated @Deprecated
@PlaybackException.ErrorCode public static @PlaybackException.ErrorCode int getErrorCodeForMediaDrmErrorCode(
public static int getErrorCodeForMediaDrmErrorCode(int mediaDrmErrorCode) { int mediaDrmErrorCode) {
return Util.getErrorCodeForMediaDrmErrorCode(mediaDrmErrorCode); return Util.getErrorCodeForMediaDrmErrorCode(mediaDrmErrorCode);
} }
} }

View File

@ -15,6 +15,8 @@
*/ */
package androidx.media3.common; package androidx.media3.common;
import static java.lang.annotation.ElementType.TYPE_USE;
import android.os.Bundle; import android.os.Bundle;
import androidx.annotation.IntDef; import androidx.annotation.IntDef;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@ -22,6 +24,7 @@ import androidx.media3.common.util.UnstableApi;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Arrays; import java.util.Arrays;
import org.checkerframework.dataflow.qual.Pure; import org.checkerframework.dataflow.qual.Pure;
@ -35,8 +38,7 @@ public final class ColorInfo implements Bundleable {
* made. * made.
*/ */
@Pure @Pure
@C.ColorSpace public static @C.ColorSpace int isoColorPrimariesToColorSpace(int isoColorPrimaries) {
public static int isoColorPrimariesToColorSpace(int isoColorPrimaries) {
switch (isoColorPrimaries) { switch (isoColorPrimaries) {
case 1: case 1:
return C.COLOR_SPACE_BT709; return C.COLOR_SPACE_BT709;
@ -58,8 +60,8 @@ public final class ColorInfo implements Bundleable {
* mapping can be made. * mapping can be made.
*/ */
@Pure @Pure
@C.ColorTransfer public static @C.ColorTransfer int isoTransferCharacteristicsToColorTransfer(
public static int isoTransferCharacteristicsToColorTransfer(int isoTransferCharacteristics) { int isoTransferCharacteristics) {
switch (isoTransferCharacteristics) { switch (isoTransferCharacteristics) {
case 1: // BT.709. case 1: // BT.709.
case 6: // SMPTE 170M. case 6: // SMPTE 170M.
@ -78,20 +80,20 @@ public final class ColorInfo implements Bundleable {
* The color space of the video. Valid values are {@link C#COLOR_SPACE_BT601}, {@link * The color space of the video. Valid values are {@link C#COLOR_SPACE_BT601}, {@link
* C#COLOR_SPACE_BT709}, {@link C#COLOR_SPACE_BT2020} or {@link Format#NO_VALUE} if unknown. * C#COLOR_SPACE_BT709}, {@link C#COLOR_SPACE_BT2020} or {@link Format#NO_VALUE} if unknown.
*/ */
@C.ColorSpace public final int colorSpace; public final @C.ColorSpace int colorSpace;
/** /**
* The color range of the video. Valid values are {@link C#COLOR_RANGE_LIMITED}, {@link * The color range of the video. Valid values are {@link C#COLOR_RANGE_LIMITED}, {@link
* C#COLOR_RANGE_FULL} or {@link Format#NO_VALUE} if unknown. * C#COLOR_RANGE_FULL} or {@link Format#NO_VALUE} if unknown.
*/ */
@C.ColorRange public final int colorRange; public final @C.ColorRange int colorRange;
/** /**
* The color transfer characteristics of the video. Valid values are {@link C#COLOR_TRANSFER_HLG}, * The color transfer characteristics of the video. Valid values are {@link C#COLOR_TRANSFER_HLG},
* {@link C#COLOR_TRANSFER_ST2084}, {@link C#COLOR_TRANSFER_SDR} or {@link Format#NO_VALUE} if * {@link C#COLOR_TRANSFER_ST2084}, {@link C#COLOR_TRANSFER_SDR} or {@link Format#NO_VALUE} if
* unknown. * unknown.
*/ */
@C.ColorTransfer public final int colorTransfer; public final @C.ColorTransfer int colorTransfer;
/** HdrStaticInfo as defined in CTA-861.3, or null if none specified. */ /** HdrStaticInfo as defined in CTA-861.3, or null if none specified. */
@Nullable public final byte[] hdrStaticInfo; @Nullable public final byte[] hdrStaticInfo;
@ -163,6 +165,7 @@ public final class ColorInfo implements Bundleable {
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@IntDef({ @IntDef({
FIELD_COLOR_SPACE, FIELD_COLOR_SPACE,
FIELD_COLOR_RANGE, FIELD_COLOR_RANGE,

View File

@ -15,12 +15,13 @@
*/ */
package androidx.media3.common; package androidx.media3.common;
import static java.lang.annotation.ElementType.TYPE_USE;
import android.os.Bundle; import android.os.Bundle;
import androidx.annotation.IntDef; import androidx.annotation.IntDef;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.UnstableApi;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
@ -31,7 +32,7 @@ public final class DeviceInfo implements Bundleable {
/** Types of playback. One of {@link #PLAYBACK_TYPE_LOCAL} or {@link #PLAYBACK_TYPE_REMOTE}. */ /** Types of playback. One of {@link #PLAYBACK_TYPE_LOCAL} or {@link #PLAYBACK_TYPE_REMOTE}. */
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target({ElementType.TYPE_USE}) @Target(TYPE_USE)
@IntDef({ @IntDef({
PLAYBACK_TYPE_LOCAL, PLAYBACK_TYPE_LOCAL,
PLAYBACK_TYPE_REMOTE, PLAYBACK_TYPE_REMOTE,
@ -88,6 +89,7 @@ public final class DeviceInfo implements Bundleable {
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@IntDef({FIELD_PLAYBACK_TYPE, FIELD_MIN_VOLUME, FIELD_MAX_VOLUME}) @IntDef({FIELD_PLAYBACK_TYPE, FIELD_MIN_VOLUME, FIELD_MAX_VOLUME})
private @interface FieldNumber {} private @interface FieldNumber {}

View File

@ -52,7 +52,8 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable {
* @param mediaData DRM session acquisition data obtained from the media. * @param mediaData DRM session acquisition data obtained from the media.
* @return A {@link DrmInitData} obtained from merging a media manifest and a media stream. * @return A {@link DrmInitData} obtained from merging a media manifest and a media stream.
*/ */
public static @Nullable DrmInitData createSessionCreationData( @Nullable
public static DrmInitData createSessionCreationData(
@Nullable DrmInitData manifestData, @Nullable DrmInitData mediaData) { @Nullable DrmInitData manifestData, @Nullable DrmInitData mediaData) {
ArrayList<SchemeData> result = new ArrayList<>(); ArrayList<SchemeData> result = new ArrayList<>();
String schemeType = null; String schemeType = null;
@ -91,7 +92,9 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable {
/** Number of {@link SchemeData}s. */ /** Number of {@link SchemeData}s. */
public final int schemeDataCount; public final int schemeDataCount;
/** @param schemeDatas Scheme initialization data for possibly multiple DRM schemes. */ /**
* @param schemeDatas Scheme initialization data for possibly multiple DRM schemes.
*/
public DrmInitData(List<SchemeData> schemeDatas) { public DrmInitData(List<SchemeData> schemeDatas) {
this(null, false, schemeDatas.toArray(new SchemeData[0])); this(null, false, schemeDatas.toArray(new SchemeData[0]));
} }
@ -104,7 +107,9 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable {
this(schemeType, false, schemeDatas.toArray(new SchemeData[0])); this(schemeType, false, schemeDatas.toArray(new SchemeData[0]));
} }
/** @param schemeDatas Scheme initialization data for possibly multiple DRM schemes. */ /**
* @param schemeDatas Scheme initialization data for possibly multiple DRM schemes.
*/
public DrmInitData(SchemeData... schemeDatas) { public DrmInitData(SchemeData... schemeDatas) {
this(null, schemeDatas); this(null, schemeDatas);
} }

View File

@ -16,6 +16,7 @@
package androidx.media3.common; package androidx.media3.common;
import static androidx.media3.common.MimeTypes.normalizeMimeType; import static androidx.media3.common.MimeTypes.normalizeMimeType;
import static java.lang.annotation.ElementType.TYPE_USE;
import android.net.Uri; import android.net.Uri;
import androidx.annotation.IntDef; import androidx.annotation.IntDef;
@ -25,6 +26,7 @@ import androidx.media3.common.util.UnstableApi;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -39,6 +41,7 @@ public final class FileTypes {
*/ */
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@IntDef({ @IntDef({
UNKNOWN, AC3, AC4, ADTS, AMR, FLAC, FLV, MATROSKA, MP3, MP4, OGG, PS, TS, WAV, WEBVTT, JPEG UNKNOWN, AC3, AC4, ADTS, AMR, FLAC, FLV, MATROSKA, MP3, MP4, OGG, PS, TS, WAV, WEBVTT, JPEG
}) })
@ -111,8 +114,8 @@ public final class FileTypes {
private FileTypes() {} private FileTypes() {}
/** Returns the {@link Type} corresponding to the response headers provided. */ /** Returns the {@link Type} corresponding to the response headers provided. */
@FileTypes.Type public static @FileTypes.Type int inferFileTypeFromResponseHeaders(
public static int inferFileTypeFromResponseHeaders(Map<String, List<String>> responseHeaders) { Map<String, List<String>> responseHeaders) {
@Nullable List<String> contentTypes = responseHeaders.get(HEADER_CONTENT_TYPE); @Nullable List<String> contentTypes = responseHeaders.get(HEADER_CONTENT_TYPE);
@Nullable @Nullable
String mimeType = contentTypes == null || contentTypes.isEmpty() ? null : contentTypes.get(0); String mimeType = contentTypes == null || contentTypes.isEmpty() ? null : contentTypes.get(0);
@ -124,8 +127,7 @@ public final class FileTypes {
* *
* <p>Returns {@link #UNKNOWN} if the mime type is {@code null}. * <p>Returns {@link #UNKNOWN} if the mime type is {@code null}.
*/ */
@FileTypes.Type public static @FileTypes.Type int inferFileTypeFromMimeType(@Nullable String mimeType) {
public static int inferFileTypeFromMimeType(@Nullable String mimeType) {
if (mimeType == null) { if (mimeType == null) {
return FileTypes.UNKNOWN; return FileTypes.UNKNOWN;
} }
@ -175,8 +177,7 @@ public final class FileTypes {
} }
/** Returns the {@link Type} corresponding to the {@link Uri} provided. */ /** Returns the {@link Type} corresponding to the {@link Uri} provided. */
@FileTypes.Type public static @FileTypes.Type int inferFileTypeFromUri(Uri uri) {
public static int inferFileTypeFromUri(Uri uri) {
@Nullable String filename = uri.getLastPathSegment(); @Nullable String filename = uri.getLastPathSegment();
if (filename == null) { if (filename == null) {
return FileTypes.UNKNOWN; return FileTypes.UNKNOWN;

View File

@ -15,6 +15,8 @@
*/ */
package androidx.media3.common; package androidx.media3.common;
import static java.lang.annotation.ElementType.TYPE_USE;
import android.os.Bundle; import android.os.Bundle;
import androidx.annotation.IntDef; import androidx.annotation.IntDef;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@ -25,6 +27,7 @@ import com.google.common.base.Joiner;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
@ -153,14 +156,14 @@ public final class Format implements Bundleable {
private int rotationDegrees; private int rotationDegrees;
private float pixelWidthHeightRatio; private float pixelWidthHeightRatio;
@Nullable private byte[] projectionData; @Nullable private byte[] projectionData;
@C.StereoMode private int stereoMode; private @C.StereoMode int stereoMode;
@Nullable private ColorInfo colorInfo; @Nullable private ColorInfo colorInfo;
// Audio specific. // Audio specific.
private int channelCount; private int channelCount;
private int sampleRate; private int sampleRate;
@C.PcmEncoding private int pcmEncoding; private @C.PcmEncoding int pcmEncoding;
private int encoderDelay; private int encoderDelay;
private int encoderPadding; private int encoderPadding;
@ -170,7 +173,7 @@ public final class Format implements Bundleable {
// Provided by the source. // Provided by the source.
@C.CryptoType private int cryptoType; private @C.CryptoType int cryptoType;
/** Creates a new instance with default values. */ /** Creates a new instance with default values. */
public Builder() { public Builder() {
@ -724,7 +727,7 @@ public final class Format implements Bundleable {
* modes are {@link C#STEREO_MODE_MONO}, {@link C#STEREO_MODE_TOP_BOTTOM}, {@link * modes are {@link C#STEREO_MODE_MONO}, {@link C#STEREO_MODE_TOP_BOTTOM}, {@link
* C#STEREO_MODE_LEFT_RIGHT}, {@link C#STEREO_MODE_STEREO_MESH}. * C#STEREO_MODE_LEFT_RIGHT}, {@link C#STEREO_MODE_STEREO_MESH}.
*/ */
@UnstableApi @C.StereoMode public final int stereoMode; @UnstableApi public final @C.StereoMode int stereoMode;
/** The color metadata associated with the video, or null if not applicable. */ /** The color metadata associated with the video, or null if not applicable. */
@UnstableApi @Nullable public final ColorInfo colorInfo; @UnstableApi @Nullable public final ColorInfo colorInfo;
@ -735,7 +738,7 @@ public final class Format implements Bundleable {
/** The audio sampling rate in Hz, or {@link #NO_VALUE} if unknown or not applicable. */ /** The audio sampling rate in Hz, or {@link #NO_VALUE} if unknown or not applicable. */
public final int sampleRate; public final int sampleRate;
/** The {@link C.PcmEncoding} for PCM audio. Set to {@link #NO_VALUE} for other media types. */ /** The {@link C.PcmEncoding} for PCM audio. Set to {@link #NO_VALUE} for other media types. */
@UnstableApi @C.PcmEncoding public final int pcmEncoding; @UnstableApi public final @C.PcmEncoding int pcmEncoding;
/** /**
* The number of frames to trim from the start of the decoded audio stream, or 0 if not * The number of frames to trim from the start of the decoded audio stream, or 0 if not
* applicable. * applicable.
@ -759,14 +762,16 @@ public final class Format implements Bundleable {
* {@link #drmInitData} is non-null, but may be {@link C#CRYPTO_TYPE_UNSUPPORTED} to indicate that * {@link #drmInitData} is non-null, but may be {@link C#CRYPTO_TYPE_UNSUPPORTED} to indicate that
* the samples are encrypted using an unsupported crypto type. * the samples are encrypted using an unsupported crypto type.
*/ */
@UnstableApi @C.CryptoType public final int cryptoType; @UnstableApi public final @C.CryptoType int cryptoType;
// Lazily initialized hashcode. // Lazily initialized hashcode.
private int hashCode; private int hashCode;
// Video. // Video.
/** @deprecated Use {@link Format.Builder}. */ /**
* @deprecated Use {@link Format.Builder}.
*/
@UnstableApi @UnstableApi
@Deprecated @Deprecated
public static Format createVideoSampleFormat( public static Format createVideoSampleFormat(
@ -795,7 +800,9 @@ public final class Format implements Bundleable {
.build(); .build();
} }
/** @deprecated Use {@link Format.Builder}. */ /**
* @deprecated Use {@link Format.Builder}.
*/
@UnstableApi @UnstableApi
@Deprecated @Deprecated
public static Format createVideoSampleFormat( public static Format createVideoSampleFormat(
@ -830,7 +837,9 @@ public final class Format implements Bundleable {
// Audio. // Audio.
/** @deprecated Use {@link Format.Builder}. */ /**
* @deprecated Use {@link Format.Builder}.
*/
@UnstableApi @UnstableApi
@Deprecated @Deprecated
public static Format createAudioSampleFormat( public static Format createAudioSampleFormat(
@ -861,7 +870,9 @@ public final class Format implements Bundleable {
.build(); .build();
} }
/** @deprecated Use {@link Format.Builder}. */ /**
* @deprecated Use {@link Format.Builder}.
*/
@UnstableApi @UnstableApi
@Deprecated @Deprecated
public static Format createAudioSampleFormat( public static Format createAudioSampleFormat(
@ -896,7 +907,9 @@ public final class Format implements Bundleable {
// Generic. // Generic.
/** @deprecated Use {@link Format.Builder}. */ /**
* @deprecated Use {@link Format.Builder}.
*/
@UnstableApi @UnstableApi
@Deprecated @Deprecated
public static Format createContainerFormat( public static Format createContainerFormat(
@ -923,7 +936,9 @@ public final class Format implements Bundleable {
.build(); .build();
} }
/** @deprecated Use {@link Format.Builder}. */ /**
* @deprecated Use {@link Format.Builder}.
*/
@UnstableApi @UnstableApi
@Deprecated @Deprecated
public static Format createSampleFormat(@Nullable String id, @Nullable String sampleMimeType) { public static Format createSampleFormat(@Nullable String id, @Nullable String sampleMimeType) {
@ -983,28 +998,36 @@ public final class Format implements Bundleable {
return new Builder(this); return new Builder(this);
} }
/** @deprecated Use {@link #buildUpon()} and {@link Builder#setMaxInputSize(int)}. */ /**
* @deprecated Use {@link #buildUpon()} and {@link Builder#setMaxInputSize(int)}.
*/
@UnstableApi @UnstableApi
@Deprecated @Deprecated
public Format copyWithMaxInputSize(int maxInputSize) { public Format copyWithMaxInputSize(int maxInputSize) {
return buildUpon().setMaxInputSize(maxInputSize).build(); return buildUpon().setMaxInputSize(maxInputSize).build();
} }
/** @deprecated Use {@link #buildUpon()} and {@link Builder#setSubsampleOffsetUs(long)}. */ /**
* @deprecated Use {@link #buildUpon()} and {@link Builder#setSubsampleOffsetUs(long)}.
*/
@UnstableApi @UnstableApi
@Deprecated @Deprecated
public Format copyWithSubsampleOffsetUs(long subsampleOffsetUs) { public Format copyWithSubsampleOffsetUs(long subsampleOffsetUs) {
return buildUpon().setSubsampleOffsetUs(subsampleOffsetUs).build(); return buildUpon().setSubsampleOffsetUs(subsampleOffsetUs).build();
} }
/** @deprecated Use {@link #buildUpon()} and {@link Builder#setLabel(String)} . */ /**
* @deprecated Use {@link #buildUpon()} and {@link Builder#setLabel(String)} .
*/
@UnstableApi @UnstableApi
@Deprecated @Deprecated
public Format copyWithLabel(@Nullable String label) { public Format copyWithLabel(@Nullable String label) {
return buildUpon().setLabel(label).build(); return buildUpon().setLabel(label).build();
} }
/** @deprecated Use {@link #withManifestFormatInfo(Format)}. */ /**
* @deprecated Use {@link #withManifestFormatInfo(Format)}.
*/
@UnstableApi @UnstableApi
@Deprecated @Deprecated
public Format copyWithManifestFormatInfo(Format manifestFormat) { public Format copyWithManifestFormatInfo(Format manifestFormat) {
@ -1089,21 +1112,27 @@ public final class Format implements Bundleable {
return buildUpon().setEncoderDelay(encoderDelay).setEncoderPadding(encoderPadding).build(); return buildUpon().setEncoderDelay(encoderDelay).setEncoderPadding(encoderPadding).build();
} }
/** @deprecated Use {@link #buildUpon()} and {@link Builder#setFrameRate(float)}. */ /**
* @deprecated Use {@link #buildUpon()} and {@link Builder#setFrameRate(float)}.
*/
@UnstableApi @UnstableApi
@Deprecated @Deprecated
public Format copyWithFrameRate(float frameRate) { public Format copyWithFrameRate(float frameRate) {
return buildUpon().setFrameRate(frameRate).build(); return buildUpon().setFrameRate(frameRate).build();
} }
/** @deprecated Use {@link #buildUpon()} and {@link Builder#setDrmInitData(DrmInitData)}. */ /**
* @deprecated Use {@link #buildUpon()} and {@link Builder#setDrmInitData(DrmInitData)}.
*/
@UnstableApi @UnstableApi
@Deprecated @Deprecated
public Format copyWithDrmInitData(@Nullable DrmInitData drmInitData) { public Format copyWithDrmInitData(@Nullable DrmInitData drmInitData) {
return buildUpon().setDrmInitData(drmInitData).build(); return buildUpon().setDrmInitData(drmInitData).build();
} }
/** @deprecated Use {@link #buildUpon()} and {@link Builder#setMetadata(Metadata)}. */ /**
* @deprecated Use {@link #buildUpon()} and {@link Builder#setMetadata(Metadata)}.
*/
@UnstableApi @UnstableApi
@Deprecated @Deprecated
public Format copyWithMetadata(@Nullable Metadata metadata) { public Format copyWithMetadata(@Nullable Metadata metadata) {
@ -1417,6 +1446,7 @@ public final class Format implements Bundleable {
// Bundleable implementation. // Bundleable implementation.
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@IntDef({ @IntDef({
FIELD_ID, FIELD_ID,
FIELD_LABEL, FIELD_LABEL,

View File

@ -16,6 +16,7 @@
package androidx.media3.common; package androidx.media3.common;
import static androidx.media3.common.util.Assertions.checkArgument; import static androidx.media3.common.util.Assertions.checkArgument;
import static java.lang.annotation.ElementType.TYPE_USE;
import android.os.Bundle; import android.os.Bundle;
import androidx.annotation.IntDef; import androidx.annotation.IntDef;
@ -25,6 +26,7 @@ import com.google.common.base.Objects;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/** /**
* A rating expressed as "heart" or "no heart". It can be used to indicate whether the content is a * A rating expressed as "heart" or "no heart". It can be used to indicate whether the content is a
@ -77,10 +79,11 @@ public final class HeartRating extends Rating {
// Bundleable implementation. // Bundleable implementation.
@RatingType private static final int TYPE = RATING_TYPE_HEART; private static final @RatingType int TYPE = RATING_TYPE_HEART;
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@IntDef({FIELD_RATING_TYPE, FIELD_RATED, FIELD_IS_HEART}) @IntDef({FIELD_RATING_TYPE, FIELD_RATED, FIELD_IS_HEART})
private @interface FieldNumber {} private @interface FieldNumber {}
@ -102,7 +105,7 @@ public final class HeartRating extends Rating {
private static HeartRating fromBundle(Bundle bundle) { private static HeartRating fromBundle(Bundle bundle) {
checkArgument( checkArgument(
bundle.getInt(keyForField(FIELD_RATING_TYPE), /* defaultValue= */ RATING_TYPE_DEFAULT) bundle.getInt(keyForField(FIELD_RATING_TYPE), /* defaultValue= */ RATING_TYPE_UNSET)
== TYPE); == TYPE);
boolean isRated = bundle.getBoolean(keyForField(FIELD_RATED), /* defaultValue= */ false); boolean isRated = bundle.getBoolean(keyForField(FIELD_RATED), /* defaultValue= */ false);
return isRated return isRated

View File

@ -17,6 +17,7 @@ package androidx.media3.common;
import static androidx.media3.common.util.Assertions.checkNotNull; import static androidx.media3.common.util.Assertions.checkNotNull;
import static androidx.media3.common.util.Assertions.checkState; import static androidx.media3.common.util.Assertions.checkState;
import static java.lang.annotation.ElementType.TYPE_USE;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
@ -31,6 +32,7 @@ import com.google.common.collect.ImmutableMap;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
@ -710,7 +712,9 @@ public final class MediaItem implements Bundleable {
/** The UUID of the protection scheme. */ /** The UUID of the protection scheme. */
public final UUID scheme; public final UUID scheme;
/** @deprecated Use {@link #scheme} instead. */ /**
* @deprecated Use {@link #scheme} instead.
*/
@UnstableApi @Deprecated public final UUID uuid; @UnstableApi @Deprecated public final UUID uuid;
/** /**
@ -719,7 +723,9 @@ public final class MediaItem implements Bundleable {
*/ */
@Nullable public final Uri licenseUri; @Nullable public final Uri licenseUri;
/** @deprecated Use {@link #licenseRequestHeaders} instead. */ /**
* @deprecated Use {@link #licenseRequestHeaders} instead.
*/
@UnstableApi @Deprecated public final ImmutableMap<String, String> requestHeaders; @UnstableApi @Deprecated public final ImmutableMap<String, String> requestHeaders;
/** The headers to attach to requests sent to the DRM license server. */ /** The headers to attach to requests sent to the DRM license server. */
@ -740,7 +746,9 @@ public final class MediaItem implements Bundleable {
*/ */
public final boolean forceDefaultLicenseUri; public final boolean forceDefaultLicenseUri;
/** @deprecated Use {@link #forcedSessionTrackTypes}. */ /**
* @deprecated Use {@link #forcedSessionTrackTypes}.
*/
@UnstableApi @Deprecated public final ImmutableList<@C.TrackType Integer> sessionForClearTypes; @UnstableApi @Deprecated public final ImmutableList<@C.TrackType Integer> sessionForClearTypes;
/** /**
* The types of tracks for which to always use a DRM session even if the content is unencrypted. * The types of tracks for which to always use a DRM session even if the content is unencrypted.
@ -927,7 +935,9 @@ public final class MediaItem implements Bundleable {
/** Optional subtitles to be sideloaded. */ /** Optional subtitles to be sideloaded. */
public final ImmutableList<SubtitleConfiguration> subtitleConfigurations; public final ImmutableList<SubtitleConfiguration> subtitleConfigurations;
/** @deprecated Use {@link #subtitleConfigurations} instead. */ /**
* @deprecated Use {@link #subtitleConfigurations} instead.
*/
@UnstableApi @Deprecated public final List<Subtitle> subtitles; @UnstableApi @Deprecated public final List<Subtitle> subtitles;
/** /**
@ -996,7 +1006,9 @@ public final class MediaItem implements Bundleable {
} }
} }
/** @deprecated Use {@link LocalConfiguration}. */ /**
* @deprecated Use {@link LocalConfiguration}.
*/
@UnstableApi @UnstableApi
@Deprecated @Deprecated
public static final class PlaybackProperties extends LocalConfiguration { public static final class PlaybackProperties extends LocalConfiguration {
@ -1158,7 +1170,9 @@ public final class MediaItem implements Bundleable {
builder.maxPlaybackSpeed); builder.maxPlaybackSpeed);
} }
/** @deprecated Use {@link Builder} instead. */ /**
* @deprecated Use {@link Builder} instead.
*/
@UnstableApi @UnstableApi
@Deprecated @Deprecated
public LiveConfiguration( public LiveConfiguration(
@ -1210,6 +1224,7 @@ public final class MediaItem implements Bundleable {
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@IntDef({ @IntDef({
FIELD_TARGET_OFFSET_MS, FIELD_TARGET_OFFSET_MS,
FIELD_MIN_OFFSET_MS, FIELD_MIN_OFFSET_MS,
@ -1424,19 +1439,25 @@ public final class MediaItem implements Bundleable {
} }
} }
/** @deprecated Use {@link MediaItem.SubtitleConfiguration} instead */ /**
* @deprecated Use {@link MediaItem.SubtitleConfiguration} instead
*/
@UnstableApi @UnstableApi
@Deprecated @Deprecated
public static final class Subtitle extends SubtitleConfiguration { public static final class Subtitle extends SubtitleConfiguration {
/** @deprecated Use {@link Builder} instead. */ /**
* @deprecated Use {@link Builder} instead.
*/
@UnstableApi @UnstableApi
@Deprecated @Deprecated
public Subtitle(Uri uri, String mimeType, @Nullable String language) { public Subtitle(Uri uri, String mimeType, @Nullable String language) {
this(uri, mimeType, language, /* selectionFlags= */ 0); this(uri, mimeType, language, /* selectionFlags= */ 0);
} }
/** @deprecated Use {@link Builder} instead. */ /**
* @deprecated Use {@link Builder} instead.
*/
@UnstableApi @UnstableApi
@Deprecated @Deprecated
public Subtitle( public Subtitle(
@ -1444,7 +1465,9 @@ public final class MediaItem implements Bundleable {
this(uri, mimeType, language, selectionFlags, /* roleFlags= */ 0, /* label= */ null); this(uri, mimeType, language, selectionFlags, /* roleFlags= */ 0, /* label= */ null);
} }
/** @deprecated Use {@link Builder} instead. */ /**
* @deprecated Use {@link Builder} instead.
*/
@UnstableApi @UnstableApi
@Deprecated @Deprecated
public Subtitle( public Subtitle(
@ -1547,7 +1570,9 @@ public final class MediaItem implements Bundleable {
return buildClippingProperties(); return buildClippingProperties();
} }
/** @deprecated Use {@link #build()} instead. */ /**
* @deprecated Use {@link #build()} instead.
*/
@UnstableApi @UnstableApi
@Deprecated @Deprecated
public ClippingProperties buildClippingProperties() { public ClippingProperties buildClippingProperties() {
@ -1625,6 +1650,7 @@ public final class MediaItem implements Bundleable {
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@IntDef({ @IntDef({
FIELD_START_POSITION_MS, FIELD_START_POSITION_MS,
FIELD_END_POSITION_MS, FIELD_END_POSITION_MS,
@ -1676,7 +1702,9 @@ public final class MediaItem implements Bundleable {
} }
} }
/** @deprecated Use {@link ClippingConfiguration} instead. */ /**
* @deprecated Use {@link ClippingConfiguration} instead.
*/
@UnstableApi @UnstableApi
@Deprecated @Deprecated
public static final class ClippingProperties extends ClippingConfiguration { public static final class ClippingProperties extends ClippingConfiguration {
@ -1705,7 +1733,9 @@ public final class MediaItem implements Bundleable {
* boundaries. * boundaries.
*/ */
@Nullable public final LocalConfiguration localConfiguration; @Nullable public final LocalConfiguration localConfiguration;
/** @deprecated Use {@link #localConfiguration} instead. */ /**
* @deprecated Use {@link #localConfiguration} instead.
*/
@UnstableApi @Deprecated @Nullable public final PlaybackProperties playbackProperties; @UnstableApi @Deprecated @Nullable public final PlaybackProperties playbackProperties;
/** The live playback configuration. */ /** The live playback configuration. */
@ -1716,7 +1746,9 @@ public final class MediaItem implements Bundleable {
/** The clipping properties. */ /** The clipping properties. */
public final ClippingConfiguration clippingConfiguration; public final ClippingConfiguration clippingConfiguration;
/** @deprecated Use {@link #clippingConfiguration} instead. */ /**
* @deprecated Use {@link #clippingConfiguration} instead.
*/
@UnstableApi @Deprecated public final ClippingProperties clippingProperties; @UnstableApi @Deprecated public final ClippingProperties clippingProperties;
// Using PlaybackProperties and ClippingProperties until they're deleted. // Using PlaybackProperties and ClippingProperties until they're deleted.
@ -1773,6 +1805,7 @@ public final class MediaItem implements Bundleable {
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@IntDef({ @IntDef({
FIELD_MEDIA_ID, FIELD_MEDIA_ID,
FIELD_LIVE_CONFIGURATION, FIELD_LIVE_CONFIGURATION,
@ -1839,7 +1872,7 @@ public final class MediaItem implements Bundleable {
return new MediaItem( return new MediaItem(
mediaId, mediaId,
clippingConfiguration, clippingConfiguration,
/* playbackProperties= */ null, /* localConfiguration= */ null,
liveConfiguration, liveConfiguration,
mediaMetadata); mediaMetadata);
} }

View File

@ -56,11 +56,11 @@ public final class MediaMetadata implements Bundleable {
@Nullable private Rating userRating; @Nullable private Rating userRating;
@Nullable private Rating overallRating; @Nullable private Rating overallRating;
@Nullable private byte[] artworkData; @Nullable private byte[] artworkData;
@Nullable @PictureType private Integer artworkDataType; @Nullable private @PictureType Integer artworkDataType;
@Nullable private Uri artworkUri; @Nullable private Uri artworkUri;
@Nullable private Integer trackNumber; @Nullable private Integer trackNumber;
@Nullable private Integer totalTrackCount; @Nullable private Integer totalTrackCount;
@Nullable @FolderType private Integer folderType; @Nullable private @FolderType Integer folderType;
@Nullable private Boolean isPlayable; @Nullable private Boolean isPlayable;
@Nullable private Integer recordingYear; @Nullable private Integer recordingYear;
@Nullable private Integer recordingMonth; @Nullable private Integer recordingMonth;
@ -248,7 +248,9 @@ public final class MediaMetadata implements Bundleable {
return this; return this;
} }
/** @deprecated Use {@link #setRecordingYear(Integer)} instead. */ /**
* @deprecated Use {@link #setRecordingYear(Integer)} instead.
*/
@UnstableApi @UnstableApi
@Deprecated @Deprecated
public Builder setYear(@Nullable Integer year) { public Builder setYear(@Nullable Integer year) {
@ -521,6 +523,8 @@ public final class MediaMetadata implements Bundleable {
* href="https://www.bluetooth.com/specifications/specs/a-v-remote-control-profile-1-6-2/">Bluetooth * href="https://www.bluetooth.com/specifications/specs/a-v-remote-control-profile-1-6-2/">Bluetooth
* AVRCP 1.6.2</a>). * AVRCP 1.6.2</a>).
*/ */
// @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility
// with Kotlin usages from before TYPE_USE was added.
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE})
@ -559,6 +563,8 @@ public final class MediaMetadata implements Bundleable {
* <p>Values sourced from the ID3 v2.4 specification (See section 4.14 of * <p>Values sourced from the ID3 v2.4 specification (See section 4.14 of
* https://id3.org/id3v2.4.0-frames). * https://id3.org/id3v2.4.0-frames).
*/ */
// @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility
// with Kotlin usages from before TYPE_USE was added.
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE})
@ -650,7 +656,9 @@ public final class MediaMetadata implements Bundleable {
@Nullable public final @FolderType Integer folderType; @Nullable public final @FolderType Integer folderType;
/** Optional boolean for media playability. */ /** Optional boolean for media playability. */
@Nullable public final Boolean isPlayable; @Nullable public final Boolean isPlayable;
/** @deprecated Use {@link #recordingYear} instead. */ /**
* @deprecated Use {@link #recordingYear} instead.
*/
@UnstableApi @Deprecated @Nullable public final Integer year; @UnstableApi @Deprecated @Nullable public final Integer year;
/** Optional year of the recording date. */ /** Optional year of the recording date. */
@Nullable public final Integer recordingYear; @Nullable public final Integer recordingYear;
@ -829,6 +837,7 @@ public final class MediaMetadata implements Bundleable {
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@IntDef({ @IntDef({
FIELD_TITLE, FIELD_TITLE,
FIELD_ARTIST, FIELD_ARTIST,

View File

@ -62,12 +62,16 @@ public final class Metadata implements Parcelable {
private final Entry[] entries; private final Entry[] entries;
/** @param entries The metadata entries. */ /**
* @param entries The metadata entries.
*/
public Metadata(Entry... entries) { public Metadata(Entry... entries) {
this.entries = entries; this.entries = entries;
} }
/** @param entries The metadata entries. */ /**
* @param entries The metadata entries.
*/
public Metadata(List<? extends Entry> entries) { public Metadata(List<? extends Entry> entries) {
this.entries = entries.toArray(new Entry[0]); this.entries = entries.toArray(new Entry[0]);
} }

View File

@ -552,8 +552,7 @@ public final class MimeTypes {
* @return The corresponding {@link C.Encoding}, or {@link C#ENCODING_INVALID}. * @return The corresponding {@link C.Encoding}, or {@link C#ENCODING_INVALID}.
*/ */
@UnstableApi @UnstableApi
@C.Encoding public static @C.Encoding int getEncoding(String mimeType, @Nullable String codec) {
public static int getEncoding(String mimeType, @Nullable String codec) {
switch (mimeType) { switch (mimeType) {
case MimeTypes.AUDIO_MPEG: case MimeTypes.AUDIO_MPEG:
return C.ENCODING_MP3; return C.ENCODING_MP3;
@ -728,8 +727,7 @@ public final class MimeTypes {
} }
/** Returns the encoding for {@link #audioObjectTypeIndication}. */ /** Returns the encoding for {@link #audioObjectTypeIndication}. */
@C.Encoding public @C.Encoding int getEncoding() {
public int getEncoding() {
// See AUDIO_OBJECT_TYPE_AAC_* constants in AacUtil. // See AUDIO_OBJECT_TYPE_AAC_* constants in AacUtil.
switch (audioObjectTypeIndication) { switch (audioObjectTypeIndication) {
case 2: case 2:

View File

@ -16,6 +16,7 @@
package androidx.media3.common; package androidx.media3.common;
import static androidx.media3.common.util.Assertions.checkArgument; import static androidx.media3.common.util.Assertions.checkArgument;
import static java.lang.annotation.ElementType.TYPE_USE;
import android.os.Bundle; import android.os.Bundle;
import androidx.annotation.FloatRange; import androidx.annotation.FloatRange;
@ -26,6 +27,7 @@ import com.google.common.base.Objects;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/** A rating expressed as a percentage. */ /** A rating expressed as a percentage. */
public final class PercentageRating extends Rating { public final class PercentageRating extends Rating {
@ -75,10 +77,11 @@ public final class PercentageRating extends Rating {
// Bundleable implementation. // Bundleable implementation.
@RatingType private static final int TYPE = RATING_TYPE_PERCENTAGE; private static final @RatingType int TYPE = RATING_TYPE_PERCENTAGE;
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@IntDef({FIELD_RATING_TYPE, FIELD_PERCENT}) @IntDef({FIELD_RATING_TYPE, FIELD_PERCENT})
private @interface FieldNumber {} private @interface FieldNumber {}
@ -98,7 +101,7 @@ public final class PercentageRating extends Rating {
private static PercentageRating fromBundle(Bundle bundle) { private static PercentageRating fromBundle(Bundle bundle) {
checkArgument( checkArgument(
bundle.getInt(keyForField(FIELD_RATING_TYPE), /* defaultValue= */ RATING_TYPE_DEFAULT) bundle.getInt(keyForField(FIELD_RATING_TYPE), /* defaultValue= */ RATING_TYPE_UNSET)
== TYPE); == TYPE);
float percent = bundle.getFloat(keyForField(FIELD_PERCENT), /* defaultValue= */ RATING_UNSET); float percent = bundle.getFloat(keyForField(FIELD_PERCENT), /* defaultValue= */ RATING_UNSET);
return percent == RATING_UNSET ? new PercentageRating() : new PercentageRating(percent); return percent == RATING_UNSET ? new PercentageRating() : new PercentageRating(percent);

View File

@ -46,6 +46,8 @@ public class PlaybackException extends Exception implements Bundleable {
* <p>This list of errors may be extended in future versions, and {@link Player} implementations * <p>This list of errors may be extended in future versions, and {@link Player} implementations
* may define custom error codes. * may define custom error codes.
*/ */
// @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility
// with Kotlin usages from before TYPE_USE was added.
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE})
@ -408,6 +410,7 @@ public class PlaybackException extends Exception implements Bundleable {
*/ */
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@IntDef( @IntDef(
open = true, open = true,
value = { value = {

View File

@ -15,6 +15,8 @@
*/ */
package androidx.media3.common; package androidx.media3.common;
import static java.lang.annotation.ElementType.TYPE_USE;
import android.os.Bundle; import android.os.Bundle;
import androidx.annotation.CheckResult; import androidx.annotation.CheckResult;
import androidx.annotation.FloatRange; import androidx.annotation.FloatRange;
@ -26,6 +28,7 @@ import androidx.media3.common.util.Util;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/** Parameters that apply to playback, including speed setting. */ /** Parameters that apply to playback, including speed setting. */
public final class PlaybackParameters implements Bundleable { public final class PlaybackParameters implements Bundleable {
@ -121,6 +124,7 @@ public final class PlaybackParameters implements Bundleable {
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@IntDef({FIELD_SPEED, FIELD_PITCH}) @IntDef({FIELD_SPEED, FIELD_PITCH})
private @interface FieldNumber {} private @interface FieldNumber {}

View File

@ -15,12 +15,15 @@
*/ */
package androidx.media3.common; package androidx.media3.common;
import static java.lang.annotation.ElementType.TYPE_USE;
import android.os.Bundle; import android.os.Bundle;
import androidx.annotation.IntDef; import androidx.annotation.IntDef;
import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.UnstableApi;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/** /**
* A rating for media content. The style of a rating can be one of {@link HeartRating}, {@link * A rating for media content. The style of a rating can be one of {@link HeartRating}, {@link
@ -41,8 +44,9 @@ public abstract class Rating implements Bundleable {
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@IntDef({ @IntDef({
RATING_TYPE_DEFAULT, RATING_TYPE_UNSET,
RATING_TYPE_HEART, RATING_TYPE_HEART,
RATING_TYPE_PERCENTAGE, RATING_TYPE_PERCENTAGE,
RATING_TYPE_STAR, RATING_TYPE_STAR,
@ -50,7 +54,7 @@ public abstract class Rating implements Bundleable {
}) })
/* package */ @interface RatingType {} /* package */ @interface RatingType {}
/* package */ static final int RATING_TYPE_DEFAULT = -1; /* package */ static final int RATING_TYPE_UNSET = -1;
/* package */ static final int RATING_TYPE_HEART = 0; /* package */ static final int RATING_TYPE_HEART = 0;
/* package */ static final int RATING_TYPE_PERCENTAGE = 1; /* package */ static final int RATING_TYPE_PERCENTAGE = 1;
/* package */ static final int RATING_TYPE_STAR = 2; /* package */ static final int RATING_TYPE_STAR = 2;
@ -58,6 +62,7 @@ public abstract class Rating implements Bundleable {
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@IntDef({FIELD_RATING_TYPE}) @IntDef({FIELD_RATING_TYPE})
private @interface FieldNumber {} private @interface FieldNumber {}
@ -69,7 +74,7 @@ public abstract class Rating implements Bundleable {
private static Rating fromBundle(Bundle bundle) { private static Rating fromBundle(Bundle bundle) {
@RatingType @RatingType
int ratingType = int ratingType =
bundle.getInt(keyForField(FIELD_RATING_TYPE), /* defaultValue= */ RATING_TYPE_DEFAULT); bundle.getInt(keyForField(FIELD_RATING_TYPE), /* defaultValue= */ RATING_TYPE_UNSET);
switch (ratingType) { switch (ratingType) {
case RATING_TYPE_HEART: case RATING_TYPE_HEART:
return HeartRating.CREATOR.fromBundle(bundle); return HeartRating.CREATOR.fromBundle(bundle);
@ -79,8 +84,9 @@ public abstract class Rating implements Bundleable {
return StarRating.CREATOR.fromBundle(bundle); return StarRating.CREATOR.fromBundle(bundle);
case RATING_TYPE_THUMB: case RATING_TYPE_THUMB:
return ThumbRating.CREATOR.fromBundle(bundle); return ThumbRating.CREATOR.fromBundle(bundle);
case RATING_TYPE_UNSET:
default: default:
throw new IllegalArgumentException("Encountered unknown rating type: " + ratingType); throw new IllegalArgumentException("Unknown RatingType: " + ratingType);
} }
} }

View File

@ -16,6 +16,7 @@
package androidx.media3.common; package androidx.media3.common;
import static androidx.media3.common.util.Assertions.checkArgument; import static androidx.media3.common.util.Assertions.checkArgument;
import static java.lang.annotation.ElementType.TYPE_USE;
import android.os.Bundle; import android.os.Bundle;
import androidx.annotation.FloatRange; import androidx.annotation.FloatRange;
@ -27,6 +28,7 @@ import com.google.common.base.Objects;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/** A rating expressed as a fractional number of stars. */ /** A rating expressed as a fractional number of stars. */
public final class StarRating extends Rating { public final class StarRating extends Rating {
@ -101,11 +103,12 @@ public final class StarRating extends Rating {
// Bundleable implementation. // Bundleable implementation.
@RatingType private static final int TYPE = RATING_TYPE_STAR; private static final @RatingType int TYPE = RATING_TYPE_STAR;
private static final int MAX_STARS_DEFAULT = 5; private static final int MAX_STARS_DEFAULT = 5;
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@IntDef({FIELD_RATING_TYPE, FIELD_MAX_STARS, FIELD_STAR_RATING}) @IntDef({FIELD_RATING_TYPE, FIELD_MAX_STARS, FIELD_STAR_RATING})
private @interface FieldNumber {} private @interface FieldNumber {}
@ -127,7 +130,7 @@ public final class StarRating extends Rating {
private static StarRating fromBundle(Bundle bundle) { private static StarRating fromBundle(Bundle bundle) {
checkArgument( checkArgument(
bundle.getInt(keyForField(FIELD_RATING_TYPE), /* defaultValue= */ RATING_TYPE_DEFAULT) bundle.getInt(keyForField(FIELD_RATING_TYPE), /* defaultValue= */ RATING_TYPE_UNSET)
== TYPE); == TYPE);
int maxStars = int maxStars =
bundle.getInt(keyForField(FIELD_MAX_STARS), /* defaultValue= */ MAX_STARS_DEFAULT); bundle.getInt(keyForField(FIELD_MAX_STARS), /* defaultValue= */ MAX_STARS_DEFAULT);

View File

@ -44,7 +44,9 @@ public final class StreamKey implements Comparable<StreamKey>, Parcelable {
/** The stream index. */ /** The stream index. */
public final int streamIndex; public final int streamIndex;
/** @deprecated Use {@link #streamIndex}. */ /**
* @deprecated Use {@link #streamIndex}.
*/
@Deprecated public final int trackIndex; @Deprecated public final int trackIndex;
/** /**

View File

@ -16,6 +16,7 @@
package androidx.media3.common; package androidx.media3.common;
import static androidx.media3.common.util.Assertions.checkArgument; import static androidx.media3.common.util.Assertions.checkArgument;
import static java.lang.annotation.ElementType.TYPE_USE;
import android.os.Bundle; import android.os.Bundle;
import androidx.annotation.IntDef; import androidx.annotation.IntDef;
@ -25,6 +26,7 @@ import com.google.common.base.Objects;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/** A rating expressed as "thumbs up" or "thumbs down". */ /** A rating expressed as "thumbs up" or "thumbs down". */
public final class ThumbRating extends Rating { public final class ThumbRating extends Rating {
@ -74,10 +76,11 @@ public final class ThumbRating extends Rating {
// Bundleable implementation. // Bundleable implementation.
@RatingType private static final int TYPE = RATING_TYPE_THUMB; private static final @RatingType int TYPE = RATING_TYPE_THUMB;
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@IntDef({FIELD_RATING_TYPE, FIELD_RATED, FIELD_IS_THUMBS_UP}) @IntDef({FIELD_RATING_TYPE, FIELD_RATED, FIELD_IS_THUMBS_UP})
private @interface FieldNumber {} private @interface FieldNumber {}
@ -99,7 +102,7 @@ public final class ThumbRating extends Rating {
private static ThumbRating fromBundle(Bundle bundle) { private static ThumbRating fromBundle(Bundle bundle) {
checkArgument( checkArgument(
bundle.getInt(keyForField(FIELD_RATING_TYPE), /* defaultValue= */ RATING_TYPE_DEFAULT) bundle.getInt(keyForField(FIELD_RATING_TYPE), /* defaultValue= */ RATING_TYPE_UNSET)
== TYPE); == TYPE);
boolean rated = bundle.getBoolean(keyForField(FIELD_RATED), /* defaultValue= */ false); boolean rated = bundle.getBoolean(keyForField(FIELD_RATED), /* defaultValue= */ false);
return rated return rated

View File

@ -20,6 +20,7 @@ import static androidx.media3.common.util.Assertions.checkArgument;
import static androidx.media3.common.util.Assertions.checkState; import static androidx.media3.common.util.Assertions.checkState;
import static java.lang.Math.max; import static java.lang.Math.max;
import static java.lang.Math.min; import static java.lang.Math.min;
import static java.lang.annotation.ElementType.TYPE_USE;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
@ -37,6 +38,7 @@ import com.google.errorprone.annotations.InlineMe;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -167,7 +169,9 @@ public abstract class Timeline implements Bundleable {
*/ */
public Object uid; public Object uid;
/** @deprecated Use {@link #mediaItem} instead. */ /**
* @deprecated Use {@link #mediaItem} instead.
*/
@UnstableApi @Deprecated @Nullable public Object tag; @UnstableApi @Deprecated @Nullable public Object tag;
/** The {@link MediaItem} associated to the window. Not necessarily unique. */ /** The {@link MediaItem} associated to the window. Not necessarily unique. */
@ -210,7 +214,9 @@ public abstract class Timeline implements Bundleable {
/** Whether this window may change when the timeline is updated. */ /** Whether this window may change when the timeline is updated. */
public boolean isDynamic; public boolean isDynamic;
/** @deprecated Use {@link #isLive()} instead. */ /**
* @deprecated Use {@link #isLive()} instead.
*/
@UnstableApi @Deprecated public boolean isLive; @UnstableApi @Deprecated public boolean isLive;
/** /**
@ -414,6 +420,7 @@ public abstract class Timeline implements Bundleable {
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@IntDef({ @IntDef({
FIELD_MEDIA_ITEM, FIELD_MEDIA_ITEM,
FIELD_PRESENTATION_START_TIME_MS, FIELD_PRESENTATION_START_TIME_MS,
@ -903,6 +910,7 @@ public abstract class Timeline implements Bundleable {
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@IntDef({ @IntDef({
FIELD_WINDOW_INDEX, FIELD_WINDOW_INDEX,
FIELD_DURATION_US, FIELD_DURATION_US,
@ -1174,7 +1182,9 @@ public abstract class Timeline implements Bundleable {
== C.INDEX_UNSET; == C.INDEX_UNSET;
} }
/** @deprecated Use {@link #getPeriodPositionUs(Window, Period, int, long)} instead. */ /**
* @deprecated Use {@link #getPeriodPositionUs(Window, Period, int, long)} instead.
*/
@UnstableApi @UnstableApi
@Deprecated @Deprecated
@InlineMe(replacement = "this.getPeriodPositionUs(window, period, windowIndex, windowPositionUs)") @InlineMe(replacement = "this.getPeriodPositionUs(window, period, windowIndex, windowPositionUs)")
@ -1182,7 +1192,9 @@ public abstract class Timeline implements Bundleable {
Window window, Period period, int windowIndex, long windowPositionUs) { Window window, Period period, int windowIndex, long windowPositionUs) {
return getPeriodPositionUs(window, period, windowIndex, windowPositionUs); return getPeriodPositionUs(window, period, windowIndex, windowPositionUs);
} }
/** @deprecated Use {@link #getPeriodPositionUs(Window, Period, int, long, long)} instead. */ /**
* @deprecated Use {@link #getPeriodPositionUs(Window, Period, int, long, long)} instead.
*/
@UnstableApi @UnstableApi
@Deprecated @Deprecated
@Nullable @Nullable
@ -1362,6 +1374,7 @@ public abstract class Timeline implements Bundleable {
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@IntDef({ @IntDef({
FIELD_WINDOWS, FIELD_WINDOWS,
FIELD_PERIODS, FIELD_PERIODS,

View File

@ -16,6 +16,7 @@
package androidx.media3.common; package androidx.media3.common;
import static androidx.media3.common.util.Assertions.checkArgument; import static androidx.media3.common.util.Assertions.checkArgument;
import static java.lang.annotation.ElementType.TYPE_USE;
import android.os.Bundle; import android.os.Bundle;
import androidx.annotation.CheckResult; import androidx.annotation.CheckResult;
@ -29,6 +30,7 @@ import com.google.common.collect.Lists;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
@ -41,6 +43,8 @@ public final class TrackGroup implements Bundleable {
public final int length; public final int length;
/** An identifier for the track group. */ /** An identifier for the track group. */
public final String id; public final String id;
/** The type of tracks in the group. */
public final @C.TrackType int type;
private final Format[] formats; private final Format[] formats;
@ -69,6 +73,11 @@ public final class TrackGroup implements Bundleable {
this.id = id; this.id = id;
this.formats = formats; this.formats = formats;
this.length = formats.length; this.length = formats.length;
@C.TrackType int type = MimeTypes.getTrackType(formats[0].sampleMimeType);
if (type == C.TRACK_TYPE_UNKNOWN) {
type = MimeTypes.getTrackType(formats[0].containerMimeType);
}
this.type = type;
verifyCorrectness(); verifyCorrectness();
} }
@ -132,13 +141,14 @@ public final class TrackGroup implements Bundleable {
return false; return false;
} }
TrackGroup other = (TrackGroup) obj; TrackGroup other = (TrackGroup) obj;
return length == other.length && id.equals(other.id) && Arrays.equals(formats, other.formats); return id.equals(other.id) && Arrays.equals(formats, other.formats);
} }
// Bundleable implementation. // Bundleable implementation.
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@IntDef({FIELD_FORMATS, FIELD_ID}) @IntDef({FIELD_FORMATS, FIELD_ID})
private @interface FieldNumber {} private @interface FieldNumber {}
@ -204,8 +214,7 @@ public final class TrackGroup implements Bundleable {
return language == null || language.equals(C.LANGUAGE_UNDETERMINED) ? "" : language; return language == null || language.equals(C.LANGUAGE_UNDETERMINED) ? "" : language;
} }
@C.RoleFlags private static @C.RoleFlags int normalizeRoleFlags(@C.RoleFlags int roleFlags) {
private static int normalizeRoleFlags(@C.RoleFlags int roleFlags) {
// Treat trick-play and non-trick-play formats as compatible. // Treat trick-play and non-trick-play formats as compatible.
return roleFlags | C.ROLE_FLAG_TRICK_PLAY; return roleFlags | C.ROLE_FLAG_TRICK_PLAY;
} }

View File

@ -15,6 +15,8 @@
*/ */
package androidx.media3.common; package androidx.media3.common;
import static java.lang.annotation.ElementType.TYPE_USE;
import android.os.Bundle; import android.os.Bundle;
import androidx.annotation.IntDef; import androidx.annotation.IntDef;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@ -25,6 +27,7 @@ import com.google.common.collect.ImmutableList;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.List; import java.util.List;
/** An immutable array of {@link TrackGroup}s. */ /** An immutable array of {@link TrackGroup}s. */
@ -105,6 +108,7 @@ public final class TrackGroupArray implements Bundleable {
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@IntDef({ @IntDef({
FIELD_TRACK_GROUPS, FIELD_TRACK_GROUPS,
}) })

View File

@ -15,10 +15,11 @@
*/ */
package androidx.media3.common; package androidx.media3.common;
import static java.lang.annotation.ElementType.TYPE_USE;
import androidx.annotation.IntDef; import androidx.annotation.IntDef;
import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.UnstableApi;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
@ -38,7 +39,7 @@ public interface TrackSelection {
*/ */
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target({ElementType.TYPE_USE}) @Target(TYPE_USE)
@IntDef( @IntDef(
open = true, open = true,
value = {TYPE_UNSET}) value = {TYPE_UNSET})

View File

@ -32,7 +32,9 @@ public final class TrackSelectionArray {
// Lazily initialized hashcode. // Lazily initialized hashcode.
private int hashCode; private int hashCode;
/** @param trackSelections The selections. Must not be null, but may contain null elements. */ /**
* @param trackSelections The selections. Must not be null, but may contain null elements.
*/
public TrackSelectionArray(@NullableType TrackSelection... trackSelections) { public TrackSelectionArray(@NullableType TrackSelection... trackSelections) {
this.trackSelections = trackSelections; this.trackSelections = trackSelections;
this.length = trackSelections.length; this.length = trackSelections.length;

View File

@ -0,0 +1,139 @@
/*
* Copyright (C) 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 androidx.media3.common;
import static androidx.media3.common.util.Assertions.checkNotNull;
import static java.util.Collections.max;
import static java.util.Collections.min;
import android.os.Bundle;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import androidx.media3.common.util.UnstableApi;
import com.google.common.collect.ImmutableList;
import com.google.common.primitives.Ints;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
/**
* Forces the selection of {@link #trackIndices} for a {@link TrackGroup}.
*
* <p>If multiple tracks in {@link #trackGroup} are overridden, as many as possible will be selected
* depending on the player capabilities.
*
* <p>If {@link #trackIndices} is empty, no tracks from {@link #trackGroup} will be played. This is
* similar to {@link TrackSelectionParameters#disabledTrackTypes}, except it will only affect the
* playback of the associated {@link TrackGroup}. For example, if the only {@link
* C#TRACK_TYPE_VIDEO} {@link TrackGroup} is associated with no tracks, no video will play until the
* next video starts.
*/
public final class TrackSelectionOverride implements Bundleable {
/** The {@link TrackGroup} whose {@link #trackIndices} are forced to be selected. */
public final TrackGroup trackGroup;
/** The indices of tracks in a {@link TrackGroup} to be selected. */
public final ImmutableList<Integer> trackIndices;
@Documented
@Retention(RetentionPolicy.SOURCE)
@IntDef({
FIELD_TRACK_GROUP,
FIELD_TRACKS,
})
private @interface FieldNumber {}
private static final int FIELD_TRACK_GROUP = 0;
private static final int FIELD_TRACKS = 1;
/** Constructs an instance to force all tracks in {@code trackGroup} to be selected. */
public TrackSelectionOverride(TrackGroup trackGroup) {
this.trackGroup = trackGroup;
ImmutableList.Builder<Integer> builder = new ImmutableList.Builder<>();
for (int i = 0; i < trackGroup.length; i++) {
builder.add(i);
}
this.trackIndices = builder.build();
}
/**
* Constructs an instance to force {@code trackIndices} in {@code trackGroup} to be selected.
*
* @param trackGroup The {@link TrackGroup} for which to override the track selection.
* @param trackIndices The indices of the tracks in the {@link TrackGroup} to select.
*/
public TrackSelectionOverride(TrackGroup trackGroup, List<Integer> trackIndices) {
if (!trackIndices.isEmpty()) {
if (min(trackIndices) < 0 || max(trackIndices) >= trackGroup.length) {
throw new IndexOutOfBoundsException();
}
}
this.trackGroup = trackGroup;
this.trackIndices = ImmutableList.copyOf(trackIndices);
}
/** Returns the {@link C.TrackType} of the overridden track group. */
public @C.TrackType int getTrackType() {
return trackGroup.type;
}
@Override
public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
TrackSelectionOverride that = (TrackSelectionOverride) obj;
return trackGroup.equals(that.trackGroup) && trackIndices.equals(that.trackIndices);
}
@Override
public int hashCode() {
return trackGroup.hashCode() + 31 * trackIndices.hashCode();
}
// Bundleable implementation
@UnstableApi
@Override
public Bundle toBundle() {
Bundle bundle = new Bundle();
bundle.putBundle(keyForField(FIELD_TRACK_GROUP), trackGroup.toBundle());
bundle.putIntArray(keyForField(FIELD_TRACKS), Ints.toArray(trackIndices));
return bundle;
}
/** Object that can restore {@code TrackSelectionOverride} from a {@link Bundle}. */
@UnstableApi
public static final Creator<TrackSelectionOverride> CREATOR =
bundle -> {
@Nullable Bundle trackGroupBundle = bundle.getBundle(keyForField(FIELD_TRACK_GROUP));
checkNotNull(trackGroupBundle); // Mandatory as there are no reasonable defaults.
TrackGroup trackGroup = TrackGroup.CREATOR.fromBundle(trackGroupBundle);
@Nullable int[] tracks = bundle.getIntArray(keyForField(FIELD_TRACKS));
if (tracks == null) {
return new TrackSelectionOverride(trackGroup);
}
return new TrackSelectionOverride(trackGroup, Ints.asList(tracks));
};
private static String keyForField(@FieldNumber int field) {
return Integer.toString(field, Character.MAX_RADIX);
}
}

View File

@ -1,309 +0,0 @@
/*
* Copyright (C) 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 androidx.media3.common;
import static androidx.media3.common.util.Assertions.checkNotNull;
import static androidx.media3.common.util.BundleableUtil.fromBundleNullableList;
import static androidx.media3.common.util.BundleableUtil.toBundleArrayList;
import static java.util.Collections.max;
import static java.util.Collections.min;
import android.os.Bundle;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import androidx.media3.common.util.UnstableApi;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.primitives.Ints;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* Forces the selection of the specified tracks in {@link TrackGroup TrackGroups}.
*
* <p>Each {@link TrackSelectionOverride override} only affects the selection of tracks of that
* {@link C.TrackType type}. For example overriding the selection of an {@link C#TRACK_TYPE_AUDIO
* audio} {@link TrackGroup} will not affect the selection of {@link C#TRACK_TYPE_VIDEO video} or
* {@link C#TRACK_TYPE_TEXT text} tracks.
*
* <p>If multiple {@link TrackGroup TrackGroups} of the same {@link C.TrackType} are overridden,
* which tracks will be selected depend on the player capabilities. For example, by default {@code
* ExoPlayer} doesn't support selecting more than one {@link TrackGroup} per {@link C.TrackType}.
*
* <p>Overrides of {@link TrackGroup} that are not currently available are ignored. For example,
* when the player transitions to the next {@link MediaItem} in a playlist, any overrides of the
* previous {@link MediaItem} are ignored.
*
* @see TrackSelectionParameters#trackSelectionOverrides
*/
public final class TrackSelectionOverrides implements Bundleable {
/** Builder for {@link TrackSelectionOverrides}. */
public static final class Builder {
// Cannot use ImmutableMap.Builder as it doesn't support removing entries.
private final HashMap<TrackGroup, TrackSelectionOverride> overrides;
/** Creates an builder with no {@link TrackSelectionOverride}. */
public Builder() {
overrides = new HashMap<>();
}
private Builder(Map<TrackGroup, TrackSelectionOverride> overrides) {
this.overrides = new HashMap<>(overrides);
}
/** Adds an override for the provided {@link TrackGroup}. */
@UnstableApi
public Builder addOverride(TrackSelectionOverride override) {
overrides.put(override.trackGroup, override);
return this;
}
/** Removes the override associated with the provided {@link TrackGroup} if present. */
@UnstableApi
public Builder clearOverride(TrackGroup trackGroup) {
overrides.remove(trackGroup);
return this;
}
/** Set the override for the type of the provided {@link TrackGroup}. */
public Builder setOverrideForType(TrackSelectionOverride override) {
clearOverridesOfType(override.getTrackType());
overrides.put(override.trackGroup, override);
return this;
}
/**
* Remove any override associated with {@link TrackGroup TrackGroups} of type {@code trackType}.
*/
public Builder clearOverridesOfType(@C.TrackType int trackType) {
for (Iterator<TrackSelectionOverride> it = overrides.values().iterator(); it.hasNext(); ) {
TrackSelectionOverride trackSelectionOverride = it.next();
if (trackSelectionOverride.getTrackType() == trackType) {
it.remove();
}
}
return this;
}
/** Returns a new {@link TrackSelectionOverrides} instance with the current builder values. */
public TrackSelectionOverrides build() {
return new TrackSelectionOverrides(overrides);
}
}
/**
* Forces the selection of {@link #trackIndices} for a {@link TrackGroup}.
*
* <p>If multiple tracks in {@link #trackGroup} are overridden, as many as possible will be
* selected depending on the player capabilities.
*
* <p>If {@link #trackIndices} is empty, no tracks from {@link #trackGroup} will be played. This
* is similar to {@link TrackSelectionParameters#disabledTrackTypes}, except it will only affect
* the playback of the associated {@link TrackGroup}. For example, if the only {@link
* C#TRACK_TYPE_VIDEO} {@link TrackGroup} is associated with no tracks, no video will play until
* the next video starts.
*/
public static final class TrackSelectionOverride implements Bundleable {
/** The {@link TrackGroup} whose {@link #trackIndices} are forced to be selected. */
public final TrackGroup trackGroup;
/** The indices of tracks in a {@link TrackGroup} to be selected. */
public final ImmutableList<Integer> trackIndices;
/** Constructs an instance to force all tracks in {@code trackGroup} to be selected. */
public TrackSelectionOverride(TrackGroup trackGroup) {
this.trackGroup = trackGroup;
ImmutableList.Builder<Integer> builder = new ImmutableList.Builder<>();
for (int i = 0; i < trackGroup.length; i++) {
builder.add(i);
}
this.trackIndices = builder.build();
}
/**
* Constructs an instance to force {@code trackIndices} in {@code trackGroup} to be selected.
*
* @param trackGroup The {@link TrackGroup} for which to override the track selection.
* @param trackIndices The indices of the tracks in the {@link TrackGroup} to select.
*/
public TrackSelectionOverride(TrackGroup trackGroup, List<Integer> trackIndices) {
if (!trackIndices.isEmpty()) {
if (min(trackIndices) < 0 || max(trackIndices) >= trackGroup.length) {
throw new IndexOutOfBoundsException();
}
}
this.trackGroup = trackGroup;
this.trackIndices = ImmutableList.copyOf(trackIndices);
}
@Override
public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
TrackSelectionOverride that = (TrackSelectionOverride) obj;
return trackGroup.equals(that.trackGroup) && trackIndices.equals(that.trackIndices);
}
@Override
public int hashCode() {
return trackGroup.hashCode() + 31 * trackIndices.hashCode();
}
/** Returns the {@link C.TrackType} of the overriden track group. */
public @C.TrackType int getTrackType() {
return MimeTypes.getTrackType(trackGroup.getFormat(0).sampleMimeType);
}
// Bundleable implementation
@Documented
@Retention(RetentionPolicy.SOURCE)
@IntDef({
FIELD_TRACK_GROUP,
FIELD_TRACKS,
})
private @interface FieldNumber {}
private static final int FIELD_TRACK_GROUP = 0;
private static final int FIELD_TRACKS = 1;
@UnstableApi
@Override
public Bundle toBundle() {
Bundle bundle = new Bundle();
bundle.putBundle(keyForField(FIELD_TRACK_GROUP), trackGroup.toBundle());
bundle.putIntArray(keyForField(FIELD_TRACKS), Ints.toArray(trackIndices));
return bundle;
}
/** Object that can restore {@code TrackSelectionOverride} from a {@link Bundle}. */
@UnstableApi
public static final Creator<TrackSelectionOverride> CREATOR =
bundle -> {
@Nullable Bundle trackGroupBundle = bundle.getBundle(keyForField(FIELD_TRACK_GROUP));
checkNotNull(trackGroupBundle); // Mandatory as there are no reasonable defaults.
TrackGroup trackGroup = TrackGroup.CREATOR.fromBundle(trackGroupBundle);
@Nullable int[] tracks = bundle.getIntArray(keyForField(FIELD_TRACKS));
if (tracks == null) {
return new TrackSelectionOverride(trackGroup);
}
return new TrackSelectionOverride(trackGroup, Ints.asList(tracks));
};
private static String keyForField(@FieldNumber int field) {
return Integer.toString(field, Character.MAX_RADIX);
}
}
/** Empty {@code TrackSelectionOverrides}, where no track selection is overridden. */
public static final TrackSelectionOverrides EMPTY =
new TrackSelectionOverrides(ImmutableMap.of());
private final ImmutableMap<TrackGroup, TrackSelectionOverride> overrides;
private TrackSelectionOverrides(Map<TrackGroup, TrackSelectionOverride> overrides) {
this.overrides = ImmutableMap.copyOf(overrides);
}
/** Returns a {@link Builder} initialized with the values of this instance. */
public Builder buildUpon() {
return new Builder(overrides);
}
/** Returns a list of the {@link TrackSelectionOverride overrides}. */
@UnstableApi
public ImmutableList<TrackSelectionOverride> asList() {
return ImmutableList.copyOf(overrides.values());
}
/**
* Returns the {@link TrackSelectionOverride} of the provided {@link TrackGroup} or {@code null}
* if there is none.
*/
@Nullable
public TrackSelectionOverride getOverride(TrackGroup trackGroup) {
return overrides.get(trackGroup);
}
@Override
public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
TrackSelectionOverrides that = (TrackSelectionOverrides) obj;
return overrides.equals(that.overrides);
}
@Override
public int hashCode() {
return overrides.hashCode();
}
// Bundleable implementation
@Documented
@Retention(RetentionPolicy.SOURCE)
@IntDef({
FIELD_OVERRIDES,
})
private @interface FieldNumber {}
private static final int FIELD_OVERRIDES = 0;
@UnstableApi
@Override
public Bundle toBundle() {
Bundle bundle = new Bundle();
bundle.putParcelableArrayList(
keyForField(FIELD_OVERRIDES), toBundleArrayList(overrides.values()));
return bundle;
}
/** Object that can restore {@code TrackSelectionOverrides} from a {@link Bundle}. */
@UnstableApi
public static final Creator<TrackSelectionOverrides> CREATOR =
bundle -> {
List<TrackSelectionOverride> trackSelectionOverrides =
fromBundleNullableList(
TrackSelectionOverride.CREATOR,
bundle.getParcelableArrayList(keyForField(FIELD_OVERRIDES)),
ImmutableList.of());
ImmutableMap.Builder<TrackGroup, TrackSelectionOverride> builder =
new ImmutableMap.Builder<>();
for (int i = 0; i < trackSelectionOverrides.size(); i++) {
TrackSelectionOverride trackSelectionOverride = trackSelectionOverrides.get(i);
builder.put(trackSelectionOverride.trackGroup, trackSelectionOverride);
}
return new TrackSelectionOverrides(builder.build());
};
private static String keyForField(@FieldNumber int field) {
return Integer.toString(field, Character.MAX_RADIX);
}
}

View File

@ -16,7 +16,8 @@
package androidx.media3.common; package androidx.media3.common;
import static androidx.media3.common.util.Assertions.checkNotNull; import static androidx.media3.common.util.Assertions.checkNotNull;
import static androidx.media3.common.util.BundleableUtil.fromNullableBundle; import static androidx.media3.common.util.BundleableUtil.fromBundleNullableList;
import static androidx.media3.common.util.BundleableUtil.toBundleArrayList;
import static com.google.common.base.MoreObjects.firstNonNull; import static com.google.common.base.MoreObjects.firstNonNull;
import android.content.Context; import android.content.Context;
@ -30,11 +31,15 @@ import androidx.annotation.RequiresApi;
import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.UnstableApi;
import androidx.media3.common.util.Util; import androidx.media3.common.util.Util;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.primitives.Ints; import com.google.common.primitives.Ints;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Set; import java.util.Set;
import org.checkerframework.checker.initialization.qual.UnknownInitialization; import org.checkerframework.checker.initialization.qual.UnknownInitialization;
@ -93,7 +98,7 @@ public class TrackSelectionParameters implements Bundleable {
// General // General
private boolean forceLowestBitrate; private boolean forceLowestBitrate;
private boolean forceHighestSupportedBitrate; private boolean forceHighestSupportedBitrate;
private TrackSelectionOverrides trackSelectionOverrides; private HashMap<TrackGroup, TrackSelectionOverride> overrides;
private ImmutableSet<@C.TrackType Integer> disabledTrackTypes; private ImmutableSet<@C.TrackType Integer> disabledTrackTypes;
/** /**
@ -126,7 +131,7 @@ public class TrackSelectionParameters implements Bundleable {
// General // General
forceLowestBitrate = false; forceLowestBitrate = false;
forceHighestSupportedBitrate = false; forceHighestSupportedBitrate = false;
trackSelectionOverrides = TrackSelectionOverrides.EMPTY; overrides = new HashMap<>();
disabledTrackTypes = ImmutableSet.of(); disabledTrackTypes = ImmutableSet.of();
} }
@ -234,11 +239,16 @@ public class TrackSelectionParameters implements Bundleable {
bundle.getBoolean( bundle.getBoolean(
keyForField(FIELD_FORCE_HIGHEST_SUPPORTED_BITRATE), keyForField(FIELD_FORCE_HIGHEST_SUPPORTED_BITRATE),
DEFAULT_WITHOUT_CONTEXT.forceHighestSupportedBitrate); DEFAULT_WITHOUT_CONTEXT.forceHighestSupportedBitrate);
trackSelectionOverrides = overrides = new HashMap<>();
fromNullableBundle( List<TrackSelectionOverride> overrideList =
TrackSelectionOverrides.CREATOR, fromBundleNullableList(
bundle.getBundle(keyForField(FIELD_SELECTION_OVERRIDE_KEYS)), TrackSelectionOverride.CREATOR,
TrackSelectionOverrides.EMPTY); bundle.getParcelableArrayList(keyForField(FIELD_SELECTION_OVERRIDES)),
ImmutableList.of());
for (int i = 0; i < overrideList.size(); i++) {
TrackSelectionOverride override = overrideList.get(i);
overrides.put(override.trackGroup, override);
}
disabledTrackTypes = disabledTrackTypes =
ImmutableSet.copyOf( ImmutableSet.copyOf(
Ints.asList( Ints.asList(
@ -252,7 +262,7 @@ public class TrackSelectionParameters implements Bundleable {
"preferredAudioLanguages", "preferredAudioLanguages",
"preferredAudioMimeTypes", "preferredAudioMimeTypes",
"preferredTextLanguages", "preferredTextLanguages",
"trackSelectionOverrides", "overrides",
"disabledTrackTypes", "disabledTrackTypes",
}) })
private void init(@UnknownInitialization Builder this, TrackSelectionParameters parameters) { private void init(@UnknownInitialization Builder this, TrackSelectionParameters parameters) {
@ -283,8 +293,8 @@ public class TrackSelectionParameters implements Bundleable {
// General // General
forceLowestBitrate = parameters.forceLowestBitrate; forceLowestBitrate = parameters.forceLowestBitrate;
forceHighestSupportedBitrate = parameters.forceHighestSupportedBitrate; forceHighestSupportedBitrate = parameters.forceHighestSupportedBitrate;
trackSelectionOverrides = parameters.trackSelectionOverrides;
disabledTrackTypes = parameters.disabledTrackTypes; disabledTrackTypes = parameters.disabledTrackTypes;
overrides = new HashMap<>(parameters.overrides);
} }
/** Overrides the value of the builder with the value of {@link TrackSelectionParameters}. */ /** Overrides the value of the builder with the value of {@link TrackSelectionParameters}. */
@ -644,14 +654,42 @@ public class TrackSelectionParameters implements Bundleable {
return this; return this;
} }
/** Adds an override for the provided {@link TrackGroup}. */
public Builder addOverride(TrackSelectionOverride override) {
overrides.put(override.trackGroup, override);
return this;
}
/** Removes the override associated with the provided {@link TrackGroup} if present. */
public Builder clearOverride(TrackGroup trackGroup) {
overrides.remove(trackGroup);
return this;
}
/** Set the override for the type of the provided {@link TrackGroup}. */
public Builder setOverrideForType(TrackSelectionOverride override) {
clearOverridesOfType(override.getTrackType());
overrides.put(override.trackGroup, override);
return this;
}
/** /**
* Sets the selection overrides. * Remove any override associated with {@link TrackGroup TrackGroups} of type {@code trackType}.
*
* @param trackSelectionOverrides The track selection overrides.
* @return This builder.
*/ */
public Builder setTrackSelectionOverrides(TrackSelectionOverrides trackSelectionOverrides) { public Builder clearOverridesOfType(@C.TrackType int trackType) {
this.trackSelectionOverrides = trackSelectionOverrides; Iterator<TrackSelectionOverride> it = overrides.values().iterator();
while (it.hasNext()) {
TrackSelectionOverride override = it.next();
if (override.getTrackType() == trackType) {
it.remove();
}
}
return this;
}
/** Removes all track overrides. */
public Builder clearOverrides() {
overrides.clear();
return this; return this;
} }
@ -858,8 +896,9 @@ public class TrackSelectionParameters implements Bundleable {
*/ */
public final boolean forceHighestSupportedBitrate; public final boolean forceHighestSupportedBitrate;
/** Overrides to force tracks to be selected. */ /** Overrides to force selection of specific tracks. */
public final TrackSelectionOverrides trackSelectionOverrides; public final ImmutableMap<TrackGroup, TrackSelectionOverride> overrides;
/** /**
* The track types that are disabled. No track of a disabled type will be selected, thus no track * The track types that are disabled. No track of a disabled type will be selected, thus no track
* type contained in the set will be played. The default value is that no track type is disabled * type contained in the set will be played. The default value is that no track type is disabled
@ -896,7 +935,7 @@ public class TrackSelectionParameters implements Bundleable {
// General // General
this.forceLowestBitrate = builder.forceLowestBitrate; this.forceLowestBitrate = builder.forceLowestBitrate;
this.forceHighestSupportedBitrate = builder.forceHighestSupportedBitrate; this.forceHighestSupportedBitrate = builder.forceHighestSupportedBitrate;
this.trackSelectionOverrides = builder.trackSelectionOverrides; this.overrides = ImmutableMap.copyOf(builder.overrides);
this.disabledTrackTypes = builder.disabledTrackTypes; this.disabledTrackTypes = builder.disabledTrackTypes;
} }
@ -941,7 +980,7 @@ public class TrackSelectionParameters implements Bundleable {
// General // General
&& forceLowestBitrate == other.forceLowestBitrate && forceLowestBitrate == other.forceLowestBitrate
&& forceHighestSupportedBitrate == other.forceHighestSupportedBitrate && forceHighestSupportedBitrate == other.forceHighestSupportedBitrate
&& trackSelectionOverrides.equals(other.trackSelectionOverrides) && overrides.equals(other.overrides)
&& disabledTrackTypes.equals(other.disabledTrackTypes); && disabledTrackTypes.equals(other.disabledTrackTypes);
} }
@ -975,7 +1014,7 @@ public class TrackSelectionParameters implements Bundleable {
// General // General
result = 31 * result + (forceLowestBitrate ? 1 : 0); result = 31 * result + (forceLowestBitrate ? 1 : 0);
result = 31 * result + (forceHighestSupportedBitrate ? 1 : 0); result = 31 * result + (forceHighestSupportedBitrate ? 1 : 0);
result = 31 * result + trackSelectionOverrides.hashCode(); result = 31 * result + overrides.hashCode();
result = 31 * result + disabledTrackTypes.hashCode(); result = 31 * result + disabledTrackTypes.hashCode();
return result; return result;
} }
@ -1007,8 +1046,7 @@ public class TrackSelectionParameters implements Bundleable {
FIELD_PREFERRED_AUDIO_MIME_TYPES, FIELD_PREFERRED_AUDIO_MIME_TYPES,
FIELD_FORCE_LOWEST_BITRATE, FIELD_FORCE_LOWEST_BITRATE,
FIELD_FORCE_HIGHEST_SUPPORTED_BITRATE, FIELD_FORCE_HIGHEST_SUPPORTED_BITRATE,
FIELD_SELECTION_OVERRIDE_KEYS, FIELD_SELECTION_OVERRIDES,
FIELD_SELECTION_OVERRIDE_VALUES,
FIELD_DISABLED_TRACK_TYPE, FIELD_DISABLED_TRACK_TYPE,
FIELD_PREFERRED_VIDEO_ROLE_FLAGS FIELD_PREFERRED_VIDEO_ROLE_FLAGS
}) })
@ -1036,10 +1074,9 @@ public class TrackSelectionParameters implements Bundleable {
private static final int FIELD_PREFERRED_AUDIO_MIME_TYPES = 20; private static final int FIELD_PREFERRED_AUDIO_MIME_TYPES = 20;
private static final int FIELD_FORCE_LOWEST_BITRATE = 21; private static final int FIELD_FORCE_LOWEST_BITRATE = 21;
private static final int FIELD_FORCE_HIGHEST_SUPPORTED_BITRATE = 22; private static final int FIELD_FORCE_HIGHEST_SUPPORTED_BITRATE = 22;
private static final int FIELD_SELECTION_OVERRIDE_KEYS = 23; private static final int FIELD_SELECTION_OVERRIDES = 23;
private static final int FIELD_SELECTION_OVERRIDE_VALUES = 24; private static final int FIELD_DISABLED_TRACK_TYPE = 24;
private static final int FIELD_DISABLED_TRACK_TYPE = 25; private static final int FIELD_PREFERRED_VIDEO_ROLE_FLAGS = 25;
private static final int FIELD_PREFERRED_VIDEO_ROLE_FLAGS = 26;
@UnstableApi @UnstableApi
@Override @Override
@ -1083,8 +1120,8 @@ public class TrackSelectionParameters implements Bundleable {
bundle.putBoolean(keyForField(FIELD_FORCE_LOWEST_BITRATE), forceLowestBitrate); bundle.putBoolean(keyForField(FIELD_FORCE_LOWEST_BITRATE), forceLowestBitrate);
bundle.putBoolean( bundle.putBoolean(
keyForField(FIELD_FORCE_HIGHEST_SUPPORTED_BITRATE), forceHighestSupportedBitrate); keyForField(FIELD_FORCE_HIGHEST_SUPPORTED_BITRATE), forceHighestSupportedBitrate);
bundle.putBundle( bundle.putParcelableArrayList(
keyForField(FIELD_SELECTION_OVERRIDE_KEYS), trackSelectionOverrides.toBundle()); keyForField(FIELD_SELECTION_OVERRIDES), toBundleArrayList(overrides.values()));
bundle.putIntArray(keyForField(FIELD_DISABLED_TRACK_TYPE), Ints.toArray(disabledTrackTypes)); bundle.putIntArray(keyForField(FIELD_DISABLED_TRACK_TYPE), Ints.toArray(disabledTrackTypes));
return bundle; return bundle;

View File

@ -20,6 +20,7 @@ import static androidx.media3.common.util.Assertions.checkNotNull;
import static androidx.media3.common.util.BundleableUtil.fromBundleNullableList; import static androidx.media3.common.util.BundleableUtil.fromBundleNullableList;
import static androidx.media3.common.util.BundleableUtil.fromNullableBundle; import static androidx.media3.common.util.BundleableUtil.fromNullableBundle;
import static androidx.media3.common.util.BundleableUtil.toBundleArrayList; import static androidx.media3.common.util.BundleableUtil.toBundleArrayList;
import static java.lang.annotation.ElementType.TYPE_USE;
import android.os.Bundle; import android.os.Bundle;
import androidx.annotation.IntDef; import androidx.annotation.IntDef;
@ -31,79 +32,135 @@ import com.google.common.primitives.Booleans;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
/** Immutable information ({@link TrackGroupInfo}) about tracks. */ /** Information about groups of tracks. */
public final class TracksInfo implements Bundleable { public final class TracksInfo implements Bundleable {
/** /**
* Information about tracks in a {@link TrackGroup}: their {@link C.TrackType}, if their format is * Information about a single group of tracks, including the underlying {@link TrackGroup}, the
* supported by the player and if they are selected for playback. * {@link C.TrackType type} of tracks it contains, and the level to which each track is supported
* by the player.
*/ */
public static final class TrackGroupInfo implements Bundleable { public static final class TrackGroupInfo implements Bundleable {
/** The number of tracks in the group. */
public final int length;
private final TrackGroup trackGroup; private final TrackGroup trackGroup;
@C.FormatSupport private final int[] trackSupport; private final boolean adaptiveSupported;
private final @C.TrackType int trackType; private final @C.FormatSupport int[] trackSupport;
private final boolean[] trackSelected; private final boolean[] trackSelected;
/** /**
* Constructs a TrackGroupInfo. * Constructs a TrackGroupInfo.
* *
* @param trackGroup The {@link TrackGroup} described. * @param trackGroup The {@link TrackGroup} described.
* @param adaptiveSupported Whether adaptive selections containing more than one track in the
* {@code trackGroup} are supported.
* @param trackSupport The {@link C.FormatSupport} of each track in the {@code trackGroup}. * @param trackSupport The {@link C.FormatSupport} of each track in the {@code trackGroup}.
* @param trackType The {@link C.TrackType} of the tracks in the {@code trackGroup}. * @param tracksSelected Whether each track in the {@code trackGroup} is selected.
* @param tracksSelected Whether a track is selected for each track in {@code trackGroup}.
*/ */
@UnstableApi @UnstableApi
public TrackGroupInfo( public TrackGroupInfo(
TrackGroup trackGroup, TrackGroup trackGroup,
boolean adaptiveSupported,
@C.FormatSupport int[] trackSupport, @C.FormatSupport int[] trackSupport,
@C.TrackType int trackType,
boolean[] tracksSelected) { boolean[] tracksSelected) {
int length = trackGroup.length; length = trackGroup.length;
checkArgument(length == trackSupport.length && length == tracksSelected.length); checkArgument(length == trackSupport.length && length == tracksSelected.length);
this.trackGroup = trackGroup; this.trackGroup = trackGroup;
this.adaptiveSupported = adaptiveSupported && length > 1;
this.trackSupport = trackSupport.clone(); this.trackSupport = trackSupport.clone();
this.trackType = trackType;
this.trackSelected = tracksSelected.clone(); this.trackSelected = tracksSelected.clone();
} }
/** Returns the {@link TrackGroup} described by this {@code TrackGroupInfo}. */ /** Returns the underlying {@link TrackGroup}. */
public TrackGroup getTrackGroup() { public TrackGroup getTrackGroup() {
return trackGroup; return trackGroup;
} }
/** /**
* Returns the level of support for a track in a {@link TrackGroup}. * Returns the {@link Format} for a specified track.
*
* @param trackIndex The index of the track in the {@link TrackGroup}.
* @return The {@link Format} of the track.
*/
public Format getTrackFormat(int trackIndex) {
return trackGroup.getFormat(trackIndex);
}
/**
* Returns the level of support for a specified track.
* *
* @param trackIndex The index of the track in the {@link TrackGroup}. * @param trackIndex The index of the track in the {@link TrackGroup}.
* @return The {@link C.FormatSupport} of the track. * @return The {@link C.FormatSupport} of the track.
*/ */
@UnstableApi @UnstableApi
@C.FormatSupport public @C.FormatSupport int getTrackSupport(int trackIndex) {
public int getTrackSupport(int trackIndex) {
return trackSupport[trackIndex]; return trackSupport[trackIndex];
} }
/** /**
* Returns if a track in a {@link TrackGroup} is supported for playback. * Returns whether a specified track is supported for playback, without exceeding the advertised
* capabilities of the device. Equivalent to {@code isTrackSupported(trackIndex, false)}.
* *
* @param trackIndex The index of the track in the {@link TrackGroup}. * @param trackIndex The index of the track in the {@link TrackGroup}.
* @return True if the track's format can be played, false otherwise. * @return True if the track's format can be played, false otherwise.
*/ */
public boolean isTrackSupported(int trackIndex) { public boolean isTrackSupported(int trackIndex) {
return trackSupport[trackIndex] == C.FORMAT_HANDLED; return isTrackSupported(trackIndex, /* allowExceedsCapabilities= */ false);
} }
/** Returns if at least one track in a {@link TrackGroup} is selected for playback. */ /**
* Returns whether a specified track is supported for playback.
*
* @param trackIndex The index of the track in the {@link TrackGroup}.
* @param allowExceedsCapabilities Whether to consider the track as supported if it has a
* supported {@link Format#sampleMimeType MIME type}, but otherwise exceeds the advertised
* capabilities of the device. For example, a video track for which there's a corresponding
* decoder whose maximum advertised resolution is exceeded by the resolution of the track.
* Such tracks may be playable in some cases.
* @return True if the track's format can be played, false otherwise.
*/
public boolean isTrackSupported(int trackIndex, boolean allowExceedsCapabilities) {
return trackSupport[trackIndex] == C.FORMAT_HANDLED
|| (allowExceedsCapabilities
&& trackSupport[trackIndex] == C.FORMAT_EXCEEDS_CAPABILITIES);
}
/** Returns whether at least one track in the group is selected for playback. */
public boolean isSelected() { public boolean isSelected() {
return Booleans.contains(trackSelected, true); return Booleans.contains(trackSelected, true);
} }
/** Returns if at least one track in a {@link TrackGroup} is supported. */ /** Returns whether adaptive selections containing more than one track are supported. */
public boolean isAdaptiveSupported() {
return adaptiveSupported;
}
/**
* Returns whether at least one track in the group is supported for playback, without exceeding
* the advertised capabilities of the device. Equivalent to {@code isSupported(false)}.
*/
public boolean isSupported() { public boolean isSupported() {
return isSupported(/* allowExceedsCapabilities= */ false);
}
/**
* Returns whether at least one track in the group is supported for playback.
*
* @param allowExceedsCapabilities Whether to consider a track as supported if it has a
* supported {@link Format#sampleMimeType MIME type}, but otherwise exceeds the advertised
* capabilities of the device. For example, a video track for which there's a corresponding
* decoder whose maximum advertised resolution is exceeded by the resolution of the track.
* Such tracks may be playable in some cases.
*/
public boolean isSupported(boolean allowExceedsCapabilities) {
for (int i = 0; i < trackSupport.length; i++) { for (int i = 0; i < trackSupport.length; i++) {
if (isTrackSupported(i)) { if (isTrackSupported(i, allowExceedsCapabilities)) {
return true; return true;
} }
} }
@ -111,29 +168,26 @@ public final class TracksInfo implements Bundleable {
} }
/** /**
* Returns if a track in a {@link TrackGroup} is selected for playback. * Returns whether a specified track is selected for playback.
* *
* <p>Multiple tracks of a track group may be selected. This is common in adaptive streaming, * <p>Note that multiple tracks in the group may be selected. This is common in adaptive
* where multiple tracks of different quality are selected and the player switches between them * streaming, where tracks of different qualities are selected and the player switches between
* depending on the network and the {@link TrackSelectionParameters}. * them during playback (e.g., based on the available network bandwidth).
* *
* <p>While this class doesn't provide which selected track is currently playing, some player * <p>This class doesn't provide a way to determine which of the selected tracks is currently
* implementations have ways of getting such information. For example ExoPlayer provides this * playing, however some player implementations have ways of getting such information. For
* information in {@code ExoTrackSelection.getSelectedFormat}. * example, ExoPlayer provides this information via {@code ExoTrackSelection.getSelectedFormat}.
* *
* @param trackIndex The index of the track in the {@link TrackGroup}. * @param trackIndex The index of the track in the {@link TrackGroup}.
* @return true if the track is selected, false otherwise. * @return True if the track is selected, false otherwise.
*/ */
public boolean isTrackSelected(int trackIndex) { public boolean isTrackSelected(int trackIndex) {
return trackSelected[trackIndex]; return trackSelected[trackIndex];
} }
/** /** Returns the {@link C.TrackType} of the group. */
* Returns the {@link C.TrackType} of the tracks in the {@link TrackGroup}. Tracks in a group
* are all of the same type.
*/
public @C.TrackType int getTrackType() { public @C.TrackType int getTrackType() {
return trackType; return trackGroup.type;
} }
@Override @Override
@ -145,7 +199,7 @@ public final class TracksInfo implements Bundleable {
return false; return false;
} }
TrackGroupInfo that = (TrackGroupInfo) other; TrackGroupInfo that = (TrackGroupInfo) other;
return trackType == that.trackType return adaptiveSupported == that.adaptiveSupported
&& trackGroup.equals(that.trackGroup) && trackGroup.equals(that.trackGroup)
&& Arrays.equals(trackSupport, that.trackSupport) && Arrays.equals(trackSupport, that.trackSupport)
&& Arrays.equals(trackSelected, that.trackSelected); && Arrays.equals(trackSelected, that.trackSelected);
@ -154,8 +208,8 @@ public final class TracksInfo implements Bundleable {
@Override @Override
public int hashCode() { public int hashCode() {
int result = trackGroup.hashCode(); int result = trackGroup.hashCode();
result = 31 * result + (adaptiveSupported ? 1 : 0);
result = 31 * result + Arrays.hashCode(trackSupport); result = 31 * result + Arrays.hashCode(trackSupport);
result = 31 * result + trackType;
result = 31 * result + Arrays.hashCode(trackSelected); result = 31 * result + Arrays.hashCode(trackSelected);
return result; return result;
} }
@ -163,26 +217,27 @@ public final class TracksInfo implements Bundleable {
// Bundleable implementation. // Bundleable implementation.
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@IntDef({ @IntDef({
FIELD_TRACK_GROUP, FIELD_TRACK_GROUP,
FIELD_TRACK_SUPPORT, FIELD_TRACK_SUPPORT,
FIELD_TRACK_TYPE,
FIELD_TRACK_SELECTED, FIELD_TRACK_SELECTED,
FIELD_ADAPTIVE_SUPPORTED,
}) })
private @interface FieldNumber {} private @interface FieldNumber {}
private static final int FIELD_TRACK_GROUP = 0; private static final int FIELD_TRACK_GROUP = 0;
private static final int FIELD_TRACK_SUPPORT = 1; private static final int FIELD_TRACK_SUPPORT = 1;
private static final int FIELD_TRACK_TYPE = 2;
private static final int FIELD_TRACK_SELECTED = 3; private static final int FIELD_TRACK_SELECTED = 3;
private static final int FIELD_ADAPTIVE_SUPPORTED = 4;
@Override @Override
public Bundle toBundle() { public Bundle toBundle() {
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();
bundle.putBundle(keyForField(FIELD_TRACK_GROUP), trackGroup.toBundle()); bundle.putBundle(keyForField(FIELD_TRACK_GROUP), trackGroup.toBundle());
bundle.putIntArray(keyForField(FIELD_TRACK_SUPPORT), trackSupport); bundle.putIntArray(keyForField(FIELD_TRACK_SUPPORT), trackSupport);
bundle.putInt(keyForField(FIELD_TRACK_TYPE), trackType);
bundle.putBooleanArray(keyForField(FIELD_TRACK_SELECTED), trackSelected); bundle.putBooleanArray(keyForField(FIELD_TRACK_SELECTED), trackSelected);
bundle.putBoolean(keyForField(FIELD_ADAPTIVE_SUPPORTED), adaptiveSupported);
return bundle; return bundle;
} }
@ -194,17 +249,16 @@ public final class TracksInfo implements Bundleable {
fromNullableBundle( fromNullableBundle(
TrackGroup.CREATOR, bundle.getBundle(keyForField(FIELD_TRACK_GROUP))); TrackGroup.CREATOR, bundle.getBundle(keyForField(FIELD_TRACK_GROUP)));
checkNotNull(trackGroup); // Can't create a trackGroup info without a trackGroup checkNotNull(trackGroup); // Can't create a trackGroup info without a trackGroup
@C.FormatSupport final @C.FormatSupport int[] trackSupport =
final int[] trackSupport =
MoreObjects.firstNonNull( MoreObjects.firstNonNull(
bundle.getIntArray(keyForField(FIELD_TRACK_SUPPORT)), new int[trackGroup.length]); bundle.getIntArray(keyForField(FIELD_TRACK_SUPPORT)), new int[trackGroup.length]);
@C.TrackType
int trackType = bundle.getInt(keyForField(FIELD_TRACK_TYPE), C.TRACK_TYPE_UNKNOWN);
boolean[] selected = boolean[] selected =
MoreObjects.firstNonNull( MoreObjects.firstNonNull(
bundle.getBooleanArray(keyForField(FIELD_TRACK_SELECTED)), bundle.getBooleanArray(keyForField(FIELD_TRACK_SELECTED)),
new boolean[trackGroup.length]); new boolean[trackGroup.length]);
return new TrackGroupInfo(trackGroup, trackSupport, trackType, selected); boolean adaptiveSupported =
bundle.getBoolean(keyForField(FIELD_ADAPTIVE_SUPPORTED), false);
return new TrackGroupInfo(trackGroup, adaptiveSupported, trackSupport, selected);
}; };
private static String keyForField(@FieldNumber int field) { private static String keyForField(@FieldNumber int field) {
@ -214,36 +268,85 @@ public final class TracksInfo implements Bundleable {
private final ImmutableList<TrackGroupInfo> trackGroupInfos; private final ImmutableList<TrackGroupInfo> trackGroupInfos;
/** An empty {@code TrackInfo} containing no {@link TrackGroupInfo}. */ /** An {@code TrackInfo} that contains no tracks. */
@UnstableApi public static final TracksInfo EMPTY = new TracksInfo(ImmutableList.of()); @UnstableApi public static final TracksInfo EMPTY = new TracksInfo(ImmutableList.of());
/** Constructs {@code TracksInfo} from the provided {@link TrackGroupInfo}. */ /**
* Constructs an instance.
*
* @param trackGroupInfos The {@link TrackGroupInfo TrackGroupInfos} describing the groups of
* tracks.
*/
@UnstableApi @UnstableApi
public TracksInfo(List<TrackGroupInfo> trackGroupInfos) { public TracksInfo(List<TrackGroupInfo> trackGroupInfos) {
this.trackGroupInfos = ImmutableList.copyOf(trackGroupInfos); this.trackGroupInfos = ImmutableList.copyOf(trackGroupInfos);
} }
/** Returns the {@link TrackGroupInfo TrackGroupInfos}, describing each {@link TrackGroup}. */ /** Returns the {@link TrackGroupInfo TrackGroupInfos} describing the groups of tracks. */
public ImmutableList<TrackGroupInfo> getTrackGroupInfos() { public ImmutableList<TrackGroupInfo> getTrackGroupInfos() {
return trackGroupInfos; return trackGroupInfos;
} }
/** Returns if there is at least one track of type {@code trackType} but none are supported. */ /** Returns true if there are tracks of type {@code trackType}, and false otherwise. */
public boolean isTypeSupportedOrEmpty(@C.TrackType int trackType) { public boolean containsType(@C.TrackType int trackType) {
boolean supported = true;
for (int i = 0; i < trackGroupInfos.size(); i++) { for (int i = 0; i < trackGroupInfos.size(); i++) {
if (trackGroupInfos.get(i).trackType == trackType) { if (trackGroupInfos.get(i).getTrackType() == trackType) {
if (trackGroupInfos.get(i).isSupported()) { return true;
}
}
return false;
}
/**
* Returns true if at least one track of type {@code trackType} is {@link
* TrackGroupInfo#isTrackSupported(int) supported}.
*/
public boolean isTypeSupported(@C.TrackType int trackType) {
return isTypeSupported(trackType, /* allowExceedsCapabilities= */ false);
}
/**
* Returns true if at least one track of type {@code trackType} is {@link
* TrackGroupInfo#isTrackSupported(int, boolean) supported}.
*
* @param allowExceedsCapabilities Whether to consider the track as supported if it has a
* supported {@link Format#sampleMimeType MIME type}, but otherwise exceeds the advertised
* capabilities of the device. For example, a video track for which there's a corresponding
* decoder whose maximum advertised resolution is exceeded by the resolution of the track.
* Such tracks may be playable in some cases.
*/
public boolean isTypeSupported(@C.TrackType int trackType, boolean allowExceedsCapabilities) {
for (int i = 0; i < trackGroupInfos.size(); i++) {
if (trackGroupInfos.get(i).getTrackType() == trackType) {
if (trackGroupInfos.get(i).isSupported(allowExceedsCapabilities)) {
return true; return true;
} else {
supported = false;
} }
} }
} }
return supported; return false;
} }
/** Returns if at least one track of the type {@code trackType} is selected for playback. */ /**
* @deprecated Use {@link #containsType(int)} and {@link #isTypeSupported(int)}.
*/
@Deprecated
@UnstableApi
@SuppressWarnings("deprecation")
public boolean isTypeSupportedOrEmpty(@C.TrackType int trackType) {
return isTypeSupportedOrEmpty(trackType, /* allowExceedsCapabilities= */ false);
}
/**
* @deprecated Use {@link #containsType(int)} and {@link #isTypeSupported(int, boolean)}.
*/
@Deprecated
@UnstableApi
public boolean isTypeSupportedOrEmpty(
@C.TrackType int trackType, boolean allowExceedsCapabilities) {
return !containsType(trackType) || isTypeSupported(trackType, allowExceedsCapabilities);
}
/** Returns true if at least one track of the type {@code trackType} is selected for playback. */
public boolean isTypeSelected(@C.TrackType int trackType) { public boolean isTypeSelected(@C.TrackType int trackType) {
for (int i = 0; i < trackGroupInfos.size(); i++) { for (int i = 0; i < trackGroupInfos.size(); i++) {
TrackGroupInfo trackGroupInfo = trackGroupInfos.get(i); TrackGroupInfo trackGroupInfo = trackGroupInfos.get(i);
@ -274,6 +377,7 @@ public final class TracksInfo implements Bundleable {
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@IntDef({ @IntDef({
FIELD_TRACK_GROUP_INFOS, FIELD_TRACK_GROUP_INFOS,
}) })

View File

@ -15,6 +15,8 @@
*/ */
package androidx.media3.common; package androidx.media3.common;
import static java.lang.annotation.ElementType.TYPE_USE;
import android.os.Bundle; import android.os.Bundle;
import androidx.annotation.FloatRange; import androidx.annotation.FloatRange;
import androidx.annotation.IntDef; import androidx.annotation.IntDef;
@ -24,6 +26,7 @@ import androidx.media3.common.util.UnstableApi;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/** Represents the video size. */ /** Represents the video size. */
public final class VideoSize implements Bundleable { public final class VideoSize implements Bundleable {
@ -131,6 +134,7 @@ public final class VideoSize implements Bundleable {
// Bundleable implementation. // Bundleable implementation.
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@IntDef({ @IntDef({
FIELD_WIDTH, FIELD_WIDTH,
FIELD_HEIGHT, FIELD_HEIGHT,

View File

@ -59,6 +59,8 @@ public final class Cue implements Bundleable {
* The type of anchor, which may be unset. One of {@link #TYPE_UNSET}, {@link #ANCHOR_TYPE_START}, * The type of anchor, which may be unset. One of {@link #TYPE_UNSET}, {@link #ANCHOR_TYPE_START},
* {@link #ANCHOR_TYPE_MIDDLE} or {@link #ANCHOR_TYPE_END}. * {@link #ANCHOR_TYPE_MIDDLE} or {@link #ANCHOR_TYPE_END}.
*/ */
// @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility
// with Kotlin usages from before TYPE_USE was added.
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE})
@ -87,6 +89,8 @@ public final class Cue implements Bundleable {
* The type of line, which may be unset. One of {@link #TYPE_UNSET}, {@link #LINE_TYPE_FRACTION} * The type of line, which may be unset. One of {@link #TYPE_UNSET}, {@link #LINE_TYPE_FRACTION}
* or {@link #LINE_TYPE_NUMBER}. * or {@link #LINE_TYPE_NUMBER}.
*/ */
// @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility
// with Kotlin usages from before TYPE_USE was added.
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE})
@ -104,6 +108,8 @@ public final class Cue implements Bundleable {
* {@link #TEXT_SIZE_TYPE_FRACTIONAL}, {@link #TEXT_SIZE_TYPE_FRACTIONAL_IGNORE_PADDING} or {@link * {@link #TEXT_SIZE_TYPE_FRACTIONAL}, {@link #TEXT_SIZE_TYPE_FRACTIONAL_IGNORE_PADDING} or {@link
* #TEXT_SIZE_TYPE_ABSOLUTE}. * #TEXT_SIZE_TYPE_ABSOLUTE}.
*/ */
// @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility
// with Kotlin usages from before TYPE_USE was added.
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE})
@ -128,6 +134,8 @@ public final class Cue implements Bundleable {
* The type of vertical layout for this cue, which may be unset (i.e. horizontal). One of {@link * The type of vertical layout for this cue, which may be unset (i.e. horizontal). One of {@link
* #TYPE_UNSET}, {@link #VERTICAL_TYPE_RL} or {@link #VERTICAL_TYPE_LR}. * #TYPE_UNSET}, {@link #VERTICAL_TYPE_RL} or {@link #VERTICAL_TYPE_LR}.
*/ */
// @Target list includes both 'default' targets and TYPE_USE, to ensure backwards compatibility
// with Kotlin usages from before TYPE_USE was added.
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE}) @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE})
@ -561,7 +569,7 @@ public final class Cue implements Bundleable {
@Nullable private Alignment textAlignment; @Nullable private Alignment textAlignment;
@Nullable private Alignment multiRowAlignment; @Nullable private Alignment multiRowAlignment;
private float line; private float line;
@LineType private int lineType; private @LineType int lineType;
private @AnchorType int lineAnchor; private @AnchorType int lineAnchor;
private float position; private float position;
private @AnchorType int positionAnchor; private @AnchorType int positionAnchor;
@ -722,8 +730,7 @@ public final class Cue implements Bundleable {
* @see Cue#lineType * @see Cue#lineType
*/ */
@Pure @Pure
@LineType public @LineType int getLineType() {
public int getLineType() {
return lineType; return lineType;
} }
@ -743,8 +750,7 @@ public final class Cue implements Bundleable {
* @see Cue#lineAnchor * @see Cue#lineAnchor
*/ */
@Pure @Pure
@AnchorType public @AnchorType int getLineAnchor() {
public int getLineAnchor() {
return lineAnchor; return lineAnchor;
} }
@ -786,8 +792,7 @@ public final class Cue implements Bundleable {
* @see Cue#positionAnchor * @see Cue#positionAnchor
*/ */
@Pure @Pure
@AnchorType public @AnchorType int getPositionAnchor() {
public int getPositionAnchor() {
return positionAnchor; return positionAnchor;
} }
@ -809,8 +814,7 @@ public final class Cue implements Bundleable {
* @see Cue#textSizeType * @see Cue#textSizeType
*/ */
@Pure @Pure
@TextSizeType public @TextSizeType int getTextSizeType() {
public int getTextSizeType() {
return textSizeType; return textSizeType;
} }
@ -928,8 +932,7 @@ public final class Cue implements Bundleable {
* @see Cue#verticalType * @see Cue#verticalType
*/ */
@Pure @Pure
@VerticalType public @VerticalType int getVerticalType() {
public int getVerticalType() {
return verticalType; return verticalType;
} }
@ -960,6 +963,7 @@ public final class Cue implements Bundleable {
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@IntDef({ @IntDef({
FIELD_TEXT, FIELD_TEXT,
FIELD_TEXT_ALIGNMENT, FIELD_TEXT_ALIGNMENT,

View File

@ -39,7 +39,7 @@ public final class RubySpan implements LanguageFeatureSpan {
public final String rubyText; public final String rubyText;
/** The position of the ruby text relative to the base text. */ /** The position of the ruby text relative to the base text. */
@TextAnnotation.Position public final int position; public final @TextAnnotation.Position int position;
public RubySpan(String rubyText, @TextAnnotation.Position int position) { public RubySpan(String rubyText, @TextAnnotation.Position int position) {
this.rubyText = rubyText; this.rubyText = rubyText;

View File

@ -15,12 +15,14 @@
*/ */
package androidx.media3.common.text; package androidx.media3.common.text;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.SOURCE; import static java.lang.annotation.RetentionPolicy.SOURCE;
import androidx.annotation.IntDef; import androidx.annotation.IntDef;
import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.UnstableApi;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.Target;
/** Properties of a text annotation (i.e. ruby, text emphasis marks). */ /** Properties of a text annotation (i.e. ruby, text emphasis marks). */
@UnstableApi @UnstableApi
@ -57,6 +59,7 @@ public final class TextAnnotation {
*/ */
@Documented @Documented
@Retention(SOURCE) @Retention(SOURCE)
@Target(TYPE_USE)
@IntDef({POSITION_UNKNOWN, POSITION_BEFORE, POSITION_AFTER}) @IntDef({POSITION_UNKNOWN, POSITION_BEFORE, POSITION_AFTER})
public @interface Position {} public @interface Position {}

View File

@ -15,12 +15,14 @@
*/ */
package androidx.media3.common.text; package androidx.media3.common.text;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.SOURCE; import static java.lang.annotation.RetentionPolicy.SOURCE;
import androidx.annotation.IntDef; import androidx.annotation.IntDef;
import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.UnstableApi;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.Target;
/** /**
* A styling span for text emphasis marks. * A styling span for text emphasis marks.
@ -50,6 +52,7 @@ public final class TextEmphasisSpan implements LanguageFeatureSpan {
*/ */
@Documented @Documented
@Retention(SOURCE) @Retention(SOURCE)
@Target(TYPE_USE)
@IntDef({MARK_SHAPE_NONE, MARK_SHAPE_CIRCLE, MARK_SHAPE_DOT, MARK_SHAPE_SESAME}) @IntDef({MARK_SHAPE_NONE, MARK_SHAPE_CIRCLE, MARK_SHAPE_DOT, MARK_SHAPE_SESAME})
public @interface MarkShape {} public @interface MarkShape {}
@ -71,6 +74,7 @@ public final class TextEmphasisSpan implements LanguageFeatureSpan {
*/ */
@Documented @Documented
@Retention(SOURCE) @Retention(SOURCE)
@Target(TYPE_USE)
@IntDef({MARK_FILL_UNKNOWN, MARK_FILL_FILLED, MARK_FILL_OPEN}) @IntDef({MARK_FILL_UNKNOWN, MARK_FILL_FILLED, MARK_FILL_OPEN})
public @interface MarkFill {} public @interface MarkFill {}
@ -79,13 +83,13 @@ public final class TextEmphasisSpan implements LanguageFeatureSpan {
public static final int MARK_FILL_OPEN = 2; public static final int MARK_FILL_OPEN = 2;
/** The mark shape used for text emphasis. */ /** The mark shape used for text emphasis. */
@MarkShape public int markShape; public @MarkShape int markShape;
/** The mark fill for the text emphasis mark. */ /** The mark fill for the text emphasis mark. */
@MarkShape public int markFill; public @MarkShape int markFill;
/** The position of the text emphasis relative to the base text. */ /** The position of the text emphasis relative to the base text. */
@TextAnnotation.Position public final int position; public final @TextAnnotation.Position int position;
public TextEmphasisSpan( public TextEmphasisSpan(
@MarkShape int shape, @MarkFill int fill, @TextAnnotation.Position int position) { @MarkShape int shape, @MarkFill int fill, @TextAnnotation.Position int position) {

View File

@ -36,10 +36,14 @@ public interface Clock {
*/ */
long currentTimeMillis(); long currentTimeMillis();
/** @see android.os.SystemClock#elapsedRealtime() */ /**
* @see android.os.SystemClock#elapsedRealtime()
*/
long elapsedRealtime(); long elapsedRealtime();
/** @see android.os.SystemClock#uptimeMillis() */ /**
* @see android.os.SystemClock#uptimeMillis()
*/
long uptimeMillis(); long uptimeMillis();
/** /**

View File

@ -15,6 +15,8 @@
*/ */
package androidx.media3.common.util; package androidx.media3.common.util;
import static java.lang.annotation.ElementType.TYPE_USE;
import android.graphics.SurfaceTexture; import android.graphics.SurfaceTexture;
import android.opengl.EGL14; import android.opengl.EGL14;
import android.opengl.EGLConfig; import android.opengl.EGLConfig;
@ -29,6 +31,7 @@ import androidx.annotation.RequiresApi;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/** Generates a {@link SurfaceTexture} using EGL/GLES functions. */ /** Generates a {@link SurfaceTexture} using EGL/GLES functions. */
@RequiresApi(17) @RequiresApi(17)
@ -47,6 +50,7 @@ public final class EGLSurfaceTexture implements SurfaceTexture.OnFrameAvailableL
*/ */
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@IntDef({SECURE_MODE_NONE, SECURE_MODE_SURFACELESS_CONTEXT, SECURE_MODE_PROTECTED_PBUFFER}) @IntDef({SECURE_MODE_NONE, SECURE_MODE_SURFACELESS_CONTEXT, SECURE_MODE_PROTECTED_PBUFFER})
public @interface SecureMode {} public @interface SecureMode {}

View File

@ -0,0 +1,403 @@
/*
* 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.checkNotNull;
import android.content.Context;
import android.opengl.GLES11Ext;
import android.opengl.GLES20;
import androidx.annotation.Nullable;
import java.io.IOException;
import java.nio.Buffer;
import java.util.HashMap;
import java.util.Map;
/**
* Represents a GLSL shader program.
*
* <p>After constructing a program, keep a reference for its lifetime and call {@link #delete()} (or
* release the current GL context) when it's no longer needed.
*/
@UnstableApi
public final class GlProgram {
// https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_YUV_target.txt
private static final int GL_SAMPLER_EXTERNAL_2D_Y2Y_EXT = 0x8BE7;
/** The identifier of a compiled and linked GLSL shader program. */
private final int programId;
private final Attribute[] attributes;
private final Uniform[] uniforms;
private final Map<String, Attribute> attributeByName;
private final Map<String, Uniform> uniformByName;
/**
* Compiles a GL shader program from vertex and fragment shader GLSL GLES20 code.
*
* @param context The {@link Context}.
* @param vertexShaderFilePath The path to a vertex shader program.
* @param fragmentShaderFilePath The path to a fragment shader program.
* @throws IOException When failing to read shader files.
*/
public GlProgram(Context context, String vertexShaderFilePath, String fragmentShaderFilePath)
throws IOException {
this(
GlUtil.loadAsset(context, vertexShaderFilePath),
GlUtil.loadAsset(context, fragmentShaderFilePath));
}
/**
* Creates a GL shader program from vertex and fragment shader GLSL GLES20 code.
*
* <p>This involves slow steps, like compiling, linking, and switching the GL program, so do not
* call this in fast rendering loops.
*
* @param vertexShaderGlsl The vertex shader program.
* @param fragmentShaderGlsl The fragment shader program.
*/
public GlProgram(String vertexShaderGlsl, String fragmentShaderGlsl) {
programId = GLES20.glCreateProgram();
GlUtil.checkGlError();
// Add the vertex and fragment shaders.
addShader(programId, GLES20.GL_VERTEX_SHADER, vertexShaderGlsl);
addShader(programId, GLES20.GL_FRAGMENT_SHADER, fragmentShaderGlsl);
// Link and use the program, and enumerate attributes/uniforms.
GLES20.glLinkProgram(programId);
int[] linkStatus = new int[] {GLES20.GL_FALSE};
GLES20.glGetProgramiv(programId, GLES20.GL_LINK_STATUS, linkStatus, /* offset= */ 0);
if (linkStatus[0] != GLES20.GL_TRUE) {
GlUtil.throwGlException(
"Unable to link shader program: \n" + GLES20.glGetProgramInfoLog(programId));
}
GLES20.glUseProgram(programId);
attributeByName = new HashMap<>();
int[] attributeCount = new int[1];
GLES20.glGetProgramiv(programId, GLES20.GL_ACTIVE_ATTRIBUTES, attributeCount, /* offset= */ 0);
attributes = new Attribute[attributeCount[0]];
for (int i = 0; i < attributeCount[0]; i++) {
Attribute attribute = Attribute.create(programId, i);
attributes[i] = attribute;
attributeByName.put(attribute.name, attribute);
}
uniformByName = new HashMap<>();
int[] uniformCount = new int[1];
GLES20.glGetProgramiv(programId, GLES20.GL_ACTIVE_UNIFORMS, uniformCount, /* offset= */ 0);
uniforms = new Uniform[uniformCount[0]];
for (int i = 0; i < uniformCount[0]; i++) {
Uniform uniform = Uniform.create(programId, i);
uniforms[i] = uniform;
uniformByName.put(uniform.name, uniform);
}
GlUtil.checkGlError();
}
private static void addShader(int programId, int type, String glsl) {
int shader = GLES20.glCreateShader(type);
GLES20.glShaderSource(shader, glsl);
GLES20.glCompileShader(shader);
int[] result = new int[] {GLES20.GL_FALSE};
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, result, /* offset= */ 0);
if (result[0] != GLES20.GL_TRUE) {
GlUtil.throwGlException(GLES20.glGetShaderInfoLog(shader) + ", source: " + glsl);
}
GLES20.glAttachShader(programId, shader);
GLES20.glDeleteShader(shader);
GlUtil.checkGlError();
}
private static int getAttributeLocation(int programId, String attributeName) {
return GLES20.glGetAttribLocation(programId, attributeName);
}
/** Returns the location of an {@link Attribute}. */
private int getAttributeLocation(String attributeName) {
return getAttributeLocation(programId, attributeName);
}
private static int getUniformLocation(int programId, String uniformName) {
return GLES20.glGetUniformLocation(programId, uniformName);
}
/** Returns the location of a {@link Uniform}. */
public int getUniformLocation(String uniformName) {
return getUniformLocation(programId, uniformName);
}
/**
* Uses the program.
*
* <p>Call this in the rendering loop to switch between different programs.
*/
public void use() {
// TODO(b/214975934): When multiple GL programs are supported by Transformer, make sure
// to call use() to switch between programs.
GLES20.glUseProgram(programId);
GlUtil.checkGlError();
}
/** Deletes the program. Deleted programs cannot be used again. */
public void delete() {
GLES20.glDeleteProgram(programId);
GlUtil.checkGlError();
}
/**
* Returns the location of an {@link Attribute}, which has been enabled as a vertex attribute
* array.
*/
public int getAttributeArrayLocationAndEnable(String attributeName) {
int location = getAttributeLocation(attributeName);
GLES20.glEnableVertexAttribArray(location);
GlUtil.checkGlError();
return location;
}
/** Sets a float buffer type attribute. */
public void setBufferAttribute(String name, float[] values, int size) {
checkNotNull(attributeByName.get(name)).setBuffer(values, size);
}
/** Sets a texture sampler type uniform. */
public void setSamplerTexIdUniform(String name, int texId, int unit) {
checkNotNull(uniformByName.get(name)).setSamplerTexId(texId, unit);
}
/** Sets a float type uniform. */
public void setFloatUniform(String name, float value) {
checkNotNull(uniformByName.get(name)).setFloat(value);
}
/** Sets a float array type uniform. */
public void setFloatsUniform(String name, float[] value) {
checkNotNull(uniformByName.get(name)).setFloats(value);
}
/** Binds all attributes and uniforms in the program. */
public void bindAttributesAndUniforms() {
for (Attribute attribute : attributes) {
attribute.bind();
}
for (Uniform uniform : uniforms) {
uniform.bind();
}
}
/** Returns the length of the null-terminated C string in {@code cString}. */
private static int getCStringLength(byte[] cString) {
for (int i = 0; i < cString.length; ++i) {
if (cString[i] == '\0') {
return i;
}
}
return cString.length;
}
/**
* GL attribute, which can be attached to a buffer with {@link Attribute#setBuffer(float[], int)}.
*/
private static final class Attribute {
/* Returns the attribute at the given index in the program. */
public static Attribute create(int programId, int index) {
int[] length = new int[1];
GLES20.glGetProgramiv(
programId, GLES20.GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, length, /* offset= */ 0);
byte[] nameBytes = new byte[length[0]];
GLES20.glGetActiveAttrib(
programId,
index,
length[0],
/* unusedLength */ new int[1],
/* lengthOffset= */ 0,
/* unusedSize */ new int[1],
/* sizeOffset= */ 0,
/* unusedType */ new int[1],
/* typeOffset= */ 0,
nameBytes,
/* nameOffset= */ 0);
String name = new String(nameBytes, /* offset= */ 0, getCStringLength(nameBytes));
int location = getAttributeLocation(programId, name);
return new Attribute(name, index, location);
}
/** The name of the attribute in the GLSL sources. */
public final String name;
private final int index;
private final int location;
@Nullable private Buffer buffer;
private int size;
private Attribute(String name, int index, int location) {
this.name = name;
this.index = index;
this.location = location;
}
/**
* Configures {@link #bind()} to attach vertices in {@code buffer} (each of size {@code size}
* elements) to this {@link Attribute}.
*
* @param buffer Buffer to bind to this attribute.
* @param size Number of elements per vertex.
*/
public void setBuffer(float[] buffer, int size) {
this.buffer = GlUtil.createBuffer(buffer);
this.size = size;
}
/**
* Sets the vertex attribute to whatever was attached via {@link #setBuffer(float[], int)}.
*
* <p>Should be called before each drawing call.
*/
public void bind() {
Buffer buffer = checkNotNull(this.buffer, "call setBuffer before bind");
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, /* buffer= */ 0);
GLES20.glVertexAttribPointer(
location, size, GLES20.GL_FLOAT, /* normalized= */ false, /* stride= */ 0, buffer);
GLES20.glEnableVertexAttribArray(index);
GlUtil.checkGlError();
}
}
/**
* GL uniform, which can be attached to a sampler using {@link Uniform#setSamplerTexId(int, int)}.
*/
private static final class Uniform {
/** Returns the uniform at the given index in the program. */
public static Uniform create(int programId, int index) {
int[] length = new int[1];
GLES20.glGetProgramiv(
programId, GLES20.GL_ACTIVE_UNIFORM_MAX_LENGTH, length, /* offset= */ 0);
int[] type = new int[1];
byte[] nameBytes = new byte[length[0]];
GLES20.glGetActiveUniform(
programId,
index,
length[0],
/* unusedLength */ new int[1],
/* lengthOffset= */ 0,
/* unusedSize */ new int[1],
/*sizeOffset= */ 0,
type,
/* typeOffset= */ 0,
nameBytes,
/* nameOffset= */ 0);
String name = new String(nameBytes, /* offset= */ 0, getCStringLength(nameBytes));
int location = getUniformLocation(programId, name);
return new Uniform(name, location, type[0]);
}
/** The name of the uniform in the GLSL sources. */
public final String name;
private final int location;
private final int type;
private final float[] value;
private int texId;
private int unit;
private Uniform(String name, int location, int type) {
this.name = name;
this.location = location;
this.type = type;
this.value = new float[16];
}
/**
* Configures {@link #bind()} to use the specified {@code texId} for this sampler uniform.
*
* @param texId The GL texture identifier from which to sample.
* @param unit The GL texture unit index.
*/
public void setSamplerTexId(int texId, int unit) {
this.texId = texId;
this.unit = unit;
}
/** Configures {@link #bind()} to use the specified float {@code value} for this uniform. */
public void setFloat(float value) {
this.value[0] = value;
}
/** Configures {@link #bind()} to use the specified float[] {@code value} for this uniform. */
public void setFloats(float[] value) {
System.arraycopy(value, /* srcPos= */ 0, this.value, /* destPos= */ 0, value.length);
}
/**
* Sets the uniform to whatever value was passed via {@link #setSamplerTexId(int, int)}, {@link
* #setFloat(float)} or {@link #setFloats(float[])}.
*
* <p>Should be called before each drawing call.
*/
public void bind() {
if (type == GLES20.GL_FLOAT) {
GLES20.glUniform1fv(location, /* count= */ 1, value, /* offset= */ 0);
GlUtil.checkGlError();
return;
}
if (type == GLES20.GL_FLOAT_MAT3) {
GLES20.glUniformMatrix3fv(
location, /* count= */ 1, /* transpose= */ false, value, /* offset= */ 0);
GlUtil.checkGlError();
return;
}
if (type == GLES20.GL_FLOAT_MAT4) {
GLES20.glUniformMatrix4fv(
location, /* count= */ 1, /* transpose= */ false, value, /* offset= */ 0);
GlUtil.checkGlError();
return;
}
if (texId == 0) {
throw new IllegalStateException("No call to setSamplerTexId() before bind.");
}
GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + unit);
if (type == GLES11Ext.GL_SAMPLER_EXTERNAL_OES || type == GL_SAMPLER_EXTERNAL_2D_Y2Y_EXT) {
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, texId);
} else if (type == GLES20.GL_SAMPLER_2D) {
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texId);
} else {
throw new IllegalStateException("Unexpected uniform type: " + type);
}
GLES20.glUniform1i(location, unit);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(
GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(
GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
GlUtil.checkGlError();
}
}
}

View File

@ -16,7 +16,6 @@
package androidx.media3.common.util; package androidx.media3.common.util;
import static android.opengl.GLU.gluErrorString; import static android.opengl.GLU.gluErrorString;
import static androidx.media3.common.util.Assertions.checkNotNull;
import android.content.Context; import android.content.Context;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
@ -33,16 +32,13 @@ import androidx.annotation.RequiresApi;
import androidx.media3.common.C; import androidx.media3.common.C;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.nio.Buffer;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.nio.FloatBuffer; import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.HashMap;
import java.util.Map;
import javax.microedition.khronos.egl.EGL10; import javax.microedition.khronos.egl.EGL10;
/** OpenGL ES 2.0 utilities. */ /** OpenGL ES utilities. */
@SuppressWarnings("InlinedApi") // GLES constants are used safely based on the API version.
@UnstableApi @UnstableApi
public final class GlUtil { public final class GlUtil {
@ -54,163 +50,73 @@ public final class GlUtil {
} }
} }
/**
* Represents a GLSL shader program.
*
* <p>After constructing a program, keep a reference for its lifetime and call {@link #delete()}
* (or release the current GL context) when it's no longer needed.
*/
public static final class Program {
/** The identifier of a compiled and linked GLSL shader program. */
private final int programId;
private final Attribute[] attributes;
private final Uniform[] uniforms;
private final Map<String, Attribute> attributeByName;
private final Map<String, Uniform> uniformByName;
/**
* Compiles a GL shader program from vertex and fragment shader GLSL GLES20 code.
*
* @param context The {@link Context}.
* @param vertexShaderFilePath The path to a vertex shader program.
* @param fragmentShaderFilePath The path to a fragment shader program.
* @throws IOException When failing to read shader files.
*/
public Program(Context context, String vertexShaderFilePath, String fragmentShaderFilePath)
throws IOException {
this(loadAsset(context, vertexShaderFilePath), loadAsset(context, fragmentShaderFilePath));
}
/**
* Creates a GL shader program from vertex and fragment shader GLSL GLES20 code.
*
* <p>This involves slow steps, like compiling, linking, and switching the GL program, so do not
* call this in fast rendering loops.
*
* @param vertexShaderGlsl The vertex shader program.
* @param fragmentShaderGlsl The fragment shader program.
*/
public Program(String vertexShaderGlsl, String fragmentShaderGlsl) {
programId = GLES20.glCreateProgram();
checkGlError();
// Add the vertex and fragment shaders.
addShader(programId, GLES20.GL_VERTEX_SHADER, vertexShaderGlsl);
addShader(programId, GLES20.GL_FRAGMENT_SHADER, fragmentShaderGlsl);
// Link and use the program, and enumerate attributes/uniforms.
GLES20.glLinkProgram(programId);
int[] linkStatus = new int[] {GLES20.GL_FALSE};
GLES20.glGetProgramiv(programId, GLES20.GL_LINK_STATUS, linkStatus, /* offset= */ 0);
if (linkStatus[0] != GLES20.GL_TRUE) {
throwGlException(
"Unable to link shader program: \n" + GLES20.glGetProgramInfoLog(programId));
}
GLES20.glUseProgram(programId);
attributeByName = new HashMap<>();
int[] attributeCount = new int[1];
GLES20.glGetProgramiv(
programId, GLES20.GL_ACTIVE_ATTRIBUTES, attributeCount, /* offset= */ 0);
attributes = new Attribute[attributeCount[0]];
for (int i = 0; i < attributeCount[0]; i++) {
Attribute attribute = Attribute.create(programId, i);
attributes[i] = attribute;
attributeByName.put(attribute.name, attribute);
}
uniformByName = new HashMap<>();
int[] uniformCount = new int[1];
GLES20.glGetProgramiv(programId, GLES20.GL_ACTIVE_UNIFORMS, uniformCount, /* offset= */ 0);
uniforms = new Uniform[uniformCount[0]];
for (int i = 0; i < uniformCount[0]; i++) {
Uniform uniform = Uniform.create(programId, i);
uniforms[i] = uniform;
uniformByName.put(uniform.name, uniform);
}
checkGlError();
}
/**
* Uses the program.
*
* <p>Call this in the rendering loop to switch between different programs.
*/
public void use() {
// TODO(http://b/205002913): When multiple GL programs are supported by Transformer, make sure
// to call use() to switch between programs.
GLES20.glUseProgram(programId);
checkGlError();
}
/** Deletes the program. Deleted programs cannot be used again. */
public void delete() {
GLES20.glDeleteProgram(programId);
checkGlError();
}
/**
* Returns the location of an {@link Attribute}, which has been enabled as a vertex attribute
* array.
*/
public int getAttributeArrayLocationAndEnable(String attributeName) {
int location = getAttributeLocation(attributeName);
GLES20.glEnableVertexAttribArray(location);
checkGlError();
return location;
}
/** Returns the location of an {@link Attribute}. */
private int getAttributeLocation(String attributeName) {
return GlUtil.getAttributeLocation(programId, attributeName);
}
/** Returns the location of a {@link Uniform}. */
public int getUniformLocation(String uniformName) {
return GlUtil.getUniformLocation(programId, uniformName);
}
/** Sets a float buffer type attribute. */
public void setBufferAttribute(String name, float[] values, int size) {
checkNotNull(attributeByName.get(name)).setBuffer(values, size);
}
/** Sets a texture sampler type uniform. */
public void setSamplerTexIdUniform(String name, int texId, int unit) {
checkNotNull(uniformByName.get(name)).setSamplerTexId(texId, unit);
}
/** Sets a float type uniform. */
public void setFloatUniform(String name, float value) {
checkNotNull(uniformByName.get(name)).setFloat(value);
}
/** Sets a float array type uniform. */
public void setFloatsUniform(String name, float[] value) {
checkNotNull(uniformByName.get(name)).setFloats(value);
}
/** Binds all attributes and uniforms in the program. */
public void bindAttributesAndUniforms() {
for (Attribute attribute : attributes) {
attribute.bind();
}
for (Uniform uniform : uniforms) {
uniform.bind();
}
}
}
/** Whether to throw a {@link GlException} in case of an OpenGL error. */ /** Whether to throw a {@link GlException} in case of an OpenGL error. */
public static boolean glAssertionsEnabled = false; public static boolean glAssertionsEnabled = false;
/** Number of vertices in a rectangle. */
public static final int RECTANGLE_VERTICES_COUNT = 4;
private static final String TAG = "GlUtil"; private static final String TAG = "GlUtil";
// https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_protected_content.txt
private static final String EXTENSION_PROTECTED_CONTENT = "EGL_EXT_protected_content"; private static final String EXTENSION_PROTECTED_CONTENT = "EGL_EXT_protected_content";
// https://www.khronos.org/registry/EGL/extensions/KHR/EGL_KHR_surfaceless_context.txt
private static final String EXTENSION_SURFACELESS_CONTEXT = "EGL_KHR_surfaceless_context"; private static final String EXTENSION_SURFACELESS_CONTEXT = "EGL_KHR_surfaceless_context";
// https://www.khronos.org/registry/EGL/extensions/KHR/EGL_KHR_gl_colorspace.txt
private static final int EGL_GL_COLORSPACE_KHR = 0x309D;
// https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_gl_colorspace_bt2020_linear.txt
private static final int EGL_GL_COLORSPACE_BT2020_PQ_EXT = 0x3340;
private static final int[] EGL_WINDOW_SURFACE_ATTRIBUTES_NONE = new int[] {EGL14.EGL_NONE};
private static final int[] EGL_WINDOW_SURFACE_ATTRIBUTES_BT2020_PQ =
new int[] {EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_BT2020_PQ_EXT, EGL14.EGL_NONE};
private static final int[] EGL_CONFIG_ATTRIBUTES_RGBA_8888 =
new int[] {
EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
EGL14.EGL_RED_SIZE, /* redSize= */ 8,
EGL14.EGL_GREEN_SIZE, /* greenSize= */ 8,
EGL14.EGL_BLUE_SIZE, /* blueSize= */ 8,
EGL14.EGL_ALPHA_SIZE, /* alphaSize= */ 8,
EGL14.EGL_DEPTH_SIZE, /* depthSize= */ 0,
EGL14.EGL_STENCIL_SIZE, /* stencilSize= */ 0,
EGL14.EGL_NONE
};
private static final int[] EGL_CONFIG_ATTRIBUTES_RGBA_1010102 =
new int[] {
EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
EGL14.EGL_RED_SIZE, /* redSize= */ 10,
EGL14.EGL_GREEN_SIZE, /* greenSize= */ 10,
EGL14.EGL_BLUE_SIZE, /* blueSize= */ 10,
EGL14.EGL_ALPHA_SIZE, /* alphaSize= */ 2,
EGL14.EGL_DEPTH_SIZE, /* depthSize= */ 0,
EGL14.EGL_STENCIL_SIZE, /* stencilSize= */ 0,
EGL14.EGL_NONE
};
/** Class only contains static methods. */ /** Class only contains static methods. */
private GlUtil() {} private GlUtil() {}
/** Bounds of normalized device coordinates, commonly used for defining viewport boundaries. */
public static float[] getNormalizedCoordinateBounds() {
return new float[] {
-1, -1, 0, 1,
1, -1, 0, 1,
-1, 1, 0, 1,
1, 1, 0, 1
};
}
/** Typical bounds used for sampling from textures. */
public static float[] getTextureCoordinateBounds() {
return new float[] {
0, 0, 0, 1,
1, 0, 0, 1,
0, 1, 0, 1,
1, 1, 0, 1
};
}
/** /**
* Returns whether creating a GL context with {@value #EXTENSION_PROTECTED_CONTENT} is possible. * Returns whether creating a GL context with {@value #EXTENSION_PROTECTED_CONTENT} is possible.
* If {@code true}, the device supports a protected output path for DRM content when using GL. * If {@code true}, the device supports a protected output path for DRM content when using GL.
@ -260,7 +166,16 @@ public final class GlUtil {
/** Returns a new {@link EGLContext} for the specified {@link EGLDisplay}. */ /** Returns a new {@link EGLContext} for the specified {@link EGLDisplay}. */
@RequiresApi(17) @RequiresApi(17)
public static EGLContext createEglContext(EGLDisplay eglDisplay) { public static EGLContext createEglContext(EGLDisplay eglDisplay) {
return Api17.createEglContext(eglDisplay); return Api17.createEglContext(eglDisplay, /* version= */ 2, EGL_CONFIG_ATTRIBUTES_RGBA_8888);
}
/**
* Returns a new {@link EGLContext} for the specified {@link EGLDisplay}, requesting ES 3 and an
* RGBA 1010102 config.
*/
@RequiresApi(17)
public static EGLContext createEglContextEs3Rgba1010102(EGLDisplay eglDisplay) {
return Api17.createEglContext(eglDisplay, /* version= */ 3, EGL_CONFIG_ATTRIBUTES_RGBA_1010102);
} }
/** /**
@ -271,7 +186,24 @@ public final class GlUtil {
*/ */
@RequiresApi(17) @RequiresApi(17)
public static EGLSurface getEglSurface(EGLDisplay eglDisplay, Object surface) { public static EGLSurface getEglSurface(EGLDisplay eglDisplay, Object surface) {
return Api17.getEglSurface(eglDisplay, surface); return Api17.getEglSurface(
eglDisplay, surface, EGL_CONFIG_ATTRIBUTES_RGBA_8888, EGL_WINDOW_SURFACE_ATTRIBUTES_NONE);
}
/**
* Returns a new {@link EGLSurface} wrapping the specified {@code surface}, for HDR rendering with
* Rec. 2020 color primaries and using the PQ transfer function.
*
* @param eglDisplay The {@link EGLDisplay} to attach the surface to.
* @param surface The surface to wrap; must be a surface, surface texture or surface holder.
*/
@RequiresApi(17)
public static EGLSurface getEglSurfaceBt2020Pq(EGLDisplay eglDisplay, Object surface) {
return Api17.getEglSurface(
eglDisplay,
surface,
EGL_CONFIG_ATTRIBUTES_RGBA_1010102,
EGL_WINDOW_SURFACE_ATTRIBUTES_BT2020_PQ);
} }
/** /**
@ -291,13 +223,29 @@ public final class GlUtil {
} }
/** /**
* Makes the specified {@code surface} the render target, using a viewport of {@code width} by * Makes the specified {@code eglSurface} the render target, using a viewport of {@code width} by
* {@code height} pixels. * {@code height} pixels.
*/ */
@RequiresApi(17) @RequiresApi(17)
public static void focusSurface( public static void focusEglSurface(
EGLDisplay eglDisplay, EGLContext eglContext, EGLSurface surface, int width, int height) { EGLDisplay eglDisplay, EGLContext eglContext, EGLSurface eglSurface, int width, int height) {
Api17.focusSurface(eglDisplay, eglContext, surface, width, height); Api17.focusRenderTarget(
eglDisplay, eglContext, eglSurface, /* framebuffer= */ 0, width, height);
}
/**
* Makes the specified {@code framebuffer} the render target, using a viewport of {@code width} by
* {@code height} pixels.
*/
@RequiresApi(17)
public static void focusFramebuffer(
EGLDisplay eglDisplay,
EGLContext eglContext,
EGLSurface eglSurface,
int framebuffer,
int width,
int height) {
Api17.focusRenderTarget(eglDisplay, eglContext, eglSurface, framebuffer, width, height);
} }
/** /**
@ -362,46 +310,73 @@ public final class GlUtil {
* GL_CLAMP_TO_EDGE wrapping. * GL_CLAMP_TO_EDGE wrapping.
*/ */
public static int createExternalTexture() { public static int createExternalTexture() {
return generateAndBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES);
}
/**
* Returns the texture identifier for a newly-allocated texture with the specified dimensions.
*
* @param width of the new texture in pixels
* @param height of the new texture in pixels
*/
public static int createTexture(int width, int height) {
int texId = generateAndBindTexture(GLES20.GL_TEXTURE_2D);
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(width * height * 4);
GLES20.glTexImage2D(
GLES20.GL_TEXTURE_2D,
/* level= */ 0,
GLES20.GL_RGBA,
width,
height,
/* border= */ 0,
GLES20.GL_RGBA,
GLES20.GL_UNSIGNED_BYTE,
byteBuffer);
checkGlError();
return texId;
}
/**
* Returns a GL texture identifier of a newly generated and bound texture of the requested type
* with default configuration of GL_LINEAR filtering and GL_CLAMP_TO_EDGE wrapping.
*
* @param textureTarget The target to which the texture is bound, e.g. {@link
* GLES20#GL_TEXTURE_2D} for a two-dimensional texture or {@link
* GLES11Ext#GL_TEXTURE_EXTERNAL_OES} for an external texture.
*/
private static int generateAndBindTexture(int textureTarget) {
int[] texId = new int[1]; int[] texId = new int[1];
GLES20.glGenTextures(/* n= */ 1, IntBuffer.wrap(texId)); GLES20.glGenTextures(/* n= */ 1, texId, /* offset= */ 0);
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, texId[0]); checkGlError();
GLES20.glTexParameteri( GLES20.glBindTexture(textureTarget, texId[0]);
GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); checkGlError();
GLES20.glTexParameteri( GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); checkGlError();
GLES20.glTexParameteri( GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); checkGlError();
GLES20.glTexParameteri( GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
checkGlError(); checkGlError();
return texId[0]; return texId[0];
} }
private static void addShader(int programId, int type, String glsl) { /**
int shader = GLES20.glCreateShader(type); * Returns a new framebuffer for the texture.
GLES20.glShaderSource(shader, glsl); *
GLES20.glCompileShader(shader); * @param texId The identifier of the texture to attach to the framebuffer.
*/
int[] result = new int[] {GLES20.GL_FALSE}; public static int createFboForTexture(int texId) {
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, result, /* offset= */ 0); int[] fboId = new int[1];
if (result[0] != GLES20.GL_TRUE) { GLES20.glGenFramebuffers(/* n= */ 1, fboId, /* offset= */ 0);
throwGlException(GLES20.glGetShaderInfoLog(shader) + ", source: " + glsl);
}
GLES20.glAttachShader(programId, shader);
GLES20.glDeleteShader(shader);
checkGlError(); checkGlError();
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fboId[0]);
checkGlError();
GLES20.glFramebufferTexture2D(
GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, texId, 0);
checkGlError();
return fboId[0];
} }
private static int getAttributeLocation(int programId, String attributeName) { /* package */ static void throwGlException(String errorMsg) {
return GLES20.glGetAttribLocation(programId, attributeName);
}
private static int getUniformLocation(int programId, String uniformName) {
return GLES20.glGetUniformLocation(programId, uniformName);
}
private static void throwGlException(String errorMsg) {
Log.e(TAG, errorMsg); Log.e(TAG, errorMsg);
if (glAssertionsEnabled) { if (glAssertionsEnabled) {
throw new GlException(errorMsg); throw new GlException(errorMsg);
@ -414,200 +389,6 @@ public final class GlUtil {
} }
} }
/** Returns the length of the null-terminated string in {@code strVal}. */
private static int strlen(byte[] strVal) {
for (int i = 0; i < strVal.length; ++i) {
if (strVal[i] == '\0') {
return i;
}
}
return strVal.length;
}
/**
* GL attribute, which can be attached to a buffer with {@link Attribute#setBuffer(float[], int)}.
*/
private static final class Attribute {
/* Returns the attribute at the given index in the program. */
public static Attribute create(int programId, int index) {
int[] length = new int[1];
GLES20.glGetProgramiv(
programId, GLES20.GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, length, /* offset= */ 0);
byte[] nameBytes = new byte[length[0]];
GLES20.glGetActiveAttrib(
programId,
index,
length[0],
/* unusedLength */ new int[1],
/* lengthOffset= */ 0,
/* unusedSize */ new int[1],
/* sizeOffset= */ 0,
/* unusedType */ new int[1],
/* typeOffset= */ 0,
nameBytes,
/* nameOffset= */ 0);
String name = new String(nameBytes, /* offset= */ 0, strlen(nameBytes));
int location = getAttributeLocation(programId, name);
return new Attribute(name, index, location);
}
/** The name of the attribute in the GLSL sources. */
public final String name;
private final int index;
private final int location;
@Nullable private Buffer buffer;
private int size;
private Attribute(String name, int index, int location) {
this.name = name;
this.index = index;
this.location = location;
}
/**
* Configures {@link #bind()} to attach vertices in {@code buffer} (each of size {@code size}
* elements) to this {@link Attribute}.
*
* @param buffer Buffer to bind to this attribute.
* @param size Number of elements per vertex.
*/
public void setBuffer(float[] buffer, int size) {
this.buffer = createBuffer(buffer);
this.size = size;
}
/**
* Sets the vertex attribute to whatever was attached via {@link #setBuffer(float[], int)}.
*
* <p>Should be called before each drawing call.
*/
public void bind() {
Buffer buffer = checkNotNull(this.buffer, "call setBuffer before bind");
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, /* buffer= */ 0);
GLES20.glVertexAttribPointer(
location, size, GLES20.GL_FLOAT, /* normalized= */ false, /* stride= */ 0, buffer);
GLES20.glEnableVertexAttribArray(index);
checkGlError();
}
}
/**
* GL uniform, which can be attached to a sampler using {@link Uniform#setSamplerTexId(int, int)}.
*/
private static final class Uniform {
/** Returns the uniform at the given index in the program. */
public static Uniform create(int programId, int index) {
int[] length = new int[1];
GLES20.glGetProgramiv(
programId, GLES20.GL_ACTIVE_UNIFORM_MAX_LENGTH, length, /* offset= */ 0);
int[] type = new int[1];
byte[] nameBytes = new byte[length[0]];
GLES20.glGetActiveUniform(
programId,
index,
length[0],
/* unusedLength */ new int[1],
/* lengthOffset= */ 0,
/* unusedSize */ new int[1],
/*sizeOffset= */ 0,
type,
/* typeOffset= */ 0,
nameBytes,
/* nameOffset= */ 0);
String name = new String(nameBytes, /* offset= */ 0, strlen(nameBytes));
int location = getUniformLocation(programId, name);
return new Uniform(name, location, type[0]);
}
/** The name of the uniform in the GLSL sources. */
public final String name;
private final int location;
private final int type;
private final float[] value;
private int texId;
private int unit;
private Uniform(String name, int location, int type) {
this.name = name;
this.location = location;
this.type = type;
this.value = new float[16];
}
/**
* Configures {@link #bind()} to use the specified {@code texId} for this sampler uniform.
*
* @param texId The GL texture identifier from which to sample.
* @param unit The GL texture unit index.
*/
public void setSamplerTexId(int texId, int unit) {
this.texId = texId;
this.unit = unit;
}
/** Configures {@link #bind()} to use the specified float {@code value} for this uniform. */
public void setFloat(float value) {
this.value[0] = value;
}
/** Configures {@link #bind()} to use the specified float[] {@code value} for this uniform. */
public void setFloats(float[] value) {
System.arraycopy(value, /* srcPos= */ 0, this.value, /* destPos= */ 0, value.length);
}
/**
* Sets the uniform to whatever value was passed via {@link #setSamplerTexId(int, int)}, {@link
* #setFloat(float)} or {@link #setFloats(float[])}.
*
* <p>Should be called before each drawing call.
*/
public void bind() {
if (type == GLES20.GL_FLOAT) {
GLES20.glUniform1fv(location, /* count= */ 1, value, /* offset= */ 0);
checkGlError();
return;
}
if (type == GLES20.GL_FLOAT_MAT4) {
GLES20.glUniformMatrix4fv(
location, /* count= */ 1, /* transpose= */ false, value, /* offset= */ 0);
checkGlError();
return;
}
if (texId == 0) {
throw new IllegalStateException("No call to setSamplerTexId() before bind.");
}
GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + unit);
if (type == GLES11Ext.GL_SAMPLER_EXTERNAL_OES) {
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, texId);
} else if (type == GLES20.GL_SAMPLER_2D) {
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texId);
} else {
throw new IllegalStateException("Unexpected uniform type: " + type);
}
GLES20.glUniform1i(location, unit);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(
GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(
GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
checkGlError();
}
}
@RequiresApi(17) @RequiresApi(17)
private static final class Api17 { private static final class Api17 {
private Api17() {} private Api17() {}
@ -629,12 +410,13 @@ public final class GlUtil {
} }
@DoNotInline @DoNotInline
public static EGLContext createEglContext(EGLDisplay eglDisplay) { public static EGLContext createEglContext(
int[] contextAttributes = {EGL14.EGL_CONTEXT_CLIENT_VERSION, 2, EGL14.EGL_NONE}; EGLDisplay eglDisplay, int version, int[] configAttributes) {
int[] contextAttributes = {EGL14.EGL_CONTEXT_CLIENT_VERSION, version, EGL14.EGL_NONE};
EGLContext eglContext = EGLContext eglContext =
EGL14.eglCreateContext( EGL14.eglCreateContext(
eglDisplay, eglDisplay,
getEglConfig(eglDisplay), getEglConfig(eglDisplay, configAttributes),
EGL14.EGL_NO_CONTEXT, EGL14.EGL_NO_CONTEXT,
contextAttributes, contextAttributes,
/* offset= */ 0); /* offset= */ 0);
@ -642,32 +424,41 @@ public final class GlUtil {
EGL14.eglTerminate(eglDisplay); EGL14.eglTerminate(eglDisplay);
throwGlException( throwGlException(
"eglCreateContext() failed to create a valid context. The device may not support EGL" "eglCreateContext() failed to create a valid context. The device may not support EGL"
+ " version 2"); + " version "
+ version);
} }
checkGlError(); checkGlError();
return eglContext; return eglContext;
} }
@DoNotInline @DoNotInline
public static EGLSurface getEglSurface(EGLDisplay eglDisplay, Object surface) { public static EGLSurface getEglSurface(
EGLDisplay eglDisplay,
Object surface,
int[] configAttributes,
int[] windowSurfaceAttributes) {
return EGL14.eglCreateWindowSurface( return EGL14.eglCreateWindowSurface(
eglDisplay, eglDisplay,
getEglConfig(eglDisplay), getEglConfig(eglDisplay, configAttributes),
surface, surface,
new int[] {EGL14.EGL_NONE}, windowSurfaceAttributes,
/* offset= */ 0); /* offset= */ 0);
} }
@DoNotInline @DoNotInline
public static void focusSurface( public static void focusRenderTarget(
EGLDisplay eglDisplay, EGLContext eglContext, EGLSurface surface, int width, int height) { EGLDisplay eglDisplay,
int[] boundFrameBuffer = new int[1]; EGLContext eglContext,
GLES20.glGetIntegerv(GLES20.GL_FRAMEBUFFER_BINDING, boundFrameBuffer, /* offset= */ 0); EGLSurface eglSurface,
int defaultFrameBuffer = 0; int framebuffer,
if (boundFrameBuffer[0] != defaultFrameBuffer) { int width,
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, defaultFrameBuffer); int height) {
int[] boundFramebuffer = new int[1];
GLES20.glGetIntegerv(GLES20.GL_FRAMEBUFFER_BINDING, boundFramebuffer, /* offset= */ 0);
if (boundFramebuffer[0] != framebuffer) {
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, framebuffer);
} }
EGL14.eglMakeCurrent(eglDisplay, surface, surface, eglContext); EGL14.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);
GLES20.glViewport(/* x= */ 0, /* y= */ 0, width, height); GLES20.glViewport(/* x= */ 0, /* y= */ 0, width, height);
} }
@ -695,22 +486,11 @@ public final class GlUtil {
} }
@DoNotInline @DoNotInline
private static EGLConfig getEglConfig(EGLDisplay eglDisplay) { private static EGLConfig getEglConfig(EGLDisplay eglDisplay, int[] attributes) {
int[] defaultConfiguration =
new int[] {
EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
EGL14.EGL_RED_SIZE, /* redSize= */ 8,
EGL14.EGL_GREEN_SIZE, /* greenSize= */ 8,
EGL14.EGL_BLUE_SIZE, /* blueSize= */ 8,
EGL14.EGL_ALPHA_SIZE, /* alphaSize= */ 8,
EGL14.EGL_DEPTH_SIZE, /* depthSize= */ 0,
EGL14.EGL_STENCIL_SIZE, /* stencilSize= */ 0,
EGL14.EGL_NONE
};
EGLConfig[] eglConfigs = new EGLConfig[1]; EGLConfig[] eglConfigs = new EGLConfig[1];
if (!EGL14.eglChooseConfig( if (!EGL14.eglChooseConfig(
eglDisplay, eglDisplay,
defaultConfiguration, attributes,
/* attrib_listOffset= */ 0, /* attrib_listOffset= */ 0,
eglConfigs, eglConfigs,
/* configsOffset= */ 0, /* configsOffset= */ 0,

View File

@ -19,7 +19,7 @@ import java.util.Arrays;
/** Configurable loader for native libraries. */ /** Configurable loader for native libraries. */
@UnstableApi @UnstableApi
public final class LibraryLoader { public abstract class LibraryLoader {
private static final String TAG = "LibraryLoader"; private static final String TAG = "LibraryLoader";
@ -27,7 +27,9 @@ public final class LibraryLoader {
private boolean loadAttempted; private boolean loadAttempted;
private boolean isAvailable; private boolean isAvailable;
/** @param libraries The names of the libraries to load. */ /**
* @param libraries The names of the libraries to load.
*/
public LibraryLoader(String... libraries) { public LibraryLoader(String... libraries) {
nativeLibraries = libraries; nativeLibraries = libraries;
} }
@ -49,7 +51,7 @@ public final class LibraryLoader {
loadAttempted = true; loadAttempted = true;
try { try {
for (String lib : nativeLibraries) { for (String lib : nativeLibraries) {
System.loadLibrary(lib); loadLibrary(lib);
} }
isAvailable = true; isAvailable = true;
} catch (UnsatisfiedLinkError exception) { } catch (UnsatisfiedLinkError exception) {
@ -59,4 +61,17 @@ public final class LibraryLoader {
} }
return isAvailable; return isAvailable;
} }
/**
* Should be implemented to call {@code System.loadLibrary(name)}.
*
* <p>It's necessary for each subclass to implement this method because {@link
* System#loadLibrary(String)} uses reflection to obtain the calling class, which is then used to
* obtain the class loader to use when loading the native library. If this class were to implement
* the method directly, and if a subclass were to have a different class loader, then loading of
* the native library would fail.
*
* @param name The name of the library to load.
*/
protected abstract void loadLibrary(String name);
} }

View File

@ -118,6 +118,21 @@ public final class ListenerSet<T extends @NonNull Object> {
*/ */
@CheckResult @CheckResult
public ListenerSet<T> copy(Looper looper, IterationFinishedEvent<T> iterationFinishedEvent) { public ListenerSet<T> copy(Looper looper, IterationFinishedEvent<T> iterationFinishedEvent) {
return copy(looper, clock, iterationFinishedEvent);
}
/**
* Copies the listener set.
*
* @param looper The new {@link Looper} for the copied listener set.
* @param clock The new {@link Clock} for the copied listener set.
* @param iterationFinishedEvent The new {@link IterationFinishedEvent} sent when all other events
* sent during one {@link Looper} message queue iteration were handled by the listeners.
* @return The copied listener set.
*/
@CheckResult
public ListenerSet<T> copy(
Looper looper, Clock clock, IterationFinishedEvent<T> iterationFinishedEvent) {
return new ListenerSet<>(listeners, looper, clock, iterationFinishedEvent); return new ListenerSet<>(listeners, looper, clock, iterationFinishedEvent);
} }
@ -152,6 +167,11 @@ public final class ListenerSet<T extends @NonNull Object> {
} }
} }
/** Removes all listeners from the set. */
public void clear() {
listeners.clear();
}
/** Returns the number of added listeners. */ /** Returns the number of added listeners. */
public int size() { public int size() {
return listeners.size(); return listeners.size();

View File

@ -15,6 +15,8 @@
*/ */
package androidx.media3.common.util; package androidx.media3.common.util;
import static java.lang.annotation.ElementType.TYPE_USE;
import android.text.TextUtils; import android.text.TextUtils;
import androidx.annotation.IntDef; import androidx.annotation.IntDef;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@ -22,6 +24,7 @@ import androidx.annotation.Size;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import org.checkerframework.dataflow.qual.Pure; import org.checkerframework.dataflow.qual.Pure;
@ -35,8 +38,9 @@ public final class Log {
*/ */
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@IntDef({LOG_LEVEL_ALL, LOG_LEVEL_INFO, LOG_LEVEL_WARNING, LOG_LEVEL_ERROR, LOG_LEVEL_OFF}) @IntDef({LOG_LEVEL_ALL, LOG_LEVEL_INFO, LOG_LEVEL_WARNING, LOG_LEVEL_ERROR, LOG_LEVEL_OFF})
@interface LogLevel {} public @interface LogLevel {}
/** Log level to log all messages. */ /** Log level to log all messages. */
public static final int LOG_LEVEL_ALL = 0; public static final int LOG_LEVEL_ALL = 0;
/** Log level to only log informative, warning and error messages. */ /** Log level to only log informative, warning and error messages. */
@ -78,7 +82,9 @@ public final class Log {
Log.logStackTraces = logStackTraces; Log.logStackTraces = logStackTraces;
} }
/** @see android.util.Log#d(String, String) */ /**
* @see android.util.Log#d(String, String)
*/
@Pure @Pure
public static void d(@Size(max = 23) String tag, String message) { public static void d(@Size(max = 23) String tag, String message) {
if (logLevel == LOG_LEVEL_ALL) { if (logLevel == LOG_LEVEL_ALL) {
@ -86,13 +92,17 @@ public final class Log {
} }
} }
/** @see android.util.Log#d(String, String, Throwable) */ /**
* @see android.util.Log#d(String, String, Throwable)
*/
@Pure @Pure
public static void d(@Size(max = 23) String tag, String message, @Nullable Throwable throwable) { public static void d(@Size(max = 23) String tag, String message, @Nullable Throwable throwable) {
d(tag, appendThrowableString(message, throwable)); d(tag, appendThrowableString(message, throwable));
} }
/** @see android.util.Log#i(String, String) */ /**
* @see android.util.Log#i(String, String)
*/
@Pure @Pure
public static void i(@Size(max = 23) String tag, String message) { public static void i(@Size(max = 23) String tag, String message) {
if (logLevel <= LOG_LEVEL_INFO) { if (logLevel <= LOG_LEVEL_INFO) {
@ -100,13 +110,17 @@ public final class Log {
} }
} }
/** @see android.util.Log#i(String, String, Throwable) */ /**
* @see android.util.Log#i(String, String, Throwable)
*/
@Pure @Pure
public static void i(@Size(max = 23) String tag, String message, @Nullable Throwable throwable) { public static void i(@Size(max = 23) String tag, String message, @Nullable Throwable throwable) {
i(tag, appendThrowableString(message, throwable)); i(tag, appendThrowableString(message, throwable));
} }
/** @see android.util.Log#w(String, String) */ /**
* @see android.util.Log#w(String, String)
*/
@Pure @Pure
public static void w(@Size(max = 23) String tag, String message) { public static void w(@Size(max = 23) String tag, String message) {
if (logLevel <= LOG_LEVEL_WARNING) { if (logLevel <= LOG_LEVEL_WARNING) {
@ -114,13 +128,17 @@ public final class Log {
} }
} }
/** @see android.util.Log#w(String, String, Throwable) */ /**
* @see android.util.Log#w(String, String, Throwable)
*/
@Pure @Pure
public static void w(@Size(max = 23) String tag, String message, @Nullable Throwable throwable) { public static void w(@Size(max = 23) String tag, String message, @Nullable Throwable throwable) {
w(tag, appendThrowableString(message, throwable)); w(tag, appendThrowableString(message, throwable));
} }
/** @see android.util.Log#e(String, String) */ /**
* @see android.util.Log#e(String, String)
*/
@Pure @Pure
public static void e(@Size(max = 23) String tag, String message) { public static void e(@Size(max = 23) String tag, String message) {
if (logLevel <= LOG_LEVEL_ERROR) { if (logLevel <= LOG_LEVEL_ERROR) {
@ -128,7 +146,9 @@ public final class Log {
} }
} }
/** @see android.util.Log#e(String, String, Throwable) */ /**
* @see android.util.Log#e(String, String, Throwable)
*/
@Pure @Pure
public static void e(@Size(max = 23) String tag, String message, @Nullable Throwable throwable) { public static void e(@Size(max = 23) String tag, String message, @Nullable Throwable throwable) {
e(tag, appendThrowableString(message, throwable)); e(tag, appendThrowableString(message, throwable));

View File

@ -30,7 +30,9 @@ public final class LongArray {
this(DEFAULT_INITIAL_CAPACITY); this(DEFAULT_INITIAL_CAPACITY);
} }
/** @param initialCapacity The initial capacity of the array. */ /**
* @param initialCapacity The initial capacity of the array.
*/
public LongArray(int initialCapacity) { public LongArray(int initialCapacity) {
values = new long[initialCapacity]; values = new long[initialCapacity];
} }

View File

@ -221,6 +221,17 @@ public final class MediaFormatUtil {
case C.ENCODING_PCM_FLOAT: case C.ENCODING_PCM_FLOAT:
mediaFormatPcmEncoding = AudioFormat.ENCODING_PCM_FLOAT; mediaFormatPcmEncoding = AudioFormat.ENCODING_PCM_FLOAT;
break; break;
case C.ENCODING_PCM_24BIT:
mediaFormatPcmEncoding = AudioFormat.ENCODING_PCM_24BIT_PACKED;
break;
case C.ENCODING_PCM_32BIT:
mediaFormatPcmEncoding = AudioFormat.ENCODING_PCM_32BIT;
break;
case C.ENCODING_INVALID:
mediaFormatPcmEncoding = AudioFormat.ENCODING_INVALID;
break;
case Format.NO_VALUE:
case C.ENCODING_PCM_16BIT_BIG_ENDIAN:
default: default:
// No matching value. Do nothing. // No matching value. Do nothing.
return; return;

Some files were not shown because too many files have changed in this diff Show More