Make Track selection objects Bundleable

Most of those objects needs to be sent to MediaControler.

`TrackSelectior.Parameters` could have stayed Parcelable,
but it needs to be `Bundleable` as it inherit from
`TrackSelectionParameters` that is and needs to be
serializable anyway for the demo app.
As a result it has also been migrated to bundleable.

PiperOrigin-RevId: 391353293
This commit is contained in:
krocard 2021-08-17 20:40:50 +01:00 committed by bachinger
parent b689fbd44e
commit cd297b048a
13 changed files with 899 additions and 501 deletions

View File

@ -98,7 +98,7 @@ public class PlayerActivity extends AppCompatActivity
// Activity lifecycle.
@Override
public void onCreate(Bundle savedInstanceState) {
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
dataSourceFactory = DemoUtil.getDataSourceFactory(/* context= */ this);
@ -114,7 +114,9 @@ public class PlayerActivity extends AppCompatActivity
playerView.requestFocus();
if (savedInstanceState != null) {
trackSelectorParameters = savedInstanceState.getParcelable(KEY_TRACK_SELECTOR_PARAMETERS);
trackSelectorParameters =
DefaultTrackSelector.Parameters.CREATOR.fromBundle(
savedInstanceState.getBundle(KEY_TRACK_SELECTOR_PARAMETERS));
startAutoPlay = savedInstanceState.getBoolean(KEY_AUTO_PLAY);
startWindow = savedInstanceState.getInt(KEY_WINDOW);
startPosition = savedInstanceState.getLong(KEY_POSITION);
@ -207,7 +209,7 @@ public class PlayerActivity extends AppCompatActivity
super.onSaveInstanceState(outState);
updateTrackSelectorParameters();
updateStartPosition();
outState.putParcelable(KEY_TRACK_SELECTOR_PARAMETERS, trackSelectorParameters);
outState.putBundle(KEY_TRACK_SELECTOR_PARAMETERS, trackSelectorParameters.toBundle());
outState.putBoolean(KEY_AUTO_PLAY, startAutoPlay);
outState.putInt(KEY_WINDOW, startWindow);
outState.putLong(KEY_POSITION, startPosition);

View File

@ -15,19 +15,21 @@
*/
package com.google.android.exoplayer2;
import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.Bundle;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.drm.DrmInitData;
import com.google.android.exoplayer2.drm.ExoMediaCrypto;
import com.google.android.exoplayer2.drm.UnsupportedMediaCrypto;
import com.google.android.exoplayer2.metadata.Metadata;
import com.google.android.exoplayer2.util.BundleableUtils;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.Util;
import com.google.android.exoplayer2.video.ColorInfo;
import com.google.common.base.Joiner;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@ -112,7 +114,7 @@ import java.util.UUID;
* <li>{@link #accessibilityChannel}
* </ul>
*/
public final class Format implements Parcelable {
public final class Format implements Bundleable {
/**
* Builds {@link Format} instances.
@ -610,6 +612,8 @@ public final class Format implements Parcelable {
*/
public static final long OFFSET_SAMPLE_RELATIVE = Long.MAX_VALUE;
private static final Format DEFAULT = new Builder().build();
/** An identifier for the format, or null if unknown or not applicable. */
@Nullable public final String id;
/** The human readable label, or null if unknown or not applicable. */
@ -968,54 +972,6 @@ public final class Format implements Parcelable {
}
}
// Some fields are deprecated but they're still assigned below.
@SuppressWarnings({"ResourceType"})
/* package */ Format(Parcel in) {
id = in.readString();
label = in.readString();
language = in.readString();
selectionFlags = in.readInt();
roleFlags = in.readInt();
averageBitrate = in.readInt();
peakBitrate = in.readInt();
bitrate = peakBitrate != NO_VALUE ? peakBitrate : averageBitrate;
codecs = in.readString();
metadata = in.readParcelable(Metadata.class.getClassLoader());
// Container specific.
containerMimeType = in.readString();
// Sample specific.
sampleMimeType = in.readString();
maxInputSize = in.readInt();
int initializationDataSize = in.readInt();
initializationData = new ArrayList<>(initializationDataSize);
for (int i = 0; i < initializationDataSize; i++) {
initializationData.add(checkNotNull(in.createByteArray()));
}
drmInitData = in.readParcelable(DrmInitData.class.getClassLoader());
subsampleOffsetUs = in.readLong();
// Video specific.
width = in.readInt();
height = in.readInt();
frameRate = in.readFloat();
rotationDegrees = in.readInt();
pixelWidthHeightRatio = in.readFloat();
boolean hasProjectionData = Util.readBoolean(in);
projectionData = hasProjectionData ? in.createByteArray() : null;
stereoMode = in.readInt();
colorInfo = in.readParcelable(ColorInfo.class.getClassLoader());
// Audio specific.
channelCount = in.readInt();
sampleRate = in.readInt();
pcmEncoding = in.readInt();
encoderDelay = in.readInt();
encoderPadding = in.readInt();
// Text specific.
accessibilityChannel = in.readInt();
// Provided by source.
// Encrypted content must always have a non-null exoMediaCryptoType.
exoMediaCryptoType = drmInitData != null ? UnsupportedMediaCrypto.class : null;
}
/** Returns a {@link Format.Builder} initialized with the values of this instance. */
public Builder buildUpon() {
return new Builder(this);
@ -1371,69 +1327,208 @@ public final class Format implements Parcelable {
return builder.toString();
}
// Parcelable implementation.
// Bundleable implementation.
@Documented
@Retention(RetentionPolicy.SOURCE)
@IntDef({
FIELD_ID,
FIELD_LABEL,
FIELD_LANGUAGE,
FIELD_SELECTION_FLAGS,
FIELD_ROLE_FLAGS,
FIELD_AVERAGE_BITRATE,
FIELD_PEAK_BITRATE,
FIELD_CODECS,
FIELD_METADATA,
FIELD_CONTAINER_MIME_TYPE,
FIELD_SAMPLE_MIME_TYPE,
FIELD_MAX_INPUT_SIZE,
FIELD_INITIALIZATION_DATA,
FIELD_DRM_INIT_DATA,
FIELD_SUBSAMPLE_OFFSET_US,
FIELD_WIDTH,
FIELD_HEIGHT,
FIELD_FRAME_RATE,
FIELD_ROTATION_DEGREES,
FIELD_PIXEL_WIDTH_HEIGHT_RATIO,
FIELD_PROJECTION_DATA,
FIELD_STEREO_MODE,
FIELD_COLOR_INFO,
FIELD_CHANNEL_COUNT,
FIELD_SAMPLE_RATE,
FIELD_PCM_ENCODING,
FIELD_ENCODER_DELAY,
FIELD_ENCODER_PADDING,
FIELD_ACCESSIBILITY_CHANNEL,
})
private @interface FieldNumber {}
@Override
public int describeContents() {
return 0;
}
private static final int FIELD_ID = 0;
private static final int FIELD_LABEL = 1;
private static final int FIELD_LANGUAGE = 2;
private static final int FIELD_SELECTION_FLAGS = 3;
private static final int FIELD_ROLE_FLAGS = 4;
private static final int FIELD_AVERAGE_BITRATE = 5;
private static final int FIELD_PEAK_BITRATE = 6;
private static final int FIELD_CODECS = 7;
private static final int FIELD_METADATA = 8;
private static final int FIELD_CONTAINER_MIME_TYPE = 9;
private static final int FIELD_SAMPLE_MIME_TYPE = 10;
private static final int FIELD_MAX_INPUT_SIZE = 11;
private static final int FIELD_INITIALIZATION_DATA = 12;
private static final int FIELD_DRM_INIT_DATA = 13;
private static final int FIELD_SUBSAMPLE_OFFSET_US = 14;
private static final int FIELD_WIDTH = 15;
private static final int FIELD_HEIGHT = 16;
private static final int FIELD_FRAME_RATE = 17;
private static final int FIELD_ROTATION_DEGREES = 18;
private static final int FIELD_PIXEL_WIDTH_HEIGHT_RATIO = 19;
private static final int FIELD_PROJECTION_DATA = 20;
private static final int FIELD_STEREO_MODE = 21;
private static final int FIELD_COLOR_INFO = 22;
private static final int FIELD_CHANNEL_COUNT = 23;
private static final int FIELD_SAMPLE_RATE = 24;
private static final int FIELD_PCM_ENCODING = 25;
private static final int FIELD_ENCODER_DELAY = 26;
private static final int FIELD_ENCODER_PADDING = 27;
private static final int FIELD_ACCESSIBILITY_CHANNEL = 28;
/**
* {@inheritDoc}
*
* <p>Omits the {@link #exoMediaCryptoType} field. The {@link #exoMediaCryptoType} of an instance
* restored by {@link #CREATOR} will always be {@link UnsupportedMediaCrypto}.
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(id);
dest.writeString(label);
dest.writeString(language);
dest.writeInt(selectionFlags);
dest.writeInt(roleFlags);
dest.writeInt(averageBitrate);
dest.writeInt(peakBitrate);
dest.writeString(codecs);
dest.writeParcelable(metadata, 0);
public Bundle toBundle() {
Bundle bundle = new Bundle();
bundle.putString(keyForField(FIELD_ID), id);
bundle.putString(keyForField(FIELD_LABEL), label);
bundle.putString(keyForField(FIELD_LANGUAGE), language);
bundle.putInt(keyForField(FIELD_SELECTION_FLAGS), selectionFlags);
bundle.putInt(keyForField(FIELD_ROLE_FLAGS), roleFlags);
bundle.putInt(keyForField(FIELD_AVERAGE_BITRATE), averageBitrate);
bundle.putInt(keyForField(FIELD_PEAK_BITRATE), peakBitrate);
bundle.putString(keyForField(FIELD_CODECS), codecs);
// Metadata is currently not Bundleable because Metadata.Entry is an Interface,
// which would be difficult to unbundle in a backward compatible way.
// The entries are additionally of limited usefulness to remote processes.
bundle.putParcelable(keyForField(FIELD_METADATA), metadata);
// Container specific.
dest.writeString(containerMimeType);
bundle.putString(keyForField(FIELD_CONTAINER_MIME_TYPE), containerMimeType);
// Sample specific.
dest.writeString(sampleMimeType);
dest.writeInt(maxInputSize);
int initializationDataSize = initializationData.size();
dest.writeInt(initializationDataSize);
for (int i = 0; i < initializationDataSize; i++) {
dest.writeByteArray(initializationData.get(i));
bundle.putString(keyForField(FIELD_SAMPLE_MIME_TYPE), sampleMimeType);
bundle.putInt(keyForField(FIELD_MAX_INPUT_SIZE), maxInputSize);
for (int i = 0; i < initializationData.size(); i++) {
bundle.putByteArray(keyForInitializationData(i), initializationData.get(i));
}
dest.writeParcelable(drmInitData, 0);
dest.writeLong(subsampleOffsetUs);
// DrmInitData doesn't need to be Bundleable as it's only used in the playing process to
// initialize the decoder.
bundle.putParcelable(keyForField(FIELD_DRM_INIT_DATA), drmInitData);
bundle.putLong(keyForField(FIELD_SUBSAMPLE_OFFSET_US), subsampleOffsetUs);
// Video specific.
dest.writeInt(width);
dest.writeInt(height);
dest.writeFloat(frameRate);
dest.writeInt(rotationDegrees);
dest.writeFloat(pixelWidthHeightRatio);
Util.writeBoolean(dest, projectionData != null);
if (projectionData != null) {
dest.writeByteArray(projectionData);
}
dest.writeInt(stereoMode);
dest.writeParcelable(colorInfo, flags);
bundle.putInt(keyForField(FIELD_WIDTH), width);
bundle.putInt(keyForField(FIELD_HEIGHT), height);
bundle.putFloat(keyForField(FIELD_FRAME_RATE), frameRate);
bundle.putInt(keyForField(FIELD_ROTATION_DEGREES), rotationDegrees);
bundle.putFloat(keyForField(FIELD_PIXEL_WIDTH_HEIGHT_RATIO), pixelWidthHeightRatio);
bundle.putByteArray(keyForField(FIELD_PROJECTION_DATA), projectionData);
bundle.putInt(keyForField(FIELD_STEREO_MODE), stereoMode);
bundle.putBundle(keyForField(FIELD_COLOR_INFO), BundleableUtils.toNullableBundle(colorInfo));
// Audio specific.
dest.writeInt(channelCount);
dest.writeInt(sampleRate);
dest.writeInt(pcmEncoding);
dest.writeInt(encoderDelay);
dest.writeInt(encoderPadding);
bundle.putInt(keyForField(FIELD_CHANNEL_COUNT), channelCount);
bundle.putInt(keyForField(FIELD_SAMPLE_RATE), sampleRate);
bundle.putInt(keyForField(FIELD_PCM_ENCODING), pcmEncoding);
bundle.putInt(keyForField(FIELD_ENCODER_DELAY), encoderDelay);
bundle.putInt(keyForField(FIELD_ENCODER_PADDING), encoderPadding);
// Text specific.
dest.writeInt(accessibilityChannel);
bundle.putInt(keyForField(FIELD_ACCESSIBILITY_CHANNEL), accessibilityChannel);
return bundle;
}
public static final Creator<Format> CREATOR =
new Creator<Format>() {
/** Object that can restore {@code Format} from a {@link Bundle}. */
public static final Creator<Format> CREATOR = Format::fromBundle;
@Override
public Format createFromParcel(Parcel in) {
return new Format(in);
private static Format fromBundle(Bundle bundle) {
Builder builder = new Builder();
BundleableUtils.ensureClassLoader(bundle);
builder
.setId(defaultIfNull(bundle.getString(keyForField(FIELD_ID)), DEFAULT.id))
.setLabel(defaultIfNull(bundle.getString(keyForField(FIELD_LABEL)), DEFAULT.label))
.setLanguage(defaultIfNull(bundle.getString(keyForField(FIELD_LANGUAGE)), DEFAULT.language))
.setSelectionFlags(
bundle.getInt(keyForField(FIELD_SELECTION_FLAGS), DEFAULT.selectionFlags))
.setRoleFlags(bundle.getInt(keyForField(FIELD_ROLE_FLAGS), DEFAULT.roleFlags))
.setAverageBitrate(
bundle.getInt(keyForField(FIELD_AVERAGE_BITRATE), DEFAULT.averageBitrate))
.setPeakBitrate(bundle.getInt(keyForField(FIELD_PEAK_BITRATE), DEFAULT.peakBitrate))
.setCodecs(defaultIfNull(bundle.getString(keyForField(FIELD_CODECS)), DEFAULT.codecs))
.setMetadata(
defaultIfNull(bundle.getParcelable(keyForField(FIELD_METADATA)), DEFAULT.metadata))
// Container specific.
.setContainerMimeType(
defaultIfNull(
bundle.getString(keyForField(FIELD_CONTAINER_MIME_TYPE)),
DEFAULT.containerMimeType))
// Sample specific.
.setSampleMimeType(
defaultIfNull(
bundle.getString(keyForField(FIELD_SAMPLE_MIME_TYPE)), DEFAULT.sampleMimeType))
.setMaxInputSize(bundle.getInt(keyForField(FIELD_MAX_INPUT_SIZE), DEFAULT.maxInputSize));
List<byte[]> initializationData = new ArrayList<>();
for (int i = 0; ; i++) {
@Nullable byte[] data = bundle.getByteArray(keyForInitializationData(i));
if (data == null) {
break;
}
initializationData.add(data);
}
builder
.setInitializationData(initializationData)
.setDrmInitData(bundle.getParcelable(keyForField(FIELD_DRM_INIT_DATA)))
.setSubsampleOffsetUs(
bundle.getLong(keyForField(FIELD_SUBSAMPLE_OFFSET_US), DEFAULT.subsampleOffsetUs))
// Video specific.
.setWidth(bundle.getInt(keyForField(FIELD_WIDTH), DEFAULT.width))
.setHeight(bundle.getInt(keyForField(FIELD_HEIGHT), DEFAULT.height))
.setFrameRate(bundle.getFloat(keyForField(FIELD_FRAME_RATE), DEFAULT.frameRate))
.setRotationDegrees(
bundle.getInt(keyForField(FIELD_ROTATION_DEGREES), DEFAULT.rotationDegrees))
.setPixelWidthHeightRatio(
bundle.getFloat(
keyForField(FIELD_PIXEL_WIDTH_HEIGHT_RATIO), DEFAULT.pixelWidthHeightRatio))
.setProjectionData(bundle.getByteArray(keyForField(FIELD_PROJECTION_DATA)))
.setStereoMode(bundle.getInt(keyForField(FIELD_STEREO_MODE), DEFAULT.stereoMode))
.setColorInfo(
BundleableUtils.fromNullableBundle(
ColorInfo.CREATOR, bundle.getBundle(keyForField(FIELD_COLOR_INFO))))
// Audio specific.
.setChannelCount(bundle.getInt(keyForField(FIELD_CHANNEL_COUNT), DEFAULT.channelCount))
.setSampleRate(bundle.getInt(keyForField(FIELD_SAMPLE_RATE), DEFAULT.sampleRate))
.setPcmEncoding(bundle.getInt(keyForField(FIELD_PCM_ENCODING), DEFAULT.pcmEncoding))
.setEncoderDelay(bundle.getInt(keyForField(FIELD_ENCODER_DELAY), DEFAULT.encoderDelay))
.setEncoderPadding(
bundle.getInt(keyForField(FIELD_ENCODER_PADDING), DEFAULT.encoderPadding))
// Text specific.
.setAccessibilityChannel(
bundle.getInt(keyForField(FIELD_ACCESSIBILITY_CHANNEL), DEFAULT.accessibilityChannel));
return builder.build();
}
@Override
public Format[] newArray(int size) {
return new Format[size];
private static String keyForField(@FieldNumber int field) {
return Integer.toString(field, Character.MAX_RADIX);
}
private static String keyForInitializationData(int initialisationDataIndex) {
return keyForField(FIELD_INITIALIZATION_DATA)
+ "_"
+ Integer.toString(initialisationDataIndex, Character.MAX_RADIX);
}
@Nullable
private static <T> T defaultIfNull(@Nullable T value, @Nullable T defaultValue) {
return value != null ? value : defaultValue;
}
};
}

View File

@ -15,17 +15,26 @@
*/
package com.google.android.exoplayer2.source;
import android.os.Parcel;
import android.os.Parcelable;
import static com.google.android.exoplayer2.util.Assertions.checkArgument;
import android.os.Bundle;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.Bundleable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.BundleableUtils;
import com.google.android.exoplayer2.util.Log;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.List;
/** Defines an immutable group of tracks identified by their format identity. */
public final class TrackGroup implements Parcelable {
public final class TrackGroup implements Bundleable {
private static final String TAG = "TrackGroup";
@ -37,22 +46,18 @@ public final class TrackGroup implements Parcelable {
// Lazily initialized hashcode.
private int hashCode;
/** @param formats The track formats. At least one {@link Format} must be provided. */
/**
* Constructs an instance {@code TrackGroup} containing the provided {@code formats}.
*
* @param formats Non empty array of format.
*/
public TrackGroup(Format... formats) {
Assertions.checkState(formats.length > 0);
checkArgument(formats.length > 0);
this.formats = formats;
this.length = formats.length;
verifyCorrectness();
}
/* package */ TrackGroup(Parcel in) {
length = in.readInt();
formats = new Format[length];
for (int i = 0; i < length; i++) {
formats[i] = in.readParcelable(Format.class.getClassLoader());
}
}
/**
* Returns the format of the track at a given index.
*
@ -103,35 +108,40 @@ public final class TrackGroup implements Parcelable {
return length == other.length && Arrays.equals(formats, other.formats);
}
// Parcelable implementation.
// Bundleable implementation.
@Documented
@Retention(RetentionPolicy.SOURCE)
@IntDef({
FIELD_FORMATS,
})
private @interface FieldNumber {}
private static final int FIELD_FORMATS = 0;
@Override
public int describeContents() {
return 0;
public Bundle toBundle() {
Bundle bundle = new Bundle();
bundle.putParcelableArrayList(
keyForField(FIELD_FORMATS), BundleableUtils.toBundleArrayList(Lists.newArrayList(formats)));
return bundle;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(length);
for (int i = 0; i < length; i++) {
dest.writeParcelable(formats[i], 0);
}
}
public static final Parcelable.Creator<TrackGroup> CREATOR =
new Parcelable.Creator<TrackGroup>() {
@Override
public TrackGroup createFromParcel(Parcel in) {
return new TrackGroup(in);
}
@Override
public TrackGroup[] newArray(int size) {
return new TrackGroup[size];
}
/** Object that can restore {@code TrackGroup} from a {@link Bundle}. */
public static final Creator<TrackGroup> CREATOR =
bundle -> {
List<Format> formats =
BundleableUtils.fromBundleNullableList(
Format.CREATOR,
bundle.getParcelableArrayList(keyForField(FIELD_FORMATS)),
ImmutableList.of());
return new TrackGroup(formats.toArray(new Format[0]));
};
private static String keyForField(@FieldNumber int field) {
return Integer.toString(field, Character.MAX_RADIX);
}
private void verifyCorrectness() {
// TrackGroups should only contain tracks with exactly the same content (but in different
// qualities). We only log an error instead of throwing to not break backwards-compatibility for

View File

@ -15,14 +15,22 @@
*/
package com.google.android.exoplayer2.source;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.Bundle;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.Bundleable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.util.BundleableUtils;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.List;
/** An immutable array of {@link TrackGroup}s. */
public final class TrackGroupArray implements Parcelable {
public final class TrackGroupArray implements Bundleable {
/** The empty array. */
public static final TrackGroupArray EMPTY = new TrackGroupArray();
@ -35,20 +43,12 @@ public final class TrackGroupArray implements Parcelable {
// Lazily initialized hashcode.
private int hashCode;
/** @param trackGroups The groups. May be empty. */
/** Construct a {@code TrackGroupArray} from an array of (possibly empty) trackGroups. */
public TrackGroupArray(TrackGroup... trackGroups) {
this.trackGroups = trackGroups;
this.length = trackGroups.length;
}
/* package */ TrackGroupArray(Parcel in) {
length = in.readInt();
trackGroups = new TrackGroup[length];
for (int i = 0; i < length; i++) {
trackGroups[i] = in.readParcelable(TrackGroup.class.getClassLoader());
}
}
/**
* Returns the group at a given index.
*
@ -102,32 +102,38 @@ public final class TrackGroupArray implements Parcelable {
return length == other.length && Arrays.equals(trackGroups, other.trackGroups);
}
// Parcelable implementation.
// Bundleable implementation.
@Documented
@Retention(RetentionPolicy.SOURCE)
@IntDef({
FIELD_TRACK_GROUPS,
})
private @interface FieldNumber {}
private static final int FIELD_TRACK_GROUPS = 0;
@Override
public int describeContents() {
return 0;
public Bundle toBundle() {
Bundle bundle = new Bundle();
bundle.putParcelableArrayList(
keyForField(FIELD_TRACK_GROUPS),
BundleableUtils.toBundleArrayList(Lists.newArrayList(trackGroups)));
return bundle;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(length);
for (int i = 0; i < length; i++) {
dest.writeParcelable(trackGroups[i], 0);
}
}
public static final Parcelable.Creator<TrackGroupArray> CREATOR =
new Parcelable.Creator<TrackGroupArray>() {
@Override
public TrackGroupArray createFromParcel(Parcel in) {
return new TrackGroupArray(in);
}
@Override
public TrackGroupArray[] newArray(int size) {
return new TrackGroupArray[size];
}
/** Object that can restores a TrackGroupArray from a {@link Bundle}. */
public static final Creator<TrackGroupArray> CREATOR =
bundle -> {
List<TrackGroup> trackGroups =
BundleableUtils.fromBundleNullableList(
TrackGroup.CREATOR,
bundle.getParcelableArrayList(keyForField(FIELD_TRACK_GROUPS)),
/* defaultValue= */ ImmutableList.of());
return new TrackGroupArray(trackGroups.toArray(new TrackGroup[0]));
};
private static String keyForField(@FieldNumber int field) {
return Integer.toString(field, Character.MAX_RADIX);
}
}

View File

@ -16,23 +16,28 @@
package com.google.android.exoplayer2.trackselection;
import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
import static com.google.common.base.MoreObjects.firstNonNull;
import android.content.Context;
import android.graphics.Point;
import android.os.Bundle;
import android.os.Looper;
import android.os.Parcel;
import android.os.Parcelable;
import android.view.accessibility.CaptioningManager;
import androidx.annotation.CallSuper;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import com.google.android.exoplayer2.Bundleable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.util.Util;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Locale;
/** Constraint parameters for track selection. */
public class TrackSelectionParameters implements Parcelable {
public class TrackSelectionParameters implements Bundleable {
/**
* A builder for {@link TrackSelectionParameters}. See the {@link TrackSelectionParameters}
@ -108,10 +113,7 @@ public class TrackSelectionParameters implements Parcelable {
setViewportSizeToPhysicalDisplaySize(context, /* viewportOrientationMayChange= */ true);
}
/**
* @param initialValues The {@link TrackSelectionParameters} from which the initial values of
* the builder are obtained.
*/
/** Creates a builder with the initial values specified in {@code initialValues}. */
protected Builder(TrackSelectionParameters initialValues) {
// Video
maxVideoWidth = initialValues.maxVideoWidth;
@ -141,6 +143,89 @@ public class TrackSelectionParameters implements Parcelable {
forceHighestSupportedBitrate = initialValues.forceHighestSupportedBitrate;
}
/** Creates a builder with the initial values specified in {@code bundle}. */
protected Builder(Bundle bundle) {
// Video
maxVideoWidth =
bundle.getInt(keyForField(FIELD_MAX_VIDEO_WIDTH), DEFAULT_WITHOUT_CONTEXT.maxVideoWidth);
maxVideoHeight =
bundle.getInt(
keyForField(FIELD_MAX_VIDEO_HEIGHT), DEFAULT_WITHOUT_CONTEXT.maxVideoHeight);
maxVideoFrameRate =
bundle.getInt(
keyForField(FIELD_MAX_VIDEO_FRAMERATE), DEFAULT_WITHOUT_CONTEXT.maxVideoFrameRate);
maxVideoBitrate =
bundle.getInt(
keyForField(FIELD_MAX_VIDEO_BITRATE), DEFAULT_WITHOUT_CONTEXT.maxVideoBitrate);
minVideoWidth =
bundle.getInt(keyForField(FIELD_MIN_VIDEO_WIDTH), DEFAULT_WITHOUT_CONTEXT.minVideoWidth);
minVideoHeight =
bundle.getInt(
keyForField(FIELD_MIN_VIDEO_HEIGHT), DEFAULT_WITHOUT_CONTEXT.minVideoHeight);
minVideoFrameRate =
bundle.getInt(
keyForField(FIELD_MIN_VIDEO_FRAMERATE), DEFAULT_WITHOUT_CONTEXT.minVideoFrameRate);
minVideoBitrate =
bundle.getInt(
keyForField(FIELD_MIN_VIDEO_BITRATE), DEFAULT_WITHOUT_CONTEXT.minVideoBitrate);
viewportWidth =
bundle.getInt(keyForField(FIELD_VIEWPORT_WIDTH), DEFAULT_WITHOUT_CONTEXT.viewportWidth);
viewportHeight =
bundle.getInt(keyForField(FIELD_VIEWPORT_HEIGHT), DEFAULT_WITHOUT_CONTEXT.viewportHeight);
viewportOrientationMayChange =
bundle.getBoolean(
keyForField(FIELD_VIEWPORT_ORIENTATION_MAY_CHANGE),
DEFAULT_WITHOUT_CONTEXT.viewportOrientationMayChange);
preferredVideoMimeTypes =
ImmutableList.copyOf(
firstNonNull(
bundle.getStringArray(keyForField(FIELD_PREFERRED_VIDEO_MIMETYPES)),
new String[0]));
// Audio
String[] preferredAudioLanguages1 =
firstNonNull(
bundle.getStringArray(keyForField(FIELD_PREFERRED_AUDIO_LANGUAGES)), new String[0]);
preferredAudioLanguages = normalizeLanguageCodes(preferredAudioLanguages1);
preferredAudioRoleFlags =
bundle.getInt(
keyForField(FIELD_PREFERRED_AUDIO_ROLE_FLAGS),
DEFAULT_WITHOUT_CONTEXT.preferredAudioRoleFlags);
maxAudioChannelCount =
bundle.getInt(
keyForField(FIELD_MAX_AUDIO_CHANNEL_COUNT),
DEFAULT_WITHOUT_CONTEXT.maxAudioChannelCount);
maxAudioBitrate =
bundle.getInt(
keyForField(FIELD_MAX_AUDIO_BITRATE), DEFAULT_WITHOUT_CONTEXT.maxAudioBitrate);
preferredAudioMimeTypes =
ImmutableList.copyOf(
firstNonNull(
bundle.getStringArray(keyForField(FIELD_PREFERRED_AUDIO_MIME_TYPES)),
new String[0]));
// Text
preferredTextLanguages =
normalizeLanguageCodes(
firstNonNull(
bundle.getStringArray(keyForField(FIELD_PREFERRED_TEXT_LANGUAGES)),
new String[0]));
preferredTextRoleFlags =
bundle.getInt(
keyForField(FIELD_PREFERRED_TEXT_ROLE_FLAGS),
DEFAULT_WITHOUT_CONTEXT.preferredTextRoleFlags);
selectUndeterminedTextLanguage =
bundle.getBoolean(
keyForField(FIELD_SELECT_UNDETERMINED_TEXT_LANGUAGE),
DEFAULT_WITHOUT_CONTEXT.selectUndeterminedTextLanguage);
// General
forceLowestBitrate =
bundle.getBoolean(
keyForField(FIELD_FORCE_LOWEST_BITRATE), DEFAULT_WITHOUT_CONTEXT.forceLowestBitrate);
forceHighestSupportedBitrate =
bundle.getBoolean(
keyForField(FIELD_FORCE_HIGHEST_SUPPORTED_BITRATE),
DEFAULT_WITHOUT_CONTEXT.forceHighestSupportedBitrate);
}
// Video
/**
@ -322,11 +407,7 @@ public class TrackSelectionParameters implements Parcelable {
* @return This builder.
*/
public Builder setPreferredAudioLanguages(String... preferredAudioLanguages) {
ImmutableList.Builder<String> listBuilder = ImmutableList.builder();
for (String language : checkNotNull(preferredAudioLanguages)) {
listBuilder.add(Util.normalizeLanguageCode(checkNotNull(language)));
}
this.preferredAudioLanguages = listBuilder.build();
this.preferredAudioLanguages = normalizeLanguageCodes(preferredAudioLanguages);
return this;
}
@ -427,11 +508,7 @@ public class TrackSelectionParameters implements Parcelable {
* @return This builder.
*/
public Builder setPreferredTextLanguages(String... preferredTextLanguages) {
ImmutableList.Builder<String> listBuilder = ImmutableList.builder();
for (String language : checkNotNull(preferredTextLanguages)) {
listBuilder.add(Util.normalizeLanguageCode(checkNotNull(language)));
}
this.preferredTextLanguages = listBuilder.build();
this.preferredTextLanguages = normalizeLanguageCodes(preferredTextLanguages);
return this;
}
@ -512,6 +589,14 @@ public class TrackSelectionParameters implements Parcelable {
preferredTextLanguages = ImmutableList.of(Util.getLocaleLanguageTag(preferredLocale));
}
}
private static ImmutableList<String> normalizeLanguageCodes(String[] preferredTextLanguages) {
ImmutableList.Builder<String> listBuilder = ImmutableList.builder();
for (String language : checkNotNull(preferredTextLanguages)) {
listBuilder.add(Util.normalizeLanguageCode(checkNotNull(language)));
}
return listBuilder.build();
}
}
/**
@ -537,20 +622,6 @@ public class TrackSelectionParameters implements Parcelable {
*/
@Deprecated public static final TrackSelectionParameters DEFAULT = DEFAULT_WITHOUT_CONTEXT;
public static final Creator<TrackSelectionParameters> CREATOR =
new Creator<TrackSelectionParameters>() {
@Override
public TrackSelectionParameters createFromParcel(Parcel in) {
return new TrackSelectionParameters(in);
}
@Override
public TrackSelectionParameters[] newArray(int size) {
return new TrackSelectionParameters[size];
}
};
/** Returns an instance configured with default values. */
public static TrackSelectionParameters getDefaults(Context context) {
return new Builder(context).build();
@ -707,42 +778,6 @@ public class TrackSelectionParameters implements Parcelable {
this.forceHighestSupportedBitrate = builder.forceHighestSupportedBitrate;
}
/* package */ TrackSelectionParameters(Parcel in) {
ArrayList<String> preferredAudioLanguages = new ArrayList<>();
in.readList(preferredAudioLanguages, /* loader= */ null);
this.preferredAudioLanguages = ImmutableList.copyOf(preferredAudioLanguages);
this.preferredAudioRoleFlags = in.readInt();
ArrayList<String> preferredTextLanguages1 = new ArrayList<>();
in.readList(preferredTextLanguages1, /* loader= */ null);
this.preferredTextLanguages = ImmutableList.copyOf(preferredTextLanguages1);
this.preferredTextRoleFlags = in.readInt();
this.selectUndeterminedTextLanguage = Util.readBoolean(in);
// Video
this.maxVideoWidth = in.readInt();
this.maxVideoHeight = in.readInt();
this.maxVideoFrameRate = in.readInt();
this.maxVideoBitrate = in.readInt();
this.minVideoWidth = in.readInt();
this.minVideoHeight = in.readInt();
this.minVideoFrameRate = in.readInt();
this.minVideoBitrate = in.readInt();
this.viewportWidth = in.readInt();
this.viewportHeight = in.readInt();
this.viewportOrientationMayChange = Util.readBoolean(in);
ArrayList<String> preferredVideoMimeTypes = new ArrayList<>();
in.readList(preferredVideoMimeTypes, /* loader= */ null);
this.preferredVideoMimeTypes = ImmutableList.copyOf(preferredVideoMimeTypes);
// Audio
this.maxAudioChannelCount = in.readInt();
this.maxAudioBitrate = in.readInt();
ArrayList<String> preferredAudioMimeTypes = new ArrayList<>();
in.readList(preferredAudioMimeTypes, /* loader= */ null);
this.preferredAudioMimeTypes = ImmutableList.copyOf(preferredAudioMimeTypes);
// General
this.forceLowestBitrate = Util.readBoolean(in);
this.forceHighestSupportedBitrate = Util.readBoolean(in);
}
/** Creates a new {@link Builder}, copying the initial values from this instance. */
public Builder buildUpon() {
return new Builder(this);
@ -817,39 +852,109 @@ public class TrackSelectionParameters implements Parcelable {
return result;
}
// Parcelable implementation.
// Bundleable implementation
@Documented
@Retention(RetentionPolicy.SOURCE)
@IntDef({
FIELD_PREFERRED_AUDIO_LANGUAGES,
FIELD_PREFERRED_AUDIO_ROLE_FLAGS,
FIELD_PREFERRED_TEXT_LANGUAGES,
FIELD_PREFERRED_TEXT_ROLE_FLAGS,
FIELD_SELECT_UNDETERMINED_TEXT_LANGUAGE,
FIELD_MAX_VIDEO_WIDTH,
FIELD_MAX_VIDEO_HEIGHT,
FIELD_MAX_VIDEO_FRAMERATE,
FIELD_MAX_VIDEO_BITRATE,
FIELD_MIN_VIDEO_WIDTH,
FIELD_MIN_VIDEO_HEIGHT,
FIELD_MIN_VIDEO_FRAMERATE,
FIELD_MIN_VIDEO_BITRATE,
FIELD_VIEWPORT_WIDTH,
FIELD_VIEWPORT_HEIGHT,
FIELD_VIEWPORT_ORIENTATION_MAY_CHANGE,
FIELD_PREFERRED_VIDEO_MIMETYPES,
FIELD_MAX_AUDIO_CHANNEL_COUNT,
FIELD_MAX_AUDIO_BITRATE,
FIELD_PREFERRED_AUDIO_MIME_TYPES,
FIELD_FORCE_LOWEST_BITRATE,
FIELD_FORCE_HIGHEST_SUPPORTED_BITRATE,
})
private @interface FieldNumber {}
private static final int FIELD_PREFERRED_AUDIO_LANGUAGES = 1;
private static final int FIELD_PREFERRED_AUDIO_ROLE_FLAGS = 2;
private static final int FIELD_PREFERRED_TEXT_LANGUAGES = 3;
private static final int FIELD_PREFERRED_TEXT_ROLE_FLAGS = 4;
private static final int FIELD_SELECT_UNDETERMINED_TEXT_LANGUAGE = 5;
private static final int FIELD_MAX_VIDEO_WIDTH = 6;
private static final int FIELD_MAX_VIDEO_HEIGHT = 7;
private static final int FIELD_MAX_VIDEO_FRAMERATE = 8;
private static final int FIELD_MAX_VIDEO_BITRATE = 9;
private static final int FIELD_MIN_VIDEO_WIDTH = 10;
private static final int FIELD_MIN_VIDEO_HEIGHT = 11;
private static final int FIELD_MIN_VIDEO_FRAMERATE = 12;
private static final int FIELD_MIN_VIDEO_BITRATE = 13;
private static final int FIELD_VIEWPORT_WIDTH = 14;
private static final int FIELD_VIEWPORT_HEIGHT = 15;
private static final int FIELD_VIEWPORT_ORIENTATION_MAY_CHANGE = 16;
private static final int FIELD_PREFERRED_VIDEO_MIMETYPES = 17;
private static final int FIELD_MAX_AUDIO_CHANNEL_COUNT = 18;
private static final int FIELD_MAX_AUDIO_BITRATE = 19;
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_HIGHEST_SUPPORTED_BITRATE = 22;
@Override
public int describeContents() {
return 0;
@CallSuper
public Bundle toBundle() {
Bundle bundle = new Bundle();
// Video
bundle.putInt(keyForField(FIELD_MAX_VIDEO_WIDTH), maxVideoWidth);
bundle.putInt(keyForField(FIELD_MAX_VIDEO_HEIGHT), maxVideoHeight);
bundle.putInt(keyForField(FIELD_MAX_VIDEO_FRAMERATE), maxVideoFrameRate);
bundle.putInt(keyForField(FIELD_MAX_VIDEO_BITRATE), maxVideoBitrate);
bundle.putInt(keyForField(FIELD_MIN_VIDEO_WIDTH), minVideoWidth);
bundle.putInt(keyForField(FIELD_MIN_VIDEO_HEIGHT), minVideoHeight);
bundle.putInt(keyForField(FIELD_MIN_VIDEO_FRAMERATE), minVideoFrameRate);
bundle.putInt(keyForField(FIELD_MIN_VIDEO_BITRATE), minVideoBitrate);
bundle.putInt(keyForField(FIELD_VIEWPORT_WIDTH), viewportWidth);
bundle.putInt(keyForField(FIELD_VIEWPORT_HEIGHT), viewportHeight);
bundle.putBoolean(
keyForField(FIELD_VIEWPORT_ORIENTATION_MAY_CHANGE), viewportOrientationMayChange);
bundle.putStringArray(
keyForField(FIELD_PREFERRED_VIDEO_MIMETYPES),
preferredVideoMimeTypes.toArray(new String[0]));
// Audio
bundle.putStringArray(
keyForField(FIELD_PREFERRED_AUDIO_LANGUAGES),
preferredAudioLanguages.toArray(new String[0]));
bundle.putInt(keyForField(FIELD_PREFERRED_AUDIO_ROLE_FLAGS), preferredAudioRoleFlags);
bundle.putInt(keyForField(FIELD_MAX_AUDIO_CHANNEL_COUNT), maxAudioChannelCount);
bundle.putInt(keyForField(FIELD_MAX_AUDIO_BITRATE), maxAudioBitrate);
bundle.putStringArray(
keyForField(FIELD_PREFERRED_AUDIO_MIME_TYPES),
preferredAudioMimeTypes.toArray(new String[0]));
// Text
bundle.putStringArray(
keyForField(FIELD_PREFERRED_TEXT_LANGUAGES), preferredTextLanguages.toArray(new String[0]));
bundle.putInt(keyForField(FIELD_PREFERRED_TEXT_ROLE_FLAGS), preferredTextRoleFlags);
bundle.putBoolean(
keyForField(FIELD_SELECT_UNDETERMINED_TEXT_LANGUAGE), selectUndeterminedTextLanguage);
// General
bundle.putBoolean(keyForField(FIELD_FORCE_LOWEST_BITRATE), forceLowestBitrate);
bundle.putBoolean(
keyForField(FIELD_FORCE_HIGHEST_SUPPORTED_BITRATE), forceHighestSupportedBitrate);
return bundle;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeList(preferredAudioLanguages);
dest.writeInt(preferredAudioRoleFlags);
dest.writeList(preferredTextLanguages);
dest.writeInt(preferredTextRoleFlags);
Util.writeBoolean(dest, selectUndeterminedTextLanguage);
// Video
dest.writeInt(maxVideoWidth);
dest.writeInt(maxVideoHeight);
dest.writeInt(maxVideoFrameRate);
dest.writeInt(maxVideoBitrate);
dest.writeInt(minVideoWidth);
dest.writeInt(minVideoHeight);
dest.writeInt(minVideoFrameRate);
dest.writeInt(minVideoBitrate);
dest.writeInt(viewportWidth);
dest.writeInt(viewportHeight);
Util.writeBoolean(dest, viewportOrientationMayChange);
dest.writeList(preferredVideoMimeTypes);
// Audio
dest.writeInt(maxAudioChannelCount);
dest.writeInt(maxAudioBitrate);
dest.writeList(preferredAudioMimeTypes);
// General
Util.writeBoolean(dest, forceLowestBitrate);
Util.writeBoolean(dest, forceHighestSupportedBitrate);
/** Object that can restore {@code TrackSelectionParameters} from a {@link Bundle}. */
public static final Creator<TrackSelectionParameters> CREATOR =
bundle -> new Builder(bundle).build();
private static String keyForField(@FieldNumber int field) {
return Integer.toString(field, Character.MAX_RADIX);
}
}

View File

@ -15,7 +15,11 @@
*/
package com.google.android.exoplayer2.util;
import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
import static com.google.android.exoplayer2.util.Util.castNonNull;
import android.os.Bundle;
import android.util.SparseArray;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.Bundleable;
import com.google.common.collect.ImmutableList;
@ -68,13 +72,41 @@ public final class BundleableUtils {
Bundleable.Creator<T> creator, List<Bundle> bundleList) {
ImmutableList.Builder<T> builder = ImmutableList.builder();
for (int i = 0; i < bundleList.size(); i++) {
Bundle bundle = bundleList.get(i);
Bundle bundle = checkNotNull(bundleList.get(i)); // Fail fast during parsing.
T bundleable = creator.fromBundle(bundle);
builder.add(bundleable);
}
return builder.build();
}
/**
* Converts a list of {@link Bundle} to a list of {@link Bundleable}. Returns {@code defaultValue}
* if {@code bundleList} is null.
*/
public static <T extends Bundleable> List<T> fromBundleNullableList(
Bundleable.Creator<T> creator, @Nullable List<Bundle> bundleList, List<T> defaultValue) {
return (bundleList == null) ? defaultValue : fromBundleList(creator, bundleList);
}
/**
* Converts a {@link SparseArray} of {@link Bundle} to a {@link SparseArray} of {@link
* Bundleable}. Returns {@code defaultValue} if {@code bundleSparseArray} is null.
*/
public static <T extends Bundleable> SparseArray<T> fromBundleNullableSparseArray(
Bundleable.Creator<T> creator,
@Nullable SparseArray<Bundle> bundleSparseArray,
SparseArray<T> defaultValue) {
if (bundleSparseArray == null) {
return defaultValue;
}
// Can't use ImmutableList as it doesn't support null elements.
SparseArray<T> result = new SparseArray<>(bundleSparseArray.size());
for (int i = 0; i < bundleSparseArray.size(); i++) {
result.put(bundleSparseArray.keyAt(i), creator.fromBundle(bundleSparseArray.valueAt(i)));
}
return result;
}
/**
* Converts a list of {@link Bundleable} to an {@link ArrayList} of {@link Bundle} so that the
* returned list can be put to {@link Bundle} using {@link Bundle#putParcelableArrayList}
@ -88,5 +120,31 @@ public final class BundleableUtils {
return arrayList;
}
/**
* Converts a {@link SparseArray} of {@link Bundleable} to an {@link SparseArray} of {@link
* Bundle} so that the returned {@link SparseArray} can be put to {@link Bundle} using {@link
* Bundle#putSparseParcelableArray} conveniently.
*/
public static <T extends Bundleable> SparseArray<Bundle> toBundleSparseArray(
SparseArray<T> bundleableSparseArray) {
SparseArray<Bundle> sparseArray = new SparseArray<>(bundleableSparseArray.size());
for (int i = 0; i < bundleableSparseArray.size(); i++) {
sparseArray.put(bundleableSparseArray.keyAt(i), bundleableSparseArray.valueAt(i).toBundle());
}
return sparseArray;
}
/**
* Set the application class loader to the given {@link Bundle} if no class loader is present.
*
* <p>This assumes that all classes unparceled from {@code bundle} are sharing the class loader of
* {@code BundleableUtils}.
*/
public static void ensureClassLoader(@Nullable Bundle bundle) {
if (bundle != null) {
bundle.setClassLoader(castNonNull(BundleableUtils.class.getClassLoader()));
}
}
private BundleableUtils() {}
}

View File

@ -15,17 +15,20 @@
*/
package com.google.android.exoplayer2.video;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.Bundle;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.Bundleable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.util.Util;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import org.checkerframework.dataflow.qual.Pure;
/** Stores color info. */
public final class ColorInfo implements Parcelable {
public final class ColorInfo implements Bundleable {
/**
* Returns the {@link C.ColorSpace} corresponding to the given ISO color primary code, as per
@ -116,16 +119,6 @@ public final class ColorInfo implements Parcelable {
this.hdrStaticInfo = hdrStaticInfo;
}
@SuppressWarnings("ResourceType")
/* package */ ColorInfo(Parcel in) {
colorSpace = in.readInt();
colorRange = in.readInt();
colorTransfer = in.readInt();
boolean hasHdrStaticInfo = Util.readBoolean(in);
hdrStaticInfo = hasHdrStaticInfo ? in.createByteArray() : null;
}
// Parcelable implementation.
@Override
public boolean equals(@Nullable Object obj) {
if (this == obj) {
@ -167,32 +160,42 @@ public final class ColorInfo implements Parcelable {
return hashCode;
}
@Override
public int describeContents() {
return 0;
}
// Bundleable implementation
@Documented
@Retention(RetentionPolicy.SOURCE)
@IntDef({
FIELD_COLOR_SPACE,
FIELD_COLOR_RANGE,
FIELD_COLOR_TRANSFER,
FIELD_HDR_STATIC_INFO,
})
private @interface FieldNumber {}
private static final int FIELD_COLOR_SPACE = 0;
private static final int FIELD_COLOR_RANGE = 1;
private static final int FIELD_COLOR_TRANSFER = 2;
private static final int FIELD_HDR_STATIC_INFO = 3;
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(colorSpace);
dest.writeInt(colorRange);
dest.writeInt(colorTransfer);
Util.writeBoolean(dest, hdrStaticInfo != null);
if (hdrStaticInfo != null) {
dest.writeByteArray(hdrStaticInfo);
}
public Bundle toBundle() {
Bundle bundle = new Bundle();
bundle.putInt(keyForField(FIELD_COLOR_SPACE), colorSpace);
bundle.putInt(keyForField(FIELD_COLOR_RANGE), colorRange);
bundle.putInt(keyForField(FIELD_COLOR_TRANSFER), colorTransfer);
bundle.putByteArray(keyForField(FIELD_HDR_STATIC_INFO), hdrStaticInfo);
return bundle;
}
public static final Parcelable.Creator<ColorInfo> CREATOR =
new Parcelable.Creator<ColorInfo>() {
@Override
public ColorInfo createFromParcel(Parcel in) {
return new ColorInfo(in);
}
public static final Creator<ColorInfo> CREATOR =
bundle ->
new ColorInfo(
bundle.getInt(keyForField(FIELD_COLOR_SPACE), Format.NO_VALUE),
bundle.getInt(keyForField(FIELD_COLOR_RANGE), Format.NO_VALUE),
bundle.getInt(keyForField(FIELD_COLOR_TRANSFER), Format.NO_VALUE),
bundle.getByteArray(keyForField(FIELD_HDR_STATIC_INFO)));
@Override
public ColorInfo[] newArray(int size) {
return new ColorInfo[size];
private static String keyForField(@FieldNumber int field) {
return Integer.toString(field, Character.MAX_RADIX);
}
};
}

View File

@ -20,7 +20,6 @@ import static com.google.android.exoplayer2.util.MimeTypes.VIDEO_MP4;
import static com.google.android.exoplayer2.util.MimeTypes.VIDEO_WEBM;
import static com.google.common.truth.Truth.assertThat;
import android.os.Parcel;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.drm.DrmInitData;
import com.google.android.exoplayer2.drm.ExoMediaCrypto;
@ -46,21 +45,15 @@ public final class FormatTest {
}
@Test
public void parcelFormat_createsEqualFormat_exceptExoMediaCryptoType() {
Format formatToParcel = createTestFormat();
public void roundTripViaBundle_ofParameters_yieldsEqualInstanceExceptExoMediaCryptoType() {
Format formatToBundle = createTestFormat();
Parcel parcel = Parcel.obtain();
formatToParcel.writeToParcel(parcel, 0);
parcel.setDataPosition(0);
Format formatFromBundle = Format.CREATOR.fromBundle(formatToBundle.toBundle());
Format formatFromParcel = Format.CREATOR.createFromParcel(parcel);
Format expectedFormat =
formatToParcel.buildUpon().setExoMediaCryptoType(UnsupportedMediaCrypto.class).build();
assertThat(formatFromParcel.exoMediaCryptoType).isEqualTo(UnsupportedMediaCrypto.class);
assertThat(formatFromParcel).isEqualTo(expectedFormat);
parcel.recycle();
assertThat(formatFromBundle.exoMediaCryptoType).isEqualTo(UnsupportedMediaCrypto.class);
assertThat(formatFromBundle)
.isEqualTo(
formatToBundle.buildUpon().setExoMediaCryptoType(UnsupportedMediaCrypto.class).build());
}
private static Format createTestFormat() {

View File

@ -25,6 +25,7 @@ import com.google.android.exoplayer2.C.FormatSupport;
import com.google.android.exoplayer2.source.MediaPeriodId;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.BundleableUtils;
import com.google.android.exoplayer2.util.Util;
import java.io.IOException;
import java.lang.annotation.Documented;
@ -234,7 +235,9 @@ public final class ExoPlaybackException extends PlaybackException {
rendererName = bundle.getString(keyForField(FIELD_RENDERER_NAME));
rendererIndex =
bundle.getInt(keyForField(FIELD_RENDERER_INDEX), /* defaultValue= */ C.INDEX_UNSET);
rendererFormat = bundle.getParcelable(keyForField(FIELD_RENDERER_FORMAT));
rendererFormat =
BundleableUtils.fromNullableBundle(
Format.CREATOR, bundle.getBundle(keyForField(FIELD_RENDERER_FORMAT)));
rendererFormatSupport =
bundle.getInt(
keyForField(FIELD_RENDERER_FORMAT_SUPPORT), /* defaultValue= */ C.FORMAT_HANDLED);
@ -396,7 +399,8 @@ public final class ExoPlaybackException extends PlaybackException {
bundle.putInt(keyForField(FIELD_TYPE), type);
bundle.putString(keyForField(FIELD_RENDERER_NAME), rendererName);
bundle.putInt(keyForField(FIELD_RENDERER_INDEX), rendererIndex);
bundle.putParcelable(keyForField(FIELD_RENDERER_FORMAT), rendererFormat);
bundle.putBundle(
keyForField(FIELD_RENDERER_FORMAT), BundleableUtils.toNullableBundle(rendererFormat));
bundle.putInt(keyForField(FIELD_RENDERER_FORMAT_SUPPORT), rendererFormatSupport);
bundle.putBoolean(keyForField(FIELD_IS_RECOVERABLE), isRecoverable);
return bundle;

View File

@ -17,13 +17,14 @@ package com.google.android.exoplayer2.trackselection;
import android.content.Context;
import android.graphics.Point;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Pair;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.Bundleable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.C.FormatSupport;
import com.google.android.exoplayer2.ExoPlaybackException;
@ -39,11 +40,15 @@ import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.BundleableUtils;
import com.google.android.exoplayer2.util.Util;
import com.google.common.collect.ComparisonChain;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Ordering;
import com.google.common.primitives.Ints;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@ -239,6 +244,66 @@ public class DefaultTrackSelector extends MappingTrackSelector {
rendererDisabledFlags = initialValues.rendererDisabledFlags.clone();
}
@SuppressWarnings("method.invocation") // Only setter are invoked.
private ParametersBuilder(Bundle bundle) {
super(bundle);
Parameters defaultValue = Parameters.DEFAULT_WITHOUT_CONTEXT;
// Video
setExceedVideoConstraintsIfNecessary(
bundle.getBoolean(
Parameters.keyForField(Parameters.FIELD_EXCEED_VIDEO_CONSTRAINTS_IF_NECESSARY),
defaultValue.exceedVideoConstraintsIfNecessary));
setAllowVideoMixedMimeTypeAdaptiveness(
bundle.getBoolean(
Parameters.keyForField(Parameters.FIELD_ALLOW_VIDEO_MIXED_MIME_TYPE_ADAPTIVENESS),
defaultValue.allowVideoMixedMimeTypeAdaptiveness));
setAllowVideoNonSeamlessAdaptiveness(
bundle.getBoolean(
Parameters.keyForField(Parameters.FIELD_ALLOW_VIDEO_NON_SEAMLESS_ADAPTIVENESS),
defaultValue.allowVideoNonSeamlessAdaptiveness));
// Audio
setExceedAudioConstraintsIfNecessary(
bundle.getBoolean(
Parameters.keyForField(Parameters.FIELD_EXCEED_AUDIO_CONSTRAINTS_IF_NCESSARY),
defaultValue.exceedAudioConstraintsIfNecessary));
setAllowAudioMixedMimeTypeAdaptiveness(
bundle.getBoolean(
Parameters.keyForField(Parameters.FIELD_ALLOW_AUDIO_MIXED_MIME_TYPE_ADAPTIVENESS),
defaultValue.allowAudioMixedMimeTypeAdaptiveness));
setAllowAudioMixedSampleRateAdaptiveness(
bundle.getBoolean(
Parameters.keyForField(Parameters.FIELD_ALLOW_AUDIO_MIXED_SAMPLE_RATE_ADAPTIVENESS),
defaultValue.allowAudioMixedSampleRateAdaptiveness));
setAllowAudioMixedChannelCountAdaptiveness(
bundle.getBoolean(
Parameters.keyForField(Parameters.FIELD_ALLOW_AUDIO_MIXED_CHANNEL_COUNT_ADAPTIVENESS),
defaultValue.allowAudioMixedChannelCountAdaptiveness));
// Text
setDisabledTextTrackSelectionFlags(
bundle.getInt(
Parameters.keyForField(Parameters.FIELD_DISABLED_TEXT_TRACK_SELECTION_FLAGS),
defaultValue.disabledTextTrackSelectionFlags));
// General
setExceedRendererCapabilitiesIfNecessary(
bundle.getBoolean(
Parameters.keyForField(Parameters.FIELD_EXCEED_RENDERER_CAPABILITIES_IF_NECESSARY),
defaultValue.exceedRendererCapabilitiesIfNecessary));
setTunnelingEnabled(
bundle.getBoolean(
Parameters.keyForField(Parameters.FIELD_TUNNELING_ENABLED),
defaultValue.tunnelingEnabled));
setAllowMultipleAdaptiveSelections(
bundle.getBoolean(
Parameters.keyForField(Parameters.FIELD_ALLOW_MULTIPLE_ADAPTIVE_SELECTIONS),
defaultValue.allowMultipleAdaptiveSelections));
selectionOverrides = new SparseArray<>();
setSelectionOverridesFromBundle(bundle);
rendererDisabledFlags = new SparseBooleanArray();
setRendererDisableFlagsFromBundle(bundle);
}
// Video
@Override
@ -724,12 +789,52 @@ public class DefaultTrackSelector extends MappingTrackSelector {
}
return clone;
}
private void setSelectionOverridesFromBundle(Bundle bundle) {
@Nullable
int[] rendererIndexes =
bundle.getIntArray(
Parameters.keyForField(Parameters.FIELD_SELECTION_OVERRIDES_RENDERER_INDEXES));
List<TrackGroupArray> trackGroupArrays =
BundleableUtils.fromBundleNullableList(
TrackGroupArray.CREATOR,
bundle.getParcelableArrayList(
Parameters.keyForField(Parameters.FIELD_SELECTION_OVERRIDES_TRACK_GROUP_ARRAYS)),
/* defaultValue= */ ImmutableList.of());
SparseArray<SelectionOverride> selectionOverrides =
BundleableUtils.fromBundleNullableSparseArray(
SelectionOverride.CREATOR,
bundle.getSparseParcelableArray(
Parameters.keyForField(Parameters.FIELD_SELECTION_OVERRIDES)),
/* defaultValue= */ new SparseArray<>());
if (rendererIndexes == null || rendererIndexes.length != trackGroupArrays.size()) {
return; // Incorrect format, ignore all overrides.
}
for (int i = 0; i < rendererIndexes.length; i++) {
int rendererIndex = rendererIndexes[i];
TrackGroupArray groups = trackGroupArrays.get(i);
@Nullable SelectionOverride selectionOverride = selectionOverrides.get(i);
setSelectionOverride(rendererIndex, groups, selectionOverride);
}
}
private void setRendererDisableFlagsFromBundle(Bundle bundle) {
int[] rendererIndexes =
bundle.getIntArray(Parameters.keyForField(Parameters.FIELD_RENDERER_DISABLED_INDEXES));
if (rendererIndexes == null) {
return;
}
for (int rendererIndex : rendererIndexes) {
setRendererDisabled(rendererIndex, true);
}
}
}
/**
* Extends {@link Parameters} by adding fields that are specific to {@link DefaultTrackSelector}.
*/
public static final class Parameters extends TrackSelectionParameters implements Parcelable {
public static final class Parameters extends TrackSelectionParameters implements Bundleable {
/**
* An instance with default values, except those obtained from the {@link Context}.
@ -755,19 +860,6 @@ public class DefaultTrackSelector extends MappingTrackSelector {
*/
@Deprecated public static final Parameters DEFAULT = DEFAULT_WITHOUT_CONTEXT;
public static final Creator<Parameters> CREATOR =
new Creator<Parameters>() {
@Override
public Parameters createFromParcel(Parcel in) {
return new Parameters(in);
}
@Override
public Parameters[] newArray(int size) {
return new Parameters[size];
}
};
/**
* Bitmask of selection flags that are disabled for text track selections. See {@link
* C.SelectionFlags}. The default value is {@code 0} (i.e. no flags).
@ -868,28 +960,6 @@ public class DefaultTrackSelector extends MappingTrackSelector {
rendererDisabledFlags = builder.rendererDisabledFlags;
}
/* package */ Parameters(Parcel in) {
super(in);
// Video
this.exceedVideoConstraintsIfNecessary = Util.readBoolean(in);
this.allowVideoMixedMimeTypeAdaptiveness = Util.readBoolean(in);
this.allowVideoNonSeamlessAdaptiveness = Util.readBoolean(in);
// Audio
this.exceedAudioConstraintsIfNecessary = Util.readBoolean(in);
this.allowAudioMixedMimeTypeAdaptiveness = Util.readBoolean(in);
this.allowAudioMixedSampleRateAdaptiveness = Util.readBoolean(in);
this.allowAudioMixedChannelCountAdaptiveness = Util.readBoolean(in);
// Text
this.disabledTextTrackSelectionFlags = in.readInt();
// General
this.exceedRendererCapabilitiesIfNecessary = Util.readBoolean(in);
this.tunnelingEnabled = Util.readBoolean(in);
this.allowMultipleAdaptiveSelections = Util.readBoolean(in);
// Overrides
this.selectionOverrides = readSelectionOverrides(in);
this.rendererDisabledFlags = Util.castNonNull(in.readSparseBooleanArray());
}
/**
* Returns whether the renderer is disabled.
*
@ -928,6 +998,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
}
/** Creates a new {@link ParametersBuilder}, copying the initial values from this instance. */
@Override
public ParametersBuilder buildUpon() {
return new ParametersBuilder(this);
}
@ -987,78 +1058,139 @@ public class DefaultTrackSelector extends MappingTrackSelector {
return result;
}
// Parcelable implementation.
// Bundleable implementation.
@Documented
@Retention(RetentionPolicy.SOURCE)
@IntDef({
FIELD_EXCEED_VIDEO_CONSTRAINTS_IF_NECESSARY,
FIELD_ALLOW_VIDEO_MIXED_MIME_TYPE_ADAPTIVENESS,
FIELD_ALLOW_VIDEO_NON_SEAMLESS_ADAPTIVENESS,
FIELD_EXCEED_AUDIO_CONSTRAINTS_IF_NCESSARY,
FIELD_ALLOW_AUDIO_MIXED_MIME_TYPE_ADAPTIVENESS,
FIELD_ALLOW_AUDIO_MIXED_SAMPLE_RATE_ADAPTIVENESS,
FIELD_ALLOW_AUDIO_MIXED_CHANNEL_COUNT_ADAPTIVENESS,
FIELD_DISABLED_TEXT_TRACK_SELECTION_FLAGS,
FIELD_EXCEED_RENDERER_CAPABILITIES_IF_NECESSARY,
FIELD_TUNNELING_ENABLED,
FIELD_ALLOW_MULTIPLE_ADAPTIVE_SELECTIONS,
FIELD_SELECTION_OVERRIDES_RENDERER_INDEXES,
FIELD_SELECTION_OVERRIDES_TRACK_GROUP_ARRAYS,
FIELD_SELECTION_OVERRIDES,
FIELD_RENDERER_DISABLED_INDEXES,
})
private @interface FieldNumber {}
// Start at 1000 to avoid conflict with the base class fields.
private static final int FIELD_EXCEED_VIDEO_CONSTRAINTS_IF_NECESSARY = 1000;
private static final int FIELD_ALLOW_VIDEO_MIXED_MIME_TYPE_ADAPTIVENESS = 1001;
private static final int FIELD_ALLOW_VIDEO_NON_SEAMLESS_ADAPTIVENESS = 1002;
private static final int FIELD_EXCEED_AUDIO_CONSTRAINTS_IF_NCESSARY = 1003;
private static final int FIELD_ALLOW_AUDIO_MIXED_MIME_TYPE_ADAPTIVENESS = 1004;
private static final int FIELD_ALLOW_AUDIO_MIXED_SAMPLE_RATE_ADAPTIVENESS = 1005;
private static final int FIELD_ALLOW_AUDIO_MIXED_CHANNEL_COUNT_ADAPTIVENESS = 1006;
private static final int FIELD_DISABLED_TEXT_TRACK_SELECTION_FLAGS = 1007;
private static final int FIELD_EXCEED_RENDERER_CAPABILITIES_IF_NECESSARY = 1008;
private static final int FIELD_TUNNELING_ENABLED = 1009;
private static final int FIELD_ALLOW_MULTIPLE_ADAPTIVE_SELECTIONS = 1010;
private static final int FIELD_SELECTION_OVERRIDES_RENDERER_INDEXES = 1011;
private static final int FIELD_SELECTION_OVERRIDES_TRACK_GROUP_ARRAYS = 1012;
private static final int FIELD_SELECTION_OVERRIDES = 1013;
private static final int FIELD_RENDERER_DISABLED_INDEXES = 1014;
@Override
public int describeContents() {
return 0;
}
public Bundle toBundle() {
Bundle bundle = super.toBundle();
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
// Video
Util.writeBoolean(dest, exceedVideoConstraintsIfNecessary);
Util.writeBoolean(dest, allowVideoMixedMimeTypeAdaptiveness);
Util.writeBoolean(dest, allowVideoNonSeamlessAdaptiveness);
bundle.putBoolean(
keyForField(FIELD_EXCEED_VIDEO_CONSTRAINTS_IF_NECESSARY),
exceedVideoConstraintsIfNecessary);
bundle.putBoolean(
keyForField(FIELD_ALLOW_VIDEO_MIXED_MIME_TYPE_ADAPTIVENESS),
allowVideoMixedMimeTypeAdaptiveness);
bundle.putBoolean(
keyForField(FIELD_ALLOW_VIDEO_NON_SEAMLESS_ADAPTIVENESS),
allowVideoNonSeamlessAdaptiveness);
// Audio
Util.writeBoolean(dest, exceedAudioConstraintsIfNecessary);
Util.writeBoolean(dest, allowAudioMixedMimeTypeAdaptiveness);
Util.writeBoolean(dest, allowAudioMixedSampleRateAdaptiveness);
Util.writeBoolean(dest, allowAudioMixedChannelCountAdaptiveness);
bundle.putBoolean(
keyForField(FIELD_EXCEED_AUDIO_CONSTRAINTS_IF_NCESSARY),
exceedAudioConstraintsIfNecessary);
bundle.putBoolean(
keyForField(FIELD_ALLOW_AUDIO_MIXED_MIME_TYPE_ADAPTIVENESS),
allowAudioMixedMimeTypeAdaptiveness);
bundle.putBoolean(
keyForField(FIELD_ALLOW_AUDIO_MIXED_SAMPLE_RATE_ADAPTIVENESS),
allowAudioMixedSampleRateAdaptiveness);
bundle.putBoolean(
keyForField(FIELD_ALLOW_AUDIO_MIXED_CHANNEL_COUNT_ADAPTIVENESS),
allowAudioMixedChannelCountAdaptiveness);
// Text
dest.writeInt(disabledTextTrackSelectionFlags);
bundle.putInt(
keyForField(FIELD_DISABLED_TEXT_TRACK_SELECTION_FLAGS), disabledTextTrackSelectionFlags);
// General
Util.writeBoolean(dest, exceedRendererCapabilitiesIfNecessary);
Util.writeBoolean(dest, tunnelingEnabled);
Util.writeBoolean(dest, allowMultipleAdaptiveSelections);
// Overrides
writeSelectionOverridesToParcel(dest, selectionOverrides);
dest.writeSparseBooleanArray(rendererDisabledFlags);
bundle.putBoolean(
keyForField(FIELD_EXCEED_RENDERER_CAPABILITIES_IF_NECESSARY),
exceedRendererCapabilitiesIfNecessary);
bundle.putBoolean(keyForField(FIELD_TUNNELING_ENABLED), tunnelingEnabled);
bundle.putBoolean(
keyForField(FIELD_ALLOW_MULTIPLE_ADAPTIVE_SELECTIONS), allowMultipleAdaptiveSelections);
putSelectionOverridesToBundle(bundle, selectionOverrides);
putRendererDisabledFlagsToBundle(bundle, rendererDisabledFlags);
return bundle;
}
// Static utility methods.
/** Object that can restore {@code Parameters} from a {@link Bundle}. */
public static final Creator<Parameters> CREATOR =
bundle -> new ParametersBuilder(bundle).build();
private static SparseArray<Map<TrackGroupArray, @NullableType SelectionOverride>>
readSelectionOverrides(Parcel in) {
int renderersWithOverridesCount = in.readInt();
SparseArray<Map<TrackGroupArray, @NullableType SelectionOverride>> selectionOverrides =
new SparseArray<>(renderersWithOverridesCount);
for (int i = 0; i < renderersWithOverridesCount; i++) {
int rendererIndex = in.readInt();
int overrideCount = in.readInt();
Map<TrackGroupArray, @NullableType SelectionOverride> overrides =
new HashMap<>(overrideCount);
for (int j = 0; j < overrideCount; j++) {
TrackGroupArray trackGroups =
Assertions.checkNotNull(in.readParcelable(TrackGroupArray.class.getClassLoader()));
@Nullable
SelectionOverride override = in.readParcelable(SelectionOverride.class.getClassLoader());
overrides.put(trackGroups, override);
}
selectionOverrides.put(rendererIndex, overrides);
}
return selectionOverrides;
private static String keyForField(@FieldNumber int field) {
return Integer.toString(field, Character.MAX_RADIX);
}
private static void writeSelectionOverridesToParcel(
Parcel dest,
/**
* Bundles selection overrides in 3 arrays of equal length. Each triplet of matching index is -
* the selection override (stored in a sparse array as they can be null) - the trackGroupArray
* of that override - the rendererIndex of that override
*/
private static void putSelectionOverridesToBundle(
Bundle bundle,
SparseArray<Map<TrackGroupArray, @NullableType SelectionOverride>> selectionOverrides) {
int renderersWithOverridesCount = selectionOverrides.size();
dest.writeInt(renderersWithOverridesCount);
for (int i = 0; i < renderersWithOverridesCount; i++) {
ArrayList<Integer> rendererIndexes = new ArrayList<>();
ArrayList<TrackGroupArray> trackGroupArrays = new ArrayList<>();
SparseArray<SelectionOverride> selections = new SparseArray<>();
for (int i = 0; i < selectionOverrides.size(); i++) {
int rendererIndex = selectionOverrides.keyAt(i);
Map<TrackGroupArray, @NullableType SelectionOverride> overrides =
selectionOverrides.valueAt(i);
int overrideCount = overrides.size();
dest.writeInt(rendererIndex);
dest.writeInt(overrideCount);
for (Map.Entry<TrackGroupArray, @NullableType SelectionOverride> override :
overrides.entrySet()) {
dest.writeParcelable(override.getKey(), /* parcelableFlags= */ 0);
dest.writeParcelable(override.getValue(), /* parcelableFlags= */ 0);
selectionOverrides.valueAt(i).entrySet()) {
@Nullable SelectionOverride selection = override.getValue();
if (selection != null) {
selections.put(trackGroupArrays.size(), selection);
}
trackGroupArrays.add(override.getKey());
rendererIndexes.add(rendererIndex);
}
bundle.putIntArray(
keyForField(FIELD_SELECTION_OVERRIDES_RENDERER_INDEXES), Ints.toArray(rendererIndexes));
bundle.putParcelableArrayList(
keyForField(FIELD_SELECTION_OVERRIDES_TRACK_GROUP_ARRAYS),
BundleableUtils.toBundleArrayList(trackGroupArrays));
bundle.putSparseParcelableArray(
keyForField(FIELD_SELECTION_OVERRIDES),
BundleableUtils.toBundleSparseArray(selections));
}
}
private static void putRendererDisabledFlagsToBundle(
Bundle bundle, SparseBooleanArray rendererDisabledFlags) {
int[] rendererIndexes = new int[rendererDisabledFlags.size()];
for (int i = 0; i < rendererDisabledFlags.size(); i++) {
rendererIndexes[i] = rendererDisabledFlags.keyAt(i);
}
bundle.putIntArray(keyForField(FIELD_RENDERER_DISABLED_INDEXES), rendererIndexes);
}
private static boolean areRendererDisabledFlagsEqual(
@ -1113,7 +1245,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
}
/** A track selection override. */
public static final class SelectionOverride implements Parcelable {
public static final class SelectionOverride implements Bundleable {
public final int groupIndex;
public final int[] tracks;
@ -1121,6 +1253,8 @@ public class DefaultTrackSelector extends MappingTrackSelector {
public final int type;
/**
* Constructs a {@code SelectionOverride} to override tracks of a group.
*
* @param groupIndex The overriding track group index.
* @param tracks The overriding track indices within the track group.
*/
@ -1129,6 +1263,8 @@ public class DefaultTrackSelector extends MappingTrackSelector {
}
/**
* Constructs a {@code SelectionOverride} of the given type to override tracks of a group.
*
* @param groupIndex The overriding track group index.
* @param tracks The overriding track indices within the track group.
* @param type The type that will be returned from {@link TrackSelection#getType()}.
@ -1141,14 +1277,6 @@ public class DefaultTrackSelector extends MappingTrackSelector {
Arrays.sort(this.tracks);
}
/* package */ SelectionOverride(Parcel in) {
groupIndex = in.readInt();
length = in.readByte();
tracks = new int[length];
in.readIntArray(tracks);
type = in.readInt();
}
/** Returns whether this override contains the specified track index. */
public boolean containsTrack(int track) {
for (int overrideTrack : tracks) {
@ -1179,34 +1307,44 @@ public class DefaultTrackSelector extends MappingTrackSelector {
&& type == other.type;
}
// Parcelable implementation.
// Bundleable implementation.
@Documented
@Retention(RetentionPolicy.SOURCE)
@IntDef({
FIELD_GROUP_INDEX,
FIELD_TRACKS,
FIELD_TRACK_TYPE,
})
private @interface FieldNumber {}
private static final int FIELD_GROUP_INDEX = 0;
private static final int FIELD_TRACKS = 1;
private static final int FIELD_TRACK_TYPE = 2;
@Override
public int describeContents() {
return 0;
public Bundle toBundle() {
Bundle bundle = new Bundle();
bundle.putInt(keyForField(FIELD_GROUP_INDEX), groupIndex);
bundle.putIntArray(keyForField(FIELD_TRACKS), tracks);
bundle.putInt(keyForField(FIELD_TRACK_TYPE), type);
return bundle;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(groupIndex);
dest.writeInt(tracks.length);
dest.writeIntArray(tracks);
dest.writeInt(type);
}
public static final Parcelable.Creator<SelectionOverride> CREATOR =
new Parcelable.Creator<SelectionOverride>() {
@Override
public SelectionOverride createFromParcel(Parcel in) {
return new SelectionOverride(in);
}
@Override
public SelectionOverride[] newArray(int size) {
return new SelectionOverride[size];
}
/** Object that can restore {@code SelectionOverride} from a {@link Bundle}. */
public static final Creator<SelectionOverride> CREATOR =
bundle -> {
int groupIndex = bundle.getInt(keyForField(FIELD_GROUP_INDEX), -1);
@Nullable int[] tracks = bundle.getIntArray(keyForField(FIELD_TRACKS));
int trackType = bundle.getInt(keyForField(FIELD_TRACK_TYPE), -1);
Assertions.checkArgument(groupIndex >= 0 && trackType >= 0);
Assertions.checkNotNull(tracks);
return new SelectionOverride(groupIndex, tracks, trackType);
};
private static String keyForField(@FieldNumber int field) {
return Integer.toString(field, Character.MAX_RADIX);
}
}
/**

View File

@ -17,7 +17,6 @@ package com.google.android.exoplayer2.source;
import static com.google.common.truth.Truth.assertThat;
import android.os.Parcel;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.util.MimeTypes;
@ -29,7 +28,7 @@ import org.junit.runner.RunWith;
public final class TrackGroupArrayTest {
@Test
public void parcelable() {
public void roundTripViaBundle_ofTrackGroupArray_yieldsEqualInstance() {
Format.Builder formatBuilder = new Format.Builder();
Format format1 = formatBuilder.setSampleMimeType(MimeTypes.VIDEO_H264).build();
Format format2 = formatBuilder.setSampleMimeType(MimeTypes.AUDIO_AAC).build();
@ -38,15 +37,11 @@ public final class TrackGroupArrayTest {
TrackGroup trackGroup1 = new TrackGroup(format1, format2);
TrackGroup trackGroup2 = new TrackGroup(format3);
TrackGroupArray trackGroupArrayToParcel = new TrackGroupArray(trackGroup1, trackGroup2);
TrackGroupArray trackGroupArrayToBundle = new TrackGroupArray(trackGroup1, trackGroup2);
Parcel parcel = Parcel.obtain();
trackGroupArrayToParcel.writeToParcel(parcel, 0);
parcel.setDataPosition(0);
TrackGroupArray trackGroupArrayFromBundle =
TrackGroupArray.CREATOR.fromBundle(trackGroupArrayToBundle.toBundle());
TrackGroupArray trackGroupArrayFromParcel = TrackGroupArray.CREATOR.createFromParcel(parcel);
assertThat(trackGroupArrayFromParcel).isEqualTo(trackGroupArrayToParcel);
parcel.recycle();
assertThat(trackGroupArrayFromBundle).isEqualTo(trackGroupArrayToBundle);
}
}

View File

@ -17,7 +17,6 @@ package com.google.android.exoplayer2.source;
import static com.google.common.truth.Truth.assertThat;
import android.os.Parcel;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.util.MimeTypes;
@ -29,20 +28,15 @@ import org.junit.runner.RunWith;
public final class TrackGroupTest {
@Test
public void parcelable() {
public void roundTripViaBundle_ofTrackGroup_yieldsEqualInstance() {
Format.Builder formatBuilder = new Format.Builder();
Format format1 = formatBuilder.setSampleMimeType(MimeTypes.VIDEO_H264).build();
Format format2 = formatBuilder.setSampleMimeType(MimeTypes.AUDIO_AAC).build();
TrackGroup trackGroupToParcel = new TrackGroup(format1, format2);
TrackGroup trackGroupToBundle = new TrackGroup(format1, format2);
Parcel parcel = Parcel.obtain();
trackGroupToParcel.writeToParcel(parcel, 0);
parcel.setDataPosition(0);
TrackGroup trackGroupFromBundle = TrackGroup.CREATOR.fromBundle(trackGroupToBundle.toBundle());
TrackGroup trackGroupFromParcel = TrackGroup.CREATOR.createFromParcel(parcel);
assertThat(trackGroupFromParcel).isEqualTo(trackGroupToParcel);
parcel.recycle();
assertThat(trackGroupFromBundle).isEqualTo(trackGroupToBundle);
}
}

View File

@ -29,9 +29,9 @@ import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.initMocks;
import android.content.Context;
import android.os.Parcel;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.Bundleable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.Format;
@ -139,37 +139,26 @@ public final class DefaultTrackSelectorTest {
assertThat(parameters.buildUpon().build()).isEqualTo(parameters);
}
/** Tests {@link Parameters} {@link android.os.Parcelable} implementation. */
/** Tests {@link Parameters} {@link Bundleable} implementation. */
@Test
public void parameters_parcelAndUnParcelable() {
Parameters parametersToParcel = buildParametersForEqualsTest();
public void roundTripViaBundle_ofParameters_yieldsEqualInstance() {
Parameters parametersToBundle = buildParametersForEqualsTest();
Parcel parcel = Parcel.obtain();
parametersToParcel.writeToParcel(parcel, 0);
parcel.setDataPosition(0);
Parameters parametersFromBundle = Parameters.CREATOR.fromBundle(parametersToBundle.toBundle());
Parameters parametersFromParcel = Parameters.CREATOR.createFromParcel(parcel);
assertThat(parametersFromParcel).isEqualTo(parametersToParcel);
parcel.recycle();
assertThat(parametersFromBundle).isEqualTo(parametersToBundle);
}
/** Tests {@link SelectionOverride}'s {@link android.os.Parcelable} implementation. */
/** Tests {@link SelectionOverride}'s {@link Bundleable} implementation. */
@Test
public void selectionOverrideParcelable() {
int[] tracks = new int[] {2, 3};
SelectionOverride selectionOverrideToParcel =
new SelectionOverride(/* groupIndex= */ 1, tracks);
public void roundTripViaBundle_ofSelectionOverride_yieldsEqualInstance() {
SelectionOverride selectionOverrideToBundle =
new SelectionOverride(/* groupIndex= */ 1, /* tracks...= */ 2, 3);
Parcel parcel = Parcel.obtain();
selectionOverrideToParcel.writeToParcel(parcel, 0);
parcel.setDataPosition(0);
SelectionOverride selectionOverrideFromBundle =
SelectionOverride.CREATOR.fromBundle(selectionOverrideToBundle.toBundle());
SelectionOverride selectionOverrideFromParcel =
SelectionOverride.CREATOR.createFromParcel(parcel);
assertThat(selectionOverrideFromParcel).isEqualTo(selectionOverrideToParcel);
parcel.recycle();
assertThat(selectionOverrideFromBundle).isEqualTo(selectionOverrideToBundle);
}
/** Tests that a null override clears a track selection. */
@ -1764,7 +1753,13 @@ public final class DefaultTrackSelectorTest {
/* rendererIndex= */ 2,
new TrackGroupArray(VIDEO_TRACK_GROUP),
new SelectionOverride(0, 1))
.setSelectionOverride(
/* rendererIndex= */ 2, new TrackGroupArray(AUDIO_TRACK_GROUP), /* override= */ null)
.setSelectionOverride(
/* rendererIndex= */ 5, new TrackGroupArray(VIDEO_TRACK_GROUP), /* override= */ null)
.setRendererDisabled(1, true)
.setRendererDisabled(3, true)
.setRendererDisabled(5, false)
.build();
}