/* * 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. *
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. *
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. *
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,
Arrays.hashCode(mExperimentIds), mVideoFramesPlayed, mVideoFramesDropped,
mAudioUnderrunCount, mNetworkBytesRead, mLocalBytesRead,
mNetworkTransferDurationMillis, Arrays.hashCode(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 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;
}
}
}