1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.media.metrics; 18 19 import android.annotation.FloatRange; 20 import android.annotation.IntDef; 21 import android.annotation.IntRange; 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.os.Bundle; 25 import android.os.Parcel; 26 import android.os.Parcelable; 27 28 import java.lang.annotation.Retention; 29 import java.lang.annotation.RetentionPolicy; 30 import java.util.Objects; 31 32 /** 33 * Playback track change event. 34 */ 35 public final class TrackChangeEvent extends Event implements Parcelable { 36 /** The track is off. */ 37 public static final int TRACK_STATE_OFF = 0; 38 /** The track is on. */ 39 public static final int TRACK_STATE_ON = 1; 40 41 /** Unknown track change reason. */ 42 public static final int TRACK_CHANGE_REASON_UNKNOWN = 0; 43 /** Other track change reason. */ 44 public static final int TRACK_CHANGE_REASON_OTHER = 1; 45 /** Track change reason for initial state. */ 46 public static final int TRACK_CHANGE_REASON_INITIAL = 2; 47 /** Track change reason for manual changes. */ 48 public static final int TRACK_CHANGE_REASON_MANUAL = 3; 49 /** Track change reason for adaptive changes. */ 50 public static final int TRACK_CHANGE_REASON_ADAPTIVE = 4; 51 52 /** Audio track. */ 53 public static final int TRACK_TYPE_AUDIO = 0; 54 /** Video track. */ 55 public static final int TRACK_TYPE_VIDEO = 1; 56 /** Text track. */ 57 public static final int TRACK_TYPE_TEXT = 2; 58 59 private final int mState; 60 private final int mReason; 61 private final @Nullable String mContainerMimeType; 62 private final @Nullable String mSampleMimeType; 63 private final @Nullable String mCodecName; 64 private final int mBitrate; 65 private final long mTimeSinceCreatedMillis; 66 private final int mType; 67 private final @Nullable String mLanguage; 68 private final @Nullable String mLanguageRegion; 69 private final int mChannelCount; 70 private final int mAudioSampleRate; 71 private final int mWidth; 72 private final int mHeight; 73 private final float mVideoFrameRate; 74 75 76 77 /** @hide */ 78 @IntDef(prefix = "TRACK_STATE_", value = { 79 TRACK_STATE_OFF, 80 TRACK_STATE_ON 81 }) 82 @Retention(RetentionPolicy.SOURCE) 83 public @interface TrackState {} 84 85 /** @hide */ 86 @IntDef(prefix = "TRACK_CHANGE_REASON_", value = { 87 TRACK_CHANGE_REASON_UNKNOWN, 88 TRACK_CHANGE_REASON_OTHER, 89 TRACK_CHANGE_REASON_INITIAL, 90 TRACK_CHANGE_REASON_MANUAL, 91 TRACK_CHANGE_REASON_ADAPTIVE 92 }) 93 @Retention(RetentionPolicy.SOURCE) 94 public @interface TrackChangeReason {} 95 96 /** @hide */ 97 @IntDef(prefix = "TRACK_TYPE_", value = { 98 TRACK_TYPE_AUDIO, 99 TRACK_TYPE_VIDEO, 100 TRACK_TYPE_TEXT 101 }) 102 @Retention(RetentionPolicy.SOURCE) 103 public @interface TrackType {} 104 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)105 private TrackChangeEvent( 106 int state, 107 int reason, 108 @Nullable String containerMimeType, 109 @Nullable String sampleMimeType, 110 @Nullable String codecName, 111 int bitrate, 112 long timeSinceCreatedMillis, 113 int type, 114 @Nullable String language, 115 @Nullable String languageRegion, 116 int channelCount, 117 int sampleRate, 118 int width, 119 int height, 120 float videoFrameRate, 121 @NonNull Bundle extras) { 122 this.mState = state; 123 this.mReason = reason; 124 this.mContainerMimeType = containerMimeType; 125 this.mSampleMimeType = sampleMimeType; 126 this.mCodecName = codecName; 127 this.mBitrate = bitrate; 128 this.mTimeSinceCreatedMillis = timeSinceCreatedMillis; 129 this.mType = type; 130 this.mLanguage = language; 131 this.mLanguageRegion = languageRegion; 132 this.mChannelCount = channelCount; 133 this.mAudioSampleRate = sampleRate; 134 this.mWidth = width; 135 this.mHeight = height; 136 this.mVideoFrameRate = videoFrameRate; 137 this.mMetricsBundle = extras.deepCopy(); 138 } 139 140 /** 141 * Gets track state. 142 */ 143 @TrackState getTrackState()144 public int getTrackState() { 145 return mState; 146 } 147 148 /** 149 * Gets track change reason. 150 */ 151 @TrackChangeReason getTrackChangeReason()152 public int getTrackChangeReason() { 153 return mReason; 154 } 155 156 /** 157 * Gets container MIME type. 158 */ getContainerMimeType()159 public @Nullable String getContainerMimeType() { 160 return mContainerMimeType; 161 } 162 163 /** 164 * Gets the MIME type of the video/audio/text samples. 165 */ getSampleMimeType()166 public @Nullable String getSampleMimeType() { 167 return mSampleMimeType; 168 } 169 170 /** 171 * Gets codec name. 172 */ getCodecName()173 public @Nullable String getCodecName() { 174 return mCodecName; 175 } 176 177 /** 178 * Gets bitrate. 179 * @return the bitrate, or -1 if unknown. 180 */ 181 @IntRange(from = -1, to = Integer.MAX_VALUE) getBitrate()182 public int getBitrate() { 183 return mBitrate; 184 } 185 186 /** 187 * Gets timestamp since the creation of the log session in milliseconds. 188 * @return the timestamp since the creation in milliseconds, or -1 if unknown. 189 * @see LogSessionId 190 * @see PlaybackSession 191 * @see RecordingSession 192 */ 193 @Override 194 @IntRange(from = -1) getTimeSinceCreatedMillis()195 public long getTimeSinceCreatedMillis() { 196 return mTimeSinceCreatedMillis; 197 } 198 199 /** 200 * Gets the track type. 201 * <p>The track type must be one of {@link #TRACK_TYPE_AUDIO}, {@link #TRACK_TYPE_VIDEO}, 202 * {@link #TRACK_TYPE_TEXT}. 203 */ 204 @TrackType getTrackType()205 public int getTrackType() { 206 return mType; 207 } 208 209 /** 210 * Gets language code. 211 * @return a two-letter ISO 639-1 language code. 212 */ getLanguage()213 public @Nullable String getLanguage() { 214 return mLanguage; 215 } 216 217 218 /** 219 * Gets language region code. 220 * @return an IETF BCP 47 optional language region subtag based on a two-letter country code. 221 */ getLanguageRegion()222 public @Nullable String getLanguageRegion() { 223 return mLanguageRegion; 224 } 225 226 /** 227 * Gets channel count. 228 * @return the channel count, or -1 if unknown. 229 */ 230 @IntRange(from = -1, to = Integer.MAX_VALUE) getChannelCount()231 public int getChannelCount() { 232 return mChannelCount; 233 } 234 235 /** 236 * Gets audio sample rate. 237 * @return the sample rate, or -1 if unknown. 238 */ 239 @IntRange(from = -1, to = Integer.MAX_VALUE) getAudioSampleRate()240 public int getAudioSampleRate() { 241 return mAudioSampleRate; 242 } 243 244 /** 245 * Gets video width. 246 * @return the video width, or -1 if unknown. 247 */ 248 @IntRange(from = -1, to = Integer.MAX_VALUE) getWidth()249 public int getWidth() { 250 return mWidth; 251 } 252 253 /** 254 * Gets video height. 255 * @return the video height, or -1 if unknown. 256 */ 257 @IntRange(from = -1, to = Integer.MAX_VALUE) getHeight()258 public int getHeight() { 259 return mHeight; 260 } 261 262 /** 263 * Gets video frame rate. 264 * @return the video frame rate, or -1 if unknown. 265 */ 266 @FloatRange(from = -1, to = Float.MAX_VALUE) getVideoFrameRate()267 public float getVideoFrameRate() { 268 return mVideoFrameRate; 269 } 270 271 /** 272 * Gets metrics-related information that is not supported by dedicated methods. 273 * <p>It is intended to be used for backwards compatibility by the metrics infrastructure. 274 */ 275 @Override 276 @NonNull getMetricsBundle()277 public Bundle getMetricsBundle() { 278 return mMetricsBundle; 279 } 280 281 @Override writeToParcel(@onNull Parcel dest, int flags)282 public void writeToParcel(@NonNull Parcel dest, int flags) { 283 int flg = 0; 284 if (mContainerMimeType != null) flg |= 0x4; 285 if (mSampleMimeType != null) flg |= 0x8; 286 if (mCodecName != null) flg |= 0x10; 287 if (mLanguage != null) flg |= 0x100; 288 if (mLanguageRegion != null) flg |= 0x200; 289 dest.writeInt(flg); 290 dest.writeInt(mState); 291 dest.writeInt(mReason); 292 if (mContainerMimeType != null) dest.writeString(mContainerMimeType); 293 if (mSampleMimeType != null) dest.writeString(mSampleMimeType); 294 if (mCodecName != null) dest.writeString(mCodecName); 295 dest.writeInt(mBitrate); 296 dest.writeLong(mTimeSinceCreatedMillis); 297 dest.writeInt(mType); 298 if (mLanguage != null) dest.writeString(mLanguage); 299 if (mLanguageRegion != null) dest.writeString(mLanguageRegion); 300 dest.writeInt(mChannelCount); 301 dest.writeInt(mAudioSampleRate); 302 dest.writeInt(mWidth); 303 dest.writeInt(mHeight); 304 dest.writeFloat(mVideoFrameRate); 305 dest.writeBundle(mMetricsBundle); 306 } 307 308 @Override describeContents()309 public int describeContents() { 310 return 0; 311 } 312 TrackChangeEvent(@onNull Parcel in)313 private TrackChangeEvent(@NonNull Parcel in) { 314 int flg = in.readInt(); 315 int state = in.readInt(); 316 int reason = in.readInt(); 317 String containerMimeType = (flg & 0x4) == 0 ? null : in.readString(); 318 String sampleMimeType = (flg & 0x8) == 0 ? null : in.readString(); 319 String codecName = (flg & 0x10) == 0 ? null : in.readString(); 320 int bitrate = in.readInt(); 321 long timeSinceCreatedMillis = in.readLong(); 322 int type = in.readInt(); 323 String language = (flg & 0x100) == 0 ? null : in.readString(); 324 String languageRegion = (flg & 0x200) == 0 ? null : in.readString(); 325 int channelCount = in.readInt(); 326 int sampleRate = in.readInt(); 327 int width = in.readInt(); 328 int height = in.readInt(); 329 float videoFrameRate = in.readFloat(); 330 Bundle extras = in.readBundle(); 331 332 this.mState = state; 333 this.mReason = reason; 334 this.mContainerMimeType = containerMimeType; 335 this.mSampleMimeType = sampleMimeType; 336 this.mCodecName = codecName; 337 this.mBitrate = bitrate; 338 this.mTimeSinceCreatedMillis = timeSinceCreatedMillis; 339 this.mType = type; 340 this.mLanguage = language; 341 this.mLanguageRegion = languageRegion; 342 this.mChannelCount = channelCount; 343 this.mAudioSampleRate = sampleRate; 344 this.mWidth = width; 345 this.mHeight = height; 346 this.mVideoFrameRate = videoFrameRate; 347 this.mMetricsBundle = extras; 348 } 349 350 public static final @NonNull Parcelable.Creator<TrackChangeEvent> CREATOR = 351 new Parcelable.Creator<TrackChangeEvent>() { 352 @Override 353 public TrackChangeEvent[] newArray(int size) { 354 return new TrackChangeEvent[size]; 355 } 356 357 @Override 358 public TrackChangeEvent createFromParcel(@NonNull Parcel in) { 359 return new TrackChangeEvent(in); 360 } 361 }; 362 363 @Override toString()364 public String toString() { 365 return "TrackChangeEvent { " 366 + "state = " + mState + ", " 367 + "reason = " + mReason + ", " 368 + "containerMimeType = " + mContainerMimeType + ", " 369 + "sampleMimeType = " + mSampleMimeType + ", " 370 + "codecName = " + mCodecName + ", " 371 + "bitrate = " + mBitrate + ", " 372 + "timeSinceCreatedMillis = " + mTimeSinceCreatedMillis + ", " 373 + "type = " + mType + ", " 374 + "language = " + mLanguage + ", " 375 + "languageRegion = " + mLanguageRegion + ", " 376 + "channelCount = " + mChannelCount + ", " 377 + "sampleRate = " + mAudioSampleRate + ", " 378 + "width = " + mWidth + ", " 379 + "height = " + mHeight + ", " 380 + "videoFrameRate = " + mVideoFrameRate 381 + " }"; 382 } 383 384 @Override equals(@ullable Object o)385 public boolean equals(@Nullable Object o) { 386 if (this == o) return true; 387 if (o == null || getClass() != o.getClass()) return false; 388 TrackChangeEvent that = (TrackChangeEvent) o; 389 return mState == that.mState 390 && mReason == that.mReason 391 && Objects.equals(mContainerMimeType, that.mContainerMimeType) 392 && Objects.equals(mSampleMimeType, that.mSampleMimeType) 393 && Objects.equals(mCodecName, that.mCodecName) 394 && mBitrate == that.mBitrate 395 && mTimeSinceCreatedMillis == that.mTimeSinceCreatedMillis 396 && mType == that.mType 397 && Objects.equals(mLanguage, that.mLanguage) 398 && Objects.equals(mLanguageRegion, that.mLanguageRegion) 399 && mChannelCount == that.mChannelCount 400 && mAudioSampleRate == that.mAudioSampleRate 401 && mWidth == that.mWidth 402 && mHeight == that.mHeight 403 && mVideoFrameRate == that.mVideoFrameRate; 404 } 405 406 @Override hashCode()407 public int hashCode() { 408 return Objects.hash(mState, mReason, mContainerMimeType, mSampleMimeType, mCodecName, 409 mBitrate, mTimeSinceCreatedMillis, mType, mLanguage, mLanguageRegion, 410 mChannelCount, mAudioSampleRate, mWidth, mHeight, mVideoFrameRate); 411 } 412 413 /** 414 * A builder for {@link TrackChangeEvent} 415 */ 416 public static final class Builder { 417 // TODO: check track type for the setters. 418 private int mState = TRACK_STATE_OFF; 419 private int mReason = TRACK_CHANGE_REASON_UNKNOWN; 420 private @Nullable String mContainerMimeType; 421 private @Nullable String mSampleMimeType; 422 private @Nullable String mCodecName; 423 private int mBitrate = -1; 424 private long mTimeSinceCreatedMillis = -1; 425 private final int mType; 426 private @Nullable String mLanguage; 427 private @Nullable String mLanguageRegion; 428 private int mChannelCount = -1; 429 private int mAudioSampleRate = -1; 430 private int mWidth = -1; 431 private int mHeight = -1; 432 private float mVideoFrameRate = -1; 433 private Bundle mMetricsBundle = new Bundle(); 434 435 private long mBuilderFieldsSet = 0L; 436 437 /** 438 * Creates a new Builder. 439 * @param type the track type. It must be one of {@link #TRACK_TYPE_AUDIO}, 440 * {@link #TRACK_TYPE_VIDEO}, {@link #TRACK_TYPE_TEXT}. 441 */ Builder(@rackType int type)442 public Builder(@TrackType int type) { 443 if (type != TRACK_TYPE_AUDIO && type != TRACK_TYPE_VIDEO && type != TRACK_TYPE_TEXT) { 444 throw new IllegalArgumentException("track type must be one of TRACK_TYPE_AUDIO, " 445 + "TRACK_TYPE_VIDEO, TRACK_TYPE_TEXT."); 446 } 447 mType = type; 448 } 449 450 /** 451 * Sets track state. 452 */ setTrackState(@rackState int value)453 public @NonNull Builder setTrackState(@TrackState int value) { 454 checkNotUsed(); 455 mBuilderFieldsSet |= 0x1; 456 mState = value; 457 return this; 458 } 459 460 /** 461 * Sets track change reason. 462 */ setTrackChangeReason(@rackChangeReason int value)463 public @NonNull Builder setTrackChangeReason(@TrackChangeReason int value) { 464 checkNotUsed(); 465 mBuilderFieldsSet |= 0x2; 466 mReason = value; 467 return this; 468 } 469 470 /** 471 * Sets container MIME type. 472 */ setContainerMimeType(@onNull String value)473 public @NonNull Builder setContainerMimeType(@NonNull String value) { 474 checkNotUsed(); 475 mBuilderFieldsSet |= 0x4; 476 mContainerMimeType = value; 477 return this; 478 } 479 480 /** 481 * Sets the MIME type of the video/audio/text samples. 482 */ setSampleMimeType(@onNull String value)483 public @NonNull Builder setSampleMimeType(@NonNull String value) { 484 checkNotUsed(); 485 mBuilderFieldsSet |= 0x8; 486 mSampleMimeType = value; 487 return this; 488 } 489 490 /** 491 * Sets codec name. 492 */ setCodecName(@onNull String value)493 public @NonNull Builder setCodecName(@NonNull String value) { 494 checkNotUsed(); 495 mBuilderFieldsSet |= 0x10; 496 mCodecName = value; 497 return this; 498 } 499 500 /** 501 * Sets bitrate in bits per second. 502 * @param value the bitrate in bits per second. -1 indicates the value is unknown. 503 */ setBitrate(@ntRangefrom = -1, to = Integer.MAX_VALUE) int value)504 public @NonNull Builder setBitrate(@IntRange(from = -1, to = Integer.MAX_VALUE) int value) { 505 checkNotUsed(); 506 mBuilderFieldsSet |= 0x20; 507 mBitrate = value; 508 return this; 509 } 510 511 /** 512 * Sets timestamp since the creation in milliseconds. 513 * @param value the timestamp since the creation in milliseconds. 514 * -1 indicates the value is unknown. 515 * @see #getTimeSinceCreatedMillis() 516 */ setTimeSinceCreatedMillis(@ntRangefrom = -1) long value)517 public @NonNull Builder setTimeSinceCreatedMillis(@IntRange(from = -1) long value) { 518 checkNotUsed(); 519 mBuilderFieldsSet |= 0x40; 520 mTimeSinceCreatedMillis = value; 521 return this; 522 } 523 524 /** 525 * Sets language code. 526 * @param value a two-letter ISO 639-1 language code. 527 */ setLanguage(@onNull String value)528 public @NonNull Builder setLanguage(@NonNull String value) { 529 checkNotUsed(); 530 mBuilderFieldsSet |= 0x100; 531 mLanguage = value; 532 return this; 533 } 534 535 /** 536 * Sets language region code. 537 * @param value an IETF BCP 47 optional language region subtag based on a two-letter country 538 * code. 539 */ setLanguageRegion(@onNull String value)540 public @NonNull Builder setLanguageRegion(@NonNull String value) { 541 checkNotUsed(); 542 mBuilderFieldsSet |= 0x200; 543 mLanguageRegion = value; 544 return this; 545 } 546 547 /** 548 * Sets channel count. 549 * @param value the channel count. -1 indicates the value is unknown. 550 */ setChannelCount( @ntRangefrom = -1, to = Integer.MAX_VALUE) int value)551 public @NonNull Builder setChannelCount( 552 @IntRange(from = -1, to = Integer.MAX_VALUE) int value) { 553 checkNotUsed(); 554 mBuilderFieldsSet |= 0x400; 555 mChannelCount = value; 556 return this; 557 } 558 559 /** 560 * Sets sample rate. 561 * @param value the sample rate. -1 indicates the value is unknown. 562 */ setAudioSampleRate( @ntRangefrom = -1, to = Integer.MAX_VALUE) int value)563 public @NonNull Builder setAudioSampleRate( 564 @IntRange(from = -1, to = Integer.MAX_VALUE) int value) { 565 checkNotUsed(); 566 mBuilderFieldsSet |= 0x800; 567 mAudioSampleRate = value; 568 return this; 569 } 570 571 /** 572 * Sets video width. 573 * @param value the video width. -1 indicates the value is unknown. 574 */ setWidth(@ntRangefrom = -1, to = Integer.MAX_VALUE) int value)575 public @NonNull Builder setWidth(@IntRange(from = -1, to = Integer.MAX_VALUE) int value) { 576 checkNotUsed(); 577 mBuilderFieldsSet |= 0x1000; 578 mWidth = value; 579 return this; 580 } 581 582 /** 583 * Sets video height. 584 * @param value the video height. -1 indicates the value is unknown. 585 */ setHeight(@ntRangefrom = -1, to = Integer.MAX_VALUE) int value)586 public @NonNull Builder setHeight(@IntRange(from = -1, to = Integer.MAX_VALUE) int value) { 587 checkNotUsed(); 588 mBuilderFieldsSet |= 0x2000; 589 mHeight = value; 590 return this; 591 } 592 593 /** 594 * Sets video frame rate. 595 * @param value the video frame rate. -1 indicates the value is unknown. 596 */ setVideoFrameRate( @loatRangefrom = -1, to = Float.MAX_VALUE) float value)597 public @NonNull Builder setVideoFrameRate( 598 @FloatRange(from = -1, to = Float.MAX_VALUE) float value) { 599 checkNotUsed(); 600 mVideoFrameRate = value; 601 return this; 602 } 603 604 /** 605 * Sets metrics-related information that is not supported by dedicated 606 * methods. 607 * <p>It is intended to be used for backwards compatibility by the 608 * metrics infrastructure. 609 */ setMetricsBundle(@onNull Bundle metricsBundle)610 public @NonNull Builder setMetricsBundle(@NonNull Bundle metricsBundle) { 611 mMetricsBundle = metricsBundle; 612 return this; 613 } 614 615 /** Builds the instance. This builder should not be touched after calling this! */ build()616 public @NonNull TrackChangeEvent build() { 617 checkNotUsed(); 618 mBuilderFieldsSet |= 0x4000; // Mark builder used 619 620 TrackChangeEvent o = new TrackChangeEvent( 621 mState, 622 mReason, 623 mContainerMimeType, 624 mSampleMimeType, 625 mCodecName, 626 mBitrate, 627 mTimeSinceCreatedMillis, 628 mType, 629 mLanguage, 630 mLanguageRegion, 631 mChannelCount, 632 mAudioSampleRate, 633 mWidth, 634 mHeight, 635 mVideoFrameRate, 636 mMetricsBundle); 637 return o; 638 } 639 checkNotUsed()640 private void checkNotUsed() { 641 if ((mBuilderFieldsSet & 0x4000) != 0) { 642 throw new IllegalStateException( 643 "This Builder should not be reused. Use a new Builder instance instead"); 644 } 645 } 646 } 647 } 648