blob: 65d011cf6758cd1dec7d52365ba125382cf50472 [file] [log] [blame]
/*
* Copyright (C) 2020 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.metrics;
import android.annotation.FloatRange;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
/**
* Playback track change event.
*/
public final class TrackChangeEvent extends Event implements Parcelable {
/** The track is off. */
public static final int TRACK_STATE_OFF = 0;
/** The track is on. */
public static final int TRACK_STATE_ON = 1;
/** Unknown track change reason. */
public static final int TRACK_CHANGE_REASON_UNKNOWN = 0;
/** Other track change reason. */
public static final int TRACK_CHANGE_REASON_OTHER = 1;
/** Track change reason for initial state. */
public static final int TRACK_CHANGE_REASON_INITIAL = 2;
/** Track change reason for manual changes. */
public static final int TRACK_CHANGE_REASON_MANUAL = 3;
/** Track change reason for adaptive changes. */
public static final int TRACK_CHANGE_REASON_ADAPTIVE = 4;
/** Audio track. */
public static final int TRACK_TYPE_AUDIO = 0;
/** Video track. */
public static final int TRACK_TYPE_VIDEO = 1;
/** Text track. */
public static final int TRACK_TYPE_TEXT = 2;
private final int mState;
private final int mReason;
private final @Nullable String mContainerMimeType;
private final @Nullable String mSampleMimeType;
private final @Nullable String mCodecName;
private final int mBitrate;
private final long mTimeSinceCreatedMillis;
private final int mType;
private final @Nullable String mLanguage;
private final @Nullable String mLanguageRegion;
private final int mChannelCount;
private final int mAudioSampleRate;
private final int mWidth;
private final int mHeight;
private final float mVideoFrameRate;
/** @hide */
@IntDef(prefix = "TRACK_STATE_", value = {
TRACK_STATE_OFF,
TRACK_STATE_ON
})
@Retention(RetentionPolicy.SOURCE)
public @interface TrackState {}
/** @hide */
@IntDef(prefix = "TRACK_CHANGE_REASON_", value = {
TRACK_CHANGE_REASON_UNKNOWN,
TRACK_CHANGE_REASON_OTHER,
TRACK_CHANGE_REASON_INITIAL,
TRACK_CHANGE_REASON_MANUAL,
TRACK_CHANGE_REASON_ADAPTIVE
})
@Retention(RetentionPolicy.SOURCE)
public @interface TrackChangeReason {}
/** @hide */
@IntDef(prefix = "TRACK_TYPE_", value = {
TRACK_TYPE_AUDIO,
TRACK_TYPE_VIDEO,
TRACK_TYPE_TEXT
})
@Retention(RetentionPolicy.SOURCE)
public @interface TrackType {}
private TrackChangeEvent(
int state,
int reason,
@Nullable String containerMimeType,
@Nullable String sampleMimeType,
@Nullable String codecName,
int bitrate,
long timeSinceCreatedMillis,
int type,
@Nullable String language,
@Nullable String languageRegion,
int channelCount,
int sampleRate,
int width,
int height,
float videoFrameRate,
@NonNull Bundle extras) {
this.mState = state;
this.mReason = reason;
this.mContainerMimeType = containerMimeType;
this.mSampleMimeType = sampleMimeType;
this.mCodecName = codecName;
this.mBitrate = bitrate;
this.mTimeSinceCreatedMillis = timeSinceCreatedMillis;
this.mType = type;
this.mLanguage = language;
this.mLanguageRegion = languageRegion;
this.mChannelCount = channelCount;
this.mAudioSampleRate = sampleRate;
this.mWidth = width;
this.mHeight = height;
this.mVideoFrameRate = videoFrameRate;
this.mMetricsBundle = extras.deepCopy();
}
/**
* Gets track state.
*/
@TrackState
public int getTrackState() {
return mState;
}
/**
* Gets track change reason.
*/
@TrackChangeReason
public int getTrackChangeReason() {
return mReason;
}
/**
* Gets container MIME type.
*/
public @Nullable String getContainerMimeType() {
return mContainerMimeType;
}
/**
* Gets the MIME type of the video/audio/text samples.
*/
public @Nullable String getSampleMimeType() {
return mSampleMimeType;
}
/**
* Gets codec name.
*/
public @Nullable String getCodecName() {
return mCodecName;
}
/**
* Gets bitrate.
* @return the bitrate, or -1 if unknown.
*/
@IntRange(from = -1, to = Integer.MAX_VALUE)
public int getBitrate() {
return mBitrate;
}
/**
* Gets timestamp since the creation of the log session in milliseconds.
* @return the timestamp since the creation in milliseconds, or -1 if unknown.
* @see LogSessionId
* @see PlaybackSession
* @see RecordingSession
*/
@Override
@IntRange(from = -1)
public long getTimeSinceCreatedMillis() {
return mTimeSinceCreatedMillis;
}
/**
* Gets the track type.
* <p>The track type must be one of {@link #TRACK_TYPE_AUDIO}, {@link #TRACK_TYPE_VIDEO},
* {@link #TRACK_TYPE_TEXT}.
*/
@TrackType
public int getTrackType() {
return mType;
}
/**
* Gets language code.
* @return a two-letter ISO 639-1 language code.
*/
public @Nullable String getLanguage() {
return mLanguage;
}
/**
* Gets language region code.
* @return an IETF BCP 47 optional language region subtag based on a two-letter country code.
*/
public @Nullable String getLanguageRegion() {
return mLanguageRegion;
}
/**
* Gets channel count.
* @return the channel count, or -1 if unknown.
*/
@IntRange(from = -1, to = Integer.MAX_VALUE)
public int getChannelCount() {
return mChannelCount;
}
/**
* Gets audio sample rate.
* @return the sample rate, or -1 if unknown.
*/
@IntRange(from = -1, to = Integer.MAX_VALUE)
public int getAudioSampleRate() {
return mAudioSampleRate;
}
/**
* Gets video width.
* @return the video width, or -1 if unknown.
*/
@IntRange(from = -1, to = Integer.MAX_VALUE)
public int getWidth() {
return mWidth;
}
/**
* Gets video height.
* @return the video height, or -1 if unknown.
*/
@IntRange(from = -1, to = Integer.MAX_VALUE)
public int getHeight() {
return mHeight;
}
/**
* Gets video frame rate.
* @return the video frame rate, or -1 if unknown.
*/
@FloatRange(from = -1, to = Float.MAX_VALUE)
public float getVideoFrameRate() {
return mVideoFrameRate;
}
/**
* Gets metrics-related information that is not supported by dedicated methods.
* <p>It is intended to be used for backwards compatibility by the metrics infrastructure.
*/
@Override
@NonNull
public Bundle getMetricsBundle() {
return mMetricsBundle;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
int flg = 0;
if (mContainerMimeType != null) flg |= 0x4;
if (mSampleMimeType != null) flg |= 0x8;
if (mCodecName != null) flg |= 0x10;
if (mLanguage != null) flg |= 0x100;
if (mLanguageRegion != null) flg |= 0x200;
dest.writeInt(flg);
dest.writeInt(mState);
dest.writeInt(mReason);
if (mContainerMimeType != null) dest.writeString(mContainerMimeType);
if (mSampleMimeType != null) dest.writeString(mSampleMimeType);
if (mCodecName != null) dest.writeString(mCodecName);
dest.writeInt(mBitrate);
dest.writeLong(mTimeSinceCreatedMillis);
dest.writeInt(mType);
if (mLanguage != null) dest.writeString(mLanguage);
if (mLanguageRegion != null) dest.writeString(mLanguageRegion);
dest.writeInt(mChannelCount);
dest.writeInt(mAudioSampleRate);
dest.writeInt(mWidth);
dest.writeInt(mHeight);
dest.writeFloat(mVideoFrameRate);
dest.writeBundle(mMetricsBundle);
}
@Override
public int describeContents() {
return 0;
}
private TrackChangeEvent(@NonNull Parcel in) {
int flg = in.readInt();
int state = in.readInt();
int reason = in.readInt();
String containerMimeType = (flg & 0x4) == 0 ? null : in.readString();
String sampleMimeType = (flg & 0x8) == 0 ? null : in.readString();
String codecName = (flg & 0x10) == 0 ? null : in.readString();
int bitrate = in.readInt();
long timeSinceCreatedMillis = in.readLong();
int type = in.readInt();
String language = (flg & 0x100) == 0 ? null : in.readString();
String languageRegion = (flg & 0x200) == 0 ? null : in.readString();
int channelCount = in.readInt();
int sampleRate = in.readInt();
int width = in.readInt();
int height = in.readInt();
float videoFrameRate = in.readFloat();
Bundle extras = in.readBundle();
this.mState = state;
this.mReason = reason;
this.mContainerMimeType = containerMimeType;
this.mSampleMimeType = sampleMimeType;
this.mCodecName = codecName;
this.mBitrate = bitrate;
this.mTimeSinceCreatedMillis = timeSinceCreatedMillis;
this.mType = type;
this.mLanguage = language;
this.mLanguageRegion = languageRegion;
this.mChannelCount = channelCount;
this.mAudioSampleRate = sampleRate;
this.mWidth = width;
this.mHeight = height;
this.mVideoFrameRate = videoFrameRate;
this.mMetricsBundle = extras;
}
public static final @NonNull Parcelable.Creator<TrackChangeEvent> CREATOR =
new Parcelable.Creator<TrackChangeEvent>() {
@Override
public TrackChangeEvent[] newArray(int size) {
return new TrackChangeEvent[size];
}
@Override
public TrackChangeEvent createFromParcel(@NonNull Parcel in) {
return new TrackChangeEvent(in);
}
};
@Override
public String toString() {
return "TrackChangeEvent { "
+ "state = " + mState + ", "
+ "reason = " + mReason + ", "
+ "containerMimeType = " + mContainerMimeType + ", "
+ "sampleMimeType = " + mSampleMimeType + ", "
+ "codecName = " + mCodecName + ", "
+ "bitrate = " + mBitrate + ", "
+ "timeSinceCreatedMillis = " + mTimeSinceCreatedMillis + ", "
+ "type = " + mType + ", "
+ "language = " + mLanguage + ", "
+ "languageRegion = " + mLanguageRegion + ", "
+ "channelCount = " + mChannelCount + ", "
+ "sampleRate = " + mAudioSampleRate + ", "
+ "width = " + mWidth + ", "
+ "height = " + mHeight + ", "
+ "videoFrameRate = " + mVideoFrameRate
+ " }";
}
@Override
public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TrackChangeEvent that = (TrackChangeEvent) o;
return mState == that.mState
&& mReason == that.mReason
&& Objects.equals(mContainerMimeType, that.mContainerMimeType)
&& Objects.equals(mSampleMimeType, that.mSampleMimeType)
&& Objects.equals(mCodecName, that.mCodecName)
&& mBitrate == that.mBitrate
&& mTimeSinceCreatedMillis == that.mTimeSinceCreatedMillis
&& mType == that.mType
&& Objects.equals(mLanguage, that.mLanguage)
&& Objects.equals(mLanguageRegion, that.mLanguageRegion)
&& mChannelCount == that.mChannelCount
&& mAudioSampleRate == that.mAudioSampleRate
&& mWidth == that.mWidth
&& mHeight == that.mHeight
&& mVideoFrameRate == that.mVideoFrameRate;
}
@Override
public int hashCode() {
return Objects.hash(mState, mReason, mContainerMimeType, mSampleMimeType, mCodecName,
mBitrate, mTimeSinceCreatedMillis, mType, mLanguage, mLanguageRegion,
mChannelCount, mAudioSampleRate, mWidth, mHeight, mVideoFrameRate);
}
/**
* A builder for {@link TrackChangeEvent}
*/
public static final class Builder {
// TODO: check track type for the setters.
private int mState = TRACK_STATE_OFF;
private int mReason = TRACK_CHANGE_REASON_UNKNOWN;
private @Nullable String mContainerMimeType;
private @Nullable String mSampleMimeType;
private @Nullable String mCodecName;
private int mBitrate = -1;
private long mTimeSinceCreatedMillis = -1;
private final int mType;
private @Nullable String mLanguage;
private @Nullable String mLanguageRegion;
private int mChannelCount = -1;
private int mAudioSampleRate = -1;
private int mWidth = -1;
private int mHeight = -1;
private float mVideoFrameRate = -1;
private Bundle mMetricsBundle = new Bundle();
private long mBuilderFieldsSet = 0L;
/**
* Creates a new Builder.
* @param type the track type. It must be one of {@link #TRACK_TYPE_AUDIO},
* {@link #TRACK_TYPE_VIDEO}, {@link #TRACK_TYPE_TEXT}.
*/
public Builder(@TrackType int type) {
if (type != TRACK_TYPE_AUDIO && type != TRACK_TYPE_VIDEO && type != TRACK_TYPE_TEXT) {
throw new IllegalArgumentException("track type must be one of TRACK_TYPE_AUDIO, "
+ "TRACK_TYPE_VIDEO, TRACK_TYPE_TEXT.");
}
mType = type;
}
/**
* Sets track state.
*/
public @NonNull Builder setTrackState(@TrackState int value) {
checkNotUsed();
mBuilderFieldsSet |= 0x1;
mState = value;
return this;
}
/**
* Sets track change reason.
*/
public @NonNull Builder setTrackChangeReason(@TrackChangeReason int value) {
checkNotUsed();
mBuilderFieldsSet |= 0x2;
mReason = value;
return this;
}
/**
* Sets container MIME type.
*/
public @NonNull Builder setContainerMimeType(@NonNull String value) {
checkNotUsed();
mBuilderFieldsSet |= 0x4;
mContainerMimeType = value;
return this;
}
/**
* Sets the MIME type of the video/audio/text samples.
*/
public @NonNull Builder setSampleMimeType(@NonNull String value) {
checkNotUsed();
mBuilderFieldsSet |= 0x8;
mSampleMimeType = value;
return this;
}
/**
* Sets codec name.
*/
public @NonNull Builder setCodecName(@NonNull String value) {
checkNotUsed();
mBuilderFieldsSet |= 0x10;
mCodecName = value;
return this;
}
/**
* Sets bitrate in bits per second.
* @param value the bitrate in bits per second. -1 indicates the value is unknown.
*/
public @NonNull Builder setBitrate(@IntRange(from = -1, to = Integer.MAX_VALUE) int value) {
checkNotUsed();
mBuilderFieldsSet |= 0x20;
mBitrate = value;
return this;
}
/**
* Sets timestamp since the creation in milliseconds.
* @param value the timestamp since the creation in milliseconds.
* -1 indicates the value is unknown.
* @see #getTimeSinceCreatedMillis()
*/
public @NonNull Builder setTimeSinceCreatedMillis(@IntRange(from = -1) long value) {
checkNotUsed();
mBuilderFieldsSet |= 0x40;
mTimeSinceCreatedMillis = value;
return this;
}
/**
* Sets language code.
* @param value a two-letter ISO 639-1 language code.
*/
public @NonNull Builder setLanguage(@NonNull String value) {
checkNotUsed();
mBuilderFieldsSet |= 0x100;
mLanguage = value;
return this;
}
/**
* Sets language region code.
* @param value an IETF BCP 47 optional language region subtag based on a two-letter country
* code.
*/
public @NonNull Builder setLanguageRegion(@NonNull String value) {
checkNotUsed();
mBuilderFieldsSet |= 0x200;
mLanguageRegion = value;
return this;
}
/**
* Sets channel count.
* @param value the channel count. -1 indicates the value is unknown.
*/
public @NonNull Builder setChannelCount(
@IntRange(from = -1, to = Integer.MAX_VALUE) int value) {
checkNotUsed();
mBuilderFieldsSet |= 0x400;
mChannelCount = value;
return this;
}
/**
* Sets sample rate.
* @param value the sample rate. -1 indicates the value is unknown.
*/
public @NonNull Builder setAudioSampleRate(
@IntRange(from = -1, to = Integer.MAX_VALUE) int value) {
checkNotUsed();
mBuilderFieldsSet |= 0x800;
mAudioSampleRate = value;
return this;
}
/**
* Sets video width.
* @param value the video width. -1 indicates the value is unknown.
*/
public @NonNull Builder setWidth(@IntRange(from = -1, to = Integer.MAX_VALUE) int value) {
checkNotUsed();
mBuilderFieldsSet |= 0x1000;
mWidth = value;
return this;
}
/**
* Sets video height.
* @param value the video height. -1 indicates the value is unknown.
*/
public @NonNull Builder setHeight(@IntRange(from = -1, to = Integer.MAX_VALUE) int value) {
checkNotUsed();
mBuilderFieldsSet |= 0x2000;
mHeight = value;
return this;
}
/**
* Sets video frame rate.
* @param value the video frame rate. -1 indicates the value is unknown.
*/
public @NonNull Builder setVideoFrameRate(
@FloatRange(from = -1, to = Float.MAX_VALUE) float value) {
checkNotUsed();
mVideoFrameRate = value;
return this;
}
/**
* Sets metrics-related information that is not supported by dedicated
* methods.
* <p>It is intended to be used for backwards compatibility by the
* metrics infrastructure.
*/
public @NonNull Builder setMetricsBundle(@NonNull Bundle metricsBundle) {
mMetricsBundle = metricsBundle;
return this;
}
/** Builds the instance. This builder should not be touched after calling this! */
public @NonNull TrackChangeEvent build() {
checkNotUsed();
mBuilderFieldsSet |= 0x4000; // Mark builder used
TrackChangeEvent o = new TrackChangeEvent(
mState,
mReason,
mContainerMimeType,
mSampleMimeType,
mCodecName,
mBitrate,
mTimeSinceCreatedMillis,
mType,
mLanguage,
mLanguageRegion,
mChannelCount,
mAudioSampleRate,
mWidth,
mHeight,
mVideoFrameRate,
mMetricsBundle);
return o;
}
private void checkNotUsed() {
if ((mBuilderFieldsSet & 0x4000) != 0) {
throw new IllegalStateException(
"This Builder should not be reused. Use a new Builder instance instead");
}
}
}
}