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