Add MediaItem2
Bug: 121216661 Test: build Change-Id: I7dfd36bbc1feeeb0a80641940579b3d6187bd2f2
This commit is contained in:
324
media/java/android/media/MediaItem2.java
Normal file
324
media/java/android/media/MediaItem2.java
Normal file
@@ -0,0 +1,324 @@
|
||||
/*
|
||||
* Copyright 2018 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 android.media;
|
||||
|
||||
import static android.media.MediaMetadata.METADATA_KEY_MEDIA_ID;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* A class with information on a single media item with the metadata information. Here are use
|
||||
* cases.
|
||||
* <ul>
|
||||
* <li>Specify media items to {@link SessionPlayer2} for playback.
|
||||
* <li>Share media items across the processes.
|
||||
* </ul>
|
||||
* <p>
|
||||
* Subclasses of the session player may only accept certain subclasses of the media items. Check
|
||||
* the player documentation that you're interested in.
|
||||
* <p>
|
||||
* When it's shared across the processes, we cannot guarantee that they contain the right values
|
||||
* because media items are application dependent especially for the metadata.
|
||||
* <p>
|
||||
* This object is thread safe.
|
||||
* <p>
|
||||
* This API is not generally intended for third party application developers.
|
||||
* Use the <a href="{@docRoot}tools/extras/support-library.html">Support Library</a>
|
||||
* {@link androidx.media2.MediaItem} for consistent behavior across all devices.
|
||||
* </p>
|
||||
* @hide
|
||||
*/
|
||||
public class MediaItem2 implements Parcelable {
|
||||
private static final String TAG = "MediaItem2";
|
||||
|
||||
// intentionally less than long.MAX_VALUE.
|
||||
// Declare this first to avoid 'illegal forward reference'.
|
||||
static final long LONG_MAX = 0x7ffffffffffffffL;
|
||||
|
||||
/**
|
||||
* Used when a position is unknown.
|
||||
*
|
||||
* @see #getEndPosition()
|
||||
*/
|
||||
public static final long POSITION_UNKNOWN = LONG_MAX;
|
||||
|
||||
public static final Parcelable.Creator<MediaItem2> CREATOR =
|
||||
new Parcelable.Creator<MediaItem2>() {
|
||||
@Override
|
||||
public MediaItem2 createFromParcel(Parcel in) {
|
||||
return new MediaItem2(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaItem2[] newArray(int size) {
|
||||
return new MediaItem2[size];
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: Use SessionPlayer2.UNKNOWN_TIME instead
|
||||
private static final long UNKNOWN_TIME = -1;
|
||||
|
||||
private final long mStartPositionMs;
|
||||
private final long mEndPositionMs;
|
||||
|
||||
private final Object mLock = new Object();
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private MediaMetadata mMetadata;
|
||||
@GuardedBy("mLock")
|
||||
private final List<Pair<OnMetadataChangedListener, Executor>> mListeners = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Used by {@link MediaItem2.Builder}.
|
||||
*/
|
||||
// Note: Needs to be protected when we want to allow 3rd party player to define customized
|
||||
// MediaItem2.
|
||||
@SuppressWarnings("WeakerAccess") /* synthetic access */
|
||||
MediaItem2(Builder builder) {
|
||||
this(builder.mMetadata, builder.mStartPositionMs, builder.mEndPositionMs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by Parcelable.Creator.
|
||||
*/
|
||||
// Note: Needs to be protected when we want to allow 3rd party player to define customized
|
||||
// MediaItem2.
|
||||
@SuppressWarnings("WeakerAccess") /* synthetic access */
|
||||
MediaItem2(Parcel in) {
|
||||
this(in.readParcelable(MediaItem2.class.getClassLoader()), in.readLong(), in.readLong());
|
||||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess") /* synthetic access */
|
||||
MediaItem2(MediaItem2 item) {
|
||||
this(item.mMetadata, item.mStartPositionMs, item.mEndPositionMs);
|
||||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess") /* synthetic access */
|
||||
MediaItem2(@Nullable MediaMetadata metadata, long startPositionMs, long endPositionMs) {
|
||||
if (startPositionMs > endPositionMs) {
|
||||
throw new IllegalArgumentException("Illegal start/end position: "
|
||||
+ startPositionMs + " : " + endPositionMs);
|
||||
}
|
||||
if (metadata != null && metadata.containsKey(MediaMetadata.METADATA_KEY_DURATION)) {
|
||||
long durationMs = metadata.getLong(MediaMetadata.METADATA_KEY_DURATION);
|
||||
if (durationMs != UNKNOWN_TIME && endPositionMs != POSITION_UNKNOWN
|
||||
&& endPositionMs > durationMs) {
|
||||
throw new IllegalArgumentException("endPositionMs shouldn't be greater than"
|
||||
+ " duration in the metdata, endPositionMs=" + endPositionMs
|
||||
+ ", durationMs=" + durationMs);
|
||||
}
|
||||
}
|
||||
mMetadata = metadata;
|
||||
mStartPositionMs = startPositionMs;
|
||||
mEndPositionMs = endPositionMs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder sb = new StringBuilder(getClass().getSimpleName());
|
||||
synchronized (mLock) {
|
||||
sb.append("{mMetadata=").append(mMetadata);
|
||||
sb.append(", mStartPositionMs=").append(mStartPositionMs);
|
||||
sb.append(", mEndPositionMs=").append(mEndPositionMs);
|
||||
sb.append('}');
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets metadata. If the metadata is not {@code null}, its id should be matched with this
|
||||
* instance's media id.
|
||||
*
|
||||
* @param metadata metadata to update
|
||||
* @see MediaMetadata#METADATA_KEY_MEDIA_ID
|
||||
*/
|
||||
public void setMetadata(@Nullable MediaMetadata metadata) {
|
||||
List<Pair<OnMetadataChangedListener, Executor>> listeners = new ArrayList<>();
|
||||
synchronized (mLock) {
|
||||
if (mMetadata != null && metadata != null
|
||||
&& !TextUtils.equals(getMediaId(), metadata.getString(METADATA_KEY_MEDIA_ID))) {
|
||||
Log.d(TAG, "MediaItem2's media ID shouldn't be changed");
|
||||
return;
|
||||
}
|
||||
mMetadata = metadata;
|
||||
listeners.addAll(mListeners);
|
||||
}
|
||||
|
||||
for (Pair<OnMetadataChangedListener, Executor> pair : listeners) {
|
||||
final OnMetadataChangedListener listener = pair.first;
|
||||
pair.second.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listener.onMetadataChanged(MediaItem2.this);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the metadata of the media.
|
||||
*
|
||||
* @return metadata from the session
|
||||
*/
|
||||
public @Nullable MediaMetadata getMetadata() {
|
||||
synchronized (mLock) {
|
||||
return mMetadata;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the position in milliseconds at which the playback will start.
|
||||
* @return the position in milliseconds at which the playback will start
|
||||
*/
|
||||
public long getStartPosition() {
|
||||
return mStartPositionMs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the position in milliseconds at which the playback will end.
|
||||
* {@link #POSITION_UNKNOWN} means ending at the end of source content.
|
||||
* @return the position in milliseconds at which the playback will end
|
||||
*/
|
||||
public long getEndPosition() {
|
||||
return mEndPositionMs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeParcelable(mMetadata, 0);
|
||||
dest.writeLong(mStartPositionMs);
|
||||
dest.writeLong(mEndPositionMs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the media id for this item. If it's not {@code null}, it's a persistent unique key
|
||||
* for the underlying media content.
|
||||
*
|
||||
* @return media Id from the session
|
||||
*/
|
||||
@Nullable String getMediaId() {
|
||||
synchronized (mLock) {
|
||||
return mMetadata != null
|
||||
? mMetadata.getString(METADATA_KEY_MEDIA_ID) : null;
|
||||
}
|
||||
}
|
||||
|
||||
void addOnMetadataChangedListener(Executor executor, OnMetadataChangedListener listener) {
|
||||
synchronized (mLock) {
|
||||
for (Pair<OnMetadataChangedListener, Executor> pair : mListeners) {
|
||||
if (pair.first == listener) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
mListeners.add(new Pair<>(listener, executor));
|
||||
}
|
||||
}
|
||||
|
||||
void removeOnMetadataChangedListener(OnMetadataChangedListener listener) {
|
||||
synchronized (mLock) {
|
||||
for (int i = mListeners.size() - 1; i >= 0; i--) {
|
||||
if (mListeners.get(i).first == listener) {
|
||||
mListeners.remove(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for {@link MediaItem2}.
|
||||
*/
|
||||
public static class Builder {
|
||||
@SuppressWarnings("WeakerAccess") /* synthetic access */
|
||||
MediaMetadata mMetadata;
|
||||
@SuppressWarnings("WeakerAccess") /* synthetic access */
|
||||
long mStartPositionMs = 0;
|
||||
@SuppressWarnings("WeakerAccess") /* synthetic access */
|
||||
long mEndPositionMs = POSITION_UNKNOWN;
|
||||
|
||||
/**
|
||||
* Set the metadata of this instance. {@code null} for unset.
|
||||
*
|
||||
* @param metadata metadata
|
||||
* @return this instance for chaining
|
||||
*/
|
||||
public @NonNull Builder setMetadata(@Nullable MediaMetadata metadata) {
|
||||
mMetadata = metadata;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the start position in milliseconds at which the playback will start.
|
||||
* Any negative number is treated as 0.
|
||||
*
|
||||
* @param position the start position in milliseconds at which the playback will start
|
||||
* @return the same Builder instance.
|
||||
*/
|
||||
public @NonNull Builder setStartPosition(long position) {
|
||||
if (position < 0) {
|
||||
position = 0;
|
||||
}
|
||||
mStartPositionMs = position;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the end position in milliseconds at which the playback will end.
|
||||
* Any negative number is treated as maximum length of the media item.
|
||||
*
|
||||
* @param position the end position in milliseconds at which the playback will end
|
||||
* @return the same Builder instance.
|
||||
*/
|
||||
public @NonNull Builder setEndPosition(long position) {
|
||||
if (position < 0) {
|
||||
position = POSITION_UNKNOWN;
|
||||
}
|
||||
mEndPositionMs = position;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build {@link MediaItem2}.
|
||||
*
|
||||
* @return a new {@link MediaItem2}.
|
||||
*/
|
||||
public @NonNull MediaItem2 build() {
|
||||
return new MediaItem2(this);
|
||||
}
|
||||
}
|
||||
|
||||
interface OnMetadataChangedListener {
|
||||
void onMetadataChanged(MediaItem2 item);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user