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