1 /* 2 * Copyright (C) 2011 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 package android.media.cts; 17 18 import static org.junit.Assert.assertEquals; 19 import static org.junit.Assert.assertFalse; 20 import static org.junit.Assert.assertNotNull; 21 import static org.junit.Assert.assertTrue; 22 import static org.junit.Assert.fail; 23 24 import android.app.Activity; 25 import android.app.Instrumentation; 26 import android.content.Context; 27 import android.content.pm.PackageManager; 28 import android.content.res.AssetFileDescriptor; 29 import android.media.MediaPlayer; 30 import android.media.cts.TestUtils.Monitor; 31 import android.net.Uri; 32 import android.os.ConditionVariable; 33 import android.os.ParcelFileDescriptor; 34 import android.os.PersistableBundle; 35 36 import androidx.annotation.CallSuper; 37 import androidx.test.core.app.ActivityScenario; 38 import androidx.test.platform.app.InstrumentationRegistry; 39 40 import com.android.compatibility.common.util.MediaUtils; 41 42 import java.io.File; 43 import java.io.FileNotFoundException; 44 import java.io.IOException; 45 import java.net.HttpCookie; 46 import java.util.List; 47 import java.util.logging.Logger; 48 import java.util.Map; 49 import java.util.Set; 50 51 import org.junit.After; 52 import org.junit.Before; 53 54 /** 55 * Base class for tests which use MediaPlayer to play audio or video. 56 */ 57 public class MediaPlayerTestBase extends MediaTestBase { 58 private static final Logger LOG = Logger.getLogger(MediaPlayerTestBase.class.getName()); 59 60 protected Monitor mOnVideoSizeChangedCalled = new Monitor(); 61 protected Monitor mOnVideoRenderingStartCalled = new Monitor(); 62 protected Monitor mOnBufferingUpdateCalled = new Monitor(); 63 protected Monitor mOnPrepareCalled = new Monitor(); 64 protected Monitor mOnSeekCompleteCalled = new Monitor(); 65 protected Monitor mOnCompletionCalled = new Monitor(); 66 protected Monitor mOnInfoCalled = new Monitor(); 67 protected Monitor mOnErrorCalled = new Monitor(); 68 69 protected MediaPlayer mMediaPlayer = null; 70 protected MediaPlayer mMediaPlayer2 = null; 71 72 @Before 73 @Override setUp()74 public void setUp() throws Throwable { 75 super.setUp(); 76 runOnUiThread(() -> { 77 mMediaPlayer = new MediaPlayer(); 78 mMediaPlayer2 = new MediaPlayer(); 79 }); 80 } 81 @After 82 @Override tearDown()83 public void tearDown() { 84 if (mMediaPlayer != null) { 85 mMediaPlayer.release(); 86 mMediaPlayer = null; 87 } 88 if (mMediaPlayer2 != null) { 89 mMediaPlayer2.release(); 90 mMediaPlayer2 = null; 91 } 92 super.tearDown(); 93 } 94 runOnUiThread(Runnable runnable)95 protected void runOnUiThread(Runnable runnable) throws Throwable { 96 Throwable[] throwableHolder = new Throwable[1]; 97 getInstrumentation().runOnMainSync(() -> { 98 try { 99 runnable.run(); 100 } catch (Throwable throwable) { 101 throwableHolder[0] = throwable; 102 } 103 }); 104 if (throwableHolder[0] != null) { 105 throw throwableHolder[0]; 106 } 107 } 108 playLiveVideoTest(String path, int playTime)109 protected void playLiveVideoTest(String path, int playTime) throws Exception { 110 playVideoWithRetries(path, null, null, playTime); 111 } 112 playLiveAudioOnlyTest(String path, int playTime)113 protected void playLiveAudioOnlyTest(String path, int playTime) throws Exception { 114 playVideoWithRetries(path, -1, -1, playTime); 115 } 116 playVideoTest(String path, int width, int height)117 protected void playVideoTest(String path, int width, int height) throws Exception { 118 playVideoWithRetries(path, width, height, 0); 119 } 120 playVideoWithRetries(String path, Integer width, Integer height, int playTime)121 protected void playVideoWithRetries(String path, Integer width, Integer height, int playTime) 122 throws Exception { 123 boolean playedSuccessfully = false; 124 for (int i = 0; i < STREAM_RETRIES; i++) { 125 try { 126 mMediaPlayer.reset(); 127 mMediaPlayer.setDataSource(path); 128 playLoadedVideo(width, height, playTime); 129 playedSuccessfully = true; 130 break; 131 } catch (PrepareFailedException e) { 132 // prepare() can fail because of network issues, so try again 133 LOG.warning("prepare() failed on try " + i + ", trying playback again"); 134 } 135 } 136 assertTrue("Stream did not play successfully after all attempts", playedSuccessfully); 137 } 138 playLiveVideoTest( Uri uri, Map<String, String> headers, List<HttpCookie> cookies, int playTime)139 protected void playLiveVideoTest( 140 Uri uri, Map<String, String> headers, List<HttpCookie> cookies, 141 int playTime) throws Exception { 142 playVideoWithRetries(uri, headers, cookies, null /* width */, null /* height */, playTime); 143 } 144 playLiveAudioOnlyTest( Uri uri, Map<String, String> headers, List<HttpCookie> cookies, int playTime)145 protected void playLiveAudioOnlyTest( 146 Uri uri, Map<String, String> headers, List<HttpCookie> cookies, 147 int playTime) throws Exception { 148 playVideoWithRetries(uri, headers, cookies, -1 /* width */, -1 /* height */, playTime); 149 } 150 playVideoWithRetries( Uri uri, Map<String, String> headers, List<HttpCookie> cookies, Integer width, Integer height, int playTime)151 protected void playVideoWithRetries( 152 Uri uri, Map<String, String> headers, List<HttpCookie> cookies, 153 Integer width, Integer height, int playTime) throws Exception { 154 boolean playedSuccessfully = false; 155 for (int i = 0; i < STREAM_RETRIES; i++) { 156 try { 157 mMediaPlayer.reset(); 158 mMediaPlayer.setDataSource( 159 getInstrumentation().getTargetContext(), 160 uri, 161 headers, 162 cookies); 163 playLoadedVideo(width, height, playTime); 164 playedSuccessfully = true; 165 break; 166 } catch (PrepareFailedException e) { 167 // prepare() can fail because of network issues, so try again 168 // playLoadedVideo already has reset the player so we can try again safely. 169 LOG.warning("prepare() failed on try " + i + ", trying playback again"); 170 } 171 } 172 assertTrue("Stream did not play successfully after all attempts", playedSuccessfully); 173 } 174 175 /** 176 * Play a video which has already been loaded with setDataSource(). 177 * 178 * @param width width of the video to verify, or null to skip verification 179 * @param height height of the video to verify, or null to skip verification 180 * @param playTime length of time to play video, or 0 to play entire video. 181 * with a non-negative value, this method stops the playback after the length of 182 * time or the duration the video is elapsed. With a value of -1, 183 * this method simply starts the video and returns immediately without 184 * stoping the video playback. 185 */ playLoadedVideo(final Integer width, final Integer height, int playTime)186 protected void playLoadedVideo(final Integer width, final Integer height, int playTime) 187 throws Exception { 188 final float leftVolume = 0.5f; 189 final float rightVolume = 0.5f; 190 191 boolean audioOnly = (width != null && width == -1) || 192 (height != null && height == -1); 193 194 mMediaPlayer.setDisplay(mActivity.getSurfaceHolder()); 195 mMediaPlayer.setScreenOnWhilePlaying(true); 196 mMediaPlayer.setOnVideoSizeChangedListener((mp, w, h) -> { 197 if (w == 0 && h == 0) { 198 // A size of 0x0 can be sent initially one time when using NuPlayer. 199 assertFalse(mOnVideoSizeChangedCalled.isSignalled()); 200 return; 201 } 202 mOnVideoSizeChangedCalled.signal(); 203 if (width != null) { 204 assertEquals(width.intValue(), w); 205 } 206 if (height != null) { 207 assertEquals(height.intValue(), h); 208 } 209 }); 210 mMediaPlayer.setOnErrorListener((mp, what, extra) -> { 211 fail("Media player had error " + what + " playing video"); 212 return true; 213 }); 214 mMediaPlayer.setOnInfoListener((mp, what, extra) -> { 215 if (what == MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START) { 216 mOnVideoRenderingStartCalled.signal(); 217 } 218 return true; 219 }); 220 try { 221 mMediaPlayer.prepare(); 222 } catch (IOException e) { 223 mMediaPlayer.reset(); 224 throw new PrepareFailedException(); 225 } 226 227 mMediaPlayer.start(); 228 if (!audioOnly) { 229 mOnVideoSizeChangedCalled.waitForSignal(); 230 mOnVideoRenderingStartCalled.waitForSignal(); 231 } 232 mMediaPlayer.setVolume(leftVolume, rightVolume); 233 234 // waiting to complete 235 if (playTime == -1) { 236 return; 237 } else if (playTime == 0) { 238 while (mMediaPlayer.isPlaying()) { 239 Thread.sleep(SLEEP_TIME); 240 } 241 } else { 242 Thread.sleep(playTime); 243 } 244 245 // validate a few MediaMetrics. 246 PersistableBundle metrics = mMediaPlayer.getMetrics(); 247 if (metrics == null) { 248 fail("MediaPlayer.getMetrics() returned null metrics"); 249 } else if (metrics.isEmpty()) { 250 fail("MediaPlayer.getMetrics() returned empty metrics"); 251 } else { 252 253 int size = metrics.size(); 254 Set<String> keys = metrics.keySet(); 255 256 if (keys == null) { 257 fail("MediaMetricsSet returned no keys"); 258 } else if (keys.size() != size) { 259 fail("MediaMetricsSet.keys().size() mismatch MediaMetricsSet.size()"); 260 } 261 262 // we played something; so one of these should be non-null 263 String vmime = metrics.getString(MediaPlayer.MetricsConstants.MIME_TYPE_VIDEO, null); 264 String amime = metrics.getString(MediaPlayer.MetricsConstants.MIME_TYPE_AUDIO, null); 265 if (vmime == null && amime == null) { 266 fail("getMetrics() returned neither video nor audio mime value"); 267 } 268 269 long duration = metrics.getLong(MediaPlayer.MetricsConstants.DURATION, -2); 270 if (duration == -2) { 271 fail("getMetrics() didn't return a duration"); 272 } 273 long playing = metrics.getLong(MediaPlayer.MetricsConstants.PLAYING, -2); 274 if (playing == -2) { 275 fail("getMetrics() didn't return a playing time"); 276 } 277 if (!keys.contains(MediaPlayer.MetricsConstants.PLAYING)) { 278 fail("MediaMetricsSet.keys() missing: " + MediaPlayer.MetricsConstants.PLAYING); 279 } 280 } 281 282 mMediaPlayer.stop(); 283 } 284 285 private static class PrepareFailedException extends Exception {} 286 setOnErrorListener()287 protected void setOnErrorListener() { 288 mMediaPlayer.setOnErrorListener((mp, what, extra) -> { 289 mOnErrorCalled.signal(); 290 return false; 291 }); 292 } 293 } 294