Merge branch 'main' into rtp-mpeg4
@ -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'
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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"
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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"
|
||||||
|
@ -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"/>
|
||||||
|
@ -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": [
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
|
@ -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"
|
||||||
|
@ -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')
|
||||||
}
|
}
|
||||||
|
@ -21,10 +21,9 @@
|
|||||||
<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
|
||||||
@ -38,13 +37,11 @@
|
|||||||
|
|
||||||
<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"
|
||||||
|
@ -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",
|
||||||
|
@ -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()
|
||||||
|
After Width: | Height: | Size: 114 B |
After Width: | Height: | Size: 251 B |
After Width: | Height: | Size: 109 B |
After Width: | Height: | Size: 191 B |
After Width: | Height: | Size: 113 B |
After Width: | Height: | Size: 298 B |
After Width: | Height: | Size: 121 B |
After Width: | Height: | Size: 383 B |
After Width: | Height: | Size: 126 B |
After Width: | Height: | Size: 455 B |
@ -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"
|
||||||
|
@ -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>
|
||||||
|
@ -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>
|
||||||
|
@ -27,7 +27,9 @@
|
|||||||
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"/>
|
||||||
|
9
demos/transformer/README.md
Normal 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
|
61
demos/transformer/build.gradle
Normal 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')
|
||||||
|
}
|
60
demos/transformer/src/main/AndroidManifest.xml
Normal 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>
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
206
demos/transformer/src/main/res/layout/configuration_activity.xml
Normal 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>
|
@ -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.
|
||||||
@ -14,10 +14,13 @@
|
|||||||
~ 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" />
|
100
demos/transformer/src/main/res/layout/transformer_activity.xml
Normal 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>
|
BIN
demos/transformer/src/main/res/mipmap-hdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
BIN
demos/transformer/src/main/res/mipmap-mdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
demos/transformer/src/main/res/mipmap-xhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
demos/transformer/src/main/res/mipmap-xxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 7.3 KiB |
BIN
demos/transformer/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 10 KiB |
39
demos/transformer/src/main/res/values/strings.xml
Normal 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>
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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 {}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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,
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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]);
|
||||||
}
|
}
|
||||||
|
@ -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:
|
||||||
|
@ -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);
|
||||||
|
@ -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 = {
|
||||||
|
@ -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 {}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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
|
||||||
|
@ -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,
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
})
|
})
|
||||||
|
@ -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})
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -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;
|
||||||
|
@ -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 true;
|
||||||
} else {
|
|
||||||
supported = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
return false;
|
||||||
return supported;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns if at least one track of the type {@code trackType} is selected for playback. */
|
/**
|
||||||
|
* 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 false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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,
|
||||||
})
|
})
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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;
|
||||||
|
@ -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 {}
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
|
@ -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();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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 {}
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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,
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
@ -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));
|
||||||
|
@ -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];
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|