1 /* 2 * Copyright (C) 2006 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; 18 19 import android.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.app.ActivityThread; 23 import android.compat.annotation.UnsupportedAppUsage; 24 import android.content.ContentProvider; 25 import android.content.ContentResolver; 26 import android.content.Context; 27 import android.content.res.AssetFileDescriptor; 28 import android.graphics.SurfaceTexture; 29 import android.media.SubtitleController.Anchor; 30 import android.media.SubtitleTrack.RenderingWidget; 31 import android.net.Uri; 32 import android.os.Handler; 33 import android.os.HandlerThread; 34 import android.os.IBinder; 35 import android.os.Looper; 36 import android.os.Message; 37 import android.os.Parcel; 38 import android.os.Parcelable; 39 import android.os.PersistableBundle; 40 import android.os.PowerManager; 41 import android.os.Process; 42 import android.os.SystemProperties; 43 import android.provider.Settings; 44 import android.system.ErrnoException; 45 import android.system.Os; 46 import android.system.OsConstants; 47 import android.util.ArrayMap; 48 import android.util.Log; 49 import android.util.Pair; 50 import android.view.Surface; 51 import android.view.SurfaceHolder; 52 import android.widget.VideoView; 53 54 import com.android.internal.annotations.GuardedBy; 55 import com.android.internal.util.Preconditions; 56 57 import libcore.io.IoBridge; 58 import libcore.io.Streams; 59 60 import java.io.ByteArrayOutputStream; 61 import java.io.File; 62 import java.io.FileDescriptor; 63 import java.io.FileInputStream; 64 import java.io.IOException; 65 import java.io.InputStream; 66 import java.lang.annotation.Retention; 67 import java.lang.annotation.RetentionPolicy; 68 import java.lang.ref.WeakReference; 69 import java.net.CookieHandler; 70 import java.net.CookieManager; 71 import java.net.HttpCookie; 72 import java.net.HttpURLConnection; 73 import java.net.InetSocketAddress; 74 import java.net.URL; 75 import java.nio.ByteOrder; 76 import java.util.Arrays; 77 import java.util.BitSet; 78 import java.util.HashMap; 79 import java.util.List; 80 import java.util.Map; 81 import java.util.Scanner; 82 import java.util.Set; 83 import java.util.UUID; 84 import java.util.Vector; 85 86 87 /** 88 * MediaPlayer class can be used to control playback 89 * of audio/video files and streams. An example on how to use the methods in 90 * this class can be found in {@link android.widget.VideoView}. 91 * 92 * <p>Topics covered here are: 93 * <ol> 94 * <li><a href="#StateDiagram">State Diagram</a> 95 * <li><a href="#Valid_and_Invalid_States">Valid and Invalid States</a> 96 * <li><a href="#Permissions">Permissions</a> 97 * <li><a href="#Callbacks">Register informational and error callbacks</a> 98 * </ol> 99 * 100 * <div class="special reference"> 101 * <h3>Developer Guides</h3> 102 * <p>For more information about how to use MediaPlayer, read the 103 * <a href="{@docRoot}guide/topics/media/mediaplayer.html">Media Playback</a> developer guide.</p> 104 * </div> 105 * 106 * <a name="StateDiagram"></a> 107 * <h3>State Diagram</h3> 108 * 109 * <p>Playback control of audio/video files and streams is managed as a state 110 * machine. The following diagram shows the life cycle and the states of a 111 * MediaPlayer object driven by the supported playback control operations. 112 * The ovals represent the states a MediaPlayer object may reside 113 * in. The arcs represent the playback control operations that drive the object 114 * state transition. There are two types of arcs. The arcs with a single arrow 115 * head represent synchronous method calls, while those with 116 * a double arrow head represent asynchronous method calls.</p> 117 * 118 * <p><img src="../../../images/mediaplayer_state_diagram.gif" 119 * alt="MediaPlayer State diagram" 120 * border="0" /></p> 121 * 122 * <p>From this state diagram, one can see that a MediaPlayer object has the 123 * following states:</p> 124 * <ul> 125 * <li>When a MediaPlayer object is just created using <code>new</code> or 126 * after {@link #reset()} is called, it is in the <em>Idle</em> state; and after 127 * {@link #release()} is called, it is in the <em>End</em> state. Between these 128 * two states is the life cycle of the MediaPlayer object. 129 * <ul> 130 * <li>There is a subtle but important difference between a newly constructed 131 * MediaPlayer object and the MediaPlayer object after {@link #reset()} 132 * is called. It is a programming error to invoke methods such 133 * as {@link #getCurrentPosition()}, 134 * {@link #getDuration()}, {@link #getVideoHeight()}, 135 * {@link #getVideoWidth()}, {@link #setAudioAttributes(AudioAttributes)}, 136 * {@link #setLooping(boolean)}, 137 * {@link #setVolume(float, float)}, {@link #pause()}, {@link #start()}, 138 * {@link #stop()}, {@link #seekTo(long, int)}, {@link #prepare()} or 139 * {@link #prepareAsync()} in the <em>Idle</em> state for both cases. If any of these 140 * methods is called right after a MediaPlayer object is constructed, 141 * the user supplied callback method OnErrorListener.onError() won't be 142 * called by the internal player engine and the object state remains 143 * unchanged; but if these methods are called right after {@link #reset()}, 144 * the user supplied callback method OnErrorListener.onError() will be 145 * invoked by the internal player engine and the object will be 146 * transfered to the <em>Error</em> state. </li> 147 * <li>It is also recommended that once 148 * a MediaPlayer object is no longer being used, call {@link #release()} immediately 149 * so that resources used by the internal player engine associated with the 150 * MediaPlayer object can be released immediately. Resource may include 151 * singleton resources such as hardware acceleration components and 152 * failure to call {@link #release()} may cause subsequent instances of 153 * MediaPlayer objects to fallback to software implementations or fail 154 * altogether. Once the MediaPlayer 155 * object is in the <em>End</em> state, it can no longer be used and 156 * there is no way to bring it back to any other state. </li> 157 * <li>Furthermore, 158 * the MediaPlayer objects created using <code>new</code> is in the 159 * <em>Idle</em> state, while those created with one 160 * of the overloaded convenient <code>create</code> methods are <em>NOT</em> 161 * in the <em>Idle</em> state. In fact, the objects are in the <em>Prepared</em> 162 * state if the creation using <code>create</code> method is successful. 163 * </li> 164 * </ul> 165 * </li> 166 * <li>In general, some playback control operation may fail due to various 167 * reasons, such as unsupported audio/video format, poorly interleaved 168 * audio/video, resolution too high, streaming timeout, and the like. 169 * Thus, error reporting and recovery is an important concern under 170 * these circumstances. Sometimes, due to programming errors, invoking a playback 171 * control operation in an invalid state may also occur. Under all these 172 * error conditions, the internal player engine invokes a user supplied 173 * OnErrorListener.onError() method if an OnErrorListener has been 174 * registered beforehand via 175 * {@link #setOnErrorListener(android.media.MediaPlayer.OnErrorListener)}. 176 * <ul> 177 * <li>It is important to note that once an error occurs, the 178 * MediaPlayer object enters the <em>Error</em> state (except as noted 179 * above), even if an error listener has not been registered by the application.</li> 180 * <li>In order to reuse a MediaPlayer object that is in the <em> 181 * Error</em> state and recover from the error, 182 * {@link #reset()} can be called to restore the object to its <em>Idle</em> 183 * state.</li> 184 * <li>It is good programming practice to have your application 185 * register a OnErrorListener to look out for error notifications from 186 * the internal player engine.</li> 187 * <li>IllegalStateException is 188 * thrown to prevent programming errors such as calling {@link #prepare()}, 189 * {@link #prepareAsync()}, or one of the overloaded <code>setDataSource 190 * </code> methods in an invalid state. </li> 191 * </ul> 192 * </li> 193 * <li>Calling 194 * {@link #setDataSource(FileDescriptor)}, or 195 * {@link #setDataSource(String)}, or 196 * {@link #setDataSource(Context, Uri)}, or 197 * {@link #setDataSource(FileDescriptor, long, long)}, or 198 * {@link #setDataSource(MediaDataSource)} transfers a 199 * MediaPlayer object in the <em>Idle</em> state to the 200 * <em>Initialized</em> state. 201 * <ul> 202 * <li>An IllegalStateException is thrown if 203 * setDataSource() is called in any other state.</li> 204 * <li>It is good programming 205 * practice to always look out for <code>IllegalArgumentException</code> 206 * and <code>IOException</code> that may be thrown from the overloaded 207 * <code>setDataSource</code> methods.</li> 208 * </ul> 209 * </li> 210 * <li>A MediaPlayer object must first enter the <em>Prepared</em> state 211 * before playback can be started. 212 * <ul> 213 * <li>There are two ways (synchronous vs. 214 * asynchronous) that the <em>Prepared</em> state can be reached: 215 * either a call to {@link #prepare()} (synchronous) which 216 * transfers the object to the <em>Prepared</em> state once the method call 217 * returns, or a call to {@link #prepareAsync()} (asynchronous) which 218 * first transfers the object to the <em>Preparing</em> state after the 219 * call returns (which occurs almost right away) while the internal 220 * player engine continues working on the rest of preparation work 221 * until the preparation work completes. When the preparation completes or when {@link #prepare()} call returns, 222 * the internal player engine then calls a user supplied callback method, 223 * onPrepared() of the OnPreparedListener interface, if an 224 * OnPreparedListener is registered beforehand via {@link 225 * #setOnPreparedListener(android.media.MediaPlayer.OnPreparedListener)}.</li> 226 * <li>It is important to note that 227 * the <em>Preparing</em> state is a transient state, and the behavior 228 * of calling any method with side effect while a MediaPlayer object is 229 * in the <em>Preparing</em> state is undefined.</li> 230 * <li>An IllegalStateException is 231 * thrown if {@link #prepare()} or {@link #prepareAsync()} is called in 232 * any other state.</li> 233 * <li>While in the <em>Prepared</em> state, properties 234 * such as audio/sound volume, screenOnWhilePlaying, looping can be 235 * adjusted by invoking the corresponding set methods.</li> 236 * </ul> 237 * </li> 238 * <li>To start the playback, {@link #start()} must be called. After 239 * {@link #start()} returns successfully, the MediaPlayer object is in the 240 * <em>Started</em> state. {@link #isPlaying()} can be called to test 241 * whether the MediaPlayer object is in the <em>Started</em> state. 242 * <ul> 243 * <li>While in the <em>Started</em> state, the internal player engine calls 244 * a user supplied OnBufferingUpdateListener.onBufferingUpdate() callback 245 * method if a OnBufferingUpdateListener has been registered beforehand 246 * via {@link #setOnBufferingUpdateListener(OnBufferingUpdateListener)}. 247 * This callback allows applications to keep track of the buffering status 248 * while streaming audio/video.</li> 249 * <li>Calling {@link #start()} has not effect 250 * on a MediaPlayer object that is already in the <em>Started</em> state.</li> 251 * </ul> 252 * </li> 253 * <li>Playback can be paused and stopped, and the current playback position 254 * can be adjusted. Playback can be paused via {@link #pause()}. When the call to 255 * {@link #pause()} returns, the MediaPlayer object enters the 256 * <em>Paused</em> state. Note that the transition from the <em>Started</em> 257 * state to the <em>Paused</em> state and vice versa happens 258 * asynchronously in the player engine. It may take some time before 259 * the state is updated in calls to {@link #isPlaying()}, and it can be 260 * a number of seconds in the case of streamed content. 261 * <ul> 262 * <li>Calling {@link #start()} to resume playback for a paused 263 * MediaPlayer object, and the resumed playback 264 * position is the same as where it was paused. When the call to 265 * {@link #start()} returns, the paused MediaPlayer object goes back to 266 * the <em>Started</em> state.</li> 267 * <li>Calling {@link #pause()} has no effect on 268 * a MediaPlayer object that is already in the <em>Paused</em> state.</li> 269 * </ul> 270 * </li> 271 * <li>Calling {@link #stop()} stops playback and causes a 272 * MediaPlayer in the <em>Started</em>, <em>Paused</em>, <em>Prepared 273 * </em> or <em>PlaybackCompleted</em> state to enter the 274 * <em>Stopped</em> state. 275 * <ul> 276 * <li>Once in the <em>Stopped</em> state, playback cannot be started 277 * until {@link #prepare()} or {@link #prepareAsync()} are called to set 278 * the MediaPlayer object to the <em>Prepared</em> state again.</li> 279 * <li>Calling {@link #stop()} has no effect on a MediaPlayer 280 * object that is already in the <em>Stopped</em> state.</li> 281 * </ul> 282 * </li> 283 * <li>The playback position can be adjusted with a call to 284 * {@link #seekTo(long, int)}. 285 * <ul> 286 * <li>Although the asynchronuous {@link #seekTo(long, int)} 287 * call returns right away, the actual seek operation may take a while to 288 * finish, especially for audio/video being streamed. When the actual 289 * seek operation completes, the internal player engine calls a user 290 * supplied OnSeekComplete.onSeekComplete() if an OnSeekCompleteListener 291 * has been registered beforehand via 292 * {@link #setOnSeekCompleteListener(OnSeekCompleteListener)}.</li> 293 * <li>Please 294 * note that {@link #seekTo(long, int)} can also be called in the other states, 295 * such as <em>Prepared</em>, <em>Paused</em> and <em>PlaybackCompleted 296 * </em> state. When {@link #seekTo(long, int)} is called in those states, 297 * one video frame will be displayed if the stream has video and the requested 298 * position is valid. 299 * </li> 300 * <li>Furthermore, the actual current playback position 301 * can be retrieved with a call to {@link #getCurrentPosition()}, which 302 * is helpful for applications such as a Music player that need to keep 303 * track of the playback progress.</li> 304 * </ul> 305 * </li> 306 * <li>When the playback reaches the end of stream, the playback completes. 307 * <ul> 308 * <li>If the looping mode was being set to <var>true</var>with 309 * {@link #setLooping(boolean)}, the MediaPlayer object shall remain in 310 * the <em>Started</em> state.</li> 311 * <li>If the looping mode was set to <var>false 312 * </var>, the player engine calls a user supplied callback method, 313 * OnCompletion.onCompletion(), if a OnCompletionListener is registered 314 * beforehand via {@link #setOnCompletionListener(OnCompletionListener)}. 315 * The invoke of the callback signals that the object is now in the <em> 316 * PlaybackCompleted</em> state.</li> 317 * <li>While in the <em>PlaybackCompleted</em> 318 * state, calling {@link #start()} can restart the playback from the 319 * beginning of the audio/video source.</li> 320 * </ul> 321 * 322 * 323 * <a name="Valid_and_Invalid_States"></a> 324 * <h3>Valid and invalid states</h3> 325 * 326 * <table border="0" cellspacing="0" cellpadding="0"> 327 * <tr><td>Method Name </p></td> 328 * <td>Valid States </p></td> 329 * <td>Invalid States </p></td> 330 * <td>Comments </p></td></tr> 331 * <tr><td>attachAuxEffect </p></td> 332 * <td>{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted} </p></td> 333 * <td>{Idle, Error} </p></td> 334 * <td>This method must be called after setDataSource. 335 * Calling it does not change the object state. </p></td></tr> 336 * <tr><td>getAudioSessionId </p></td> 337 * <td>any </p></td> 338 * <td>{} </p></td> 339 * <td>This method can be called in any state and calling it does not change 340 * the object state. </p></td></tr> 341 * <tr><td>getCurrentPosition </p></td> 342 * <td>{Idle, Initialized, Prepared, Started, Paused, Stopped, 343 * PlaybackCompleted} </p></td> 344 * <td>{Error}</p></td> 345 * <td>Successful invoke of this method in a valid state does not change the 346 * state. Calling this method in an invalid state transfers the object 347 * to the <em>Error</em> state. </p></td></tr> 348 * <tr><td>getDuration </p></td> 349 * <td>{Prepared, Started, Paused, Stopped, PlaybackCompleted} </p></td> 350 * <td>{Idle, Initialized, Error} </p></td> 351 * <td>Successful invoke of this method in a valid state does not change the 352 * state. Calling this method in an invalid state transfers the object 353 * to the <em>Error</em> state. </p></td></tr> 354 * <tr><td>getVideoHeight </p></td> 355 * <td>{Idle, Initialized, Prepared, Started, Paused, Stopped, 356 * PlaybackCompleted}</p></td> 357 * <td>{Error}</p></td> 358 * <td>Successful invoke of this method in a valid state does not change the 359 * state. Calling this method in an invalid state transfers the object 360 * to the <em>Error</em> state. </p></td></tr> 361 * <tr><td>getVideoWidth </p></td> 362 * <td>{Idle, Initialized, Prepared, Started, Paused, Stopped, 363 * PlaybackCompleted}</p></td> 364 * <td>{Error}</p></td> 365 * <td>Successful invoke of this method in a valid state does not change 366 * the state. Calling this method in an invalid state transfers the 367 * object to the <em>Error</em> state. </p></td></tr> 368 * <tr><td>isPlaying </p></td> 369 * <td>{Idle, Initialized, Prepared, Started, Paused, Stopped, 370 * PlaybackCompleted}</p></td> 371 * <td>{Error}</p></td> 372 * <td>Successful invoke of this method in a valid state does not change 373 * the state. Calling this method in an invalid state transfers the 374 * object to the <em>Error</em> state. </p></td></tr> 375 * <tr><td>pause </p></td> 376 * <td>{Started, Paused, PlaybackCompleted}</p></td> 377 * <td>{Idle, Initialized, Prepared, Stopped, Error}</p></td> 378 * <td>Successful invoke of this method in a valid state transfers the 379 * object to the <em>Paused</em> state. Calling this method in an 380 * invalid state transfers the object to the <em>Error</em> state.</p></td></tr> 381 * <tr><td>prepare </p></td> 382 * <td>{Initialized, Stopped} </p></td> 383 * <td>{Idle, Prepared, Started, Paused, PlaybackCompleted, Error} </p></td> 384 * <td>Successful invoke of this method in a valid state transfers the 385 * object to the <em>Prepared</em> state. Calling this method in an 386 * invalid state throws an IllegalStateException.</p></td></tr> 387 * <tr><td>prepareAsync </p></td> 388 * <td>{Initialized, Stopped} </p></td> 389 * <td>{Idle, Prepared, Started, Paused, PlaybackCompleted, Error} </p></td> 390 * <td>Successful invoke of this method in a valid state transfers the 391 * object to the <em>Preparing</em> state. Calling this method in an 392 * invalid state throws an IllegalStateException.</p></td></tr> 393 * <tr><td>release </p></td> 394 * <td>any </p></td> 395 * <td>{} </p></td> 396 * <td>After {@link #release()}, the object is no longer available. </p></td></tr> 397 * <tr><td>reset </p></td> 398 * <td>{Idle, Initialized, Prepared, Started, Paused, Stopped, 399 * PlaybackCompleted, Error}</p></td> 400 * <td>{}</p></td> 401 * <td>After {@link #reset()}, the object is like being just created.</p></td></tr> 402 * <tr><td>seekTo </p></td> 403 * <td>{Prepared, Started, Paused, PlaybackCompleted} </p></td> 404 * <td>{Idle, Initialized, Stopped, Error}</p></td> 405 * <td>Successful invoke of this method in a valid state does not change 406 * the state. Calling this method in an invalid state transfers the 407 * object to the <em>Error</em> state. </p></td></tr> 408 * <tr><td>setAudioAttributes </p></td> 409 * <td>{Idle, Initialized, Stopped, Prepared, Started, Paused, 410 * PlaybackCompleted}</p></td> 411 * <td>{Error}</p></td> 412 * <td>Successful invoke of this method does not change the state. In order for the 413 * target audio attributes type to become effective, this method must be called before 414 * prepare() or prepareAsync().</p></td></tr> 415 * <tr><td>setAudioSessionId </p></td> 416 * <td>{Idle} </p></td> 417 * <td>{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted, 418 * Error} </p></td> 419 * <td>This method must be called in idle state as the audio session ID must be known before 420 * calling setDataSource. Calling it does not change the object state. </p></td></tr> 421 * <tr><td>setAudioStreamType (deprecated)</p></td> 422 * <td>{Idle, Initialized, Stopped, Prepared, Started, Paused, 423 * PlaybackCompleted}</p></td> 424 * <td>{Error}</p></td> 425 * <td>Successful invoke of this method does not change the state. In order for the 426 * target audio stream type to become effective, this method must be called before 427 * prepare() or prepareAsync().</p></td></tr> 428 * <tr><td>setAuxEffectSendLevel </p></td> 429 * <td>any</p></td> 430 * <td>{} </p></td> 431 * <td>Calling this method does not change the object state. </p></td></tr> 432 * <tr><td>setDataSource </p></td> 433 * <td>{Idle} </p></td> 434 * <td>{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted, 435 * Error} </p></td> 436 * <td>Successful invoke of this method in a valid state transfers the 437 * object to the <em>Initialized</em> state. Calling this method in an 438 * invalid state throws an IllegalStateException.</p></td></tr> 439 * <tr><td>setDisplay </p></td> 440 * <td>any </p></td> 441 * <td>{} </p></td> 442 * <td>This method can be called in any state and calling it does not change 443 * the object state. </p></td></tr> 444 * <tr><td>setSurface </p></td> 445 * <td>any </p></td> 446 * <td>{} </p></td> 447 * <td>This method can be called in any state and calling it does not change 448 * the object state. </p></td></tr> 449 * <tr><td>setVideoScalingMode </p></td> 450 * <td>{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted} </p></td> 451 * <td>{Idle, Error}</p></td> 452 * <td>Successful invoke of this method does not change the state.</p></td></tr> 453 * <tr><td>setLooping </p></td> 454 * <td>{Idle, Initialized, Stopped, Prepared, Started, Paused, 455 * PlaybackCompleted}</p></td> 456 * <td>{Error}</p></td> 457 * <td>Successful invoke of this method in a valid state does not change 458 * the state. Calling this method in an 459 * invalid state transfers the object to the <em>Error</em> state.</p></td></tr> 460 * <tr><td>isLooping </p></td> 461 * <td>any </p></td> 462 * <td>{} </p></td> 463 * <td>This method can be called in any state and calling it does not change 464 * the object state. </p></td></tr> 465 * <tr><td>setOnBufferingUpdateListener </p></td> 466 * <td>any </p></td> 467 * <td>{} </p></td> 468 * <td>This method can be called in any state and calling it does not change 469 * the object state. </p></td></tr> 470 * <tr><td>setOnCompletionListener </p></td> 471 * <td>any </p></td> 472 * <td>{} </p></td> 473 * <td>This method can be called in any state and calling it does not change 474 * the object state. </p></td></tr> 475 * <tr><td>setOnErrorListener </p></td> 476 * <td>any </p></td> 477 * <td>{} </p></td> 478 * <td>This method can be called in any state and calling it does not change 479 * the object state. </p></td></tr> 480 * <tr><td>setOnPreparedListener </p></td> 481 * <td>any </p></td> 482 * <td>{} </p></td> 483 * <td>This method can be called in any state and calling it does not change 484 * the object state. </p></td></tr> 485 * <tr><td>setOnSeekCompleteListener </p></td> 486 * <td>any </p></td> 487 * <td>{} </p></td> 488 * <td>This method can be called in any state and calling it does not change 489 * the object state. </p></td></tr> 490 * <tr><td>setPlaybackParams</p></td> 491 * <td>{Initialized, Prepared, Started, Paused, PlaybackCompleted, Error}</p></td> 492 * <td>{Idle, Stopped} </p></td> 493 * <td>This method will change state in some cases, depending on when it's called. 494 * </p></td></tr> 495 * <tr><td>setScreenOnWhilePlaying</></td> 496 * <td>any </p></td> 497 * <td>{} </p></td> 498 * <td>This method can be called in any state and calling it does not change 499 * the object state. </p></td></tr> 500 * <tr><td>setVolume </p></td> 501 * <td>{Idle, Initialized, Stopped, Prepared, Started, Paused, 502 * PlaybackCompleted}</p></td> 503 * <td>{Error}</p></td> 504 * <td>Successful invoke of this method does not change the state. 505 * <tr><td>setWakeMode </p></td> 506 * <td>any </p></td> 507 * <td>{} </p></td> 508 * <td>This method can be called in any state and calling it does not change 509 * the object state.</p></td></tr> 510 * <tr><td>start </p></td> 511 * <td>{Prepared, Started, Paused, PlaybackCompleted}</p></td> 512 * <td>{Idle, Initialized, Stopped, Error}</p></td> 513 * <td>Successful invoke of this method in a valid state transfers the 514 * object to the <em>Started</em> state. Calling this method in an 515 * invalid state transfers the object to the <em>Error</em> state.</p></td></tr> 516 * <tr><td>stop </p></td> 517 * <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td> 518 * <td>{Idle, Initialized, Error}</p></td> 519 * <td>Successful invoke of this method in a valid state transfers the 520 * object to the <em>Stopped</em> state. Calling this method in an 521 * invalid state transfers the object to the <em>Error</em> state.</p></td></tr> 522 * <tr><td>getTrackInfo </p></td> 523 * <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td> 524 * <td>{Idle, Initialized, Error}</p></td> 525 * <td>Successful invoke of this method does not change the state.</p></td></tr> 526 * <tr><td>addTimedTextSource </p></td> 527 * <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td> 528 * <td>{Idle, Initialized, Error}</p></td> 529 * <td>Successful invoke of this method does not change the state.</p></td></tr> 530 * <tr><td>selectTrack </p></td> 531 * <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td> 532 * <td>{Idle, Initialized, Error}</p></td> 533 * <td>Successful invoke of this method does not change the state.</p></td></tr> 534 * <tr><td>deselectTrack </p></td> 535 * <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td> 536 * <td>{Idle, Initialized, Error}</p></td> 537 * <td>Successful invoke of this method does not change the state.</p></td></tr> 538 * 539 * </table> 540 * 541 * <a name="Permissions"></a> 542 * <h3>Permissions</h3> 543 * <p>One may need to declare a corresponding WAKE_LOCK permission {@link 544 * android.R.styleable#AndroidManifestUsesPermission <uses-permission>} 545 * element. 546 * 547 * <p>This class requires the {@link android.Manifest.permission#INTERNET} permission 548 * when used with network-based content. 549 * 550 * <a name="Callbacks"></a> 551 * <h3>Callbacks</h3> 552 * <p>Applications may want to register for informational and error 553 * events in order to be informed of some internal state update and 554 * possible runtime errors during playback or streaming. Registration for 555 * these events is done by properly setting the appropriate listeners (via calls 556 * to 557 * {@link #setOnPreparedListener(OnPreparedListener)}setOnPreparedListener, 558 * {@link #setOnVideoSizeChangedListener(OnVideoSizeChangedListener)}setOnVideoSizeChangedListener, 559 * {@link #setOnSeekCompleteListener(OnSeekCompleteListener)}setOnSeekCompleteListener, 560 * {@link #setOnCompletionListener(OnCompletionListener)}setOnCompletionListener, 561 * {@link #setOnBufferingUpdateListener(OnBufferingUpdateListener)}setOnBufferingUpdateListener, 562 * {@link #setOnInfoListener(OnInfoListener)}setOnInfoListener, 563 * {@link #setOnErrorListener(OnErrorListener)}setOnErrorListener, etc). 564 * In order to receive the respective callback 565 * associated with these listeners, applications are required to create 566 * MediaPlayer objects on a thread with its own Looper running (main UI 567 * thread by default has a Looper running). 568 * 569 */ 570 public class MediaPlayer extends PlayerBase 571 implements SubtitleController.Listener 572 , VolumeAutomation 573 , AudioRouting 574 { 575 /** 576 Constant to retrieve only the new metadata since the last 577 call. 578 // FIXME: unhide. 579 // FIXME: add link to getMetadata(boolean, boolean) 580 {@hide} 581 */ 582 public static final boolean METADATA_UPDATE_ONLY = true; 583 584 /** 585 Constant to retrieve all the metadata. 586 // FIXME: unhide. 587 // FIXME: add link to getMetadata(boolean, boolean) 588 {@hide} 589 */ 590 @UnsupportedAppUsage 591 public static final boolean METADATA_ALL = false; 592 593 /** 594 Constant to enable the metadata filter during retrieval. 595 // FIXME: unhide. 596 // FIXME: add link to getMetadata(boolean, boolean) 597 {@hide} 598 */ 599 public static final boolean APPLY_METADATA_FILTER = true; 600 601 /** 602 Constant to disable the metadata filter during retrieval. 603 // FIXME: unhide. 604 // FIXME: add link to getMetadata(boolean, boolean) 605 {@hide} 606 */ 607 @UnsupportedAppUsage 608 public static final boolean BYPASS_METADATA_FILTER = false; 609 610 static { 611 System.loadLibrary("media_jni"); native_init()612 native_init(); 613 } 614 615 private final static String TAG = "MediaPlayer"; 616 // Name of the remote interface for the media player. Must be kept 617 // in sync with the 2nd parameter of the IMPLEMENT_META_INTERFACE 618 // macro invocation in IMediaPlayer.cpp 619 private final static String IMEDIA_PLAYER = "android.media.IMediaPlayer"; 620 621 private long mNativeContext; // accessed by native methods 622 private long mNativeSurfaceTexture; // accessed by native methods 623 private int mListenerContext; // accessed by native methods 624 private SurfaceHolder mSurfaceHolder; 625 @UnsupportedAppUsage 626 private EventHandler mEventHandler; 627 private PowerManager.WakeLock mWakeLock = null; 628 private boolean mScreenOnWhilePlaying; 629 private boolean mStayAwake; 630 private int mStreamType = AudioManager.USE_DEFAULT_STREAM_TYPE; 631 private int mUsage = -1; 632 633 // Modular DRM 634 private UUID mDrmUUID; 635 private final Object mDrmLock = new Object(); 636 private DrmInfo mDrmInfo; 637 private MediaDrm mDrmObj; 638 private byte[] mDrmSessionId; 639 private boolean mDrmInfoResolved; 640 private boolean mActiveDrmScheme; 641 private boolean mDrmConfigAllowed; 642 private boolean mDrmProvisioningInProgress; 643 private boolean mPrepareDrmInProgress; 644 private ProvisioningThread mDrmProvisioningThread; 645 646 /** 647 * Default constructor. Consider using one of the create() methods for 648 * synchronously instantiating a MediaPlayer from a Uri or resource. 649 * <p>When done with the MediaPlayer, you should call {@link #release()}, 650 * to free the resources. If not released, too many MediaPlayer instances may 651 * result in an exception.</p> 652 */ MediaPlayer()653 public MediaPlayer() { 654 super(new AudioAttributes.Builder().build(), 655 AudioPlaybackConfiguration.PLAYER_TYPE_JAM_MEDIAPLAYER); 656 657 Looper looper; 658 if ((looper = Looper.myLooper()) != null) { 659 mEventHandler = new EventHandler(this, looper); 660 } else if ((looper = Looper.getMainLooper()) != null) { 661 mEventHandler = new EventHandler(this, looper); 662 } else { 663 mEventHandler = null; 664 } 665 666 mTimeProvider = new TimeProvider(this); 667 mOpenSubtitleSources = new Vector<InputStream>(); 668 669 /* Native setup requires a weak reference to our object. 670 * It's easier to create it here than in C++. 671 */ 672 native_setup(new WeakReference<MediaPlayer>(this)); 673 674 baseRegisterPlayer(); 675 } 676 677 /* 678 * Update the MediaPlayer SurfaceTexture. 679 * Call after setting a new display surface. 680 */ _setVideoSurface(Surface surface)681 private native void _setVideoSurface(Surface surface); 682 683 /* Do not change these values (starting with INVOKE_ID) without updating 684 * their counterparts in include/media/mediaplayer.h! 685 */ 686 private static final int INVOKE_ID_GET_TRACK_INFO = 1; 687 private static final int INVOKE_ID_ADD_EXTERNAL_SOURCE = 2; 688 private static final int INVOKE_ID_ADD_EXTERNAL_SOURCE_FD = 3; 689 private static final int INVOKE_ID_SELECT_TRACK = 4; 690 private static final int INVOKE_ID_DESELECT_TRACK = 5; 691 private static final int INVOKE_ID_SET_VIDEO_SCALE_MODE = 6; 692 private static final int INVOKE_ID_GET_SELECTED_TRACK = 7; 693 694 /** 695 * Create a request parcel which can be routed to the native media 696 * player using {@link #invoke(Parcel, Parcel)}. The Parcel 697 * returned has the proper InterfaceToken set. The caller should 698 * not overwrite that token, i.e it can only append data to the 699 * Parcel. 700 * 701 * @return A parcel suitable to hold a request for the native 702 * player. 703 * {@hide} 704 */ 705 @UnsupportedAppUsage newRequest()706 public Parcel newRequest() { 707 Parcel parcel = Parcel.obtain(); 708 parcel.writeInterfaceToken(IMEDIA_PLAYER); 709 return parcel; 710 } 711 712 /** 713 * Invoke a generic method on the native player using opaque 714 * parcels for the request and reply. Both payloads' format is a 715 * convention between the java caller and the native player. 716 * Must be called after setDataSource to make sure a native player 717 * exists. On failure, a RuntimeException is thrown. 718 * 719 * @param request Parcel with the data for the extension. The 720 * caller must use {@link #newRequest()} to get one. 721 * 722 * @param reply Output parcel with the data returned by the 723 * native player. 724 * {@hide} 725 */ 726 @UnsupportedAppUsage invoke(Parcel request, Parcel reply)727 public void invoke(Parcel request, Parcel reply) { 728 int retcode = native_invoke(request, reply); 729 reply.setDataPosition(0); 730 if (retcode != 0) { 731 throw new RuntimeException("failure code: " + retcode); 732 } 733 } 734 735 /** 736 * Sets the {@link SurfaceHolder} to use for displaying the video 737 * portion of the media. 738 * 739 * Either a surface holder or surface must be set if a display or video sink 740 * is needed. Not calling this method or {@link #setSurface(Surface)} 741 * when playing back a video will result in only the audio track being played. 742 * A null surface holder or surface will result in only the audio track being 743 * played. 744 * 745 * @param sh the SurfaceHolder to use for video display 746 * @throws IllegalStateException if the internal player engine has not been 747 * initialized or has been released. 748 */ setDisplay(SurfaceHolder sh)749 public void setDisplay(SurfaceHolder sh) { 750 mSurfaceHolder = sh; 751 Surface surface; 752 if (sh != null) { 753 surface = sh.getSurface(); 754 } else { 755 surface = null; 756 } 757 _setVideoSurface(surface); 758 updateSurfaceScreenOn(); 759 } 760 761 /** 762 * Sets the {@link Surface} to be used as the sink for the video portion of 763 * the media. This is similar to {@link #setDisplay(SurfaceHolder)}, but 764 * does not support {@link #setScreenOnWhilePlaying(boolean)}. Setting a 765 * Surface will un-set any Surface or SurfaceHolder that was previously set. 766 * A null surface will result in only the audio track being played. 767 * 768 * If the Surface sends frames to a {@link SurfaceTexture}, the timestamps 769 * returned from {@link SurfaceTexture#getTimestamp()} will have an 770 * unspecified zero point. These timestamps cannot be directly compared 771 * between different media sources, different instances of the same media 772 * source, or multiple runs of the same program. The timestamp is normally 773 * monotonically increasing and is unaffected by time-of-day adjustments, 774 * but it is reset when the position is set. 775 * 776 * @param surface The {@link Surface} to be used for the video portion of 777 * the media. 778 * @throws IllegalStateException if the internal player engine has not been 779 * initialized or has been released. 780 */ setSurface(Surface surface)781 public void setSurface(Surface surface) { 782 if (mScreenOnWhilePlaying && surface != null) { 783 Log.w(TAG, "setScreenOnWhilePlaying(true) is ineffective for Surface"); 784 } 785 mSurfaceHolder = null; 786 _setVideoSurface(surface); 787 updateSurfaceScreenOn(); 788 } 789 790 /* Do not change these video scaling mode values below without updating 791 * their counterparts in system/window.h! Please do not forget to update 792 * {@link #isVideoScalingModeSupported} when new video scaling modes 793 * are added. 794 */ 795 /** 796 * Specifies a video scaling mode. The content is stretched to the 797 * surface rendering area. When the surface has the same aspect ratio 798 * as the content, the aspect ratio of the content is maintained; 799 * otherwise, the aspect ratio of the content is not maintained when video 800 * is being rendered. Unlike {@link #VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING}, 801 * there is no content cropping with this video scaling mode. 802 */ 803 public static final int VIDEO_SCALING_MODE_SCALE_TO_FIT = 1; 804 805 /** 806 * Specifies a video scaling mode. The content is scaled, maintaining 807 * its aspect ratio. The whole surface area is always used. When the 808 * aspect ratio of the content is the same as the surface, no content 809 * is cropped; otherwise, content is cropped to fit the surface. 810 */ 811 public static final int VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING = 2; 812 /** 813 * Sets video scaling mode. To make the target video scaling mode 814 * effective during playback, this method must be called after 815 * data source is set. If not called, the default video 816 * scaling mode is {@link #VIDEO_SCALING_MODE_SCALE_TO_FIT}. 817 * 818 * <p> The supported video scaling modes are: 819 * <ul> 820 * <li> {@link #VIDEO_SCALING_MODE_SCALE_TO_FIT} 821 * <li> {@link #VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING} 822 * </ul> 823 * 824 * @param mode target video scaling mode. Must be one of the supported 825 * video scaling modes; otherwise, IllegalArgumentException will be thrown. 826 * 827 * @see MediaPlayer#VIDEO_SCALING_MODE_SCALE_TO_FIT 828 * @see MediaPlayer#VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING 829 */ setVideoScalingMode(int mode)830 public void setVideoScalingMode(int mode) { 831 if (!isVideoScalingModeSupported(mode)) { 832 final String msg = "Scaling mode " + mode + " is not supported"; 833 throw new IllegalArgumentException(msg); 834 } 835 Parcel request = Parcel.obtain(); 836 Parcel reply = Parcel.obtain(); 837 try { 838 request.writeInterfaceToken(IMEDIA_PLAYER); 839 request.writeInt(INVOKE_ID_SET_VIDEO_SCALE_MODE); 840 request.writeInt(mode); 841 invoke(request, reply); 842 } finally { 843 request.recycle(); 844 reply.recycle(); 845 } 846 } 847 848 /** 849 * Convenience method to create a MediaPlayer for a given Uri. 850 * On success, {@link #prepare()} will already have been called and must not be called again. 851 * <p>When done with the MediaPlayer, you should call {@link #release()}, 852 * to free the resources. If not released, too many MediaPlayer instances will 853 * result in an exception.</p> 854 * <p>Note that since {@link #prepare()} is called automatically in this method, 855 * you cannot change the audio 856 * session ID (see {@link #setAudioSessionId(int)}) or audio attributes 857 * (see {@link #setAudioAttributes(AudioAttributes)} of the new MediaPlayer.</p> 858 * 859 * @param context the Context to use 860 * @param uri the Uri from which to get the datasource 861 * @return a MediaPlayer object, or null if creation failed 862 */ create(Context context, Uri uri)863 public static MediaPlayer create(Context context, Uri uri) { 864 return create (context, uri, null); 865 } 866 867 /** 868 * Convenience method to create a MediaPlayer for a given Uri. 869 * On success, {@link #prepare()} will already have been called and must not be called again. 870 * <p>When done with the MediaPlayer, you should call {@link #release()}, 871 * to free the resources. If not released, too many MediaPlayer instances will 872 * result in an exception.</p> 873 * <p>Note that since {@link #prepare()} is called automatically in this method, 874 * you cannot change the audio 875 * session ID (see {@link #setAudioSessionId(int)}) or audio attributes 876 * (see {@link #setAudioAttributes(AudioAttributes)} of the new MediaPlayer.</p> 877 * 878 * @param context the Context to use 879 * @param uri the Uri from which to get the datasource 880 * @param holder the SurfaceHolder to use for displaying the video 881 * @return a MediaPlayer object, or null if creation failed 882 */ create(Context context, Uri uri, SurfaceHolder holder)883 public static MediaPlayer create(Context context, Uri uri, SurfaceHolder holder) { 884 int s = AudioSystem.newAudioSessionId(); 885 return create(context, uri, holder, null, s > 0 ? s : 0); 886 } 887 888 /** 889 * Same factory method as {@link #create(Context, Uri, SurfaceHolder)} but that lets you specify 890 * the audio attributes and session ID to be used by the new MediaPlayer instance. 891 * @param context the Context to use 892 * @param uri the Uri from which to get the datasource 893 * @param holder the SurfaceHolder to use for displaying the video, may be null. 894 * @param audioAttributes the {@link AudioAttributes} to be used by the media player. 895 * @param audioSessionId the audio session ID to be used by the media player, 896 * see {@link AudioManager#generateAudioSessionId()} to obtain a new session. 897 * @return a MediaPlayer object, or null if creation failed 898 */ create(Context context, Uri uri, SurfaceHolder holder, AudioAttributes audioAttributes, int audioSessionId)899 public static MediaPlayer create(Context context, Uri uri, SurfaceHolder holder, 900 AudioAttributes audioAttributes, int audioSessionId) { 901 902 try { 903 MediaPlayer mp = new MediaPlayer(); 904 final AudioAttributes aa = audioAttributes != null ? audioAttributes : 905 new AudioAttributes.Builder().build(); 906 mp.setAudioAttributes(aa); 907 mp.setAudioSessionId(audioSessionId); 908 mp.setDataSource(context, uri); 909 if (holder != null) { 910 mp.setDisplay(holder); 911 } 912 mp.prepare(); 913 return mp; 914 } catch (IOException ex) { 915 Log.d(TAG, "create failed:", ex); 916 // fall through 917 } catch (IllegalArgumentException ex) { 918 Log.d(TAG, "create failed:", ex); 919 // fall through 920 } catch (SecurityException ex) { 921 Log.d(TAG, "create failed:", ex); 922 // fall through 923 } 924 925 return null; 926 } 927 928 // Note no convenience method to create a MediaPlayer with SurfaceTexture sink. 929 930 /** 931 * Convenience method to create a MediaPlayer for a given resource id. 932 * On success, {@link #prepare()} will already have been called and must not be called again. 933 * <p>When done with the MediaPlayer, you should call {@link #release()}, 934 * to free the resources. If not released, too many MediaPlayer instances will 935 * result in an exception.</p> 936 * <p>Note that since {@link #prepare()} is called automatically in this method, 937 * you cannot change the audio 938 * session ID (see {@link #setAudioSessionId(int)}) or audio attributes 939 * (see {@link #setAudioAttributes(AudioAttributes)} of the new MediaPlayer.</p> 940 * 941 * @param context the Context to use 942 * @param resid the raw resource id (<var>R.raw.<something></var>) for 943 * the resource to use as the datasource 944 * @return a MediaPlayer object, or null if creation failed 945 */ create(Context context, int resid)946 public static MediaPlayer create(Context context, int resid) { 947 int s = AudioSystem.newAudioSessionId(); 948 return create(context, resid, null, s > 0 ? s : 0); 949 } 950 951 /** 952 * Same factory method as {@link #create(Context, int)} but that lets you specify the audio 953 * attributes and session ID to be used by the new MediaPlayer instance. 954 * @param context the Context to use 955 * @param resid the raw resource id (<var>R.raw.<something></var>) for 956 * the resource to use as the datasource 957 * @param audioAttributes the {@link AudioAttributes} to be used by the media player. 958 * @param audioSessionId the audio session ID to be used by the media player, 959 * see {@link AudioManager#generateAudioSessionId()} to obtain a new session. 960 * @return a MediaPlayer object, or null if creation failed 961 */ create(Context context, int resid, AudioAttributes audioAttributes, int audioSessionId)962 public static MediaPlayer create(Context context, int resid, 963 AudioAttributes audioAttributes, int audioSessionId) { 964 try { 965 AssetFileDescriptor afd = context.getResources().openRawResourceFd(resid); 966 if (afd == null) return null; 967 968 MediaPlayer mp = new MediaPlayer(); 969 970 final AudioAttributes aa = audioAttributes != null ? audioAttributes : 971 new AudioAttributes.Builder().build(); 972 mp.setAudioAttributes(aa); 973 mp.setAudioSessionId(audioSessionId); 974 975 mp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength()); 976 afd.close(); 977 mp.prepare(); 978 return mp; 979 } catch (IOException ex) { 980 Log.d(TAG, "create failed:", ex); 981 // fall through 982 } catch (IllegalArgumentException ex) { 983 Log.d(TAG, "create failed:", ex); 984 // fall through 985 } catch (SecurityException ex) { 986 Log.d(TAG, "create failed:", ex); 987 // fall through 988 } 989 return null; 990 } 991 992 /** 993 * Sets the data source as a content Uri. 994 * 995 * @param context the Context to use when resolving the Uri 996 * @param uri the Content URI of the data you want to play 997 * @throws IllegalStateException if it is called in an invalid state 998 */ setDataSource(@onNull Context context, @NonNull Uri uri)999 public void setDataSource(@NonNull Context context, @NonNull Uri uri) 1000 throws IOException, IllegalArgumentException, SecurityException, IllegalStateException { 1001 setDataSource(context, uri, null, null); 1002 } 1003 1004 /** 1005 * Sets the data source as a content Uri. 1006 * 1007 * To provide cookies for the subsequent HTTP requests, you can install your own default cookie 1008 * handler and use other variants of setDataSource APIs instead. Alternatively, you can use 1009 * this API to pass the cookies as a list of HttpCookie. If the app has not installed 1010 * a CookieHandler already, this API creates a CookieManager and populates its CookieStore with 1011 * the provided cookies. If the app has installed its own handler already, this API requires the 1012 * handler to be of CookieManager type such that the API can update the manager’s CookieStore. 1013 * 1014 * <p><strong>Note</strong> that the cross domain redirection is allowed by default, 1015 * but that can be changed with key/value pairs through the headers parameter with 1016 * "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value to 1017 * disallow or allow cross domain redirection. 1018 * 1019 * @param context the Context to use when resolving the Uri 1020 * @param uri the Content URI of the data you want to play 1021 * @param headers the headers to be sent together with the request for the data 1022 * The headers must not include cookies. Instead, use the cookies param. 1023 * @param cookies the cookies to be sent together with the request 1024 * @throws IllegalArgumentException if cookies are provided and the installed handler is not 1025 * a CookieManager 1026 * @throws IllegalStateException if it is called in an invalid state 1027 * @throws NullPointerException if context or uri is null 1028 * @throws IOException if uri has a file scheme and an I/O error occurs 1029 */ setDataSource(@onNull Context context, @NonNull Uri uri, @Nullable Map<String, String> headers, @Nullable List<HttpCookie> cookies)1030 public void setDataSource(@NonNull Context context, @NonNull Uri uri, 1031 @Nullable Map<String, String> headers, @Nullable List<HttpCookie> cookies) 1032 throws IOException { 1033 if (context == null) { 1034 throw new NullPointerException("context param can not be null."); 1035 } 1036 1037 if (uri == null) { 1038 throw new NullPointerException("uri param can not be null."); 1039 } 1040 1041 if (cookies != null) { 1042 CookieHandler cookieHandler = CookieHandler.getDefault(); 1043 if (cookieHandler != null && !(cookieHandler instanceof CookieManager)) { 1044 throw new IllegalArgumentException("The cookie handler has to be of CookieManager " 1045 + "type when cookies are provided."); 1046 } 1047 } 1048 1049 // The context and URI usually belong to the calling user. Get a resolver for that user 1050 // and strip out the userId from the URI if present. 1051 final ContentResolver resolver = context.getContentResolver(); 1052 final String scheme = uri.getScheme(); 1053 final String authority = ContentProvider.getAuthorityWithoutUserId(uri.getAuthority()); 1054 if (ContentResolver.SCHEME_FILE.equals(scheme)) { 1055 setDataSource(uri.getPath()); 1056 return; 1057 } else if (ContentResolver.SCHEME_CONTENT.equals(scheme) 1058 && Settings.AUTHORITY.equals(authority)) { 1059 // Try cached ringtone first since the actual provider may not be 1060 // encryption aware, or it may be stored on CE media storage 1061 final int type = RingtoneManager.getDefaultType(uri); 1062 final Uri cacheUri = RingtoneManager.getCacheForType(type, context.getUserId()); 1063 final Uri actualUri = RingtoneManager.getActualDefaultRingtoneUri(context, type); 1064 if (attemptDataSource(resolver, cacheUri)) { 1065 return; 1066 } else if (attemptDataSource(resolver, actualUri)) { 1067 return; 1068 } else { 1069 setDataSource(uri.toString(), headers, cookies); 1070 } 1071 } else { 1072 // Try requested Uri locally first, or fallback to media server 1073 if (attemptDataSource(resolver, uri)) { 1074 return; 1075 } else { 1076 setDataSource(uri.toString(), headers, cookies); 1077 } 1078 } 1079 } 1080 1081 /** 1082 * Sets the data source as a content Uri. 1083 * 1084 * <p><strong>Note</strong> that the cross domain redirection is allowed by default, 1085 * but that can be changed with key/value pairs through the headers parameter with 1086 * "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value to 1087 * disallow or allow cross domain redirection. 1088 * 1089 * @param context the Context to use when resolving the Uri 1090 * @param uri the Content URI of the data you want to play 1091 * @param headers the headers to be sent together with the request for the data 1092 * @throws IllegalStateException if it is called in an invalid state 1093 */ setDataSource(@onNull Context context, @NonNull Uri uri, @Nullable Map<String, String> headers)1094 public void setDataSource(@NonNull Context context, @NonNull Uri uri, 1095 @Nullable Map<String, String> headers) 1096 throws IOException, IllegalArgumentException, SecurityException, IllegalStateException { 1097 setDataSource(context, uri, headers, null); 1098 } 1099 attemptDataSource(ContentResolver resolver, Uri uri)1100 private boolean attemptDataSource(ContentResolver resolver, Uri uri) { 1101 try (AssetFileDescriptor afd = resolver.openAssetFileDescriptor(uri, "r")) { 1102 setDataSource(afd); 1103 return true; 1104 } catch (NullPointerException | SecurityException | IOException ex) { 1105 Log.w(TAG, "Couldn't open " + (uri == null ? "null uri" : uri.toSafeString()), ex); 1106 return false; 1107 } 1108 } 1109 1110 /** 1111 * Sets the data source (file-path or http/rtsp URL) to use. 1112 * 1113 * <p>When <code>path</code> refers to a local file, the file may actually be opened by a 1114 * process other than the calling application. This implies that the pathname 1115 * should be an absolute path (as any other process runs with unspecified current working 1116 * directory), and that the pathname should reference a world-readable file. 1117 * As an alternative, the application could first open the file for reading, 1118 * and then use the file descriptor form {@link #setDataSource(FileDescriptor)}. 1119 * 1120 * @param path the path of the file, or the http/rtsp URL of the stream you want to play 1121 * @throws IllegalStateException if it is called in an invalid state 1122 */ setDataSource(String path)1123 public void setDataSource(String path) 1124 throws IOException, IllegalArgumentException, SecurityException, IllegalStateException { 1125 setDataSource(path, null, null); 1126 } 1127 1128 /** 1129 * Sets the data source (file-path or http/rtsp URL) to use. 1130 * 1131 * @param path the path of the file, or the http/rtsp URL of the stream you want to play 1132 * @param headers the headers associated with the http request for the stream you want to play 1133 * @throws IllegalStateException if it is called in an invalid state 1134 * @hide pending API council 1135 */ 1136 @UnsupportedAppUsage setDataSource(String path, Map<String, String> headers)1137 public void setDataSource(String path, Map<String, String> headers) 1138 throws IOException, IllegalArgumentException, SecurityException, IllegalStateException { 1139 setDataSource(path, headers, null); 1140 } 1141 1142 @UnsupportedAppUsage setDataSource(String path, Map<String, String> headers, List<HttpCookie> cookies)1143 private void setDataSource(String path, Map<String, String> headers, List<HttpCookie> cookies) 1144 throws IOException, IllegalArgumentException, SecurityException, IllegalStateException 1145 { 1146 String[] keys = null; 1147 String[] values = null; 1148 1149 if (headers != null) { 1150 keys = new String[headers.size()]; 1151 values = new String[headers.size()]; 1152 1153 int i = 0; 1154 for (Map.Entry<String, String> entry: headers.entrySet()) { 1155 keys[i] = entry.getKey(); 1156 values[i] = entry.getValue(); 1157 ++i; 1158 } 1159 } 1160 setDataSource(path, keys, values, cookies); 1161 } 1162 1163 @UnsupportedAppUsage setDataSource(String path, String[] keys, String[] values, List<HttpCookie> cookies)1164 private void setDataSource(String path, String[] keys, String[] values, 1165 List<HttpCookie> cookies) 1166 throws IOException, IllegalArgumentException, SecurityException, IllegalStateException { 1167 final Uri uri = Uri.parse(path); 1168 final String scheme = uri.getScheme(); 1169 if ("file".equals(scheme)) { 1170 path = uri.getPath(); 1171 } else if (scheme != null) { 1172 // handle non-file sources 1173 nativeSetDataSource( 1174 MediaHTTPService.createHttpServiceBinderIfNecessary(path, cookies), 1175 path, 1176 keys, 1177 values); 1178 return; 1179 } 1180 1181 final File file = new File(path); 1182 try (FileInputStream is = new FileInputStream(file)) { 1183 setDataSource(is.getFD()); 1184 } 1185 } 1186 nativeSetDataSource( IBinder httpServiceBinder, String path, String[] keys, String[] values)1187 private native void nativeSetDataSource( 1188 IBinder httpServiceBinder, String path, String[] keys, String[] values) 1189 throws IOException, IllegalArgumentException, SecurityException, IllegalStateException; 1190 1191 /** 1192 * Sets the data source (AssetFileDescriptor) to use. It is the caller's 1193 * responsibility to close the file descriptor. It is safe to do so as soon 1194 * as this call returns. 1195 * 1196 * @param afd the AssetFileDescriptor for the file you want to play 1197 * @throws IllegalStateException if it is called in an invalid state 1198 * @throws IllegalArgumentException if afd is not a valid AssetFileDescriptor 1199 * @throws IOException if afd can not be read 1200 */ setDataSource(@onNull AssetFileDescriptor afd)1201 public void setDataSource(@NonNull AssetFileDescriptor afd) 1202 throws IOException, IllegalArgumentException, IllegalStateException { 1203 Preconditions.checkNotNull(afd); 1204 // Note: using getDeclaredLength so that our behavior is the same 1205 // as previous versions when the content provider is returning 1206 // a full file. 1207 if (afd.getDeclaredLength() < 0) { 1208 setDataSource(afd.getFileDescriptor()); 1209 } else { 1210 setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getDeclaredLength()); 1211 } 1212 } 1213 1214 /** 1215 * Sets the data source (FileDescriptor) to use. It is the caller's responsibility 1216 * to close the file descriptor. It is safe to do so as soon as this call returns. 1217 * 1218 * @param fd the FileDescriptor for the file you want to play 1219 * @throws IllegalStateException if it is called in an invalid state 1220 * @throws IllegalArgumentException if fd is not a valid FileDescriptor 1221 * @throws IOException if fd can not be read 1222 */ setDataSource(FileDescriptor fd)1223 public void setDataSource(FileDescriptor fd) 1224 throws IOException, IllegalArgumentException, IllegalStateException { 1225 // intentionally less than LONG_MAX 1226 setDataSource(fd, 0, 0x7ffffffffffffffL); 1227 } 1228 1229 /** 1230 * Sets the data source (FileDescriptor) to use. The FileDescriptor must be 1231 * seekable (N.B. a LocalSocket is not seekable). It is the caller's responsibility 1232 * to close the file descriptor. It is safe to do so as soon as this call returns. 1233 * 1234 * @param fd the FileDescriptor for the file you want to play 1235 * @param offset the offset into the file where the data to be played starts, in bytes 1236 * @param length the length in bytes of the data to be played 1237 * @throws IllegalStateException if it is called in an invalid state 1238 * @throws IllegalArgumentException if fd is not a valid FileDescriptor 1239 * @throws IOException if fd can not be read 1240 */ setDataSource(FileDescriptor fd, long offset, long length)1241 public void setDataSource(FileDescriptor fd, long offset, long length) 1242 throws IOException, IllegalArgumentException, IllegalStateException { 1243 _setDataSource(fd, offset, length); 1244 } 1245 _setDataSource(FileDescriptor fd, long offset, long length)1246 private native void _setDataSource(FileDescriptor fd, long offset, long length) 1247 throws IOException, IllegalArgumentException, IllegalStateException; 1248 1249 /** 1250 * Sets the data source (MediaDataSource) to use. 1251 * 1252 * @param dataSource the MediaDataSource for the media you want to play 1253 * @throws IllegalStateException if it is called in an invalid state 1254 * @throws IllegalArgumentException if dataSource is not a valid MediaDataSource 1255 */ setDataSource(MediaDataSource dataSource)1256 public void setDataSource(MediaDataSource dataSource) 1257 throws IllegalArgumentException, IllegalStateException { 1258 _setDataSource(dataSource); 1259 } 1260 _setDataSource(MediaDataSource dataSource)1261 private native void _setDataSource(MediaDataSource dataSource) 1262 throws IllegalArgumentException, IllegalStateException; 1263 1264 /** 1265 * Prepares the player for playback, synchronously. 1266 * 1267 * After setting the datasource and the display surface, you need to either 1268 * call prepare() or prepareAsync(). For files, it is OK to call prepare(), 1269 * which blocks until MediaPlayer is ready for playback. 1270 * 1271 * @throws IllegalStateException if it is called in an invalid state 1272 */ prepare()1273 public void prepare() throws IOException, IllegalStateException { 1274 _prepare(); 1275 scanInternalSubtitleTracks(); 1276 1277 // DrmInfo, if any, has been resolved by now. 1278 synchronized (mDrmLock) { 1279 mDrmInfoResolved = true; 1280 } 1281 } 1282 _prepare()1283 private native void _prepare() throws IOException, IllegalStateException; 1284 1285 /** 1286 * Prepares the player for playback, asynchronously. 1287 * 1288 * After setting the datasource and the display surface, you need to either 1289 * call prepare() or prepareAsync(). For streams, you should call prepareAsync(), 1290 * which returns immediately, rather than blocking until enough data has been 1291 * buffered. 1292 * 1293 * @throws IllegalStateException if it is called in an invalid state 1294 */ prepareAsync()1295 public native void prepareAsync() throws IllegalStateException; 1296 1297 /** 1298 * Starts or resumes playback. If playback had previously been paused, 1299 * playback will continue from where it was paused. If playback had 1300 * been stopped, or never started before, playback will start at the 1301 * beginning. 1302 * 1303 * @throws IllegalStateException if it is called in an invalid state 1304 */ start()1305 public void start() throws IllegalStateException { 1306 //FIXME use lambda to pass startImpl to superclass 1307 final int delay = getStartDelayMs(); 1308 if (delay == 0) { 1309 startImpl(); 1310 } else { 1311 new Thread() { 1312 public void run() { 1313 try { 1314 Thread.sleep(delay); 1315 } catch (InterruptedException e) { 1316 e.printStackTrace(); 1317 } 1318 baseSetStartDelayMs(0); 1319 try { 1320 startImpl(); 1321 } catch (IllegalStateException e) { 1322 // fail silently for a state exception when it is happening after 1323 // a delayed start, as the player state could have changed between the 1324 // call to start() and the execution of startImpl() 1325 } 1326 } 1327 }.start(); 1328 } 1329 } 1330 startImpl()1331 private void startImpl() { 1332 baseStart(); 1333 stayAwake(true); 1334 _start(); 1335 } 1336 _start()1337 private native void _start() throws IllegalStateException; 1338 1339 getAudioStreamType()1340 private int getAudioStreamType() { 1341 if (mStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) { 1342 mStreamType = _getAudioStreamType(); 1343 } 1344 return mStreamType; 1345 } 1346 _getAudioStreamType()1347 private native int _getAudioStreamType() throws IllegalStateException; 1348 1349 /** 1350 * Stops playback after playback has been started or paused. 1351 * 1352 * @throws IllegalStateException if the internal player engine has not been 1353 * initialized. 1354 */ stop()1355 public void stop() throws IllegalStateException { 1356 stayAwake(false); 1357 _stop(); 1358 baseStop(); 1359 } 1360 _stop()1361 private native void _stop() throws IllegalStateException; 1362 1363 /** 1364 * Pauses playback. Call start() to resume. 1365 * 1366 * @throws IllegalStateException if the internal player engine has not been 1367 * initialized. 1368 */ pause()1369 public void pause() throws IllegalStateException { 1370 stayAwake(false); 1371 _pause(); 1372 basePause(); 1373 } 1374 _pause()1375 private native void _pause() throws IllegalStateException; 1376 1377 @Override playerStart()1378 void playerStart() { 1379 start(); 1380 } 1381 1382 @Override playerPause()1383 void playerPause() { 1384 pause(); 1385 } 1386 1387 @Override playerStop()1388 void playerStop() { 1389 stop(); 1390 } 1391 1392 @Override playerApplyVolumeShaper( @onNull VolumeShaper.Configuration configuration, @NonNull VolumeShaper.Operation operation)1393 /* package */ int playerApplyVolumeShaper( 1394 @NonNull VolumeShaper.Configuration configuration, 1395 @NonNull VolumeShaper.Operation operation) { 1396 return native_applyVolumeShaper(configuration, operation); 1397 } 1398 1399 @Override playerGetVolumeShaperState(int id)1400 /* package */ @Nullable VolumeShaper.State playerGetVolumeShaperState(int id) { 1401 return native_getVolumeShaperState(id); 1402 } 1403 1404 @Override createVolumeShaper( @onNull VolumeShaper.Configuration configuration)1405 public @NonNull VolumeShaper createVolumeShaper( 1406 @NonNull VolumeShaper.Configuration configuration) { 1407 return new VolumeShaper(configuration, this); 1408 } 1409 native_applyVolumeShaper( @onNull VolumeShaper.Configuration configuration, @NonNull VolumeShaper.Operation operation)1410 private native int native_applyVolumeShaper( 1411 @NonNull VolumeShaper.Configuration configuration, 1412 @NonNull VolumeShaper.Operation operation); 1413 native_getVolumeShaperState(int id)1414 private native @Nullable VolumeShaper.State native_getVolumeShaperState(int id); 1415 1416 //-------------------------------------------------------------------------- 1417 // Explicit Routing 1418 //-------------------- 1419 private AudioDeviceInfo mPreferredDevice = null; 1420 1421 /** 1422 * Specifies an audio device (via an {@link AudioDeviceInfo} object) to route 1423 * the output from this MediaPlayer. 1424 * @param deviceInfo The {@link AudioDeviceInfo} specifying the audio sink or source. 1425 * If deviceInfo is null, default routing is restored. 1426 * @return true if succesful, false if the specified {@link AudioDeviceInfo} is non-null and 1427 * does not correspond to a valid audio device. 1428 */ 1429 @Override setPreferredDevice(AudioDeviceInfo deviceInfo)1430 public boolean setPreferredDevice(AudioDeviceInfo deviceInfo) { 1431 if (deviceInfo != null && !deviceInfo.isSink()) { 1432 return false; 1433 } 1434 int preferredDeviceId = deviceInfo != null ? deviceInfo.getId() : 0; 1435 boolean status = native_setOutputDevice(preferredDeviceId); 1436 if (status == true) { 1437 synchronized (this) { 1438 mPreferredDevice = deviceInfo; 1439 } 1440 } 1441 return status; 1442 } 1443 1444 /** 1445 * Returns the selected output specified by {@link #setPreferredDevice}. Note that this 1446 * is not guaranteed to correspond to the actual device being used for playback. 1447 */ 1448 @Override getPreferredDevice()1449 public AudioDeviceInfo getPreferredDevice() { 1450 synchronized (this) { 1451 return mPreferredDevice; 1452 } 1453 } 1454 1455 /** 1456 * Returns an {@link AudioDeviceInfo} identifying the current routing of this MediaPlayer 1457 * Note: The query is only valid if the MediaPlayer is currently playing. 1458 * If the player is not playing, the returned device can be null or correspond to previously 1459 * selected device when the player was last active. 1460 */ 1461 @Override getRoutedDevice()1462 public AudioDeviceInfo getRoutedDevice() { 1463 int deviceId = native_getRoutedDeviceId(); 1464 if (deviceId == 0) { 1465 return null; 1466 } 1467 AudioDeviceInfo[] devices = 1468 AudioManager.getDevicesStatic(AudioManager.GET_DEVICES_OUTPUTS); 1469 for (int i = 0; i < devices.length; i++) { 1470 if (devices[i].getId() == deviceId) { 1471 return devices[i]; 1472 } 1473 } 1474 return null; 1475 } 1476 1477 /* 1478 * Call BEFORE adding a routing callback handler or AFTER removing a routing callback handler. 1479 */ 1480 @GuardedBy("mRoutingChangeListeners") enableNativeRoutingCallbacksLocked(boolean enabled)1481 private void enableNativeRoutingCallbacksLocked(boolean enabled) { 1482 if (mRoutingChangeListeners.size() == 0) { 1483 native_enableDeviceCallback(enabled); 1484 } 1485 } 1486 1487 /** 1488 * The list of AudioRouting.OnRoutingChangedListener interfaces added (with 1489 * {@link #addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, Handler)} 1490 * by an app to receive (re)routing notifications. 1491 */ 1492 @GuardedBy("mRoutingChangeListeners") 1493 private ArrayMap<AudioRouting.OnRoutingChangedListener, 1494 NativeRoutingEventHandlerDelegate> mRoutingChangeListeners = new ArrayMap<>(); 1495 1496 /** 1497 * Adds an {@link AudioRouting.OnRoutingChangedListener} to receive notifications of routing 1498 * changes on this MediaPlayer. 1499 * @param listener The {@link AudioRouting.OnRoutingChangedListener} interface to receive 1500 * notifications of rerouting events. 1501 * @param handler Specifies the {@link Handler} object for the thread on which to execute 1502 * the callback. If <code>null</code>, the handler on the main looper will be used. 1503 */ 1504 @Override addOnRoutingChangedListener(AudioRouting.OnRoutingChangedListener listener, Handler handler)1505 public void addOnRoutingChangedListener(AudioRouting.OnRoutingChangedListener listener, 1506 Handler handler) { 1507 synchronized (mRoutingChangeListeners) { 1508 if (listener != null && !mRoutingChangeListeners.containsKey(listener)) { 1509 enableNativeRoutingCallbacksLocked(true); 1510 mRoutingChangeListeners.put( 1511 listener, new NativeRoutingEventHandlerDelegate(this, listener, 1512 handler != null ? handler : mEventHandler)); 1513 } 1514 } 1515 } 1516 1517 /** 1518 * Removes an {@link AudioRouting.OnRoutingChangedListener} which has been previously added 1519 * to receive rerouting notifications. 1520 * @param listener The previously added {@link AudioRouting.OnRoutingChangedListener} interface 1521 * to remove. 1522 */ 1523 @Override removeOnRoutingChangedListener(AudioRouting.OnRoutingChangedListener listener)1524 public void removeOnRoutingChangedListener(AudioRouting.OnRoutingChangedListener listener) { 1525 synchronized (mRoutingChangeListeners) { 1526 if (mRoutingChangeListeners.containsKey(listener)) { 1527 mRoutingChangeListeners.remove(listener); 1528 enableNativeRoutingCallbacksLocked(false); 1529 } 1530 } 1531 } 1532 native_setOutputDevice(int deviceId)1533 private native final boolean native_setOutputDevice(int deviceId); native_getRoutedDeviceId()1534 private native final int native_getRoutedDeviceId(); native_enableDeviceCallback(boolean enabled)1535 private native final void native_enableDeviceCallback(boolean enabled); 1536 1537 /** 1538 * Set the low-level power management behavior for this MediaPlayer. This 1539 * can be used when the MediaPlayer is not playing through a SurfaceHolder 1540 * set with {@link #setDisplay(SurfaceHolder)} and thus can use the 1541 * high-level {@link #setScreenOnWhilePlaying(boolean)} feature. 1542 * 1543 * <p>This function has the MediaPlayer access the low-level power manager 1544 * service to control the device's power usage while playing is occurring. 1545 * The parameter is a combination of {@link android.os.PowerManager} wake flags. 1546 * Use of this method requires {@link android.Manifest.permission#WAKE_LOCK} 1547 * permission. 1548 * By default, no attempt is made to keep the device awake during playback. 1549 * 1550 * @param context the Context to use 1551 * @param mode the power/wake mode to set 1552 * @see android.os.PowerManager 1553 */ setWakeMode(Context context, int mode)1554 public void setWakeMode(Context context, int mode) { 1555 boolean washeld = false; 1556 1557 /* Disable persistant wakelocks in media player based on property */ 1558 if (SystemProperties.getBoolean("audio.offload.ignore_setawake", false) == true) { 1559 Log.w(TAG, "IGNORING setWakeMode " + mode); 1560 return; 1561 } 1562 1563 if (mWakeLock != null) { 1564 if (mWakeLock.isHeld()) { 1565 washeld = true; 1566 mWakeLock.release(); 1567 } 1568 mWakeLock = null; 1569 } 1570 1571 PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); 1572 mWakeLock = pm.newWakeLock(mode|PowerManager.ON_AFTER_RELEASE, MediaPlayer.class.getName()); 1573 mWakeLock.setReferenceCounted(false); 1574 if (washeld) { 1575 mWakeLock.acquire(); 1576 } 1577 } 1578 1579 /** 1580 * Control whether we should use the attached SurfaceHolder to keep the 1581 * screen on while video playback is occurring. This is the preferred 1582 * method over {@link #setWakeMode} where possible, since it doesn't 1583 * require that the application have permission for low-level wake lock 1584 * access. 1585 * 1586 * @param screenOn Supply true to keep the screen on, false to allow it 1587 * to turn off. 1588 */ setScreenOnWhilePlaying(boolean screenOn)1589 public void setScreenOnWhilePlaying(boolean screenOn) { 1590 if (mScreenOnWhilePlaying != screenOn) { 1591 if (screenOn && mSurfaceHolder == null) { 1592 Log.w(TAG, "setScreenOnWhilePlaying(true) is ineffective without a SurfaceHolder"); 1593 } 1594 mScreenOnWhilePlaying = screenOn; 1595 updateSurfaceScreenOn(); 1596 } 1597 } 1598 stayAwake(boolean awake)1599 private void stayAwake(boolean awake) { 1600 if (mWakeLock != null) { 1601 if (awake && !mWakeLock.isHeld()) { 1602 mWakeLock.acquire(); 1603 } else if (!awake && mWakeLock.isHeld()) { 1604 mWakeLock.release(); 1605 } 1606 } 1607 mStayAwake = awake; 1608 updateSurfaceScreenOn(); 1609 } 1610 updateSurfaceScreenOn()1611 private void updateSurfaceScreenOn() { 1612 if (mSurfaceHolder != null) { 1613 mSurfaceHolder.setKeepScreenOn(mScreenOnWhilePlaying && mStayAwake); 1614 } 1615 } 1616 1617 /** 1618 * Returns the width of the video. 1619 * 1620 * @return the width of the video, or 0 if there is no video, 1621 * no display surface was set, or the width has not been determined 1622 * yet. The OnVideoSizeChangedListener can be registered via 1623 * {@link #setOnVideoSizeChangedListener(OnVideoSizeChangedListener)} 1624 * to provide a notification when the width is available. 1625 */ getVideoWidth()1626 public native int getVideoWidth(); 1627 1628 /** 1629 * Returns the height of the video. 1630 * 1631 * @return the height of the video, or 0 if there is no video, 1632 * no display surface was set, or the height has not been determined 1633 * yet. The OnVideoSizeChangedListener can be registered via 1634 * {@link #setOnVideoSizeChangedListener(OnVideoSizeChangedListener)} 1635 * to provide a notification when the height is available. 1636 */ getVideoHeight()1637 public native int getVideoHeight(); 1638 1639 /** 1640 * Return Metrics data about the current player. 1641 * 1642 * @return a {@link PersistableBundle} containing the set of attributes and values 1643 * available for the media being handled by this instance of MediaPlayer 1644 * The attributes are descibed in {@link MetricsConstants}. 1645 * 1646 * Additional vendor-specific fields may also be present in 1647 * the return value. 1648 */ getMetrics()1649 public PersistableBundle getMetrics() { 1650 PersistableBundle bundle = native_getMetrics(); 1651 return bundle; 1652 } 1653 native_getMetrics()1654 private native PersistableBundle native_getMetrics(); 1655 1656 /** 1657 * Checks whether the MediaPlayer is playing. 1658 * 1659 * @return true if currently playing, false otherwise 1660 * @throws IllegalStateException if the internal player engine has not been 1661 * initialized or has been released. 1662 */ isPlaying()1663 public native boolean isPlaying(); 1664 1665 /** 1666 * Change playback speed of audio by resampling the audio. 1667 * <p> 1668 * Specifies resampling as audio mode for variable rate playback, i.e., 1669 * resample the waveform based on the requested playback rate to get 1670 * a new waveform, and play back the new waveform at the original sampling 1671 * frequency. 1672 * When rate is larger than 1.0, pitch becomes higher. 1673 * When rate is smaller than 1.0, pitch becomes lower. 1674 * 1675 * @hide 1676 */ 1677 public static final int PLAYBACK_RATE_AUDIO_MODE_RESAMPLE = 2; 1678 1679 /** 1680 * Change playback speed of audio without changing its pitch. 1681 * <p> 1682 * Specifies time stretching as audio mode for variable rate playback. 1683 * Time stretching changes the duration of the audio samples without 1684 * affecting its pitch. 1685 * <p> 1686 * This mode is only supported for a limited range of playback speed factors, 1687 * e.g. between 1/2x and 2x. 1688 * 1689 * @hide 1690 */ 1691 public static final int PLAYBACK_RATE_AUDIO_MODE_STRETCH = 1; 1692 1693 /** 1694 * Change playback speed of audio without changing its pitch, and 1695 * possibly mute audio if time stretching is not supported for the playback 1696 * speed. 1697 * <p> 1698 * Try to keep audio pitch when changing the playback rate, but allow the 1699 * system to determine how to change audio playback if the rate is out 1700 * of range. 1701 * 1702 * @hide 1703 */ 1704 public static final int PLAYBACK_RATE_AUDIO_MODE_DEFAULT = 0; 1705 1706 /** @hide */ 1707 @IntDef( 1708 value = { 1709 PLAYBACK_RATE_AUDIO_MODE_DEFAULT, 1710 PLAYBACK_RATE_AUDIO_MODE_STRETCH, 1711 PLAYBACK_RATE_AUDIO_MODE_RESAMPLE, 1712 }) 1713 @Retention(RetentionPolicy.SOURCE) 1714 public @interface PlaybackRateAudioMode {} 1715 1716 /** 1717 * Sets playback rate and audio mode. 1718 * 1719 * @param rate the ratio between desired playback rate and normal one. 1720 * @param audioMode audio playback mode. Must be one of the supported 1721 * audio modes. 1722 * 1723 * @throws IllegalStateException if the internal player engine has not been 1724 * initialized. 1725 * @throws IllegalArgumentException if audioMode is not supported. 1726 * 1727 * @hide 1728 */ 1729 @NonNull easyPlaybackParams(float rate, @PlaybackRateAudioMode int audioMode)1730 public PlaybackParams easyPlaybackParams(float rate, @PlaybackRateAudioMode int audioMode) { 1731 PlaybackParams params = new PlaybackParams(); 1732 params.allowDefaults(); 1733 switch (audioMode) { 1734 case PLAYBACK_RATE_AUDIO_MODE_DEFAULT: 1735 params.setSpeed(rate).setPitch(1.0f); 1736 break; 1737 case PLAYBACK_RATE_AUDIO_MODE_STRETCH: 1738 params.setSpeed(rate).setPitch(1.0f) 1739 .setAudioFallbackMode(params.AUDIO_FALLBACK_MODE_FAIL); 1740 break; 1741 case PLAYBACK_RATE_AUDIO_MODE_RESAMPLE: 1742 params.setSpeed(rate).setPitch(rate); 1743 break; 1744 default: 1745 final String msg = "Audio playback mode " + audioMode + " is not supported"; 1746 throw new IllegalArgumentException(msg); 1747 } 1748 return params; 1749 } 1750 1751 /** 1752 * Sets playback rate using {@link PlaybackParams}. The object sets its internal 1753 * PlaybackParams to the input, except that the object remembers previous speed 1754 * when input speed is zero. This allows the object to resume at previous speed 1755 * when start() is called. Calling it before the object is prepared does not change 1756 * the object state. After the object is prepared, calling it with zero speed is 1757 * equivalent to calling pause(). After the object is prepared, calling it with 1758 * non-zero speed is equivalent to calling start(). 1759 * 1760 * @param params the playback params. 1761 * 1762 * @throws IllegalStateException if the internal player engine has not been 1763 * initialized or has been released. 1764 * @throws IllegalArgumentException if params is not supported. 1765 */ setPlaybackParams(@onNull PlaybackParams params)1766 public native void setPlaybackParams(@NonNull PlaybackParams params); 1767 1768 /** 1769 * Gets the playback params, containing the current playback rate. 1770 * 1771 * @return the playback params. 1772 * @throws IllegalStateException if the internal player engine has not been 1773 * initialized. 1774 */ 1775 @NonNull getPlaybackParams()1776 public native PlaybackParams getPlaybackParams(); 1777 1778 /** 1779 * Sets A/V sync mode. 1780 * 1781 * @param params the A/V sync params to apply 1782 * 1783 * @throws IllegalStateException if the internal player engine has not been 1784 * initialized. 1785 * @throws IllegalArgumentException if params are not supported. 1786 */ setSyncParams(@onNull SyncParams params)1787 public native void setSyncParams(@NonNull SyncParams params); 1788 1789 /** 1790 * Gets the A/V sync mode. 1791 * 1792 * @return the A/V sync params 1793 * 1794 * @throws IllegalStateException if the internal player engine has not been 1795 * initialized. 1796 */ 1797 @NonNull getSyncParams()1798 public native SyncParams getSyncParams(); 1799 1800 /** 1801 * Seek modes used in method seekTo(long, int) to move media position 1802 * to a specified location. 1803 * 1804 * Do not change these mode values without updating their counterparts 1805 * in include/media/IMediaSource.h! 1806 */ 1807 /** 1808 * This mode is used with {@link #seekTo(long, int)} to move media position to 1809 * a sync (or key) frame associated with a data source that is located 1810 * right before or at the given time. 1811 * 1812 * @see #seekTo(long, int) 1813 */ 1814 public static final int SEEK_PREVIOUS_SYNC = 0x00; 1815 /** 1816 * This mode is used with {@link #seekTo(long, int)} to move media position to 1817 * a sync (or key) frame associated with a data source that is located 1818 * right after or at the given time. 1819 * 1820 * @see #seekTo(long, int) 1821 */ 1822 public static final int SEEK_NEXT_SYNC = 0x01; 1823 /** 1824 * This mode is used with {@link #seekTo(long, int)} to move media position to 1825 * a sync (or key) frame associated with a data source that is located 1826 * closest to (in time) or at the given time. 1827 * 1828 * @see #seekTo(long, int) 1829 */ 1830 public static final int SEEK_CLOSEST_SYNC = 0x02; 1831 /** 1832 * This mode is used with {@link #seekTo(long, int)} to move media position to 1833 * a frame (not necessarily a key frame) associated with a data source that 1834 * is located closest to or at the given time. 1835 * 1836 * @see #seekTo(long, int) 1837 */ 1838 public static final int SEEK_CLOSEST = 0x03; 1839 1840 /** @hide */ 1841 @IntDef( 1842 value = { 1843 SEEK_PREVIOUS_SYNC, 1844 SEEK_NEXT_SYNC, 1845 SEEK_CLOSEST_SYNC, 1846 SEEK_CLOSEST, 1847 }) 1848 @Retention(RetentionPolicy.SOURCE) 1849 public @interface SeekMode {} 1850 _seekTo(long msec, int mode)1851 private native final void _seekTo(long msec, int mode); 1852 1853 /** 1854 * Moves the media to specified time position by considering the given mode. 1855 * <p> 1856 * When seekTo is finished, the user will be notified via OnSeekComplete supplied by the user. 1857 * There is at most one active seekTo processed at any time. If there is a to-be-completed 1858 * seekTo, new seekTo requests will be queued in such a way that only the last request 1859 * is kept. When current seekTo is completed, the queued request will be processed if 1860 * that request is different from just-finished seekTo operation, i.e., the requested 1861 * position or mode is different. 1862 * 1863 * @param msec the offset in milliseconds from the start to seek to. 1864 * When seeking to the given time position, there is no guarantee that the data source 1865 * has a frame located at the position. When this happens, a frame nearby will be rendered. 1866 * If msec is negative, time position zero will be used. 1867 * If msec is larger than duration, duration will be used. 1868 * @param mode the mode indicating where exactly to seek to. 1869 * Use {@link #SEEK_PREVIOUS_SYNC} if one wants to seek to a sync frame 1870 * that has a timestamp earlier than or the same as msec. Use 1871 * {@link #SEEK_NEXT_SYNC} if one wants to seek to a sync frame 1872 * that has a timestamp later than or the same as msec. Use 1873 * {@link #SEEK_CLOSEST_SYNC} if one wants to seek to a sync frame 1874 * that has a timestamp closest to or the same as msec. Use 1875 * {@link #SEEK_CLOSEST} if one wants to seek to a frame that may 1876 * or may not be a sync frame but is closest to or the same as msec. 1877 * {@link #SEEK_CLOSEST} often has larger performance overhead compared 1878 * to the other options if there is no sync frame located at msec. 1879 * @throws IllegalStateException if the internal player engine has not been 1880 * initialized 1881 * @throws IllegalArgumentException if the mode is invalid. 1882 */ seekTo(long msec, @SeekMode int mode)1883 public void seekTo(long msec, @SeekMode int mode) { 1884 if (mode < SEEK_PREVIOUS_SYNC || mode > SEEK_CLOSEST) { 1885 final String msg = "Illegal seek mode: " + mode; 1886 throw new IllegalArgumentException(msg); 1887 } 1888 // TODO: pass long to native, instead of truncating here. 1889 if (msec > Integer.MAX_VALUE) { 1890 Log.w(TAG, "seekTo offset " + msec + " is too large, cap to " + Integer.MAX_VALUE); 1891 msec = Integer.MAX_VALUE; 1892 } else if (msec < Integer.MIN_VALUE) { 1893 Log.w(TAG, "seekTo offset " + msec + " is too small, cap to " + Integer.MIN_VALUE); 1894 msec = Integer.MIN_VALUE; 1895 } 1896 _seekTo(msec, mode); 1897 } 1898 1899 /** 1900 * Seeks to specified time position. 1901 * Same as {@link #seekTo(long, int)} with {@code mode = SEEK_PREVIOUS_SYNC}. 1902 * 1903 * @param msec the offset in milliseconds from the start to seek to 1904 * @throws IllegalStateException if the internal player engine has not been 1905 * initialized 1906 */ seekTo(int msec)1907 public void seekTo(int msec) throws IllegalStateException { 1908 seekTo(msec, SEEK_PREVIOUS_SYNC /* mode */); 1909 } 1910 1911 /** 1912 * Get current playback position as a {@link MediaTimestamp}. 1913 * <p> 1914 * The MediaTimestamp represents how the media time correlates to the system time in 1915 * a linear fashion using an anchor and a clock rate. During regular playback, the media 1916 * time moves fairly constantly (though the anchor frame may be rebased to a current 1917 * system time, the linear correlation stays steady). Therefore, this method does not 1918 * need to be called often. 1919 * <p> 1920 * To help users get current playback position, this method always anchors the timestamp 1921 * to the current {@link System#nanoTime system time}, so 1922 * {@link MediaTimestamp#getAnchorMediaTimeUs} can be used as current playback position. 1923 * 1924 * @return a MediaTimestamp object if a timestamp is available, or {@code null} if no timestamp 1925 * is available, e.g. because the media player has not been initialized. 1926 * 1927 * @see MediaTimestamp 1928 */ 1929 @Nullable getTimestamp()1930 public MediaTimestamp getTimestamp() 1931 { 1932 try { 1933 // TODO: get the timestamp from native side 1934 return new MediaTimestamp( 1935 getCurrentPosition() * 1000L, 1936 System.nanoTime(), 1937 isPlaying() ? getPlaybackParams().getSpeed() : 0.f); 1938 } catch (IllegalStateException e) { 1939 return null; 1940 } 1941 } 1942 1943 /** 1944 * Gets the current playback position. 1945 * 1946 * @return the current position in milliseconds 1947 */ getCurrentPosition()1948 public native int getCurrentPosition(); 1949 1950 /** 1951 * Gets the duration of the file. 1952 * 1953 * @return the duration in milliseconds, if no duration is available 1954 * (for example, if streaming live content), -1 is returned. 1955 */ getDuration()1956 public native int getDuration(); 1957 1958 /** 1959 * Gets the media metadata. 1960 * 1961 * @param update_only controls whether the full set of available 1962 * metadata is returned or just the set that changed since the 1963 * last call. See {@see #METADATA_UPDATE_ONLY} and {@see 1964 * #METADATA_ALL}. 1965 * 1966 * @param apply_filter if true only metadata that matches the 1967 * filter is returned. See {@see #APPLY_METADATA_FILTER} and {@see 1968 * #BYPASS_METADATA_FILTER}. 1969 * 1970 * @return The metadata, possibly empty. null if an error occured. 1971 // FIXME: unhide. 1972 * {@hide} 1973 */ 1974 @UnsupportedAppUsage getMetadata(final boolean update_only, final boolean apply_filter)1975 public Metadata getMetadata(final boolean update_only, 1976 final boolean apply_filter) { 1977 Parcel reply = Parcel.obtain(); 1978 Metadata data = new Metadata(); 1979 1980 if (!native_getMetadata(update_only, apply_filter, reply)) { 1981 reply.recycle(); 1982 return null; 1983 } 1984 1985 // Metadata takes over the parcel, don't recycle it unless 1986 // there is an error. 1987 if (!data.parse(reply)) { 1988 reply.recycle(); 1989 return null; 1990 } 1991 return data; 1992 } 1993 1994 /** 1995 * Set a filter for the metadata update notification and update 1996 * retrieval. The caller provides 2 set of metadata keys, allowed 1997 * and blocked. The blocked set always takes precedence over the 1998 * allowed one. 1999 * Metadata.MATCH_ALL and Metadata.MATCH_NONE are 2 sets available as 2000 * shorthands to allow/block all or no metadata. 2001 * 2002 * By default, there is no filter set. 2003 * 2004 * @param allow Is the set of metadata the client is interested 2005 * in receiving new notifications for. 2006 * @param block Is the set of metadata the client is not interested 2007 * in receiving new notifications for. 2008 * @return The call status code. 2009 * 2010 // FIXME: unhide. 2011 * {@hide} 2012 */ setMetadataFilter(Set<Integer> allow, Set<Integer> block)2013 public int setMetadataFilter(Set<Integer> allow, Set<Integer> block) { 2014 // Do our serialization manually instead of calling 2015 // Parcel.writeArray since the sets are made of the same type 2016 // we avoid paying the price of calling writeValue (used by 2017 // writeArray) which burns an extra int per element to encode 2018 // the type. 2019 Parcel request = newRequest(); 2020 2021 // The parcel starts already with an interface token. There 2022 // are 2 filters. Each one starts with a 4bytes number to 2023 // store the len followed by a number of int (4 bytes as well) 2024 // representing the metadata type. 2025 int capacity = request.dataSize() + 4 * (1 + allow.size() + 1 + block.size()); 2026 2027 if (request.dataCapacity() < capacity) { 2028 request.setDataCapacity(capacity); 2029 } 2030 2031 request.writeInt(allow.size()); 2032 for(Integer t: allow) { 2033 request.writeInt(t); 2034 } 2035 request.writeInt(block.size()); 2036 for(Integer t: block) { 2037 request.writeInt(t); 2038 } 2039 return native_setMetadataFilter(request); 2040 } 2041 2042 /** 2043 * Set the MediaPlayer to start when this MediaPlayer finishes playback 2044 * (i.e. reaches the end of the stream). 2045 * The media framework will attempt to transition from this player to 2046 * the next as seamlessly as possible. The next player can be set at 2047 * any time before completion, but shall be after setDataSource has been 2048 * called successfully. The next player must be prepared by the 2049 * app, and the application should not call start() on it. 2050 * The next MediaPlayer must be different from 'this'. An exception 2051 * will be thrown if next == this. 2052 * The application may call setNextMediaPlayer(null) to indicate no 2053 * next player should be started at the end of playback. 2054 * If the current player is looping, it will keep looping and the next 2055 * player will not be started. 2056 * 2057 * @param next the player to start after this one completes playback. 2058 * 2059 */ setNextMediaPlayer(MediaPlayer next)2060 public native void setNextMediaPlayer(MediaPlayer next); 2061 2062 /** 2063 * Releases resources associated with this MediaPlayer object. 2064 * It is considered good practice to call this method when you're 2065 * done using the MediaPlayer. In particular, whenever an Activity 2066 * of an application is paused (its onPause() method is called), 2067 * or stopped (its onStop() method is called), this method should be 2068 * invoked to release the MediaPlayer object, unless the application 2069 * has a special need to keep the object around. In addition to 2070 * unnecessary resources (such as memory and instances of codecs) 2071 * being held, failure to call this method immediately if a 2072 * MediaPlayer object is no longer needed may also lead to 2073 * continuous battery consumption for mobile devices, and playback 2074 * failure for other applications if no multiple instances of the 2075 * same codec are supported on a device. Even if multiple instances 2076 * of the same codec are supported, some performance degradation 2077 * may be expected when unnecessary multiple instances are used 2078 * at the same time. 2079 */ release()2080 public void release() { 2081 baseRelease(); 2082 stayAwake(false); 2083 updateSurfaceScreenOn(); 2084 mOnPreparedListener = null; 2085 mOnBufferingUpdateListener = null; 2086 mOnCompletionListener = null; 2087 mOnSeekCompleteListener = null; 2088 mOnErrorListener = null; 2089 mOnInfoListener = null; 2090 mOnVideoSizeChangedListener = null; 2091 mOnTimedTextListener = null; 2092 synchronized (mTimeProviderLock) { 2093 if (mTimeProvider != null) { 2094 mTimeProvider.close(); 2095 mTimeProvider = null; 2096 } 2097 } 2098 synchronized(this) { 2099 mSubtitleDataListenerDisabled = false; 2100 mExtSubtitleDataListener = null; 2101 mExtSubtitleDataHandler = null; 2102 mOnMediaTimeDiscontinuityListener = null; 2103 mOnMediaTimeDiscontinuityHandler = null; 2104 } 2105 2106 // Modular DRM clean up 2107 mOnDrmConfigHelper = null; 2108 mOnDrmInfoHandlerDelegate = null; 2109 mOnDrmPreparedHandlerDelegate = null; 2110 resetDrmState(); 2111 2112 _release(); 2113 } 2114 _release()2115 private native void _release(); 2116 2117 /** 2118 * Resets the MediaPlayer to its uninitialized state. After calling 2119 * this method, you will have to initialize it again by setting the 2120 * data source and calling prepare(). 2121 */ reset()2122 public void reset() { 2123 mSelectedSubtitleTrackIndex = -1; 2124 synchronized(mOpenSubtitleSources) { 2125 for (final InputStream is: mOpenSubtitleSources) { 2126 try { 2127 is.close(); 2128 } catch (IOException e) { 2129 } 2130 } 2131 mOpenSubtitleSources.clear(); 2132 } 2133 if (mSubtitleController != null) { 2134 mSubtitleController.reset(); 2135 } 2136 synchronized (mTimeProviderLock) { 2137 if (mTimeProvider != null) { 2138 mTimeProvider.close(); 2139 mTimeProvider = null; 2140 } 2141 } 2142 2143 stayAwake(false); 2144 _reset(); 2145 // make sure none of the listeners get called anymore 2146 if (mEventHandler != null) { 2147 mEventHandler.removeCallbacksAndMessages(null); 2148 } 2149 2150 synchronized (mIndexTrackPairs) { 2151 mIndexTrackPairs.clear(); 2152 mInbandTrackIndices.clear(); 2153 }; 2154 2155 resetDrmState(); 2156 } 2157 _reset()2158 private native void _reset(); 2159 2160 /** 2161 * Set up a timer for {@link #TimeProvider}. {@link #TimeProvider} will be 2162 * notified when the presentation time reaches (becomes greater than or equal to) 2163 * the value specified. 2164 * 2165 * @param mediaTimeUs presentation time to get timed event callback at 2166 * @hide 2167 */ notifyAt(long mediaTimeUs)2168 public void notifyAt(long mediaTimeUs) { 2169 _notifyAt(mediaTimeUs); 2170 } 2171 _notifyAt(long mediaTimeUs)2172 private native void _notifyAt(long mediaTimeUs); 2173 2174 /** 2175 * Sets the audio stream type for this MediaPlayer. See {@link AudioManager} 2176 * for a list of stream types. Must call this method before prepare() or 2177 * prepareAsync() in order for the target stream type to become effective 2178 * thereafter. 2179 * 2180 * @param streamtype the audio stream type 2181 * @deprecated use {@link #setAudioAttributes(AudioAttributes)} 2182 * @see android.media.AudioManager 2183 */ setAudioStreamType(int streamtype)2184 public void setAudioStreamType(int streamtype) { 2185 deprecateStreamTypeForPlayback(streamtype, "MediaPlayer", "setAudioStreamType()"); 2186 baseUpdateAudioAttributes( 2187 new AudioAttributes.Builder().setInternalLegacyStreamType(streamtype).build()); 2188 _setAudioStreamType(streamtype); 2189 mStreamType = streamtype; 2190 } 2191 _setAudioStreamType(int streamtype)2192 private native void _setAudioStreamType(int streamtype); 2193 2194 // Keep KEY_PARAMETER_* in sync with include/media/mediaplayer.h 2195 private final static int KEY_PARAMETER_AUDIO_ATTRIBUTES = 1400; 2196 /** 2197 * Sets the parameter indicated by key. 2198 * @param key key indicates the parameter to be set. 2199 * @param value value of the parameter to be set. 2200 * @return true if the parameter is set successfully, false otherwise 2201 * {@hide} 2202 */ 2203 @UnsupportedAppUsage setParameter(int key, Parcel value)2204 private native boolean setParameter(int key, Parcel value); 2205 2206 /** 2207 * Sets the audio attributes for this MediaPlayer. 2208 * See {@link AudioAttributes} for how to build and configure an instance of this class. 2209 * You must call this method before {@link #prepare()} or {@link #prepareAsync()} in order 2210 * for the audio attributes to become effective thereafter. 2211 * @param attributes a non-null set of audio attributes 2212 */ setAudioAttributes(AudioAttributes attributes)2213 public void setAudioAttributes(AudioAttributes attributes) throws IllegalArgumentException { 2214 if (attributes == null) { 2215 final String msg = "Cannot set AudioAttributes to null"; 2216 throw new IllegalArgumentException(msg); 2217 } 2218 baseUpdateAudioAttributes(attributes); 2219 mUsage = attributes.getUsage(); 2220 Parcel pattributes = Parcel.obtain(); 2221 attributes.writeToParcel(pattributes, AudioAttributes.FLATTEN_TAGS); 2222 setParameter(KEY_PARAMETER_AUDIO_ATTRIBUTES, pattributes); 2223 pattributes.recycle(); 2224 } 2225 2226 /** 2227 * Sets the player to be looping or non-looping. 2228 * 2229 * @param looping whether to loop or not 2230 */ setLooping(boolean looping)2231 public native void setLooping(boolean looping); 2232 2233 /** 2234 * Checks whether the MediaPlayer is looping or non-looping. 2235 * 2236 * @return true if the MediaPlayer is currently looping, false otherwise 2237 */ isLooping()2238 public native boolean isLooping(); 2239 2240 /** 2241 * Sets the volume on this player. 2242 * This API is recommended for balancing the output of audio streams 2243 * within an application. Unless you are writing an application to 2244 * control user settings, this API should be used in preference to 2245 * {@link AudioManager#setStreamVolume(int, int, int)} which sets the volume of ALL streams of 2246 * a particular type. Note that the passed volume values are raw scalars in range 0.0 to 1.0. 2247 * UI controls should be scaled logarithmically. 2248 * 2249 * @param leftVolume left volume scalar 2250 * @param rightVolume right volume scalar 2251 */ 2252 /* 2253 * FIXME: Merge this into javadoc comment above when setVolume(float) is not @hide. 2254 * The single parameter form below is preferred if the channel volumes don't need 2255 * to be set independently. 2256 */ setVolume(float leftVolume, float rightVolume)2257 public void setVolume(float leftVolume, float rightVolume) { 2258 baseSetVolume(leftVolume, rightVolume); 2259 } 2260 2261 @Override playerSetVolume(boolean muting, float leftVolume, float rightVolume)2262 void playerSetVolume(boolean muting, float leftVolume, float rightVolume) { 2263 _setVolume(muting ? 0.0f : leftVolume, muting ? 0.0f : rightVolume); 2264 } 2265 _setVolume(float leftVolume, float rightVolume)2266 private native void _setVolume(float leftVolume, float rightVolume); 2267 2268 /** 2269 * Similar, excepts sets volume of all channels to same value. 2270 * @hide 2271 */ setVolume(float volume)2272 public void setVolume(float volume) { 2273 setVolume(volume, volume); 2274 } 2275 2276 /** 2277 * Sets the audio session ID. 2278 * 2279 * @param sessionId the audio session ID. 2280 * The audio session ID is a system wide unique identifier for the audio stream played by 2281 * this MediaPlayer instance. 2282 * The primary use of the audio session ID is to associate audio effects to a particular 2283 * instance of MediaPlayer: if an audio session ID is provided when creating an audio effect, 2284 * this effect will be applied only to the audio content of media players within the same 2285 * audio session and not to the output mix. 2286 * When created, a MediaPlayer instance automatically generates its own audio session ID. 2287 * However, it is possible to force this player to be part of an already existing audio session 2288 * by calling this method. 2289 * This method must be called before one of the overloaded <code> setDataSource </code> methods. 2290 * @throws IllegalStateException if it is called in an invalid state 2291 */ setAudioSessionId(int sessionId)2292 public native void setAudioSessionId(int sessionId) throws IllegalArgumentException, IllegalStateException; 2293 2294 /** 2295 * Returns the audio session ID. 2296 * 2297 * @return the audio session ID. {@see #setAudioSessionId(int)} 2298 * Note that the audio session ID is 0 only if a problem occured when the MediaPlayer was contructed. 2299 */ getAudioSessionId()2300 public native int getAudioSessionId(); 2301 2302 /** 2303 * Attaches an auxiliary effect to the player. A typical auxiliary effect is a reverberation 2304 * effect which can be applied on any sound source that directs a certain amount of its 2305 * energy to this effect. This amount is defined by setAuxEffectSendLevel(). 2306 * See {@link #setAuxEffectSendLevel(float)}. 2307 * <p>After creating an auxiliary effect (e.g. 2308 * {@link android.media.audiofx.EnvironmentalReverb}), retrieve its ID with 2309 * {@link android.media.audiofx.AudioEffect#getId()} and use it when calling this method 2310 * to attach the player to the effect. 2311 * <p>To detach the effect from the player, call this method with a null effect id. 2312 * <p>This method must be called after one of the overloaded <code> setDataSource </code> 2313 * methods. 2314 * @param effectId system wide unique id of the effect to attach 2315 */ attachAuxEffect(int effectId)2316 public native void attachAuxEffect(int effectId); 2317 2318 2319 /** 2320 * Sets the send level of the player to the attached auxiliary effect. 2321 * See {@link #attachAuxEffect(int)}. The level value range is 0 to 1.0. 2322 * <p>By default the send level is 0, so even if an effect is attached to the player 2323 * this method must be called for the effect to be applied. 2324 * <p>Note that the passed level value is a raw scalar. UI controls should be scaled 2325 * logarithmically: the gain applied by audio framework ranges from -72dB to 0dB, 2326 * so an appropriate conversion from linear UI input x to level is: 2327 * x == 0 -> level = 0 2328 * 0 < x <= R -> level = 10^(72*(x-R)/20/R) 2329 * @param level send level scalar 2330 */ setAuxEffectSendLevel(float level)2331 public void setAuxEffectSendLevel(float level) { 2332 baseSetAuxEffectSendLevel(level); 2333 } 2334 2335 @Override playerSetAuxEffectSendLevel(boolean muting, float level)2336 int playerSetAuxEffectSendLevel(boolean muting, float level) { 2337 _setAuxEffectSendLevel(muting ? 0.0f : level); 2338 return AudioSystem.SUCCESS; 2339 } 2340 _setAuxEffectSendLevel(float level)2341 private native void _setAuxEffectSendLevel(float level); 2342 2343 /* 2344 * @param request Parcel destinated to the media player. The 2345 * Interface token must be set to the IMediaPlayer 2346 * one to be routed correctly through the system. 2347 * @param reply[out] Parcel that will contain the reply. 2348 * @return The status code. 2349 */ native_invoke(Parcel request, Parcel reply)2350 private native final int native_invoke(Parcel request, Parcel reply); 2351 2352 2353 /* 2354 * @param update_only If true fetch only the set of metadata that have 2355 * changed since the last invocation of getMetadata. 2356 * The set is built using the unfiltered 2357 * notifications the native player sent to the 2358 * MediaPlayerService during that period of 2359 * time. If false, all the metadatas are considered. 2360 * @param apply_filter If true, once the metadata set has been built based on 2361 * the value update_only, the current filter is applied. 2362 * @param reply[out] On return contains the serialized 2363 * metadata. Valid only if the call was successful. 2364 * @return The status code. 2365 */ native_getMetadata(boolean update_only, boolean apply_filter, Parcel reply)2366 private native final boolean native_getMetadata(boolean update_only, 2367 boolean apply_filter, 2368 Parcel reply); 2369 2370 /* 2371 * @param request Parcel with the 2 serialized lists of allowed 2372 * metadata types followed by the one to be 2373 * dropped. Each list starts with an integer 2374 * indicating the number of metadata type elements. 2375 * @return The status code. 2376 */ native_setMetadataFilter(Parcel request)2377 private native final int native_setMetadataFilter(Parcel request); 2378 native_init()2379 private static native final void native_init(); native_setup(Object mediaplayer_this)2380 private native final void native_setup(Object mediaplayer_this); native_finalize()2381 private native final void native_finalize(); 2382 2383 /** 2384 * Class for MediaPlayer to return each audio/video/subtitle track's metadata. 2385 * 2386 * @see android.media.MediaPlayer#getTrackInfo 2387 */ 2388 static public class TrackInfo implements Parcelable { 2389 /** 2390 * Gets the track type. 2391 * @return TrackType which indicates if the track is video, audio, timed text. 2392 */ getTrackType()2393 public @TrackType int getTrackType() { 2394 return mTrackType; 2395 } 2396 2397 /** 2398 * Gets the language code of the track. 2399 * @return a language code in either way of ISO-639-1 or ISO-639-2. 2400 * When the language is unknown or could not be determined, 2401 * ISO-639-2 language code, "und", is returned. 2402 */ getLanguage()2403 public String getLanguage() { 2404 String language = mFormat.getString(MediaFormat.KEY_LANGUAGE); 2405 return language == null ? "und" : language; 2406 } 2407 2408 /** 2409 * Gets the {@link MediaFormat} of the track. If the format is 2410 * unknown or could not be determined, null is returned. 2411 */ getFormat()2412 public MediaFormat getFormat() { 2413 if (mTrackType == MEDIA_TRACK_TYPE_TIMEDTEXT 2414 || mTrackType == MEDIA_TRACK_TYPE_SUBTITLE) { 2415 return mFormat; 2416 } 2417 return null; 2418 } 2419 2420 public static final int MEDIA_TRACK_TYPE_UNKNOWN = 0; 2421 public static final int MEDIA_TRACK_TYPE_VIDEO = 1; 2422 public static final int MEDIA_TRACK_TYPE_AUDIO = 2; 2423 public static final int MEDIA_TRACK_TYPE_TIMEDTEXT = 3; 2424 public static final int MEDIA_TRACK_TYPE_SUBTITLE = 4; 2425 public static final int MEDIA_TRACK_TYPE_METADATA = 5; 2426 2427 /** @hide */ 2428 @IntDef(flag = false, prefix = "MEDIA_TRACK_TYPE", value = { 2429 MEDIA_TRACK_TYPE_UNKNOWN, 2430 MEDIA_TRACK_TYPE_VIDEO, 2431 MEDIA_TRACK_TYPE_AUDIO, 2432 MEDIA_TRACK_TYPE_TIMEDTEXT, 2433 MEDIA_TRACK_TYPE_SUBTITLE, 2434 MEDIA_TRACK_TYPE_METADATA } 2435 ) 2436 @Retention(RetentionPolicy.SOURCE) 2437 public @interface TrackType {} 2438 2439 2440 final int mTrackType; 2441 final MediaFormat mFormat; 2442 TrackInfo(Parcel in)2443 TrackInfo(Parcel in) { 2444 mTrackType = in.readInt(); 2445 // TODO: parcel in the full MediaFormat; currently we are using createSubtitleFormat 2446 // even for audio/video tracks, meaning we only set the mime and language. 2447 String mime = in.readString(); 2448 String language = in.readString(); 2449 mFormat = MediaFormat.createSubtitleFormat(mime, language); 2450 2451 if (mTrackType == MEDIA_TRACK_TYPE_SUBTITLE) { 2452 mFormat.setInteger(MediaFormat.KEY_IS_AUTOSELECT, in.readInt()); 2453 mFormat.setInteger(MediaFormat.KEY_IS_DEFAULT, in.readInt()); 2454 mFormat.setInteger(MediaFormat.KEY_IS_FORCED_SUBTITLE, in.readInt()); 2455 } 2456 } 2457 2458 /** @hide */ TrackInfo(int type, MediaFormat format)2459 TrackInfo(int type, MediaFormat format) { 2460 mTrackType = type; 2461 mFormat = format; 2462 } 2463 2464 /** 2465 * {@inheritDoc} 2466 */ 2467 @Override describeContents()2468 public int describeContents() { 2469 return 0; 2470 } 2471 2472 /** 2473 * {@inheritDoc} 2474 */ 2475 @Override writeToParcel(Parcel dest, int flags)2476 public void writeToParcel(Parcel dest, int flags) { 2477 dest.writeInt(mTrackType); 2478 dest.writeString(mFormat.getString(MediaFormat.KEY_MIME)); 2479 dest.writeString(getLanguage()); 2480 2481 if (mTrackType == MEDIA_TRACK_TYPE_SUBTITLE) { 2482 dest.writeInt(mFormat.getInteger(MediaFormat.KEY_IS_AUTOSELECT)); 2483 dest.writeInt(mFormat.getInteger(MediaFormat.KEY_IS_DEFAULT)); 2484 dest.writeInt(mFormat.getInteger(MediaFormat.KEY_IS_FORCED_SUBTITLE)); 2485 } 2486 } 2487 2488 @Override toString()2489 public String toString() { 2490 StringBuilder out = new StringBuilder(128); 2491 out.append(getClass().getName()); 2492 out.append('{'); 2493 switch (mTrackType) { 2494 case MEDIA_TRACK_TYPE_VIDEO: 2495 out.append("VIDEO"); 2496 break; 2497 case MEDIA_TRACK_TYPE_AUDIO: 2498 out.append("AUDIO"); 2499 break; 2500 case MEDIA_TRACK_TYPE_TIMEDTEXT: 2501 out.append("TIMEDTEXT"); 2502 break; 2503 case MEDIA_TRACK_TYPE_SUBTITLE: 2504 out.append("SUBTITLE"); 2505 break; 2506 default: 2507 out.append("UNKNOWN"); 2508 break; 2509 } 2510 out.append(", " + mFormat.toString()); 2511 out.append("}"); 2512 return out.toString(); 2513 } 2514 2515 /** 2516 * Used to read a TrackInfo from a Parcel. 2517 */ 2518 @UnsupportedAppUsage 2519 static final @android.annotation.NonNull Parcelable.Creator<TrackInfo> CREATOR 2520 = new Parcelable.Creator<TrackInfo>() { 2521 @Override 2522 public TrackInfo createFromParcel(Parcel in) { 2523 return new TrackInfo(in); 2524 } 2525 2526 @Override 2527 public TrackInfo[] newArray(int size) { 2528 return new TrackInfo[size]; 2529 } 2530 }; 2531 2532 }; 2533 2534 // We would like domain specific classes with more informative names than the `first` and `second` 2535 // in generic Pair, but we would also like to avoid creating new/trivial classes. As a compromise 2536 // we document the meanings of `first` and `second` here: 2537 // 2538 // Pair.first - inband track index; non-null iff representing an inband track. 2539 // Pair.second - a SubtitleTrack registered with mSubtitleController; non-null iff representing 2540 // an inband subtitle track or any out-of-band track (subtitle or timedtext). 2541 private Vector<Pair<Integer, SubtitleTrack>> mIndexTrackPairs = new Vector<>(); 2542 private BitSet mInbandTrackIndices = new BitSet(); 2543 2544 /** 2545 * Returns an array of track information. 2546 * 2547 * @return Array of track info. The total number of tracks is the array length. 2548 * Must be called again if an external timed text source has been added after any of the 2549 * addTimedTextSource methods are called. 2550 * @throws IllegalStateException if it is called in an invalid state. 2551 */ getTrackInfo()2552 public TrackInfo[] getTrackInfo() throws IllegalStateException { 2553 TrackInfo trackInfo[] = getInbandTrackInfo(); 2554 // add out-of-band tracks 2555 synchronized (mIndexTrackPairs) { 2556 TrackInfo allTrackInfo[] = new TrackInfo[mIndexTrackPairs.size()]; 2557 for (int i = 0; i < allTrackInfo.length; i++) { 2558 Pair<Integer, SubtitleTrack> p = mIndexTrackPairs.get(i); 2559 if (p.first != null) { 2560 // inband track 2561 allTrackInfo[i] = trackInfo[p.first]; 2562 } else { 2563 SubtitleTrack track = p.second; 2564 allTrackInfo[i] = new TrackInfo(track.getTrackType(), track.getFormat()); 2565 } 2566 } 2567 return allTrackInfo; 2568 } 2569 } 2570 getInbandTrackInfo()2571 private TrackInfo[] getInbandTrackInfo() throws IllegalStateException { 2572 Parcel request = Parcel.obtain(); 2573 Parcel reply = Parcel.obtain(); 2574 try { 2575 request.writeInterfaceToken(IMEDIA_PLAYER); 2576 request.writeInt(INVOKE_ID_GET_TRACK_INFO); 2577 invoke(request, reply); 2578 TrackInfo trackInfo[] = reply.createTypedArray(TrackInfo.CREATOR); 2579 return trackInfo; 2580 } finally { 2581 request.recycle(); 2582 reply.recycle(); 2583 } 2584 } 2585 2586 /* Do not change these values without updating their counterparts 2587 * in include/media/stagefright/MediaDefs.h and media/libstagefright/MediaDefs.cpp! 2588 */ 2589 /** 2590 * MIME type for SubRip (SRT) container. Used in addTimedTextSource APIs. 2591 * @deprecated use {@link MediaFormat#MIMETYPE_TEXT_SUBRIP} 2592 */ 2593 public static final String MEDIA_MIMETYPE_TEXT_SUBRIP = MediaFormat.MIMETYPE_TEXT_SUBRIP; 2594 2595 /** 2596 * MIME type for WebVTT subtitle data. 2597 * @hide 2598 * @deprecated 2599 */ 2600 public static final String MEDIA_MIMETYPE_TEXT_VTT = MediaFormat.MIMETYPE_TEXT_VTT; 2601 2602 /** 2603 * MIME type for CEA-608 closed caption data. 2604 * @hide 2605 * @deprecated 2606 */ 2607 public static final String MEDIA_MIMETYPE_TEXT_CEA_608 = MediaFormat.MIMETYPE_TEXT_CEA_608; 2608 2609 /** 2610 * MIME type for CEA-708 closed caption data. 2611 * @hide 2612 * @deprecated 2613 */ 2614 public static final String MEDIA_MIMETYPE_TEXT_CEA_708 = MediaFormat.MIMETYPE_TEXT_CEA_708; 2615 2616 /* 2617 * A helper function to check if the mime type is supported by media framework. 2618 */ availableMimeTypeForExternalSource(String mimeType)2619 private static boolean availableMimeTypeForExternalSource(String mimeType) { 2620 if (MEDIA_MIMETYPE_TEXT_SUBRIP.equals(mimeType)) { 2621 return true; 2622 } 2623 return false; 2624 } 2625 2626 private SubtitleController mSubtitleController; 2627 2628 /** @hide */ 2629 @UnsupportedAppUsage setSubtitleAnchor( SubtitleController controller, SubtitleController.Anchor anchor)2630 public void setSubtitleAnchor( 2631 SubtitleController controller, 2632 SubtitleController.Anchor anchor) { 2633 // TODO: create SubtitleController in MediaPlayer 2634 mSubtitleController = controller; 2635 mSubtitleController.setAnchor(anchor); 2636 } 2637 2638 /** 2639 * The private version of setSubtitleAnchor is used internally to set mSubtitleController if 2640 * necessary when clients don't provide their own SubtitleControllers using the public version 2641 * {@link #setSubtitleAnchor(SubtitleController, Anchor)} (e.g. {@link VideoView} provides one). 2642 */ setSubtitleAnchor()2643 private synchronized void setSubtitleAnchor() { 2644 if ((mSubtitleController == null) && (ActivityThread.currentApplication() != null)) { 2645 final TimeProvider timeProvider = (TimeProvider) getMediaTimeProvider(); 2646 final HandlerThread thread = new HandlerThread("SetSubtitleAnchorThread"); 2647 thread.start(); 2648 Handler handler = new Handler(thread.getLooper()); 2649 handler.post(new Runnable() { 2650 @Override 2651 public void run() { 2652 Context context = ActivityThread.currentApplication(); 2653 mSubtitleController = 2654 new SubtitleController(context, timeProvider, MediaPlayer.this); 2655 mSubtitleController.setAnchor(new Anchor() { 2656 @Override 2657 public void setSubtitleWidget(RenderingWidget subtitleWidget) { 2658 } 2659 2660 @Override 2661 public Looper getSubtitleLooper() { 2662 return timeProvider.mEventHandler.getLooper(); 2663 } 2664 }); 2665 thread.getLooper().quitSafely(); 2666 } 2667 }); 2668 try { 2669 thread.join(); 2670 } catch (InterruptedException e) { 2671 Thread.currentThread().interrupt(); 2672 Log.w(TAG, "failed to join SetSubtitleAnchorThread"); 2673 } 2674 } 2675 } 2676 2677 private int mSelectedSubtitleTrackIndex = -1; 2678 private Vector<InputStream> mOpenSubtitleSources; 2679 2680 private final OnSubtitleDataListener mIntSubtitleDataListener = new OnSubtitleDataListener() { 2681 @Override 2682 public void onSubtitleData(MediaPlayer mp, SubtitleData data) { 2683 int index = data.getTrackIndex(); 2684 synchronized (mIndexTrackPairs) { 2685 for (Pair<Integer, SubtitleTrack> p : mIndexTrackPairs) { 2686 if (p.first != null && p.first == index && p.second != null) { 2687 // inband subtitle track that owns data 2688 SubtitleTrack track = p.second; 2689 track.onData(data); 2690 } 2691 } 2692 } 2693 } 2694 }; 2695 2696 /** @hide */ 2697 @Override onSubtitleTrackSelected(SubtitleTrack track)2698 public void onSubtitleTrackSelected(SubtitleTrack track) { 2699 if (mSelectedSubtitleTrackIndex >= 0) { 2700 try { 2701 selectOrDeselectInbandTrack(mSelectedSubtitleTrackIndex, false); 2702 } catch (IllegalStateException e) { 2703 } 2704 mSelectedSubtitleTrackIndex = -1; 2705 } 2706 synchronized (this) { 2707 mSubtitleDataListenerDisabled = true; 2708 } 2709 if (track == null) { 2710 return; 2711 } 2712 2713 synchronized (mIndexTrackPairs) { 2714 for (Pair<Integer, SubtitleTrack> p : mIndexTrackPairs) { 2715 if (p.first != null && p.second == track) { 2716 // inband subtitle track that is selected 2717 mSelectedSubtitleTrackIndex = p.first; 2718 break; 2719 } 2720 } 2721 } 2722 2723 if (mSelectedSubtitleTrackIndex >= 0) { 2724 try { 2725 selectOrDeselectInbandTrack(mSelectedSubtitleTrackIndex, true); 2726 } catch (IllegalStateException e) { 2727 } 2728 synchronized (this) { 2729 mSubtitleDataListenerDisabled = false; 2730 } 2731 } 2732 // no need to select out-of-band tracks 2733 } 2734 2735 /** @hide */ 2736 @UnsupportedAppUsage addSubtitleSource(InputStream is, MediaFormat format)2737 public void addSubtitleSource(InputStream is, MediaFormat format) 2738 throws IllegalStateException 2739 { 2740 final InputStream fIs = is; 2741 final MediaFormat fFormat = format; 2742 2743 if (is != null) { 2744 // Ensure all input streams are closed. It is also a handy 2745 // way to implement timeouts in the future. 2746 synchronized(mOpenSubtitleSources) { 2747 mOpenSubtitleSources.add(is); 2748 } 2749 } else { 2750 Log.w(TAG, "addSubtitleSource called with null InputStream"); 2751 } 2752 2753 getMediaTimeProvider(); 2754 2755 // process each subtitle in its own thread 2756 final HandlerThread thread = new HandlerThread("SubtitleReadThread", 2757 Process.THREAD_PRIORITY_BACKGROUND + Process.THREAD_PRIORITY_MORE_FAVORABLE); 2758 thread.start(); 2759 Handler handler = new Handler(thread.getLooper()); 2760 handler.post(new Runnable() { 2761 private int addTrack() { 2762 if (fIs == null || mSubtitleController == null) { 2763 return MEDIA_INFO_UNSUPPORTED_SUBTITLE; 2764 } 2765 2766 SubtitleTrack track = mSubtitleController.addTrack(fFormat); 2767 if (track == null) { 2768 return MEDIA_INFO_UNSUPPORTED_SUBTITLE; 2769 } 2770 2771 // TODO: do the conversion in the subtitle track 2772 Scanner scanner = new Scanner(fIs, "UTF-8"); 2773 String contents = scanner.useDelimiter("\\A").next(); 2774 synchronized(mOpenSubtitleSources) { 2775 mOpenSubtitleSources.remove(fIs); 2776 } 2777 scanner.close(); 2778 synchronized (mIndexTrackPairs) { 2779 mIndexTrackPairs.add(Pair.<Integer, SubtitleTrack>create(null, track)); 2780 } 2781 synchronized (mTimeProviderLock) { 2782 if (mTimeProvider != null) { 2783 Handler h = mTimeProvider.mEventHandler; 2784 int what = TimeProvider.NOTIFY; 2785 int arg1 = TimeProvider.NOTIFY_TRACK_DATA; 2786 Pair<SubtitleTrack, byte[]> trackData = 2787 Pair.create(track, contents.getBytes()); 2788 Message m = h.obtainMessage(what, arg1, 0, trackData); 2789 h.sendMessage(m); 2790 } 2791 } 2792 return MEDIA_INFO_EXTERNAL_METADATA_UPDATE; 2793 } 2794 2795 public void run() { 2796 int res = addTrack(); 2797 if (mEventHandler != null) { 2798 Message m = mEventHandler.obtainMessage(MEDIA_INFO, res, 0, null); 2799 mEventHandler.sendMessage(m); 2800 } 2801 thread.getLooper().quitSafely(); 2802 } 2803 }); 2804 } 2805 scanInternalSubtitleTracks()2806 private void scanInternalSubtitleTracks() { 2807 setSubtitleAnchor(); 2808 2809 populateInbandTracks(); 2810 2811 if (mSubtitleController != null) { 2812 mSubtitleController.selectDefaultTrack(); 2813 } 2814 } 2815 populateInbandTracks()2816 private void populateInbandTracks() { 2817 TrackInfo[] tracks = getInbandTrackInfo(); 2818 synchronized (mIndexTrackPairs) { 2819 for (int i = 0; i < tracks.length; i++) { 2820 if (mInbandTrackIndices.get(i)) { 2821 continue; 2822 } else { 2823 mInbandTrackIndices.set(i); 2824 } 2825 2826 if (tracks[i] == null) { 2827 Log.w(TAG, "unexpected NULL track at index " + i); 2828 } 2829 // newly appeared inband track 2830 if (tracks[i] != null 2831 && tracks[i].getTrackType() == TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE) { 2832 SubtitleTrack track = mSubtitleController.addTrack( 2833 tracks[i].getFormat()); 2834 mIndexTrackPairs.add(Pair.create(i, track)); 2835 } else { 2836 mIndexTrackPairs.add(Pair.<Integer, SubtitleTrack>create(i, null)); 2837 } 2838 } 2839 } 2840 } 2841 2842 /* TODO: Limit the total number of external timed text source to a reasonable number. 2843 */ 2844 /** 2845 * Adds an external timed text source file. 2846 * 2847 * Currently supported format is SubRip with the file extension .srt, case insensitive. 2848 * Note that a single external timed text source may contain multiple tracks in it. 2849 * One can find the total number of available tracks using {@link #getTrackInfo()} to see what 2850 * additional tracks become available after this method call. 2851 * 2852 * @param path The file path of external timed text source file. 2853 * @param mimeType The mime type of the file. Must be one of the mime types listed above. 2854 * @throws IOException if the file cannot be accessed or is corrupted. 2855 * @throws IllegalArgumentException if the mimeType is not supported. 2856 * @throws IllegalStateException if called in an invalid state. 2857 */ addTimedTextSource(String path, String mimeType)2858 public void addTimedTextSource(String path, String mimeType) 2859 throws IOException, IllegalArgumentException, IllegalStateException { 2860 if (!availableMimeTypeForExternalSource(mimeType)) { 2861 final String msg = "Illegal mimeType for timed text source: " + mimeType; 2862 throw new IllegalArgumentException(msg); 2863 } 2864 2865 final File file = new File(path); 2866 try (FileInputStream is = new FileInputStream(file)) { 2867 addTimedTextSource(is.getFD(), mimeType); 2868 } 2869 } 2870 2871 /** 2872 * Adds an external timed text source file (Uri). 2873 * 2874 * Currently supported format is SubRip with the file extension .srt, case insensitive. 2875 * Note that a single external timed text source may contain multiple tracks in it. 2876 * One can find the total number of available tracks using {@link #getTrackInfo()} to see what 2877 * additional tracks become available after this method call. 2878 * 2879 * @param context the Context to use when resolving the Uri 2880 * @param uri the Content URI of the data you want to play 2881 * @param mimeType The mime type of the file. Must be one of the mime types listed above. 2882 * @throws IOException if the file cannot be accessed or is corrupted. 2883 * @throws IllegalArgumentException if the mimeType is not supported. 2884 * @throws IllegalStateException if called in an invalid state. 2885 */ addTimedTextSource(Context context, Uri uri, String mimeType)2886 public void addTimedTextSource(Context context, Uri uri, String mimeType) 2887 throws IOException, IllegalArgumentException, IllegalStateException { 2888 String scheme = uri.getScheme(); 2889 if(scheme == null || scheme.equals("file")) { 2890 addTimedTextSource(uri.getPath(), mimeType); 2891 return; 2892 } 2893 2894 AssetFileDescriptor fd = null; 2895 try { 2896 ContentResolver resolver = context.getContentResolver(); 2897 fd = resolver.openAssetFileDescriptor(uri, "r"); 2898 if (fd == null) { 2899 return; 2900 } 2901 addTimedTextSource(fd.getFileDescriptor(), mimeType); 2902 return; 2903 } catch (SecurityException ex) { 2904 } catch (IOException ex) { 2905 } finally { 2906 if (fd != null) { 2907 fd.close(); 2908 } 2909 } 2910 } 2911 2912 /** 2913 * Adds an external timed text source file (FileDescriptor). 2914 * 2915 * It is the caller's responsibility to close the file descriptor. 2916 * It is safe to do so as soon as this call returns. 2917 * 2918 * Currently supported format is SubRip. Note that a single external timed text source may 2919 * contain multiple tracks in it. One can find the total number of available tracks 2920 * using {@link #getTrackInfo()} to see what additional tracks become available 2921 * after this method call. 2922 * 2923 * @param fd the FileDescriptor for the file you want to play 2924 * @param mimeType The mime type of the file. Must be one of the mime types listed above. 2925 * @throws IllegalArgumentException if the mimeType is not supported. 2926 * @throws IllegalStateException if called in an invalid state. 2927 */ addTimedTextSource(FileDescriptor fd, String mimeType)2928 public void addTimedTextSource(FileDescriptor fd, String mimeType) 2929 throws IllegalArgumentException, IllegalStateException { 2930 // intentionally less than LONG_MAX 2931 addTimedTextSource(fd, 0, 0x7ffffffffffffffL, mimeType); 2932 } 2933 2934 /** 2935 * Adds an external timed text file (FileDescriptor). 2936 * 2937 * It is the caller's responsibility to close the file descriptor. 2938 * It is safe to do so as soon as this call returns. 2939 * 2940 * Currently supported format is SubRip. Note that a single external timed text source may 2941 * contain multiple tracks in it. One can find the total number of available tracks 2942 * using {@link #getTrackInfo()} to see what additional tracks become available 2943 * after this method call. 2944 * 2945 * @param fd the FileDescriptor for the file you want to play 2946 * @param offset the offset into the file where the data to be played starts, in bytes 2947 * @param length the length in bytes of the data to be played 2948 * @param mime The mime type of the file. Must be one of the mime types listed above. 2949 * @throws IllegalArgumentException if the mimeType is not supported. 2950 * @throws IllegalStateException if called in an invalid state. 2951 */ addTimedTextSource(FileDescriptor fd, long offset, long length, String mime)2952 public void addTimedTextSource(FileDescriptor fd, long offset, long length, String mime) 2953 throws IllegalArgumentException, IllegalStateException { 2954 if (!availableMimeTypeForExternalSource(mime)) { 2955 throw new IllegalArgumentException("Illegal mimeType for timed text source: " + mime); 2956 } 2957 2958 final FileDescriptor dupedFd; 2959 try { 2960 dupedFd = Os.dup(fd); 2961 } catch (ErrnoException ex) { 2962 Log.e(TAG, ex.getMessage(), ex); 2963 throw new RuntimeException(ex); 2964 } 2965 2966 final MediaFormat fFormat = new MediaFormat(); 2967 fFormat.setString(MediaFormat.KEY_MIME, mime); 2968 fFormat.setInteger(MediaFormat.KEY_IS_TIMED_TEXT, 1); 2969 2970 // A MediaPlayer created by a VideoView should already have its mSubtitleController set. 2971 if (mSubtitleController == null) { 2972 setSubtitleAnchor(); 2973 } 2974 2975 if (!mSubtitleController.hasRendererFor(fFormat)) { 2976 // test and add not atomic 2977 Context context = ActivityThread.currentApplication(); 2978 mSubtitleController.registerRenderer(new SRTRenderer(context, mEventHandler)); 2979 } 2980 final SubtitleTrack track = mSubtitleController.addTrack(fFormat); 2981 synchronized (mIndexTrackPairs) { 2982 mIndexTrackPairs.add(Pair.<Integer, SubtitleTrack>create(null, track)); 2983 } 2984 2985 getMediaTimeProvider(); 2986 2987 final long offset2 = offset; 2988 final long length2 = length; 2989 final HandlerThread thread = new HandlerThread( 2990 "TimedTextReadThread", 2991 Process.THREAD_PRIORITY_BACKGROUND + Process.THREAD_PRIORITY_MORE_FAVORABLE); 2992 thread.start(); 2993 Handler handler = new Handler(thread.getLooper()); 2994 handler.post(new Runnable() { 2995 private int addTrack() { 2996 final ByteArrayOutputStream bos = new ByteArrayOutputStream(); 2997 try { 2998 Os.lseek(dupedFd, offset2, OsConstants.SEEK_SET); 2999 byte[] buffer = new byte[4096]; 3000 for (long total = 0; total < length2;) { 3001 int bytesToRead = (int) Math.min(buffer.length, length2 - total); 3002 int bytes = IoBridge.read(dupedFd, buffer, 0, bytesToRead); 3003 if (bytes < 0) { 3004 break; 3005 } else { 3006 bos.write(buffer, 0, bytes); 3007 total += bytes; 3008 } 3009 } 3010 synchronized (mTimeProviderLock) { 3011 if (mTimeProvider != null) { 3012 Handler h = mTimeProvider.mEventHandler; 3013 int what = TimeProvider.NOTIFY; 3014 int arg1 = TimeProvider.NOTIFY_TRACK_DATA; 3015 Pair<SubtitleTrack, byte[]> trackData = 3016 Pair.create(track, bos.toByteArray()); 3017 Message m = h.obtainMessage(what, arg1, 0, trackData); 3018 h.sendMessage(m); 3019 } 3020 } 3021 return MEDIA_INFO_EXTERNAL_METADATA_UPDATE; 3022 } catch (Exception e) { 3023 Log.e(TAG, e.getMessage(), e); 3024 return MEDIA_INFO_TIMED_TEXT_ERROR; 3025 } finally { 3026 try { 3027 Os.close(dupedFd); 3028 } catch (ErrnoException e) { 3029 Log.e(TAG, e.getMessage(), e); 3030 } 3031 } 3032 } 3033 3034 public void run() { 3035 int res = addTrack(); 3036 if (mEventHandler != null) { 3037 Message m = mEventHandler.obtainMessage(MEDIA_INFO, res, 0, null); 3038 mEventHandler.sendMessage(m); 3039 } 3040 thread.getLooper().quitSafely(); 3041 } 3042 }); 3043 } 3044 3045 /** 3046 * Returns the index of the audio, video, or subtitle track currently selected for playback, 3047 * The return value is an index into the array returned by {@link #getTrackInfo()}, and can 3048 * be used in calls to {@link #selectTrack(int)} or {@link #deselectTrack(int)}. 3049 * 3050 * @param trackType should be one of {@link TrackInfo#MEDIA_TRACK_TYPE_VIDEO}, 3051 * {@link TrackInfo#MEDIA_TRACK_TYPE_AUDIO}, or 3052 * {@link TrackInfo#MEDIA_TRACK_TYPE_SUBTITLE} 3053 * @return index of the audio, video, or subtitle track currently selected for playback; 3054 * a negative integer is returned when there is no selected track for {@code trackType} or 3055 * when {@code trackType} is not one of audio, video, or subtitle. 3056 * @throws IllegalStateException if called after {@link #release()} 3057 * 3058 * @see #getTrackInfo() 3059 * @see #selectTrack(int) 3060 * @see #deselectTrack(int) 3061 */ getSelectedTrack(int trackType)3062 public int getSelectedTrack(int trackType) throws IllegalStateException { 3063 if (mSubtitleController != null 3064 && (trackType == TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE 3065 || trackType == TrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT)) { 3066 SubtitleTrack subtitleTrack = mSubtitleController.getSelectedTrack(); 3067 if (subtitleTrack != null) { 3068 synchronized (mIndexTrackPairs) { 3069 for (int i = 0; i < mIndexTrackPairs.size(); i++) { 3070 Pair<Integer, SubtitleTrack> p = mIndexTrackPairs.get(i); 3071 if (p.second == subtitleTrack && subtitleTrack.getTrackType() == trackType) { 3072 return i; 3073 } 3074 } 3075 } 3076 } 3077 } 3078 3079 Parcel request = Parcel.obtain(); 3080 Parcel reply = Parcel.obtain(); 3081 try { 3082 request.writeInterfaceToken(IMEDIA_PLAYER); 3083 request.writeInt(INVOKE_ID_GET_SELECTED_TRACK); 3084 request.writeInt(trackType); 3085 invoke(request, reply); 3086 int inbandTrackIndex = reply.readInt(); 3087 synchronized (mIndexTrackPairs) { 3088 for (int i = 0; i < mIndexTrackPairs.size(); i++) { 3089 Pair<Integer, SubtitleTrack> p = mIndexTrackPairs.get(i); 3090 if (p.first != null && p.first == inbandTrackIndex) { 3091 return i; 3092 } 3093 } 3094 } 3095 return -1; 3096 } finally { 3097 request.recycle(); 3098 reply.recycle(); 3099 } 3100 } 3101 3102 /** 3103 * Selects a track. 3104 * <p> 3105 * If a MediaPlayer is in invalid state, it throws an IllegalStateException exception. 3106 * If a MediaPlayer is in <em>Started</em> state, the selected track is presented immediately. 3107 * If a MediaPlayer is not in Started state, it just marks the track to be played. 3108 * </p> 3109 * <p> 3110 * In any valid state, if it is called multiple times on the same type of track (ie. Video, 3111 * Audio, Timed Text), the most recent one will be chosen. 3112 * </p> 3113 * <p> 3114 * The first audio and video tracks are selected by default if available, even though 3115 * this method is not called. However, no timed text track will be selected until 3116 * this function is called. 3117 * </p> 3118 * <p> 3119 * Currently, only timed text, subtitle or audio tracks can be selected via this method. 3120 * In addition, the support for selecting an audio track at runtime is pretty limited 3121 * in that an audio track can only be selected in the <em>Prepared</em> state. 3122 * </p> 3123 * @param index the index of the track to be selected. The valid range of the index 3124 * is 0..total number of track - 1. The total number of tracks as well as the type of 3125 * each individual track can be found by calling {@link #getTrackInfo()} method. 3126 * @throws IllegalStateException if called in an invalid state. 3127 * 3128 * @see android.media.MediaPlayer#getTrackInfo 3129 */ selectTrack(int index)3130 public void selectTrack(int index) throws IllegalStateException { 3131 selectOrDeselectTrack(index, true /* select */); 3132 } 3133 3134 /** 3135 * Deselect a track. 3136 * <p> 3137 * Currently, the track must be a timed text track and no audio or video tracks can be 3138 * deselected. If the timed text track identified by index has not been 3139 * selected before, it throws an exception. 3140 * </p> 3141 * @param index the index of the track to be deselected. The valid range of the index 3142 * is 0..total number of tracks - 1. The total number of tracks as well as the type of 3143 * each individual track can be found by calling {@link #getTrackInfo()} method. 3144 * @throws IllegalStateException if called in an invalid state. 3145 * 3146 * @see android.media.MediaPlayer#getTrackInfo 3147 */ deselectTrack(int index)3148 public void deselectTrack(int index) throws IllegalStateException { 3149 selectOrDeselectTrack(index, false /* select */); 3150 } 3151 selectOrDeselectTrack(int index, boolean select)3152 private void selectOrDeselectTrack(int index, boolean select) 3153 throws IllegalStateException { 3154 // handle subtitle track through subtitle controller 3155 populateInbandTracks(); 3156 3157 Pair<Integer,SubtitleTrack> p = null; 3158 try { 3159 p = mIndexTrackPairs.get(index); 3160 } catch (ArrayIndexOutOfBoundsException e) { 3161 // ignore bad index 3162 return; 3163 } 3164 3165 SubtitleTrack track = p.second; 3166 if (track == null) { 3167 // inband (de)select 3168 selectOrDeselectInbandTrack(p.first, select); 3169 return; 3170 } 3171 3172 if (mSubtitleController == null) { 3173 return; 3174 } 3175 3176 if (!select) { 3177 // out-of-band deselect 3178 if (mSubtitleController.getSelectedTrack() == track) { 3179 mSubtitleController.selectTrack(null); 3180 } else { 3181 Log.w(TAG, "trying to deselect track that was not selected"); 3182 } 3183 return; 3184 } 3185 3186 // out-of-band select 3187 if (track.getTrackType() == TrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT) { 3188 int ttIndex = getSelectedTrack(TrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT); 3189 synchronized (mIndexTrackPairs) { 3190 if (ttIndex >= 0 && ttIndex < mIndexTrackPairs.size()) { 3191 Pair<Integer,SubtitleTrack> p2 = mIndexTrackPairs.get(ttIndex); 3192 if (p2.first != null && p2.second == null) { 3193 // deselect inband counterpart 3194 selectOrDeselectInbandTrack(p2.first, false); 3195 } 3196 } 3197 } 3198 } 3199 mSubtitleController.selectTrack(track); 3200 } 3201 selectOrDeselectInbandTrack(int index, boolean select)3202 private void selectOrDeselectInbandTrack(int index, boolean select) 3203 throws IllegalStateException { 3204 Parcel request = Parcel.obtain(); 3205 Parcel reply = Parcel.obtain(); 3206 try { 3207 request.writeInterfaceToken(IMEDIA_PLAYER); 3208 request.writeInt(select? INVOKE_ID_SELECT_TRACK: INVOKE_ID_DESELECT_TRACK); 3209 request.writeInt(index); 3210 invoke(request, reply); 3211 } finally { 3212 request.recycle(); 3213 reply.recycle(); 3214 } 3215 } 3216 3217 3218 /** 3219 * @param reply Parcel with audio/video duration info for battery 3220 tracking usage 3221 * @return The status code. 3222 * {@hide} 3223 */ native_pullBatteryData(Parcel reply)3224 public native static int native_pullBatteryData(Parcel reply); 3225 3226 /** 3227 * Sets the target UDP re-transmit endpoint for the low level player. 3228 * Generally, the address portion of the endpoint is an IP multicast 3229 * address, although a unicast address would be equally valid. When a valid 3230 * retransmit endpoint has been set, the media player will not decode and 3231 * render the media presentation locally. Instead, the player will attempt 3232 * to re-multiplex its media data using the Android@Home RTP profile and 3233 * re-transmit to the target endpoint. Receiver devices (which may be 3234 * either the same as the transmitting device or different devices) may 3235 * instantiate, prepare, and start a receiver player using a setDataSource 3236 * URL of the form... 3237 * 3238 * aahRX://<multicastIP>:<port> 3239 * 3240 * to receive, decode and render the re-transmitted content. 3241 * 3242 * setRetransmitEndpoint may only be called before setDataSource has been 3243 * called; while the player is in the Idle state. 3244 * 3245 * @param endpoint the address and UDP port of the re-transmission target or 3246 * null if no re-transmission is to be performed. 3247 * @throws IllegalStateException if it is called in an invalid state 3248 * @throws IllegalArgumentException if the retransmit endpoint is supplied, 3249 * but invalid. 3250 * 3251 * {@hide} pending API council 3252 */ 3253 @UnsupportedAppUsage setRetransmitEndpoint(InetSocketAddress endpoint)3254 public void setRetransmitEndpoint(InetSocketAddress endpoint) 3255 throws IllegalStateException, IllegalArgumentException 3256 { 3257 String addrString = null; 3258 int port = 0; 3259 3260 if (null != endpoint) { 3261 addrString = endpoint.getAddress().getHostAddress(); 3262 port = endpoint.getPort(); 3263 } 3264 3265 int ret = native_setRetransmitEndpoint(addrString, port); 3266 if (ret != 0) { 3267 throw new IllegalArgumentException("Illegal re-transmit endpoint; native ret " + ret); 3268 } 3269 } 3270 native_setRetransmitEndpoint(String addrString, int port)3271 private native final int native_setRetransmitEndpoint(String addrString, int port); 3272 3273 @Override finalize()3274 protected void finalize() { 3275 baseRelease(); 3276 native_finalize(); 3277 } 3278 3279 /* Do not change these values without updating their counterparts 3280 * in include/media/mediaplayer.h! 3281 */ 3282 private static final int MEDIA_NOP = 0; // interface test message 3283 private static final int MEDIA_PREPARED = 1; 3284 private static final int MEDIA_PLAYBACK_COMPLETE = 2; 3285 private static final int MEDIA_BUFFERING_UPDATE = 3; 3286 private static final int MEDIA_SEEK_COMPLETE = 4; 3287 private static final int MEDIA_SET_VIDEO_SIZE = 5; 3288 private static final int MEDIA_STARTED = 6; 3289 private static final int MEDIA_PAUSED = 7; 3290 private static final int MEDIA_STOPPED = 8; 3291 private static final int MEDIA_SKIPPED = 9; 3292 private static final int MEDIA_NOTIFY_TIME = 98; 3293 private static final int MEDIA_TIMED_TEXT = 99; 3294 private static final int MEDIA_ERROR = 100; 3295 private static final int MEDIA_INFO = 200; 3296 private static final int MEDIA_SUBTITLE_DATA = 201; 3297 private static final int MEDIA_META_DATA = 202; 3298 private static final int MEDIA_DRM_INFO = 210; 3299 private static final int MEDIA_TIME_DISCONTINUITY = 211; 3300 private static final int MEDIA_AUDIO_ROUTING_CHANGED = 10000; 3301 3302 private TimeProvider mTimeProvider; 3303 private final Object mTimeProviderLock = new Object(); 3304 3305 /** @hide */ 3306 @UnsupportedAppUsage getMediaTimeProvider()3307 public MediaTimeProvider getMediaTimeProvider() { 3308 synchronized (mTimeProviderLock) { 3309 if (mTimeProvider == null) { 3310 mTimeProvider = new TimeProvider(this); 3311 } 3312 return mTimeProvider; 3313 } 3314 } 3315 3316 private class EventHandler extends Handler 3317 { 3318 private MediaPlayer mMediaPlayer; 3319 EventHandler(MediaPlayer mp, Looper looper)3320 public EventHandler(MediaPlayer mp, Looper looper) { 3321 super(looper); 3322 mMediaPlayer = mp; 3323 } 3324 3325 @Override handleMessage(Message msg)3326 public void handleMessage(Message msg) { 3327 if (mMediaPlayer.mNativeContext == 0) { 3328 Log.w(TAG, "mediaplayer went away with unhandled events"); 3329 return; 3330 } 3331 switch(msg.what) { 3332 case MEDIA_PREPARED: 3333 try { 3334 scanInternalSubtitleTracks(); 3335 } catch (RuntimeException e) { 3336 // send error message instead of crashing; 3337 // send error message instead of inlining a call to onError 3338 // to avoid code duplication. 3339 Message msg2 = obtainMessage( 3340 MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_UNSUPPORTED, null); 3341 sendMessage(msg2); 3342 } 3343 3344 OnPreparedListener onPreparedListener = mOnPreparedListener; 3345 if (onPreparedListener != null) 3346 onPreparedListener.onPrepared(mMediaPlayer); 3347 return; 3348 3349 case MEDIA_DRM_INFO: 3350 Log.v(TAG, "MEDIA_DRM_INFO " + mOnDrmInfoHandlerDelegate); 3351 3352 if (msg.obj == null) { 3353 Log.w(TAG, "MEDIA_DRM_INFO msg.obj=NULL"); 3354 } else if (msg.obj instanceof Parcel) { 3355 // The parcel was parsed already in postEventFromNative 3356 DrmInfo drmInfo = null; 3357 3358 OnDrmInfoHandlerDelegate onDrmInfoHandlerDelegate; 3359 synchronized (mDrmLock) { 3360 if (mOnDrmInfoHandlerDelegate != null && mDrmInfo != null) { 3361 drmInfo = mDrmInfo.makeCopy(); 3362 } 3363 // local copy while keeping the lock 3364 onDrmInfoHandlerDelegate = mOnDrmInfoHandlerDelegate; 3365 } 3366 3367 // notifying the client outside the lock 3368 if (onDrmInfoHandlerDelegate != null) { 3369 onDrmInfoHandlerDelegate.notifyClient(drmInfo); 3370 } 3371 } else { 3372 Log.w(TAG, "MEDIA_DRM_INFO msg.obj of unexpected type " + msg.obj); 3373 } 3374 return; 3375 3376 case MEDIA_PLAYBACK_COMPLETE: 3377 { 3378 mOnCompletionInternalListener.onCompletion(mMediaPlayer); 3379 OnCompletionListener onCompletionListener = mOnCompletionListener; 3380 if (onCompletionListener != null) 3381 onCompletionListener.onCompletion(mMediaPlayer); 3382 } 3383 stayAwake(false); 3384 return; 3385 3386 case MEDIA_STOPPED: 3387 { 3388 TimeProvider timeProvider = mTimeProvider; 3389 if (timeProvider != null) { 3390 timeProvider.onStopped(); 3391 } 3392 } 3393 break; 3394 3395 case MEDIA_STARTED: 3396 case MEDIA_PAUSED: 3397 { 3398 TimeProvider timeProvider = mTimeProvider; 3399 if (timeProvider != null) { 3400 timeProvider.onPaused(msg.what == MEDIA_PAUSED); 3401 } 3402 } 3403 break; 3404 3405 case MEDIA_BUFFERING_UPDATE: 3406 OnBufferingUpdateListener onBufferingUpdateListener = mOnBufferingUpdateListener; 3407 if (onBufferingUpdateListener != null) 3408 onBufferingUpdateListener.onBufferingUpdate(mMediaPlayer, msg.arg1); 3409 return; 3410 3411 case MEDIA_SEEK_COMPLETE: 3412 OnSeekCompleteListener onSeekCompleteListener = mOnSeekCompleteListener; 3413 if (onSeekCompleteListener != null) { 3414 onSeekCompleteListener.onSeekComplete(mMediaPlayer); 3415 } 3416 // fall through 3417 3418 case MEDIA_SKIPPED: 3419 { 3420 TimeProvider timeProvider = mTimeProvider; 3421 if (timeProvider != null) { 3422 timeProvider.onSeekComplete(mMediaPlayer); 3423 } 3424 } 3425 return; 3426 3427 case MEDIA_SET_VIDEO_SIZE: 3428 OnVideoSizeChangedListener onVideoSizeChangedListener = mOnVideoSizeChangedListener; 3429 if (onVideoSizeChangedListener != null) { 3430 onVideoSizeChangedListener.onVideoSizeChanged( 3431 mMediaPlayer, msg.arg1, msg.arg2); 3432 } 3433 return; 3434 3435 case MEDIA_ERROR: 3436 Log.e(TAG, "Error (" + msg.arg1 + "," + msg.arg2 + ")"); 3437 boolean error_was_handled = false; 3438 OnErrorListener onErrorListener = mOnErrorListener; 3439 if (onErrorListener != null) { 3440 error_was_handled = onErrorListener.onError(mMediaPlayer, msg.arg1, msg.arg2); 3441 } 3442 { 3443 mOnCompletionInternalListener.onCompletion(mMediaPlayer); 3444 OnCompletionListener onCompletionListener = mOnCompletionListener; 3445 if (onCompletionListener != null && ! error_was_handled) { 3446 onCompletionListener.onCompletion(mMediaPlayer); 3447 } 3448 } 3449 stayAwake(false); 3450 return; 3451 3452 case MEDIA_INFO: 3453 switch (msg.arg1) { 3454 case MEDIA_INFO_VIDEO_TRACK_LAGGING: 3455 Log.i(TAG, "Info (" + msg.arg1 + "," + msg.arg2 + ")"); 3456 break; 3457 case MEDIA_INFO_METADATA_UPDATE: 3458 try { 3459 scanInternalSubtitleTracks(); 3460 } catch (RuntimeException e) { 3461 Message msg2 = obtainMessage( 3462 MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_UNSUPPORTED, null); 3463 sendMessage(msg2); 3464 } 3465 // fall through 3466 3467 case MEDIA_INFO_EXTERNAL_METADATA_UPDATE: 3468 msg.arg1 = MEDIA_INFO_METADATA_UPDATE; 3469 // update default track selection 3470 if (mSubtitleController != null) { 3471 mSubtitleController.selectDefaultTrack(); 3472 } 3473 break; 3474 case MEDIA_INFO_BUFFERING_START: 3475 case MEDIA_INFO_BUFFERING_END: 3476 TimeProvider timeProvider = mTimeProvider; 3477 if (timeProvider != null) { 3478 timeProvider.onBuffering(msg.arg1 == MEDIA_INFO_BUFFERING_START); 3479 } 3480 break; 3481 } 3482 3483 OnInfoListener onInfoListener = mOnInfoListener; 3484 if (onInfoListener != null) { 3485 onInfoListener.onInfo(mMediaPlayer, msg.arg1, msg.arg2); 3486 } 3487 // No real default action so far. 3488 return; 3489 3490 case MEDIA_NOTIFY_TIME: 3491 TimeProvider timeProvider = mTimeProvider; 3492 if (timeProvider != null) { 3493 timeProvider.onNotifyTime(); 3494 } 3495 return; 3496 3497 case MEDIA_TIMED_TEXT: 3498 OnTimedTextListener onTimedTextListener = mOnTimedTextListener; 3499 if (onTimedTextListener == null) 3500 return; 3501 if (msg.obj == null) { 3502 onTimedTextListener.onTimedText(mMediaPlayer, null); 3503 } else { 3504 if (msg.obj instanceof Parcel) { 3505 Parcel parcel = (Parcel)msg.obj; 3506 TimedText text = new TimedText(parcel); 3507 parcel.recycle(); 3508 onTimedTextListener.onTimedText(mMediaPlayer, text); 3509 } 3510 } 3511 return; 3512 3513 case MEDIA_SUBTITLE_DATA: 3514 final OnSubtitleDataListener extSubtitleListener; 3515 final Handler extSubtitleHandler; 3516 synchronized(this) { 3517 if (mSubtitleDataListenerDisabled) { 3518 return; 3519 } 3520 extSubtitleListener = mExtSubtitleDataListener; 3521 extSubtitleHandler = mExtSubtitleDataHandler; 3522 } 3523 if (msg.obj instanceof Parcel) { 3524 Parcel parcel = (Parcel) msg.obj; 3525 final SubtitleData data = new SubtitleData(parcel); 3526 parcel.recycle(); 3527 3528 mIntSubtitleDataListener.onSubtitleData(mMediaPlayer, data); 3529 3530 if (extSubtitleListener != null) { 3531 if (extSubtitleHandler == null) { 3532 extSubtitleListener.onSubtitleData(mMediaPlayer, data); 3533 } else { 3534 extSubtitleHandler.post(new Runnable() { 3535 @Override 3536 public void run() { 3537 extSubtitleListener.onSubtitleData(mMediaPlayer, data); 3538 } 3539 }); 3540 } 3541 } 3542 } 3543 return; 3544 3545 case MEDIA_META_DATA: 3546 OnTimedMetaDataAvailableListener onTimedMetaDataAvailableListener = 3547 mOnTimedMetaDataAvailableListener; 3548 if (onTimedMetaDataAvailableListener == null) { 3549 return; 3550 } 3551 if (msg.obj instanceof Parcel) { 3552 Parcel parcel = (Parcel) msg.obj; 3553 TimedMetaData data = TimedMetaData.createTimedMetaDataFromParcel(parcel); 3554 parcel.recycle(); 3555 onTimedMetaDataAvailableListener.onTimedMetaDataAvailable(mMediaPlayer, data); 3556 } 3557 return; 3558 3559 case MEDIA_NOP: // interface test message - ignore 3560 break; 3561 3562 case MEDIA_AUDIO_ROUTING_CHANGED: 3563 AudioManager.resetAudioPortGeneration(); 3564 synchronized (mRoutingChangeListeners) { 3565 for (NativeRoutingEventHandlerDelegate delegate 3566 : mRoutingChangeListeners.values()) { 3567 delegate.notifyClient(); 3568 } 3569 } 3570 return; 3571 3572 case MEDIA_TIME_DISCONTINUITY: 3573 final OnMediaTimeDiscontinuityListener mediaTimeListener; 3574 final Handler mediaTimeHandler; 3575 synchronized(this) { 3576 mediaTimeListener = mOnMediaTimeDiscontinuityListener; 3577 mediaTimeHandler = mOnMediaTimeDiscontinuityHandler; 3578 } 3579 if (mediaTimeListener == null) { 3580 return; 3581 } 3582 if (msg.obj instanceof Parcel) { 3583 Parcel parcel = (Parcel) msg.obj; 3584 parcel.setDataPosition(0); 3585 long anchorMediaUs = parcel.readLong(); 3586 long anchorRealUs = parcel.readLong(); 3587 float playbackRate = parcel.readFloat(); 3588 parcel.recycle(); 3589 final MediaTimestamp timestamp; 3590 if (anchorMediaUs != -1 && anchorRealUs != -1) { 3591 timestamp = new MediaTimestamp( 3592 anchorMediaUs /*Us*/, anchorRealUs * 1000 /*Ns*/, playbackRate); 3593 } else { 3594 timestamp = MediaTimestamp.TIMESTAMP_UNKNOWN; 3595 } 3596 if (mediaTimeHandler == null) { 3597 mediaTimeListener.onMediaTimeDiscontinuity(mMediaPlayer, timestamp); 3598 } else { 3599 mediaTimeHandler.post(new Runnable() { 3600 @Override 3601 public void run() { 3602 mediaTimeListener.onMediaTimeDiscontinuity(mMediaPlayer, timestamp); 3603 } 3604 }); 3605 } 3606 } 3607 return; 3608 3609 default: 3610 Log.e(TAG, "Unknown message type " + msg.what); 3611 return; 3612 } 3613 } 3614 } 3615 3616 /* 3617 * Called from native code when an interesting event happens. This method 3618 * just uses the EventHandler system to post the event back to the main app thread. 3619 * We use a weak reference to the original MediaPlayer object so that the native 3620 * code is safe from the object disappearing from underneath it. (This is 3621 * the cookie passed to native_setup().) 3622 */ postEventFromNative(Object mediaplayer_ref, int what, int arg1, int arg2, Object obj)3623 private static void postEventFromNative(Object mediaplayer_ref, 3624 int what, int arg1, int arg2, Object obj) 3625 { 3626 final MediaPlayer mp = (MediaPlayer)((WeakReference)mediaplayer_ref).get(); 3627 if (mp == null) { 3628 return; 3629 } 3630 3631 switch (what) { 3632 case MEDIA_INFO: 3633 if (arg1 == MEDIA_INFO_STARTED_AS_NEXT) { 3634 new Thread(new Runnable() { 3635 @Override 3636 public void run() { 3637 // this acquires the wakelock if needed, and sets the client side state 3638 mp.start(); 3639 } 3640 }).start(); 3641 Thread.yield(); 3642 } 3643 break; 3644 3645 case MEDIA_DRM_INFO: 3646 // We need to derive mDrmInfo before prepare() returns so processing it here 3647 // before the notification is sent to EventHandler below. EventHandler runs in the 3648 // notification looper so its handleMessage might process the event after prepare() 3649 // has returned. 3650 Log.v(TAG, "postEventFromNative MEDIA_DRM_INFO"); 3651 if (obj instanceof Parcel) { 3652 Parcel parcel = (Parcel)obj; 3653 DrmInfo drmInfo = new DrmInfo(parcel); 3654 synchronized (mp.mDrmLock) { 3655 mp.mDrmInfo = drmInfo; 3656 } 3657 } else { 3658 Log.w(TAG, "MEDIA_DRM_INFO msg.obj of unexpected type " + obj); 3659 } 3660 break; 3661 3662 case MEDIA_PREPARED: 3663 // By this time, we've learned about DrmInfo's presence or absence. This is meant 3664 // mainly for prepareAsync() use case. For prepare(), this still can run to a race 3665 // condition b/c MediaPlayerNative releases the prepare() lock before calling notify 3666 // so we also set mDrmInfoResolved in prepare(). 3667 synchronized (mp.mDrmLock) { 3668 mp.mDrmInfoResolved = true; 3669 } 3670 break; 3671 3672 } 3673 3674 if (mp.mEventHandler != null) { 3675 Message m = mp.mEventHandler.obtainMessage(what, arg1, arg2, obj); 3676 mp.mEventHandler.sendMessage(m); 3677 } 3678 } 3679 3680 /** 3681 * Interface definition for a callback to be invoked when the media 3682 * source is ready for playback. 3683 */ 3684 public interface OnPreparedListener 3685 { 3686 /** 3687 * Called when the media file is ready for playback. 3688 * 3689 * @param mp the MediaPlayer that is ready for playback 3690 */ onPrepared(MediaPlayer mp)3691 void onPrepared(MediaPlayer mp); 3692 } 3693 3694 /** 3695 * Register a callback to be invoked when the media source is ready 3696 * for playback. 3697 * 3698 * @param listener the callback that will be run 3699 */ setOnPreparedListener(OnPreparedListener listener)3700 public void setOnPreparedListener(OnPreparedListener listener) 3701 { 3702 mOnPreparedListener = listener; 3703 } 3704 3705 @UnsupportedAppUsage 3706 private OnPreparedListener mOnPreparedListener; 3707 3708 /** 3709 * Interface definition for a callback to be invoked when playback of 3710 * a media source has completed. 3711 */ 3712 public interface OnCompletionListener 3713 { 3714 /** 3715 * Called when the end of a media source is reached during playback. 3716 * 3717 * @param mp the MediaPlayer that reached the end of the file 3718 */ onCompletion(MediaPlayer mp)3719 void onCompletion(MediaPlayer mp); 3720 } 3721 3722 /** 3723 * Register a callback to be invoked when the end of a media source 3724 * has been reached during playback. 3725 * 3726 * @param listener the callback that will be run 3727 */ setOnCompletionListener(OnCompletionListener listener)3728 public void setOnCompletionListener(OnCompletionListener listener) 3729 { 3730 mOnCompletionListener = listener; 3731 } 3732 3733 @UnsupportedAppUsage 3734 private OnCompletionListener mOnCompletionListener; 3735 3736 /** 3737 * @hide 3738 * Internal completion listener to update PlayerBase of the play state. Always "registered". 3739 */ 3740 private final OnCompletionListener mOnCompletionInternalListener = new OnCompletionListener() { 3741 @Override 3742 public void onCompletion(MediaPlayer mp) { 3743 baseStop(); 3744 } 3745 }; 3746 3747 /** 3748 * Interface definition of a callback to be invoked indicating buffering 3749 * status of a media resource being streamed over the network. 3750 */ 3751 public interface OnBufferingUpdateListener 3752 { 3753 /** 3754 * Called to update status in buffering a media stream received through 3755 * progressive HTTP download. The received buffering percentage 3756 * indicates how much of the content has been buffered or played. 3757 * For example a buffering update of 80 percent when half the content 3758 * has already been played indicates that the next 30 percent of the 3759 * content to play has been buffered. 3760 * 3761 * @param mp the MediaPlayer the update pertains to 3762 * @param percent the percentage (0-100) of the content 3763 * that has been buffered or played thus far 3764 */ onBufferingUpdate(MediaPlayer mp, int percent)3765 void onBufferingUpdate(MediaPlayer mp, int percent); 3766 } 3767 3768 /** 3769 * Register a callback to be invoked when the status of a network 3770 * stream's buffer has changed. 3771 * 3772 * @param listener the callback that will be run. 3773 */ setOnBufferingUpdateListener(OnBufferingUpdateListener listener)3774 public void setOnBufferingUpdateListener(OnBufferingUpdateListener listener) 3775 { 3776 mOnBufferingUpdateListener = listener; 3777 } 3778 3779 private OnBufferingUpdateListener mOnBufferingUpdateListener; 3780 3781 /** 3782 * Interface definition of a callback to be invoked indicating 3783 * the completion of a seek operation. 3784 */ 3785 public interface OnSeekCompleteListener 3786 { 3787 /** 3788 * Called to indicate the completion of a seek operation. 3789 * 3790 * @param mp the MediaPlayer that issued the seek operation 3791 */ onSeekComplete(MediaPlayer mp)3792 public void onSeekComplete(MediaPlayer mp); 3793 } 3794 3795 /** 3796 * Register a callback to be invoked when a seek operation has been 3797 * completed. 3798 * 3799 * @param listener the callback that will be run 3800 */ setOnSeekCompleteListener(OnSeekCompleteListener listener)3801 public void setOnSeekCompleteListener(OnSeekCompleteListener listener) 3802 { 3803 mOnSeekCompleteListener = listener; 3804 } 3805 3806 @UnsupportedAppUsage 3807 private OnSeekCompleteListener mOnSeekCompleteListener; 3808 3809 /** 3810 * Interface definition of a callback to be invoked when the 3811 * video size is first known or updated 3812 */ 3813 public interface OnVideoSizeChangedListener 3814 { 3815 /** 3816 * Called to indicate the video size 3817 * 3818 * The video size (width and height) could be 0 if there was no video, 3819 * no display surface was set, or the value was not determined yet. 3820 * 3821 * @param mp the MediaPlayer associated with this callback 3822 * @param width the width of the video 3823 * @param height the height of the video 3824 */ onVideoSizeChanged(MediaPlayer mp, int width, int height)3825 public void onVideoSizeChanged(MediaPlayer mp, int width, int height); 3826 } 3827 3828 /** 3829 * Register a callback to be invoked when the video size is 3830 * known or updated. 3831 * 3832 * @param listener the callback that will be run 3833 */ setOnVideoSizeChangedListener(OnVideoSizeChangedListener listener)3834 public void setOnVideoSizeChangedListener(OnVideoSizeChangedListener listener) 3835 { 3836 mOnVideoSizeChangedListener = listener; 3837 } 3838 3839 private OnVideoSizeChangedListener mOnVideoSizeChangedListener; 3840 3841 /** 3842 * Interface definition of a callback to be invoked when a 3843 * timed text is available for display. 3844 */ 3845 public interface OnTimedTextListener 3846 { 3847 /** 3848 * Called to indicate an avaliable timed text 3849 * 3850 * @param mp the MediaPlayer associated with this callback 3851 * @param text the timed text sample which contains the text 3852 * needed to be displayed and the display format. 3853 */ onTimedText(MediaPlayer mp, TimedText text)3854 public void onTimedText(MediaPlayer mp, TimedText text); 3855 } 3856 3857 /** 3858 * Register a callback to be invoked when a timed text is available 3859 * for display. 3860 * 3861 * @param listener the callback that will be run 3862 */ setOnTimedTextListener(OnTimedTextListener listener)3863 public void setOnTimedTextListener(OnTimedTextListener listener) 3864 { 3865 mOnTimedTextListener = listener; 3866 } 3867 3868 @UnsupportedAppUsage 3869 private OnTimedTextListener mOnTimedTextListener; 3870 3871 /** 3872 * Interface definition of a callback to be invoked when a player subtitle track has new 3873 * subtitle data available. 3874 * See the {@link MediaPlayer#setOnSubtitleDataListener(OnSubtitleDataListener, Handler)} 3875 * method for the description of which track will report data through this listener. 3876 */ 3877 public interface OnSubtitleDataListener { 3878 /** 3879 * Method called when new subtitle data is available 3880 * @param mp the player that reports the new subtitle data 3881 * @param data the subtitle data 3882 */ onSubtitleData(@onNull MediaPlayer mp, @NonNull SubtitleData data)3883 public void onSubtitleData(@NonNull MediaPlayer mp, @NonNull SubtitleData data); 3884 } 3885 3886 /** 3887 * Sets the listener to be invoked when a subtitle track has new data available. 3888 * The subtitle data comes from a subtitle track previously selected with 3889 * {@link #selectTrack(int)}. Use {@link #getTrackInfo()} to determine which tracks are 3890 * subtitles (of type {@link TrackInfo#MEDIA_TRACK_TYPE_SUBTITLE}), Subtitle track encodings 3891 * can be determined by {@link TrackInfo#getFormat()}).<br> 3892 * See {@link SubtitleData} for an example of querying subtitle encoding. 3893 * @param listener the listener called when new data is available 3894 * @param handler the {@link Handler} that receives the listener events 3895 */ setOnSubtitleDataListener(@onNull OnSubtitleDataListener listener, @NonNull Handler handler)3896 public void setOnSubtitleDataListener(@NonNull OnSubtitleDataListener listener, 3897 @NonNull Handler handler) { 3898 if (listener == null) { 3899 throw new IllegalArgumentException("Illegal null listener"); 3900 } 3901 if (handler == null) { 3902 throw new IllegalArgumentException("Illegal null handler"); 3903 } 3904 setOnSubtitleDataListenerInt(listener, handler); 3905 } 3906 /** 3907 * Sets the listener to be invoked when a subtitle track has new data available. 3908 * The subtitle data comes from a subtitle track previously selected with 3909 * {@link #selectTrack(int)}. Use {@link #getTrackInfo()} to determine which tracks are 3910 * subtitles (of type {@link TrackInfo#MEDIA_TRACK_TYPE_SUBTITLE}), Subtitle track encodings 3911 * can be determined by {@link TrackInfo#getFormat()}).<br> 3912 * See {@link SubtitleData} for an example of querying subtitle encoding.<br> 3913 * The listener will be called on the same thread as the one in which the MediaPlayer was 3914 * created. 3915 * @param listener the listener called when new data is available 3916 */ setOnSubtitleDataListener(@onNull OnSubtitleDataListener listener)3917 public void setOnSubtitleDataListener(@NonNull OnSubtitleDataListener listener) 3918 { 3919 if (listener == null) { 3920 throw new IllegalArgumentException("Illegal null listener"); 3921 } 3922 setOnSubtitleDataListenerInt(listener, null); 3923 } 3924 3925 /** 3926 * Clears the listener previously set with 3927 * {@link #setOnSubtitleDataListener(OnSubtitleDataListener)} or 3928 * {@link #setOnSubtitleDataListener(OnSubtitleDataListener, Handler)}. 3929 */ clearOnSubtitleDataListener()3930 public void clearOnSubtitleDataListener() { 3931 setOnSubtitleDataListenerInt(null, null); 3932 } 3933 setOnSubtitleDataListenerInt( @ullable OnSubtitleDataListener listener, @Nullable Handler handler)3934 private void setOnSubtitleDataListenerInt( 3935 @Nullable OnSubtitleDataListener listener, @Nullable Handler handler) { 3936 synchronized (this) { 3937 mExtSubtitleDataListener = listener; 3938 mExtSubtitleDataHandler = handler; 3939 } 3940 } 3941 3942 private boolean mSubtitleDataListenerDisabled; 3943 /** External OnSubtitleDataListener, the one set by {@link #setOnSubtitleDataListener}. */ 3944 private OnSubtitleDataListener mExtSubtitleDataListener; 3945 private Handler mExtSubtitleDataHandler; 3946 3947 /** 3948 * Interface definition of a callback to be invoked when discontinuity in the normal progression 3949 * of the media time is detected. 3950 * The "normal progression" of media time is defined as the expected increase of the playback 3951 * position when playing media, relative to the playback speed (for instance every second, media 3952 * time increases by two seconds when playing at 2x).<br> 3953 * Discontinuities are encountered in the following cases: 3954 * <ul> 3955 * <li>when the player is starved for data and cannot play anymore</li> 3956 * <li>when the player encounters a playback error</li> 3957 * <li>when the a seek operation starts, and when it's completed</li> 3958 * <li>when the playback speed changes</li> 3959 * <li>when the playback state changes</li> 3960 * <li>when the player is reset</li> 3961 * </ul> 3962 * See the 3963 * {@link MediaPlayer#setOnMediaTimeDiscontinuityListener(OnMediaTimeDiscontinuityListener, Handler)} 3964 * method to set a listener for these events. 3965 */ 3966 public interface OnMediaTimeDiscontinuityListener { 3967 /** 3968 * Called to indicate a time discontinuity has occured. 3969 * @param mp the MediaPlayer for which the discontinuity has occured. 3970 * @param mts the timestamp that correlates media time, system time and clock rate, 3971 * or {@link MediaTimestamp#TIMESTAMP_UNKNOWN} in an error case. 3972 */ onMediaTimeDiscontinuity(@onNull MediaPlayer mp, @NonNull MediaTimestamp mts)3973 public void onMediaTimeDiscontinuity(@NonNull MediaPlayer mp, @NonNull MediaTimestamp mts); 3974 } 3975 3976 /** 3977 * Sets the listener to be invoked when a media time discontinuity is encountered. 3978 * @param listener the listener called after a discontinuity 3979 * @param handler the {@link Handler} that receives the listener events 3980 */ setOnMediaTimeDiscontinuityListener( @onNull OnMediaTimeDiscontinuityListener listener, @NonNull Handler handler)3981 public void setOnMediaTimeDiscontinuityListener( 3982 @NonNull OnMediaTimeDiscontinuityListener listener, @NonNull Handler handler) { 3983 if (listener == null) { 3984 throw new IllegalArgumentException("Illegal null listener"); 3985 } 3986 if (handler == null) { 3987 throw new IllegalArgumentException("Illegal null handler"); 3988 } 3989 setOnMediaTimeDiscontinuityListenerInt(listener, handler); 3990 } 3991 3992 /** 3993 * Sets the listener to be invoked when a media time discontinuity is encountered. 3994 * The listener will be called on the same thread as the one in which the MediaPlayer was 3995 * created. 3996 * @param listener the listener called after a discontinuity 3997 */ setOnMediaTimeDiscontinuityListener( @onNull OnMediaTimeDiscontinuityListener listener)3998 public void setOnMediaTimeDiscontinuityListener( 3999 @NonNull OnMediaTimeDiscontinuityListener listener) 4000 { 4001 if (listener == null) { 4002 throw new IllegalArgumentException("Illegal null listener"); 4003 } 4004 setOnMediaTimeDiscontinuityListenerInt(listener, null); 4005 } 4006 4007 /** 4008 * Clears the listener previously set with 4009 * {@link #setOnMediaTimeDiscontinuityListener(OnMediaTimeDiscontinuityListener)} 4010 * or {@link #setOnMediaTimeDiscontinuityListener(OnMediaTimeDiscontinuityListener, Handler)} 4011 */ clearOnMediaTimeDiscontinuityListener()4012 public void clearOnMediaTimeDiscontinuityListener() { 4013 setOnMediaTimeDiscontinuityListenerInt(null, null); 4014 } 4015 setOnMediaTimeDiscontinuityListenerInt( @ullable OnMediaTimeDiscontinuityListener listener, @Nullable Handler handler)4016 private void setOnMediaTimeDiscontinuityListenerInt( 4017 @Nullable OnMediaTimeDiscontinuityListener listener, @Nullable Handler handler) { 4018 synchronized (this) { 4019 mOnMediaTimeDiscontinuityListener = listener; 4020 mOnMediaTimeDiscontinuityHandler = handler; 4021 } 4022 } 4023 4024 private OnMediaTimeDiscontinuityListener mOnMediaTimeDiscontinuityListener; 4025 private Handler mOnMediaTimeDiscontinuityHandler; 4026 4027 /** 4028 * Interface definition of a callback to be invoked when a 4029 * track has timed metadata available. 4030 * 4031 * @see MediaPlayer#setOnTimedMetaDataAvailableListener(OnTimedMetaDataAvailableListener) 4032 */ 4033 public interface OnTimedMetaDataAvailableListener 4034 { 4035 /** 4036 * Called to indicate avaliable timed metadata 4037 * <p> 4038 * This method will be called as timed metadata is extracted from the media, 4039 * in the same order as it occurs in the media. The timing of this event is 4040 * not controlled by the associated timestamp. 4041 * 4042 * @param mp the MediaPlayer associated with this callback 4043 * @param data the timed metadata sample associated with this event 4044 */ onTimedMetaDataAvailable(MediaPlayer mp, TimedMetaData data)4045 public void onTimedMetaDataAvailable(MediaPlayer mp, TimedMetaData data); 4046 } 4047 4048 /** 4049 * Register a callback to be invoked when a selected track has timed metadata available. 4050 * <p> 4051 * Currently only HTTP live streaming data URI's embedded with timed ID3 tags generates 4052 * {@link TimedMetaData}. 4053 * 4054 * @see MediaPlayer#selectTrack(int) 4055 * @see MediaPlayer.OnTimedMetaDataAvailableListener 4056 * @see TimedMetaData 4057 * 4058 * @param listener the callback that will be run 4059 */ setOnTimedMetaDataAvailableListener(OnTimedMetaDataAvailableListener listener)4060 public void setOnTimedMetaDataAvailableListener(OnTimedMetaDataAvailableListener listener) 4061 { 4062 mOnTimedMetaDataAvailableListener = listener; 4063 } 4064 4065 private OnTimedMetaDataAvailableListener mOnTimedMetaDataAvailableListener; 4066 4067 /* Do not change these values without updating their counterparts 4068 * in include/media/mediaplayer.h! 4069 */ 4070 /** Unspecified media player error. 4071 * @see android.media.MediaPlayer.OnErrorListener 4072 */ 4073 public static final int MEDIA_ERROR_UNKNOWN = 1; 4074 4075 /** Media server died. In this case, the application must release the 4076 * MediaPlayer object and instantiate a new one. 4077 * @see android.media.MediaPlayer.OnErrorListener 4078 */ 4079 public static final int MEDIA_ERROR_SERVER_DIED = 100; 4080 4081 /** The video is streamed and its container is not valid for progressive 4082 * playback i.e the video's index (e.g moov atom) is not at the start of the 4083 * file. 4084 * @see android.media.MediaPlayer.OnErrorListener 4085 */ 4086 public static final int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200; 4087 4088 /** File or network related operation errors. */ 4089 public static final int MEDIA_ERROR_IO = -1004; 4090 /** Bitstream is not conforming to the related coding standard or file spec. */ 4091 public static final int MEDIA_ERROR_MALFORMED = -1007; 4092 /** Bitstream is conforming to the related coding standard or file spec, but 4093 * the media framework does not support the feature. */ 4094 public static final int MEDIA_ERROR_UNSUPPORTED = -1010; 4095 /** Some operation takes too long to complete, usually more than 3-5 seconds. */ 4096 public static final int MEDIA_ERROR_TIMED_OUT = -110; 4097 4098 /** Unspecified low-level system error. This value originated from UNKNOWN_ERROR in 4099 * system/core/include/utils/Errors.h 4100 * @see android.media.MediaPlayer.OnErrorListener 4101 * @hide 4102 */ 4103 public static final int MEDIA_ERROR_SYSTEM = -2147483648; 4104 4105 /** 4106 * Interface definition of a callback to be invoked when there 4107 * has been an error during an asynchronous operation (other errors 4108 * will throw exceptions at method call time). 4109 */ 4110 public interface OnErrorListener 4111 { 4112 /** 4113 * Called to indicate an error. 4114 * 4115 * @param mp the MediaPlayer the error pertains to 4116 * @param what the type of error that has occurred: 4117 * <ul> 4118 * <li>{@link #MEDIA_ERROR_UNKNOWN} 4119 * <li>{@link #MEDIA_ERROR_SERVER_DIED} 4120 * </ul> 4121 * @param extra an extra code, specific to the error. Typically 4122 * implementation dependent. 4123 * <ul> 4124 * <li>{@link #MEDIA_ERROR_IO} 4125 * <li>{@link #MEDIA_ERROR_MALFORMED} 4126 * <li>{@link #MEDIA_ERROR_UNSUPPORTED} 4127 * <li>{@link #MEDIA_ERROR_TIMED_OUT} 4128 * <li><code>MEDIA_ERROR_SYSTEM (-2147483648)</code> - low-level system error. 4129 * </ul> 4130 * @return True if the method handled the error, false if it didn't. 4131 * Returning false, or not having an OnErrorListener at all, will 4132 * cause the OnCompletionListener to be called. 4133 */ onError(MediaPlayer mp, int what, int extra)4134 boolean onError(MediaPlayer mp, int what, int extra); 4135 } 4136 4137 /** 4138 * Register a callback to be invoked when an error has happened 4139 * during an asynchronous operation. 4140 * 4141 * @param listener the callback that will be run 4142 */ setOnErrorListener(OnErrorListener listener)4143 public void setOnErrorListener(OnErrorListener listener) 4144 { 4145 mOnErrorListener = listener; 4146 } 4147 4148 @UnsupportedAppUsage 4149 private OnErrorListener mOnErrorListener; 4150 4151 4152 /* Do not change these values without updating their counterparts 4153 * in include/media/mediaplayer.h! 4154 */ 4155 /** Unspecified media player info. 4156 * @see android.media.MediaPlayer.OnInfoListener 4157 */ 4158 public static final int MEDIA_INFO_UNKNOWN = 1; 4159 4160 /** The player was started because it was used as the next player for another 4161 * player, which just completed playback. 4162 * @see android.media.MediaPlayer#setNextMediaPlayer(MediaPlayer) 4163 * @see android.media.MediaPlayer.OnInfoListener 4164 */ 4165 public static final int MEDIA_INFO_STARTED_AS_NEXT = 2; 4166 4167 /** The player just pushed the very first video frame for rendering. 4168 * @see android.media.MediaPlayer.OnInfoListener 4169 */ 4170 public static final int MEDIA_INFO_VIDEO_RENDERING_START = 3; 4171 4172 /** The video is too complex for the decoder: it can't decode frames fast 4173 * enough. Possibly only the audio plays fine at this stage. 4174 * @see android.media.MediaPlayer.OnInfoListener 4175 */ 4176 public static final int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700; 4177 4178 /** MediaPlayer is temporarily pausing playback internally in order to 4179 * buffer more data. 4180 * @see android.media.MediaPlayer.OnInfoListener 4181 */ 4182 public static final int MEDIA_INFO_BUFFERING_START = 701; 4183 4184 /** MediaPlayer is resuming playback after filling buffers. 4185 * @see android.media.MediaPlayer.OnInfoListener 4186 */ 4187 public static final int MEDIA_INFO_BUFFERING_END = 702; 4188 4189 /** Estimated network bandwidth information (kbps) is available; currently this event fires 4190 * simultaneously as {@link #MEDIA_INFO_BUFFERING_START} and {@link #MEDIA_INFO_BUFFERING_END} 4191 * when playing network files. 4192 * @see android.media.MediaPlayer.OnInfoListener 4193 * @hide 4194 */ 4195 public static final int MEDIA_INFO_NETWORK_BANDWIDTH = 703; 4196 4197 /** Bad interleaving means that a media has been improperly interleaved or 4198 * not interleaved at all, e.g has all the video samples first then all the 4199 * audio ones. Video is playing but a lot of disk seeks may be happening. 4200 * @see android.media.MediaPlayer.OnInfoListener 4201 */ 4202 public static final int MEDIA_INFO_BAD_INTERLEAVING = 800; 4203 4204 /** The media cannot be seeked (e.g live stream) 4205 * @see android.media.MediaPlayer.OnInfoListener 4206 */ 4207 public static final int MEDIA_INFO_NOT_SEEKABLE = 801; 4208 4209 /** A new set of metadata is available. 4210 * @see android.media.MediaPlayer.OnInfoListener 4211 */ 4212 public static final int MEDIA_INFO_METADATA_UPDATE = 802; 4213 4214 /** A new set of external-only metadata is available. Used by 4215 * JAVA framework to avoid triggering track scanning. 4216 * @hide 4217 */ 4218 @UnsupportedAppUsage 4219 public static final int MEDIA_INFO_EXTERNAL_METADATA_UPDATE = 803; 4220 4221 /** Informs that audio is not playing. Note that playback of the video 4222 * is not interrupted. 4223 * @see android.media.MediaPlayer.OnInfoListener 4224 */ 4225 public static final int MEDIA_INFO_AUDIO_NOT_PLAYING = 804; 4226 4227 /** Informs that video is not playing. Note that playback of the audio 4228 * is not interrupted. 4229 * @see android.media.MediaPlayer.OnInfoListener 4230 */ 4231 public static final int MEDIA_INFO_VIDEO_NOT_PLAYING = 805; 4232 4233 /** Failed to handle timed text track properly. 4234 * @see android.media.MediaPlayer.OnInfoListener 4235 * 4236 * {@hide} 4237 */ 4238 @UnsupportedAppUsage 4239 public static final int MEDIA_INFO_TIMED_TEXT_ERROR = 900; 4240 4241 /** Subtitle track was not supported by the media framework. 4242 * @see android.media.MediaPlayer.OnInfoListener 4243 */ 4244 public static final int MEDIA_INFO_UNSUPPORTED_SUBTITLE = 901; 4245 4246 /** Reading the subtitle track takes too long. 4247 * @see android.media.MediaPlayer.OnInfoListener 4248 */ 4249 public static final int MEDIA_INFO_SUBTITLE_TIMED_OUT = 902; 4250 4251 /** 4252 * Interface definition of a callback to be invoked to communicate some 4253 * info and/or warning about the media or its playback. 4254 */ 4255 public interface OnInfoListener 4256 { 4257 /** 4258 * Called to indicate an info or a warning. 4259 * 4260 * @param mp the MediaPlayer the info pertains to. 4261 * @param what the type of info or warning. 4262 * <ul> 4263 * <li>{@link #MEDIA_INFO_UNKNOWN} 4264 * <li>{@link #MEDIA_INFO_VIDEO_TRACK_LAGGING} 4265 * <li>{@link #MEDIA_INFO_VIDEO_RENDERING_START} 4266 * <li>{@link #MEDIA_INFO_BUFFERING_START} 4267 * <li>{@link #MEDIA_INFO_BUFFERING_END} 4268 * <li><code>MEDIA_INFO_NETWORK_BANDWIDTH (703)</code> - 4269 * bandwidth information is available (as <code>extra</code> kbps) 4270 * <li>{@link #MEDIA_INFO_BAD_INTERLEAVING} 4271 * <li>{@link #MEDIA_INFO_NOT_SEEKABLE} 4272 * <li>{@link #MEDIA_INFO_METADATA_UPDATE} 4273 * <li>{@link #MEDIA_INFO_UNSUPPORTED_SUBTITLE} 4274 * <li>{@link #MEDIA_INFO_SUBTITLE_TIMED_OUT} 4275 * </ul> 4276 * @param extra an extra code, specific to the info. Typically 4277 * implementation dependent. 4278 * @return True if the method handled the info, false if it didn't. 4279 * Returning false, or not having an OnInfoListener at all, will 4280 * cause the info to be discarded. 4281 */ onInfo(MediaPlayer mp, int what, int extra)4282 boolean onInfo(MediaPlayer mp, int what, int extra); 4283 } 4284 4285 /** 4286 * Register a callback to be invoked when an info/warning is available. 4287 * 4288 * @param listener the callback that will be run 4289 */ setOnInfoListener(OnInfoListener listener)4290 public void setOnInfoListener(OnInfoListener listener) 4291 { 4292 mOnInfoListener = listener; 4293 } 4294 4295 @UnsupportedAppUsage 4296 private OnInfoListener mOnInfoListener; 4297 4298 // Modular DRM begin 4299 4300 /** 4301 * Interface definition of a callback to be invoked when the app 4302 * can do DRM configuration (get/set properties) before the session 4303 * is opened. This facilitates configuration of the properties, like 4304 * 'securityLevel', which has to be set after DRM scheme creation but 4305 * before the DRM session is opened. 4306 * 4307 * The only allowed DRM calls in this listener are {@code getDrmPropertyString} 4308 * and {@code setDrmPropertyString}. 4309 * 4310 */ 4311 public interface OnDrmConfigHelper 4312 { 4313 /** 4314 * Called to give the app the opportunity to configure DRM before the session is created 4315 * 4316 * @param mp the {@code MediaPlayer} associated with this callback 4317 */ onDrmConfig(MediaPlayer mp)4318 public void onDrmConfig(MediaPlayer mp); 4319 } 4320 4321 /** 4322 * Register a callback to be invoked for configuration of the DRM object before 4323 * the session is created. 4324 * The callback will be invoked synchronously during the execution 4325 * of {@link #prepareDrm(UUID uuid)}. 4326 * 4327 * @param listener the callback that will be run 4328 */ setOnDrmConfigHelper(OnDrmConfigHelper listener)4329 public void setOnDrmConfigHelper(OnDrmConfigHelper listener) 4330 { 4331 synchronized (mDrmLock) { 4332 mOnDrmConfigHelper = listener; 4333 } // synchronized 4334 } 4335 4336 private OnDrmConfigHelper mOnDrmConfigHelper; 4337 4338 /** 4339 * Interface definition of a callback to be invoked when the 4340 * DRM info becomes available 4341 */ 4342 public interface OnDrmInfoListener 4343 { 4344 /** 4345 * Called to indicate DRM info is available 4346 * 4347 * @param mp the {@code MediaPlayer} associated with this callback 4348 * @param drmInfo DRM info of the source including PSSH, and subset 4349 * of crypto schemes supported by this device 4350 */ onDrmInfo(MediaPlayer mp, DrmInfo drmInfo)4351 public void onDrmInfo(MediaPlayer mp, DrmInfo drmInfo); 4352 } 4353 4354 /** 4355 * Register a callback to be invoked when the DRM info is 4356 * known. 4357 * 4358 * @param listener the callback that will be run 4359 */ setOnDrmInfoListener(OnDrmInfoListener listener)4360 public void setOnDrmInfoListener(OnDrmInfoListener listener) 4361 { 4362 setOnDrmInfoListener(listener, null); 4363 } 4364 4365 /** 4366 * Register a callback to be invoked when the DRM info is 4367 * known. 4368 * 4369 * @param listener the callback that will be run 4370 */ setOnDrmInfoListener(OnDrmInfoListener listener, Handler handler)4371 public void setOnDrmInfoListener(OnDrmInfoListener listener, Handler handler) 4372 { 4373 synchronized (mDrmLock) { 4374 if (listener != null) { 4375 mOnDrmInfoHandlerDelegate = new OnDrmInfoHandlerDelegate(this, listener, handler); 4376 } else { 4377 mOnDrmInfoHandlerDelegate = null; 4378 } 4379 } // synchronized 4380 } 4381 4382 private OnDrmInfoHandlerDelegate mOnDrmInfoHandlerDelegate; 4383 4384 4385 /** 4386 * The status codes for {@link OnDrmPreparedListener#onDrmPrepared} listener. 4387 * <p> 4388 * 4389 * DRM preparation has succeeded. 4390 */ 4391 public static final int PREPARE_DRM_STATUS_SUCCESS = 0; 4392 4393 /** 4394 * The device required DRM provisioning but couldn't reach the provisioning server. 4395 */ 4396 public static final int PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR = 1; 4397 4398 /** 4399 * The device required DRM provisioning but the provisioning server denied the request. 4400 */ 4401 public static final int PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR = 2; 4402 4403 /** 4404 * The DRM preparation has failed . 4405 */ 4406 public static final int PREPARE_DRM_STATUS_PREPARATION_ERROR = 3; 4407 4408 4409 /** @hide */ 4410 @IntDef({ 4411 PREPARE_DRM_STATUS_SUCCESS, 4412 PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR, 4413 PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR, 4414 PREPARE_DRM_STATUS_PREPARATION_ERROR, 4415 }) 4416 @Retention(RetentionPolicy.SOURCE) 4417 public @interface PrepareDrmStatusCode {} 4418 4419 /** 4420 * Interface definition of a callback to notify the app when the 4421 * DRM is ready for key request/response 4422 */ 4423 public interface OnDrmPreparedListener 4424 { 4425 /** 4426 * Called to notify the app that prepareDrm is finished and ready for key request/response 4427 * 4428 * @param mp the {@code MediaPlayer} associated with this callback 4429 * @param status the result of DRM preparation which can be 4430 * {@link #PREPARE_DRM_STATUS_SUCCESS}, 4431 * {@link #PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR}, 4432 * {@link #PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR}, or 4433 * {@link #PREPARE_DRM_STATUS_PREPARATION_ERROR}. 4434 */ onDrmPrepared(MediaPlayer mp, @PrepareDrmStatusCode int status)4435 public void onDrmPrepared(MediaPlayer mp, @PrepareDrmStatusCode int status); 4436 } 4437 4438 /** 4439 * Register a callback to be invoked when the DRM object is prepared. 4440 * 4441 * @param listener the callback that will be run 4442 */ setOnDrmPreparedListener(OnDrmPreparedListener listener)4443 public void setOnDrmPreparedListener(OnDrmPreparedListener listener) 4444 { 4445 setOnDrmPreparedListener(listener, null); 4446 } 4447 4448 /** 4449 * Register a callback to be invoked when the DRM object is prepared. 4450 * 4451 * @param listener the callback that will be run 4452 * @param handler the Handler that will receive the callback 4453 */ setOnDrmPreparedListener(OnDrmPreparedListener listener, Handler handler)4454 public void setOnDrmPreparedListener(OnDrmPreparedListener listener, Handler handler) 4455 { 4456 synchronized (mDrmLock) { 4457 if (listener != null) { 4458 mOnDrmPreparedHandlerDelegate = new OnDrmPreparedHandlerDelegate(this, 4459 listener, handler); 4460 } else { 4461 mOnDrmPreparedHandlerDelegate = null; 4462 } 4463 } // synchronized 4464 } 4465 4466 private OnDrmPreparedHandlerDelegate mOnDrmPreparedHandlerDelegate; 4467 4468 4469 private class OnDrmInfoHandlerDelegate { 4470 private MediaPlayer mMediaPlayer; 4471 private OnDrmInfoListener mOnDrmInfoListener; 4472 private Handler mHandler; 4473 OnDrmInfoHandlerDelegate(MediaPlayer mp, OnDrmInfoListener listener, Handler handler)4474 OnDrmInfoHandlerDelegate(MediaPlayer mp, OnDrmInfoListener listener, Handler handler) { 4475 mMediaPlayer = mp; 4476 mOnDrmInfoListener = listener; 4477 4478 // find the looper for our new event handler 4479 if (handler != null) { 4480 mHandler = handler; 4481 } else { 4482 // handler == null 4483 // Will let OnDrmInfoListener be called in mEventHandler similar to other 4484 // legacy notifications. This is because MEDIA_DRM_INFO's notification has to be 4485 // sent before MEDIA_PREPARED's (i.e., in the same order they are issued by 4486 // mediaserver). As a result, the callback has to be called directly by 4487 // EventHandler.handleMessage similar to onPrepared. 4488 } 4489 } 4490 notifyClient(DrmInfo drmInfo)4491 void notifyClient(DrmInfo drmInfo) { 4492 if (mHandler != null) { 4493 mHandler.post(new Runnable() { 4494 @Override 4495 public void run() { 4496 mOnDrmInfoListener.onDrmInfo(mMediaPlayer, drmInfo); 4497 } 4498 }); 4499 } 4500 else { // no handler: direct call by mEventHandler 4501 mOnDrmInfoListener.onDrmInfo(mMediaPlayer, drmInfo); 4502 } 4503 } 4504 } 4505 4506 private class OnDrmPreparedHandlerDelegate { 4507 private MediaPlayer mMediaPlayer; 4508 private OnDrmPreparedListener mOnDrmPreparedListener; 4509 private Handler mHandler; 4510 OnDrmPreparedHandlerDelegate(MediaPlayer mp, OnDrmPreparedListener listener, Handler handler)4511 OnDrmPreparedHandlerDelegate(MediaPlayer mp, OnDrmPreparedListener listener, 4512 Handler handler) { 4513 mMediaPlayer = mp; 4514 mOnDrmPreparedListener = listener; 4515 4516 // find the looper for our new event handler 4517 if (handler != null) { 4518 mHandler = handler; 4519 } else if (mEventHandler != null) { 4520 // Otherwise, use mEventHandler 4521 mHandler = mEventHandler; 4522 } else { 4523 Log.e(TAG, "OnDrmPreparedHandlerDelegate: Unexpected null mEventHandler"); 4524 } 4525 } 4526 notifyClient(int status)4527 void notifyClient(int status) { 4528 if (mHandler != null) { 4529 mHandler.post(new Runnable() { 4530 @Override 4531 public void run() { 4532 mOnDrmPreparedListener.onDrmPrepared(mMediaPlayer, status); 4533 } 4534 }); 4535 } else { 4536 Log.e(TAG, "OnDrmPreparedHandlerDelegate:notifyClient: Unexpected null mHandler"); 4537 } 4538 } 4539 } 4540 4541 /** 4542 * Retrieves the DRM Info associated with the current source 4543 * 4544 * @throws IllegalStateException if called before prepare() 4545 */ getDrmInfo()4546 public DrmInfo getDrmInfo() 4547 { 4548 DrmInfo drmInfo = null; 4549 4550 // there is not much point if the app calls getDrmInfo within an OnDrmInfoListenet; 4551 // regardless below returns drmInfo anyway instead of raising an exception 4552 synchronized (mDrmLock) { 4553 if (!mDrmInfoResolved && mDrmInfo == null) { 4554 final String msg = "The Player has not been prepared yet"; 4555 Log.v(TAG, msg); 4556 throw new IllegalStateException(msg); 4557 } 4558 4559 if (mDrmInfo != null) { 4560 drmInfo = mDrmInfo.makeCopy(); 4561 } 4562 } // synchronized 4563 4564 return drmInfo; 4565 } 4566 4567 4568 /** 4569 * Prepares the DRM for the current source 4570 * <p> 4571 * If {@code OnDrmConfigHelper} is registered, it will be called during 4572 * preparation to allow configuration of the DRM properties before opening the 4573 * DRM session. Note that the callback is called synchronously in the thread that called 4574 * {@code prepareDrm}. It should be used only for a series of {@code getDrmPropertyString} 4575 * and {@code setDrmPropertyString} calls and refrain from any lengthy operation. 4576 * <p> 4577 * If the device has not been provisioned before, this call also provisions the device 4578 * which involves accessing the provisioning server and can take a variable time to 4579 * complete depending on the network connectivity. 4580 * If {@code OnDrmPreparedListener} is registered, prepareDrm() runs in non-blocking 4581 * mode by launching the provisioning in the background and returning. The listener 4582 * will be called when provisioning and preparation has finished. If a 4583 * {@code OnDrmPreparedListener} is not registered, prepareDrm() waits till provisioning 4584 * and preparation has finished, i.e., runs in blocking mode. 4585 * <p> 4586 * If {@code OnDrmPreparedListener} is registered, it is called to indicate the DRM 4587 * session being ready. The application should not make any assumption about its call 4588 * sequence (e.g., before or after prepareDrm returns), or the thread context that will 4589 * execute the listener (unless the listener is registered with a handler thread). 4590 * <p> 4591 * 4592 * @param uuid The UUID of the crypto scheme. If not known beforehand, it can be retrieved 4593 * from the source through {@code getDrmInfo} or registering a {@code onDrmInfoListener}. 4594 * 4595 * @throws IllegalStateException if called before prepare(), or the DRM was 4596 * prepared already 4597 * @throws UnsupportedSchemeException if the crypto scheme is not supported 4598 * @throws ResourceBusyException if required DRM resources are in use 4599 * @throws ProvisioningNetworkErrorException if provisioning is required but failed due to a 4600 * network error 4601 * @throws ProvisioningServerErrorException if provisioning is required but failed due to 4602 * the request denied by the provisioning server 4603 */ prepareDrm(@onNull UUID uuid)4604 public void prepareDrm(@NonNull UUID uuid) 4605 throws UnsupportedSchemeException, ResourceBusyException, 4606 ProvisioningNetworkErrorException, ProvisioningServerErrorException 4607 { 4608 Log.v(TAG, "prepareDrm: uuid: " + uuid + " mOnDrmConfigHelper: " + mOnDrmConfigHelper); 4609 4610 boolean allDoneWithoutProvisioning = false; 4611 // get a snapshot as we'll use them outside the lock 4612 OnDrmPreparedHandlerDelegate onDrmPreparedHandlerDelegate = null; 4613 4614 synchronized (mDrmLock) { 4615 4616 // only allowing if tied to a protected source; might relax for releasing offline keys 4617 if (mDrmInfo == null) { 4618 final String msg = "prepareDrm(): Wrong usage: The player must be prepared and " + 4619 "DRM info be retrieved before this call."; 4620 Log.e(TAG, msg); 4621 throw new IllegalStateException(msg); 4622 } 4623 4624 if (mActiveDrmScheme) { 4625 final String msg = "prepareDrm(): Wrong usage: There is already " + 4626 "an active DRM scheme with " + mDrmUUID; 4627 Log.e(TAG, msg); 4628 throw new IllegalStateException(msg); 4629 } 4630 4631 if (mPrepareDrmInProgress) { 4632 final String msg = "prepareDrm(): Wrong usage: There is already " + 4633 "a pending prepareDrm call."; 4634 Log.e(TAG, msg); 4635 throw new IllegalStateException(msg); 4636 } 4637 4638 if (mDrmProvisioningInProgress) { 4639 final String msg = "prepareDrm(): Unexpectd: Provisioning is already in progress."; 4640 Log.e(TAG, msg); 4641 throw new IllegalStateException(msg); 4642 } 4643 4644 // shouldn't need this; just for safeguard 4645 cleanDrmObj(); 4646 4647 mPrepareDrmInProgress = true; 4648 // local copy while the lock is held 4649 onDrmPreparedHandlerDelegate = mOnDrmPreparedHandlerDelegate; 4650 4651 try { 4652 // only creating the DRM object to allow pre-openSession configuration 4653 prepareDrm_createDrmStep(uuid); 4654 } catch (Exception e) { 4655 Log.w(TAG, "prepareDrm(): Exception ", e); 4656 mPrepareDrmInProgress = false; 4657 throw e; 4658 } 4659 4660 mDrmConfigAllowed = true; 4661 } // synchronized 4662 4663 4664 // call the callback outside the lock 4665 if (mOnDrmConfigHelper != null) { 4666 mOnDrmConfigHelper.onDrmConfig(this); 4667 } 4668 4669 synchronized (mDrmLock) { 4670 mDrmConfigAllowed = false; 4671 boolean earlyExit = false; 4672 4673 try { 4674 prepareDrm_openSessionStep(uuid); 4675 4676 mDrmUUID = uuid; 4677 mActiveDrmScheme = true; 4678 4679 allDoneWithoutProvisioning = true; 4680 } catch (IllegalStateException e) { 4681 final String msg = "prepareDrm(): Wrong usage: The player must be " + 4682 "in the prepared state to call prepareDrm()."; 4683 Log.e(TAG, msg); 4684 earlyExit = true; 4685 throw new IllegalStateException(msg); 4686 } catch (NotProvisionedException e) { 4687 Log.w(TAG, "prepareDrm: NotProvisionedException"); 4688 4689 // handle provisioning internally; it'll reset mPrepareDrmInProgress 4690 int result = HandleProvisioninig(uuid); 4691 4692 // if blocking mode, we're already done; 4693 // if non-blocking mode, we attempted to launch background provisioning 4694 if (result != PREPARE_DRM_STATUS_SUCCESS) { 4695 earlyExit = true; 4696 String msg; 4697 4698 switch (result) { 4699 case PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR: 4700 msg = "prepareDrm: Provisioning was required but failed " + 4701 "due to a network error."; 4702 Log.e(TAG, msg); 4703 throw new ProvisioningNetworkErrorException(msg); 4704 4705 case PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR: 4706 msg = "prepareDrm: Provisioning was required but the request " + 4707 "was denied by the server."; 4708 Log.e(TAG, msg); 4709 throw new ProvisioningServerErrorException(msg); 4710 4711 case PREPARE_DRM_STATUS_PREPARATION_ERROR: 4712 default: // default for safeguard 4713 msg = "prepareDrm: Post-provisioning preparation failed."; 4714 Log.e(TAG, msg); 4715 throw new IllegalStateException(msg); 4716 } 4717 } 4718 // nothing else to do; 4719 // if blocking or non-blocking, HandleProvisioninig does the re-attempt & cleanup 4720 } catch (Exception e) { 4721 Log.e(TAG, "prepareDrm: Exception " + e); 4722 earlyExit = true; 4723 throw e; 4724 } finally { 4725 if (!mDrmProvisioningInProgress) {// if early exit other than provisioning exception 4726 mPrepareDrmInProgress = false; 4727 } 4728 if (earlyExit) { // cleaning up object if didn't succeed 4729 cleanDrmObj(); 4730 } 4731 } // finally 4732 } // synchronized 4733 4734 4735 // if finished successfully without provisioning, call the callback outside the lock 4736 if (allDoneWithoutProvisioning) { 4737 if (onDrmPreparedHandlerDelegate != null) 4738 onDrmPreparedHandlerDelegate.notifyClient(PREPARE_DRM_STATUS_SUCCESS); 4739 } 4740 4741 } 4742 4743 _releaseDrm()4744 private native void _releaseDrm(); 4745 4746 /** 4747 * Releases the DRM session 4748 * <p> 4749 * The player has to have an active DRM session and be in stopped, or prepared 4750 * state before this call is made. 4751 * A {@code reset()} call will release the DRM session implicitly. 4752 * 4753 * @throws NoDrmSchemeException if there is no active DRM session to release 4754 */ releaseDrm()4755 public void releaseDrm() 4756 throws NoDrmSchemeException 4757 { 4758 Log.v(TAG, "releaseDrm:"); 4759 4760 synchronized (mDrmLock) { 4761 if (!mActiveDrmScheme) { 4762 Log.e(TAG, "releaseDrm(): No active DRM scheme to release."); 4763 throw new NoDrmSchemeException("releaseDrm: No active DRM scheme to release."); 4764 } 4765 4766 try { 4767 // we don't have the player's state in this layer. The below call raises 4768 // exception if we're in a non-stopped/prepared state. 4769 4770 // for cleaning native/mediaserver crypto object 4771 _releaseDrm(); 4772 4773 // for cleaning client-side MediaDrm object; only called if above has succeeded 4774 cleanDrmObj(); 4775 4776 mActiveDrmScheme = false; 4777 } catch (IllegalStateException e) { 4778 Log.w(TAG, "releaseDrm: Exception ", e); 4779 throw new IllegalStateException("releaseDrm: The player is not in a valid state."); 4780 } catch (Exception e) { 4781 Log.e(TAG, "releaseDrm: Exception ", e); 4782 } 4783 } // synchronized 4784 } 4785 4786 4787 /** 4788 * A key request/response exchange occurs between the app and a license server 4789 * to obtain or release keys used to decrypt encrypted content. 4790 * <p> 4791 * getKeyRequest() is used to obtain an opaque key request byte array that is 4792 * delivered to the license server. The opaque key request byte array is returned 4793 * in KeyRequest.data. The recommended URL to deliver the key request to is 4794 * returned in KeyRequest.defaultUrl. 4795 * <p> 4796 * After the app has received the key request response from the server, 4797 * it should deliver to the response to the DRM engine plugin using the method 4798 * {@link #provideKeyResponse}. 4799 * 4800 * @param keySetId is the key-set identifier of the offline keys being released when keyType is 4801 * {@link MediaDrm#KEY_TYPE_RELEASE}. It should be set to null for other key requests, when 4802 * keyType is {@link MediaDrm#KEY_TYPE_STREAMING} or {@link MediaDrm#KEY_TYPE_OFFLINE}. 4803 * 4804 * @param initData is the container-specific initialization data when the keyType is 4805 * {@link MediaDrm#KEY_TYPE_STREAMING} or {@link MediaDrm#KEY_TYPE_OFFLINE}. Its meaning is 4806 * interpreted based on the mime type provided in the mimeType parameter. It could 4807 * contain, for example, the content ID, key ID or other data obtained from the content 4808 * metadata that is required in generating the key request. 4809 * When the keyType is {@link MediaDrm#KEY_TYPE_RELEASE}, it should be set to null. 4810 * 4811 * @param mimeType identifies the mime type of the content 4812 * 4813 * @param keyType specifies the type of the request. The request may be to acquire 4814 * keys for streaming, {@link MediaDrm#KEY_TYPE_STREAMING}, or for offline content 4815 * {@link MediaDrm#KEY_TYPE_OFFLINE}, or to release previously acquired 4816 * keys ({@link MediaDrm#KEY_TYPE_RELEASE}), which are identified by a keySetId. 4817 * 4818 * @param optionalParameters are included in the key request message to 4819 * allow a client application to provide additional message parameters to the server. 4820 * This may be {@code null} if no additional parameters are to be sent. 4821 * 4822 * @throws NoDrmSchemeException if there is no active DRM session 4823 */ 4824 @NonNull getKeyRequest(@ullable byte[] keySetId, @Nullable byte[] initData, @Nullable String mimeType, @MediaDrm.KeyType int keyType, @Nullable Map<String, String> optionalParameters)4825 public MediaDrm.KeyRequest getKeyRequest(@Nullable byte[] keySetId, @Nullable byte[] initData, 4826 @Nullable String mimeType, @MediaDrm.KeyType int keyType, 4827 @Nullable Map<String, String> optionalParameters) 4828 throws NoDrmSchemeException 4829 { 4830 Log.v(TAG, "getKeyRequest: " + 4831 " keySetId: " + keySetId + " initData:" + initData + " mimeType: " + mimeType + 4832 " keyType: " + keyType + " optionalParameters: " + optionalParameters); 4833 4834 synchronized (mDrmLock) { 4835 if (!mActiveDrmScheme) { 4836 Log.e(TAG, "getKeyRequest NoDrmSchemeException"); 4837 throw new NoDrmSchemeException("getKeyRequest: Has to set a DRM scheme first."); 4838 } 4839 4840 try { 4841 byte[] scope = (keyType != MediaDrm.KEY_TYPE_RELEASE) ? 4842 mDrmSessionId : // sessionId for KEY_TYPE_STREAMING/OFFLINE 4843 keySetId; // keySetId for KEY_TYPE_RELEASE 4844 4845 HashMap<String, String> hmapOptionalParameters = 4846 (optionalParameters != null) ? 4847 new HashMap<String, String>(optionalParameters) : 4848 null; 4849 4850 MediaDrm.KeyRequest request = mDrmObj.getKeyRequest(scope, initData, mimeType, 4851 keyType, hmapOptionalParameters); 4852 Log.v(TAG, "getKeyRequest: --> request: " + request); 4853 4854 return request; 4855 4856 } catch (NotProvisionedException e) { 4857 Log.w(TAG, "getKeyRequest NotProvisionedException: " + 4858 "Unexpected. Shouldn't have reached here."); 4859 throw new IllegalStateException("getKeyRequest: Unexpected provisioning error."); 4860 } catch (Exception e) { 4861 Log.w(TAG, "getKeyRequest Exception " + e); 4862 throw e; 4863 } 4864 4865 } // synchronized 4866 } 4867 4868 4869 /** 4870 * A key response is received from the license server by the app, then it is 4871 * provided to the DRM engine plugin using provideKeyResponse. When the 4872 * response is for an offline key request, a key-set identifier is returned that 4873 * can be used to later restore the keys to a new session with the method 4874 * {@ link # restoreKeys}. 4875 * When the response is for a streaming or release request, null is returned. 4876 * 4877 * @param keySetId When the response is for a release request, keySetId identifies 4878 * the saved key associated with the release request (i.e., the same keySetId 4879 * passed to the earlier {@ link # getKeyRequest} call. It MUST be null when the 4880 * response is for either streaming or offline key requests. 4881 * 4882 * @param response the byte array response from the server 4883 * 4884 * @throws NoDrmSchemeException if there is no active DRM session 4885 * @throws DeniedByServerException if the response indicates that the 4886 * server rejected the request 4887 */ provideKeyResponse(@ullable byte[] keySetId, @NonNull byte[] response)4888 public byte[] provideKeyResponse(@Nullable byte[] keySetId, @NonNull byte[] response) 4889 throws NoDrmSchemeException, DeniedByServerException 4890 { 4891 Log.v(TAG, "provideKeyResponse: keySetId: " + keySetId + " response: " + response); 4892 4893 synchronized (mDrmLock) { 4894 4895 if (!mActiveDrmScheme) { 4896 Log.e(TAG, "getKeyRequest NoDrmSchemeException"); 4897 throw new NoDrmSchemeException("getKeyRequest: Has to set a DRM scheme first."); 4898 } 4899 4900 try { 4901 byte[] scope = (keySetId == null) ? 4902 mDrmSessionId : // sessionId for KEY_TYPE_STREAMING/OFFLINE 4903 keySetId; // keySetId for KEY_TYPE_RELEASE 4904 4905 byte[] keySetResult = mDrmObj.provideKeyResponse(scope, response); 4906 4907 Log.v(TAG, "provideKeyResponse: keySetId: " + keySetId + " response: " + response + 4908 " --> " + keySetResult); 4909 4910 4911 return keySetResult; 4912 4913 } catch (NotProvisionedException e) { 4914 Log.w(TAG, "provideKeyResponse NotProvisionedException: " + 4915 "Unexpected. Shouldn't have reached here."); 4916 throw new IllegalStateException("provideKeyResponse: " + 4917 "Unexpected provisioning error."); 4918 } catch (Exception e) { 4919 Log.w(TAG, "provideKeyResponse Exception " + e); 4920 throw e; 4921 } 4922 } // synchronized 4923 } 4924 4925 4926 /** 4927 * Restore persisted offline keys into a new session. keySetId identifies the 4928 * keys to load, obtained from a prior call to {@link #provideKeyResponse}. 4929 * 4930 * @param keySetId identifies the saved key set to restore 4931 */ restoreKeys(@onNull byte[] keySetId)4932 public void restoreKeys(@NonNull byte[] keySetId) 4933 throws NoDrmSchemeException 4934 { 4935 Log.v(TAG, "restoreKeys: keySetId: " + keySetId); 4936 4937 synchronized (mDrmLock) { 4938 4939 if (!mActiveDrmScheme) { 4940 Log.w(TAG, "restoreKeys NoDrmSchemeException"); 4941 throw new NoDrmSchemeException("restoreKeys: Has to set a DRM scheme first."); 4942 } 4943 4944 try { 4945 mDrmObj.restoreKeys(mDrmSessionId, keySetId); 4946 } catch (Exception e) { 4947 Log.w(TAG, "restoreKeys Exception " + e); 4948 throw e; 4949 } 4950 4951 } // synchronized 4952 } 4953 4954 4955 /** 4956 * Read a DRM engine plugin String property value, given the property name string. 4957 * <p> 4958 * @param propertyName the property name 4959 * 4960 * Standard fields names are: 4961 * {@link MediaDrm#PROPERTY_VENDOR}, {@link MediaDrm#PROPERTY_VERSION}, 4962 * {@link MediaDrm#PROPERTY_DESCRIPTION}, {@link MediaDrm#PROPERTY_ALGORITHMS} 4963 */ 4964 @NonNull getDrmPropertyString(@onNull @ediaDrm.StringProperty String propertyName)4965 public String getDrmPropertyString(@NonNull @MediaDrm.StringProperty String propertyName) 4966 throws NoDrmSchemeException 4967 { 4968 Log.v(TAG, "getDrmPropertyString: propertyName: " + propertyName); 4969 4970 String value; 4971 synchronized (mDrmLock) { 4972 4973 if (!mActiveDrmScheme && !mDrmConfigAllowed) { 4974 Log.w(TAG, "getDrmPropertyString NoDrmSchemeException"); 4975 throw new NoDrmSchemeException("getDrmPropertyString: Has to prepareDrm() first."); 4976 } 4977 4978 try { 4979 value = mDrmObj.getPropertyString(propertyName); 4980 } catch (Exception e) { 4981 Log.w(TAG, "getDrmPropertyString Exception " + e); 4982 throw e; 4983 } 4984 } // synchronized 4985 4986 Log.v(TAG, "getDrmPropertyString: propertyName: " + propertyName + " --> value: " + value); 4987 4988 return value; 4989 } 4990 4991 4992 /** 4993 * Set a DRM engine plugin String property value. 4994 * <p> 4995 * @param propertyName the property name 4996 * @param value the property value 4997 * 4998 * Standard fields names are: 4999 * {@link MediaDrm#PROPERTY_VENDOR}, {@link MediaDrm#PROPERTY_VERSION}, 5000 * {@link MediaDrm#PROPERTY_DESCRIPTION}, {@link MediaDrm#PROPERTY_ALGORITHMS} 5001 */ setDrmPropertyString(@onNull @ediaDrm.StringProperty String propertyName, @NonNull String value)5002 public void setDrmPropertyString(@NonNull @MediaDrm.StringProperty String propertyName, 5003 @NonNull String value) 5004 throws NoDrmSchemeException 5005 { 5006 Log.v(TAG, "setDrmPropertyString: propertyName: " + propertyName + " value: " + value); 5007 5008 synchronized (mDrmLock) { 5009 5010 if ( !mActiveDrmScheme && !mDrmConfigAllowed ) { 5011 Log.w(TAG, "setDrmPropertyString NoDrmSchemeException"); 5012 throw new NoDrmSchemeException("setDrmPropertyString: Has to prepareDrm() first."); 5013 } 5014 5015 try { 5016 mDrmObj.setPropertyString(propertyName, value); 5017 } catch ( Exception e ) { 5018 Log.w(TAG, "setDrmPropertyString Exception " + e); 5019 throw e; 5020 } 5021 } // synchronized 5022 } 5023 5024 /** 5025 * Encapsulates the DRM properties of the source. 5026 */ 5027 public static final class DrmInfo { 5028 private Map<UUID, byte[]> mapPssh; 5029 private UUID[] supportedSchemes; 5030 5031 /** 5032 * Returns the PSSH info of the data source for each supported DRM scheme. 5033 */ getPssh()5034 public Map<UUID, byte[]> getPssh() { 5035 return mapPssh; 5036 } 5037 5038 /** 5039 * Returns the intersection of the data source and the device DRM schemes. 5040 * It effectively identifies the subset of the source's DRM schemes which 5041 * are supported by the device too. 5042 */ getSupportedSchemes()5043 public UUID[] getSupportedSchemes() { 5044 return supportedSchemes; 5045 } 5046 DrmInfo(Map<UUID, byte[]> Pssh, UUID[] SupportedSchemes)5047 private DrmInfo(Map<UUID, byte[]> Pssh, UUID[] SupportedSchemes) { 5048 mapPssh = Pssh; 5049 supportedSchemes = SupportedSchemes; 5050 } 5051 DrmInfo(Parcel parcel)5052 private DrmInfo(Parcel parcel) { 5053 Log.v(TAG, "DrmInfo(" + parcel + ") size " + parcel.dataSize()); 5054 5055 int psshsize = parcel.readInt(); 5056 byte[] pssh = new byte[psshsize]; 5057 parcel.readByteArray(pssh); 5058 5059 Log.v(TAG, "DrmInfo() PSSH: " + arrToHex(pssh)); 5060 mapPssh = parsePSSH(pssh, psshsize); 5061 Log.v(TAG, "DrmInfo() PSSH: " + mapPssh); 5062 5063 int supportedDRMsCount = parcel.readInt(); 5064 supportedSchemes = new UUID[supportedDRMsCount]; 5065 for (int i = 0; i < supportedDRMsCount; i++) { 5066 byte[] uuid = new byte[16]; 5067 parcel.readByteArray(uuid); 5068 5069 supportedSchemes[i] = bytesToUUID(uuid); 5070 5071 Log.v(TAG, "DrmInfo() supportedScheme[" + i + "]: " + 5072 supportedSchemes[i]); 5073 } 5074 5075 Log.v(TAG, "DrmInfo() Parcel psshsize: " + psshsize + 5076 " supportedDRMsCount: " + supportedDRMsCount); 5077 } 5078 makeCopy()5079 private DrmInfo makeCopy() { 5080 return new DrmInfo(this.mapPssh, this.supportedSchemes); 5081 } 5082 arrToHex(byte[] bytes)5083 private String arrToHex(byte[] bytes) { 5084 String out = "0x"; 5085 for (int i = 0; i < bytes.length; i++) { 5086 out += String.format("%02x", bytes[i]); 5087 } 5088 5089 return out; 5090 } 5091 bytesToUUID(byte[] uuid)5092 private UUID bytesToUUID(byte[] uuid) { 5093 long msb = 0, lsb = 0; 5094 for (int i = 0; i < 8; i++) { 5095 msb |= ( ((long)uuid[i] & 0xff) << (8 * (7 - i)) ); 5096 lsb |= ( ((long)uuid[i+8] & 0xff) << (8 * (7 - i)) ); 5097 } 5098 5099 return new UUID(msb, lsb); 5100 } 5101 parsePSSH(byte[] pssh, int psshsize)5102 private Map<UUID, byte[]> parsePSSH(byte[] pssh, int psshsize) { 5103 Map<UUID, byte[]> result = new HashMap<UUID, byte[]>(); 5104 5105 final int UUID_SIZE = 16; 5106 final int DATALEN_SIZE = 4; 5107 5108 int len = psshsize; 5109 int numentries = 0; 5110 int i = 0; 5111 5112 while (len > 0) { 5113 if (len < UUID_SIZE) { 5114 Log.w(TAG, String.format("parsePSSH: len is too short to parse " + 5115 "UUID: (%d < 16) pssh: %d", len, psshsize)); 5116 return null; 5117 } 5118 5119 byte[] subset = Arrays.copyOfRange(pssh, i, i + UUID_SIZE); 5120 UUID uuid = bytesToUUID(subset); 5121 i += UUID_SIZE; 5122 len -= UUID_SIZE; 5123 5124 // get data length 5125 if (len < 4) { 5126 Log.w(TAG, String.format("parsePSSH: len is too short to parse " + 5127 "datalen: (%d < 4) pssh: %d", len, psshsize)); 5128 return null; 5129 } 5130 5131 subset = Arrays.copyOfRange(pssh, i, i+DATALEN_SIZE); 5132 int datalen = (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN) ? 5133 ((subset[3] & 0xff) << 24) | ((subset[2] & 0xff) << 16) | 5134 ((subset[1] & 0xff) << 8) | (subset[0] & 0xff) : 5135 ((subset[0] & 0xff) << 24) | ((subset[1] & 0xff) << 16) | 5136 ((subset[2] & 0xff) << 8) | (subset[3] & 0xff) ; 5137 i += DATALEN_SIZE; 5138 len -= DATALEN_SIZE; 5139 5140 if (len < datalen) { 5141 Log.w(TAG, String.format("parsePSSH: len is too short to parse " + 5142 "data: (%d < %d) pssh: %d", len, datalen, psshsize)); 5143 return null; 5144 } 5145 5146 byte[] data = Arrays.copyOfRange(pssh, i, i+datalen); 5147 5148 // skip the data 5149 i += datalen; 5150 len -= datalen; 5151 5152 Log.v(TAG, String.format("parsePSSH[%d]: <%s, %s> pssh: %d", 5153 numentries, uuid, arrToHex(data), psshsize)); 5154 numentries++; 5155 result.put(uuid, data); 5156 } 5157 5158 return result; 5159 } 5160 5161 }; // DrmInfo 5162 5163 /** 5164 * Thrown when a DRM method is called before preparing a DRM scheme through prepareDrm(). 5165 * Extends MediaDrm.MediaDrmException 5166 */ 5167 public static final class NoDrmSchemeException extends MediaDrmException { NoDrmSchemeException(String detailMessage)5168 public NoDrmSchemeException(String detailMessage) { 5169 super(detailMessage); 5170 } 5171 } 5172 5173 /** 5174 * Thrown when the device requires DRM provisioning but the provisioning attempt has 5175 * failed due to a network error (Internet reachability, timeout, etc.). 5176 * Extends MediaDrm.MediaDrmException 5177 */ 5178 public static final class ProvisioningNetworkErrorException extends MediaDrmException { ProvisioningNetworkErrorException(String detailMessage)5179 public ProvisioningNetworkErrorException(String detailMessage) { 5180 super(detailMessage); 5181 } 5182 } 5183 5184 /** 5185 * Thrown when the device requires DRM provisioning but the provisioning attempt has 5186 * failed due to the provisioning server denying the request. 5187 * Extends MediaDrm.MediaDrmException 5188 */ 5189 public static final class ProvisioningServerErrorException extends MediaDrmException { ProvisioningServerErrorException(String detailMessage)5190 public ProvisioningServerErrorException(String detailMessage) { 5191 super(detailMessage); 5192 } 5193 } 5194 5195 _prepareDrm(@onNull byte[] uuid, @NonNull byte[] drmSessionId)5196 private native void _prepareDrm(@NonNull byte[] uuid, @NonNull byte[] drmSessionId); 5197 5198 // Modular DRM helpers 5199 prepareDrm_createDrmStep(@onNull UUID uuid)5200 private void prepareDrm_createDrmStep(@NonNull UUID uuid) 5201 throws UnsupportedSchemeException { 5202 Log.v(TAG, "prepareDrm_createDrmStep: UUID: " + uuid); 5203 5204 try { 5205 mDrmObj = new MediaDrm(uuid); 5206 Log.v(TAG, "prepareDrm_createDrmStep: Created mDrmObj=" + mDrmObj); 5207 } catch (Exception e) { // UnsupportedSchemeException 5208 Log.e(TAG, "prepareDrm_createDrmStep: MediaDrm failed with " + e); 5209 throw e; 5210 } 5211 } 5212 prepareDrm_openSessionStep(@onNull UUID uuid)5213 private void prepareDrm_openSessionStep(@NonNull UUID uuid) 5214 throws NotProvisionedException, ResourceBusyException { 5215 Log.v(TAG, "prepareDrm_openSessionStep: uuid: " + uuid); 5216 5217 // TODO: don't need an open session for a future specialKeyReleaseDrm mode but we should do 5218 // it anyway so it raises provisioning error if needed. We'd rather handle provisioning 5219 // at prepareDrm/openSession rather than getKeyRequest/provideKeyResponse 5220 try { 5221 mDrmSessionId = mDrmObj.openSession(); 5222 Log.v(TAG, "prepareDrm_openSessionStep: mDrmSessionId=" + mDrmSessionId); 5223 5224 // Sending it down to native/mediaserver to create the crypto object 5225 // This call could simply fail due to bad player state, e.g., after start(). 5226 _prepareDrm(getByteArrayFromUUID(uuid), mDrmSessionId); 5227 Log.v(TAG, "prepareDrm_openSessionStep: _prepareDrm/Crypto succeeded"); 5228 5229 } catch (Exception e) { //ResourceBusyException, NotProvisionedException 5230 Log.e(TAG, "prepareDrm_openSessionStep: open/crypto failed with " + e); 5231 throw e; 5232 } 5233 5234 } 5235 5236 private class ProvisioningThread extends Thread 5237 { 5238 public static final int TIMEOUT_MS = 60000; 5239 5240 private UUID uuid; 5241 private String urlStr; 5242 private Object drmLock; 5243 private OnDrmPreparedHandlerDelegate onDrmPreparedHandlerDelegate; 5244 private MediaPlayer mediaPlayer; 5245 private int status; 5246 private boolean finished; status()5247 public int status() { 5248 return status; 5249 } 5250 initialize(MediaDrm.ProvisionRequest request, UUID uuid, MediaPlayer mediaPlayer)5251 public ProvisioningThread initialize(MediaDrm.ProvisionRequest request, 5252 UUID uuid, MediaPlayer mediaPlayer) { 5253 // lock is held by the caller 5254 drmLock = mediaPlayer.mDrmLock; 5255 onDrmPreparedHandlerDelegate = mediaPlayer.mOnDrmPreparedHandlerDelegate; 5256 this.mediaPlayer = mediaPlayer; 5257 5258 urlStr = request.getDefaultUrl() + "&signedRequest=" + new String(request.getData()); 5259 this.uuid = uuid; 5260 5261 status = PREPARE_DRM_STATUS_PREPARATION_ERROR; 5262 5263 Log.v(TAG, "HandleProvisioninig: Thread is initialised url: " + urlStr); 5264 return this; 5265 } 5266 run()5267 public void run() { 5268 5269 byte[] response = null; 5270 boolean provisioningSucceeded = false; 5271 try { 5272 URL url = new URL(urlStr); 5273 final HttpURLConnection connection = (HttpURLConnection) url.openConnection(); 5274 try { 5275 connection.setRequestMethod("POST"); 5276 connection.setDoOutput(false); 5277 connection.setDoInput(true); 5278 connection.setConnectTimeout(TIMEOUT_MS); 5279 connection.setReadTimeout(TIMEOUT_MS); 5280 5281 connection.connect(); 5282 response = Streams.readFully(connection.getInputStream()); 5283 5284 Log.v(TAG, "HandleProvisioninig: Thread run: response " + 5285 response.length + " " + response); 5286 } catch (Exception e) { 5287 status = PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR; 5288 Log.w(TAG, "HandleProvisioninig: Thread run: connect " + e + " url: " + url); 5289 } finally { 5290 connection.disconnect(); 5291 } 5292 } catch (Exception e) { 5293 status = PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR; 5294 Log.w(TAG, "HandleProvisioninig: Thread run: openConnection " + e); 5295 } 5296 5297 if (response != null) { 5298 try { 5299 mDrmObj.provideProvisionResponse(response); 5300 Log.v(TAG, "HandleProvisioninig: Thread run: " + 5301 "provideProvisionResponse SUCCEEDED!"); 5302 5303 provisioningSucceeded = true; 5304 } catch (Exception e) { 5305 status = PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR; 5306 Log.w(TAG, "HandleProvisioninig: Thread run: " + 5307 "provideProvisionResponse " + e); 5308 } 5309 } 5310 5311 boolean succeeded = false; 5312 5313 // non-blocking mode needs the lock 5314 if (onDrmPreparedHandlerDelegate != null) { 5315 5316 synchronized (drmLock) { 5317 // continuing with prepareDrm 5318 if (provisioningSucceeded) { 5319 succeeded = mediaPlayer.resumePrepareDrm(uuid); 5320 status = (succeeded) ? 5321 PREPARE_DRM_STATUS_SUCCESS : 5322 PREPARE_DRM_STATUS_PREPARATION_ERROR; 5323 } 5324 mediaPlayer.mDrmProvisioningInProgress = false; 5325 mediaPlayer.mPrepareDrmInProgress = false; 5326 if (!succeeded) { 5327 cleanDrmObj(); // cleaning up if it hasn't gone through while in the lock 5328 } 5329 } // synchronized 5330 5331 // calling the callback outside the lock 5332 onDrmPreparedHandlerDelegate.notifyClient(status); 5333 } else { // blocking mode already has the lock 5334 5335 // continuing with prepareDrm 5336 if (provisioningSucceeded) { 5337 succeeded = mediaPlayer.resumePrepareDrm(uuid); 5338 status = (succeeded) ? 5339 PREPARE_DRM_STATUS_SUCCESS : 5340 PREPARE_DRM_STATUS_PREPARATION_ERROR; 5341 } 5342 mediaPlayer.mDrmProvisioningInProgress = false; 5343 mediaPlayer.mPrepareDrmInProgress = false; 5344 if (!succeeded) { 5345 cleanDrmObj(); // cleaning up if it hasn't gone through 5346 } 5347 } 5348 5349 finished = true; 5350 } // run() 5351 5352 } // ProvisioningThread 5353 HandleProvisioninig(UUID uuid)5354 private int HandleProvisioninig(UUID uuid) 5355 { 5356 // the lock is already held by the caller 5357 5358 if (mDrmProvisioningInProgress) { 5359 Log.e(TAG, "HandleProvisioninig: Unexpected mDrmProvisioningInProgress"); 5360 return PREPARE_DRM_STATUS_PREPARATION_ERROR; 5361 } 5362 5363 MediaDrm.ProvisionRequest provReq = mDrmObj.getProvisionRequest(); 5364 if (provReq == null) { 5365 Log.e(TAG, "HandleProvisioninig: getProvisionRequest returned null."); 5366 return PREPARE_DRM_STATUS_PREPARATION_ERROR; 5367 } 5368 5369 Log.v(TAG, "HandleProvisioninig provReq " + 5370 " data: " + provReq.getData() + " url: " + provReq.getDefaultUrl()); 5371 5372 // networking in a background thread 5373 mDrmProvisioningInProgress = true; 5374 5375 mDrmProvisioningThread = new ProvisioningThread().initialize(provReq, uuid, this); 5376 mDrmProvisioningThread.start(); 5377 5378 int result; 5379 5380 // non-blocking: this is not the final result 5381 if (mOnDrmPreparedHandlerDelegate != null) { 5382 result = PREPARE_DRM_STATUS_SUCCESS; 5383 } else { 5384 // if blocking mode, wait till provisioning is done 5385 try { 5386 mDrmProvisioningThread.join(); 5387 } catch (Exception e) { 5388 Log.w(TAG, "HandleProvisioninig: Thread.join Exception " + e); 5389 } 5390 result = mDrmProvisioningThread.status(); 5391 // no longer need the thread 5392 mDrmProvisioningThread = null; 5393 } 5394 5395 return result; 5396 } 5397 resumePrepareDrm(UUID uuid)5398 private boolean resumePrepareDrm(UUID uuid) 5399 { 5400 Log.v(TAG, "resumePrepareDrm: uuid: " + uuid); 5401 5402 // mDrmLock is guaranteed to be held 5403 boolean success = false; 5404 try { 5405 // resuming 5406 prepareDrm_openSessionStep(uuid); 5407 5408 mDrmUUID = uuid; 5409 mActiveDrmScheme = true; 5410 5411 success = true; 5412 } catch (Exception e) { 5413 Log.w(TAG, "HandleProvisioninig: Thread run _prepareDrm resume failed with " + e); 5414 // mDrmObj clean up is done by the caller 5415 } 5416 5417 return success; 5418 } 5419 resetDrmState()5420 private void resetDrmState() 5421 { 5422 synchronized (mDrmLock) { 5423 Log.v(TAG, "resetDrmState: " + 5424 " mDrmInfo=" + mDrmInfo + 5425 " mDrmProvisioningThread=" + mDrmProvisioningThread + 5426 " mPrepareDrmInProgress=" + mPrepareDrmInProgress + 5427 " mActiveDrmScheme=" + mActiveDrmScheme); 5428 5429 mDrmInfoResolved = false; 5430 mDrmInfo = null; 5431 5432 if (mDrmProvisioningThread != null) { 5433 // timeout; relying on HttpUrlConnection 5434 try { 5435 mDrmProvisioningThread.join(); 5436 } 5437 catch (InterruptedException e) { 5438 Log.w(TAG, "resetDrmState: ProvThread.join Exception " + e); 5439 } 5440 mDrmProvisioningThread = null; 5441 } 5442 5443 mPrepareDrmInProgress = false; 5444 mActiveDrmScheme = false; 5445 5446 cleanDrmObj(); 5447 } // synchronized 5448 } 5449 cleanDrmObj()5450 private void cleanDrmObj() 5451 { 5452 // the caller holds mDrmLock 5453 Log.v(TAG, "cleanDrmObj: mDrmObj=" + mDrmObj + " mDrmSessionId=" + mDrmSessionId); 5454 5455 if (mDrmSessionId != null) { 5456 mDrmObj.closeSession(mDrmSessionId); 5457 mDrmSessionId = null; 5458 } 5459 if (mDrmObj != null) { 5460 mDrmObj.release(); 5461 mDrmObj = null; 5462 } 5463 } 5464 getByteArrayFromUUID(@onNull UUID uuid)5465 private static final byte[] getByteArrayFromUUID(@NonNull UUID uuid) { 5466 long msb = uuid.getMostSignificantBits(); 5467 long lsb = uuid.getLeastSignificantBits(); 5468 5469 byte[] uuidBytes = new byte[16]; 5470 for (int i = 0; i < 8; ++i) { 5471 uuidBytes[i] = (byte)(msb >>> (8 * (7 - i))); 5472 uuidBytes[8 + i] = (byte)(lsb >>> (8 * (7 - i))); 5473 } 5474 5475 return uuidBytes; 5476 } 5477 5478 // Modular DRM end 5479 5480 /* 5481 * Test whether a given video scaling mode is supported. 5482 */ isVideoScalingModeSupported(int mode)5483 private boolean isVideoScalingModeSupported(int mode) { 5484 return (mode == VIDEO_SCALING_MODE_SCALE_TO_FIT || 5485 mode == VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING); 5486 } 5487 5488 /** @hide */ 5489 static class TimeProvider implements MediaPlayer.OnSeekCompleteListener, 5490 MediaTimeProvider { 5491 private static final String TAG = "MTP"; 5492 private static final long MAX_NS_WITHOUT_POSITION_CHECK = 5000000000L; 5493 private static final long MAX_EARLY_CALLBACK_US = 1000; 5494 private static final long TIME_ADJUSTMENT_RATE = 2; /* meaning 1/2 */ 5495 private long mLastTimeUs = 0; 5496 private MediaPlayer mPlayer; 5497 private boolean mPaused = true; 5498 private boolean mStopped = true; 5499 private boolean mBuffering; 5500 private long mLastReportedTime; 5501 // since we are expecting only a handful listeners per stream, there is 5502 // no need for log(N) search performance 5503 private MediaTimeProvider.OnMediaTimeListener mListeners[]; 5504 private long mTimes[]; 5505 private Handler mEventHandler; 5506 private boolean mRefresh = false; 5507 private boolean mPausing = false; 5508 private boolean mSeeking = false; 5509 private static final int NOTIFY = 1; 5510 private static final int NOTIFY_TIME = 0; 5511 private static final int NOTIFY_STOP = 2; 5512 private static final int NOTIFY_SEEK = 3; 5513 private static final int NOTIFY_TRACK_DATA = 4; 5514 private HandlerThread mHandlerThread; 5515 5516 /** @hide */ 5517 public boolean DEBUG = false; 5518 TimeProvider(MediaPlayer mp)5519 public TimeProvider(MediaPlayer mp) { 5520 mPlayer = mp; 5521 try { 5522 getCurrentTimeUs(true, false); 5523 } catch (IllegalStateException e) { 5524 // we assume starting position 5525 mRefresh = true; 5526 } 5527 5528 Looper looper; 5529 if ((looper = Looper.myLooper()) == null && 5530 (looper = Looper.getMainLooper()) == null) { 5531 // Create our own looper here in case MP was created without one 5532 mHandlerThread = new HandlerThread("MediaPlayerMTPEventThread", 5533 Process.THREAD_PRIORITY_FOREGROUND); 5534 mHandlerThread.start(); 5535 looper = mHandlerThread.getLooper(); 5536 } 5537 mEventHandler = new EventHandler(looper); 5538 5539 mListeners = new MediaTimeProvider.OnMediaTimeListener[0]; 5540 mTimes = new long[0]; 5541 mLastTimeUs = 0; 5542 } 5543 scheduleNotification(int type, long delayUs)5544 private void scheduleNotification(int type, long delayUs) { 5545 // ignore time notifications until seek is handled 5546 if (mSeeking && type == NOTIFY_TIME) { 5547 return; 5548 } 5549 5550 if (DEBUG) Log.v(TAG, "scheduleNotification " + type + " in " + delayUs); 5551 mEventHandler.removeMessages(NOTIFY); 5552 Message msg = mEventHandler.obtainMessage(NOTIFY, type, 0); 5553 mEventHandler.sendMessageDelayed(msg, (int) (delayUs / 1000)); 5554 } 5555 5556 /** @hide */ close()5557 public void close() { 5558 mEventHandler.removeMessages(NOTIFY); 5559 if (mHandlerThread != null) { 5560 mHandlerThread.quitSafely(); 5561 mHandlerThread = null; 5562 } 5563 } 5564 5565 /** @hide */ finalize()5566 protected void finalize() { 5567 if (mHandlerThread != null) { 5568 mHandlerThread.quitSafely(); 5569 } 5570 } 5571 5572 /** @hide */ onNotifyTime()5573 public void onNotifyTime() { 5574 synchronized (this) { 5575 if (DEBUG) Log.d(TAG, "onNotifyTime: "); 5576 scheduleNotification(NOTIFY_TIME, 0 /* delay */); 5577 } 5578 } 5579 5580 /** @hide */ onPaused(boolean paused)5581 public void onPaused(boolean paused) { 5582 synchronized(this) { 5583 if (DEBUG) Log.d(TAG, "onPaused: " + paused); 5584 if (mStopped) { // handle as seek if we were stopped 5585 mStopped = false; 5586 mSeeking = true; 5587 scheduleNotification(NOTIFY_SEEK, 0 /* delay */); 5588 } else { 5589 mPausing = paused; // special handling if player disappeared 5590 mSeeking = false; 5591 scheduleNotification(NOTIFY_TIME, 0 /* delay */); 5592 } 5593 } 5594 } 5595 5596 /** @hide */ onBuffering(boolean buffering)5597 public void onBuffering(boolean buffering) { 5598 synchronized (this) { 5599 if (DEBUG) Log.d(TAG, "onBuffering: " + buffering); 5600 mBuffering = buffering; 5601 scheduleNotification(NOTIFY_TIME, 0 /* delay */); 5602 } 5603 } 5604 5605 /** @hide */ onStopped()5606 public void onStopped() { 5607 synchronized(this) { 5608 if (DEBUG) Log.d(TAG, "onStopped"); 5609 mPaused = true; 5610 mStopped = true; 5611 mSeeking = false; 5612 mBuffering = false; 5613 scheduleNotification(NOTIFY_STOP, 0 /* delay */); 5614 } 5615 } 5616 5617 /** @hide */ 5618 @Override onSeekComplete(MediaPlayer mp)5619 public void onSeekComplete(MediaPlayer mp) { 5620 synchronized(this) { 5621 mStopped = false; 5622 mSeeking = true; 5623 scheduleNotification(NOTIFY_SEEK, 0 /* delay */); 5624 } 5625 } 5626 5627 /** @hide */ onNewPlayer()5628 public void onNewPlayer() { 5629 if (mRefresh) { 5630 synchronized(this) { 5631 mStopped = false; 5632 mSeeking = true; 5633 mBuffering = false; 5634 scheduleNotification(NOTIFY_SEEK, 0 /* delay */); 5635 } 5636 } 5637 } 5638 notifySeek()5639 private synchronized void notifySeek() { 5640 mSeeking = false; 5641 try { 5642 long timeUs = getCurrentTimeUs(true, false); 5643 if (DEBUG) Log.d(TAG, "onSeekComplete at " + timeUs); 5644 5645 for (MediaTimeProvider.OnMediaTimeListener listener: mListeners) { 5646 if (listener == null) { 5647 break; 5648 } 5649 listener.onSeek(timeUs); 5650 } 5651 } catch (IllegalStateException e) { 5652 // we should not be there, but at least signal pause 5653 if (DEBUG) Log.d(TAG, "onSeekComplete but no player"); 5654 mPausing = true; // special handling if player disappeared 5655 notifyTimedEvent(false /* refreshTime */); 5656 } 5657 } 5658 notifyTrackData(Pair<SubtitleTrack, byte[]> trackData)5659 private synchronized void notifyTrackData(Pair<SubtitleTrack, byte[]> trackData) { 5660 SubtitleTrack track = trackData.first; 5661 byte[] data = trackData.second; 5662 track.onData(data, true /* eos */, ~0 /* runID: keep forever */); 5663 } 5664 notifyStop()5665 private synchronized void notifyStop() { 5666 for (MediaTimeProvider.OnMediaTimeListener listener: mListeners) { 5667 if (listener == null) { 5668 break; 5669 } 5670 listener.onStop(); 5671 } 5672 } 5673 registerListener(MediaTimeProvider.OnMediaTimeListener listener)5674 private int registerListener(MediaTimeProvider.OnMediaTimeListener listener) { 5675 int i = 0; 5676 for (; i < mListeners.length; i++) { 5677 if (mListeners[i] == listener || mListeners[i] == null) { 5678 break; 5679 } 5680 } 5681 5682 // new listener 5683 if (i >= mListeners.length) { 5684 MediaTimeProvider.OnMediaTimeListener[] newListeners = 5685 new MediaTimeProvider.OnMediaTimeListener[i + 1]; 5686 long[] newTimes = new long[i + 1]; 5687 System.arraycopy(mListeners, 0, newListeners, 0, mListeners.length); 5688 System.arraycopy(mTimes, 0, newTimes, 0, mTimes.length); 5689 mListeners = newListeners; 5690 mTimes = newTimes; 5691 } 5692 5693 if (mListeners[i] == null) { 5694 mListeners[i] = listener; 5695 mTimes[i] = MediaTimeProvider.NO_TIME; 5696 } 5697 return i; 5698 } 5699 notifyAt( long timeUs, MediaTimeProvider.OnMediaTimeListener listener)5700 public void notifyAt( 5701 long timeUs, MediaTimeProvider.OnMediaTimeListener listener) { 5702 synchronized(this) { 5703 if (DEBUG) Log.d(TAG, "notifyAt " + timeUs); 5704 mTimes[registerListener(listener)] = timeUs; 5705 scheduleNotification(NOTIFY_TIME, 0 /* delay */); 5706 } 5707 } 5708 scheduleUpdate(MediaTimeProvider.OnMediaTimeListener listener)5709 public void scheduleUpdate(MediaTimeProvider.OnMediaTimeListener listener) { 5710 synchronized(this) { 5711 if (DEBUG) Log.d(TAG, "scheduleUpdate"); 5712 int i = registerListener(listener); 5713 5714 if (!mStopped) { 5715 mTimes[i] = 0; 5716 scheduleNotification(NOTIFY_TIME, 0 /* delay */); 5717 } 5718 } 5719 } 5720 cancelNotifications( MediaTimeProvider.OnMediaTimeListener listener)5721 public void cancelNotifications( 5722 MediaTimeProvider.OnMediaTimeListener listener) { 5723 synchronized(this) { 5724 int i = 0; 5725 for (; i < mListeners.length; i++) { 5726 if (mListeners[i] == listener) { 5727 System.arraycopy(mListeners, i + 1, 5728 mListeners, i, mListeners.length - i - 1); 5729 System.arraycopy(mTimes, i + 1, 5730 mTimes, i, mTimes.length - i - 1); 5731 mListeners[mListeners.length - 1] = null; 5732 mTimes[mTimes.length - 1] = NO_TIME; 5733 break; 5734 } else if (mListeners[i] == null) { 5735 break; 5736 } 5737 } 5738 5739 scheduleNotification(NOTIFY_TIME, 0 /* delay */); 5740 } 5741 } 5742 notifyTimedEvent(boolean refreshTime)5743 private synchronized void notifyTimedEvent(boolean refreshTime) { 5744 // figure out next callback 5745 long nowUs; 5746 try { 5747 nowUs = getCurrentTimeUs(refreshTime, true); 5748 } catch (IllegalStateException e) { 5749 // assume we paused until new player arrives 5750 mRefresh = true; 5751 mPausing = true; // this ensures that call succeeds 5752 nowUs = getCurrentTimeUs(refreshTime, true); 5753 } 5754 long nextTimeUs = nowUs; 5755 5756 if (mSeeking) { 5757 // skip timed-event notifications until seek is complete 5758 return; 5759 } 5760 5761 if (DEBUG) { 5762 StringBuilder sb = new StringBuilder(); 5763 sb.append("notifyTimedEvent(").append(mLastTimeUs).append(" -> ") 5764 .append(nowUs).append(") from {"); 5765 boolean first = true; 5766 for (long time: mTimes) { 5767 if (time == NO_TIME) { 5768 continue; 5769 } 5770 if (!first) sb.append(", "); 5771 sb.append(time); 5772 first = false; 5773 } 5774 sb.append("}"); 5775 Log.d(TAG, sb.toString()); 5776 } 5777 5778 Vector<MediaTimeProvider.OnMediaTimeListener> activatedListeners = 5779 new Vector<MediaTimeProvider.OnMediaTimeListener>(); 5780 for (int ix = 0; ix < mTimes.length; ix++) { 5781 if (mListeners[ix] == null) { 5782 break; 5783 } 5784 if (mTimes[ix] <= NO_TIME) { 5785 // ignore, unless we were stopped 5786 } else if (mTimes[ix] <= nowUs + MAX_EARLY_CALLBACK_US) { 5787 activatedListeners.add(mListeners[ix]); 5788 if (DEBUG) Log.d(TAG, "removed"); 5789 mTimes[ix] = NO_TIME; 5790 } else if (nextTimeUs == nowUs || mTimes[ix] < nextTimeUs) { 5791 nextTimeUs = mTimes[ix]; 5792 } 5793 } 5794 5795 if (nextTimeUs > nowUs && !mPaused) { 5796 // schedule callback at nextTimeUs 5797 if (DEBUG) Log.d(TAG, "scheduling for " + nextTimeUs + " and " + nowUs); 5798 mPlayer.notifyAt(nextTimeUs); 5799 } else { 5800 mEventHandler.removeMessages(NOTIFY); 5801 // no more callbacks 5802 } 5803 5804 for (MediaTimeProvider.OnMediaTimeListener listener: activatedListeners) { 5805 listener.onTimedEvent(nowUs); 5806 } 5807 } 5808 getCurrentTimeUs(boolean refreshTime, boolean monotonic)5809 public long getCurrentTimeUs(boolean refreshTime, boolean monotonic) 5810 throws IllegalStateException { 5811 synchronized (this) { 5812 // we always refresh the time when the paused-state changes, because 5813 // we expect to have received the pause-change event delayed. 5814 if (mPaused && !refreshTime) { 5815 return mLastReportedTime; 5816 } 5817 5818 try { 5819 mLastTimeUs = mPlayer.getCurrentPosition() * 1000L; 5820 mPaused = !mPlayer.isPlaying() || mBuffering; 5821 if (DEBUG) Log.v(TAG, (mPaused ? "paused" : "playing") + " at " + mLastTimeUs); 5822 } catch (IllegalStateException e) { 5823 if (mPausing) { 5824 // if we were pausing, get last estimated timestamp 5825 mPausing = false; 5826 if (!monotonic || mLastReportedTime < mLastTimeUs) { 5827 mLastReportedTime = mLastTimeUs; 5828 } 5829 mPaused = true; 5830 if (DEBUG) Log.d(TAG, "illegal state, but pausing: estimating at " + mLastReportedTime); 5831 return mLastReportedTime; 5832 } 5833 // TODO get time when prepared 5834 throw e; 5835 } 5836 if (monotonic && mLastTimeUs < mLastReportedTime) { 5837 /* have to adjust time */ 5838 if (mLastReportedTime - mLastTimeUs > 1000000) { 5839 // schedule seeked event if time jumped significantly 5840 // TODO: do this properly by introducing an exception 5841 mStopped = false; 5842 mSeeking = true; 5843 scheduleNotification(NOTIFY_SEEK, 0 /* delay */); 5844 } 5845 } else { 5846 mLastReportedTime = mLastTimeUs; 5847 } 5848 5849 return mLastReportedTime; 5850 } 5851 } 5852 5853 private class EventHandler extends Handler { EventHandler(Looper looper)5854 public EventHandler(Looper looper) { 5855 super(looper); 5856 } 5857 5858 @Override handleMessage(Message msg)5859 public void handleMessage(Message msg) { 5860 if (msg.what == NOTIFY) { 5861 switch (msg.arg1) { 5862 case NOTIFY_TIME: 5863 notifyTimedEvent(true /* refreshTime */); 5864 break; 5865 case NOTIFY_STOP: 5866 notifyStop(); 5867 break; 5868 case NOTIFY_SEEK: 5869 notifySeek(); 5870 break; 5871 case NOTIFY_TRACK_DATA: 5872 notifyTrackData((Pair<SubtitleTrack, byte[]>)msg.obj); 5873 break; 5874 } 5875 } 5876 } 5877 } 5878 } 5879 5880 public final static class MetricsConstants 5881 { MetricsConstants()5882 private MetricsConstants() {} 5883 5884 /** 5885 * Key to extract the MIME type of the video track 5886 * from the {@link MediaPlayer#getMetrics} return value. 5887 * The value is a String. 5888 */ 5889 public static final String MIME_TYPE_VIDEO = "android.media.mediaplayer.video.mime"; 5890 5891 /** 5892 * Key to extract the codec being used to decode the video track 5893 * from the {@link MediaPlayer#getMetrics} return value. 5894 * The value is a String. 5895 */ 5896 public static final String CODEC_VIDEO = "android.media.mediaplayer.video.codec"; 5897 5898 /** 5899 * Key to extract the width (in pixels) of the video track 5900 * from the {@link MediaPlayer#getMetrics} return value. 5901 * The value is an integer. 5902 */ 5903 public static final String WIDTH = "android.media.mediaplayer.width"; 5904 5905 /** 5906 * Key to extract the height (in pixels) of the video track 5907 * from the {@link MediaPlayer#getMetrics} return value. 5908 * The value is an integer. 5909 */ 5910 public static final String HEIGHT = "android.media.mediaplayer.height"; 5911 5912 /** 5913 * Key to extract the count of video frames played 5914 * from the {@link MediaPlayer#getMetrics} return value. 5915 * The value is an integer. 5916 */ 5917 public static final String FRAMES = "android.media.mediaplayer.frames"; 5918 5919 /** 5920 * Key to extract the count of video frames dropped 5921 * from the {@link MediaPlayer#getMetrics} return value. 5922 * The value is an integer. 5923 */ 5924 public static final String FRAMES_DROPPED = "android.media.mediaplayer.dropped"; 5925 5926 /** 5927 * Key to extract the MIME type of the audio track 5928 * from the {@link MediaPlayer#getMetrics} return value. 5929 * The value is a String. 5930 */ 5931 public static final String MIME_TYPE_AUDIO = "android.media.mediaplayer.audio.mime"; 5932 5933 /** 5934 * Key to extract the codec being used to decode the audio track 5935 * from the {@link MediaPlayer#getMetrics} return value. 5936 * The value is a String. 5937 */ 5938 public static final String CODEC_AUDIO = "android.media.mediaplayer.audio.codec"; 5939 5940 /** 5941 * Key to extract the duration (in milliseconds) of the 5942 * media being played 5943 * from the {@link MediaPlayer#getMetrics} return value. 5944 * The value is a long. 5945 */ 5946 public static final String DURATION = "android.media.mediaplayer.durationMs"; 5947 5948 /** 5949 * Key to extract the playing time (in milliseconds) of the 5950 * media being played 5951 * from the {@link MediaPlayer#getMetrics} return value. 5952 * The value is a long. 5953 */ 5954 public static final String PLAYING = "android.media.mediaplayer.playingMs"; 5955 5956 /** 5957 * Key to extract the count of errors encountered while 5958 * playing the media 5959 * from the {@link MediaPlayer#getMetrics} return value. 5960 * The value is an integer. 5961 */ 5962 public static final String ERRORS = "android.media.mediaplayer.err"; 5963 5964 /** 5965 * Key to extract an (optional) error code detected while 5966 * playing the media 5967 * from the {@link MediaPlayer#getMetrics} return value. 5968 * The value is an integer. 5969 */ 5970 public static final String ERROR_CODE = "android.media.mediaplayer.errcode"; 5971 5972 } 5973 } 5974