blob: e71ee204f564581652857f1fbc77b9f66e44482f [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.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 com.android.internal.util.AnnotationValidations;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
/**
* This class is used to store playback data.
*/
public final class PlaybackMetrics implements Parcelable {
/** Unknown stream source. */
public static final int STREAM_SOURCE_UNKNOWN = 0;
/** Stream from network. */
public static final int STREAM_SOURCE_NETWORK = 1;
/** Stream from device. */
public static final int STREAM_SOURCE_DEVICE = 2;
/** Stream from more than one sources. */
public static final int STREAM_SOURCE_MIXED = 3;
/** Unknown stream type. */
public static final int STREAM_TYPE_UNKNOWN = 0;
/** Other stream type. */
public static final int STREAM_TYPE_OTHER = 1;
/** Progressive stream type. */
public static final int STREAM_TYPE_PROGRESSIVE = 2;
/** DASH (Dynamic Adaptive Streaming over HTTP) stream type. */
public static final int STREAM_TYPE_DASH = 3;
/** HLS (HTTP Live Streaming) stream type. */
public static final int STREAM_TYPE_HLS = 4;
/** SS (HTTP Smooth Streaming) stream type. */
public static final int STREAM_TYPE_SS = 5;
/** Unknown playback type. */
public static final int PLAYBACK_TYPE_UNKNOWN = 0;
/** VOD (Video on Demand) playback type. */
public static final int PLAYBACK_TYPE_VOD = 1;
/** Live playback type. */
public static final int PLAYBACK_TYPE_LIVE = 2;
/** Other playback type. */
public static final int PLAYBACK_TYPE_OTHER = 3;
/** DRM is not used. */
public static final int DRM_TYPE_NONE = 0;
/** Other DRM type. */
public static final int DRM_TYPE_OTHER = 1;
/** Play ready DRM type. */
public static final int DRM_TYPE_PLAY_READY = 2;
/** Widevine L1 DRM type. */
public static final int DRM_TYPE_WIDEVINE_L1 = 3;
/** Widevine L3 DRM type. */
public static final int DRM_TYPE_WIDEVINE_L3 = 4;
/** Widevine L3 fallback DRM type. */
public static final int DRM_TYPE_WV_L3_FALLBACK = 5;
/** Clear key DRM type. */
public static final int DRM_TYPE_CLEARKEY = 6;
/** Unknown content type. */
public static final int CONTENT_TYPE_UNKNOWN = 0;
/** Main contents. */
public static final int CONTENT_TYPE_MAIN = 1;
/** Advertisement contents. */
public static final int CONTENT_TYPE_AD = 2;
/** Other contents. */
public static final int CONTENT_TYPE_OTHER = 3;
/** @hide */
@IntDef(prefix = "STREAM_SOURCE_", value = {
STREAM_SOURCE_UNKNOWN,
STREAM_SOURCE_NETWORK,
STREAM_SOURCE_DEVICE,
STREAM_SOURCE_MIXED
})
@Retention(RetentionPolicy.SOURCE)
public @interface StreamSource {}
/** @hide */
@IntDef(prefix = "STREAM_TYPE_", value = {
STREAM_TYPE_UNKNOWN,
STREAM_TYPE_OTHER,
STREAM_TYPE_PROGRESSIVE,
STREAM_TYPE_DASH,
STREAM_TYPE_HLS,
STREAM_TYPE_SS
})
@Retention(RetentionPolicy.SOURCE)
public @interface StreamType {}
/** @hide */
@IntDef(prefix = "PLAYBACK_TYPE_", value = {
PLAYBACK_TYPE_UNKNOWN,
PLAYBACK_TYPE_VOD,
PLAYBACK_TYPE_LIVE,
PLAYBACK_TYPE_OTHER
})
@Retention(RetentionPolicy.SOURCE)
public @interface PlaybackType {}
/** @hide */
@IntDef(prefix = "DRM_TYPE_", value = {
DRM_TYPE_NONE,
DRM_TYPE_OTHER,
DRM_TYPE_PLAY_READY,
DRM_TYPE_WIDEVINE_L1,
DRM_TYPE_WIDEVINE_L3,
DRM_TYPE_WV_L3_FALLBACK,
DRM_TYPE_CLEARKEY
})
@Retention(RetentionPolicy.SOURCE)
public @interface DrmType {}
/** @hide */
@IntDef(prefix = "CONTENT_TYPE_", value = {
CONTENT_TYPE_UNKNOWN,
CONTENT_TYPE_MAIN,
CONTENT_TYPE_AD,
CONTENT_TYPE_OTHER
})
@Retention(RetentionPolicy.SOURCE)
public @interface ContentType {}
private final long mMediaDurationMillis;
private final int mStreamSource;
private final int mStreamType;
private final int mPlaybackType;
private final int mDrmType;
private final int mContentType;
private final @Nullable String mPlayerName;
private final @Nullable String mPlayerVersion;
private final @NonNull long[] mExperimentIds;
private final int mVideoFramesPlayed;
private final int mVideoFramesDropped;
private final int mAudioUnderrunCount;
private final long mNetworkBytesRead;
private final long mLocalBytesRead;
private final long mNetworkTransferDurationMillis;
private final byte[] mDrmSessionId;
private final @NonNull Bundle mMetricsBundle;
/**
* Creates a new PlaybackMetrics.
*
* @hide
*/
public PlaybackMetrics(
long mediaDurationMillis,
int streamSource,
int streamType,
int playbackType,
int drmType,
int contentType,
@Nullable String playerName,
@Nullable String playerVersion,
@NonNull long[] experimentIds,
int videoFramesPlayed,
int videoFramesDropped,
int audioUnderrunCount,
long networkBytesRead,
long localBytesRead,
long networkTransferDurationMillis,
byte[] drmSessionId,
@NonNull Bundle extras) {
this.mMediaDurationMillis = mediaDurationMillis;
this.mStreamSource = streamSource;
this.mStreamType = streamType;
this.mPlaybackType = playbackType;
this.mDrmType = drmType;
this.mContentType = contentType;
this.mPlayerName = playerName;
this.mPlayerVersion = playerVersion;
this.mExperimentIds = experimentIds;
AnnotationValidations.validate(NonNull.class, null, mExperimentIds);
this.mVideoFramesPlayed = videoFramesPlayed;
this.mVideoFramesDropped = videoFramesDropped;
this.mAudioUnderrunCount = audioUnderrunCount;
this.mNetworkBytesRead = networkBytesRead;
this.mLocalBytesRead = localBytesRead;
this.mNetworkTransferDurationMillis = networkTransferDurationMillis;
this.mDrmSessionId = drmSessionId;
this.mMetricsBundle = extras.deepCopy();
}
/**
* Gets the media duration in milliseconds.
* <p>Media duration is the length of the media.
* @return the media duration in milliseconds, or -1 if unknown.
*/
@IntRange(from = -1)
public long getMediaDurationMillis() {
return mMediaDurationMillis;
}
/**
* Gets stream source type.
*/
@StreamSource
public int getStreamSource() {
return mStreamSource;
}
/**
* Gets stream type.
*/
@StreamType
public int getStreamType() {
return mStreamType;
}
/**
* Gets playback type.
*/
@PlaybackType
public int getPlaybackType() {
return mPlaybackType;
}
/**
* Gets DRM type.
*/
@DrmType
public int getDrmType() {
return mDrmType;
}
/**
* Gets content type.
*/
@ContentType
public int getContentType() {
return mContentType;
}
/**
* Gets player name.
*/
public @Nullable String getPlayerName() {
return mPlayerName;
}
/**
* Gets player version.
*/
public @Nullable String getPlayerVersion() {
return mPlayerVersion;
}
/**
* Gets experiment IDs.
*/
public @NonNull long[] getExperimentIds() {
return Arrays.copyOf(mExperimentIds, mExperimentIds.length);
}
/**
* Gets video frames played.
* @return the video frames played, or -1 if unknown.
*/
@IntRange(from = -1, to = Integer.MAX_VALUE)
public int getVideoFramesPlayed() {
return mVideoFramesPlayed;
}
/**
* Gets video frames dropped.
* @return the video frames dropped, or -1 if unknown.
*/
@IntRange(from = -1, to = Integer.MAX_VALUE)
public int getVideoFramesDropped() {
return mVideoFramesDropped;
}
/**
* Gets audio underrun count.
* @return the audio underrun count, or -1 if unknown.
*/
@IntRange(from = -1, to = Integer.MAX_VALUE)
public int getAudioUnderrunCount() {
return mAudioUnderrunCount;
}
/**
* Gets number of network bytes read.
* @return the number of network bytes read, or -1 if unknown.
*/
@IntRange(from = -1)
public long getNetworkBytesRead() {
return mNetworkBytesRead;
}
/**
* Gets number of local bytes read.
*/
@IntRange(from = -1)
public long getLocalBytesRead() {
return mLocalBytesRead;
}
/**
* Gets network transfer duration in milliseconds.
* <p>Total transfer time spent reading from the network in ms. For parallel requests, the
* overlapping time intervals are counted only once.
*/
@IntRange(from = -1)
public long getNetworkTransferDurationMillis() {
return mNetworkTransferDurationMillis;
}
/**
* Gets DRM session ID.
*/
@NonNull
public byte[] getDrmSessionId() {
return mDrmSessionId;
}
/**
* 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.
*/
@NonNull
public Bundle getMetricsBundle() {
return mMetricsBundle;
}
@Override
public String toString() {
return "PlaybackMetrics { "
+ "mediaDurationMillis = " + mMediaDurationMillis + ", "
+ "streamSource = " + mStreamSource + ", "
+ "streamType = " + mStreamType + ", "
+ "playbackType = " + mPlaybackType + ", "
+ "drmType = " + mDrmType + ", "
+ "contentType = " + mContentType + ", "
+ "playerName = " + mPlayerName + ", "
+ "playerVersion = " + mPlayerVersion + ", "
+ "experimentIds = " + Arrays.toString(mExperimentIds) + ", "
+ "videoFramesPlayed = " + mVideoFramesPlayed + ", "
+ "videoFramesDropped = " + mVideoFramesDropped + ", "
+ "audioUnderrunCount = " + mAudioUnderrunCount + ", "
+ "networkBytesRead = " + mNetworkBytesRead + ", "
+ "localBytesRead = " + mLocalBytesRead + ", "
+ "networkTransferDurationMillis = " + mNetworkTransferDurationMillis
+ "drmSessionId = " + Arrays.toString(mDrmSessionId)
+ " }";
}
@Override
public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PlaybackMetrics that = (PlaybackMetrics) o;
return mMediaDurationMillis == that.mMediaDurationMillis
&& mStreamSource == that.mStreamSource
&& mStreamType == that.mStreamType
&& mPlaybackType == that.mPlaybackType
&& mDrmType == that.mDrmType
&& mContentType == that.mContentType
&& Objects.equals(mPlayerName, that.mPlayerName)
&& Objects.equals(mPlayerVersion, that.mPlayerVersion)
&& Arrays.equals(mExperimentIds, that.mExperimentIds)
&& mVideoFramesPlayed == that.mVideoFramesPlayed
&& mVideoFramesDropped == that.mVideoFramesDropped
&& mAudioUnderrunCount == that.mAudioUnderrunCount
&& mNetworkBytesRead == that.mNetworkBytesRead
&& mLocalBytesRead == that.mLocalBytesRead
&& mNetworkTransferDurationMillis == that.mNetworkTransferDurationMillis
&& Arrays.equals(mDrmSessionId, that.mDrmSessionId);
}
@Override
public int hashCode() {
return Objects.hash(mMediaDurationMillis, mStreamSource, mStreamType, mPlaybackType,
mDrmType, mContentType, mPlayerName, mPlayerVersion, mExperimentIds,
mVideoFramesPlayed, mVideoFramesDropped, mAudioUnderrunCount, mNetworkBytesRead,
mLocalBytesRead, mNetworkTransferDurationMillis, mDrmSessionId);
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
long flg = 0;
if (mPlayerName != null) flg |= 0x80;
if (mPlayerVersion != null) flg |= 0x100;
dest.writeLong(flg);
dest.writeLong(mMediaDurationMillis);
dest.writeInt(mStreamSource);
dest.writeInt(mStreamType);
dest.writeInt(mPlaybackType);
dest.writeInt(mDrmType);
dest.writeInt(mContentType);
if (mPlayerName != null) dest.writeString(mPlayerName);
if (mPlayerVersion != null) dest.writeString(mPlayerVersion);
dest.writeLongArray(mExperimentIds);
dest.writeInt(mVideoFramesPlayed);
dest.writeInt(mVideoFramesDropped);
dest.writeInt(mAudioUnderrunCount);
dest.writeLong(mNetworkBytesRead);
dest.writeLong(mLocalBytesRead);
dest.writeLong(mNetworkTransferDurationMillis);
dest.writeInt(mDrmSessionId.length);
dest.writeByteArray(mDrmSessionId);
dest.writeBundle(mMetricsBundle);
}
@Override
public int describeContents() {
return 0;
}
/** @hide */
/* package-private */ PlaybackMetrics(@NonNull Parcel in) {
long flg = in.readLong();
long mediaDurationMillis = in.readLong();
int streamSource = in.readInt();
int streamType = in.readInt();
int playbackType = in.readInt();
int drmType = in.readInt();
int contentType = in.readInt();
String playerName = (flg & 0x80) == 0 ? null : in.readString();
String playerVersion = (flg & 0x100) == 0 ? null : in.readString();
long[] experimentIds = in.createLongArray();
int videoFramesPlayed = in.readInt();
int videoFramesDropped = in.readInt();
int audioUnderrunCount = in.readInt();
long networkBytesRead = in.readLong();
long localBytesRead = in.readLong();
long networkTransferDurationMillis = in.readLong();
int drmSessionIdLen = in.readInt();
byte[] drmSessionId = new byte[drmSessionIdLen];
in.readByteArray(drmSessionId);
Bundle extras = in.readBundle();
this.mMediaDurationMillis = mediaDurationMillis;
this.mStreamSource = streamSource;
this.mStreamType = streamType;
this.mPlaybackType = playbackType;
this.mDrmType = drmType;
this.mContentType = contentType;
this.mPlayerName = playerName;
this.mPlayerVersion = playerVersion;
this.mExperimentIds = experimentIds;
AnnotationValidations.validate(NonNull.class, null, mExperimentIds);
this.mVideoFramesPlayed = videoFramesPlayed;
this.mVideoFramesDropped = videoFramesDropped;
this.mAudioUnderrunCount = audioUnderrunCount;
this.mNetworkBytesRead = networkBytesRead;
this.mLocalBytesRead = localBytesRead;
this.mNetworkTransferDurationMillis = networkTransferDurationMillis;
this.mDrmSessionId = drmSessionId;
this.mMetricsBundle = extras;
}
public static final @NonNull Parcelable.Creator<PlaybackMetrics> CREATOR =
new Parcelable.Creator<PlaybackMetrics>() {
@Override
public PlaybackMetrics[] newArray(int size) {
return new PlaybackMetrics[size];
}
@Override
public PlaybackMetrics createFromParcel(@NonNull Parcel in) {
return new PlaybackMetrics(in);
}
};
/**
* A builder for {@link PlaybackMetrics}
*/
public static final class Builder {
private long mMediaDurationMillis = -1;
private int mStreamSource = STREAM_SOURCE_UNKNOWN;
private int mStreamType = STREAM_TYPE_UNKNOWN;
private int mPlaybackType = PLAYBACK_TYPE_UNKNOWN;
private int mDrmType = DRM_TYPE_NONE;
private int mContentType = CONTENT_TYPE_UNKNOWN;
private @Nullable String mPlayerName;
private @Nullable String mPlayerVersion;
private @NonNull List<Long> mExperimentIds = new ArrayList<>();
private int mVideoFramesPlayed = -1;
private int mVideoFramesDropped = -1;
private int mAudioUnderrunCount = -1;
private long mNetworkBytesRead = -1;
private long mLocalBytesRead = -1;
private long mNetworkTransferDurationMillis = -1;
private byte[] mDrmSessionId = new byte[0];
private Bundle mMetricsBundle = new Bundle();
/**
* Creates a new Builder.
*/
public Builder() {
}
/**
* Sets the media duration in milliseconds.
* @param value the media duration in milliseconds. -1 indicates the value is unknown.
* @see #getMediaDurationMillis()
*/
public @NonNull Builder setMediaDurationMillis(@IntRange(from = -1) long value) {
mMediaDurationMillis = value;
return this;
}
/**
* Sets the stream source type.
*/
public @NonNull Builder setStreamSource(@StreamSource int value) {
mStreamSource = value;
return this;
}
/**
* Sets the stream type.
*/
public @NonNull Builder setStreamType(@StreamType int value) {
mStreamType = value;
return this;
}
/**
* Sets the playback type.
*/
public @NonNull Builder setPlaybackType(@PlaybackType int value) {
mPlaybackType = value;
return this;
}
/**
* Sets the DRM type.
*/
public @NonNull Builder setDrmType(@DrmType int value) {
mDrmType = value;
return this;
}
/**
* Sets the content type.
*/
public @NonNull Builder setContentType(@ContentType int value) {
mContentType = value;
return this;
}
/**
* Sets the player name.
*/
public @NonNull Builder setPlayerName(@NonNull String value) {
mPlayerName = value;
return this;
}
/**
* Sets the player version.
*/
public @NonNull Builder setPlayerVersion(@NonNull String value) {
mPlayerVersion = value;
return this;
}
/**
* Adds the experiment ID.
*/
public @NonNull Builder addExperimentId(long value) {
mExperimentIds.add(value);
return this;
}
/**
* Sets the video frames played.
* @param value the video frames played. -1 indicates the value is unknown.
*/
public @NonNull Builder setVideoFramesPlayed(
@IntRange(from = -1, to = Integer.MAX_VALUE) int value) {
mVideoFramesPlayed = value;
return this;
}
/**
* Sets the video frames dropped.
* @param value the video frames dropped. -1 indicates the value is unknown.
*/
public @NonNull Builder setVideoFramesDropped(
@IntRange(from = -1, to = Integer.MAX_VALUE) int value) {
mVideoFramesDropped = value;
return this;
}
/**
* Sets the audio underrun count.
* @param value the audio underrun count. -1 indicates the value is unknown.
*/
public @NonNull Builder setAudioUnderrunCount(
@IntRange(from = -1, to = Integer.MAX_VALUE) int value) {
mAudioUnderrunCount = value;
return this;
}
/**
* Sets the number of network bytes read.
* @param value the number of network bytes read. -1 indicates the value is unknown.
*/
public @NonNull Builder setNetworkBytesRead(@IntRange(from = -1) long value) {
mNetworkBytesRead = value;
return this;
}
/**
* Sets the number of local bytes read.
* @param value the number of local bytes read. -1 indicates the value is unknown.
*/
public @NonNull Builder setLocalBytesRead(@IntRange(from = -1) long value) {
mLocalBytesRead = value;
return this;
}
/**
* Sets the network transfer duration in milliseconds.
* @param value the network transfer duration in milliseconds.
* -1 indicates the value is unknown.
* @see #getNetworkTransferDurationMillis()
*/
public @NonNull Builder setNetworkTransferDurationMillis(@IntRange(from = -1) long value) {
mNetworkTransferDurationMillis = value;
return this;
}
/**
* Sets DRM session ID.
*/
public @NonNull Builder setDrmSessionId(@NonNull byte[] drmSessionId) {
mDrmSessionId = drmSessionId;
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 PlaybackMetrics build() {
PlaybackMetrics o = new PlaybackMetrics(
mMediaDurationMillis,
mStreamSource,
mStreamType,
mPlaybackType,
mDrmType,
mContentType,
mPlayerName,
mPlayerVersion,
idsToLongArray(),
mVideoFramesPlayed,
mVideoFramesDropped,
mAudioUnderrunCount,
mNetworkBytesRead,
mLocalBytesRead,
mNetworkTransferDurationMillis,
mDrmSessionId,
mMetricsBundle);
return o;
}
private long[] idsToLongArray() {
long[] ids = new long[mExperimentIds.size()];
for (int i = 0; i < mExperimentIds.size(); i++) {
ids[i] = mExperimentIds.get(i);
}
return ids;
}
}
}