MediaPlayer.java revision 5167ede2afc57b6114b09d4a854c8103b5161cd0
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 17package android.media; 18 19import android.annotation.IntDef; 20import android.annotation.NonNull; 21import android.annotation.Nullable; 22import android.app.ActivityThread; 23import android.content.ContentProvider; 24import android.content.ContentResolver; 25import android.content.Context; 26import android.content.res.AssetFileDescriptor; 27import android.net.Uri; 28import android.os.Bundle; 29import android.os.Handler; 30import android.os.HandlerThread; 31import android.os.IBinder; 32import android.os.Looper; 33import android.os.Message; 34import android.os.Parcel; 35import android.os.Parcelable; 36import android.os.PersistableBundle; 37import android.os.Process; 38import android.os.PowerManager; 39import android.os.SystemProperties; 40import android.provider.Settings; 41import android.system.ErrnoException; 42import android.system.OsConstants; 43import android.util.Log; 44import android.util.Pair; 45import android.view.Surface; 46import android.view.SurfaceHolder; 47import android.widget.VideoView; 48import android.graphics.SurfaceTexture; 49import android.media.AudioManager; 50import android.media.MediaDrm; 51import android.media.MediaFormat; 52import android.media.MediaTimeProvider; 53import android.media.PlaybackParams; 54import android.media.SubtitleController; 55import android.media.SubtitleController.Anchor; 56import android.media.SubtitleData; 57import android.media.SubtitleTrack.RenderingWidget; 58import android.media.SyncParams; 59 60import com.android.internal.util.Preconditions; 61 62import libcore.io.IoBridge; 63import libcore.io.Libcore; 64import libcore.io.Streams; 65 66import java.io.ByteArrayOutputStream; 67import java.io.File; 68import java.io.FileDescriptor; 69import java.io.FileInputStream; 70import java.io.IOException; 71import java.io.InputStream; 72import java.lang.Runnable; 73import java.lang.annotation.Retention; 74import java.lang.annotation.RetentionPolicy; 75import java.lang.ref.WeakReference; 76import java.net.HttpCookie; 77import java.net.HttpURLConnection; 78import java.net.InetSocketAddress; 79import java.net.URL; 80import java.nio.ByteOrder; 81import java.util.Arrays; 82import java.util.BitSet; 83import java.util.HashMap; 84import java.util.List; 85import java.util.Map; 86import java.util.Scanner; 87import java.util.Set; 88import java.util.UUID; 89import 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(int, 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(int, int)}. 290 * <ul> 291 * <li>Although the asynchronuous {@link #seekTo(int, 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(int, 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(int, 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 */ 575public 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"); 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 */ 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 */ 1119 public void setDataSource(String path, Map<String, String> headers) 1120 throws IOException, IllegalArgumentException, SecurityException, IllegalStateException { 1121 setDataSource(path, headers, null); 1122 } 1123 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 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 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 */ 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 */ 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 */ 1226 public void setDataSource(FileDescriptor fd, long offset, long length) 1227 throws IOException, IllegalArgumentException, IllegalStateException { 1228 _setDataSource(fd, offset, length); 1229 } 1230 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 */ 1241 public void setDataSource(MediaDataSource dataSource) 1242 throws IllegalArgumentException, IllegalStateException { 1243 _setDataSource(dataSource); 1244 } 1245 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 */ 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 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 */ 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 */ 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 1316 private void startImpl() { 1317 baseStart(); 1318 stayAwake(true); 1319 _start(); 1320 } 1321 1322 private native void _start() throws IllegalStateException; 1323 1324 1325 private int getAudioStreamType() { 1326 if (mStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) { 1327 mStreamType = _getAudioStreamType(); 1328 } 1329 return mStreamType; 1330 } 1331 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 */ 1340 public void stop() throws IllegalStateException { 1341 stayAwake(false); 1342 _stop(); 1343 baseStop(); 1344 } 1345 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 */ 1354 public void pause() throws IllegalStateException { 1355 stayAwake(false); 1356 _pause(); 1357 basePause(); 1358 } 1359 1360 private native void _pause() throws IllegalStateException; 1361 1362 @Override 1363 void playerStart() { 1364 start(); 1365 } 1366 1367 @Override 1368 void playerPause() { 1369 pause(); 1370 } 1371 1372 @Override 1373 void playerStop() { 1374 stop(); 1375 } 1376 1377 @Override 1378 /* package */ int playerApplyVolumeShaper( 1379 @NonNull VolumeShaper.Configuration configuration, 1380 @NonNull VolumeShaper.Operation operation) { 1381 return native_applyVolumeShaper(configuration, operation); 1382 } 1383 1384 @Override 1385 /* package */ @Nullable VolumeShaper.State playerGetVolumeShaperState(int id) { 1386 return native_getVolumeShaperState(id); 1387 } 1388 1389 @Override 1390 public @NonNull VolumeShaper createVolumeShaper( 1391 @NonNull VolumeShaper.Configuration configuration) { 1392 return new VolumeShaper(configuration, this); 1393 } 1394 1395 private native int native_applyVolumeShaper( 1396 @NonNull VolumeShaper.Configuration configuration, 1397 @NonNull VolumeShaper.Operation operation); 1398 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 */ 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 */ 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 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 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 */ 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 */ 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 */ 1513 public PersistableBundle getMetrics() { 1514 PersistableBundle bundle = native_getMetrics(); 1515 return bundle; 1516 } 1517 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 */ 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 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 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 */ 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 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 */ 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 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 */ 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 1704 public native SyncParams getSyncParams(); 1705 1706 /** 1707 * Seek modes used in method seekTo(int, 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(int, 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(int, int) 1719 */ 1720 public static final int SEEK_PREVIOUS_SYNC = 0x00; 1721 /** 1722 * This mode is used with {@link #seekTo(int, 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(int, int) 1727 */ 1728 public static final int SEEK_NEXT_SYNC = 0x01; 1729 /** 1730 * This mode is used with {@link #seekTo(int, 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(int, int) 1735 */ 1736 public static final int SEEK_CLOSEST_SYNC = 0x02; 1737 /** 1738 * This mode is used with {@link #seekTo(int, 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(int, 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 1757 private native final void _seekTo(int 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 */ 1789 public void seekTo(int msec, @SeekMode int mode) throws IllegalStateException { 1790 if (mode < SEEK_PREVIOUS_SYNC || mode > SEEK_CLOSEST) { 1791 final String msg = "Illegal seek mode: " + mode; 1792 throw new IllegalArgumentException(msg); 1793 } 1794 _seekTo(msec, mode); 1795 } 1796 1797 /** 1798 * Seeks to specified time position. 1799 * Same as {@link #seekTo(int, int)} with {@code mode = SEEK_PREVIOUS_SYNC}. 1800 * 1801 * @param msec the offset in milliseconds from the start to seek to 1802 * @throws IllegalStateException if the internal player engine has not been 1803 * initialized 1804 */ 1805 public void seekTo(int msec) throws IllegalStateException { 1806 seekTo(msec, SEEK_PREVIOUS_SYNC /* mode */); 1807 } 1808 1809 /** 1810 * Get current playback position as a {@link MediaTimestamp}. 1811 * <p> 1812 * The MediaTimestamp represents how the media time correlates to the system time in 1813 * a linear fashion using an anchor and a clock rate. During regular playback, the media 1814 * time moves fairly constantly (though the anchor frame may be rebased to a current 1815 * system time, the linear correlation stays steady). Therefore, this method does not 1816 * need to be called often. 1817 * <p> 1818 * To help users get current playback position, this method always anchors the timestamp 1819 * to the current {@link System#nanoTime system time}, so 1820 * {@link MediaTimestamp#getAnchorMediaTimeUs} can be used as current playback position. 1821 * 1822 * @return a MediaTimestamp object if a timestamp is available, or {@code null} if no timestamp 1823 * is available, e.g. because the media player has not been initialized. 1824 * 1825 * @see MediaTimestamp 1826 */ 1827 @Nullable 1828 public MediaTimestamp getTimestamp() 1829 { 1830 try { 1831 // TODO: get the timestamp from native side 1832 return new MediaTimestamp( 1833 getCurrentPosition() * 1000L, 1834 System.nanoTime(), 1835 isPlaying() ? getPlaybackParams().getSpeed() : 0.f); 1836 } catch (IllegalStateException e) { 1837 return null; 1838 } 1839 } 1840 1841 /** 1842 * Gets the current playback position. 1843 * 1844 * @return the current position in milliseconds 1845 */ 1846 public native int getCurrentPosition(); 1847 1848 /** 1849 * Gets the duration of the file. 1850 * 1851 * @return the duration in milliseconds, if no duration is available 1852 * (for example, if streaming live content), -1 is returned. 1853 */ 1854 public native int getDuration(); 1855 1856 /** 1857 * Gets the media metadata. 1858 * 1859 * @param update_only controls whether the full set of available 1860 * metadata is returned or just the set that changed since the 1861 * last call. See {@see #METADATA_UPDATE_ONLY} and {@see 1862 * #METADATA_ALL}. 1863 * 1864 * @param apply_filter if true only metadata that matches the 1865 * filter is returned. See {@see #APPLY_METADATA_FILTER} and {@see 1866 * #BYPASS_METADATA_FILTER}. 1867 * 1868 * @return The metadata, possibly empty. null if an error occured. 1869 // FIXME: unhide. 1870 * {@hide} 1871 */ 1872 public Metadata getMetadata(final boolean update_only, 1873 final boolean apply_filter) { 1874 Parcel reply = Parcel.obtain(); 1875 Metadata data = new Metadata(); 1876 1877 if (!native_getMetadata(update_only, apply_filter, reply)) { 1878 reply.recycle(); 1879 return null; 1880 } 1881 1882 // Metadata takes over the parcel, don't recycle it unless 1883 // there is an error. 1884 if (!data.parse(reply)) { 1885 reply.recycle(); 1886 return null; 1887 } 1888 return data; 1889 } 1890 1891 /** 1892 * Set a filter for the metadata update notification and update 1893 * retrieval. The caller provides 2 set of metadata keys, allowed 1894 * and blocked. The blocked set always takes precedence over the 1895 * allowed one. 1896 * Metadata.MATCH_ALL and Metadata.MATCH_NONE are 2 sets available as 1897 * shorthands to allow/block all or no metadata. 1898 * 1899 * By default, there is no filter set. 1900 * 1901 * @param allow Is the set of metadata the client is interested 1902 * in receiving new notifications for. 1903 * @param block Is the set of metadata the client is not interested 1904 * in receiving new notifications for. 1905 * @return The call status code. 1906 * 1907 // FIXME: unhide. 1908 * {@hide} 1909 */ 1910 public int setMetadataFilter(Set<Integer> allow, Set<Integer> block) { 1911 // Do our serialization manually instead of calling 1912 // Parcel.writeArray since the sets are made of the same type 1913 // we avoid paying the price of calling writeValue (used by 1914 // writeArray) which burns an extra int per element to encode 1915 // the type. 1916 Parcel request = newRequest(); 1917 1918 // The parcel starts already with an interface token. There 1919 // are 2 filters. Each one starts with a 4bytes number to 1920 // store the len followed by a number of int (4 bytes as well) 1921 // representing the metadata type. 1922 int capacity = request.dataSize() + 4 * (1 + allow.size() + 1 + block.size()); 1923 1924 if (request.dataCapacity() < capacity) { 1925 request.setDataCapacity(capacity); 1926 } 1927 1928 request.writeInt(allow.size()); 1929 for(Integer t: allow) { 1930 request.writeInt(t); 1931 } 1932 request.writeInt(block.size()); 1933 for(Integer t: block) { 1934 request.writeInt(t); 1935 } 1936 return native_setMetadataFilter(request); 1937 } 1938 1939 /** 1940 * Set the MediaPlayer to start when this MediaPlayer finishes playback 1941 * (i.e. reaches the end of the stream). 1942 * The media framework will attempt to transition from this player to 1943 * the next as seamlessly as possible. The next player can be set at 1944 * any time before completion, but shall be after setDataSource has been 1945 * called successfully. The next player must be prepared by the 1946 * app, and the application should not call start() on it. 1947 * The next MediaPlayer must be different from 'this'. An exception 1948 * will be thrown if next == this. 1949 * The application may call setNextMediaPlayer(null) to indicate no 1950 * next player should be started at the end of playback. 1951 * If the current player is looping, it will keep looping and the next 1952 * player will not be started. 1953 * 1954 * @param next the player to start after this one completes playback. 1955 * 1956 */ 1957 public native void setNextMediaPlayer(MediaPlayer next); 1958 1959 /** 1960 * Releases resources associated with this MediaPlayer object. 1961 * It is considered good practice to call this method when you're 1962 * done using the MediaPlayer. In particular, whenever an Activity 1963 * of an application is paused (its onPause() method is called), 1964 * or stopped (its onStop() method is called), this method should be 1965 * invoked to release the MediaPlayer object, unless the application 1966 * has a special need to keep the object around. In addition to 1967 * unnecessary resources (such as memory and instances of codecs) 1968 * being held, failure to call this method immediately if a 1969 * MediaPlayer object is no longer needed may also lead to 1970 * continuous battery consumption for mobile devices, and playback 1971 * failure for other applications if no multiple instances of the 1972 * same codec are supported on a device. Even if multiple instances 1973 * of the same codec are supported, some performance degradation 1974 * may be expected when unnecessary multiple instances are used 1975 * at the same time. 1976 */ 1977 public void release() { 1978 baseRelease(); 1979 stayAwake(false); 1980 updateSurfaceScreenOn(); 1981 mOnPreparedListener = null; 1982 mOnBufferingUpdateListener = null; 1983 mOnCompletionListener = null; 1984 mOnSeekCompleteListener = null; 1985 mOnErrorListener = null; 1986 mOnInfoListener = null; 1987 mOnVideoSizeChangedListener = null; 1988 mOnTimedTextListener = null; 1989 if (mTimeProvider != null) { 1990 mTimeProvider.close(); 1991 mTimeProvider = null; 1992 } 1993 mOnSubtitleDataListener = null; 1994 1995 // Modular DRM clean up 1996 mOnDrmConfigHelper = null; 1997 mOnDrmInfoHandlerDelegate = null; 1998 mOnDrmPreparedHandlerDelegate = null; 1999 resetDrmState(); 2000 2001 _release(); 2002 } 2003 2004 private native void _release(); 2005 2006 /** 2007 * Resets the MediaPlayer to its uninitialized state. After calling 2008 * this method, you will have to initialize it again by setting the 2009 * data source and calling prepare(). 2010 */ 2011 public void reset() { 2012 mSelectedSubtitleTrackIndex = -1; 2013 synchronized(mOpenSubtitleSources) { 2014 for (final InputStream is: mOpenSubtitleSources) { 2015 try { 2016 is.close(); 2017 } catch (IOException e) { 2018 } 2019 } 2020 mOpenSubtitleSources.clear(); 2021 } 2022 if (mSubtitleController != null) { 2023 mSubtitleController.reset(); 2024 } 2025 if (mTimeProvider != null) { 2026 mTimeProvider.close(); 2027 mTimeProvider = null; 2028 } 2029 2030 stayAwake(false); 2031 _reset(); 2032 // make sure none of the listeners get called anymore 2033 if (mEventHandler != null) { 2034 mEventHandler.removeCallbacksAndMessages(null); 2035 } 2036 2037 synchronized (mIndexTrackPairs) { 2038 mIndexTrackPairs.clear(); 2039 mInbandTrackIndices.clear(); 2040 }; 2041 2042 resetDrmState(); 2043 } 2044 2045 private native void _reset(); 2046 2047 /** 2048 * Sets the audio stream type for this MediaPlayer. See {@link AudioManager} 2049 * for a list of stream types. Must call this method before prepare() or 2050 * prepareAsync() in order for the target stream type to become effective 2051 * thereafter. 2052 * 2053 * @param streamtype the audio stream type 2054 * @deprecated use {@link #setAudioAttributes(AudioAttributes)} 2055 * @see android.media.AudioManager 2056 */ 2057 public void setAudioStreamType(int streamtype) { 2058 deprecateStreamTypeForPlayback(streamtype, "MediaPlayer", "setAudioStreamType()"); 2059 baseUpdateAudioAttributes( 2060 new AudioAttributes.Builder().setInternalLegacyStreamType(streamtype).build()); 2061 _setAudioStreamType(streamtype); 2062 mStreamType = streamtype; 2063 } 2064 2065 private native void _setAudioStreamType(int streamtype); 2066 2067 // Keep KEY_PARAMETER_* in sync with include/media/mediaplayer.h 2068 private final static int KEY_PARAMETER_AUDIO_ATTRIBUTES = 1400; 2069 /** 2070 * Sets the parameter indicated by key. 2071 * @param key key indicates the parameter to be set. 2072 * @param value value of the parameter to be set. 2073 * @return true if the parameter is set successfully, false otherwise 2074 * {@hide} 2075 */ 2076 private native boolean setParameter(int key, Parcel value); 2077 2078 /** 2079 * Sets the audio attributes for this MediaPlayer. 2080 * See {@link AudioAttributes} for how to build and configure an instance of this class. 2081 * You must call this method before {@link #prepare()} or {@link #prepareAsync()} in order 2082 * for the audio attributes to become effective thereafter. 2083 * @param attributes a non-null set of audio attributes 2084 */ 2085 public void setAudioAttributes(AudioAttributes attributes) throws IllegalArgumentException { 2086 if (attributes == null) { 2087 final String msg = "Cannot set AudioAttributes to null"; 2088 throw new IllegalArgumentException(msg); 2089 } 2090 baseUpdateAudioAttributes(attributes); 2091 mUsage = attributes.getUsage(); 2092 mBypassInterruptionPolicy = (attributes.getAllFlags() 2093 & AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY) != 0; 2094 Parcel pattributes = Parcel.obtain(); 2095 attributes.writeToParcel(pattributes, AudioAttributes.FLATTEN_TAGS); 2096 setParameter(KEY_PARAMETER_AUDIO_ATTRIBUTES, pattributes); 2097 pattributes.recycle(); 2098 } 2099 2100 /** 2101 * Sets the player to be looping or non-looping. 2102 * 2103 * @param looping whether to loop or not 2104 */ 2105 public native void setLooping(boolean looping); 2106 2107 /** 2108 * Checks whether the MediaPlayer is looping or non-looping. 2109 * 2110 * @return true if the MediaPlayer is currently looping, false otherwise 2111 */ 2112 public native boolean isLooping(); 2113 2114 /** 2115 * Sets the volume on this player. 2116 * This API is recommended for balancing the output of audio streams 2117 * within an application. Unless you are writing an application to 2118 * control user settings, this API should be used in preference to 2119 * {@link AudioManager#setStreamVolume(int, int, int)} which sets the volume of ALL streams of 2120 * a particular type. Note that the passed volume values are raw scalars in range 0.0 to 1.0. 2121 * UI controls should be scaled logarithmically. 2122 * 2123 * @param leftVolume left volume scalar 2124 * @param rightVolume right volume scalar 2125 */ 2126 /* 2127 * FIXME: Merge this into javadoc comment above when setVolume(float) is not @hide. 2128 * The single parameter form below is preferred if the channel volumes don't need 2129 * to be set independently. 2130 */ 2131 public void setVolume(float leftVolume, float rightVolume) { 2132 baseSetVolume(leftVolume, rightVolume); 2133 } 2134 2135 @Override 2136 void playerSetVolume(boolean muting, float leftVolume, float rightVolume) { 2137 _setVolume(muting ? 0.0f : leftVolume, muting ? 0.0f : rightVolume); 2138 } 2139 2140 private native void _setVolume(float leftVolume, float rightVolume); 2141 2142 /** 2143 * Similar, excepts sets volume of all channels to same value. 2144 * @hide 2145 */ 2146 public void setVolume(float volume) { 2147 setVolume(volume, volume); 2148 } 2149 2150 /** 2151 * Sets the audio session ID. 2152 * 2153 * @param sessionId the audio session ID. 2154 * The audio session ID is a system wide unique identifier for the audio stream played by 2155 * this MediaPlayer instance. 2156 * The primary use of the audio session ID is to associate audio effects to a particular 2157 * instance of MediaPlayer: if an audio session ID is provided when creating an audio effect, 2158 * this effect will be applied only to the audio content of media players within the same 2159 * audio session and not to the output mix. 2160 * When created, a MediaPlayer instance automatically generates its own audio session ID. 2161 * However, it is possible to force this player to be part of an already existing audio session 2162 * by calling this method. 2163 * This method must be called before one of the overloaded <code> setDataSource </code> methods. 2164 * @throws IllegalStateException if it is called in an invalid state 2165 */ 2166 public native void setAudioSessionId(int sessionId) throws IllegalArgumentException, IllegalStateException; 2167 2168 /** 2169 * Returns the audio session ID. 2170 * 2171 * @return the audio session ID. {@see #setAudioSessionId(int)} 2172 * Note that the audio session ID is 0 only if a problem occured when the MediaPlayer was contructed. 2173 */ 2174 public native int getAudioSessionId(); 2175 2176 /** 2177 * Attaches an auxiliary effect to the player. A typical auxiliary effect is a reverberation 2178 * effect which can be applied on any sound source that directs a certain amount of its 2179 * energy to this effect. This amount is defined by setAuxEffectSendLevel(). 2180 * See {@link #setAuxEffectSendLevel(float)}. 2181 * <p>After creating an auxiliary effect (e.g. 2182 * {@link android.media.audiofx.EnvironmentalReverb}), retrieve its ID with 2183 * {@link android.media.audiofx.AudioEffect#getId()} and use it when calling this method 2184 * to attach the player to the effect. 2185 * <p>To detach the effect from the player, call this method with a null effect id. 2186 * <p>This method must be called after one of the overloaded <code> setDataSource </code> 2187 * methods. 2188 * @param effectId system wide unique id of the effect to attach 2189 */ 2190 public native void attachAuxEffect(int effectId); 2191 2192 2193 /** 2194 * Sets the send level of the player to the attached auxiliary effect. 2195 * See {@link #attachAuxEffect(int)}. The level value range is 0 to 1.0. 2196 * <p>By default the send level is 0, so even if an effect is attached to the player 2197 * this method must be called for the effect to be applied. 2198 * <p>Note that the passed level value is a raw scalar. UI controls should be scaled 2199 * logarithmically: the gain applied by audio framework ranges from -72dB to 0dB, 2200 * so an appropriate conversion from linear UI input x to level is: 2201 * x == 0 -> level = 0 2202 * 0 < x <= R -> level = 10^(72*(x-R)/20/R) 2203 * @param level send level scalar 2204 */ 2205 public void setAuxEffectSendLevel(float level) { 2206 baseSetAuxEffectSendLevel(level); 2207 } 2208 2209 @Override 2210 int playerSetAuxEffectSendLevel(boolean muting, float level) { 2211 _setAuxEffectSendLevel(muting ? 0.0f : level); 2212 return AudioSystem.SUCCESS; 2213 } 2214 2215 private native void _setAuxEffectSendLevel(float level); 2216 2217 /* 2218 * @param request Parcel destinated to the media player. The 2219 * Interface token must be set to the IMediaPlayer 2220 * one to be routed correctly through the system. 2221 * @param reply[out] Parcel that will contain the reply. 2222 * @return The status code. 2223 */ 2224 private native final int native_invoke(Parcel request, Parcel reply); 2225 2226 2227 /* 2228 * @param update_only If true fetch only the set of metadata that have 2229 * changed since the last invocation of getMetadata. 2230 * The set is built using the unfiltered 2231 * notifications the native player sent to the 2232 * MediaPlayerService during that period of 2233 * time. If false, all the metadatas are considered. 2234 * @param apply_filter If true, once the metadata set has been built based on 2235 * the value update_only, the current filter is applied. 2236 * @param reply[out] On return contains the serialized 2237 * metadata. Valid only if the call was successful. 2238 * @return The status code. 2239 */ 2240 private native final boolean native_getMetadata(boolean update_only, 2241 boolean apply_filter, 2242 Parcel reply); 2243 2244 /* 2245 * @param request Parcel with the 2 serialized lists of allowed 2246 * metadata types followed by the one to be 2247 * dropped. Each list starts with an integer 2248 * indicating the number of metadata type elements. 2249 * @return The status code. 2250 */ 2251 private native final int native_setMetadataFilter(Parcel request); 2252 2253 private static native final void native_init(); 2254 private native final void native_setup(Object mediaplayer_this); 2255 private native final void native_finalize(); 2256 2257 /** 2258 * Class for MediaPlayer to return each audio/video/subtitle track's metadata. 2259 * 2260 * @see android.media.MediaPlayer#getTrackInfo 2261 */ 2262 static public class TrackInfo implements Parcelable { 2263 /** 2264 * Gets the track type. 2265 * @return TrackType which indicates if the track is video, audio, timed text. 2266 */ 2267 public int getTrackType() { 2268 return mTrackType; 2269 } 2270 2271 /** 2272 * Gets the language code of the track. 2273 * @return a language code in either way of ISO-639-1 or ISO-639-2. 2274 * When the language is unknown or could not be determined, 2275 * ISO-639-2 language code, "und", is returned. 2276 */ 2277 public String getLanguage() { 2278 String language = mFormat.getString(MediaFormat.KEY_LANGUAGE); 2279 return language == null ? "und" : language; 2280 } 2281 2282 /** 2283 * Gets the {@link MediaFormat} of the track. If the format is 2284 * unknown or could not be determined, null is returned. 2285 */ 2286 public MediaFormat getFormat() { 2287 if (mTrackType == MEDIA_TRACK_TYPE_TIMEDTEXT 2288 || mTrackType == MEDIA_TRACK_TYPE_SUBTITLE) { 2289 return mFormat; 2290 } 2291 return null; 2292 } 2293 2294 public static final int MEDIA_TRACK_TYPE_UNKNOWN = 0; 2295 public static final int MEDIA_TRACK_TYPE_VIDEO = 1; 2296 public static final int MEDIA_TRACK_TYPE_AUDIO = 2; 2297 public static final int MEDIA_TRACK_TYPE_TIMEDTEXT = 3; 2298 public static final int MEDIA_TRACK_TYPE_SUBTITLE = 4; 2299 public static final int MEDIA_TRACK_TYPE_METADATA = 5; 2300 2301 final int mTrackType; 2302 final MediaFormat mFormat; 2303 2304 TrackInfo(Parcel in) { 2305 mTrackType = in.readInt(); 2306 // TODO: parcel in the full MediaFormat; currently we are using createSubtitleFormat 2307 // even for audio/video tracks, meaning we only set the mime and language. 2308 String mime = in.readString(); 2309 String language = in.readString(); 2310 mFormat = MediaFormat.createSubtitleFormat(mime, language); 2311 2312 if (mTrackType == MEDIA_TRACK_TYPE_SUBTITLE) { 2313 mFormat.setInteger(MediaFormat.KEY_IS_AUTOSELECT, in.readInt()); 2314 mFormat.setInteger(MediaFormat.KEY_IS_DEFAULT, in.readInt()); 2315 mFormat.setInteger(MediaFormat.KEY_IS_FORCED_SUBTITLE, in.readInt()); 2316 } 2317 } 2318 2319 /** @hide */ 2320 TrackInfo(int type, MediaFormat format) { 2321 mTrackType = type; 2322 mFormat = format; 2323 } 2324 2325 /** 2326 * {@inheritDoc} 2327 */ 2328 @Override 2329 public int describeContents() { 2330 return 0; 2331 } 2332 2333 /** 2334 * {@inheritDoc} 2335 */ 2336 @Override 2337 public void writeToParcel(Parcel dest, int flags) { 2338 dest.writeInt(mTrackType); 2339 dest.writeString(getLanguage()); 2340 2341 if (mTrackType == MEDIA_TRACK_TYPE_SUBTITLE) { 2342 dest.writeString(mFormat.getString(MediaFormat.KEY_MIME)); 2343 dest.writeInt(mFormat.getInteger(MediaFormat.KEY_IS_AUTOSELECT)); 2344 dest.writeInt(mFormat.getInteger(MediaFormat.KEY_IS_DEFAULT)); 2345 dest.writeInt(mFormat.getInteger(MediaFormat.KEY_IS_FORCED_SUBTITLE)); 2346 } 2347 } 2348 2349 @Override 2350 public String toString() { 2351 StringBuilder out = new StringBuilder(128); 2352 out.append(getClass().getName()); 2353 out.append('{'); 2354 switch (mTrackType) { 2355 case MEDIA_TRACK_TYPE_VIDEO: 2356 out.append("VIDEO"); 2357 break; 2358 case MEDIA_TRACK_TYPE_AUDIO: 2359 out.append("AUDIO"); 2360 break; 2361 case MEDIA_TRACK_TYPE_TIMEDTEXT: 2362 out.append("TIMEDTEXT"); 2363 break; 2364 case MEDIA_TRACK_TYPE_SUBTITLE: 2365 out.append("SUBTITLE"); 2366 break; 2367 default: 2368 out.append("UNKNOWN"); 2369 break; 2370 } 2371 out.append(", " + mFormat.toString()); 2372 out.append("}"); 2373 return out.toString(); 2374 } 2375 2376 /** 2377 * Used to read a TrackInfo from a Parcel. 2378 */ 2379 static final Parcelable.Creator<TrackInfo> CREATOR 2380 = new Parcelable.Creator<TrackInfo>() { 2381 @Override 2382 public TrackInfo createFromParcel(Parcel in) { 2383 return new TrackInfo(in); 2384 } 2385 2386 @Override 2387 public TrackInfo[] newArray(int size) { 2388 return new TrackInfo[size]; 2389 } 2390 }; 2391 2392 }; 2393 2394 // We would like domain specific classes with more informative names than the `first` and `second` 2395 // in generic Pair, but we would also like to avoid creating new/trivial classes. As a compromise 2396 // we document the meanings of `first` and `second` here: 2397 // 2398 // Pair.first - inband track index; non-null iff representing an inband track. 2399 // Pair.second - a SubtitleTrack registered with mSubtitleController; non-null iff representing 2400 // an inband subtitle track or any out-of-band track (subtitle or timedtext). 2401 private Vector<Pair<Integer, SubtitleTrack>> mIndexTrackPairs = new Vector<>(); 2402 private BitSet mInbandTrackIndices = new BitSet(); 2403 2404 /** 2405 * Returns an array of track information. 2406 * 2407 * @return Array of track info. The total number of tracks is the array length. 2408 * Must be called again if an external timed text source has been added after any of the 2409 * addTimedTextSource methods are called. 2410 * @throws IllegalStateException if it is called in an invalid state. 2411 */ 2412 public TrackInfo[] getTrackInfo() throws IllegalStateException { 2413 TrackInfo trackInfo[] = getInbandTrackInfo(); 2414 // add out-of-band tracks 2415 synchronized (mIndexTrackPairs) { 2416 TrackInfo allTrackInfo[] = new TrackInfo[mIndexTrackPairs.size()]; 2417 for (int i = 0; i < allTrackInfo.length; i++) { 2418 Pair<Integer, SubtitleTrack> p = mIndexTrackPairs.get(i); 2419 if (p.first != null) { 2420 // inband track 2421 allTrackInfo[i] = trackInfo[p.first]; 2422 } else { 2423 SubtitleTrack track = p.second; 2424 allTrackInfo[i] = new TrackInfo(track.getTrackType(), track.getFormat()); 2425 } 2426 } 2427 return allTrackInfo; 2428 } 2429 } 2430 2431 private TrackInfo[] getInbandTrackInfo() throws IllegalStateException { 2432 Parcel request = Parcel.obtain(); 2433 Parcel reply = Parcel.obtain(); 2434 try { 2435 request.writeInterfaceToken(IMEDIA_PLAYER); 2436 request.writeInt(INVOKE_ID_GET_TRACK_INFO); 2437 invoke(request, reply); 2438 TrackInfo trackInfo[] = reply.createTypedArray(TrackInfo.CREATOR); 2439 return trackInfo; 2440 } finally { 2441 request.recycle(); 2442 reply.recycle(); 2443 } 2444 } 2445 2446 /* Do not change these values without updating their counterparts 2447 * in include/media/stagefright/MediaDefs.h and media/libstagefright/MediaDefs.cpp! 2448 */ 2449 /** 2450 * MIME type for SubRip (SRT) container. Used in addTimedTextSource APIs. 2451 */ 2452 public static final String MEDIA_MIMETYPE_TEXT_SUBRIP = "application/x-subrip"; 2453 2454 /** 2455 * MIME type for WebVTT subtitle data. 2456 * @hide 2457 */ 2458 public static final String MEDIA_MIMETYPE_TEXT_VTT = "text/vtt"; 2459 2460 /** 2461 * MIME type for CEA-608 closed caption data. 2462 * @hide 2463 */ 2464 public static final String MEDIA_MIMETYPE_TEXT_CEA_608 = "text/cea-608"; 2465 2466 /** 2467 * MIME type for CEA-708 closed caption data. 2468 * @hide 2469 */ 2470 public static final String MEDIA_MIMETYPE_TEXT_CEA_708 = "text/cea-708"; 2471 2472 /* 2473 * A helper function to check if the mime type is supported by media framework. 2474 */ 2475 private static boolean availableMimeTypeForExternalSource(String mimeType) { 2476 if (MEDIA_MIMETYPE_TEXT_SUBRIP.equals(mimeType)) { 2477 return true; 2478 } 2479 return false; 2480 } 2481 2482 private SubtitleController mSubtitleController; 2483 2484 /** @hide */ 2485 public void setSubtitleAnchor( 2486 SubtitleController controller, 2487 SubtitleController.Anchor anchor) { 2488 // TODO: create SubtitleController in MediaPlayer 2489 mSubtitleController = controller; 2490 mSubtitleController.setAnchor(anchor); 2491 } 2492 2493 /** 2494 * The private version of setSubtitleAnchor is used internally to set mSubtitleController if 2495 * necessary when clients don't provide their own SubtitleControllers using the public version 2496 * {@link #setSubtitleAnchor(SubtitleController, Anchor)} (e.g. {@link VideoView} provides one). 2497 */ 2498 private synchronized void setSubtitleAnchor() { 2499 if ((mSubtitleController == null) && (ActivityThread.currentApplication() != null)) { 2500 final HandlerThread thread = new HandlerThread("SetSubtitleAnchorThread"); 2501 thread.start(); 2502 Handler handler = new Handler(thread.getLooper()); 2503 handler.post(new Runnable() { 2504 @Override 2505 public void run() { 2506 Context context = ActivityThread.currentApplication(); 2507 mSubtitleController = new SubtitleController(context, mTimeProvider, MediaPlayer.this); 2508 mSubtitleController.setAnchor(new Anchor() { 2509 @Override 2510 public void setSubtitleWidget(RenderingWidget subtitleWidget) { 2511 } 2512 2513 @Override 2514 public Looper getSubtitleLooper() { 2515 return Looper.getMainLooper(); 2516 } 2517 }); 2518 thread.getLooper().quitSafely(); 2519 } 2520 }); 2521 try { 2522 thread.join(); 2523 } catch (InterruptedException e) { 2524 Thread.currentThread().interrupt(); 2525 Log.w(TAG, "failed to join SetSubtitleAnchorThread"); 2526 } 2527 } 2528 } 2529 2530 private int mSelectedSubtitleTrackIndex = -1; 2531 private Vector<InputStream> mOpenSubtitleSources; 2532 2533 private OnSubtitleDataListener mSubtitleDataListener = new OnSubtitleDataListener() { 2534 @Override 2535 public void onSubtitleData(MediaPlayer mp, SubtitleData data) { 2536 int index = data.getTrackIndex(); 2537 synchronized (mIndexTrackPairs) { 2538 for (Pair<Integer, SubtitleTrack> p : mIndexTrackPairs) { 2539 if (p.first != null && p.first == index && p.second != null) { 2540 // inband subtitle track that owns data 2541 SubtitleTrack track = p.second; 2542 track.onData(data); 2543 } 2544 } 2545 } 2546 } 2547 }; 2548 2549 /** @hide */ 2550 @Override 2551 public void onSubtitleTrackSelected(SubtitleTrack track) { 2552 if (mSelectedSubtitleTrackIndex >= 0) { 2553 try { 2554 selectOrDeselectInbandTrack(mSelectedSubtitleTrackIndex, false); 2555 } catch (IllegalStateException e) { 2556 } 2557 mSelectedSubtitleTrackIndex = -1; 2558 } 2559 setOnSubtitleDataListener(null); 2560 if (track == null) { 2561 return; 2562 } 2563 2564 synchronized (mIndexTrackPairs) { 2565 for (Pair<Integer, SubtitleTrack> p : mIndexTrackPairs) { 2566 if (p.first != null && p.second == track) { 2567 // inband subtitle track that is selected 2568 mSelectedSubtitleTrackIndex = p.first; 2569 break; 2570 } 2571 } 2572 } 2573 2574 if (mSelectedSubtitleTrackIndex >= 0) { 2575 try { 2576 selectOrDeselectInbandTrack(mSelectedSubtitleTrackIndex, true); 2577 } catch (IllegalStateException e) { 2578 } 2579 setOnSubtitleDataListener(mSubtitleDataListener); 2580 } 2581 // no need to select out-of-band tracks 2582 } 2583 2584 /** @hide */ 2585 public void addSubtitleSource(InputStream is, MediaFormat format) 2586 throws IllegalStateException 2587 { 2588 final InputStream fIs = is; 2589 final MediaFormat fFormat = format; 2590 2591 if (is != null) { 2592 // Ensure all input streams are closed. It is also a handy 2593 // way to implement timeouts in the future. 2594 synchronized(mOpenSubtitleSources) { 2595 mOpenSubtitleSources.add(is); 2596 } 2597 } else { 2598 Log.w(TAG, "addSubtitleSource called with null InputStream"); 2599 } 2600 2601 getMediaTimeProvider(); 2602 2603 // process each subtitle in its own thread 2604 final HandlerThread thread = new HandlerThread("SubtitleReadThread", 2605 Process.THREAD_PRIORITY_BACKGROUND + Process.THREAD_PRIORITY_MORE_FAVORABLE); 2606 thread.start(); 2607 Handler handler = new Handler(thread.getLooper()); 2608 handler.post(new Runnable() { 2609 private int addTrack() { 2610 if (fIs == null || mSubtitleController == null) { 2611 return MEDIA_INFO_UNSUPPORTED_SUBTITLE; 2612 } 2613 2614 SubtitleTrack track = mSubtitleController.addTrack(fFormat); 2615 if (track == null) { 2616 return MEDIA_INFO_UNSUPPORTED_SUBTITLE; 2617 } 2618 2619 // TODO: do the conversion in the subtitle track 2620 Scanner scanner = new Scanner(fIs, "UTF-8"); 2621 String contents = scanner.useDelimiter("\\A").next(); 2622 synchronized(mOpenSubtitleSources) { 2623 mOpenSubtitleSources.remove(fIs); 2624 } 2625 scanner.close(); 2626 synchronized (mIndexTrackPairs) { 2627 mIndexTrackPairs.add(Pair.<Integer, SubtitleTrack>create(null, track)); 2628 } 2629 Handler h = mTimeProvider.mEventHandler; 2630 int what = TimeProvider.NOTIFY; 2631 int arg1 = TimeProvider.NOTIFY_TRACK_DATA; 2632 Pair<SubtitleTrack, byte[]> trackData = Pair.create(track, contents.getBytes()); 2633 Message m = h.obtainMessage(what, arg1, 0, trackData); 2634 h.sendMessage(m); 2635 return MEDIA_INFO_EXTERNAL_METADATA_UPDATE; 2636 } 2637 2638 public void run() { 2639 int res = addTrack(); 2640 if (mEventHandler != null) { 2641 Message m = mEventHandler.obtainMessage(MEDIA_INFO, res, 0, null); 2642 mEventHandler.sendMessage(m); 2643 } 2644 thread.getLooper().quitSafely(); 2645 } 2646 }); 2647 } 2648 2649 private void scanInternalSubtitleTracks() { 2650 setSubtitleAnchor(); 2651 2652 populateInbandTracks(); 2653 2654 if (mSubtitleController != null) { 2655 mSubtitleController.selectDefaultTrack(); 2656 } 2657 } 2658 2659 private void populateInbandTracks() { 2660 TrackInfo[] tracks = getInbandTrackInfo(); 2661 synchronized (mIndexTrackPairs) { 2662 for (int i = 0; i < tracks.length; i++) { 2663 if (mInbandTrackIndices.get(i)) { 2664 continue; 2665 } else { 2666 mInbandTrackIndices.set(i); 2667 } 2668 2669 // newly appeared inband track 2670 if (tracks[i].getTrackType() == TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE) { 2671 SubtitleTrack track = mSubtitleController.addTrack( 2672 tracks[i].getFormat()); 2673 mIndexTrackPairs.add(Pair.create(i, track)); 2674 } else { 2675 mIndexTrackPairs.add(Pair.<Integer, SubtitleTrack>create(i, null)); 2676 } 2677 } 2678 } 2679 } 2680 2681 /* TODO: Limit the total number of external timed text source to a reasonable number. 2682 */ 2683 /** 2684 * Adds an external timed text source file. 2685 * 2686 * Currently supported format is SubRip with the file extension .srt, case insensitive. 2687 * Note that a single external timed text source may contain multiple tracks in it. 2688 * One can find the total number of available tracks using {@link #getTrackInfo()} to see what 2689 * additional tracks become available after this method call. 2690 * 2691 * @param path The file path of external timed text source file. 2692 * @param mimeType The mime type of the file. Must be one of the mime types listed above. 2693 * @throws IOException if the file cannot be accessed or is corrupted. 2694 * @throws IllegalArgumentException if the mimeType is not supported. 2695 * @throws IllegalStateException if called in an invalid state. 2696 */ 2697 public void addTimedTextSource(String path, String mimeType) 2698 throws IOException, IllegalArgumentException, IllegalStateException { 2699 if (!availableMimeTypeForExternalSource(mimeType)) { 2700 final String msg = "Illegal mimeType for timed text source: " + mimeType; 2701 throw new IllegalArgumentException(msg); 2702 } 2703 2704 File file = new File(path); 2705 if (file.exists()) { 2706 FileInputStream is = new FileInputStream(file); 2707 FileDescriptor fd = is.getFD(); 2708 addTimedTextSource(fd, mimeType); 2709 is.close(); 2710 } else { 2711 // We do not support the case where the path is not a file. 2712 throw new IOException(path); 2713 } 2714 } 2715 2716 /** 2717 * Adds an external timed text source file (Uri). 2718 * 2719 * Currently supported format is SubRip with the file extension .srt, case insensitive. 2720 * Note that a single external timed text source may contain multiple tracks in it. 2721 * One can find the total number of available tracks using {@link #getTrackInfo()} to see what 2722 * additional tracks become available after this method call. 2723 * 2724 * @param context the Context to use when resolving the Uri 2725 * @param uri the Content URI of the data you want to play 2726 * @param mimeType The mime type of the file. Must be one of the mime types listed above. 2727 * @throws IOException if the file cannot be accessed or is corrupted. 2728 * @throws IllegalArgumentException if the mimeType is not supported. 2729 * @throws IllegalStateException if called in an invalid state. 2730 */ 2731 public void addTimedTextSource(Context context, Uri uri, String mimeType) 2732 throws IOException, IllegalArgumentException, IllegalStateException { 2733 String scheme = uri.getScheme(); 2734 if(scheme == null || scheme.equals("file")) { 2735 addTimedTextSource(uri.getPath(), mimeType); 2736 return; 2737 } 2738 2739 AssetFileDescriptor fd = null; 2740 try { 2741 ContentResolver resolver = context.getContentResolver(); 2742 fd = resolver.openAssetFileDescriptor(uri, "r"); 2743 if (fd == null) { 2744 return; 2745 } 2746 addTimedTextSource(fd.getFileDescriptor(), mimeType); 2747 return; 2748 } catch (SecurityException ex) { 2749 } catch (IOException ex) { 2750 } finally { 2751 if (fd != null) { 2752 fd.close(); 2753 } 2754 } 2755 } 2756 2757 /** 2758 * Adds an external timed text source file (FileDescriptor). 2759 * 2760 * It is the caller's responsibility to close the file descriptor. 2761 * It is safe to do so as soon as this call returns. 2762 * 2763 * Currently supported format is SubRip. Note that a single external timed text source may 2764 * contain multiple tracks in it. One can find the total number of available tracks 2765 * using {@link #getTrackInfo()} to see what additional tracks become available 2766 * after this method call. 2767 * 2768 * @param fd the FileDescriptor for the file you want to play 2769 * @param mimeType The mime type of the file. Must be one of the mime types listed above. 2770 * @throws IllegalArgumentException if the mimeType is not supported. 2771 * @throws IllegalStateException if called in an invalid state. 2772 */ 2773 public void addTimedTextSource(FileDescriptor fd, String mimeType) 2774 throws IllegalArgumentException, IllegalStateException { 2775 // intentionally less than LONG_MAX 2776 addTimedTextSource(fd, 0, 0x7ffffffffffffffL, mimeType); 2777 } 2778 2779 /** 2780 * Adds an external timed text file (FileDescriptor). 2781 * 2782 * It is the caller's responsibility to close the file descriptor. 2783 * It is safe to do so as soon as this call returns. 2784 * 2785 * Currently supported format is SubRip. Note that a single external timed text source may 2786 * contain multiple tracks in it. One can find the total number of available tracks 2787 * using {@link #getTrackInfo()} to see what additional tracks become available 2788 * after this method call. 2789 * 2790 * @param fd the FileDescriptor for the file you want to play 2791 * @param offset the offset into the file where the data to be played starts, in bytes 2792 * @param length the length in bytes of the data to be played 2793 * @param mime The mime type of the file. Must be one of the mime types listed above. 2794 * @throws IllegalArgumentException if the mimeType is not supported. 2795 * @throws IllegalStateException if called in an invalid state. 2796 */ 2797 public void addTimedTextSource(FileDescriptor fd, long offset, long length, String mime) 2798 throws IllegalArgumentException, IllegalStateException { 2799 if (!availableMimeTypeForExternalSource(mime)) { 2800 throw new IllegalArgumentException("Illegal mimeType for timed text source: " + mime); 2801 } 2802 2803 final FileDescriptor dupedFd; 2804 try { 2805 dupedFd = Libcore.os.dup(fd); 2806 } catch (ErrnoException ex) { 2807 Log.e(TAG, ex.getMessage(), ex); 2808 throw new RuntimeException(ex); 2809 } 2810 2811 final MediaFormat fFormat = new MediaFormat(); 2812 fFormat.setString(MediaFormat.KEY_MIME, mime); 2813 fFormat.setInteger(MediaFormat.KEY_IS_TIMED_TEXT, 1); 2814 2815 // A MediaPlayer created by a VideoView should already have its mSubtitleController set. 2816 if (mSubtitleController == null) { 2817 setSubtitleAnchor(); 2818 } 2819 2820 if (!mSubtitleController.hasRendererFor(fFormat)) { 2821 // test and add not atomic 2822 Context context = ActivityThread.currentApplication(); 2823 mSubtitleController.registerRenderer(new SRTRenderer(context, mEventHandler)); 2824 } 2825 final SubtitleTrack track = mSubtitleController.addTrack(fFormat); 2826 synchronized (mIndexTrackPairs) { 2827 mIndexTrackPairs.add(Pair.<Integer, SubtitleTrack>create(null, track)); 2828 } 2829 2830 getMediaTimeProvider(); 2831 2832 final long offset2 = offset; 2833 final long length2 = length; 2834 final HandlerThread thread = new HandlerThread( 2835 "TimedTextReadThread", 2836 Process.THREAD_PRIORITY_BACKGROUND + Process.THREAD_PRIORITY_MORE_FAVORABLE); 2837 thread.start(); 2838 Handler handler = new Handler(thread.getLooper()); 2839 handler.post(new Runnable() { 2840 private int addTrack() { 2841 final ByteArrayOutputStream bos = new ByteArrayOutputStream(); 2842 try { 2843 Libcore.os.lseek(dupedFd, offset2, OsConstants.SEEK_SET); 2844 byte[] buffer = new byte[4096]; 2845 for (long total = 0; total < length2;) { 2846 int bytesToRead = (int) Math.min(buffer.length, length2 - total); 2847 int bytes = IoBridge.read(dupedFd, buffer, 0, bytesToRead); 2848 if (bytes < 0) { 2849 break; 2850 } else { 2851 bos.write(buffer, 0, bytes); 2852 total += bytes; 2853 } 2854 } 2855 Handler h = mTimeProvider.mEventHandler; 2856 int what = TimeProvider.NOTIFY; 2857 int arg1 = TimeProvider.NOTIFY_TRACK_DATA; 2858 Pair<SubtitleTrack, byte[]> trackData = Pair.create(track, bos.toByteArray()); 2859 Message m = h.obtainMessage(what, arg1, 0, trackData); 2860 h.sendMessage(m); 2861 return MEDIA_INFO_EXTERNAL_METADATA_UPDATE; 2862 } catch (Exception e) { 2863 Log.e(TAG, e.getMessage(), e); 2864 return MEDIA_INFO_TIMED_TEXT_ERROR; 2865 } finally { 2866 try { 2867 Libcore.os.close(dupedFd); 2868 } catch (ErrnoException e) { 2869 Log.e(TAG, e.getMessage(), e); 2870 } 2871 } 2872 } 2873 2874 public void run() { 2875 int res = addTrack(); 2876 if (mEventHandler != null) { 2877 Message m = mEventHandler.obtainMessage(MEDIA_INFO, res, 0, null); 2878 mEventHandler.sendMessage(m); 2879 } 2880 thread.getLooper().quitSafely(); 2881 } 2882 }); 2883 } 2884 2885 /** 2886 * Returns the index of the audio, video, or subtitle track currently selected for playback, 2887 * The return value is an index into the array returned by {@link #getTrackInfo()}, and can 2888 * be used in calls to {@link #selectTrack(int)} or {@link #deselectTrack(int)}. 2889 * 2890 * @param trackType should be one of {@link TrackInfo#MEDIA_TRACK_TYPE_VIDEO}, 2891 * {@link TrackInfo#MEDIA_TRACK_TYPE_AUDIO}, or 2892 * {@link TrackInfo#MEDIA_TRACK_TYPE_SUBTITLE} 2893 * @return index of the audio, video, or subtitle track currently selected for playback; 2894 * a negative integer is returned when there is no selected track for {@code trackType} or 2895 * when {@code trackType} is not one of audio, video, or subtitle. 2896 * @throws IllegalStateException if called after {@link #release()} 2897 * 2898 * @see #getTrackInfo() 2899 * @see #selectTrack(int) 2900 * @see #deselectTrack(int) 2901 */ 2902 public int getSelectedTrack(int trackType) throws IllegalStateException { 2903 if (mSubtitleController != null 2904 && (trackType == TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE 2905 || trackType == TrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT)) { 2906 SubtitleTrack subtitleTrack = mSubtitleController.getSelectedTrack(); 2907 if (subtitleTrack != null) { 2908 synchronized (mIndexTrackPairs) { 2909 for (int i = 0; i < mIndexTrackPairs.size(); i++) { 2910 Pair<Integer, SubtitleTrack> p = mIndexTrackPairs.get(i); 2911 if (p.second == subtitleTrack && subtitleTrack.getTrackType() == trackType) { 2912 return i; 2913 } 2914 } 2915 } 2916 } 2917 } 2918 2919 Parcel request = Parcel.obtain(); 2920 Parcel reply = Parcel.obtain(); 2921 try { 2922 request.writeInterfaceToken(IMEDIA_PLAYER); 2923 request.writeInt(INVOKE_ID_GET_SELECTED_TRACK); 2924 request.writeInt(trackType); 2925 invoke(request, reply); 2926 int inbandTrackIndex = reply.readInt(); 2927 synchronized (mIndexTrackPairs) { 2928 for (int i = 0; i < mIndexTrackPairs.size(); i++) { 2929 Pair<Integer, SubtitleTrack> p = mIndexTrackPairs.get(i); 2930 if (p.first != null && p.first == inbandTrackIndex) { 2931 return i; 2932 } 2933 } 2934 } 2935 return -1; 2936 } finally { 2937 request.recycle(); 2938 reply.recycle(); 2939 } 2940 } 2941 2942 /** 2943 * Selects a track. 2944 * <p> 2945 * If a MediaPlayer is in invalid state, it throws an IllegalStateException exception. 2946 * If a MediaPlayer is in <em>Started</em> state, the selected track is presented immediately. 2947 * If a MediaPlayer is not in Started state, it just marks the track to be played. 2948 * </p> 2949 * <p> 2950 * In any valid state, if it is called multiple times on the same type of track (ie. Video, 2951 * Audio, Timed Text), the most recent one will be chosen. 2952 * </p> 2953 * <p> 2954 * The first audio and video tracks are selected by default if available, even though 2955 * this method is not called. However, no timed text track will be selected until 2956 * this function is called. 2957 * </p> 2958 * <p> 2959 * Currently, only timed text tracks or audio tracks can be selected via this method. 2960 * In addition, the support for selecting an audio track at runtime is pretty limited 2961 * in that an audio track can only be selected in the <em>Prepared</em> state. 2962 * </p> 2963 * @param index the index of the track to be selected. The valid range of the index 2964 * is 0..total number of track - 1. The total number of tracks as well as the type of 2965 * each individual track can be found by calling {@link #getTrackInfo()} method. 2966 * @throws IllegalStateException if called in an invalid state. 2967 * 2968 * @see android.media.MediaPlayer#getTrackInfo 2969 */ 2970 public void selectTrack(int index) throws IllegalStateException { 2971 selectOrDeselectTrack(index, true /* select */); 2972 } 2973 2974 /** 2975 * Deselect a track. 2976 * <p> 2977 * Currently, the track must be a timed text track and no audio or video tracks can be 2978 * deselected. If the timed text track identified by index has not been 2979 * selected before, it throws an exception. 2980 * </p> 2981 * @param index the index of the track to be deselected. The valid range of the index 2982 * is 0..total number of tracks - 1. The total number of tracks as well as the type of 2983 * each individual track can be found by calling {@link #getTrackInfo()} method. 2984 * @throws IllegalStateException if called in an invalid state. 2985 * 2986 * @see android.media.MediaPlayer#getTrackInfo 2987 */ 2988 public void deselectTrack(int index) throws IllegalStateException { 2989 selectOrDeselectTrack(index, false /* select */); 2990 } 2991 2992 private void selectOrDeselectTrack(int index, boolean select) 2993 throws IllegalStateException { 2994 // handle subtitle track through subtitle controller 2995 populateInbandTracks(); 2996 2997 Pair<Integer,SubtitleTrack> p = null; 2998 try { 2999 p = mIndexTrackPairs.get(index); 3000 } catch (ArrayIndexOutOfBoundsException e) { 3001 // ignore bad index 3002 return; 3003 } 3004 3005 SubtitleTrack track = p.second; 3006 if (track == null) { 3007 // inband (de)select 3008 selectOrDeselectInbandTrack(p.first, select); 3009 return; 3010 } 3011 3012 if (mSubtitleController == null) { 3013 return; 3014 } 3015 3016 if (!select) { 3017 // out-of-band deselect 3018 if (mSubtitleController.getSelectedTrack() == track) { 3019 mSubtitleController.selectTrack(null); 3020 } else { 3021 Log.w(TAG, "trying to deselect track that was not selected"); 3022 } 3023 return; 3024 } 3025 3026 // out-of-band select 3027 if (track.getTrackType() == TrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT) { 3028 int ttIndex = getSelectedTrack(TrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT); 3029 synchronized (mIndexTrackPairs) { 3030 if (ttIndex >= 0 && ttIndex < mIndexTrackPairs.size()) { 3031 Pair<Integer,SubtitleTrack> p2 = mIndexTrackPairs.get(ttIndex); 3032 if (p2.first != null && p2.second == null) { 3033 // deselect inband counterpart 3034 selectOrDeselectInbandTrack(p2.first, false); 3035 } 3036 } 3037 } 3038 } 3039 mSubtitleController.selectTrack(track); 3040 } 3041 3042 private void selectOrDeselectInbandTrack(int index, boolean select) 3043 throws IllegalStateException { 3044 Parcel request = Parcel.obtain(); 3045 Parcel reply = Parcel.obtain(); 3046 try { 3047 request.writeInterfaceToken(IMEDIA_PLAYER); 3048 request.writeInt(select? INVOKE_ID_SELECT_TRACK: INVOKE_ID_DESELECT_TRACK); 3049 request.writeInt(index); 3050 invoke(request, reply); 3051 } finally { 3052 request.recycle(); 3053 reply.recycle(); 3054 } 3055 } 3056 3057 3058 /** 3059 * @param reply Parcel with audio/video duration info for battery 3060 tracking usage 3061 * @return The status code. 3062 * {@hide} 3063 */ 3064 public native static int native_pullBatteryData(Parcel reply); 3065 3066 /** 3067 * Sets the target UDP re-transmit endpoint for the low level player. 3068 * Generally, the address portion of the endpoint is an IP multicast 3069 * address, although a unicast address would be equally valid. When a valid 3070 * retransmit endpoint has been set, the media player will not decode and 3071 * render the media presentation locally. Instead, the player will attempt 3072 * to re-multiplex its media data using the Android@Home RTP profile and 3073 * re-transmit to the target endpoint. Receiver devices (which may be 3074 * either the same as the transmitting device or different devices) may 3075 * instantiate, prepare, and start a receiver player using a setDataSource 3076 * URL of the form... 3077 * 3078 * aahRX://<multicastIP>:<port> 3079 * 3080 * to receive, decode and render the re-transmitted content. 3081 * 3082 * setRetransmitEndpoint may only be called before setDataSource has been 3083 * called; while the player is in the Idle state. 3084 * 3085 * @param endpoint the address and UDP port of the re-transmission target or 3086 * null if no re-transmission is to be performed. 3087 * @throws IllegalStateException if it is called in an invalid state 3088 * @throws IllegalArgumentException if the retransmit endpoint is supplied, 3089 * but invalid. 3090 * 3091 * {@hide} pending API council 3092 */ 3093 public void setRetransmitEndpoint(InetSocketAddress endpoint) 3094 throws IllegalStateException, IllegalArgumentException 3095 { 3096 String addrString = null; 3097 int port = 0; 3098 3099 if (null != endpoint) { 3100 addrString = endpoint.getAddress().getHostAddress(); 3101 port = endpoint.getPort(); 3102 } 3103 3104 int ret = native_setRetransmitEndpoint(addrString, port); 3105 if (ret != 0) { 3106 throw new IllegalArgumentException("Illegal re-transmit endpoint; native ret " + ret); 3107 } 3108 } 3109 3110 private native final int native_setRetransmitEndpoint(String addrString, int port); 3111 3112 @Override 3113 protected void finalize() { 3114 baseRelease(); 3115 native_finalize(); 3116 } 3117 3118 /* Do not change these values without updating their counterparts 3119 * in include/media/mediaplayer.h! 3120 */ 3121 private static final int MEDIA_NOP = 0; // interface test message 3122 private static final int MEDIA_PREPARED = 1; 3123 private static final int MEDIA_PLAYBACK_COMPLETE = 2; 3124 private static final int MEDIA_BUFFERING_UPDATE = 3; 3125 private static final int MEDIA_SEEK_COMPLETE = 4; 3126 private static final int MEDIA_SET_VIDEO_SIZE = 5; 3127 private static final int MEDIA_STARTED = 6; 3128 private static final int MEDIA_PAUSED = 7; 3129 private static final int MEDIA_STOPPED = 8; 3130 private static final int MEDIA_SKIPPED = 9; 3131 private static final int MEDIA_TIMED_TEXT = 99; 3132 private static final int MEDIA_ERROR = 100; 3133 private static final int MEDIA_INFO = 200; 3134 private static final int MEDIA_SUBTITLE_DATA = 201; 3135 private static final int MEDIA_META_DATA = 202; 3136 private static final int MEDIA_DRM_INFO = 210; 3137 3138 private TimeProvider mTimeProvider; 3139 3140 /** @hide */ 3141 public MediaTimeProvider getMediaTimeProvider() { 3142 if (mTimeProvider == null) { 3143 mTimeProvider = new TimeProvider(this); 3144 } 3145 return mTimeProvider; 3146 } 3147 3148 private class EventHandler extends Handler 3149 { 3150 private MediaPlayer mMediaPlayer; 3151 3152 public EventHandler(MediaPlayer mp, Looper looper) { 3153 super(looper); 3154 mMediaPlayer = mp; 3155 } 3156 3157 @Override 3158 public void handleMessage(Message msg) { 3159 if (mMediaPlayer.mNativeContext == 0) { 3160 Log.w(TAG, "mediaplayer went away with unhandled events"); 3161 return; 3162 } 3163 switch(msg.what) { 3164 case MEDIA_PREPARED: 3165 try { 3166 scanInternalSubtitleTracks(); 3167 } catch (RuntimeException e) { 3168 // send error message instead of crashing; 3169 // send error message instead of inlining a call to onError 3170 // to avoid code duplication. 3171 Message msg2 = obtainMessage( 3172 MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_UNSUPPORTED, null); 3173 sendMessage(msg2); 3174 } 3175 3176 OnPreparedListener onPreparedListener = mOnPreparedListener; 3177 if (onPreparedListener != null) 3178 onPreparedListener.onPrepared(mMediaPlayer); 3179 return; 3180 3181 case MEDIA_DRM_INFO: 3182 Log.v(TAG, "MEDIA_DRM_INFO " + mOnDrmInfoHandlerDelegate); 3183 3184 if (msg.obj == null) { 3185 Log.w(TAG, "MEDIA_DRM_INFO msg.obj=NULL"); 3186 } else if (msg.obj instanceof Parcel) { 3187 // The parcel was parsed already in postEventFromNative 3188 DrmInfo drmInfo = null; 3189 3190 OnDrmInfoHandlerDelegate onDrmInfoHandlerDelegate; 3191 synchronized (mDrmLock) { 3192 if (mOnDrmInfoHandlerDelegate != null && mDrmInfo != null) { 3193 drmInfo = mDrmInfo.makeCopy(); 3194 } 3195 // local copy while keeping the lock 3196 onDrmInfoHandlerDelegate = mOnDrmInfoHandlerDelegate; 3197 } 3198 3199 // notifying the client outside the lock 3200 if (onDrmInfoHandlerDelegate != null) { 3201 onDrmInfoHandlerDelegate.notifyClient(drmInfo); 3202 } 3203 } else { 3204 Log.w(TAG, "MEDIA_DRM_INFO msg.obj of unexpected type " + msg.obj); 3205 } 3206 return; 3207 3208 case MEDIA_PLAYBACK_COMPLETE: 3209 { 3210 mOnCompletionInternalListener.onCompletion(mMediaPlayer); 3211 OnCompletionListener onCompletionListener = mOnCompletionListener; 3212 if (onCompletionListener != null) 3213 onCompletionListener.onCompletion(mMediaPlayer); 3214 } 3215 stayAwake(false); 3216 return; 3217 3218 case MEDIA_STOPPED: 3219 { 3220 TimeProvider timeProvider = mTimeProvider; 3221 if (timeProvider != null) { 3222 timeProvider.onStopped(); 3223 } 3224 } 3225 break; 3226 3227 case MEDIA_STARTED: 3228 case MEDIA_PAUSED: 3229 { 3230 TimeProvider timeProvider = mTimeProvider; 3231 if (timeProvider != null) { 3232 timeProvider.onPaused(msg.what == MEDIA_PAUSED); 3233 } 3234 } 3235 break; 3236 3237 case MEDIA_BUFFERING_UPDATE: 3238 OnBufferingUpdateListener onBufferingUpdateListener = mOnBufferingUpdateListener; 3239 if (onBufferingUpdateListener != null) 3240 onBufferingUpdateListener.onBufferingUpdate(mMediaPlayer, msg.arg1); 3241 return; 3242 3243 case MEDIA_SEEK_COMPLETE: 3244 OnSeekCompleteListener onSeekCompleteListener = mOnSeekCompleteListener; 3245 if (onSeekCompleteListener != null) { 3246 onSeekCompleteListener.onSeekComplete(mMediaPlayer); 3247 } 3248 // fall through 3249 3250 case MEDIA_SKIPPED: 3251 { 3252 TimeProvider timeProvider = mTimeProvider; 3253 if (timeProvider != null) { 3254 timeProvider.onSeekComplete(mMediaPlayer); 3255 } 3256 } 3257 return; 3258 3259 case MEDIA_SET_VIDEO_SIZE: 3260 OnVideoSizeChangedListener onVideoSizeChangedListener = mOnVideoSizeChangedListener; 3261 if (onVideoSizeChangedListener != null) { 3262 onVideoSizeChangedListener.onVideoSizeChanged( 3263 mMediaPlayer, msg.arg1, msg.arg2); 3264 } 3265 return; 3266 3267 case MEDIA_ERROR: 3268 Log.e(TAG, "Error (" + msg.arg1 + "," + msg.arg2 + ")"); 3269 boolean error_was_handled = false; 3270 OnErrorListener onErrorListener = mOnErrorListener; 3271 if (onErrorListener != null) { 3272 error_was_handled = onErrorListener.onError(mMediaPlayer, msg.arg1, msg.arg2); 3273 } 3274 { 3275 mOnCompletionInternalListener.onCompletion(mMediaPlayer); 3276 OnCompletionListener onCompletionListener = mOnCompletionListener; 3277 if (onCompletionListener != null && ! error_was_handled) { 3278 onCompletionListener.onCompletion(mMediaPlayer); 3279 } 3280 } 3281 stayAwake(false); 3282 return; 3283 3284 case MEDIA_INFO: 3285 switch (msg.arg1) { 3286 case MEDIA_INFO_VIDEO_TRACK_LAGGING: 3287 Log.i(TAG, "Info (" + msg.arg1 + "," + msg.arg2 + ")"); 3288 break; 3289 case MEDIA_INFO_METADATA_UPDATE: 3290 try { 3291 scanInternalSubtitleTracks(); 3292 } catch (RuntimeException e) { 3293 Message msg2 = obtainMessage( 3294 MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_UNSUPPORTED, null); 3295 sendMessage(msg2); 3296 } 3297 // fall through 3298 3299 case MEDIA_INFO_EXTERNAL_METADATA_UPDATE: 3300 msg.arg1 = MEDIA_INFO_METADATA_UPDATE; 3301 // update default track selection 3302 if (mSubtitleController != null) { 3303 mSubtitleController.selectDefaultTrack(); 3304 } 3305 break; 3306 case MEDIA_INFO_BUFFERING_START: 3307 case MEDIA_INFO_BUFFERING_END: 3308 TimeProvider timeProvider = mTimeProvider; 3309 if (timeProvider != null) { 3310 timeProvider.onBuffering(msg.arg1 == MEDIA_INFO_BUFFERING_START); 3311 } 3312 break; 3313 } 3314 3315 OnInfoListener onInfoListener = mOnInfoListener; 3316 if (onInfoListener != null) { 3317 onInfoListener.onInfo(mMediaPlayer, msg.arg1, msg.arg2); 3318 } 3319 // No real default action so far. 3320 return; 3321 case MEDIA_TIMED_TEXT: 3322 OnTimedTextListener onTimedTextListener = mOnTimedTextListener; 3323 if (onTimedTextListener == null) 3324 return; 3325 if (msg.obj == null) { 3326 onTimedTextListener.onTimedText(mMediaPlayer, null); 3327 } else { 3328 if (msg.obj instanceof Parcel) { 3329 Parcel parcel = (Parcel)msg.obj; 3330 TimedText text = new TimedText(parcel); 3331 parcel.recycle(); 3332 onTimedTextListener.onTimedText(mMediaPlayer, text); 3333 } 3334 } 3335 return; 3336 3337 case MEDIA_SUBTITLE_DATA: 3338 OnSubtitleDataListener onSubtitleDataListener = mOnSubtitleDataListener; 3339 if (onSubtitleDataListener == null) { 3340 return; 3341 } 3342 if (msg.obj instanceof Parcel) { 3343 Parcel parcel = (Parcel) msg.obj; 3344 SubtitleData data = new SubtitleData(parcel); 3345 parcel.recycle(); 3346 onSubtitleDataListener.onSubtitleData(mMediaPlayer, data); 3347 } 3348 return; 3349 3350 case MEDIA_META_DATA: 3351 OnTimedMetaDataAvailableListener onTimedMetaDataAvailableListener = 3352 mOnTimedMetaDataAvailableListener; 3353 if (onTimedMetaDataAvailableListener == null) { 3354 return; 3355 } 3356 if (msg.obj instanceof Parcel) { 3357 Parcel parcel = (Parcel) msg.obj; 3358 TimedMetaData data = TimedMetaData.createTimedMetaDataFromParcel(parcel); 3359 parcel.recycle(); 3360 onTimedMetaDataAvailableListener.onTimedMetaDataAvailable(mMediaPlayer, data); 3361 } 3362 return; 3363 3364 case MEDIA_NOP: // interface test message - ignore 3365 break; 3366 3367 default: 3368 Log.e(TAG, "Unknown message type " + msg.what); 3369 return; 3370 } 3371 } 3372 } 3373 3374 /* 3375 * Called from native code when an interesting event happens. This method 3376 * just uses the EventHandler system to post the event back to the main app thread. 3377 * We use a weak reference to the original MediaPlayer object so that the native 3378 * code is safe from the object disappearing from underneath it. (This is 3379 * the cookie passed to native_setup().) 3380 */ 3381 private static void postEventFromNative(Object mediaplayer_ref, 3382 int what, int arg1, int arg2, Object obj) 3383 { 3384 MediaPlayer mp = (MediaPlayer)((WeakReference)mediaplayer_ref).get(); 3385 if (mp == null) { 3386 return; 3387 } 3388 3389 switch (what) { 3390 case MEDIA_INFO: 3391 if (arg1 == MEDIA_INFO_STARTED_AS_NEXT) { 3392 // this acquires the wakelock if needed, and sets the client side state 3393 mp.start(); 3394 } 3395 break; 3396 3397 case MEDIA_DRM_INFO: 3398 // We need to derive mDrmInfo before prepare() returns so processing it here 3399 // before the notification is sent to EventHandler below. EventHandler runs in the 3400 // notification looper so its handleMessage might process the event after prepare() 3401 // has returned. 3402 Log.v(TAG, "postEventFromNative MEDIA_DRM_INFO"); 3403 if (obj instanceof Parcel) { 3404 Parcel parcel = (Parcel)obj; 3405 DrmInfo drmInfo = new DrmInfo(parcel); 3406 synchronized (mp.mDrmLock) { 3407 mp.mDrmInfo = drmInfo; 3408 } 3409 } else { 3410 Log.w(TAG, "MEDIA_DRM_INFO msg.obj of unexpected type " + obj); 3411 } 3412 break; 3413 3414 case MEDIA_PREPARED: 3415 // By this time, we've learned about DrmInfo's presence or absence. This is meant 3416 // mainly for prepareAsync() use case. For prepare(), this still can run to a race 3417 // condition b/c MediaPlayerNative releases the prepare() lock before calling notify 3418 // so we also set mDrmInfoResolved in prepare(). 3419 synchronized (mp.mDrmLock) { 3420 mp.mDrmInfoResolved = true; 3421 } 3422 break; 3423 3424 } 3425 3426 if (mp.mEventHandler != null) { 3427 Message m = mp.mEventHandler.obtainMessage(what, arg1, arg2, obj); 3428 mp.mEventHandler.sendMessage(m); 3429 } 3430 } 3431 3432 /** 3433 * Interface definition for a callback to be invoked when the media 3434 * source is ready for playback. 3435 */ 3436 public interface OnPreparedListener 3437 { 3438 /** 3439 * Called when the media file is ready for playback. 3440 * 3441 * @param mp the MediaPlayer that is ready for playback 3442 */ 3443 void onPrepared(MediaPlayer mp); 3444 } 3445 3446 /** 3447 * Register a callback to be invoked when the media source is ready 3448 * for playback. 3449 * 3450 * @param listener the callback that will be run 3451 */ 3452 public void setOnPreparedListener(OnPreparedListener listener) 3453 { 3454 mOnPreparedListener = listener; 3455 } 3456 3457 private OnPreparedListener mOnPreparedListener; 3458 3459 /** 3460 * Interface definition for a callback to be invoked when playback of 3461 * a media source has completed. 3462 */ 3463 public interface OnCompletionListener 3464 { 3465 /** 3466 * Called when the end of a media source is reached during playback. 3467 * 3468 * @param mp the MediaPlayer that reached the end of the file 3469 */ 3470 void onCompletion(MediaPlayer mp); 3471 } 3472 3473 /** 3474 * Register a callback to be invoked when the end of a media source 3475 * has been reached during playback. 3476 * 3477 * @param listener the callback that will be run 3478 */ 3479 public void setOnCompletionListener(OnCompletionListener listener) 3480 { 3481 mOnCompletionListener = listener; 3482 } 3483 3484 private OnCompletionListener mOnCompletionListener; 3485 3486 /** 3487 * @hide 3488 * Internal completion listener to update PlayerBase of the play state. Always "registered". 3489 */ 3490 private final OnCompletionListener mOnCompletionInternalListener = new OnCompletionListener() { 3491 @Override 3492 public void onCompletion(MediaPlayer mp) { 3493 baseStop(); 3494 } 3495 }; 3496 3497 /** 3498 * Interface definition of a callback to be invoked indicating buffering 3499 * status of a media resource being streamed over the network. 3500 */ 3501 public interface OnBufferingUpdateListener 3502 { 3503 /** 3504 * Called to update status in buffering a media stream received through 3505 * progressive HTTP download. The received buffering percentage 3506 * indicates how much of the content has been buffered or played. 3507 * For example a buffering update of 80 percent when half the content 3508 * has already been played indicates that the next 30 percent of the 3509 * content to play has been buffered. 3510 * 3511 * @param mp the MediaPlayer the update pertains to 3512 * @param percent the percentage (0-100) of the content 3513 * that has been buffered or played thus far 3514 */ 3515 void onBufferingUpdate(MediaPlayer mp, int percent); 3516 } 3517 3518 /** 3519 * Register a callback to be invoked when the status of a network 3520 * stream's buffer has changed. 3521 * 3522 * @param listener the callback that will be run. 3523 */ 3524 public void setOnBufferingUpdateListener(OnBufferingUpdateListener listener) 3525 { 3526 mOnBufferingUpdateListener = listener; 3527 } 3528 3529 private OnBufferingUpdateListener mOnBufferingUpdateListener; 3530 3531 /** 3532 * Interface definition of a callback to be invoked indicating 3533 * the completion of a seek operation. 3534 */ 3535 public interface OnSeekCompleteListener 3536 { 3537 /** 3538 * Called to indicate the completion of a seek operation. 3539 * 3540 * @param mp the MediaPlayer that issued the seek operation 3541 */ 3542 public void onSeekComplete(MediaPlayer mp); 3543 } 3544 3545 /** 3546 * Register a callback to be invoked when a seek operation has been 3547 * completed. 3548 * 3549 * @param listener the callback that will be run 3550 */ 3551 public void setOnSeekCompleteListener(OnSeekCompleteListener listener) 3552 { 3553 mOnSeekCompleteListener = listener; 3554 } 3555 3556 private OnSeekCompleteListener mOnSeekCompleteListener; 3557 3558 /** 3559 * Interface definition of a callback to be invoked when the 3560 * video size is first known or updated 3561 */ 3562 public interface OnVideoSizeChangedListener 3563 { 3564 /** 3565 * Called to indicate the video size 3566 * 3567 * The video size (width and height) could be 0 if there was no video, 3568 * no display surface was set, or the value was not determined yet. 3569 * 3570 * @param mp the MediaPlayer associated with this callback 3571 * @param width the width of the video 3572 * @param height the height of the video 3573 */ 3574 public void onVideoSizeChanged(MediaPlayer mp, int width, int height); 3575 } 3576 3577 /** 3578 * Register a callback to be invoked when the video size is 3579 * known or updated. 3580 * 3581 * @param listener the callback that will be run 3582 */ 3583 public void setOnVideoSizeChangedListener(OnVideoSizeChangedListener listener) 3584 { 3585 mOnVideoSizeChangedListener = listener; 3586 } 3587 3588 private OnVideoSizeChangedListener mOnVideoSizeChangedListener; 3589 3590 /** 3591 * Interface definition of a callback to be invoked when a 3592 * timed text is available for display. 3593 */ 3594 public interface OnTimedTextListener 3595 { 3596 /** 3597 * Called to indicate an avaliable timed text 3598 * 3599 * @param mp the MediaPlayer associated with this callback 3600 * @param text the timed text sample which contains the text 3601 * needed to be displayed and the display format. 3602 */ 3603 public void onTimedText(MediaPlayer mp, TimedText text); 3604 } 3605 3606 /** 3607 * Register a callback to be invoked when a timed text is available 3608 * for display. 3609 * 3610 * @param listener the callback that will be run 3611 */ 3612 public void setOnTimedTextListener(OnTimedTextListener listener) 3613 { 3614 mOnTimedTextListener = listener; 3615 } 3616 3617 private OnTimedTextListener mOnTimedTextListener; 3618 3619 /** 3620 * Interface definition of a callback to be invoked when a 3621 * track has data available. 3622 * 3623 * @hide 3624 */ 3625 public interface OnSubtitleDataListener 3626 { 3627 public void onSubtitleData(MediaPlayer mp, SubtitleData data); 3628 } 3629 3630 /** 3631 * Register a callback to be invoked when a track has data available. 3632 * 3633 * @param listener the callback that will be run 3634 * 3635 * @hide 3636 */ 3637 public void setOnSubtitleDataListener(OnSubtitleDataListener listener) 3638 { 3639 mOnSubtitleDataListener = listener; 3640 } 3641 3642 private OnSubtitleDataListener mOnSubtitleDataListener; 3643 3644 /** 3645 * Interface definition of a callback to be invoked when a 3646 * track has timed metadata available. 3647 * 3648 * @see MediaPlayer#setOnTimedMetaDataAvailableListener(OnTimedMetaDataAvailableListener) 3649 */ 3650 public interface OnTimedMetaDataAvailableListener 3651 { 3652 /** 3653 * Called to indicate avaliable timed metadata 3654 * <p> 3655 * This method will be called as timed metadata is extracted from the media, 3656 * in the same order as it occurs in the media. The timing of this event is 3657 * not controlled by the associated timestamp. 3658 * 3659 * @param mp the MediaPlayer associated with this callback 3660 * @param data the timed metadata sample associated with this event 3661 */ 3662 public void onTimedMetaDataAvailable(MediaPlayer mp, TimedMetaData data); 3663 } 3664 3665 /** 3666 * Register a callback to be invoked when a selected track has timed metadata available. 3667 * <p> 3668 * Currently only HTTP live streaming data URI's embedded with timed ID3 tags generates 3669 * {@link TimedMetaData}. 3670 * 3671 * @see MediaPlayer#selectTrack(int) 3672 * @see MediaPlayer.OnTimedMetaDataAvailableListener 3673 * @see TimedMetaData 3674 * 3675 * @param listener the callback that will be run 3676 */ 3677 public void setOnTimedMetaDataAvailableListener(OnTimedMetaDataAvailableListener listener) 3678 { 3679 mOnTimedMetaDataAvailableListener = listener; 3680 } 3681 3682 private OnTimedMetaDataAvailableListener mOnTimedMetaDataAvailableListener; 3683 3684 /* Do not change these values without updating their counterparts 3685 * in include/media/mediaplayer.h! 3686 */ 3687 /** Unspecified media player error. 3688 * @see android.media.MediaPlayer.OnErrorListener 3689 */ 3690 public static final int MEDIA_ERROR_UNKNOWN = 1; 3691 3692 /** Media server died. In this case, the application must release the 3693 * MediaPlayer object and instantiate a new one. 3694 * @see android.media.MediaPlayer.OnErrorListener 3695 */ 3696 public static final int MEDIA_ERROR_SERVER_DIED = 100; 3697 3698 /** The video is streamed and its container is not valid for progressive 3699 * playback i.e the video's index (e.g moov atom) is not at the start of the 3700 * file. 3701 * @see android.media.MediaPlayer.OnErrorListener 3702 */ 3703 public static final int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200; 3704 3705 /** File or network related operation errors. */ 3706 public static final int MEDIA_ERROR_IO = -1004; 3707 /** Bitstream is not conforming to the related coding standard or file spec. */ 3708 public static final int MEDIA_ERROR_MALFORMED = -1007; 3709 /** Bitstream is conforming to the related coding standard or file spec, but 3710 * the media framework does not support the feature. */ 3711 public static final int MEDIA_ERROR_UNSUPPORTED = -1010; 3712 /** Some operation takes too long to complete, usually more than 3-5 seconds. */ 3713 public static final int MEDIA_ERROR_TIMED_OUT = -110; 3714 3715 /** Unspecified low-level system error. This value originated from UNKNOWN_ERROR in 3716 * system/core/include/utils/Errors.h 3717 * @see android.media.MediaPlayer.OnErrorListener 3718 * @hide 3719 */ 3720 public static final int MEDIA_ERROR_SYSTEM = -2147483648; 3721 3722 /** 3723 * Interface definition of a callback to be invoked when there 3724 * has been an error during an asynchronous operation (other errors 3725 * will throw exceptions at method call time). 3726 */ 3727 public interface OnErrorListener 3728 { 3729 /** 3730 * Called to indicate an error. 3731 * 3732 * @param mp the MediaPlayer the error pertains to 3733 * @param what the type of error that has occurred: 3734 * <ul> 3735 * <li>{@link #MEDIA_ERROR_UNKNOWN} 3736 * <li>{@link #MEDIA_ERROR_SERVER_DIED} 3737 * </ul> 3738 * @param extra an extra code, specific to the error. Typically 3739 * implementation dependent. 3740 * <ul> 3741 * <li>{@link #MEDIA_ERROR_IO} 3742 * <li>{@link #MEDIA_ERROR_MALFORMED} 3743 * <li>{@link #MEDIA_ERROR_UNSUPPORTED} 3744 * <li>{@link #MEDIA_ERROR_TIMED_OUT} 3745 * <li><code>MEDIA_ERROR_SYSTEM (-2147483648)</code> - low-level system error. 3746 * </ul> 3747 * @return True if the method handled the error, false if it didn't. 3748 * Returning false, or not having an OnErrorListener at all, will 3749 * cause the OnCompletionListener to be called. 3750 */ 3751 boolean onError(MediaPlayer mp, int what, int extra); 3752 } 3753 3754 /** 3755 * Register a callback to be invoked when an error has happened 3756 * during an asynchronous operation. 3757 * 3758 * @param listener the callback that will be run 3759 */ 3760 public void setOnErrorListener(OnErrorListener listener) 3761 { 3762 mOnErrorListener = listener; 3763 } 3764 3765 private OnErrorListener mOnErrorListener; 3766 3767 3768 /* Do not change these values without updating their counterparts 3769 * in include/media/mediaplayer.h! 3770 */ 3771 /** Unspecified media player info. 3772 * @see android.media.MediaPlayer.OnInfoListener 3773 */ 3774 public static final int MEDIA_INFO_UNKNOWN = 1; 3775 3776 /** The player was started because it was used as the next player for another 3777 * player, which just completed playback. 3778 * @see android.media.MediaPlayer.OnInfoListener 3779 * @hide 3780 */ 3781 public static final int MEDIA_INFO_STARTED_AS_NEXT = 2; 3782 3783 /** The player just pushed the very first video frame for rendering. 3784 * @see android.media.MediaPlayer.OnInfoListener 3785 */ 3786 public static final int MEDIA_INFO_VIDEO_RENDERING_START = 3; 3787 3788 /** The video is too complex for the decoder: it can't decode frames fast 3789 * enough. Possibly only the audio plays fine at this stage. 3790 * @see android.media.MediaPlayer.OnInfoListener 3791 */ 3792 public static final int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700; 3793 3794 /** MediaPlayer is temporarily pausing playback internally in order to 3795 * buffer more data. 3796 * @see android.media.MediaPlayer.OnInfoListener 3797 */ 3798 public static final int MEDIA_INFO_BUFFERING_START = 701; 3799 3800 /** MediaPlayer is resuming playback after filling buffers. 3801 * @see android.media.MediaPlayer.OnInfoListener 3802 */ 3803 public static final int MEDIA_INFO_BUFFERING_END = 702; 3804 3805 /** Estimated network bandwidth information (kbps) is available; currently this event fires 3806 * simultaneously as {@link #MEDIA_INFO_BUFFERING_START} and {@link #MEDIA_INFO_BUFFERING_END} 3807 * when playing network files. 3808 * @see android.media.MediaPlayer.OnInfoListener 3809 * @hide 3810 */ 3811 public static final int MEDIA_INFO_NETWORK_BANDWIDTH = 703; 3812 3813 /** Bad interleaving means that a media has been improperly interleaved or 3814 * not interleaved at all, e.g has all the video samples first then all the 3815 * audio ones. Video is playing but a lot of disk seeks may be happening. 3816 * @see android.media.MediaPlayer.OnInfoListener 3817 */ 3818 public static final int MEDIA_INFO_BAD_INTERLEAVING = 800; 3819 3820 /** The media cannot be seeked (e.g live stream) 3821 * @see android.media.MediaPlayer.OnInfoListener 3822 */ 3823 public static final int MEDIA_INFO_NOT_SEEKABLE = 801; 3824 3825 /** A new set of metadata is available. 3826 * @see android.media.MediaPlayer.OnInfoListener 3827 */ 3828 public static final int MEDIA_INFO_METADATA_UPDATE = 802; 3829 3830 /** A new set of external-only metadata is available. Used by 3831 * JAVA framework to avoid triggering track scanning. 3832 * @hide 3833 */ 3834 public static final int MEDIA_INFO_EXTERNAL_METADATA_UPDATE = 803; 3835 3836 /** Informs that audio is not playing. Note that playback of the video 3837 * is not interrupted. 3838 * @see android.media.MediaPlayer.OnInfoListener 3839 */ 3840 public static final int MEDIA_INFO_AUDIO_NOT_PLAYING = 804; 3841 3842 /** Informs that video is not playing. Note that playback of the audio 3843 * is not interrupted. 3844 * @see android.media.MediaPlayer.OnInfoListener 3845 */ 3846 public static final int MEDIA_INFO_VIDEO_NOT_PLAYING = 805; 3847 3848 /** Failed to handle timed text track properly. 3849 * @see android.media.MediaPlayer.OnInfoListener 3850 * 3851 * {@hide} 3852 */ 3853 public static final int MEDIA_INFO_TIMED_TEXT_ERROR = 900; 3854 3855 /** Subtitle track was not supported by the media framework. 3856 * @see android.media.MediaPlayer.OnInfoListener 3857 */ 3858 public static final int MEDIA_INFO_UNSUPPORTED_SUBTITLE = 901; 3859 3860 /** Reading the subtitle track takes too long. 3861 * @see android.media.MediaPlayer.OnInfoListener 3862 */ 3863 public static final int MEDIA_INFO_SUBTITLE_TIMED_OUT = 902; 3864 3865 /** 3866 * Interface definition of a callback to be invoked to communicate some 3867 * info and/or warning about the media or its playback. 3868 */ 3869 public interface OnInfoListener 3870 { 3871 /** 3872 * Called to indicate an info or a warning. 3873 * 3874 * @param mp the MediaPlayer the info pertains to. 3875 * @param what the type of info or warning. 3876 * <ul> 3877 * <li>{@link #MEDIA_INFO_UNKNOWN} 3878 * <li>{@link #MEDIA_INFO_VIDEO_TRACK_LAGGING} 3879 * <li>{@link #MEDIA_INFO_VIDEO_RENDERING_START} 3880 * <li>{@link #MEDIA_INFO_BUFFERING_START} 3881 * <li>{@link #MEDIA_INFO_BUFFERING_END} 3882 * <li><code>MEDIA_INFO_NETWORK_BANDWIDTH (703)</code> - 3883 * bandwidth information is available (as <code>extra</code> kbps) 3884 * <li>{@link #MEDIA_INFO_BAD_INTERLEAVING} 3885 * <li>{@link #MEDIA_INFO_NOT_SEEKABLE} 3886 * <li>{@link #MEDIA_INFO_METADATA_UPDATE} 3887 * <li>{@link #MEDIA_INFO_UNSUPPORTED_SUBTITLE} 3888 * <li>{@link #MEDIA_INFO_SUBTITLE_TIMED_OUT} 3889 * </ul> 3890 * @param extra an extra code, specific to the info. Typically 3891 * implementation dependent. 3892 * @return True if the method handled the info, false if it didn't. 3893 * Returning false, or not having an OnInfoListener at all, will 3894 * cause the info to be discarded. 3895 */ 3896 boolean onInfo(MediaPlayer mp, int what, int extra); 3897 } 3898 3899 /** 3900 * Register a callback to be invoked when an info/warning is available. 3901 * 3902 * @param listener the callback that will be run 3903 */ 3904 public void setOnInfoListener(OnInfoListener listener) 3905 { 3906 mOnInfoListener = listener; 3907 } 3908 3909 private OnInfoListener mOnInfoListener; 3910 3911 // Modular DRM begin 3912 3913 /** 3914 * Interface definition of a callback to be invoked when the app 3915 * can do DRM configuration (get/set properties) before the session 3916 * is opened. This facilitates configuration of the properties, like 3917 * 'securityLevel', which has to be set after DRM scheme creation but 3918 * before the DRM session is opened. 3919 * 3920 * The only allowed DRM calls in this listener are {@code getDrmPropertyString} 3921 * and {@code setDrmPropertyString}. 3922 * 3923 */ 3924 public interface OnDrmConfigHelper 3925 { 3926 /** 3927 * Called to give the app the opportunity to configure DRM before the session is created 3928 * 3929 * @param mp the {@code MediaPlayer} associated with this callback 3930 */ 3931 public void onDrmConfig(MediaPlayer mp); 3932 } 3933 3934 /** 3935 * Register a callback to be invoked for configuration of the DRM object before 3936 * the session is created. 3937 * The callback will be invoked synchronously during the execution 3938 * of {@link #prepareDrm(UUID uuid)}. 3939 * 3940 * @param listener the callback that will be run 3941 */ 3942 public void setOnDrmConfigHelper(OnDrmConfigHelper listener) 3943 { 3944 synchronized (mDrmLock) { 3945 mOnDrmConfigHelper = listener; 3946 } // synchronized 3947 } 3948 3949 private OnDrmConfigHelper mOnDrmConfigHelper; 3950 3951 /** 3952 * Interface definition of a callback to be invoked when the 3953 * DRM info becomes available 3954 */ 3955 public interface OnDrmInfoListener 3956 { 3957 /** 3958 * Called to indicate DRM info is available 3959 * 3960 * @param mp the {@code MediaPlayer} associated with this callback 3961 * @param drmInfo DRM info of the source including PSSH, and subset 3962 * of crypto schemes supported by this device 3963 */ 3964 public void onDrmInfo(MediaPlayer mp, DrmInfo drmInfo); 3965 } 3966 3967 /** 3968 * Register a callback to be invoked when the DRM info is 3969 * known. 3970 * 3971 * @param listener the callback that will be run 3972 */ 3973 public void setOnDrmInfoListener(OnDrmInfoListener listener) 3974 { 3975 setOnDrmInfoListener(listener, null); 3976 } 3977 3978 /** 3979 * Register a callback to be invoked when the DRM info is 3980 * known. 3981 * 3982 * @param listener the callback that will be run 3983 */ 3984 public void setOnDrmInfoListener(OnDrmInfoListener listener, Handler handler) 3985 { 3986 synchronized (mDrmLock) { 3987 if (listener != null) { 3988 mOnDrmInfoHandlerDelegate = new OnDrmInfoHandlerDelegate(this, listener, handler); 3989 } else { 3990 mOnDrmInfoHandlerDelegate = null; 3991 } 3992 } // synchronized 3993 } 3994 3995 private OnDrmInfoHandlerDelegate mOnDrmInfoHandlerDelegate; 3996 3997 3998 /** 3999 * The status codes for {@link OnDrmPreparedListener#onDrmPrepared} listener. 4000 * <p> 4001 * 4002 * DRM preparation has succeeded. 4003 */ 4004 public static final int PREPARE_DRM_STATUS_SUCCESS = 0; 4005 4006 /** 4007 * The device required DRM provisioning but couldn't reach the provisioning server. 4008 */ 4009 public static final int PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR = 1; 4010 4011 /** 4012 * The device required DRM provisioning but the provisioning server denied the request. 4013 */ 4014 public static final int PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR = 2; 4015 4016 /** 4017 * The DRM preparation has failed . 4018 */ 4019 public static final int PREPARE_DRM_STATUS_PREPARATION_ERROR = 3; 4020 4021 4022 /** @hide */ 4023 @IntDef({ 4024 PREPARE_DRM_STATUS_SUCCESS, 4025 PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR, 4026 PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR, 4027 PREPARE_DRM_STATUS_PREPARATION_ERROR, 4028 }) 4029 @Retention(RetentionPolicy.SOURCE) 4030 public @interface PrepareDrmStatusCode {} 4031 4032 /** 4033 * Interface definition of a callback to notify the app when the 4034 * DRM is ready for key request/response 4035 */ 4036 public interface OnDrmPreparedListener 4037 { 4038 /** 4039 * Called to notify the app that prepareDrm is finished and ready for key request/response 4040 * 4041 * @param mp the {@code MediaPlayer} associated with this callback 4042 * @param status the result of DRM preparation which can be 4043 * {@link #PREPARE_DRM_STATUS_SUCCESS}, 4044 * {@link #PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR}, 4045 * {@link #PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR}, or 4046 * {@link #PREPARE_DRM_STATUS_PREPARATION_ERROR}. 4047 */ 4048 public void onDrmPrepared(MediaPlayer mp, @PrepareDrmStatusCode int status); 4049 } 4050 4051 /** 4052 * Register a callback to be invoked when the DRM object is prepared. 4053 * 4054 * @param listener the callback that will be run 4055 */ 4056 public void setOnDrmPreparedListener(OnDrmPreparedListener listener) 4057 { 4058 setOnDrmPreparedListener(listener, null); 4059 } 4060 4061 /** 4062 * Register a callback to be invoked when the DRM object is prepared. 4063 * 4064 * @param listener the callback that will be run 4065 * @param handler the Handler that will receive the callback 4066 */ 4067 public void setOnDrmPreparedListener(OnDrmPreparedListener listener, Handler handler) 4068 { 4069 synchronized (mDrmLock) { 4070 if (listener != null) { 4071 mOnDrmPreparedHandlerDelegate = new OnDrmPreparedHandlerDelegate(this, 4072 listener, handler); 4073 } else { 4074 mOnDrmPreparedHandlerDelegate = null; 4075 } 4076 } // synchronized 4077 } 4078 4079 private OnDrmPreparedHandlerDelegate mOnDrmPreparedHandlerDelegate; 4080 4081 4082 private class OnDrmInfoHandlerDelegate { 4083 private MediaPlayer mMediaPlayer; 4084 private OnDrmInfoListener mOnDrmInfoListener; 4085 private Handler mHandler; 4086 4087 OnDrmInfoHandlerDelegate(MediaPlayer mp, OnDrmInfoListener listener, Handler handler) { 4088 mMediaPlayer = mp; 4089 mOnDrmInfoListener = listener; 4090 4091 // find the looper for our new event handler 4092 if (handler != null) { 4093 mHandler = handler; 4094 } else { 4095 // handler == null 4096 // Will let OnDrmInfoListener be called in mEventHandler similar to other 4097 // legacy notifications. This is because MEDIA_DRM_INFO's notification has to be 4098 // sent before MEDIA_PREPARED's (i.e., in the same order they are issued by 4099 // mediaserver). As a result, the callback has to be called directly by 4100 // EventHandler.handleMessage similar to onPrepared. 4101 } 4102 } 4103 4104 void notifyClient(DrmInfo drmInfo) { 4105 if (mHandler != null) { 4106 mHandler.post(new Runnable() { 4107 @Override 4108 public void run() { 4109 mOnDrmInfoListener.onDrmInfo(mMediaPlayer, drmInfo); 4110 } 4111 }); 4112 } 4113 else { // no handler: direct call by mEventHandler 4114 mOnDrmInfoListener.onDrmInfo(mMediaPlayer, drmInfo); 4115 } 4116 } 4117 } 4118 4119 private class OnDrmPreparedHandlerDelegate { 4120 private MediaPlayer mMediaPlayer; 4121 private OnDrmPreparedListener mOnDrmPreparedListener; 4122 private Handler mHandler; 4123 4124 OnDrmPreparedHandlerDelegate(MediaPlayer mp, OnDrmPreparedListener listener, 4125 Handler handler) { 4126 mMediaPlayer = mp; 4127 mOnDrmPreparedListener = listener; 4128 4129 // find the looper for our new event handler 4130 if (handler != null) { 4131 mHandler = handler; 4132 } else if (mEventHandler != null) { 4133 // Otherwise, use mEventHandler 4134 mHandler = mEventHandler; 4135 } else { 4136 Log.e(TAG, "OnDrmPreparedHandlerDelegate: Unexpected null mEventHandler"); 4137 } 4138 } 4139 4140 void notifyClient(int status) { 4141 if (mHandler != null) { 4142 mHandler.post(new Runnable() { 4143 @Override 4144 public void run() { 4145 mOnDrmPreparedListener.onDrmPrepared(mMediaPlayer, status); 4146 } 4147 }); 4148 } else { 4149 Log.e(TAG, "OnDrmPreparedHandlerDelegate:notifyClient: Unexpected null mHandler"); 4150 } 4151 } 4152 } 4153 4154 /** 4155 * Retrieves the DRM Info associated with the current source 4156 * 4157 * @throws IllegalStateException if called before prepare() 4158 */ 4159 public DrmInfo getDrmInfo() 4160 { 4161 DrmInfo drmInfo = null; 4162 4163 // there is not much point if the app calls getDrmInfo within an OnDrmInfoListenet; 4164 // regardless below returns drmInfo anyway instead of raising an exception 4165 synchronized (mDrmLock) { 4166 if (!mDrmInfoResolved && mDrmInfo == null) { 4167 final String msg = "The Player has not been prepared yet"; 4168 Log.v(TAG, msg); 4169 throw new IllegalStateException(msg); 4170 } 4171 4172 if (mDrmInfo != null) { 4173 drmInfo = mDrmInfo.makeCopy(); 4174 } 4175 } // synchronized 4176 4177 return drmInfo; 4178 } 4179 4180 4181 /** 4182 * Prepares the DRM for the current source 4183 * <p> 4184 * If {@code OnDrmConfigHelper} is registered, it will be called during 4185 * preparation to allow configuration of the DRM properties before opening the 4186 * DRM session. Note that the callback is called synchronously in the thread that called 4187 * {@code prepareDrm}. It should be used only for a series of {@code getDrmPropertyString} 4188 * and {@code setDrmPropertyString} calls and refrain from any lengthy operation. 4189 * <p> 4190 * If the device has not been provisioned before, this call also provisions the device 4191 * which involves accessing the provisioning server and can take a variable time to 4192 * complete depending on the network connectivity. 4193 * If {@code OnDrmPreparedListener} is registered, prepareDrm() runs in non-blocking 4194 * mode by launching the provisioning in the background and returning. The listener 4195 * will be called when provisioning and preparation has finished. If a 4196 * {@code OnDrmPreparedListener} is not registered, prepareDrm() waits till provisioning 4197 * and preparation has finished, i.e., runs in blocking mode. 4198 * <p> 4199 * If {@code OnDrmPreparedListener} is registered, it is called to indicate the DRM 4200 * session being ready. The application should not make any assumption about its call 4201 * sequence (e.g., before or after prepareDrm returns), or the thread context that will 4202 * execute the listener (unless the listener is registered with a handler thread). 4203 * <p> 4204 * 4205 * @param uuid The UUID of the crypto scheme. If not known beforehand, it can be retrieved 4206 * from the source through {@code getDrmInfo} or registering a {@code onDrmInfoListener}. 4207 * 4208 * @throws IllegalStateException if called before prepare(), or the DRM was 4209 * prepared already 4210 * @throws UnsupportedSchemeException if the crypto scheme is not supported 4211 * @throws ResourceBusyException if required DRM resources are in use 4212 * @throws ProvisioningNetworkErrorException if provisioning is required but failed due to a 4213 * network error 4214 * @throws ProvisioningServerErrorException if provisioning is required but failed due to 4215 * the request denied by the provisioning server 4216 */ 4217 public void prepareDrm(@NonNull UUID uuid) 4218 throws UnsupportedSchemeException, ResourceBusyException, 4219 ProvisioningNetworkErrorException, ProvisioningServerErrorException 4220 { 4221 Log.v(TAG, "prepareDrm: uuid: " + uuid + " mOnDrmConfigHelper: " + mOnDrmConfigHelper); 4222 4223 boolean allDoneWithoutProvisioning = false; 4224 // get a snapshot as we'll use them outside the lock 4225 OnDrmPreparedHandlerDelegate onDrmPreparedHandlerDelegate = null; 4226 4227 synchronized (mDrmLock) { 4228 4229 // only allowing if tied to a protected source; might relax for releasing offline keys 4230 if (mDrmInfo == null) { 4231 final String msg = "prepareDrm(): Wrong usage: The player must be prepared and " + 4232 "DRM info be retrieved before this call."; 4233 Log.e(TAG, msg); 4234 throw new IllegalStateException(msg); 4235 } 4236 4237 if (mActiveDrmScheme) { 4238 final String msg = "prepareDrm(): Wrong usage: There is already " + 4239 "an active DRM scheme with " + mDrmUUID; 4240 Log.e(TAG, msg); 4241 throw new IllegalStateException(msg); 4242 } 4243 4244 if (mPrepareDrmInProgress) { 4245 final String msg = "prepareDrm(): Wrong usage: There is already " + 4246 "a pending prepareDrm call."; 4247 Log.e(TAG, msg); 4248 throw new IllegalStateException(msg); 4249 } 4250 4251 if (mDrmProvisioningInProgress) { 4252 final String msg = "prepareDrm(): Unexpectd: Provisioning is already in progress."; 4253 Log.e(TAG, msg); 4254 throw new IllegalStateException(msg); 4255 } 4256 4257 // shouldn't need this; just for safeguard 4258 cleanDrmObj(); 4259 4260 mPrepareDrmInProgress = true; 4261 // local copy while the lock is held 4262 onDrmPreparedHandlerDelegate = mOnDrmPreparedHandlerDelegate; 4263 4264 try { 4265 // only creating the DRM object to allow pre-openSession configuration 4266 prepareDrm_createDrmStep(uuid); 4267 } catch (Exception e) { 4268 Log.w(TAG, "prepareDrm(): Exception ", e); 4269 mPrepareDrmInProgress = false; 4270 throw e; 4271 } 4272 4273 mDrmConfigAllowed = true; 4274 } // synchronized 4275 4276 4277 // call the callback outside the lock 4278 if (mOnDrmConfigHelper != null) { 4279 mOnDrmConfigHelper.onDrmConfig(this); 4280 } 4281 4282 synchronized (mDrmLock) { 4283 mDrmConfigAllowed = false; 4284 boolean earlyExit = false; 4285 4286 try { 4287 prepareDrm_openSessionStep(uuid); 4288 4289 mDrmUUID = uuid; 4290 mActiveDrmScheme = true; 4291 4292 allDoneWithoutProvisioning = true; 4293 } catch (IllegalStateException e) { 4294 final String msg = "prepareDrm(): Wrong usage: The player must be " + 4295 "in the prepared state to call prepareDrm()."; 4296 Log.e(TAG, msg); 4297 earlyExit = true; 4298 throw new IllegalStateException(msg); 4299 } catch (NotProvisionedException e) { 4300 Log.w(TAG, "prepareDrm: NotProvisionedException"); 4301 4302 // handle provisioning internally; it'll reset mPrepareDrmInProgress 4303 int result = HandleProvisioninig(uuid); 4304 4305 // if blocking mode, we're already done; 4306 // if non-blocking mode, we attempted to launch background provisioning 4307 if (result != PREPARE_DRM_STATUS_SUCCESS) { 4308 earlyExit = true; 4309 String msg; 4310 4311 switch (result) { 4312 case PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR: 4313 msg = "prepareDrm: Provisioning was required but failed " + 4314 "due to a network error."; 4315 Log.e(TAG, msg); 4316 throw new ProvisioningNetworkErrorException(msg); 4317 4318 case PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR: 4319 msg = "prepareDrm: Provisioning was required but the request " + 4320 "was denied by the server."; 4321 Log.e(TAG, msg); 4322 throw new ProvisioningServerErrorException(msg); 4323 4324 case PREPARE_DRM_STATUS_PREPARATION_ERROR: 4325 default: // default for safeguard 4326 msg = "prepareDrm: Post-provisioning preparation failed."; 4327 Log.e(TAG, msg); 4328 throw new IllegalStateException(msg); 4329 } 4330 } 4331 // nothing else to do; 4332 // if blocking or non-blocking, HandleProvisioninig does the re-attempt & cleanup 4333 } catch (Exception e) { 4334 Log.e(TAG, "prepareDrm: Exception " + e); 4335 earlyExit = true; 4336 throw e; 4337 } finally { 4338 if (!mDrmProvisioningInProgress) {// if early exit other than provisioning exception 4339 mPrepareDrmInProgress = false; 4340 } 4341 if (earlyExit) { // cleaning up object if didn't succeed 4342 cleanDrmObj(); 4343 } 4344 } // finally 4345 } // synchronized 4346 4347 4348 // if finished successfully without provisioning, call the callback outside the lock 4349 if (allDoneWithoutProvisioning) { 4350 if (onDrmPreparedHandlerDelegate != null) 4351 onDrmPreparedHandlerDelegate.notifyClient(PREPARE_DRM_STATUS_SUCCESS); 4352 } 4353 4354 } 4355 4356 4357 private native void _releaseDrm(); 4358 4359 /** 4360 * Releases the DRM session 4361 * <p> 4362 * The player has to have an active DRM session and be in stopped, or prepared 4363 * state before this call is made. 4364 * A {@code reset()} call will release the DRM session implicitly. 4365 * 4366 * @throws NoDrmSchemeException if there is no active DRM session to release 4367 */ 4368 public void releaseDrm() 4369 throws NoDrmSchemeException 4370 { 4371 Log.v(TAG, "releaseDrm:"); 4372 4373 synchronized (mDrmLock) { 4374 if (!mActiveDrmScheme) { 4375 Log.e(TAG, "releaseDrm(): No active DRM scheme to release."); 4376 throw new NoDrmSchemeException("releaseDrm: No active DRM scheme to release."); 4377 } 4378 4379 try { 4380 // we don't have the player's state in this layer. The below call raises 4381 // exception if we're in a non-stopped/prepared state. 4382 4383 // for cleaning native/mediaserver crypto object 4384 _releaseDrm(); 4385 4386 // for cleaning client-side MediaDrm object; only called if above has succeeded 4387 cleanDrmObj(); 4388 4389 mActiveDrmScheme = false; 4390 } catch (IllegalStateException e) { 4391 Log.w(TAG, "releaseDrm: Exception ", e); 4392 throw new IllegalStateException("releaseDrm: The player is not in a valid state."); 4393 } catch (Exception e) { 4394 Log.e(TAG, "releaseDrm: Exception ", e); 4395 } 4396 } // synchronized 4397 } 4398 4399 4400 /** 4401 * A key request/response exchange occurs between the app and a license server 4402 * to obtain or release keys used to decrypt encrypted content. 4403 * <p> 4404 * getKeyRequest() is used to obtain an opaque key request byte array that is 4405 * delivered to the license server. The opaque key request byte array is returned 4406 * in KeyRequest.data. The recommended URL to deliver the key request to is 4407 * returned in KeyRequest.defaultUrl. 4408 * <p> 4409 * After the app has received the key request response from the server, 4410 * it should deliver to the response to the DRM engine plugin using the method 4411 * {@link #provideKeyResponse}. 4412 * 4413 * @param keySetId is the key-set identifier of the offline keys being released when keyType is 4414 * {@link MediaDrm#KEY_TYPE_RELEASE}. It should be set to null for other key requests, when 4415 * keyType is {@link MediaDrm#KEY_TYPE_STREAMING} or {@link MediaDrm#KEY_TYPE_OFFLINE}. 4416 * 4417 * @param initData is the container-specific initialization data when the keyType is 4418 * {@link MediaDrm#KEY_TYPE_STREAMING} or {@link MediaDrm#KEY_TYPE_OFFLINE}. Its meaning is 4419 * interpreted based on the mime type provided in the mimeType parameter. It could 4420 * contain, for example, the content ID, key ID or other data obtained from the content 4421 * metadata that is required in generating the key request. 4422 * When the keyType is {@link MediaDrm#KEY_TYPE_RELEASE}, it should be set to null. 4423 * 4424 * @param mimeType identifies the mime type of the content 4425 * 4426 * @param keyType specifies the type of the request. The request may be to acquire 4427 * keys for streaming, {@link MediaDrm#KEY_TYPE_STREAMING}, or for offline content 4428 * {@link MediaDrm#KEY_TYPE_OFFLINE}, or to release previously acquired 4429 * keys ({@link MediaDrm#KEY_TYPE_RELEASE}), which are identified by a keySetId. 4430 * 4431 * @param optionalParameters are included in the key request message to 4432 * allow a client application to provide additional message parameters to the server. 4433 * This may be {@code null} if no additional parameters are to be sent. 4434 * 4435 * @throws NoDrmSchemeException if there is no active DRM session 4436 */ 4437 @NonNull 4438 public MediaDrm.KeyRequest getKeyRequest(@Nullable byte[] keySetId, @Nullable byte[] initData, 4439 @Nullable String mimeType, @MediaDrm.KeyType int keyType, 4440 @Nullable Map<String, String> optionalParameters) 4441 throws NoDrmSchemeException 4442 { 4443 Log.v(TAG, "getKeyRequest: " + 4444 " keySetId: " + keySetId + " initData:" + initData + " mimeType: " + mimeType + 4445 " keyType: " + keyType + " optionalParameters: " + optionalParameters); 4446 4447 synchronized (mDrmLock) { 4448 if (!mActiveDrmScheme) { 4449 Log.e(TAG, "getKeyRequest NoDrmSchemeException"); 4450 throw new NoDrmSchemeException("getKeyRequest: Has to set a DRM scheme first."); 4451 } 4452 4453 try { 4454 byte[] scope = (keyType != MediaDrm.KEY_TYPE_RELEASE) ? 4455 mDrmSessionId : // sessionId for KEY_TYPE_STREAMING/OFFLINE 4456 keySetId; // keySetId for KEY_TYPE_RELEASE 4457 4458 HashMap<String, String> hmapOptionalParameters = 4459 (optionalParameters != null) ? 4460 new HashMap<String, String>(optionalParameters) : 4461 null; 4462 4463 MediaDrm.KeyRequest request = mDrmObj.getKeyRequest(scope, initData, mimeType, 4464 keyType, hmapOptionalParameters); 4465 Log.v(TAG, "getKeyRequest: --> request: " + request); 4466 4467 return request; 4468 4469 } catch (NotProvisionedException e) { 4470 Log.w(TAG, "getKeyRequest NotProvisionedException: " + 4471 "Unexpected. Shouldn't have reached here."); 4472 throw new IllegalStateException("getKeyRequest: Unexpected provisioning error."); 4473 } catch (Exception e) { 4474 Log.w(TAG, "getKeyRequest Exception " + e); 4475 throw e; 4476 } 4477 4478 } // synchronized 4479 } 4480 4481 4482 /** 4483 * A key response is received from the license server by the app, then it is 4484 * provided to the DRM engine plugin using provideKeyResponse. When the 4485 * response is for an offline key request, a key-set identifier is returned that 4486 * can be used to later restore the keys to a new session with the method 4487 * {@ link # restoreKeys}. 4488 * When the response is for a streaming or release request, null is returned. 4489 * 4490 * @param keySetId When the response is for a release request, keySetId identifies 4491 * the saved key associated with the release request (i.e., the same keySetId 4492 * passed to the earlier {@ link # getKeyRequest} call. It MUST be null when the 4493 * response is for either streaming or offline key requests. 4494 * 4495 * @param response the byte array response from the server 4496 * 4497 * @throws NoDrmSchemeException if there is no active DRM session 4498 * @throws DeniedByServerException if the response indicates that the 4499 * server rejected the request 4500 */ 4501 public byte[] provideKeyResponse(@Nullable byte[] keySetId, @NonNull byte[] response) 4502 throws NoDrmSchemeException, DeniedByServerException 4503 { 4504 Log.v(TAG, "provideKeyResponse: keySetId: " + keySetId + " response: " + response); 4505 4506 synchronized (mDrmLock) { 4507 4508 if (!mActiveDrmScheme) { 4509 Log.e(TAG, "getKeyRequest NoDrmSchemeException"); 4510 throw new NoDrmSchemeException("getKeyRequest: Has to set a DRM scheme first."); 4511 } 4512 4513 try { 4514 byte[] scope = (keySetId == null) ? 4515 mDrmSessionId : // sessionId for KEY_TYPE_STREAMING/OFFLINE 4516 keySetId; // keySetId for KEY_TYPE_RELEASE 4517 4518 byte[] keySetResult = mDrmObj.provideKeyResponse(scope, response); 4519 4520 Log.v(TAG, "provideKeyResponse: keySetId: " + keySetId + " response: " + response + 4521 " --> " + keySetResult); 4522 4523 4524 return keySetResult; 4525 4526 } catch (NotProvisionedException e) { 4527 Log.w(TAG, "provideKeyResponse NotProvisionedException: " + 4528 "Unexpected. Shouldn't have reached here."); 4529 throw new IllegalStateException("provideKeyResponse: " + 4530 "Unexpected provisioning error."); 4531 } catch (Exception e) { 4532 Log.w(TAG, "provideKeyResponse Exception " + e); 4533 throw e; 4534 } 4535 } // synchronized 4536 } 4537 4538 4539 /** 4540 * Restore persisted offline keys into a new session. keySetId identifies the 4541 * keys to load, obtained from a prior call to {@link #provideKeyResponse}. 4542 * 4543 * @param keySetId identifies the saved key set to restore 4544 */ 4545 public void restoreKeys(@NonNull byte[] keySetId) 4546 throws NoDrmSchemeException 4547 { 4548 Log.v(TAG, "restoreKeys: keySetId: " + keySetId); 4549 4550 synchronized (mDrmLock) { 4551 4552 if (!mActiveDrmScheme) { 4553 Log.w(TAG, "restoreKeys NoDrmSchemeException"); 4554 throw new NoDrmSchemeException("restoreKeys: Has to set a DRM scheme first."); 4555 } 4556 4557 try { 4558 mDrmObj.restoreKeys(mDrmSessionId, keySetId); 4559 } catch (Exception e) { 4560 Log.w(TAG, "restoreKeys Exception " + e); 4561 throw e; 4562 } 4563 4564 } // synchronized 4565 } 4566 4567 4568 /** 4569 * Read a DRM engine plugin String property value, given the property name string. 4570 * <p> 4571 * @param propertyName the property name 4572 * 4573 * Standard fields names are: 4574 * {@link MediaDrm#PROPERTY_VENDOR}, {@link MediaDrm#PROPERTY_VERSION}, 4575 * {@link MediaDrm#PROPERTY_DESCRIPTION}, {@link MediaDrm#PROPERTY_ALGORITHMS} 4576 */ 4577 @NonNull 4578 public String getDrmPropertyString(@NonNull @MediaDrm.StringProperty String propertyName) 4579 throws NoDrmSchemeException 4580 { 4581 Log.v(TAG, "getDrmPropertyString: propertyName: " + propertyName); 4582 4583 String value; 4584 synchronized (mDrmLock) { 4585 4586 if (!mActiveDrmScheme && !mDrmConfigAllowed) { 4587 Log.w(TAG, "getDrmPropertyString NoDrmSchemeException"); 4588 throw new NoDrmSchemeException("getDrmPropertyString: Has to prepareDrm() first."); 4589 } 4590 4591 try { 4592 value = mDrmObj.getPropertyString(propertyName); 4593 } catch (Exception e) { 4594 Log.w(TAG, "getDrmPropertyString Exception " + e); 4595 throw e; 4596 } 4597 } // synchronized 4598 4599 Log.v(TAG, "getDrmPropertyString: propertyName: " + propertyName + " --> value: " + value); 4600 4601 return value; 4602 } 4603 4604 4605 /** 4606 * Set a DRM engine plugin String property value. 4607 * <p> 4608 * @param propertyName the property name 4609 * @param value the property value 4610 * 4611 * Standard fields names are: 4612 * {@link MediaDrm#PROPERTY_VENDOR}, {@link MediaDrm#PROPERTY_VERSION}, 4613 * {@link MediaDrm#PROPERTY_DESCRIPTION}, {@link MediaDrm#PROPERTY_ALGORITHMS} 4614 */ 4615 public void setDrmPropertyString(@NonNull @MediaDrm.StringProperty String propertyName, 4616 @NonNull String value) 4617 throws NoDrmSchemeException 4618 { 4619 Log.v(TAG, "setDrmPropertyString: propertyName: " + propertyName + " value: " + value); 4620 4621 synchronized (mDrmLock) { 4622 4623 if ( !mActiveDrmScheme && !mDrmConfigAllowed ) { 4624 Log.w(TAG, "setDrmPropertyString NoDrmSchemeException"); 4625 throw new NoDrmSchemeException("setDrmPropertyString: Has to prepareDrm() first."); 4626 } 4627 4628 try { 4629 mDrmObj.setPropertyString(propertyName, value); 4630 } catch ( Exception e ) { 4631 Log.w(TAG, "setDrmPropertyString Exception " + e); 4632 throw e; 4633 } 4634 } // synchronized 4635 } 4636 4637 /** 4638 * Encapsulates the DRM properties of the source. 4639 */ 4640 public static final class DrmInfo { 4641 private Map<UUID, byte[]> mapPssh; 4642 private UUID[] supportedSchemes; 4643 4644 /** 4645 * Returns the PSSH info of the data source for each supported DRM scheme. 4646 */ 4647 public Map<UUID, byte[]> getPssh() { 4648 return mapPssh; 4649 } 4650 4651 /** 4652 * Returns the intersection of the data source and the device DRM schemes. 4653 * It effectively identifies the subset of the source's DRM schemes which 4654 * are supported by the device too. 4655 */ 4656 public UUID[] getSupportedSchemes() { 4657 return supportedSchemes; 4658 } 4659 4660 private DrmInfo(Map<UUID, byte[]> Pssh, UUID[] SupportedSchemes) { 4661 mapPssh = Pssh; 4662 supportedSchemes = SupportedSchemes; 4663 } 4664 4665 private DrmInfo(Parcel parcel) { 4666 Log.v(TAG, "DrmInfo(" + parcel + ") size " + parcel.dataSize()); 4667 4668 int psshsize = parcel.readInt(); 4669 byte[] pssh = new byte[psshsize]; 4670 parcel.readByteArray(pssh); 4671 4672 Log.v(TAG, "DrmInfo() PSSH: " + arrToHex(pssh)); 4673 mapPssh = parsePSSH(pssh, psshsize); 4674 Log.v(TAG, "DrmInfo() PSSH: " + mapPssh); 4675 4676 int supportedDRMsCount = parcel.readInt(); 4677 supportedSchemes = new UUID[supportedDRMsCount]; 4678 for (int i = 0; i < supportedDRMsCount; i++) { 4679 byte[] uuid = new byte[16]; 4680 parcel.readByteArray(uuid); 4681 4682 supportedSchemes[i] = bytesToUUID(uuid); 4683 4684 Log.v(TAG, "DrmInfo() supportedScheme[" + i + "]: " + 4685 supportedSchemes[i]); 4686 } 4687 4688 Log.v(TAG, "DrmInfo() Parcel psshsize: " + psshsize + 4689 " supportedDRMsCount: " + supportedDRMsCount); 4690 } 4691 4692 private DrmInfo makeCopy() { 4693 return new DrmInfo(this.mapPssh, this.supportedSchemes); 4694 } 4695 4696 private String arrToHex(byte[] bytes) { 4697 String out = "0x"; 4698 for (int i = 0; i < bytes.length; i++) { 4699 out += String.format("%02x", bytes[i]); 4700 } 4701 4702 return out; 4703 } 4704 4705 private UUID bytesToUUID(byte[] uuid) { 4706 long msb = 0, lsb = 0; 4707 for (int i = 0; i < 8; i++) { 4708 msb |= ( ((long)uuid[i] & 0xff) << (8 * (7 - i)) ); 4709 lsb |= ( ((long)uuid[i+8] & 0xff) << (8 * (7 - i)) ); 4710 } 4711 4712 return new UUID(msb, lsb); 4713 } 4714 4715 private Map<UUID, byte[]> parsePSSH(byte[] pssh, int psshsize) { 4716 Map<UUID, byte[]> result = new HashMap<UUID, byte[]>(); 4717 4718 final int UUID_SIZE = 16; 4719 final int DATALEN_SIZE = 4; 4720 4721 int len = psshsize; 4722 int numentries = 0; 4723 int i = 0; 4724 4725 while (len > 0) { 4726 if (len < UUID_SIZE) { 4727 Log.w(TAG, String.format("parsePSSH: len is too short to parse " + 4728 "UUID: (%d < 16) pssh: %d", len, psshsize)); 4729 return null; 4730 } 4731 4732 byte[] subset = Arrays.copyOfRange(pssh, i, i + UUID_SIZE); 4733 UUID uuid = bytesToUUID(subset); 4734 i += UUID_SIZE; 4735 len -= UUID_SIZE; 4736 4737 // get data length 4738 if (len < 4) { 4739 Log.w(TAG, String.format("parsePSSH: len is too short to parse " + 4740 "datalen: (%d < 4) pssh: %d", len, psshsize)); 4741 return null; 4742 } 4743 4744 subset = Arrays.copyOfRange(pssh, i, i+DATALEN_SIZE); 4745 int datalen = (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN) ? 4746 ((subset[3] & 0xff) << 24) | ((subset[2] & 0xff) << 16) | 4747 ((subset[1] & 0xff) << 8) | (subset[0] & 0xff) : 4748 ((subset[0] & 0xff) << 24) | ((subset[1] & 0xff) << 16) | 4749 ((subset[2] & 0xff) << 8) | (subset[3] & 0xff) ; 4750 i += DATALEN_SIZE; 4751 len -= DATALEN_SIZE; 4752 4753 if (len < datalen) { 4754 Log.w(TAG, String.format("parsePSSH: len is too short to parse " + 4755 "data: (%d < %d) pssh: %d", len, datalen, psshsize)); 4756 return null; 4757 } 4758 4759 byte[] data = Arrays.copyOfRange(pssh, i, i+datalen); 4760 4761 // skip the data 4762 i += datalen; 4763 len -= datalen; 4764 4765 Log.v(TAG, String.format("parsePSSH[%d]: <%s, %s> pssh: %d", 4766 numentries, uuid, arrToHex(data), psshsize)); 4767 numentries++; 4768 result.put(uuid, data); 4769 } 4770 4771 return result; 4772 } 4773 4774 }; // DrmInfo 4775 4776 /** 4777 * Thrown when a DRM method is called before preparing a DRM scheme through prepareDrm(). 4778 * Extends MediaDrm.MediaDrmException 4779 */ 4780 public static final class NoDrmSchemeException extends MediaDrmException { 4781 public NoDrmSchemeException(String detailMessage) { 4782 super(detailMessage); 4783 } 4784 } 4785 4786 /** 4787 * Thrown when the device requires DRM provisioning but the provisioning attempt has 4788 * failed due to a network error (Internet reachability, timeout, etc.). 4789 * Extends MediaDrm.MediaDrmException 4790 */ 4791 public static final class ProvisioningNetworkErrorException extends MediaDrmException { 4792 public ProvisioningNetworkErrorException(String detailMessage) { 4793 super(detailMessage); 4794 } 4795 } 4796 4797 /** 4798 * Thrown when the device requires DRM provisioning but the provisioning attempt has 4799 * failed due to the provisioning server denying the request. 4800 * Extends MediaDrm.MediaDrmException 4801 */ 4802 public static final class ProvisioningServerErrorException extends MediaDrmException { 4803 public ProvisioningServerErrorException(String detailMessage) { 4804 super(detailMessage); 4805 } 4806 } 4807 4808 4809 private native void _prepareDrm(@NonNull byte[] uuid, @NonNull byte[] drmSessionId); 4810 4811 // Modular DRM helpers 4812 4813 private void prepareDrm_createDrmStep(@NonNull UUID uuid) 4814 throws UnsupportedSchemeException { 4815 Log.v(TAG, "prepareDrm_createDrmStep: UUID: " + uuid); 4816 4817 try { 4818 mDrmObj = new MediaDrm(uuid); 4819 Log.v(TAG, "prepareDrm_createDrmStep: Created mDrmObj=" + mDrmObj); 4820 } catch (Exception e) { // UnsupportedSchemeException 4821 Log.e(TAG, "prepareDrm_createDrmStep: MediaDrm failed with " + e); 4822 throw e; 4823 } 4824 } 4825 4826 private void prepareDrm_openSessionStep(@NonNull UUID uuid) 4827 throws NotProvisionedException, ResourceBusyException { 4828 Log.v(TAG, "prepareDrm_openSessionStep: uuid: " + uuid); 4829 4830 // TODO: don't need an open session for a future specialKeyReleaseDrm mode but we should do 4831 // it anyway so it raises provisioning error if needed. We'd rather handle provisioning 4832 // at prepareDrm/openSession rather than getKeyRequest/provideKeyResponse 4833 try { 4834 mDrmSessionId = mDrmObj.openSession(); 4835 Log.v(TAG, "prepareDrm_openSessionStep: mDrmSessionId=" + mDrmSessionId); 4836 4837 // Sending it down to native/mediaserver to create the crypto object 4838 // This call could simply fail due to bad player state, e.g., after start(). 4839 _prepareDrm(getByteArrayFromUUID(uuid), mDrmSessionId); 4840 Log.v(TAG, "prepareDrm_openSessionStep: _prepareDrm/Crypto succeeded"); 4841 4842 } catch (Exception e) { //ResourceBusyException, NotProvisionedException 4843 Log.e(TAG, "prepareDrm_openSessionStep: open/crypto failed with " + e); 4844 throw e; 4845 } 4846 4847 } 4848 4849 private class ProvisioningThread extends Thread 4850 { 4851 public static final int TIMEOUT_MS = 60000; 4852 4853 private UUID uuid; 4854 private String urlStr; 4855 private Object drmLock; 4856 private OnDrmPreparedHandlerDelegate onDrmPreparedHandlerDelegate; 4857 private MediaPlayer mediaPlayer; 4858 private int status; 4859 private boolean finished; 4860 public int status() { 4861 return status; 4862 } 4863 4864 public ProvisioningThread initialize(MediaDrm.ProvisionRequest request, 4865 UUID uuid, MediaPlayer mediaPlayer) { 4866 // lock is held by the caller 4867 drmLock = mediaPlayer.mDrmLock; 4868 onDrmPreparedHandlerDelegate = mediaPlayer.mOnDrmPreparedHandlerDelegate; 4869 this.mediaPlayer = mediaPlayer; 4870 4871 urlStr = request.getDefaultUrl() + "&signedRequest=" + new String(request.getData()); 4872 this.uuid = uuid; 4873 4874 status = PREPARE_DRM_STATUS_PREPARATION_ERROR; 4875 4876 Log.v(TAG, "HandleProvisioninig: Thread is initialised url: " + urlStr); 4877 return this; 4878 } 4879 4880 public void run() { 4881 4882 byte[] response = null; 4883 boolean provisioningSucceeded = false; 4884 try { 4885 URL url = new URL(urlStr); 4886 final HttpURLConnection connection = (HttpURLConnection) url.openConnection(); 4887 try { 4888 connection.setRequestMethod("POST"); 4889 connection.setDoOutput(false); 4890 connection.setDoInput(true); 4891 connection.setConnectTimeout(TIMEOUT_MS); 4892 connection.setReadTimeout(TIMEOUT_MS); 4893 4894 connection.connect(); 4895 response = Streams.readFully(connection.getInputStream()); 4896 4897 Log.v(TAG, "HandleProvisioninig: Thread run: response " + 4898 response.length + " " + response); 4899 } catch (Exception e) { 4900 status = PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR; 4901 Log.w(TAG, "HandleProvisioninig: Thread run: connect " + e + " url: " + url); 4902 } finally { 4903 connection.disconnect(); 4904 } 4905 } catch (Exception e) { 4906 status = PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR; 4907 Log.w(TAG, "HandleProvisioninig: Thread run: openConnection " + e); 4908 } 4909 4910 if (response != null) { 4911 try { 4912 mDrmObj.provideProvisionResponse(response); 4913 Log.v(TAG, "HandleProvisioninig: Thread run: " + 4914 "provideProvisionResponse SUCCEEDED!"); 4915 4916 provisioningSucceeded = true; 4917 } catch (Exception e) { 4918 status = PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR; 4919 Log.w(TAG, "HandleProvisioninig: Thread run: " + 4920 "provideProvisionResponse " + e); 4921 } 4922 } 4923 4924 boolean succeeded = false; 4925 4926 // non-blocking mode needs the lock 4927 if (onDrmPreparedHandlerDelegate != null) { 4928 4929 synchronized (drmLock) { 4930 // continuing with prepareDrm 4931 if (provisioningSucceeded) { 4932 succeeded = mediaPlayer.resumePrepareDrm(uuid); 4933 status = (succeeded) ? 4934 PREPARE_DRM_STATUS_SUCCESS : 4935 PREPARE_DRM_STATUS_PREPARATION_ERROR; 4936 } 4937 mediaPlayer.mDrmProvisioningInProgress = false; 4938 mediaPlayer.mPrepareDrmInProgress = false; 4939 if (!succeeded) { 4940 cleanDrmObj(); // cleaning up if it hasn't gone through while in the lock 4941 } 4942 } // synchronized 4943 4944 // calling the callback outside the lock 4945 onDrmPreparedHandlerDelegate.notifyClient(status); 4946 } else { // blocking mode already has the lock 4947 4948 // continuing with prepareDrm 4949 if (provisioningSucceeded) { 4950 succeeded = mediaPlayer.resumePrepareDrm(uuid); 4951 status = (succeeded) ? 4952 PREPARE_DRM_STATUS_SUCCESS : 4953 PREPARE_DRM_STATUS_PREPARATION_ERROR; 4954 } 4955 mediaPlayer.mDrmProvisioningInProgress = false; 4956 mediaPlayer.mPrepareDrmInProgress = false; 4957 if (!succeeded) { 4958 cleanDrmObj(); // cleaning up if it hasn't gone through 4959 } 4960 } 4961 4962 finished = true; 4963 } // run() 4964 4965 } // ProvisioningThread 4966 4967 private int HandleProvisioninig(UUID uuid) 4968 { 4969 // the lock is already held by the caller 4970 4971 if (mDrmProvisioningInProgress) { 4972 Log.e(TAG, "HandleProvisioninig: Unexpected mDrmProvisioningInProgress"); 4973 return PREPARE_DRM_STATUS_PREPARATION_ERROR; 4974 } 4975 4976 MediaDrm.ProvisionRequest provReq = mDrmObj.getProvisionRequest(); 4977 if (provReq == null) { 4978 Log.e(TAG, "HandleProvisioninig: getProvisionRequest returned null."); 4979 return PREPARE_DRM_STATUS_PREPARATION_ERROR; 4980 } 4981 4982 Log.v(TAG, "HandleProvisioninig provReq " + 4983 " data: " + provReq.getData() + " url: " + provReq.getDefaultUrl()); 4984 4985 // networking in a background thread 4986 mDrmProvisioningInProgress = true; 4987 4988 mDrmProvisioningThread = new ProvisioningThread().initialize(provReq, uuid, this); 4989 mDrmProvisioningThread.start(); 4990 4991 int result; 4992 4993 // non-blocking: this is not the final result 4994 if (mOnDrmPreparedHandlerDelegate != null) { 4995 result = PREPARE_DRM_STATUS_SUCCESS; 4996 } else { 4997 // if blocking mode, wait till provisioning is done 4998 try { 4999 mDrmProvisioningThread.join(); 5000 } catch (Exception e) { 5001 Log.w(TAG, "HandleProvisioninig: Thread.join Exception " + e); 5002 } 5003 result = mDrmProvisioningThread.status(); 5004 // no longer need the thread 5005 mDrmProvisioningThread = null; 5006 } 5007 5008 return result; 5009 } 5010 5011 private boolean resumePrepareDrm(UUID uuid) 5012 { 5013 Log.v(TAG, "resumePrepareDrm: uuid: " + uuid); 5014 5015 // mDrmLock is guaranteed to be held 5016 boolean success = false; 5017 try { 5018 // resuming 5019 prepareDrm_openSessionStep(uuid); 5020 5021 mDrmUUID = uuid; 5022 mActiveDrmScheme = true; 5023 5024 success = true; 5025 } catch (Exception e) { 5026 Log.w(TAG, "HandleProvisioninig: Thread run _prepareDrm resume failed with " + e); 5027 // mDrmObj clean up is done by the caller 5028 } 5029 5030 return success; 5031 } 5032 5033 private void resetDrmState() 5034 { 5035 synchronized (mDrmLock) { 5036 Log.v(TAG, "resetDrmState: " + 5037 " mDrmInfo=" + mDrmInfo + 5038 " mDrmProvisioningThread=" + mDrmProvisioningThread + 5039 " mPrepareDrmInProgress=" + mPrepareDrmInProgress + 5040 " mActiveDrmScheme=" + mActiveDrmScheme); 5041 5042 mDrmInfoResolved = false; 5043 mDrmInfo = null; 5044 5045 if (mDrmProvisioningThread != null) { 5046 // timeout; relying on HttpUrlConnection 5047 try { 5048 mDrmProvisioningThread.join(); 5049 } 5050 catch (InterruptedException e) { 5051 Log.w(TAG, "resetDrmState: ProvThread.join Exception " + e); 5052 } 5053 mDrmProvisioningThread = null; 5054 } 5055 5056 mPrepareDrmInProgress = false; 5057 mActiveDrmScheme = false; 5058 5059 cleanDrmObj(); 5060 } // synchronized 5061 } 5062 5063 private void cleanDrmObj() 5064 { 5065 // the caller holds mDrmLock 5066 Log.v(TAG, "cleanDrmObj: mDrmObj=" + mDrmObj + " mDrmSessionId=" + mDrmSessionId); 5067 5068 if (mDrmSessionId != null) { 5069 mDrmObj.closeSession(mDrmSessionId); 5070 mDrmSessionId = null; 5071 } 5072 if (mDrmObj != null) { 5073 mDrmObj.release(); 5074 mDrmObj = null; 5075 } 5076 } 5077 5078 private static final byte[] getByteArrayFromUUID(@NonNull UUID uuid) { 5079 long msb = uuid.getMostSignificantBits(); 5080 long lsb = uuid.getLeastSignificantBits(); 5081 5082 byte[] uuidBytes = new byte[16]; 5083 for (int i = 0; i < 8; ++i) { 5084 uuidBytes[i] = (byte)(msb >>> (8 * (7 - i))); 5085 uuidBytes[8 + i] = (byte)(lsb >>> (8 * (7 - i))); 5086 } 5087 5088 return uuidBytes; 5089 } 5090 5091 // Modular DRM end 5092 5093 /* 5094 * Test whether a given video scaling mode is supported. 5095 */ 5096 private boolean isVideoScalingModeSupported(int mode) { 5097 return (mode == VIDEO_SCALING_MODE_SCALE_TO_FIT || 5098 mode == VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING); 5099 } 5100 5101 /** @hide */ 5102 static class TimeProvider implements MediaPlayer.OnSeekCompleteListener, 5103 MediaTimeProvider { 5104 private static final String TAG = "MTP"; 5105 private static final long MAX_NS_WITHOUT_POSITION_CHECK = 5000000000L; 5106 private static final long MAX_EARLY_CALLBACK_US = 1000; 5107 private static final long TIME_ADJUSTMENT_RATE = 2; /* meaning 1/2 */ 5108 private long mLastTimeUs = 0; 5109 private MediaPlayer mPlayer; 5110 private boolean mPaused = true; 5111 private boolean mStopped = true; 5112 private boolean mBuffering; 5113 private long mLastReportedTime; 5114 private long mTimeAdjustment; 5115 // since we are expecting only a handful listeners per stream, there is 5116 // no need for log(N) search performance 5117 private MediaTimeProvider.OnMediaTimeListener mListeners[]; 5118 private long mTimes[]; 5119 private long mLastNanoTime; 5120 private Handler mEventHandler; 5121 private boolean mRefresh = false; 5122 private boolean mPausing = false; 5123 private boolean mSeeking = false; 5124 private static final int NOTIFY = 1; 5125 private static final int NOTIFY_TIME = 0; 5126 private static final int REFRESH_AND_NOTIFY_TIME = 1; 5127 private static final int NOTIFY_STOP = 2; 5128 private static final int NOTIFY_SEEK = 3; 5129 private static final int NOTIFY_TRACK_DATA = 4; 5130 private HandlerThread mHandlerThread; 5131 5132 /** @hide */ 5133 public boolean DEBUG = false; 5134 5135 public TimeProvider(MediaPlayer mp) { 5136 mPlayer = mp; 5137 try { 5138 getCurrentTimeUs(true, false); 5139 } catch (IllegalStateException e) { 5140 // we assume starting position 5141 mRefresh = true; 5142 } 5143 5144 Looper looper; 5145 if ((looper = Looper.myLooper()) == null && 5146 (looper = Looper.getMainLooper()) == null) { 5147 // Create our own looper here in case MP was created without one 5148 mHandlerThread = new HandlerThread("MediaPlayerMTPEventThread", 5149 Process.THREAD_PRIORITY_FOREGROUND); 5150 mHandlerThread.start(); 5151 looper = mHandlerThread.getLooper(); 5152 } 5153 mEventHandler = new EventHandler(looper); 5154 5155 mListeners = new MediaTimeProvider.OnMediaTimeListener[0]; 5156 mTimes = new long[0]; 5157 mLastTimeUs = 0; 5158 mTimeAdjustment = 0; 5159 } 5160 5161 private void scheduleNotification(int type, long delayUs) { 5162 // ignore time notifications until seek is handled 5163 if (mSeeking && 5164 (type == NOTIFY_TIME || type == REFRESH_AND_NOTIFY_TIME)) { 5165 return; 5166 } 5167 5168 if (DEBUG) Log.v(TAG, "scheduleNotification " + type + " in " + delayUs); 5169 mEventHandler.removeMessages(NOTIFY); 5170 Message msg = mEventHandler.obtainMessage(NOTIFY, type, 0); 5171 mEventHandler.sendMessageDelayed(msg, (int) (delayUs / 1000)); 5172 } 5173 5174 /** @hide */ 5175 public void close() { 5176 mEventHandler.removeMessages(NOTIFY); 5177 if (mHandlerThread != null) { 5178 mHandlerThread.quitSafely(); 5179 mHandlerThread = null; 5180 } 5181 } 5182 5183 /** @hide */ 5184 protected void finalize() { 5185 if (mHandlerThread != null) { 5186 mHandlerThread.quitSafely(); 5187 } 5188 } 5189 5190 /** @hide */ 5191 public void onPaused(boolean paused) { 5192 synchronized(this) { 5193 if (DEBUG) Log.d(TAG, "onPaused: " + paused); 5194 if (mStopped) { // handle as seek if we were stopped 5195 mStopped = false; 5196 mSeeking = true; 5197 scheduleNotification(NOTIFY_SEEK, 0 /* delay */); 5198 } else { 5199 mPausing = paused; // special handling if player disappeared 5200 mSeeking = false; 5201 scheduleNotification(REFRESH_AND_NOTIFY_TIME, 0 /* delay */); 5202 } 5203 } 5204 } 5205 5206 /** @hide */ 5207 public void onBuffering(boolean buffering) { 5208 synchronized (this) { 5209 if (DEBUG) Log.d(TAG, "onBuffering: " + buffering); 5210 mBuffering = buffering; 5211 scheduleNotification(REFRESH_AND_NOTIFY_TIME, 0 /* delay */); 5212 } 5213 } 5214 5215 /** @hide */ 5216 public void onStopped() { 5217 synchronized(this) { 5218 if (DEBUG) Log.d(TAG, "onStopped"); 5219 mPaused = true; 5220 mStopped = true; 5221 mSeeking = false; 5222 mBuffering = false; 5223 scheduleNotification(NOTIFY_STOP, 0 /* delay */); 5224 } 5225 } 5226 5227 /** @hide */ 5228 @Override 5229 public void onSeekComplete(MediaPlayer mp) { 5230 synchronized(this) { 5231 mStopped = false; 5232 mSeeking = true; 5233 scheduleNotification(NOTIFY_SEEK, 0 /* delay */); 5234 } 5235 } 5236 5237 /** @hide */ 5238 public void onNewPlayer() { 5239 if (mRefresh) { 5240 synchronized(this) { 5241 mStopped = false; 5242 mSeeking = true; 5243 mBuffering = false; 5244 scheduleNotification(NOTIFY_SEEK, 0 /* delay */); 5245 } 5246 } 5247 } 5248 5249 private synchronized void notifySeek() { 5250 mSeeking = false; 5251 try { 5252 long timeUs = getCurrentTimeUs(true, false); 5253 if (DEBUG) Log.d(TAG, "onSeekComplete at " + timeUs); 5254 5255 for (MediaTimeProvider.OnMediaTimeListener listener: mListeners) { 5256 if (listener == null) { 5257 break; 5258 } 5259 listener.onSeek(timeUs); 5260 } 5261 } catch (IllegalStateException e) { 5262 // we should not be there, but at least signal pause 5263 if (DEBUG) Log.d(TAG, "onSeekComplete but no player"); 5264 mPausing = true; // special handling if player disappeared 5265 notifyTimedEvent(false /* refreshTime */); 5266 } 5267 } 5268 5269 private synchronized void notifyTrackData(Pair<SubtitleTrack, byte[]> trackData) { 5270 SubtitleTrack track = trackData.first; 5271 byte[] data = trackData.second; 5272 track.onData(data, true /* eos */, ~0 /* runID: keep forever */); 5273 } 5274 5275 private synchronized void notifyStop() { 5276 for (MediaTimeProvider.OnMediaTimeListener listener: mListeners) { 5277 if (listener == null) { 5278 break; 5279 } 5280 listener.onStop(); 5281 } 5282 } 5283 5284 private int registerListener(MediaTimeProvider.OnMediaTimeListener listener) { 5285 int i = 0; 5286 for (; i < mListeners.length; i++) { 5287 if (mListeners[i] == listener || mListeners[i] == null) { 5288 break; 5289 } 5290 } 5291 5292 // new listener 5293 if (i >= mListeners.length) { 5294 MediaTimeProvider.OnMediaTimeListener[] newListeners = 5295 new MediaTimeProvider.OnMediaTimeListener[i + 1]; 5296 long[] newTimes = new long[i + 1]; 5297 System.arraycopy(mListeners, 0, newListeners, 0, mListeners.length); 5298 System.arraycopy(mTimes, 0, newTimes, 0, mTimes.length); 5299 mListeners = newListeners; 5300 mTimes = newTimes; 5301 } 5302 5303 if (mListeners[i] == null) { 5304 mListeners[i] = listener; 5305 mTimes[i] = MediaTimeProvider.NO_TIME; 5306 } 5307 return i; 5308 } 5309 5310 public void notifyAt( 5311 long timeUs, MediaTimeProvider.OnMediaTimeListener listener) { 5312 synchronized(this) { 5313 if (DEBUG) Log.d(TAG, "notifyAt " + timeUs); 5314 mTimes[registerListener(listener)] = timeUs; 5315 scheduleNotification(NOTIFY_TIME, 0 /* delay */); 5316 } 5317 } 5318 5319 public void scheduleUpdate(MediaTimeProvider.OnMediaTimeListener listener) { 5320 synchronized(this) { 5321 if (DEBUG) Log.d(TAG, "scheduleUpdate"); 5322 int i = registerListener(listener); 5323 5324 if (!mStopped) { 5325 mTimes[i] = 0; 5326 scheduleNotification(NOTIFY_TIME, 0 /* delay */); 5327 } 5328 } 5329 } 5330 5331 public void cancelNotifications( 5332 MediaTimeProvider.OnMediaTimeListener listener) { 5333 synchronized(this) { 5334 int i = 0; 5335 for (; i < mListeners.length; i++) { 5336 if (mListeners[i] == listener) { 5337 System.arraycopy(mListeners, i + 1, 5338 mListeners, i, mListeners.length - i - 1); 5339 System.arraycopy(mTimes, i + 1, 5340 mTimes, i, mTimes.length - i - 1); 5341 mListeners[mListeners.length - 1] = null; 5342 mTimes[mTimes.length - 1] = NO_TIME; 5343 break; 5344 } else if (mListeners[i] == null) { 5345 break; 5346 } 5347 } 5348 5349 scheduleNotification(NOTIFY_TIME, 0 /* delay */); 5350 } 5351 } 5352 5353 private synchronized void notifyTimedEvent(boolean refreshTime) { 5354 // figure out next callback 5355 long nowUs; 5356 try { 5357 nowUs = getCurrentTimeUs(refreshTime, true); 5358 } catch (IllegalStateException e) { 5359 // assume we paused until new player arrives 5360 mRefresh = true; 5361 mPausing = true; // this ensures that call succeeds 5362 nowUs = getCurrentTimeUs(refreshTime, true); 5363 } 5364 long nextTimeUs = nowUs; 5365 5366 if (mSeeking) { 5367 // skip timed-event notifications until seek is complete 5368 return; 5369 } 5370 5371 if (DEBUG) { 5372 StringBuilder sb = new StringBuilder(); 5373 sb.append("notifyTimedEvent(").append(mLastTimeUs).append(" -> ") 5374 .append(nowUs).append(") from {"); 5375 boolean first = true; 5376 for (long time: mTimes) { 5377 if (time == NO_TIME) { 5378 continue; 5379 } 5380 if (!first) sb.append(", "); 5381 sb.append(time); 5382 first = false; 5383 } 5384 sb.append("}"); 5385 Log.d(TAG, sb.toString()); 5386 } 5387 5388 Vector<MediaTimeProvider.OnMediaTimeListener> activatedListeners = 5389 new Vector<MediaTimeProvider.OnMediaTimeListener>(); 5390 for (int ix = 0; ix < mTimes.length; ix++) { 5391 if (mListeners[ix] == null) { 5392 break; 5393 } 5394 if (mTimes[ix] <= NO_TIME) { 5395 // ignore, unless we were stopped 5396 } else if (mTimes[ix] <= nowUs + MAX_EARLY_CALLBACK_US) { 5397 activatedListeners.add(mListeners[ix]); 5398 if (DEBUG) Log.d(TAG, "removed"); 5399 mTimes[ix] = NO_TIME; 5400 } else if (nextTimeUs == nowUs || mTimes[ix] < nextTimeUs) { 5401 nextTimeUs = mTimes[ix]; 5402 } 5403 } 5404 5405 if (nextTimeUs > nowUs && !mPaused) { 5406 // schedule callback at nextTimeUs 5407 if (DEBUG) Log.d(TAG, "scheduling for " + nextTimeUs + " and " + nowUs); 5408 scheduleNotification(NOTIFY_TIME, nextTimeUs - nowUs); 5409 } else { 5410 mEventHandler.removeMessages(NOTIFY); 5411 // no more callbacks 5412 } 5413 5414 for (MediaTimeProvider.OnMediaTimeListener listener: activatedListeners) { 5415 listener.onTimedEvent(nowUs); 5416 } 5417 } 5418 5419 private long getEstimatedTime(long nanoTime, boolean monotonic) { 5420 if (mPaused) { 5421 mLastReportedTime = mLastTimeUs + mTimeAdjustment; 5422 } else { 5423 long timeSinceRead = (nanoTime - mLastNanoTime) / 1000; 5424 mLastReportedTime = mLastTimeUs + timeSinceRead; 5425 if (mTimeAdjustment > 0) { 5426 long adjustment = 5427 mTimeAdjustment - timeSinceRead / TIME_ADJUSTMENT_RATE; 5428 if (adjustment <= 0) { 5429 mTimeAdjustment = 0; 5430 } else { 5431 mLastReportedTime += adjustment; 5432 } 5433 } 5434 } 5435 return mLastReportedTime; 5436 } 5437 5438 public long getCurrentTimeUs(boolean refreshTime, boolean monotonic) 5439 throws IllegalStateException { 5440 synchronized (this) { 5441 // we always refresh the time when the paused-state changes, because 5442 // we expect to have received the pause-change event delayed. 5443 if (mPaused && !refreshTime) { 5444 return mLastReportedTime; 5445 } 5446 5447 long nanoTime = System.nanoTime(); 5448 if (refreshTime || 5449 nanoTime >= mLastNanoTime + MAX_NS_WITHOUT_POSITION_CHECK) { 5450 try { 5451 mLastTimeUs = mPlayer.getCurrentPosition() * 1000L; 5452 mPaused = !mPlayer.isPlaying() || mBuffering; 5453 if (DEBUG) Log.v(TAG, (mPaused ? "paused" : "playing") + " at " + mLastTimeUs); 5454 } catch (IllegalStateException e) { 5455 if (mPausing) { 5456 // if we were pausing, get last estimated timestamp 5457 mPausing = false; 5458 getEstimatedTime(nanoTime, monotonic); 5459 mPaused = true; 5460 if (DEBUG) Log.d(TAG, "illegal state, but pausing: estimating at " + mLastReportedTime); 5461 return mLastReportedTime; 5462 } 5463 // TODO get time when prepared 5464 throw e; 5465 } 5466 mLastNanoTime = nanoTime; 5467 if (monotonic && mLastTimeUs < mLastReportedTime) { 5468 /* have to adjust time */ 5469 mTimeAdjustment = mLastReportedTime - mLastTimeUs; 5470 if (mTimeAdjustment > 1000000) { 5471 // schedule seeked event if time jumped significantly 5472 // TODO: do this properly by introducing an exception 5473 mStopped = false; 5474 mSeeking = true; 5475 scheduleNotification(NOTIFY_SEEK, 0 /* delay */); 5476 } 5477 } else { 5478 mTimeAdjustment = 0; 5479 } 5480 } 5481 5482 return getEstimatedTime(nanoTime, monotonic); 5483 } 5484 } 5485 5486 private class EventHandler extends Handler { 5487 public EventHandler(Looper looper) { 5488 super(looper); 5489 } 5490 5491 @Override 5492 public void handleMessage(Message msg) { 5493 if (msg.what == NOTIFY) { 5494 switch (msg.arg1) { 5495 case NOTIFY_TIME: 5496 notifyTimedEvent(false /* refreshTime */); 5497 break; 5498 case REFRESH_AND_NOTIFY_TIME: 5499 notifyTimedEvent(true /* refreshTime */); 5500 break; 5501 case NOTIFY_STOP: 5502 notifyStop(); 5503 break; 5504 case NOTIFY_SEEK: 5505 notifySeek(); 5506 break; 5507 case NOTIFY_TRACK_DATA: 5508 notifyTrackData((Pair<SubtitleTrack, byte[]>)msg.obj); 5509 break; 5510 } 5511 } 5512 } 5513 } 5514 } 5515 5516 public final static class MetricsConstants 5517 { 5518 private MetricsConstants() {} 5519 5520 /** 5521 * Key to extract the MIME type of the video track 5522 * from the {@link MediaPlayer#getMetrics} return value. 5523 * The value is a String. 5524 */ 5525 public static final String MIME_TYPE_VIDEO = "android.media.mediaplayer.video.mime"; 5526 5527 /** 5528 * Key to extract the codec being used to decode the video track 5529 * from the {@link MediaPlayer#getMetrics} return value. 5530 * The value is a String. 5531 */ 5532 public static final String CODEC_VIDEO = "android.media.mediaplayer.video.codec"; 5533 5534 /** 5535 * Key to extract the width (in pixels) of the video track 5536 * from the {@link MediaPlayer#getMetrics} return value. 5537 * The value is an integer. 5538 */ 5539 public static final String WIDTH = "android.media.mediaplayer.width"; 5540 5541 /** 5542 * Key to extract the height (in pixels) of the video track 5543 * from the {@link MediaPlayer#getMetrics} return value. 5544 * The value is an integer. 5545 */ 5546 public static final String HEIGHT = "android.media.mediaplayer.height"; 5547 5548 /** 5549 * Key to extract the count of video frames played 5550 * from the {@link MediaPlayer#getMetrics} return value. 5551 * The value is an integer. 5552 */ 5553 public static final String FRAMES = "android.media.mediaplayer.frames"; 5554 5555 /** 5556 * Key to extract the count of video frames dropped 5557 * from the {@link MediaPlayer#getMetrics} return value. 5558 * The value is an integer. 5559 */ 5560 public static final String FRAMES_DROPPED = "android.media.mediaplayer.dropped"; 5561 5562 /** 5563 * Key to extract the MIME type of the audio track 5564 * from the {@link MediaPlayer#getMetrics} return value. 5565 * The value is a String. 5566 */ 5567 public static final String MIME_TYPE_AUDIO = "android.media.mediaplayer.audio.mime"; 5568 5569 /** 5570 * Key to extract the codec being used to decode the audio track 5571 * from the {@link MediaPlayer#getMetrics} return value. 5572 * The value is a String. 5573 */ 5574 public static final String CODEC_AUDIO = "android.media.mediaplayer.audio.codec"; 5575 5576 /** 5577 * Key to extract the duration (in milliseconds) of the 5578 * media being played 5579 * from the {@link MediaPlayer#getMetrics} return value. 5580 * The value is a long. 5581 */ 5582 public static final String DURATION = "android.media.mediaplayer.durationMs"; 5583 5584 /** 5585 * Key to extract the playing time (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 PLAYING = "android.media.mediaplayer.playingMs"; 5591 5592 /** 5593 * Key to extract the count of errors encountered while 5594 * playing the media 5595 * from the {@link MediaPlayer#getMetrics} return value. 5596 * The value is an integer. 5597 */ 5598 public static final String ERRORS = "android.media.mediaplayer.err"; 5599 5600 /** 5601 * Key to extract an (optional) error code detected 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 ERROR_CODE = "android.media.mediaplayer.errcode"; 5607 5608 } 5609} 5610