1 /**
2  * Copyright (C) 2024 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.cujcommon.cts;
18 
19 import android.os.Looper;
20 import android.util.Log;
21 
22 import androidx.annotation.NonNull;
23 import androidx.media3.common.Player;
24 
25 import java.time.Clock;
26 import java.time.Duration;
27 import java.util.Random;
28 
29 public class SeekTestPlayerListener extends PlayerListener {
30 
31   private static final String LOG_TAG = SeekTestPlayerListener.class.getSimpleName();
32 
33   private final int mNumOfSeekIteration;
34   private long mSeekTimeUs;
35   private final long mSeed;
36   private boolean mSeekDone;
37 
SeekTestPlayerListener(int numOfSeekIteration, long seekTimeUs, long sendMessagePosition)38   public SeekTestPlayerListener(int numOfSeekIteration, long seekTimeUs,
39       long sendMessagePosition) {
40     super();
41     this.mNumOfSeekIteration = numOfSeekIteration;
42     this.mSeekTimeUs = seekTimeUs;
43     this.mSeed = getSeed();
44     this.mSendMessagePosition = sendMessagePosition;
45   }
46 
47   /**
48    * Returns seed for Seek test.
49    */
getSeed()50   private long getSeed() {
51     // Truncate time to the nearest day.
52     long seed = Clock.tick(Clock.systemUTC(), Duration.ofDays(1)).instant().toEpochMilli();
53     Log.d(LOG_TAG, "Random seed = " + seed);
54     return seed;
55   }
56 
57   /**
58    * Seek the player.
59    */
seek()60   private void seek() {
61     Random random = new Random(mSeed);
62     // If number of seek requested is one then seek forward or backward alternatively for
63     // mSeekTimeUs on given media list.
64     // If number of seek requested is 30 then seek for mSeekTimeUs- forward 10 times,
65     // backward 10 times and then randomly backwards or forwards 10 times on each
66     // media item.
67     for (int i = 0; i < mNumOfSeekIteration; i++) {
68       mActivity.mPlayer.seekTo(mActivity.mPlayer.getCurrentPosition() + mSeekTimeUs);
69       if (mNumOfSeekIteration == 1 || i == 10) {
70         mSeekTimeUs *= -1;
71       } else if (i >= 20) {
72         mSeekTimeUs *= random.nextBoolean() ? -1 : 1;
73       }
74     }
75     mSeekDone = true;
76   }
77 
78   @Override
getTestType()79   public TestType getTestType() {
80     return TestType.SEEK_TEST;
81   }
82 
83   @Override
onEventsPlaybackStateChanged(@onNull Player player)84   public void onEventsPlaybackStateChanged(@NonNull Player player) {
85     if (player.getPlaybackState() == Player.STATE_READY) {
86       // Add change in duration due to seek
87       if (mSeekDone) {
88         mExpectedTotalTime += (mSendMessagePosition - player.getCurrentPosition());
89         mSeekDone = false;
90       } else {
91         // At the first media transition player is not ready. So, add duration of
92         // first clip when player is ready
93         mExpectedTotalTime += player.getDuration();
94       }
95     }
96   }
97 
98   @Override
onEventsMediaItemTransition(@onNull Player player)99   public void onEventsMediaItemTransition(@NonNull Player player) {
100     mActivity.mPlayer.createMessage((messageType, payload) -> {
101           seek();
102         }).setLooper(Looper.getMainLooper()).setPosition(mSendMessagePosition)
103         .setDeleteAfterDelivery(true)
104         .send();
105   }
106 }
107