001/* 002 * Copyright (C) 2006 The Android Open Source Project 003 * Copyright (C) 2013 Zhang Rui <bbcallen@gmail.com> 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package com.baidu.cloud.media.player; 019 020import android.annotation.SuppressLint; 021import android.annotation.TargetApi; 022import android.content.ContentResolver; 023import android.content.Context; 024import android.content.SharedPreferences; 025import android.content.res.AssetFileDescriptor; 026import android.graphics.Rect; 027import android.graphics.SurfaceTexture; 028import android.media.MediaCodecInfo; 029import android.media.MediaCodecList; 030import android.media.RingtoneManager; 031import android.net.Uri; 032import android.os.Build; 033import android.os.Bundle; 034import android.os.Handler; 035import android.os.Looper; 036import android.os.Message; 037import android.os.ParcelFileDescriptor; 038import android.os.PowerManager; 039import android.provider.Settings; 040import android.text.TextUtils; 041import android.util.Log; 042import android.view.Surface; 043import android.view.SurfaceHolder; 044 045import com.baidu.cloud.media.download.LocalHlsSec; 046import com.baidu.cloud.media.player.annotations.AccessedByNative; 047import com.baidu.cloud.media.player.annotations.CalledByNative; 048import com.baidu.cloud.media.player.apm.APMEventHandle; 049import com.baidu.cloud.media.player.misc.BDCloudTrackInfo; 050import com.baidu.cloud.media.player.misc.IMediaDataSource; 051import com.baidu.cloud.media.player.misc.ITrackInfo; 052import com.baidu.cloud.media.player.pragma.DebugLog; 053 054import org.json.JSONArray; 055import org.json.JSONObject; 056 057import java.io.FileDescriptor; 058import java.io.FileNotFoundException; 059import java.io.IOException; 060import java.lang.ref.WeakReference; 061import java.lang.reflect.Field; 062import java.security.InvalidParameterException; 063import java.text.SimpleDateFormat; 064import java.util.ArrayList; 065import java.util.Date; 066import java.util.Locale; 067import java.util.Map; 068import java.util.TimeZone; 069import java.util.Timer; 070import java.util.TimerTask; 071 072/** 073 * 播放器核心类 074 * 该类的接口与安卓系统内置的MediaPlayer类似 075 */ 076public final class BDCloudMediaPlayer extends AbstractMediaPlayer { 077 private static final String TAG = BDCloudMediaPlayer.class.getName(); 078 079 private static final String SDK_VERSION = "2.2.7"; 080 private static String mAK = ""; 081 private static String playId = ""; 082 private static boolean sEnableP2P = false; 083 private static boolean sEnableCache = false; 084 private static String sCacheRootPath = null; 085 086 private static final int MEDIA_NOP = 0; // interface test message 087 private static final int MEDIA_PREPARED = 1; 088 private static final int MEDIA_PLAYBACK_STOPPED = 2; 089 private static final int MEDIA_PLAYBACK_COMPLETE = 3; 090 private static final int MEDIA_BUFFERING_UPDATE = 4; 091 private static final int MEDIA_SEEK_COMPLETE = 5; 092 private static final int MEDIA_SET_VIDEO_SIZE = 6; 093 private static final int MEDIA_TIMED_TEXT = 99; 094 private static final int MEDIA_ERROR = 100; 095 private static final int MEDIA_INFO = 200; 096 097 protected static final int MEDIA_SET_VIDEO_SAR = 10001; 098 099 private static final int AVAPP_EVENT_METADATA = 0x13000; 100 private static final int AVAPP_EVENT_DNS = 0x14000; 101 private static final int AVAPP_EVENT_HTTP_CONNECT_END = 0x30002; 102 103 // ---------------------------------------- 104 // options 105 private static final int IJK_LOG_UNKNOWN = 0; 106 private static final int IJK_LOG_DEFAULT = 1; 107 108 private static final int IJK_LOG_VERBOSE = 2; 109 private static final int IJK_LOG_DEBUG = 3; 110 private static final int IJK_LOG_INFO = 4; 111 private static final int IJK_LOG_WARN = 5; 112 private static final int IJK_LOG_ERROR = 6; 113 private static final int IJK_LOG_FATAL = 7; 114 private static final int IJK_LOG_SILENT = 8; 115 116 private static final int OPT_CATEGORY_FORMAT = 1; 117 private static final int OPT_CATEGORY_CODEC = 2; 118 private static final int OPT_CATEGORY_SWS = 3; 119 private static final int OPT_CATEGORY_PLAYER = 4; 120 121 private static final int SDL_FCC_YV12 = 0x32315659; // YV12 122 private static final int SDL_FCC_RV16 = 0x36315652; // RGB565 123 private static final int SDL_FCC_RV32 = 0x32335652; // RGBX8888 124 // ---------------------------------------- 125 126 // ---------------------------------------- 127 // properties 128 private static final int PROP_FLOAT_VIDEO_DECODE_FRAMES_PER_SECOND = 10001; 129 private static final int PROP_FLOAT_VIDEO_OUTPUT_FRAMES_PER_SECOND = 10002; 130 private static final int FFP_PROP_FLOAT_PLAYBACK_RATE = 10003; 131 132 private static final int FFP_PROP_INT64_SELECTED_VIDEO_STREAM = 20001; 133 private static final int FFP_PROP_INT64_SELECTED_AUDIO_STREAM = 20002; 134 private static final int FFP_PROP_INT64_SELECTED_TIMEDTEXT_STREAM = 20011; 135 136 private static final int FFP_PROP_INT64_VIDEO_DECODER = 20003; 137 private static final int FFP_PROP_INT64_AUDIO_DECODER = 20004; 138 private static final int FFP_PROPV_DECODER_UNKNOWN = 0; 139 private static final int FFP_PROPV_DECODER_AVCODEC = 1; 140 private static final int FFP_PROPV_DECODER_MEDIACODEC = 2; 141 private static final int FFP_PROPV_DECODER_VIDEOTOOLBOX = 3; 142 private static final int FFP_PROP_INT64_VIDEO_CACHED_DURATION = 20005; 143 private static final int FFP_PROP_INT64_AUDIO_CACHED_DURATION = 20006; 144 private static final int FFP_PROP_INT64_VIDEO_CACHED_BYTES = 20007; 145 private static final int FFP_PROP_INT64_AUDIO_CACHED_BYTES = 20008; 146 private static final int FFP_PROP_INT64_VIDEO_CACHED_PACKETS = 20009; 147 private static final int FFP_PROP_INT64_AUDIO_CACHED_PACKETS = 20010; 148 private static final int FFP_PROP_INT64_ASYNC_STATISTIC_BUF_BACKWARDS = 20201; 149 private static final int FFP_PROP_INT64_ASYNC_STATISTIC_BUF_FORWARDS = 20202; 150 private static final int FFP_PROP_INT64_ASYNC_STATISTIC_BUF_CAPACITY = 20203; 151 private static final int FFP_PROP_INT64_TRAFFIC_STATISTIC_BYTE_COUNT = 20204; 152 private static final int FFP_PROP_INT64_CACHE_STATISTIC_PHYSICAL_POS = 20205; 153 private static final int FFP_PROP_INT64_CACHE_STATISTIC_BUF_FORWARDS = 20206; 154 private static final int FFP_PROP_INT64_CACHE_STATISTIC_FILE_POS = 20207; 155 private static final int FFP_PROP_INT64_CACHE_STATISTIC_COUNT_BYTES = 20208; 156 private static final int FFP_PROP_INT64_BIT_RATE = 20100; 157 private static final int FFP_PROP_INT64_TCP_SPEED = 20200; 158 private static final int FFP_PROP_INT64_LATEST_SEEK_LOAD_DURATION = 20300; 159 // ---------------------------------------- 160 161 private static final String SP_FILE_FOR_KEY = "__cyberplayer_dl_sec"; 162 163 @AccessedByNative 164 private long mNativeMediaPlayer; 165 @AccessedByNative 166 private long mNativeMediaDataSource; 167 168 @AccessedByNative 169 private long mNativeAndroidIO; 170 171 @AccessedByNative 172 private int mNativeSurfaceTexture; 173 174 @AccessedByNative 175 private int mListenerContext; 176 177 private SurfaceHolder mSurfaceHolder; 178 private EventHandler mEventHandler; 179 private PowerManager.WakeLock mWakeLock = null; 180 private boolean mScreenOnWhilePlaying; 181 private boolean mStayAwake; 182 183 private int mVideoWidth; 184 private int mVideoHeight; 185 private int mVideoSarNum; 186 private int mVideoSarDen; 187 188 private String mDataSource; 189 private int mNetworkTimeoutInUs = 15000000; 190 191 private int stayInterval = 0; 192 private long lastStartPlayTime = 0L; 193 private JSONArray bufferStatJsonArray = new JSONArray(); 194 private Date latestBufferStartTime = null; 195 private long bufferingStartPosition = -1; 196 197 private long startPrepareTimeForApm = 0L; 198 private int maxCacheSizeForApm = 0; 199 private int cachePauseTimeForApm = 0; 200 private int firstBufferingTimeForApm = 0; 201 private boolean toggleFrameChasingForApm; 202 private volatile boolean isEndSended = true; 203 private Timer apmPlayingTimer = null; 204 205 private Context appContext = null; 206 207 private String mCustomisedHeaders = null; 208 209 /** 210 * 设置Access Key 211 * 212 * @param akOfBDCloud 百度云后台的ak,详见 百度云后台 --> 右上角的用户名 --> "安全认证" --> "Access Key" 213 */ 214 public static void setAK(String akOfBDCloud) { 215 mAK = akOfBDCloud; 216 } 217 218 /** 219 * 设置是否开启P2P下载模块 220 * 221 * @param isEnable 是否开启 222 */ 223 public static void setP2PEnabled(boolean isEnable) { 224 sEnableP2P = isEnable; 225 } 226 227 /** 228 * 设置是否开启本地缓存功能,开启该功能后,可以在观看过程中将音视频数据临时缓存到本地目录 229 * 230 * @param isEnable 是否开启 231 * @param cachePath 临时缓存路径,如果开启本地缓存功能,该路径不能为空 232 */ 233 public static void setLocalCacheEnabled(boolean isEnable, String cachePath) { 234 sEnableCache = isEnable; 235 sCacheRootPath = cachePath; 236 if (sEnableCache && TextUtils.isEmpty(sCacheRootPath)) { 237 throw new IllegalArgumentException("cachePath can not be empty if enabled local cache"); 238 } 239 } 240 241 /** 242 * Default library loader Load them by yourself, if your libraries are not installed at default place. 243 */ 244 private static final BDCloudLibLoader sLocalLibLoader = new BDCloudLibLoader() { 245 @Override 246 public void loadLibrary(String libName) throws UnsatisfiedLinkError, SecurityException { 247 System.loadLibrary(libName); 248 } 249 }; 250 251 private static volatile boolean mIsLibLoaded = false; 252 253 /** 254 * so库定制加载,一般不需要调用。因实例化BDCloudMediaPlayer时会自动加载so,若想定制加载,务必在创建player实例前调用。 255 * 256 * @param libLoader 257 */ 258 public static void loadLibrariesOnce(BDCloudLibLoader libLoader) { 259 synchronized (BDCloudMediaPlayer.class) { 260 if (!mIsLibLoaded) { 261 if (libLoader == null) { 262 libLoader = sLocalLibLoader; 263 } 264 265 try { 266 libLoader.loadLibrary("pcdn"); 267 } catch (Throwable e) { // In case p2p module is not enabled 268 // no need to log this info 269 // e.printStackTrace(); 270 } finally { 271 libLoader.loadLibrary("stlport_shared"); 272 libLoader.loadLibrary("bdsoundutils"); 273 libLoader.loadLibrary("bdplayer"); 274 mIsLibLoaded = true; 275 } 276 } 277 } 278 } 279 280 /** 281 * 默认构造方法 282 * <p> 283 * 当使用完BDCloudMediaPlayer之后,务必调用{@link #release()}以释放资源,否则过多的BDCloudMediaPlayer实例可能导致异常。 284 * </p> 285 */ 286 public BDCloudMediaPlayer(Context context) { 287 this(context, sLocalLibLoader); 288 } 289 290 /** 291 * 构造方法 292 * <p> 293 * 当使用完BDCloudMediaPlayer之后,务必调用{@link #release()}以释放资源,否则过多的BDCloudMediaPlayer实例可能导致异常。 294 * </p> 295 * 296 * @param context 上下文 297 * @param libLoader 定制so加载器 298 */ 299 public BDCloudMediaPlayer(Context context, BDCloudLibLoader libLoader) { 300 appContext = context.getApplicationContext(); 301 initPlayer(libLoader); 302 } 303 304 private void initPlayer(BDCloudLibLoader libLoader) { 305 loadLibrariesOnce(libLoader); 306 307 Looper looper; 308 if ((looper = Looper.myLooper()) != null) { 309 mEventHandler = new EventHandler(this, looper); 310 } else if ((looper = Looper.getMainLooper()) != null) { 311 mEventHandler = new EventHandler(this, looper); 312 } else { 313 mEventHandler = null; 314 } 315 316 /* 317 * Native init requires a weak reference to our object. It's easier to create it here than in C++. 318 */ 319 native_init(new WeakReference<BDCloudMediaPlayer>(this)); 320 321 setDecodeMode(DECODE_AUTO); 322 setOption(OPT_CATEGORY_PLAYER, "start-on-prepared", 0); 323 setOption(OPT_CATEGORY_PLAYER, "max-fps", 30); 324 setOption(OPT_CATEGORY_PLAYER, "framedrop", 1); 325 setOption(OPT_CATEGORY_PLAYER, "framechasing", 0); 326 setOption(OPT_CATEGORY_PLAYER, "soundtouch", 1); 327 setOption(OPT_CATEGORY_PLAYER, "subtitle", 1); 328 setOption(OPT_CATEGORY_PLAYER, "enable-accurate-seek", 1); 329 setLogEnabled(false); 330 } 331 332 private void setupPlayer() { 333 // setOption(OPT_CATEGORY_FORMAT, "auto_convert", 0); // only for concat protocol 334 setOption(OPT_CATEGORY_FORMAT, "reconnect", 1); 335 setOption(OPT_CATEGORY_FORMAT, "timeout", mNetworkTimeoutInUs); 336 native_setup(); 337 } 338 339 /* 340 * Update the BDCloudMediaPlayer SurfaceTexture. Call after setting a new display surface. 341 */ 342 private native void _setVideoSurface(Surface surface); 343 344 /** 345 * 设置 {@link SurfaceHolder} 用于显示视频 346 * <p> 347 * 如果想显示视频,需要设置SurfaceHolder或者Surface。既不设置该方法也不设置{@link #setSurface(Surface)}方法,会导致仅有音频播放。将 348 * SurfaceHolder或者Surface设置为空,也会导致仅播放音频。 349 * 350 * @param sh the SurfaceHolder to use for video display 351 */ 352 @Override 353 public void setDisplay(SurfaceHolder sh) { 354 mSurfaceHolder = sh; 355 Surface surface; 356 if (sh != null) { 357 surface = sh.getSurface(); 358 } else { 359 surface = null; 360 } 361 _setVideoSurface(surface); 362 updateSurfaceScreenOn(); 363 } 364 365 /** 366 * 设置{@link Surface}来显示视频,与接口{@link #setDisplay(SurfaceHolder)}功能类似,但不支持{@link #setScreenOnWhilePlaying(boolean)} 367 * 接口的设置。当调用该接口时,之前设置的Surface或SurfaceHolder将被替换。设置为null会仅播放音频。 368 * <p> 369 * 如果设置的Surface会往{@link SurfaceTexture}发送frames,{@link SurfaceTexture#getTimestamp()}接口返回的时间戳会有未指定的零点。这些时间戳 370 * 在不同媒体资源、同一资源的不同实例、同一程序的多次运行的情况中不具有可比性。这个时间戳会单调递增,并且不受时间调整的影响,但在位置设置后会被重置。 371 * 372 * @param surface The {@link Surface} to be used for the video portion of the media. 373 */ 374 @Override 375 public void setSurface(Surface surface) { 376 if (mScreenOnWhilePlaying && surface != null) { 377 DebugLog.w(TAG, "setScreenOnWhilePlaying(true) is ineffective for Surface"); 378 } 379 mSurfaceHolder = null; 380 _setVideoSurface(surface); 381 updateSurfaceScreenOn(); 382 } 383 384 /** 385 * 设置播放源 386 * 387 * @param context the Context to use when resolving the Uri 388 * @param uri the Content URI of the data you want to play 389 * @throws IllegalStateException if it is called in an invalid state 390 */ 391 @Override 392 public void setDataSource(Context context, Uri uri) 393 throws IOException, IllegalArgumentException, SecurityException, IllegalStateException { 394 setDataSource(context, uri, null); 395 } 396 397 /** 398 * 设置播放源,可设置请求头信息 399 * 400 * @param context the Context to use when resolving the Uri 401 * @param uri the Content URI of the data you want to play 402 * @param headers the headers to be sent together with the request for the data Note that the cross domain 403 * redirection is allowed by default, but that can be changed with key/value pairs through the headers 404 * parameter with "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value to 405 * disallow or allow cross domain redirection. 406 * @throws IllegalStateException if it is called in an invalid state 407 */ 408 @Override 409 public void setDataSource(Context context, Uri uri, Map<String, String> headers) 410 throws IOException, IllegalArgumentException, SecurityException, IllegalStateException { 411 final String scheme = uri.getScheme(); 412 if (ContentResolver.SCHEME_FILE.equals(scheme)) { 413 setDataSource(uri.getPath()); 414 return; 415 } else if (ContentResolver.SCHEME_CONTENT.equals(scheme) && Settings.AUTHORITY.equals(uri.getAuthority())) { 416 // Redirect ringtones to go directly to underlying provider 417 uri = RingtoneManager.getActualDefaultRingtoneUri(context, RingtoneManager.getDefaultType(uri)); 418 if (uri == null) { 419 throw new FileNotFoundException("Failed to resolve default ringtone"); 420 } 421 } 422 423 AssetFileDescriptor fd = null; 424 try { 425 ContentResolver resolver = context.getContentResolver(); 426 fd = resolver.openAssetFileDescriptor(uri, "r"); 427 if (fd == null) { 428 return; 429 } 430 // Note: using getDeclaredLength so that our behavior is the same 431 // as previous versions when the content provider is returning 432 // a full file. 433 if (fd.getDeclaredLength() < 0) { 434 setDataSource(fd.getFileDescriptor()); 435 } else { 436 setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getDeclaredLength()); 437 } 438 return; 439 } catch (SecurityException ignored) { 440 } catch (IOException ignored) { 441 } finally { 442 if (fd != null) { 443 fd.close(); 444 } 445 } 446 447 setDataSource(uri.toString(), headers); 448 } 449 450 /** 451 * 设置播放源 (file-path or http/rtsp URL) 452 * 453 * @param path the path of the file, or the http/rtsp URL of the stream you want to play 454 * @throws IllegalStateException if it is called in an invalid state 455 * <p> 456 * <p> 457 * When <code>path</code> refers to a local file, the file may actually be opened by a process other 458 * than the calling application. This implies that the pathname should be an absolute path (as any other 459 * process runs with unspecified current working directory), and that the pathname should reference a 460 * world-readable file. 461 */ 462 @Override 463 public void setDataSource(String path) 464 throws IOException, IllegalArgumentException, SecurityException, IllegalStateException { 465 mDataSource = path; 466 this.onInitingStat(); 467 setupPlayer(); 468 _setDataSource(path, null, null); 469 470 if (isLocalFilePath(path)) { 471// Log.d(TAG, "isLocalFile=" + path); 472 // try to fetch 473 String newFile = path.replace("file://", ""); 474 SharedPreferences sp = appContext.getSharedPreferences(SP_FILE_FOR_KEY, 0); 475 String savedKey = sp.getString(newFile, null); 476 if (savedKey != null) { 477 try { 478 String key = LocalHlsSec.decryptStr(appContext, savedKey); 479 480 if (key != null && key.length() > 0) { 481 this.setLocalDecryptKeyForHLS(key); 482 } 483 } catch (Exception e) { 484 Log.d(TAG, "", e); 485 } 486 487 } 488 } else { 489 setOption(OPT_CATEGORY_PLAYER, "enable_p2p", sEnableP2P ? 1 : 0); 490 setOption(OPT_CATEGORY_PLAYER, "enable_cache", sEnableCache ? 1 : 0); 491 setOption(OPT_CATEGORY_FORMAT, "cache_dir", sEnableCache ? sCacheRootPath : ""); 492 } 493 } 494 495 /** 496 * 设置播放源 (file-path or http/rtsp URL),可设置网络请求头部信息。 497 * 498 * @param path the path of the file, or the http/rtsp URL of the stream you want to play 499 * @param headers the headers associated with the http request for the stream you want to play 500 * @throws IllegalStateException if it is called in an invalid state 501 */ 502 public void setDataSource(String path, Map<String, String> headers) 503 throws IOException, IllegalArgumentException, SecurityException, IllegalStateException { 504 if (headers != null && !headers.isEmpty()) { 505 StringBuilder sb = new StringBuilder(); 506 for (Map.Entry<String, String> entry : headers.entrySet()) { 507 sb.append(entry.getKey()); 508 sb.append(": "); 509 String value = entry.getValue(); 510 if (!TextUtils.isEmpty(value)) { 511 sb.append(entry.getValue()); 512 } 513 sb.append("\r\n"); 514 } 515 mCustomisedHeaders = sb.toString(); 516 setOption(OPT_CATEGORY_FORMAT, "headers", mCustomisedHeaders); 517 } else { 518 mCustomisedHeaders = null; 519 } 520 521 setDataSource(path); 522 } 523 524 /** 525 * 设置播放源 (FileDescriptor). It is the caller's responsibility to close the file descriptor. It 526 * is safe to do so as soon as this call returns. 527 * 528 * @param fd the FileDescriptor for the file you want to play 529 * @throws IllegalStateException if it is called in an invalid state 530 */ 531 @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2) 532 @Override 533 public void setDataSource(FileDescriptor fd) throws IOException, IllegalArgumentException, IllegalStateException { 534 setupPlayer(); 535 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB_MR1) { 536 int native_fd = -1; 537 try { 538 Field f = fd.getClass().getDeclaredField("descriptor"); // NoSuchFieldException 539 f.setAccessible(true); 540 native_fd = f.getInt(fd); // IllegalAccessException 541 } catch (NoSuchFieldException e) { 542 throw new RuntimeException(e); 543 } catch (IllegalAccessException e) { 544 throw new RuntimeException(e); 545 } 546 _setDataSourceFd(native_fd); 547 } else { 548 ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(fd); 549 try { 550 _setDataSourceFd(pfd.getFd()); 551 } finally { 552 pfd.close(); 553 } 554 } 555 } 556 557 /** 558 * 设置播放源 (FileDescriptor). The FileDescriptor must be seekable (N.B. a LocalSocket is not 559 * seekable). It is the caller's responsibility to close the file descriptor. It is safe to do so as soon as this 560 * call returns. 561 * 562 * @param fd the FileDescriptor for the file you want to play 563 * @param offset the offset into the file where the data to be played starts, in bytes 564 * @param length the length in bytes of the data to be played 565 * @throws IllegalStateException if it is called in an invalid state 566 */ 567 private void setDataSource(FileDescriptor fd, long offset, long length) 568 throws IOException, IllegalArgumentException, IllegalStateException { 569 // FIXME: handle offset, length 570 setDataSource(fd); 571 } 572 573 public void setDataSource(IMediaDataSource mediaDataSource) 574 throws IllegalArgumentException, SecurityException, IllegalStateException { 575 setupPlayer(); 576 _setDataSource(mediaDataSource); 577 } 578 579 private native void _setDataSource(String path, String[] keys, String[] values) 580 throws IOException, IllegalArgumentException, SecurityException, IllegalStateException; 581 582 private native void _setDataSourceFd(int fd) 583 throws IOException, IllegalArgumentException, SecurityException, IllegalStateException; 584 585 private native void _setDataSource(IMediaDataSource mediaDataSource) 586 throws IllegalArgumentException, SecurityException, IllegalStateException; 587 588 /** 589 * 获取播放路径 590 * 591 * @return 592 */ 593 @Override 594 public String getDataSource() { 595 return mDataSource; 596 } 597 598 /** 599 * 异步准备,播放器仅支持异步准备。您可以在准备好后调用start来启动播放 600 * 601 * @throws IllegalStateException 602 */ 603 @Override 604 public void prepareAsync() throws IllegalStateException { 605 _prepareAsync(); 606 } 607 608 private native void _prepareAsync() throws IllegalStateException; 609 610 /** 611 * 启动播放 612 * 要求播放源已经准备好 613 * 614 * @throws IllegalStateException 615 */ 616 @Override 617 public void start() throws IllegalStateException { 618 stayAwake(true); 619 this.onResumeStat(); 620 _start(); 621 } 622 623 private native void _start() throws IllegalStateException; 624 625 /** 626 * 停止播放 627 * 628 * @throws IllegalStateException 629 */ 630 @Override 631 public void stop() throws IllegalStateException { 632 stayAwake(false); 633 this.onStopStat(0, 0); 634 this.onEndStat(); 635 _stop(); 636 } 637 638 private native void _stop() throws IllegalStateException; 639 640 /** 641 * 暂停播放 642 * 643 * @throws IllegalStateException 644 */ 645 @Override 646 public void pause() throws IllegalStateException { 647 stayAwake(false); 648 this.onPauseStat(); 649 _pause(); 650 } 651 652 private native void _pause() throws IllegalStateException; 653 654 /** 655 * 设置保持唤醒模式 656 * 657 * @param context 658 * @param mode 该模式为PowerManager.newWakeLock的接口参数 659 */ 660 @SuppressLint("Wakelock") 661 @Override 662 public void setWakeMode(Context context, int mode) { 663 boolean washeld = false; 664 if (mWakeLock != null) { 665 if (mWakeLock.isHeld()) { 666 washeld = true; 667 mWakeLock.release(); 668 } 669 mWakeLock = null; 670 } 671 672 PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 673 mWakeLock = pm.newWakeLock(mode | PowerManager.ON_AFTER_RELEASE, BDCloudMediaPlayer.class.getName()); 674 mWakeLock.setReferenceCounted(false); 675 if (washeld) { 676 mWakeLock.acquire(); 677 } 678 } 679 680 /** 681 * 设置播放时屏幕保持,仅在设置过SurfaceHolder时有效。 682 * 683 * @param screenOn 684 */ 685 @Override 686 public void setScreenOnWhilePlaying(boolean screenOn) { 687 if (mScreenOnWhilePlaying != screenOn) { 688 if (screenOn && mSurfaceHolder == null) { 689 DebugLog.w(TAG, "setScreenOnWhilePlaying(true) is ineffective without a SurfaceHolder"); 690 } 691 mScreenOnWhilePlaying = screenOn; 692 updateSurfaceScreenOn(); 693 } 694 } 695 696 @SuppressLint("Wakelock") 697 private void stayAwake(boolean awake) { 698 if (mWakeLock != null) { 699 if (awake && !mWakeLock.isHeld()) { 700 mWakeLock.acquire(); 701 } else if (!awake && mWakeLock.isHeld()) { 702 mWakeLock.release(); 703 } 704 } 705 mStayAwake = awake; 706 updateSurfaceScreenOn(); 707 } 708 709 private void updateSurfaceScreenOn() { 710 if (mSurfaceHolder != null) { 711 mSurfaceHolder.setKeepScreenOn(mScreenOnWhilePlaying && mStayAwake); 712 } 713 } 714 715 /** 716 * 获取音视频track的信息 717 * 718 * @return 719 */ 720 @Override 721 public BDCloudTrackInfo[] getTrackInfo() { 722 Bundle bundle = getMediaMeta(); 723 if (bundle == null) { 724 return null; 725 } 726 727 BDCloudMediaMeta mediaMeta = BDCloudMediaMeta.parse(bundle); 728 if (mediaMeta == null || mediaMeta.mStreams == null) { 729 return null; 730 } 731 732 ArrayList<BDCloudTrackInfo> trackInfos = new ArrayList<BDCloudTrackInfo>(); 733 for (BDCloudMediaMeta.BDCloudStreamMeta streamMeta : mediaMeta.mStreams) { 734 BDCloudTrackInfo trackInfo = new BDCloudTrackInfo(streamMeta); 735 if (streamMeta.mType.equalsIgnoreCase(BDCloudMediaMeta.IJKM_VAL_TYPE__VIDEO)) { 736 trackInfo.setTrackType(ITrackInfo.MEDIA_TRACK_TYPE_VIDEO); 737 } else if (streamMeta.mType.equalsIgnoreCase(BDCloudMediaMeta.IJKM_VAL_TYPE__AUDIO)) { 738 trackInfo.setTrackType(ITrackInfo.MEDIA_TRACK_TYPE_AUDIO); 739 } else if (streamMeta.mType.equalsIgnoreCase(BDCloudMediaMeta.IJKM_VAL_TYPE__TIMEDTEXT)) { 740 trackInfo.setTrackType(ITrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT); 741 } 742 trackInfos.add(trackInfo); 743 } 744 745 return trackInfos.toArray(new BDCloudTrackInfo[trackInfos.size()]); 746 } 747 748 // TODO: @Override 749 public int getSelectedTrack(int trackType) { 750 switch (trackType) { 751 case ITrackInfo.MEDIA_TRACK_TYPE_VIDEO: 752 return (int) _getPropertyLong(FFP_PROP_INT64_SELECTED_VIDEO_STREAM, -1); 753 case ITrackInfo.MEDIA_TRACK_TYPE_AUDIO: 754 return (int) _getPropertyLong(FFP_PROP_INT64_SELECTED_AUDIO_STREAM, -1); 755 case ITrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT: 756 return (int) _getPropertyLong(FFP_PROP_INT64_SELECTED_TIMEDTEXT_STREAM, -1); 757 default: 758 return -1; 759 } 760 } 761 762 // experimental, should set DEFAULT_MIN_FRAMES and MAX_MIN_FRAMES to 25 763 // TODO: @Override 764 public void selectTrack(int track) { 765 _setStreamSelected(track, true); 766 } 767 768 // experimental, should set DEFAULT_MIN_FRAMES and MAX_MIN_FRAMES to 25 769 // TODO: @Override 770 public void deselectTrack(int track) { 771 _setStreamSelected(track, false); 772 } 773 774 private native void _setStreamSelected(int stream, boolean select); 775 776 /** 777 * 获取视频宽度 778 * 779 * @return 780 */ 781 @Override 782 public int getVideoWidth() { 783 return mVideoWidth; 784 } 785 786 /** 787 * 获取视频高度 788 * 789 * @return 790 */ 791 @Override 792 public int getVideoHeight() { 793 return mVideoHeight; 794 } 795 796 /** 797 * 获取采样纵横比的分子 798 * 799 * @return 800 */ 801 @Override 802 public int getVideoSarNum() { 803 return mVideoSarNum; 804 } 805 806 /** 807 * 获取采样纵横比的分母 808 * 809 * @return 810 */ 811 @Override 812 public int getVideoSarDen() { 813 return mVideoSarDen; 814 } 815 816 /** 817 * 是否正在播放 818 * 819 * @return 820 */ 821 @Override 822 public native boolean isPlaying(); 823 824 /** 825 * 快速切换到某个时间点进行播放 826 * 827 * @param msec 828 * @throws IllegalStateException 829 */ 830 @Override 831 public void seekTo(long msec) throws IllegalStateException { 832 this.onSeekToStat(this.getCurrentPosition() / 1000, msec / 1000); 833 _seekTo(msec); 834 } 835 836 private native void _seekTo(long msec) throws IllegalStateException; 837 838 /** 839 * 获取当前播放位置,单位为毫秒 840 * 841 * @return 842 */ 843 @Override 844 public native long getCurrentPosition(); 845 846 /** 847 * 获取音视频时长,单位为毫秒 848 * 849 * @return 850 */ 851 @Override 852 public native long getDuration(); 853 854 /** 855 * 释放资源 856 * Releases resources associated with this BDCloudMediaPlayer object. It is considered good practice to call this 857 * method when you're done using the BDCloudMediaPlayer. In particular, whenever an Activity of an application is 858 * paused (its onPause() method is called), or stopped (its onStop() method is called), this method should be 859 * invoked to release the BDCloudMediaPlayer object, unless the application has a special need to keep the object 860 * around. In addition to unnecessary resources (such as memory and instances of codecs) being held, failure to call 861 * this method immediately if a BDCloudMediaPlayer object is no longer needed may also lead to continuous battery 862 * consumption for mobile devices, and playback failure for other applications if no multiple instances of the same 863 * codec are supported on a device. Even if multiple instances of the same codec are supported, some performance 864 * degradation may be expected when unnecessary multiple instances are used at the same time. 865 */ 866 @Override 867 public void release() { 868 stayAwake(false); 869 this.onEndStat(); 870 updateSurfaceScreenOn(); 871 resetListeners(); 872 APMEventHandle.getInstance(appContext).release(); 873 _release(); 874 } 875 876 private native void _release(); 877 878 /** 879 * 重置,将状态重置为IDLE 880 * 重置后需重新设置播放源 881 */ 882 @Override 883 public void reset() { 884 stayAwake(false); 885 this.onEndStat(); 886 _stop(); 887 _reset(); 888 // make sure none of the listeners get called anymore 889 mEventHandler.removeCallbacksAndMessages(null); 890 891 mVideoWidth = 0; 892 mVideoHeight = 0; 893 } 894 895 private native void _reset(); 896 897 /** 898 * 设置是否循环播放 899 * 900 * @param looping whether to loop or not 901 */ 902 @Override 903 public void setLooping(boolean looping) { 904 int loopCount = looping ? 0 : 1; 905 setOption(OPT_CATEGORY_PLAYER, "loop", loopCount); 906 _setLoopCount(loopCount); 907 } 908 909 private native void _setLoopCount(int loopCount); 910 911 /** 912 * 是否循环播放 913 * 914 * @return true if the MediaPlayer is currently looping, false otherwise 915 */ 916 @Override 917 public boolean isLooping() { 918 int loopCount = _getLoopCount(); 919 return loopCount != 1; 920 } 921 922 private native int _getLoopCount(); 923 924 /** 925 * 设置播放速度,目前仅支持Android 6.0及以上版本 926 * 927 * @param speed 928 */ 929 @TargetApi(Build.VERSION_CODES.M) 930 public void setSpeed(float speed) { 931 if (speed == 0) { 932 return; 933 } 934 _setPropertyFloat(FFP_PROP_FLOAT_PLAYBACK_RATE, speed); 935 } 936 937 /** 938 * 获取播放速度,目前仅支持Android 6.0及以上版本 939 * 940 * @param speed 941 * @return 942 */ 943 @TargetApi(Build.VERSION_CODES.M) 944 public float getSpeed(float speed) { 945 return _getPropertyFloat(FFP_PROP_FLOAT_PLAYBACK_RATE, .0f); 946 } 947 948 /** 949 * 获取当前视频的decoder类型,1为软解;2为硬解 950 * 951 * @return 952 */ 953 public int getVideoDecoder() { 954 return (int) _getPropertyLong(FFP_PROP_INT64_VIDEO_DECODER, FFP_PROPV_DECODER_UNKNOWN); 955 } 956 957 /** 958 * 获取视频输出帧率 959 * 960 * @return 961 */ 962 public float getVideoOutputFramesPerSecond() { 963 return _getPropertyFloat(PROP_FLOAT_VIDEO_OUTPUT_FRAMES_PER_SECOND, 0.0f); 964 } 965 966 /** 967 * 获取视频解码帧率 968 * 969 * @return 970 */ 971 public float getVideoDecodeFramesPerSecond() { 972 return _getPropertyFloat(PROP_FLOAT_VIDEO_DECODE_FRAMES_PER_SECOND, 0.0f); 973 } 974 975 /** 976 * 获取视频已缓冲好的长度 977 * 978 * @return 979 */ 980 public long getVideoCachedDuration() { 981 return _getPropertyLong(FFP_PROP_INT64_VIDEO_CACHED_DURATION, 0); 982 } 983 984 /** 985 * 获取音频已缓冲好的长度 986 * 987 * @return 988 */ 989 public long getAudioCachedDuration() { 990 return _getPropertyLong(FFP_PROP_INT64_AUDIO_CACHED_DURATION, 0); 991 } 992 993 /** 994 * 获取视频已缓冲好的字节数 995 * 996 * @return 997 */ 998 public long getVideoCachedBytes() { 999 return _getPropertyLong(FFP_PROP_INT64_VIDEO_CACHED_BYTES, 0); 1000 } 1001 1002 /** 1003 * 获取音频已缓冲好的字节数 1004 * 1005 * @return 1006 */ 1007 public long getAudioCachedBytes() { 1008 return _getPropertyLong(FFP_PROP_INT64_AUDIO_CACHED_BYTES, 0); 1009 } 1010 1011 /** 1012 * 获取视频已缓冲好的包数 1013 * 1014 * @return 1015 */ 1016 public long getVideoCachedPackets() { 1017 return _getPropertyLong(FFP_PROP_INT64_VIDEO_CACHED_PACKETS, 0); 1018 } 1019 1020 /** 1021 * 获取音频已缓冲好的包数 1022 * 1023 * @return 1024 */ 1025 public long getAudioCachedPackets() { 1026 return _getPropertyLong(FFP_PROP_INT64_AUDIO_CACHED_PACKETS, 0); 1027 } 1028 1029 public long getAsyncStatisticBufBackwards() { 1030 return _getPropertyLong(FFP_PROP_INT64_ASYNC_STATISTIC_BUF_BACKWARDS, 0); 1031 } 1032 1033 public long getAsyncStatisticBufForwards() { 1034 return _getPropertyLong(FFP_PROP_INT64_ASYNC_STATISTIC_BUF_FORWARDS, 0); 1035 } 1036 1037 public long getAsyncStatisticBufCapacity() { 1038 return _getPropertyLong(FFP_PROP_INT64_ASYNC_STATISTIC_BUF_CAPACITY, 0); 1039 } 1040 1041 public long getTrafficStatisticByteCount() { 1042 return _getPropertyLong(FFP_PROP_INT64_TRAFFIC_STATISTIC_BYTE_COUNT, 0); 1043 } 1044 1045 public long getCacheStatisticPhysicalPos() { 1046 return _getPropertyLong(FFP_PROP_INT64_CACHE_STATISTIC_PHYSICAL_POS, 0); 1047 } 1048 1049 public long getCacheStatisticBufForwards() { 1050 return _getPropertyLong(FFP_PROP_INT64_CACHE_STATISTIC_BUF_FORWARDS, 0); 1051 } 1052 1053 public long getCacheStatisticFilePos() { 1054 return _getPropertyLong(FFP_PROP_INT64_CACHE_STATISTIC_FILE_POS, 0); 1055 } 1056 1057 public long getCacheStatisticCountBytes() { 1058 return _getPropertyLong(FFP_PROP_INT64_CACHE_STATISTIC_COUNT_BYTES, 0); 1059 } 1060 1061 public long getBitRate() { 1062 return _getPropertyLong(FFP_PROP_INT64_BIT_RATE, 0); 1063 } 1064 1065 /** 1066 * 获取网络下载速度 1067 * 1068 * @return 1069 */ 1070 public long getDownloadSpeed() { 1071 return _getPropertyLong(FFP_PROP_INT64_TCP_SPEED, 0); 1072 } 1073 1074 public long getSeekLoadDuration() { 1075 return _getPropertyLong(FFP_PROP_INT64_LATEST_SEEK_LOAD_DURATION, 0); 1076 } 1077 1078 private native float _getPropertyFloat(int property, float defaultValue); 1079 1080 private native void _setPropertyFloat(int property, float value); 1081 1082 private native long _getPropertyLong(int property, long defaultValue); 1083 1084 private native void _setPropertyLong(int property, long value); 1085 1086 /** 1087 * 设置左右声道的音量 1088 * 1089 * @param leftVolume 1090 * @param rightVolume 1091 */ 1092 @Override 1093 public native void setVolume(float leftVolume, float rightVolume); 1094 1095 @Override 1096 public native int getAudioSessionId(); 1097 1098 1099 /** 1100 * 获取sdk版本,形式为 xx.xx.xx 1101 * 1102 * @return 1103 */ 1104 public static String getSdkVersion() { 1105 return SDK_VERSION; 1106 } 1107 1108 /** 1109 * 获取媒体信息 1110 * 包含解码信息与音视频流信息 1111 * 1112 * @return 1113 */ 1114 @Override 1115 public MediaInfo getMediaInfo() { 1116 MediaInfo mediaInfo = new MediaInfo(); 1117 mediaInfo.mMediaPlayerName = "bdcloudplayer"; 1118 1119 String videoCodecInfo = _getVideoCodecInfo(); 1120 if (!TextUtils.isEmpty(videoCodecInfo)) { 1121 String nodes[] = videoCodecInfo.split(","); 1122 if (nodes.length >= 2) { 1123 mediaInfo.mVideoDecoder = nodes[0]; 1124 mediaInfo.mVideoDecoderImpl = nodes[1]; 1125 } else if (nodes.length >= 1) { 1126 mediaInfo.mVideoDecoder = nodes[0]; 1127 mediaInfo.mVideoDecoderImpl = ""; 1128 } 1129 } 1130 1131 String audioCodecInfo = _getAudioCodecInfo(); 1132 if (!TextUtils.isEmpty(audioCodecInfo)) { 1133 String nodes[] = audioCodecInfo.split(","); 1134 if (nodes.length >= 2) { 1135 mediaInfo.mAudioDecoder = nodes[0]; 1136 mediaInfo.mAudioDecoderImpl = nodes[1]; 1137 } else if (nodes.length >= 1) { 1138 mediaInfo.mAudioDecoder = nodes[0]; 1139 mediaInfo.mAudioDecoderImpl = ""; 1140 } 1141 } 1142 1143 try { 1144 mediaInfo.mMeta = BDCloudMediaMeta.parse(_getMediaMeta()); 1145 } catch (Throwable e) { 1146 e.printStackTrace(); 1147 } 1148 return mediaInfo; 1149 } 1150 1151 /** 1152 * 切换多分辨率 1153 * 需要首先调用getVariantInfo拿到数组 1154 * 1155 * @param index VariantInfo数组的下标。 1156 * @return 1157 */ 1158 public boolean selectResolutionByIndex(int index) { 1159 // the same time 1160 if (getCurrentVariantIndex() == index) { 1161 Log.d(TAG, "currentVariantIndex is equals to index setted~" + index); 1162 return false; 1163 } else if (index < 0 || index >= this.getVariantInfo().length) { 1164 Log.d(TAG, "index is not in [0," + this.getVariantInfo().length + ")"); 1165 return false; 1166 } 1167 long currentPosition = this.getCurrentPosition(); 1168 this.stop(); 1169 native_setup(); 1170 // option of category[OPT_CATEGORY_FORMAT] should be restored before preparing 1171 // setOption(OPT_CATEGORY_FORMAT, "auto_convert", 0); // for concat protocol only 1172 setOption(OPT_CATEGORY_FORMAT, "reconnect", 1); 1173 setOption(OPT_CATEGORY_FORMAT, "timeout", mNetworkTimeoutInUs); 1174 setOption(OPT_CATEGORY_FORMAT, "cache_dir", sEnableCache ? sCacheRootPath : ""); 1175 if (!TextUtils.isEmpty(mCustomisedHeaders)) { 1176 setOption(OPT_CATEGORY_FORMAT, "headers", mCustomisedHeaders); 1177 } 1178 this.selectVariantByIndex(index); 1179 setInitPlayPosition(currentPosition); 1180 this.prepareAsync(); 1181 return true; 1182 } 1183 1184 /** 1185 * 设置初始播放位置, 需在prepareAsync之前调用 1186 * 1187 * @param positionInMilliSeconds 1188 */ 1189 public void setInitPlayPosition(long positionInMilliSeconds) { 1190 this.setOption(OPT_CATEGORY_PLAYER, "seek-at-start", positionInMilliSeconds); 1191 } 1192 1193 /** 1194 * 默认:优先硬解,无法硬解时软解 1195 */ 1196 public static final int DECODE_AUTO = 0; 1197 /** 1198 * 设置为软解 1199 */ 1200 public static final int DECODE_SW = 1; 1201 1202 private int decodeMode = DECODE_AUTO; 1203 1204 /** 1205 * 设置软硬解模式,默认为auto模式(自动检测,优先硬解) 1206 * 1207 * @param mode 取值为DECODE_AUTO或DECODE_SW 1208 */ 1209 public void setDecodeMode(int mode) { 1210 if (mode >= 0 && mode <= 1) { 1211 decodeMode = mode; 1212 if (decodeMode == DECODE_AUTO) { 1213 this.setOption(BDCloudMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 1); 1214 this.setOption(BDCloudMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-all-videos", 1); 1215 this.setOption(BDCloudMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 1); 1216 this.setOption(BDCloudMediaPlayer.OPT_CATEGORY_PLAYER, 1217 "mediacodec-handle-resolution-change", 1); 1218 } else { 1219 this.setOption(BDCloudMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 0); 1220 this.setOption(BDCloudMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-all-videos", 0); 1221 this.setOption(BDCloudMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 0); 1222 this.setOption(BDCloudMediaPlayer.OPT_CATEGORY_PLAYER, 1223 "mediacodec-handle-resolution-change", 0); 1224 } 1225 } else { 1226 DebugLog.e(TAG, "decodeMode shoule be DECODE_AUTO or DECODE_SW"); 1227 } 1228 } 1229 1230 /** 1231 * 获取之前设置的解码模式 1232 * 1233 * @return 1234 */ 1235 public int getDecodeMode() { 1236 return decodeMode; 1237 } 1238 1239// /** 1240// * 设置是否为直播地址,播放器会自动优化配置。 1241// * @param isLiveUrl true表示为直播链接;false表示非直播链接 1242// */ 1243// public void setIsLiveUrl(boolean isLiveUrl) { 1244// if (isLiveUrl) { 1245// // infbuf在底层的逻辑有变 1246// setOption(BDCloudMediaPlayer.OPT_CATEGORY_PLAYER, "infbuf", 1); 1247// setOption(BDCloudMediaPlayer.OPT_CATEGORY_PLAYER, "packet-buffering", 0); 1248// } else { 1249// setOption(BDCloudMediaPlayer.OPT_CATEGORY_PLAYER, "infbuf", 0); 1250// setOption(BDCloudMediaPlayer.OPT_CATEGORY_PLAYER, "packet-buffering", 1); 1251// } 1252// 1253// } 1254 1255 private native String getCdnIp(); 1256 1257 /** 1258 * 获得当前多码率视频的index 1259 * 1260 * @return 1261 */ 1262 public native int getCurrentVariantIndex(); 1263 1264 /** 1265 * 设置多码率index。特注:播放过程中设置多码率请使用selectResolutionByIndex,该函数会帮您处理播放器状态。 1266 * 1267 * @param index 1268 */ 1269 public native void selectVariantByIndex(int index); 1270 1271 /** 1272 * DRM加密相关,一般不需设置 1273 * 1274 * @param id 1275 */ 1276 public native void setCustomizedPlayerIdForHLS(String id); 1277 1278 /** 1279 * DRM加密相关,一般不需设置 1280 * 1281 * @param key 1282 */ 1283 public native void setCustomizedPlayerKeyForHLS(String key); 1284 1285 /** 1286 * DRM加密-token加密,通过该接口设置token 1287 * 1288 * @param token 1289 */ 1290 public native void setDecryptTokenForHLS(String token); 1291 1292 protected native void setLocalDecryptKeyForHLS(String key); 1293 1294 /** 1295 * 设置 最大缓冲区长度 1296 * 1297 * @param size 1298 */ 1299 public void setMaxCacheSizeInBytes(int size) { 1300 maxCacheSizeForApm = size; 1301 this.setOption(OPT_CATEGORY_PLAYER, "max-buffer-size", size); 1302 } 1303 1304 /** 1305 * 设置 缓冲过程中,起播数据时长 1306 * 1307 * @param time 1308 */ 1309 public void setBufferTimeInMs(int time) { 1310 cachePauseTimeForApm = time; 1311 this.setOption(OPT_CATEGORY_PLAYER, "buffer-time-in-ms", time); 1312 } 1313 1314 /** 1315 * 设置 缓冲过程中,起播数据字节长度 1316 * 1317 * @param size 1318 */ 1319 public void setBufferSizeInBytes(int size) { 1320 this.setOption(OPT_CATEGORY_PLAYER, "buffer-size-in-bytes", size); 1321 } 1322 1323 /** 1324 * 设置probe(音视频格式探测)最大时长,单位是毫秒 1325 * 1326 * @param maxProbeTime 1327 */ 1328 public void setMaxProbeTime(int maxProbeTime) { 1329 firstBufferingTimeForApm = maxProbeTime; 1330 this.setOption(OPT_CATEGORY_PLAYER, "max-probe-time", maxProbeTime); 1331 } 1332 1333 /** 1334 * 设置probe(音视频格式探测)最大数据大小 1335 * 1336 * @param maxProbeSize 1337 */ 1338 public void setMaxProbeSize(int maxProbeSize) { 1339 this.setOption(OPT_CATEGORY_PLAYER, "max-probe-size", maxProbeSize); 1340 } 1341 1342 /** 1343 * Setting timeout threshold of network connecting and reading process 1344 * 1345 * @param timeout timeout in us 1346 */ 1347 public void setTimeoutInUs(int timeout) { 1348 mNetworkTimeoutInUs = timeout > 0 ? timeout : mNetworkTimeoutInUs; 1349 this.setOption(OPT_CATEGORY_FORMAT, "timeout", mNetworkTimeoutInUs); 1350 } 1351 1352 /** 1353 * 设置是否开启追帧播放功能 1354 * 1355 * @param isEnable 是否开启 1356 */ 1357 public void toggleFrameChasing(boolean isEnable) { 1358 toggleFrameChasingForApm = isEnable; 1359 this.setOption(OPT_CATEGORY_PLAYER, "framechasing", isEnable ? 1 : 0); 1360 } 1361 1362 /** 1363 * 获得多码率视频的各码率信息 1364 * 1365 * @return 1366 */ 1367 public native String[] getVariantInfo(); 1368 1369 /** 1370 * 是否显示debug消息 1371 * 1372 * @param enable 当为false时,将仅显示warn级别及以上的消息; 1373 */ 1374 @Override 1375 public void setLogEnabled(boolean enable) { 1376 1377 if (enable) { 1378 native_setLogLevel(IJK_LOG_DEFAULT); 1379 DebugLog.setDebugLogEnabled(true); 1380 } else { 1381 native_setLogLevel(IJK_LOG_WARN); 1382 DebugLog.setDebugLogEnabled(false); 1383 } 1384 } 1385 1386 /** 1387 * 是否可播放,当前始终为true 1388 * 1389 * @return 1390 */ 1391 @Override 1392 public boolean isPlayable() { 1393 return true; 1394 } 1395 1396 private native String _getVideoCodecInfo(); 1397 1398 private native String _getAudioCodecInfo(); 1399 1400 /** 1401 * 设置额外信息,一般不需设置 1402 * 1403 * @param category 1404 * @param name 1405 * @param value 1406 */ 1407 public void setOption(int category, String name, String value) { 1408 _setOption(category, name, value); 1409 } 1410 1411 /** 1412 * 设置额外信息,一般不需设置 1413 * 1414 * @param category 1415 * @param name 1416 * @param value 1417 */ 1418 public void setOption(int category, String name, long value) { 1419 _setOption(category, name, value); 1420 } 1421 1422 private native void _setOption(int category, String name, String value); 1423 1424 private native void _setOption(int category, String name, long value); 1425 1426 public Bundle getMediaMeta() { 1427 return _getMediaMeta(); 1428 } 1429 1430 private native Bundle _getMediaMeta(); 1431 1432 public static String getColorFormatName(int mediaCodecColorFormat) { 1433 return _getColorFormatName(mediaCodecColorFormat); 1434 } 1435 1436 private static native String _getColorFormatName(int mediaCodecColorFormat); 1437 1438 /** 1439 * 当前该设置不生效 1440 * 1441 * @param streamtype 1442 */ 1443 @Override 1444 public void setAudioStreamType(int streamtype) { 1445 // do nothing 1446 } 1447 1448 /** 1449 * 当前该设置不生效 1450 * 1451 * @param keepInBackground 1452 */ 1453 @Override 1454 public void setKeepInBackground(boolean keepInBackground) { 1455 // do nothing 1456 } 1457 1458 private native void native_init(Object BDCloudMediaPlayer_this); 1459 1460 private native void native_setup(); 1461 1462 private native void native_finalize(); 1463 1464 private native void native_message_loop(Object BDCloudMediaPlayer_this); 1465 1466 protected void finalize() throws Throwable { 1467 super.finalize(); 1468 native_finalize(); 1469 } 1470 1471 protected void onErrorStat(int what, int extra) { 1472 try { 1473 APMEventHandle.getInstance(appContext).onPlayEnd(what, extra, 1); 1474 } catch (Exception e) { 1475 Log.d(TAG, "APMEventHandle exception:" + e.getMessage()); 1476 } 1477 } 1478 1479 protected void onCompleteStat(int what, int extra) { 1480 APMEventHandle.getInstance(appContext).onPlayEnd(what, extra, 2); 1481 } 1482 1483 protected void onStopStat(int what, int extra) { 1484 APMEventHandle.getInstance(appContext).onPlayEnd(what, extra, 3); 1485 } 1486 1487 /** 1488 * when play completeOronErrorOrStopOrRelease mediaplayer, 但不能多发 1489 */ 1490 protected void onEndStat() { 1491 // deal with complete/stop/reset/release multi Come In 1492 if (isEndSended) { 1493 return; 1494 } 1495 isEndSended = true; 1496 // add rest statis 1497 try { 1498 String strBufferStat = null; 1499 if (bufferStatJsonArray != null) { 1500 strBufferStat = bufferStatJsonArray.toString(); 1501 bufferStatJsonArray = new JSONArray(); 1502 } 1503 1504 if (apmPlayingTimer != null) { 1505 apmPlayingTimer.cancel(); 1506 apmPlayingTimer = null; 1507 } 1508 1509 if (lastStartPlayTime != 0) { 1510 int interval = (int) (System.currentTimeMillis() - lastStartPlayTime) / 1000; 1511 lastStartPlayTime = 0; 1512 this.stayInterval += interval; 1513 } 1514 int playInterval = this.stayInterval; 1515 JSONObject extra = new JSONObject(); 1516 extra.put("playInterval", playInterval); 1517 extra.put("buffering", new JSONArray(strBufferStat)); 1518 APMEventHandle.getInstance(appContext).onUserPlayEnd(extra); 1519 this.stayInterval = 0; 1520 } catch (Exception e) { 1521 Log.d(TAG, "APMEventHandle exception:" + e.getMessage()); 1522 } 1523 1524 } 1525 1526// /** 1527// * stop or release? 1528// */ 1529// protected void onStopStat() { 1530// if (apmPlayingTimer!= null) { 1531// apmPlayingTimer.cancel(); 1532// apmPlayingTimer = null; 1533// } 1534// } 1535 1536 protected void onInitingStat() { 1537 lastStartPlayTime = System.currentTimeMillis(); 1538 // start a session 1539 startPrepareTimeForApm = System.currentTimeMillis(); 1540 APMEventHandle.getInstance(appContext).updateSession(this.mDataSource); 1541 APMEventHandle.getInstance(appContext).setPlayerRelated(SDK_VERSION, "hw", mAK); 1542 } 1543 1544 protected void onDnsStat(int duration) { 1545 APMEventHandle.getInstance(appContext).onDnsParsed(duration); 1546 } 1547 1548 protected void onHttpStat(long duration) { 1549 APMEventHandle.getInstance(appContext).onHttpConnected(duration); 1550 } 1551 1552 protected void onPauseStat() { 1553 if (lastStartPlayTime != 0) { 1554 int interval = (int) (System.currentTimeMillis() - lastStartPlayTime) / 1000; 1555 lastStartPlayTime = 0; 1556 this.stayInterval += interval; 1557 } 1558 // add rest statis 1559 try { 1560 if (apmPlayingTimer != null) { 1561 apmPlayingTimer.cancel(); 1562 apmPlayingTimer = null; 1563 } 1564 APMEventHandle.getInstance(appContext).onUserPause(getCurrentPosition() / 1000f); 1565 } catch (Exception e) { 1566 Log.d(TAG, "APMEventHandle exception:" + e.getMessage()); 1567 } 1568 } 1569 1570 protected void onResumeStat() { 1571 isEndSended = false; 1572 if (lastStartPlayTime == 0L) { 1573 lastStartPlayTime = System.currentTimeMillis(); 1574 } 1575 try { 1576 if (apmPlayingTimer != null) { 1577 apmPlayingTimer.cancel(); 1578 apmPlayingTimer = null; 1579 } 1580 apmPlayingTimer = new Timer(); 1581 apmPlayingTimer.schedule(new TimerTask() { 1582 @Override 1583 public void run() { 1584 try { 1585 APMEventHandle.getInstance(appContext).onPlayCount(); 1586 long speed = getDownloadSpeed(); 1587 if (speed > 0) { 1588 APMEventHandle.getInstance(appContext).onNetworkSpeedReport((int) speed); 1589 } 1590 } catch (Exception e) { 1591 Log.d(TAG, "APMEventHandle exception:" + e.getMessage()); 1592 } 1593 } 1594 }, 0, 60 * 1000); 1595 } catch (Exception e) { 1596 Log.d(TAG, "" + e.getMessage()); 1597 } 1598 } 1599 1600 protected void onSeekToStat(long curPositionInSeconds, long seekToInSeconds) { 1601 // add rest statis 1602 try { 1603 JSONObject extra = new JSONObject(); 1604 extra.put("from", curPositionInSeconds); 1605 extra.put("to", seekToInSeconds); 1606 APMEventHandle.getInstance(appContext).onUserSeek(extra); 1607 } catch (Exception e) { 1608 Log.d(TAG, "APMEventHandle exception:" + e.getMessage()); 1609 } 1610 } 1611 1612 protected void onPreparedStat() { 1613 // add rest statis 1614 try { 1615 if (startPrepareTimeForApm > 0L) { 1616 APMEventHandle.getInstance(appContext).onFirstBufferEnd((long) (System.currentTimeMillis() 1617 - startPrepareTimeForApm)); 1618 startPrepareTimeForApm = 0L; 1619 } 1620 // get cdn-ip 1621 String cdnIp = this.getCdnIp(); 1622 if (cdnIp != null && !cdnIp.equals("")) { 1623 APMEventHandle.getInstance(appContext).onUpdateCdn(cdnIp); 1624 } 1625 JSONObject extra = new JSONObject(); 1626 extra.put("videoUrl", this.mDataSource); 1627 extra.put("videoWidth", this.getVideoWidth()); 1628 extra.put("videoHeight", this.getVideoHeight()); 1629 extra.put("playerWidth", 0); // param is deprecated;new sdk is target for mediaplayer, not view 1630 extra.put("playerHeight", 0); // param is deprecated;new sdk is target for mediaplayer, not view 1631 extra.put("duration", this.getDuration()); 1632 JSONObject settings = new JSONObject(); 1633 settings.put("maxCacheSize", maxCacheSizeForApm); 1634 settings.put("cachePauseTime", cachePauseTimeForApm); 1635 settings.put("firstBufferingTime", firstBufferingTimeForApm); 1636 settings.put("toggleFrameChasing", toggleFrameChasingForApm); 1637 settings.put("decodeMode", decodeMode); 1638 settings.put("playbackRate", getSpeed(0f)); 1639 settings.put("enableLooping", isLooping()); 1640 extra.put("settings", settings); 1641 APMEventHandle.getInstance(appContext).onUserPlay(extra); 1642 } catch (Exception e) { 1643 Log.d(TAG, "APMEventHandle exception:" + e.getMessage()); 1644 } 1645 } 1646 1647 protected void onMetadataStat(String playId) { 1648 APMEventHandle.getInstance(appContext).onUpdateMetadata(playId); 1649 } 1650 1651 protected void onInfoStat(int what, int extra) { 1652 try { 1653 if (what == IMediaPlayer.MEDIA_INFO_BUFFERING_START) { 1654 latestBufferStartTime = new Date(); 1655 bufferingStartPosition = getCurrentPosition(); 1656 1657 APMEventHandle.getInstance(appContext).onBufferingStart(); 1658 } else if (what == IMediaPlayer.MEDIA_INFO_BUFFERING_END) { 1659 APMEventHandle.getInstance(appContext).onBufferingEnd(); 1660 1661 if (this.latestBufferStartTime != null) { 1662 Date endTime = new Date(); 1663 JSONObject json = new JSONObject(); 1664 json.put("startPosition", bufferingStartPosition / 1000f); 1665 json.put("endPosition", getCurrentPosition() / 1000f); 1666 json.put("startTimestamp", latestBufferStartTime.getTime()); 1667 json.put("endTimestamp", endTime.getTime()); 1668 bufferStatJsonArray.put(json); 1669 latestBufferStartTime = null; 1670 bufferingStartPosition = -1; 1671 } 1672 } else if (what == IMediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START) { 1673 APMEventHandle.getInstance(appContext).onFirstFrameRendered(lastStartPlayTime); 1674 } else if (what == IMediaPlayer.MEDIA_INFO_FRAMECHASING_START) { 1675 APMEventHandle.getInstance(appContext).onFrameChasingStart(); 1676 } else if (what == IMediaPlayer.MEDIA_INFO_FRAMECHASING_END) { 1677 APMEventHandle.getInstance(appContext).onFrameCharingEnd(); 1678 } 1679 } catch (Exception e) { 1680 Log.d(TAG, "buffer stat exception :" + e.getMessage()); 1681 } 1682 } 1683 1684 private static class EventHandler extends Handler { 1685 private final WeakReference<BDCloudMediaPlayer> mWeakPlayer; 1686 1687 public EventHandler(BDCloudMediaPlayer mp, Looper looper) { 1688 super(looper); 1689 mWeakPlayer = new WeakReference<BDCloudMediaPlayer>(mp); 1690 } 1691 1692 @Override 1693 public void handleMessage(Message msg) { 1694 BDCloudMediaPlayer player = mWeakPlayer.get(); 1695 if (player == null || player.mNativeMediaPlayer == 0) { 1696 DebugLog.w(TAG, "BDCloudMediaPlayer went away with unhandled events"); 1697 return; 1698 } 1699 1700 switch (msg.what) { 1701 case MEDIA_PREPARED: 1702 // selectResolutionByIndex may change option seek-at-start, reset here 1703 player.setOption(OPT_CATEGORY_PLAYER, "seek-at-start", 0); 1704 player.onPreparedStat(); 1705 player.notifyOnPrepared(); 1706 return; 1707 1708 case MEDIA_PLAYBACK_COMPLETE: 1709 player.stayAwake(false); 1710 player.onCompleteStat(msg.arg1, msg.arg2); 1711 player.onEndStat(); 1712 player.notifyOnCompletion(); 1713 return; 1714 1715 case MEDIA_BUFFERING_UPDATE: 1716 long bufferPosition = msg.arg1; 1717 if (bufferPosition < 0) { 1718 bufferPosition = 0; 1719 } 1720 1721 long percent = 0; 1722 long duration = player.getDuration(); 1723 if (duration > 0 ) { 1724 if((duration - bufferPosition) >= 1000) { 1725 percent = bufferPosition * 100 / duration; 1726 } else { 1727 percent = (bufferPosition + 1000) * 100 /duration; 1728 } 1729 } 1730 1731 if (percent >= 100) { 1732 percent = 100; 1733 } 1734 1735 // DebugLog.efmt(TAG, "Buffer (%d%%) %d/%d", percent, 1736 // bufferPosition, duration); 1737 player.notifyOnBufferingUpdate((int) percent); 1738 return; 1739 1740 case MEDIA_SEEK_COMPLETE: 1741 player.notifyOnSeekComplete(); 1742 return; 1743 1744 case MEDIA_SET_VIDEO_SIZE: 1745 player.mVideoWidth = msg.arg1; 1746 player.mVideoHeight = msg.arg2; 1747 player.notifyOnVideoSizeChanged(player.mVideoWidth, player.mVideoHeight, player.mVideoSarNum, 1748 player.mVideoSarDen); 1749 return; 1750 1751 case MEDIA_ERROR: 1752 DebugLog.e(TAG, "Error (" + msg.arg1 + "," + msg.arg2 + ")"); 1753 player.onErrorStat(msg.arg1, msg.arg2); 1754 player.onEndStat(); 1755 if (!player.notifyOnError(msg.arg1, msg.arg2)) { 1756 player.notifyOnCompletion(); 1757 } 1758 player.stayAwake(false); 1759 return; 1760 case MEDIA_PLAYBACK_STOPPED: 1761 return; 1762 case MEDIA_INFO: 1763 switch (msg.arg1) { 1764 case MEDIA_INFO_VIDEO_RENDERING_START: 1765 DebugLog.i(TAG, "Info: MEDIA_INFO_VIDEO_RENDERING_START\n"); 1766 break; 1767 } 1768 player.onInfoStat(msg.arg1, msg.arg2); 1769 player.notifyOnInfo(msg.arg1, msg.arg2); 1770 // No real default action so far. 1771 return; 1772 case MEDIA_TIMED_TEXT: 1773 if (msg.obj == null) { 1774 player.notifyOnTimedText(null); 1775 } else { 1776 BDTimedText text = new BDTimedText(new Rect(0, 0, 1, 1), (String) msg.obj); 1777 player.notifyOnTimedText(text); 1778 } 1779 return; 1780 case MEDIA_NOP: // interface test message - ignore 1781 break; 1782 1783 case MEDIA_SET_VIDEO_SAR: 1784 player.mVideoSarNum = msg.arg1; 1785 player.mVideoSarDen = msg.arg2; 1786 player.notifyOnVideoSizeChanged(player.mVideoWidth, player.mVideoHeight, player.mVideoSarNum, 1787 player.mVideoSarDen); 1788 break; 1789 1790 case AVAPP_EVENT_METADATA: 1791 Bundle meta = (Bundle) msg.obj; 1792 player.notifyOnMetadata(meta); 1793 for (String key : meta.keySet()) { 1794 if(key.equals("playID") && !(meta.get(key).equals(""))) { 1795 player.onMetadataStat(meta.getString(key)); 1796 playId = meta.getString(key); 1797 } 1798 } 1799 break; 1800 case AVAPP_EVENT_DNS: 1801 player.onDnsStat(msg.arg1); 1802 break; 1803 case AVAPP_EVENT_HTTP_CONNECT_END: 1804 player.onHttpStat(msg.arg1); 1805 break; 1806 1807 default: 1808 DebugLog.e(TAG, "Unknown message type " + msg.what); 1809 } 1810 } 1811 } 1812 1813 /* 1814 * Called from native code when an interesting event happens. This method just uses the EventHandler system to post 1815 * the event back to the main app thread. We use a weak reference to the original BDCloudMediaPlayer object so that 1816 * the native code is safe from the object disappearing from underneath it. (This is the cookie passed to 1817 * native_setup().) 1818 */ 1819 @CalledByNative 1820 private static void postEventFromNative(Object weakThiz, int what, int arg1, int arg2, Object obj) { 1821 if (weakThiz == null) { 1822 return; 1823 } 1824 1825 @SuppressWarnings("rawtypes") 1826 BDCloudMediaPlayer mp = (BDCloudMediaPlayer) ((WeakReference) weakThiz).get(); 1827 if (mp == null) { 1828 return; 1829 } 1830 1831 if (what == MEDIA_INFO && arg1 == MEDIA_INFO_STARTED_AS_NEXT) { 1832 // this acquires the wakelock if needed, and sets the client side 1833 // state 1834 mp.start(); 1835 } 1836 if (mp.mEventHandler != null) { 1837 Message m = mp.mEventHandler.obtainMessage(what, arg1, arg2, obj); 1838 mp.mEventHandler.sendMessage(m); 1839 } 1840 } 1841 1842 /* 1843 * ControlMessage 1844 */ 1845 1846 private OnControlMessageListener mOnControlMessageListener; 1847 1848 private void setOnControlMessageListener(OnControlMessageListener listener) { 1849 mOnControlMessageListener = listener; 1850 } 1851 1852 private interface OnControlMessageListener { 1853 String onControlResolveSegmentUrl(int segment); 1854 } 1855 1856 /* 1857 * NativeInvoke 1858 */ 1859 1860 private OnNativeInvokeListener mOnNativeInvokeListener; 1861 1862 private void setOnNativeInvokeListener(OnNativeInvokeListener listener) { 1863 mOnNativeInvokeListener = listener; 1864 } 1865 1866 private interface OnNativeInvokeListener { 1867 1868 int CTRL_WILL_TCP_OPEN = 0x20001; // NO ARGS 1869 int CTRL_DID_TCP_OPEN = 0x20002; // ARG_ERROR, ARG_FAMILIY, ARG_IP, 1870 // ARG_PORT, ARG_FD 1871 1872 int CTRL_WILL_HTTP_OPEN = 0x20003; // ARG_URL, ARG_SEGMENT_INDEX, 1873 // ARG_RETRY_COUNTER 1874 int CTRL_WILL_LIVE_OPEN = 0x20005; // ARG_URL, ARG_RETRY_COUNTER 1875 int CTRL_WILL_CONCAT_RESOLVE_SEGMENT = 0x20007; // ARG_URL, 1876 // ARG_SEGMENT_INDEX, 1877 // ARG_RETRY_COUNTER 1878 1879 int EVENT_WILL_HTTP_OPEN = 0x1; // ARG_URL 1880 int EVENT_DID_HTTP_OPEN = 0x2; // ARG_URL, ARG_ERROR, ARG_HTTP_CODE 1881 int EVENT_WILL_HTTP_SEEK = 0x3; // ARG_URL, ARG_OFFSET 1882 int EVENT_DID_HTTP_SEEK = 0x4; // ARG_URL, ARG_OFFSET, ARG_ERROR, 1883 // ARG_HTTP_CODE 1884 1885 String ARG_URL = "url"; 1886 String ARG_SEGMENT_INDEX = "segment_index"; 1887 String ARG_RETRY_COUNTER = "retry_counter"; 1888 1889 String ARG_ERROR = "error"; 1890 String ARG_FAMILIY = "family"; 1891 String ARG_IP = "ip"; 1892 String ARG_PORT = "port"; 1893 String ARG_FD = "fd"; 1894 1895 String ARG_OFFSET = "offset"; 1896 String ARG_HTTP_CODE = "http_code"; 1897 1898 /* 1899 * @return true if invoke is handled 1900 * 1901 * @throws Exception on any error 1902 */ 1903 boolean onNativeInvoke(int what, Bundle args); 1904 } 1905 1906 @CalledByNative 1907 private static boolean onNativeInvoke(Object weakThiz, int what, Bundle args) { 1908 DebugLog.ifmt(TAG, "onNativeInvoke %x", what); 1909 if (weakThiz == null || !(weakThiz instanceof WeakReference<?>)) { 1910 throw new IllegalStateException("<null weakThiz>.onNativeInvoke()"); 1911 } 1912 1913 @SuppressWarnings("unchecked") 1914 WeakReference<BDCloudMediaPlayer> weakPlayer = (WeakReference<BDCloudMediaPlayer>) weakThiz; 1915 BDCloudMediaPlayer player = weakPlayer.get(); 1916 if (player == null) { 1917 throw new IllegalStateException("<null weakPlayer>.onNativeInvoke()"); 1918 } 1919 1920 OnNativeInvokeListener listener = player.mOnNativeInvokeListener; 1921 if (listener != null && listener.onNativeInvoke(what, args)) { 1922 return true; 1923 } 1924 1925 switch (what) { 1926 case OnNativeInvokeListener.CTRL_WILL_CONCAT_RESOLVE_SEGMENT: { 1927 OnControlMessageListener onControlMessageListener = player.mOnControlMessageListener; 1928 if (onControlMessageListener == null) { 1929 return false; 1930 } 1931 1932 int segmentIndex = args.getInt(OnNativeInvokeListener.ARG_SEGMENT_INDEX, -1); 1933 if (segmentIndex < 0) { 1934 throw new InvalidParameterException("onNativeInvoke(invalid segment index)"); 1935 } 1936 1937 String newUrl = onControlMessageListener.onControlResolveSegmentUrl(segmentIndex); 1938 if (newUrl == null) { 1939 throw new RuntimeException(new IOException("onNativeInvoke() = <NULL newUrl>")); 1940 } 1941 1942 args.putString(OnNativeInvokeListener.ARG_URL, newUrl); 1943 return true; 1944 } 1945 default: 1946 return false; 1947 } 1948 } 1949 1950 /** 1951 * 自己指定codec,一般不需要设置 1952 */ 1953 private interface OnMediaCodecSelectListener { 1954 String onMediaCodecSelect(IMediaPlayer mp, String mimeType, int profile, int level); 1955 } 1956 1957 private OnMediaCodecSelectListener mOnMediaCodecSelectListener; 1958 1959 /** 1960 * 设置codec解码器,一般不需要设置 1961 * 1962 * @param listener 1963 */ 1964 private void setOnMediaCodecSelectListener(OnMediaCodecSelectListener listener) { 1965 mOnMediaCodecSelectListener = listener; 1966 } 1967 1968 public void resetListeners() { 1969 super.resetListeners(); 1970 mOnMediaCodecSelectListener = null; 1971 } 1972 1973 private boolean isLocalFilePath(String strPath) { 1974 1975 if (null == strPath) { 1976 return false; 1977 } 1978 1979 if (strPath.startsWith("file://") || strPath.startsWith("/")) { 1980 return true; 1981 } 1982 1983 return false; 1984 } 1985 1986 @CalledByNative 1987 private static String onSelectCodec(Object weakThiz, String mimeType, int profile, int level) { 1988 if (weakThiz == null || !(weakThiz instanceof WeakReference<?>)) { 1989 return null; 1990 } 1991 1992 @SuppressWarnings("unchecked") 1993 WeakReference<BDCloudMediaPlayer> weakPlayer = (WeakReference<BDCloudMediaPlayer>) weakThiz; 1994 BDCloudMediaPlayer player = weakPlayer.get(); 1995 if (player == null) { 1996 return null; 1997 } 1998 1999 OnMediaCodecSelectListener listener = player.mOnMediaCodecSelectListener; 2000 if (listener == null) { 2001 listener = DefaultMediaCodecSelector.sInstance; 2002 } 2003 2004 return listener.onMediaCodecSelect(player, mimeType, profile, level); 2005 } 2006 2007 private static class DefaultMediaCodecSelector implements OnMediaCodecSelectListener { 2008 public static final DefaultMediaCodecSelector sInstance = new DefaultMediaCodecSelector(); 2009 2010 @SuppressWarnings("deprecation") 2011 @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 2012 public String onMediaCodecSelect(IMediaPlayer mp, String mimeType, int profile, int level) { 2013 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { 2014 return null; 2015 } 2016 2017 if (TextUtils.isEmpty(mimeType)) { 2018 return null; 2019 } 2020 2021 DebugLog.i(TAG, 2022 String.format(Locale.US, "onSelectCodec: mime=%s, profile=%d, level=%d", mimeType, profile, level)); 2023 ArrayList<BDCloudMediaCodecInfo> candidateCodecList = new ArrayList<BDCloudMediaCodecInfo>(); 2024 int numCodecs = MediaCodecList.getCodecCount(); 2025 for (int i = 0; i < numCodecs; i++) { 2026 MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i); 2027 DebugLog.d(TAG, String.format(Locale.US, " found codec: %s", codecInfo.getName())); 2028 if (codecInfo.isEncoder()) { 2029 continue; 2030 } 2031 2032 String[] types = codecInfo.getSupportedTypes(); 2033 if (types == null) { 2034 continue; 2035 } 2036 2037 for (String type : types) { 2038 if (TextUtils.isEmpty(type)) { 2039 continue; 2040 } 2041 2042 DebugLog.d(TAG, String.format(Locale.US, " mime: %s", type)); 2043 if (!type.equalsIgnoreCase(mimeType)) { 2044 continue; 2045 } 2046 2047 BDCloudMediaCodecInfo candidate = BDCloudMediaCodecInfo.setupCandidate(codecInfo, mimeType); 2048 if (candidate == null) { 2049 continue; 2050 } 2051 2052 candidateCodecList.add(candidate); 2053 DebugLog.i(TAG, String.format(Locale.US, "candidate codec: %s rank=%d", codecInfo.getName(), 2054 candidate.mRank)); 2055 candidate.dumpProfileLevels(mimeType); 2056 } 2057 } 2058 2059 if (candidateCodecList.isEmpty()) { 2060 return null; 2061 } 2062 2063 BDCloudMediaCodecInfo bestCodec = candidateCodecList.get(0); 2064 2065 for (BDCloudMediaCodecInfo codec : candidateCodecList) { 2066 if (codec.mRank > bestCodec.mRank) { 2067 bestCodec = codec; 2068 } 2069 } 2070 2071 if (bestCodec.mRank < BDCloudMediaCodecInfo.RANK_LAST_CHANCE) { 2072 Log.w(TAG, String.format(Locale.US, "unaccetable codec: %s", bestCodec.mCodecInfo.getName())); 2073 return null; 2074 } 2075 2076 DebugLog.i(TAG, String.format(Locale.US, "selected codec: %s rank=%d", bestCodec.mCodecInfo.getName(), 2077 bestCodec.mRank)); 2078 return bestCodec.mCodecInfo.getName(); 2079 } 2080 } 2081 2082 private static native void native_profileBegin(String libName); 2083 2084 private static native void native_profileEnd(); 2085 2086 private static native void native_setLogLevel(int level); 2087}