ijkplayer打造完美视频播放器

末蓝、 2022-02-01 13:25 808阅读 0赞

1.ijkplayer简介

ijkplayer是一个基于FFmpeg的轻量级Android/iOS视频播放器。FFmpeg的是全球领先的多媒体框架,能够解码,编码, 转码,复用,解复用,流,过滤器和播放大部分的视频格式。它提供了录制、转换以及流化音视频的完整解决方案。它包含了非常先进的音频/视频编解码库libavcodec,为了保证高可移植性和编解码质量,libavcodec里很多code都是从头开发的。

2.功能的展示

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3drX2JlaWNhaQ_size_16_color_FFFFFF_t_70 watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3drX2JlaWNhaQ_size_16_color_FFFFFF_t_70 1 watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3drX2JlaWNhaQ_size_16_color_FFFFFF_t_70 2

支持横竖屏切换展示,上下滑动声音亮度的调节等功能。

3. androidStudio 接入

1)添加.so库:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3drX2JlaWNhaQ_size_16_color_FFFFFF_t_70 3

如图所示:将.so文件添加到自己的项目中:

2)在Gradle中添加依赖:

20190514163824850.png

  1. // Bili required, enough for most devices.
  2. compile 'tv.danmaku.ijk.media:ijkplayer-java:0.8.8'
  3. compile 'pub.devrel:easypermissions:1.0.1'

3)开始代码的实现

1.创建一个类CusIjkView,继承frameLayout,(自定义播放器样式)

  1. 代码如下:
  2. // 打印日志的TAG
  3. private String TAG = this.getClass().getSimpleName();
  4. // mCurrentState is a object's current state.
  5. private int mCurrentState = PlayStateParams.STATE_IDLE;
  6. // mTargetState is the state that a method caller intends to reach.
  7. // For instance, regardless the VideoView object's current state, calling pause()
  8. // intends to bring the object to a target state of STATE_PAUSED.
  9. private int mTargetState = PlayStateParams.STATE_IDLE;
  10. // 播放地址通过客户端可设置
  11. private Uri mUri;
  12. // 播放器的一些基本配置
  13. private Map<String, String> mHeaders;
  14. // 所有我们需要播放和显示视频的东西
  15. private IRenderView.ISurfaceHolder mSurfaceHolder = null;
  16. // 视频宽度
  17. private int mVideoWidth;
  18. // 视频高度
  19. private int mVideoHeight;
  20. // 窗口宽度
  21. private int mSurfaceWidth;
  22. // 窗口高度
  23. private int mSurfaceHeight;
  24. // 视频旋转角度
  25. private int mVideoRotationDegree;
  26. // 媒体播放器
  27. private IMediaPlayer mMediaPlayer;
  28. // 媒体控制器
  29. private IMediaController mMediaController;
  30. // 播放完成监听
  31. private IMediaPlayer.OnCompletionListener mOnCompletionListener;
  32. // 播放准备监听
  33. private IMediaPlayer.OnPreparedListener mOnPreparedListener;
  34. // 播放缓冲监听
  35. private int mCurrentBufferPercentage;
  36. // 播放错误监听
  37. private IMediaPlayer.OnErrorListener mOnErrorListener;
  38. // 播放其他信息监听
  39. private IMediaPlayer.OnInfoListener mOnInfoListener;
  40. // 为记录seek位置而做准备
  41. private long mSeekWhenPrepared;
  42. private Context mAppContext;
  43. private IRenderView mRenderView;
  44. private int mVideoSarNum;
  45. private int mVideoSarDen;
  46. // Extend: Aspect Ratio
  47. private static final int[] s_allAspectRatio = {
  48. IRenderView.AR_ASPECT_FIT_PARENT,
  49. IRenderView.AR_ASPECT_FILL_PARENT,
  50. IRenderView.AR_ASPECT_WRAP_CONTENT,
  51. IRenderView.AR_MATCH_PARENT,
  52. IRenderView.AR_16_9_FIT_PARENT,
  53. IRenderView.AR_4_3_FIT_PARENT};
  54. private int mCurrentAspectRatioIndex = 0;
  55. private int mCurrentAspectRatio = s_allAspectRatio[mCurrentAspectRatioIndex];
  56. // Extend: Render
  57. public static final int RENDER_SURFACE_VIEW = 1;
  58. public static final int RENDER_TEXTURE_VIEW = 2;
  59. private List<Integer> mAllRenders = new ArrayList<>();
  60. // 使用编解码器硬编码还是软编码,true 硬编码 false 为软编码
  61. private boolean usingMediaCodec;
  62. // 使用编解码是否自转
  63. private boolean usingMediaCodecAutoRotate;
  64. private boolean usingAndroidPlayer;
  65. private boolean usingOpenSLES;
  66. private boolean enableBackgroundPlay;
  67. public CusIjkView(Context context) {
  68. super(context);
  69. initVideoView(context);
  70. }
  71. public CusIjkView(Context context, AttributeSet attrs) {
  72. super(context, attrs);
  73. initVideoView(context);
  74. }
  75. public CusIjkView(Context context, AttributeSet attrs, int defStyleAttr) {
  76. super(context, attrs, defStyleAttr);
  77. initVideoView(context);
  78. }
  79. /**
  80. * 初始化视频view
  81. */
  82. private void initVideoView(Context context) {
  83. mAppContext = context.getApplicationContext();
  84. initRenders();
  85. mVideoWidth = 0;
  86. mVideoHeight = 0;
  87. // 设置为可触摸并且获取焦点
  88. setFocusable(true);
  89. setFocusableInTouchMode(true);
  90. requestFocus();
  91. mCurrentState = PlayStateParams.STATE_IDLE;
  92. mTargetState = PlayStateParams.STATE_IDLE;
  93. }
  94. /**
  95. * 设置渲染器
  96. */
  97. public void setRenderView(IRenderView renderView) {
  98. if (mRenderView != null) {
  99. if (mMediaPlayer != null) {
  100. mMediaPlayer.setDisplay(null);
  101. }
  102. View renderUIView = mRenderView.getView();
  103. mRenderView.removeRenderCallback(mSHCallback);
  104. mRenderView = null;
  105. removeView(renderUIView);
  106. }
  107. if (renderView == null)
  108. return;
  109. mRenderView = renderView;
  110. renderView.setAspectRatio(mCurrentAspectRatio);
  111. if (mVideoWidth > 0 && mVideoHeight > 0)
  112. renderView.setVideoSize(mVideoWidth, mVideoHeight);
  113. if (mVideoSarNum > 0 && mVideoSarDen > 0)
  114. renderView.setVideoSampleAspectRatio(mVideoSarNum, mVideoSarDen);
  115. View renderUIView = mRenderView.getView();
  116. LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT, Gravity.CENTER);
  117. renderUIView.setLayoutParams(lp);
  118. addView(renderUIView);
  119. mRenderView.addRenderCallback(mSHCallback);
  120. mRenderView.setVideoRotation(mVideoRotationDegree);
  121. }
  122. /**
  123. * 设置旋转角度
  124. */
  125. public void setPlayerRotation(int rotation) {
  126. mVideoRotationDegree = rotation;
  127. if (mRenderView != null) {
  128. mRenderView.setVideoRotation(mVideoRotationDegree);
  129. }
  130. }
  131. /**
  132. * 旋转渲染器
  133. */
  134. public void setRender(int render) {
  135. switch (render) {
  136. case RENDER_TEXTURE_VIEW: {
  137. TextureRenderView renderView = new TextureRenderView(getContext());
  138. if (mMediaPlayer != null) {
  139. renderView.getSurfaceHolder().bindToMediaPlayer(mMediaPlayer);
  140. renderView.setVideoSize(mMediaPlayer.getVideoWidth(), mMediaPlayer.getVideoHeight());
  141. renderView.setVideoSampleAspectRatio(mMediaPlayer.getVideoSarNum(), mMediaPlayer.getVideoSarDen());
  142. renderView.setAspectRatio(mCurrentAspectRatio);
  143. }
  144. setRenderView(renderView);
  145. break;
  146. }
  147. case RENDER_SURFACE_VIEW: {
  148. SurfaceRenderView renderView = new SurfaceRenderView(getContext());
  149. setRenderView(renderView);
  150. break;
  151. }
  152. default:
  153. Log.e(TAG, String.format(Locale.getDefault(), "invalid render %d\n", render));
  154. break;
  155. }
  156. }
  157. /**
  158. * 设置视频路径
  159. */
  160. public void setVideoPath(String path) {
  161. setVideoURI(Uri.parse(path));
  162. }
  163. /**
  164. * 设置视频URI
  165. */
  166. public void setVideoURI(Uri uri) {
  167. setVideoURI(uri, null);
  168. }
  169. /**
  170. * Sets video URI using specific headers.
  171. *
  172. * @param uri the URI of the video.
  173. * @param headers the headers for the URI request.
  174. * Note that the cross domain redirection is allowed by default, but that can be
  175. * changed with key/value pairs through the headers parameter with
  176. * "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value
  177. * to disallow or allow cross domain redirection.
  178. */
  179. private void setVideoURI(Uri uri, Map<String, String> headers) {
  180. mUri = uri;
  181. mHeaders = headers;
  182. mSeekWhenPrepared = 0;
  183. openVideo();
  184. requestLayout();
  185. invalidate();
  186. }
  187. /**
  188. * 停止视频
  189. */
  190. public void stopPlayback() {
  191. if (mMediaPlayer != null) {
  192. mMediaPlayer.stop();
  193. mMediaPlayer.release();
  194. mMediaPlayer = null;
  195. mCurrentState = PlayStateParams.STATE_IDLE;
  196. mTargetState = PlayStateParams.STATE_IDLE;
  197. AudioManager am = (AudioManager) mAppContext.getSystemService(Context.AUDIO_SERVICE);
  198. if (am != null) {
  199. am.abandonAudioFocus(null);
  200. }
  201. }
  202. }
  203. /**
  204. * 打开视频
  205. */
  206. private void openVideo() {
  207. if (mUri == null || mSurfaceHolder == null) {
  208. return;
  209. }
  210. release(false);
  211. AudioManager am = (AudioManager) mAppContext.getSystemService(Context.AUDIO_SERVICE);
  212. if (am != null) {
  213. am.requestAudioFocus(null, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
  214. }
  215. try {
  216. if (usingAndroidPlayer) {
  217. mMediaPlayer = new AndroidMediaPlayer();
  218. } else {
  219. IjkMediaPlayer ijkMediaPlayer = null;
  220. if (mUri != null) {
  221. ijkMediaPlayer = new IjkMediaPlayer();
  222. IjkMediaPlayer.native_setLogLevel(IjkMediaPlayer.IJK_LOG_DEBUG);
  223. if (usingMediaCodec) {
  224. ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 1);
  225. if (usingMediaCodecAutoRotate) {
  226. ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 1);
  227. } else {
  228. ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 0);
  229. }
  230. } else {
  231. ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 0);
  232. }
  233. if (usingOpenSLES) {
  234. ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "opensles", 1);
  235. } else {
  236. ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "opensles", 0);
  237. }
  238. // Auto Select=,RGB 565=fcc-rv16,RGB 888X=fcc-rv32,YV12=fcc-yv12,默认为RGB 888X
  239. String pixelFormat = "";
  240. if (TextUtils.isEmpty(pixelFormat)) {
  241. ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "overlay-format", IjkMediaPlayer.SDL_FCC_RV32);
  242. } else {
  243. ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "overlay-format", pixelFormat);
  244. }
  245. ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "framedrop", 1);
  246. ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", 0);
  247. ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "http-detect-range-support", 0);
  248. ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "timeout", 10000000);
  249. ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "reconnect", 1);
  250. ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_loop_filter", 48);
  251. }
  252. mMediaPlayer = ijkMediaPlayer;
  253. }
  254. if (enableBackgroundPlay) {
  255. mMediaPlayer = new TextureMediaPlayer(mMediaPlayer);
  256. }
  257. if (mMediaPlayer == null) {
  258. return;
  259. }
  260. // 视频准备播放监听
  261. mMediaPlayer.setOnPreparedListener(mPreparedListener);
  262. // 视频播放完成监听
  263. mMediaPlayer.setOnCompletionListener(mCompletionListener);
  264. // 视频错误监听
  265. mMediaPlayer.setOnErrorListener(mErrorListener);
  266. // 视频其他信息监听
  267. mMediaPlayer.setOnInfoListener(mInfoListener);
  268. // 视频界面大小改变监听
  269. mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener);
  270. // 视频缓冲监听
  271. mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener);
  272. mCurrentBufferPercentage = 0;
  273. String scheme = mUri.getScheme();
  274. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
  275. && usingAndroidPlayer && (TextUtils.isEmpty(scheme) || scheme.equalsIgnoreCase("file"))) {
  276. IMediaDataSource dataSource = new FileMediaDataSource(new File(mUri.toString()));
  277. mMediaPlayer.setDataSource(dataSource);
  278. } else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
  279. mMediaPlayer.setDataSource(mAppContext, mUri, mHeaders);
  280. } else {
  281. mMediaPlayer.setDataSource(mUri.toString());
  282. }
  283. bindSurfaceHolder(mMediaPlayer, mSurfaceHolder);
  284. mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
  285. mMediaPlayer.setScreenOnWhilePlaying(true);
  286. mMediaPlayer.prepareAsync();
  287. // 这里不设置目标状态,但保护的目标状态
  288. mCurrentState = PlayStateParams.STATE_PREPARING;
  289. attachMediaController();
  290. } catch (IOException | IllegalArgumentException ex) {
  291. Log.w(TAG, "Unable to open content: " + mUri, ex);
  292. mCurrentState = PlayStateParams.STATE_ERROR;
  293. mTargetState = PlayStateParams.STATE_ERROR;
  294. mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0);
  295. }
  296. }
  297. public void setMediaController(IMediaController controller) {
  298. if (mMediaController != null) {
  299. mMediaController.hide();
  300. }
  301. mMediaController = controller;
  302. attachMediaController();
  303. }
  304. private void attachMediaController() {
  305. if (mMediaPlayer != null && mMediaController != null) {
  306. mMediaController.setMediaPlayer(this);
  307. View anchorView = this.getParent() instanceof View ? (View) this.getParent() : this;
  308. mMediaController.setAnchorView(anchorView);
  309. mMediaController.setEnabled(isInPlaybackState());
  310. }
  311. }
  312. IMediaPlayer.OnVideoSizeChangedListener mSizeChangedListener = new IMediaPlayer.OnVideoSizeChangedListener() {
  313. public void onVideoSizeChanged(IMediaPlayer mp, int width, int height, int sarNum, int sarDen) {
  314. mVideoWidth = mp.getVideoWidth();
  315. mVideoHeight = mp.getVideoHeight();
  316. mVideoSarNum = mp.getVideoSarNum();
  317. mVideoSarDen = mp.getVideoSarDen();
  318. if (mVideoWidth != 0 && mVideoHeight != 0) {
  319. if (mRenderView != null) {
  320. mRenderView.setVideoSize(mVideoWidth, mVideoHeight);
  321. mRenderView.setVideoSampleAspectRatio(mVideoSarNum, mVideoSarDen);
  322. }
  323. requestLayout();
  324. }
  325. }
  326. };
  327. IMediaPlayer.OnPreparedListener mPreparedListener = new IMediaPlayer.OnPreparedListener() {
  328. public void onPrepared(IMediaPlayer mp) {
  329. // 这里不设置目标状态,但保护的目标状态
  330. mCurrentState = PlayStateParams.STATE_PREPARED;
  331. if (mOnPreparedListener != null) {
  332. mOnPreparedListener.onPrepared(mMediaPlayer);
  333. }
  334. if (mMediaController != null) {
  335. mMediaController.setEnabled(true);
  336. }
  337. if (mOnInfoListener != null) {
  338. mOnInfoListener.onInfo(mMediaPlayer, mCurrentState, 0);
  339. }
  340. mVideoWidth = mp.getVideoWidth();
  341. mVideoHeight = mp.getVideoHeight();
  342. long seekToPosition = mSeekWhenPrepared;
  343. if (seekToPosition != 0) {
  344. seekTo((int) seekToPosition);
  345. }
  346. if (mVideoWidth != 0 && mVideoHeight != 0) {
  347. if (mRenderView != null) {
  348. mRenderView.setVideoSize(mVideoWidth, mVideoHeight);
  349. mRenderView.setVideoSampleAspectRatio(mVideoSarNum, mVideoSarDen);
  350. if (!mRenderView.shouldWaitForResize() || mSurfaceWidth == mVideoWidth && mSurfaceHeight == mVideoHeight) {
  351. if (mTargetState == PlayStateParams.STATE_PLAYING) {
  352. start();
  353. if (mMediaController != null) {
  354. mMediaController.show();
  355. }
  356. } else if (!isPlaying() &&
  357. (seekToPosition != 0 || getCurrentPosition() > 0)) {
  358. if (mMediaController != null) {
  359. mMediaController.show(0);
  360. }
  361. }
  362. }
  363. }
  364. } else {
  365. if (mTargetState == PlayStateParams.STATE_PLAYING) {
  366. start();
  367. }
  368. }
  369. }
  370. };
  371. private IMediaPlayer.OnCompletionListener mCompletionListener = new IMediaPlayer.OnCompletionListener() {
  372. public void onCompletion(IMediaPlayer mp) {
  373. mCurrentState = PlayStateParams.STATE_COMPLETED;
  374. mTargetState = PlayStateParams.STATE_COMPLETED;
  375. if (mMediaController != null) {
  376. mMediaController.hide();
  377. }
  378. if (mOnCompletionListener != null) {
  379. mOnCompletionListener.onCompletion(mMediaPlayer);
  380. }
  381. if (mOnInfoListener != null) {
  382. mOnInfoListener.onInfo(mMediaPlayer, PlayStateParams.STATE_COMPLETED, 0);
  383. }
  384. }
  385. };
  386. private IMediaPlayer.OnInfoListener mInfoListener = new IMediaPlayer.OnInfoListener() {
  387. public boolean onInfo(IMediaPlayer mp, int arg1, int arg2) {
  388. if (mOnInfoListener != null) {
  389. mOnInfoListener.onInfo(mp, arg1, arg2);
  390. }
  391. switch (arg1) {
  392. case IMediaPlayer.MEDIA_INFO_VIDEO_TRACK_LAGGING:
  393. Log.d(TAG, "MEDIA_INFO_VIDEO_TRACK_LAGGING:");
  394. break;
  395. case IMediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START:
  396. Log.d(TAG, "MEDIA_INFO_VIDEO_RENDERING_START:");
  397. break;
  398. case IMediaPlayer.MEDIA_INFO_BUFFERING_START:
  399. Log.d(TAG, "MEDIA_INFO_BUFFERING_START:");
  400. break;
  401. case IMediaPlayer.MEDIA_INFO_BUFFERING_END:
  402. Log.d(TAG, "MEDIA_INFO_BUFFERING_END:");
  403. break;
  404. case IMediaPlayer.MEDIA_INFO_NETWORK_BANDWIDTH:
  405. Log.d(TAG, "MEDIA_INFO_NETWORK_BANDWIDTH: " + arg2);
  406. break;
  407. case IMediaPlayer.MEDIA_INFO_BAD_INTERLEAVING:
  408. Log.d(TAG, "MEDIA_INFO_BAD_INTERLEAVING:");
  409. break;
  410. case IMediaPlayer.MEDIA_INFO_NOT_SEEKABLE:
  411. Log.d(TAG, "MEDIA_INFO_NOT_SEEKABLE:");
  412. break;
  413. case IMediaPlayer.MEDIA_INFO_METADATA_UPDATE:
  414. Log.d(TAG, "MEDIA_INFO_METADATA_UPDATE:");
  415. break;
  416. case IMediaPlayer.MEDIA_INFO_UNSUPPORTED_SUBTITLE:
  417. Log.d(TAG, "MEDIA_INFO_UNSUPPORTED_SUBTITLE:");
  418. break;
  419. case IMediaPlayer.MEDIA_INFO_SUBTITLE_TIMED_OUT:
  420. Log.d(TAG, "MEDIA_INFO_SUBTITLE_TIMED_OUT:");
  421. break;
  422. case IMediaPlayer.MEDIA_INFO_VIDEO_ROTATION_CHANGED:
  423. mVideoRotationDegree = arg2;
  424. Log.d(TAG, "MEDIA_INFO_VIDEO_ROTATION_CHANGED: " + arg2);
  425. if (mRenderView != null)
  426. mRenderView.setVideoRotation(arg2);
  427. break;
  428. case IMediaPlayer.MEDIA_INFO_AUDIO_RENDERING_START:
  429. Log.d(TAG, "MEDIA_INFO_AUDIO_RENDERING_START:");
  430. break;
  431. }
  432. return true;
  433. }
  434. };
  435. private IMediaPlayer.OnErrorListener mErrorListener = new IMediaPlayer.OnErrorListener() {
  436. public boolean onError(IMediaPlayer mp, int framework_err, int impl_err) {
  437. Log.d(TAG, "Error: " + framework_err + "," + impl_err);
  438. mCurrentState = PlayStateParams.STATE_ERROR;
  439. mTargetState = PlayStateParams.STATE_ERROR;
  440. if (mMediaController != null) {
  441. mMediaController.hide();
  442. }
  443. if (mOnErrorListener != null) {
  444. mOnErrorListener.onError(mMediaPlayer, framework_err, impl_err);
  445. }
  446. if (mOnInfoListener != null) {
  447. if (mOnInfoListener.onInfo(mMediaPlayer, framework_err, 0)) {
  448. return true;
  449. }
  450. }
  451. if (getWindowToken() != null) {
  452. String message = "Unknown error";
  453. if (framework_err == MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK) {
  454. message = "Invalid progressive playback";
  455. }
  456. new AlertDialog.Builder(getContext())
  457. .setMessage(message)
  458. .setPositiveButton("error", new DialogInterface.OnClickListener() {
  459. public void onClick(DialogInterface dialog, int whichButton) {
  460. if (mOnCompletionListener != null) {
  461. mOnCompletionListener.onCompletion(mMediaPlayer);
  462. }
  463. }
  464. })
  465. .setCancelable(false)
  466. .show();
  467. }
  468. return true;
  469. }
  470. };
  471. private IMediaPlayer.OnBufferingUpdateListener mBufferingUpdateListener = new IMediaPlayer.OnBufferingUpdateListener() {
  472. public void onBufferingUpdate(IMediaPlayer mp, int percent) {
  473. mCurrentBufferPercentage = percent;
  474. }
  475. };
  476. /**
  477. * Register a callback to be invoked when the media file is loaded and ready to go.
  478. */
  479. public void setOnPreparedListener(IMediaPlayer.OnPreparedListener listener) {
  480. mOnPreparedListener = listener;
  481. }
  482. /**
  483. * Register a callback to be invoked when the end of a media file has been reached during playback.
  484. */
  485. public void setOnCompletionListener(IMediaPlayer.OnCompletionListener l) {
  486. mOnCompletionListener = l;
  487. }
  488. /**
  489. * Register a callback to be invoked when an error occurs during playback or setup. If no listener
  490. * is specified, or if the listener returned false, VideoView will inform the user of any errors.
  491. */
  492. public void setOnErrorListener(IMediaPlayer.OnErrorListener l) {
  493. mOnErrorListener = l;
  494. }
  495. /**
  496. * Register a callback to be invoked when an informational event occurs during playback or setup.
  497. */
  498. public void setOnInfoListener(IMediaPlayer.OnInfoListener l) {
  499. mOnInfoListener = l;
  500. }
  501. private void bindSurfaceHolder(IMediaPlayer mp, IRenderView.ISurfaceHolder holder) {
  502. if (mp == null)
  503. return;
  504. if (holder == null) {
  505. mp.setDisplay(null);
  506. return;
  507. }
  508. holder.bindToMediaPlayer(mp);
  509. }
  510. IRenderView.IRenderCallback mSHCallback = new IRenderView.IRenderCallback() {
  511. @Override
  512. public void onSurfaceChanged(@NonNull IRenderView.ISurfaceHolder holder, int format, int w, int h) {
  513. if (holder.getRenderView() != mRenderView) {
  514. Log.e(TAG, "onSurfaceChanged: unmatched render callback\n");
  515. return;
  516. }
  517. mSurfaceWidth = w;
  518. mSurfaceHeight = h;
  519. boolean isValidState = (mTargetState == PlayStateParams.STATE_PLAYING);
  520. boolean hasValidSize = !mRenderView.shouldWaitForResize() || (mVideoWidth == w && mVideoHeight == h);
  521. if (mMediaPlayer != null && isValidState && hasValidSize) {
  522. if (mSeekWhenPrepared != 0) {
  523. seekTo((int) mSeekWhenPrepared);
  524. }
  525. start();
  526. }
  527. }
  528. @Override
  529. public void onSurfaceCreated(@NonNull IRenderView.ISurfaceHolder holder, int width, int height) {
  530. if (holder.getRenderView() != mRenderView) {
  531. Log.e(TAG, "onSurfaceCreated: unmatched render callback\n");
  532. return;
  533. }
  534. mSurfaceHolder = holder;
  535. if (mMediaPlayer != null)
  536. bindSurfaceHolder(mMediaPlayer, holder);
  537. else
  538. openVideo();
  539. }
  540. @Override
  541. public void onSurfaceDestroyed(@NonNull IRenderView.ISurfaceHolder holder) {
  542. if (holder.getRenderView() != mRenderView) {
  543. Log.e(TAG, "onSurfaceDestroyed: unmatched render callback\n");
  544. return;
  545. }
  546. mSurfaceHolder = null;
  547. releaseWithoutStop();
  548. }
  549. };
  550. public void releaseWithoutStop() {
  551. if (mMediaPlayer != null)
  552. mMediaPlayer.setDisplay(null);
  553. }
  554. /**
  555. * release the media player in any state
  556. */
  557. public void release(boolean clearTargetState) {
  558. if (mMediaPlayer != null) {
  559. mMediaPlayer.reset();
  560. mMediaPlayer.release();
  561. mMediaPlayer = null;
  562. mCurrentState = PlayStateParams.STATE_IDLE;
  563. if (clearTargetState) {
  564. mTargetState = PlayStateParams.STATE_IDLE;
  565. }
  566. AudioManager am = (AudioManager) mAppContext.getSystemService(Context.AUDIO_SERVICE);
  567. if (am != null) {
  568. am.abandonAudioFocus(null);
  569. }
  570. }
  571. }
  572. @Override
  573. public boolean onTouchEvent(MotionEvent ev) {
  574. if (isInPlaybackState() && mMediaController != null) {
  575. toggleMediaControlsVisibility();
  576. }
  577. return false;
  578. }
  579. @Override
  580. public boolean onTrackballEvent(MotionEvent ev) {
  581. if (isInPlaybackState() && mMediaController != null) {
  582. toggleMediaControlsVisibility();
  583. }
  584. return false;
  585. }
  586. @Override
  587. public boolean onKeyDown(int keyCode, KeyEvent event) {
  588. boolean isKeyCodeSupported = keyCode != KeyEvent.KEYCODE_BACK
  589. && keyCode != KeyEvent.KEYCODE_VOLUME_UP
  590. && keyCode != KeyEvent.KEYCODE_VOLUME_DOWN
  591. && keyCode != KeyEvent.KEYCODE_VOLUME_MUTE
  592. && keyCode != KeyEvent.KEYCODE_MENU
  593. && keyCode != KeyEvent.KEYCODE_CALL
  594. && keyCode != KeyEvent.KEYCODE_ENDCALL;
  595. if (isInPlaybackState() && isKeyCodeSupported && mMediaController != null) {
  596. if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) {
  597. if (mMediaPlayer.isPlaying()) {
  598. pause();
  599. mMediaController.show();
  600. } else {
  601. start();
  602. mMediaController.hide();
  603. }
  604. return true;
  605. } else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) {
  606. if (!mMediaPlayer.isPlaying()) {
  607. start();
  608. mMediaController.hide();
  609. }
  610. return true;
  611. } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP
  612. || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) {
  613. if (mMediaPlayer.isPlaying()) {
  614. pause();
  615. mMediaController.show();
  616. }
  617. return true;
  618. } else {
  619. toggleMediaControlsVisibility();
  620. }
  621. }
  622. return super.onKeyDown(keyCode, event);
  623. }
  624. private void toggleMediaControlsVisibility() {
  625. if (mMediaController.isShowing()) {
  626. mMediaController.hide();
  627. } else {
  628. mMediaController.show();
  629. }
  630. }
  631. @Override
  632. public void start() {
  633. if (isInPlaybackState()) {
  634. mMediaPlayer.start();
  635. mCurrentState = PlayStateParams.STATE_PLAYING;
  636. }
  637. mTargetState = PlayStateParams.STATE_PLAYING;
  638. }
  639. @Override
  640. public void pause() {
  641. if (isInPlaybackState()) {
  642. if (mMediaPlayer.isPlaying()) {
  643. mMediaPlayer.pause();
  644. mCurrentState = PlayStateParams.STATE_PAUSED;
  645. }
  646. }
  647. mTargetState = PlayStateParams.STATE_PAUSED;
  648. }
  649. public void onPause() {
  650. release(false);
  651. }
  652. public void onResume() {
  653. openVideo();
  654. }
  655. @Override
  656. public int getDuration() {
  657. if (isInPlaybackState()) {
  658. return (int) mMediaPlayer.getDuration();
  659. }
  660. return -1;
  661. }
  662. @Override
  663. public int getCurrentPosition() {
  664. if (isInPlaybackState()) {
  665. return (int) mMediaPlayer.getCurrentPosition();
  666. }
  667. return 0;
  668. }
  669. @Override
  670. public void seekTo(int msec) {
  671. if (isInPlaybackState()) {
  672. mMediaPlayer.seekTo(msec);
  673. mSeekWhenPrepared = 0;
  674. } else {
  675. mSeekWhenPrepared = msec;
  676. }
  677. }
  678. @Override
  679. public boolean isPlaying() {
  680. return isInPlaybackState() && mMediaPlayer.isPlaying();
  681. }
  682. @Override
  683. public int getBufferPercentage() {
  684. if (mMediaPlayer != null) {
  685. return mCurrentBufferPercentage;
  686. }
  687. return 0;
  688. }
  689. private boolean isInPlaybackState() {
  690. return (mMediaPlayer != null &&
  691. mCurrentState != PlayStateParams.STATE_ERROR &&
  692. mCurrentState != PlayStateParams.STATE_IDLE &&
  693. mCurrentState != PlayStateParams.STATE_PREPARING);
  694. }
  695. @Override
  696. public boolean canPause() {
  697. return true;
  698. }
  699. @Override
  700. public boolean canSeekBackward() {
  701. return false;
  702. }
  703. @Override
  704. public boolean canSeekForward() {
  705. return false;
  706. }
  707. @Override
  708. public int getAudioSessionId() {
  709. return 0;
  710. }
  711. public void toggleAspectRatio() {
  712. mCurrentAspectRatioIndex++;
  713. mCurrentAspectRatioIndex %= s_allAspectRatio.length;
  714. mCurrentAspectRatio = s_allAspectRatio[mCurrentAspectRatioIndex];
  715. if (mRenderView != null) {
  716. mRenderView.setAspectRatio(mCurrentAspectRatio);
  717. }
  718. }
  719. /**
  720. * 初始化渲染器
  721. */
  722. private void initRenders() {
  723. mAllRenders.clear();
  724. mAllRenders.add(RENDER_SURFACE_VIEW); // 添加surface渲染
  725. int mCurrentRenderIndex;
  726. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
  727. mAllRenders.add(RENDER_TEXTURE_VIEW); // 添加texture渲染
  728. mCurrentRenderIndex = 1;
  729. } else {
  730. mCurrentRenderIndex = 0;
  731. }
  732. int mCurrentRender = mAllRenders.get(mCurrentRenderIndex);
  733. setRender(mCurrentRender);
  734. }
  735. /**
  736. * 设置播放区域拉伸类型
  737. */
  738. public void setAspectRatio(int aspectRatio) {
  739. for (int i = 0; i < s_allAspectRatio.length; i++) {
  740. if (s_allAspectRatio[i] == aspectRatio) {
  741. mCurrentAspectRatioIndex = i;
  742. if (mRenderView != null) {
  743. mRenderView.setAspectRatio(mCurrentAspectRatio);
  744. }
  745. break;
  746. }
  747. }
  748. }

2.播放器功能的实现类(代码中有功能的注释)

  1. // 通知使用的flags
  2. private static final int MESSAGE_SHOW_PROGRESS = 1; // 同步进度
  3. private static final int MESSAGE_SEEK_NEW_POSITION = 3; // 设置新位置
  4. private static final int MESSAGE_HIDE_CENTER_BOX = 4; // 隐藏提示的box
  5. private static final int MESSAGE_RESTART_PLAY = 5; // 重新播放
  6. // 控件部分
  7. private final Activity mActivity; // 依附的容器Activity
  8. private final LayoutQuery mLayoutQuery; // 界面的中布局的查询器
  9. private final CusIjkView mIjkView; // 原生IjkPlayer
  10. private final View mTopBarView; // 播放器顶部控制bar
  11. private final View mBottomBarView; // 播放器底部控制bar
  12. private final ImageView mThumbnailImg; // 播放前的封面或缩略图
  13. private final ImageView mBackImage; // 视频返回按钮
  14. private final ImageView mBottomBarPlayerImage; // 视频bottomBar的播放按钮
  15. private final ImageView mCenterPlayerImage; // 视频中间的播放按钮
  16. private final ImageView mFullScreenImage; // 视频全屏按钮
  17. private final TextView mPlayerSpeedTv; // 视频加载速度
  18. private final SeekBar mProgressSeekBar; // 视频播放进度条
  19. // 视频相关参数
  20. private List<VideoIjkBean> mListVideos = new ArrayList<>(); // 码流列表
  21. private int mCurrStatus = PlayStateParams.STATE_IDLE; // 当前状态
  22. private int mCurrShowType = PlayStateParams.FIT_PARENT; // 视频显示比例,默认保持原视频的大小
  23. private final AudioManager mAudioManager; // 音频管理器
  24. private String mCurrVideoUrl; // 当前播放地址
  25. private int mCurrPlayPosition; // 当前播放位置
  26. private long mNewPosition = -1; // 滑动进度条得到的新位置,和当前播放位置是有区别的,mNewPosition =0也会调用设置的,故初始化值为-1
  27. private int mCurrVolume; // 当前声音大小
  28. private int mMaxVolume = 0; // 设备最大音量
  29. private final int mScreenWidthPix; // 获取当前设备的宽度
  30. private final int mPlayerViewInitHeight; // 记录播放器竖屏时的高度
  31. private float mCurrBrightness; // 当前亮度大小
  32. private int mBgState; // 记录进行后台时的播放状态0为播放,1为暂停
  33. private int mAutoConnectTime = 5000; // 自动重连的时间
  34. private boolean mIsPlayerSupport; // 第三方so是否支持,默认不支持,true为支持
  35. private boolean mIsLive; // 是否是直播,默认为非直播,true为直播false为点播。mIsLive()方法判断直播的方式比较片面
  36. private boolean mIsForbidTouch; // 禁止触摸,默认可以触摸,true为禁止false为可触摸
  37. private boolean mIsForbidHideControlPanel; // 禁止收起控制面板,默认可以收起,true为禁止false为可触摸
  38. private boolean mIsForbidDoubleUp; // 是否禁止双击,默认不禁止,true为禁止,false为不禁止
  39. private boolean mIsDragging; // 是否在拖动进度条中,默认为停止拖动,true为在拖动中,false为停止拖动
  40. private boolean mIsErrorStop = true; // 是否出错停止播放,默认是出错停止播放,true出错停止播放,false为用户点击停止播放
  41. private boolean mIsOnlyFullScreen; // 是否只有全屏,默认非全屏,true为全屏,false为非全屏
  42. private boolean mIsPortrait = true; // 是否是竖屏,默认为竖屏,true为竖屏,false为横屏
  43. private boolean mIsAutoReConnect = true; // 是否自动重连,默认5秒重连,true为重连,false为不重连
  44. private boolean mIsHideCenterPlayer; // 是否隐藏中间播放按钮,默认不隐藏,true为隐藏,false为不隐藏
  45. private boolean mIsHideTopBar; // 是否隐藏topBar,true为隐藏,false为不隐藏
  46. private boolean mIsHideBottomBar; // 是否隐藏bottomBar,true为隐藏,false为不隐藏
  47. private boolean mIsShowControlPanel; // 是否显示控制面板,默认为隐藏,true为显示false为隐藏
  48. private boolean mIsCharge; // 是否免费观看
  49. private int mMaxPlaytime; // 设置最大播放时间
  50. private boolean mIsHasSwitchStream; // 当前是否切换视频流,默认为否,true是切换视频流,false没有切换
  51. private boolean mIsNetWorkTip; // 播放时是否有网络提示,true为显示网络提示,false不显示网络提示
  52. private boolean mIsFirstPlayer = true; // 是否是第一次播放
  53. // 视频和UI的回调监听
  54. private Handler mHandler = new CusUiHandler();
  55. private AutoPlayRunnable mAutoPlayRunnable = new AutoPlayRunnable(); // 控制面板收起或者显示的轮询监听
  56. private OnPlayerBackListener mPlayerBack; // 视频的返回键监听
  57. private IMediaPlayer.OnInfoListener mOnInfoListener; // 视频播放时信息回调
  58. private OrientationEventListener mOrientationEventListener; // Activity界面方向监听
  59. private OnControlVisibilityChangeListener mOnControlVisibilityChangeListener; // 控制面板显示或隐藏监听
  60. // ========================================== 初始化 ========================================== //
  61. public PlayerView(Activity activity, View rootView) {
  62. this.mActivity = activity;
  63. try {
  64. IjkMediaPlayer.loadLibrariesOnce(null);
  65. IjkMediaPlayer.native_profileBegin("libijkplayer.so");
  66. mIsPlayerSupport = true;
  67. } catch (Throwable e) {
  68. e.printStackTrace();
  69. }
  70. try {
  71. int e = Settings.System.getInt(mActivity.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS);
  72. float progress = 1.0F * (float) e / 255.0F;
  73. WindowManager.LayoutParams layout = this.mActivity.getWindow().getAttributes();
  74. layout.screenBrightness = progress;
  75. mActivity.getWindow().setAttributes(layout);
  76. } catch (Settings.SettingNotFoundException e) {
  77. e.printStackTrace();
  78. }
  79. mAudioManager = (AudioManager) mActivity.getSystemService(Context.AUDIO_SERVICE);
  80. if (mAudioManager != null) {
  81. mMaxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
  82. }
  83. // 播放器整个界面
  84. View rootBoxRL;
  85. if (rootView == null) {
  86. mLayoutQuery = new LayoutQuery(mActivity);
  87. rootBoxRL = mActivity.findViewById(R.id.app_video_box);
  88. mIjkView = mActivity.findViewById(R.id.video_view);
  89. mTopBarView = mActivity.findViewById(R.id.app_video_top_box);
  90. mBottomBarView = mActivity.findViewById(R.id.ll_bottom_bar);
  91. mThumbnailImg = mActivity.findViewById(R.id.iv_trumb);
  92. mBackImage = mActivity.findViewById(R.id.app_video_finish);
  93. mBottomBarPlayerImage = mActivity.findViewById(R.id.app_video_play);
  94. mCenterPlayerImage = mActivity.findViewById(R.id.play_icon);
  95. mFullScreenImage = mActivity.findViewById(R.id.app_video_fullscreen);
  96. mPlayerSpeedTv = mActivity.findViewById(R.id.app_video_speed);
  97. mProgressSeekBar = mActivity.findViewById(R.id.app_video_seekBar);
  98. mProgressSeekBar.setMax(1000);
  99. } else {
  100. mLayoutQuery = new LayoutQuery(mActivity, rootView);
  101. rootBoxRL = rootView.findViewById(R.id.app_video_box);
  102. mIjkView = rootView.findViewById(R.id.video_view);
  103. mTopBarView = rootView.findViewById(R.id.app_video_top_box);
  104. mBottomBarView = rootView.findViewById(R.id.ll_bottom_bar);
  105. mThumbnailImg = rootView.findViewById(R.id.iv_trumb);
  106. mBackImage = rootView.findViewById(R.id.app_video_finish);
  107. mBottomBarPlayerImage = rootView.findViewById(R.id.app_video_play);
  108. mCenterPlayerImage = rootView.findViewById(R.id.play_icon);
  109. mFullScreenImage = rootView.findViewById(R.id.app_video_fullscreen);
  110. mPlayerSpeedTv = rootView.findViewById(R.id.app_video_speed);
  111. mProgressSeekBar = rootView.findViewById(R.id.app_video_seekBar);
  112. mProgressSeekBar.setMax(1000);
  113. }
  114. mProgressSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
  115. @Override
  116. public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
  117. if (fromUser) {
  118. long duration = getDuration();
  119. int position = (int) ((duration * progress * 1.0) / 1000);
  120. String time = generateTime(position);
  121. mLayoutQuery.id(R.id.app_video_currentTime).text(time);
  122. mLayoutQuery.id(R.id.app_video_currentTime_full).text(time);
  123. mLayoutQuery.id(R.id.app_video_currentTime_left).text(time);
  124. }
  125. }
  126. @Override
  127. public void onStartTrackingTouch(SeekBar seekBar) {
  128. mIsDragging = true;
  129. mHandler.removeMessages(MESSAGE_SHOW_PROGRESS);
  130. }
  131. @Override
  132. public void onStopTrackingTouch(SeekBar seekBar) {
  133. long duration = getDuration();
  134. mIjkView.seekTo((int) ((duration * seekBar.getProgress() * 1.0) / 1000));
  135. mHandler.removeMessages(MESSAGE_SHOW_PROGRESS);
  136. mIsDragging = false;
  137. mHandler.sendEmptyMessageDelayed(MESSAGE_SHOW_PROGRESS, 1000);
  138. }
  139. });
  140. View.OnClickListener onClickListener = new View.OnClickListener() {
  141. @Override
  142. public void onClick(View v) {
  143. if (v.getId() == R.id.app_video_fullscreen) {
  144. toggleFullScreen();
  145. } else if (v.getId() == R.id.app_video_play || v.getId() == R.id.play_icon) {
  146. if (mIjkView.isPlaying()) {
  147. if (mIsLive) {
  148. mIjkView.stopPlayback();
  149. } else {
  150. pausePlay();
  151. }
  152. } else {
  153. startPlay();
  154. if (mIjkView.isPlaying()) {
  155. // TODO ijkPlayer内部的监听没有回调,只能手动修改状态
  156. mCurrStatus = PlayStateParams.STATE_PREPARING;
  157. hideStatusUI();
  158. }
  159. }
  160. updatePausePlay();
  161. } else if (v.getId() == R.id.app_video_finish) {
  162. backClick();
  163. } else if (v.getId() == R.id.app_video_netTie_icon) {
  164. // 使用移动网络提示继续播放
  165. mIsNetWorkTip = false;
  166. hideStatusUI();
  167. startPlay();
  168. updatePausePlay();
  169. } else if (v.getId() == R.id.app_video_replay_icon) {
  170. // 重新播放
  171. mCurrStatus = PlayStateParams.STATE_ERROR;
  172. hideStatusUI();
  173. startPlay();
  174. updatePausePlay();
  175. }
  176. }
  177. };
  178. mBottomBarPlayerImage.setOnClickListener(onClickListener);
  179. mCenterPlayerImage.setOnClickListener(onClickListener);
  180. mFullScreenImage.setOnClickListener(onClickListener);
  181. mBackImage.setOnClickListener(onClickListener);
  182. mLayoutQuery.id(R.id.app_video_netTie_icon).clicked(onClickListener);
  183. mLayoutQuery.id(R.id.app_video_replay_icon).clicked(onClickListener);
  184. mIjkView.setOnInfoListener(new IMediaPlayer.OnInfoListener() {
  185. @Override
  186. public boolean onInfo(IMediaPlayer mp, int what, int extra) {
  187. if (what == PlayStateParams.MEDIA_INFO_NETWORK_BANDWIDTH || what == PlayStateParams.MEDIA_INFO_BUFFERING_BYTES_UPDATE) {
  188. if (mPlayerSpeedTv != null) {
  189. mPlayerSpeedTv.setText(getFormatSize(extra));
  190. }
  191. }
  192. statusChange(what);
  193. if (mOnInfoListener != null) {
  194. mOnInfoListener.onInfo(mp, what, extra);
  195. }
  196. if (mIsCharge && mMaxPlaytime < getCurrPlayPosition()) {
  197. mLayoutQuery.id(R.id.app_video_freeTie).visible();
  198. pausePlay();
  199. }
  200. return true;
  201. }
  202. });
  203. final GestureDetector detector = new GestureDetector(mActivity, new PlayerGestureListener());
  204. rootBoxRL.setClickable(true);
  205. rootBoxRL.setOnTouchListener(new View.OnTouchListener() {
  206. @SuppressLint("ClickableViewAccessibility")
  207. @Override
  208. public boolean onTouch(View view, MotionEvent motionEvent) {
  209. switch (motionEvent.getAction() & MotionEvent.ACTION_MASK) {
  210. case MotionEvent.ACTION_DOWN:
  211. if (mAutoPlayRunnable != null) {
  212. mAutoPlayRunnable.stop();
  213. }
  214. break;
  215. }
  216. if (detector.onTouchEvent(motionEvent)) {
  217. return true;
  218. }
  219. // 处理手势结束
  220. switch (motionEvent.getAction() & MotionEvent.ACTION_MASK) {
  221. case MotionEvent.ACTION_UP:
  222. endGesture();
  223. break;
  224. default:
  225. break;
  226. }
  227. return false;
  228. }
  229. });
  230. mOrientationEventListener = new OrientationEventListener(mActivity) {
  231. @Override
  232. public void onOrientationChanged(int orientation) {
  233. if (orientation >= 0 && orientation <= 30 || orientation >= 330 || (orientation >= 150 && orientation <= 210)) {
  234. if (mIsPortrait) {
  235. mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
  236. mOrientationEventListener.disable();
  237. }
  238. } else if ((orientation >= 90 && orientation <= 120) || (orientation >= 240 && orientation <= 300)) {
  239. if (!mIsPortrait) {
  240. mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
  241. mOrientationEventListener.disable();
  242. }
  243. }
  244. }
  245. };
  246. if (mIsOnlyFullScreen) {
  247. mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
  248. }
  249. mIsPortrait = (getScreenOrientation() == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
  250. mPlayerViewInitHeight = rootBoxRL.getLayoutParams().height;
  251. mScreenWidthPix = mActivity.getResources().getDisplayMetrics().widthPixels;
  252. hideAllUI();
  253. if (!mIsPlayerSupport) {
  254. showStatus(mActivity.getResources().getString(R.string.not_support));
  255. } else {
  256. mLayoutQuery.id(R.id.ll_bg).visible();
  257. }
  258. }
  259. // ========================================= 生命周期 ========================================= //
  260. public void onPause() {
  261. mBgState = (mIjkView.isPlaying() ? 0 : 1);
  262. getCurrPlayPosition();
  263. mIjkView.onPause();
  264. }
  265. public void onResume() {
  266. mIjkView.onResume();
  267. if (mIsLive) {
  268. mIjkView.seekTo(0);
  269. } else {
  270. mIjkView.seekTo(mCurrPlayPosition);
  271. }
  272. if (mBgState != 0) {
  273. pausePlay();
  274. }
  275. }
  276. public void onDestroy() {
  277. mOrientationEventListener.disable();
  278. mHandler.removeMessages(MESSAGE_RESTART_PLAY);
  279. mHandler.removeMessages(MESSAGE_SEEK_NEW_POSITION);
  280. mIjkView.stopPlayback();
  281. }
  282. public void onConfigurationChanged(final Configuration newConfig) {
  283. mIsPortrait = newConfig.orientation == Configuration.ORIENTATION_PORTRAIT;
  284. doOnConfigurationChanged(mIsPortrait);
  285. }
  286. public boolean onBackPressed() {
  287. if (!mIsOnlyFullScreen && getScreenOrientation() == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
  288. mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
  289. doOnNoAllScreen();
  290. return true;
  291. }
  292. return false;
  293. }
  294. // ========================================= 对外接口 ========================================= //
  295. /**
  296. * 设置播放信息监听回调
  297. */
  298. public void setOnInfoListener(IMediaPlayer.OnInfoListener onInfoListener) {
  299. this.mOnInfoListener = onInfoListener;
  300. }
  301. /**
  302. * 设置播放器中的返回键监听
  303. */
  304. public void setPlayerBackListener(OnPlayerBackListener listener) {
  305. this.mPlayerBack = listener;
  306. }
  307. /**
  308. * 设置控制面板显示隐藏监听
  309. */
  310. public void setOnControlPanelVisibilityChangListenter(OnControlVisibilityChangeListener listener) {
  311. this.mOnControlVisibilityChangeListener = listener;
  312. }
  313. /**
  314. * 显示缩略图
  315. */
  316. public void showThumbnail(OnShowThumbnailListener onShowThumbnailListener) {
  317. if (onShowThumbnailListener != null && mThumbnailImg != null) {
  318. onShowThumbnailListener.onShowThumbnail(mThumbnailImg);
  319. }
  320. }
  321. /**
  322. * 设置播放地址
  323. */
  324. public void setPlaySource(String stream, String url) {
  325. VideoIjkBean mVideoIjkBean = new VideoIjkBean();
  326. mVideoIjkBean.setStream(stream);
  327. mVideoIjkBean.setUrl(url);
  328. setPlaySource(mVideoIjkBean);
  329. }
  330. /**
  331. * 设置播放地址
  332. */
  333. public void setPlaySource(VideoIjkBean videoIjkBean) {
  334. mListVideos.clear();
  335. if (videoIjkBean != null) {
  336. mListVideos.add(videoIjkBean);
  337. if (mListVideos.size() > 0) {
  338. mCurrVideoUrl = mListVideos.get(0).getUrl();
  339. mListVideos.get(0).setSelect(true);
  340. isLive();
  341. if (mIjkView.isPlaying()) {
  342. getCurrPlayPosition();
  343. mIjkView.release(false);
  344. }
  345. mIsHasSwitchStream = true;
  346. }
  347. }
  348. }
  349. /**
  350. * 开始播放
  351. */
  352. public void startPlay() {
  353. if (mIsLive) {
  354. mIjkView.setVideoPath(mCurrVideoUrl);
  355. mIjkView.seekTo(0);
  356. } else {
  357. if (mIsHasSwitchStream || mCurrStatus == PlayStateParams.STATE_ERROR) {
  358. // TODO 换源之后声音可播,画面卡住,主要是渲染问题,目前只是提供了软解方式,后期提供设置方式
  359. mIjkView.setRender(CusIjkView.RENDER_TEXTURE_VIEW);
  360. mIjkView.setVideoPath(mCurrVideoUrl);
  361. mIjkView.seekTo(mCurrPlayPosition);
  362. mIsHasSwitchStream = false;
  363. }
  364. }
  365. hideStatusUI();
  366. if (mIsNetWorkTip && (NetworkUtils.getNetworkType(mActivity) == 4
  367. || NetworkUtils.getNetworkType(mActivity) == 5
  368. || NetworkUtils.getNetworkType(mActivity) == 6)) {
  369. mLayoutQuery.id(R.id.app_video_netTie).visible();
  370. } else {
  371. if (mIsCharge && mMaxPlaytime < getCurrPlayPosition()) {
  372. mLayoutQuery.id(R.id.app_video_freeTie).visible();
  373. } else {
  374. if (mIsPlayerSupport) {
  375. mIjkView.start();
  376. if (mIsFirstPlayer) {
  377. mLayoutQuery.id(R.id.app_video_loading).visible();
  378. mIsFirstPlayer = false;
  379. }
  380. } else {
  381. showStatus(mActivity.getResources().getString(R.string.not_support));
  382. }
  383. }
  384. }
  385. }
  386. /**
  387. * 暂停播放
  388. */
  389. public void pausePlay() {
  390. mCurrStatus = PlayStateParams.STATE_PAUSED;
  391. getCurrPlayPosition();
  392. mIjkView.pause();
  393. }
  394. /**
  395. * 停止播放
  396. */
  397. public void stopPlay() {
  398. mIjkView.stopPlayback();
  399. mIsErrorStop = true;
  400. if (mHandler != null) {
  401. mHandler.removeMessages(MESSAGE_RESTART_PLAY);
  402. }
  403. }
  404. public boolean isPlaying() {
  405. return mIjkView.isPlaying();
  406. }
  407. public void backClick() {
  408. if (!mIsOnlyFullScreen && !mIsPortrait) {
  409. mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
  410. doOnNoAllScreen();
  411. } else {
  412. if (mPlayerBack != null) {
  413. mPlayerBack.onPlayerBack();
  414. } else {
  415. mActivity.finish();
  416. }
  417. }
  418. }
  419. /**
  420. * 设置播放位置
  421. */
  422. public void seekTo(int playtime) {
  423. mIjkView.seekTo(playtime);
  424. }
  425. /**
  426. * 获取当前播放位置
  427. */
  428. public int getCurrPlayPosition() {
  429. if (!mIsLive) {
  430. mCurrPlayPosition = mIjkView.getCurrentPosition();
  431. } else {
  432. mCurrPlayPosition = -1;
  433. }
  434. return mCurrPlayPosition;
  435. }
  436. /**
  437. * 获取视频播放总时长
  438. */
  439. public long getDuration() {
  440. return (long) mIjkView.getDuration();
  441. }
  442. /**
  443. * 百分比显示切换
  444. */
  445. public void toggleAspectRatio() {
  446. if (mIjkView != null) {
  447. mIjkView.toggleAspectRatio();
  448. }
  449. }
  450. /**
  451. * 设置视频名称
  452. */
  453. public void setTitle(String title) {
  454. mLayoutQuery.id(R.id.app_video_title).text(title);
  455. }
  456. /**
  457. * 设置最大观看时长
  458. *
  459. * @param isCharge true为收费 false为免费即不做限制
  460. * @param maxPlaytime 最大能播放时长,单位秒
  461. */
  462. public void setChargeState(boolean isCharge, int maxPlaytime) {
  463. this.mIsCharge = isCharge;
  464. this.mMaxPlaytime = maxPlaytime * 1000;
  465. }
  466. /**
  467. * 设置播放区域拉伸类型
  468. */
  469. public void setScaleType(int showType) {
  470. mCurrShowType = showType;
  471. mIjkView.setAspectRatio(mCurrShowType);
  472. }
  473. /**
  474. * 旋转指定角度
  475. */
  476. public void setPlayerRotation(int rotation) {
  477. if (mIjkView != null) {
  478. mIjkView.setPlayerRotation(rotation);
  479. mIjkView.setAspectRatio(mCurrShowType);
  480. }
  481. }
  482. /**
  483. * 设置自动重连的模式或者重连时间,isAuto true 出错重连,false出错不重连,connectTime重连的时间
  484. */
  485. public void setAutoReConnect(boolean isAuto, int connectTime) {
  486. this.mIsAutoReConnect = isAuto;
  487. this.mAutoConnectTime = connectTime;
  488. }
  489. /**
  490. * 是否仅仅为全屏
  491. */
  492. public void setOnlyFullScreen(boolean isFull) {
  493. this.mIsOnlyFullScreen = isFull;
  494. tryFullScreen(mIsOnlyFullScreen);
  495. if (mIsOnlyFullScreen) {
  496. mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
  497. } else {
  498. mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
  499. }
  500. }
  501. /**
  502. * 设置是否禁止双击
  503. */
  504. public void setForbidDoubleUp(boolean flag) {
  505. this.mIsForbidDoubleUp = flag;
  506. }
  507. /**
  508. * 设置是否禁止隐藏bar
  509. */
  510. public void setForbidHideControlPanel(boolean flag) {
  511. this.mIsForbidHideControlPanel = flag;
  512. }
  513. /**
  514. * 是否禁止触摸
  515. */
  516. public void forbidTouch(boolean forbidTouch) {
  517. this.mIsForbidTouch = forbidTouch;
  518. }
  519. /**
  520. * 隐藏返回键,true隐藏,false为显示
  521. */
  522. public void hideBack(boolean isHide) {
  523. mBackImage.setVisibility(isHide ? View.GONE : View.VISIBLE);
  524. }
  525. /**
  526. * 隐藏全屏按钮,true隐藏,false为显示
  527. */
  528. public void hideFullscreen(boolean isHide) {
  529. mFullScreenImage.setVisibility(isHide ? View.GONE : View.VISIBLE);
  530. }
  531. /**
  532. * 隐藏中间播放按钮,ture为隐藏,false为不做隐藏处理,但不是显示
  533. */
  534. public void hideCenterPlayer(boolean isHide) {
  535. mIsHideCenterPlayer = isHide;
  536. mCenterPlayerImage.setVisibility(mIsHideCenterPlayer ? View.GONE : View.VISIBLE);
  537. }
  538. /**
  539. * 是否隐藏topbar,true为隐藏,false为不隐藏,但不一定是显示
  540. */
  541. public void hideHideTopBar(boolean isHide) {
  542. mIsHideTopBar = isHide;
  543. mTopBarView.setVisibility(mIsHideTopBar ? View.GONE : View.VISIBLE);
  544. }
  545. /**
  546. * 是否隐藏bottonbar,true为隐藏,false为不隐藏,但不一定是显示
  547. */
  548. public void hideBottomBar(boolean isHide) {
  549. mIsHideBottomBar = isHide;
  550. mBottomBarView.setVisibility(mIsHideBottomBar ? View.GONE : View.VISIBLE);
  551. }
  552. /**
  553. * 是否隐藏上下bar,true为隐藏,false为不隐藏,但不一定是显示
  554. */
  555. public void hideControlPanl(boolean isHide) {
  556. hideBottomBar(isHide);
  557. hideHideTopBar(isHide);
  558. }
  559. /**
  560. * 显示菜单设置
  561. */
  562. public void showMenu() {
  563. if (!mIsForbidHideControlPanel) {
  564. mTopBarView.setVisibility(View.GONE);
  565. mBottomBarView.setVisibility(View.GONE);
  566. }
  567. }
  568. /**
  569. * 设置显示加载网速或者隐藏
  570. */
  571. public void setShowSpeed(boolean isShow) {
  572. mPlayerSpeedTv.setVisibility(isShow ? View.VISIBLE : View.GONE);
  573. }
  574. // ========================================= 内部实现 ========================================= //
  575. /**
  576. * 当前播放的是否是直播
  577. */
  578. private void isLive() {
  579. mIsLive = mCurrVideoUrl != null
  580. && (mCurrVideoUrl.startsWith("rtmp://")
  581. || (mCurrVideoUrl.startsWith("http://") && mCurrVideoUrl.endsWith(".m3u8"))
  582. || (mCurrVideoUrl.startsWith("http://") && mCurrVideoUrl.endsWith(".flv")));
  583. }
  584. /**
  585. * 隐藏所有界面
  586. */
  587. private void hideAllUI() {
  588. if (!mIsForbidHideControlPanel) {
  589. mTopBarView.setVisibility(View.GONE);
  590. mBottomBarView.setVisibility(View.GONE);
  591. }
  592. hideStatusUI();
  593. }
  594. /**
  595. * 显示或隐藏操作面板
  596. */
  597. private void operatorPanel() {
  598. mIsShowControlPanel = !mIsShowControlPanel;
  599. if (mIsShowControlPanel) {
  600. mTopBarView.setVisibility(mIsHideTopBar ? View.GONE : View.VISIBLE);
  601. mBottomBarView.setVisibility(mIsHideBottomBar ? View.GONE : View.VISIBLE);
  602. if (mIsLive) {
  603. mLayoutQuery.id(R.id.app_video_process_panl).invisible();
  604. } else {
  605. mLayoutQuery.id(R.id.app_video_process_panl).visible();
  606. }
  607. if (mIsOnlyFullScreen || mIsForbidDoubleUp) {
  608. mFullScreenImage.setVisibility(View.GONE);
  609. } else {
  610. mFullScreenImage.setVisibility(View.VISIBLE);
  611. }
  612. if (mOnControlVisibilityChangeListener != null) {
  613. mOnControlVisibilityChangeListener.change(true);
  614. }
  615. // 显示面板的时候再根据状态显示播放按钮
  616. if (mCurrStatus == PlayStateParams.STATE_PLAYING
  617. || mCurrStatus == PlayStateParams.STATE_PREPARED
  618. || mCurrStatus == PlayStateParams.STATE_PREPARING
  619. || mCurrStatus == PlayStateParams.STATE_PAUSED) {
  620. if (mIsHideCenterPlayer) {
  621. mCenterPlayerImage.setVisibility(View.GONE);
  622. } else {
  623. mCenterPlayerImage.setVisibility(mIsLive ? View.GONE : View.VISIBLE);
  624. }
  625. } else {
  626. mCenterPlayerImage.setVisibility(View.GONE);
  627. }
  628. updatePausePlay();
  629. mHandler.sendEmptyMessage(MESSAGE_SHOW_PROGRESS);
  630. mAutoPlayRunnable.start();
  631. } else {
  632. if (mIsHideTopBar) {
  633. mTopBarView.setVisibility(View.GONE);
  634. } else {
  635. mTopBarView.setVisibility(mIsForbidHideControlPanel ? View.VISIBLE : View.GONE);
  636. }
  637. if (mIsHideBottomBar) {
  638. mBottomBarView.setVisibility(View.GONE);
  639. } else {
  640. mBottomBarView.setVisibility(mIsForbidHideControlPanel ? View.VISIBLE : View.GONE);
  641. }
  642. if (!mIsLive && mCurrStatus == PlayStateParams.STATE_PAUSED && !mIjkView.isPlaying()) {
  643. if (mIsHideCenterPlayer) {
  644. mCenterPlayerImage.setVisibility(View.GONE);
  645. } else {
  646. // 暂停时一直显示按钮
  647. mCenterPlayerImage.setVisibility(View.VISIBLE);
  648. }
  649. } else {
  650. mCenterPlayerImage.setVisibility(View.GONE);
  651. }
  652. mHandler.removeMessages(MESSAGE_SHOW_PROGRESS);
  653. if (mOnControlVisibilityChangeListener != null) {
  654. mOnControlVisibilityChangeListener.change(false);
  655. }
  656. mAutoPlayRunnable.stop();
  657. }
  658. }
  659. /**
  660. * 全屏切换
  661. */
  662. private void toggleFullScreen() {
  663. if (getScreenOrientation() == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
  664. mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
  665. doOnNoAllScreen();
  666. } else {
  667. mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
  668. doOnAllScreen();
  669. }
  670. updateFullScreenButton();
  671. }
  672. /**
  673. * 进度条和时长显示的方向切换
  674. */
  675. private void toggleProcessDurationOrientation() {
  676. setProcessDurationOrientation(PlayStateParams.PROCESS_PORTRAIT);
  677. }
  678. /**
  679. * 设置进度条和时长显示的方向,默认为上下显示,true为上下显示false为左右显示
  680. */
  681. private void setProcessDurationOrientation(int portrait) {
  682. if (portrait == PlayStateParams.PROCESS_CENTER) {
  683. mLayoutQuery.id(R.id.app_video_currentTime_full).gone();
  684. mLayoutQuery.id(R.id.app_video_endTime_full).gone();
  685. mLayoutQuery.id(R.id.app_video_center).gone();
  686. mLayoutQuery.id(R.id.app_video_lift).visible();
  687. } else if (portrait == PlayStateParams.PROCESS_LANDSCAPE) {
  688. mLayoutQuery.id(R.id.app_video_currentTime_full).visible();
  689. mLayoutQuery.id(R.id.app_video_endTime_full).visible();
  690. mLayoutQuery.id(R.id.app_video_center).gone();
  691. mLayoutQuery.id(R.id.app_video_lift).gone();
  692. } else {
  693. mLayoutQuery.id(R.id.app_video_currentTime_full).gone();
  694. mLayoutQuery.id(R.id.app_video_endTime_full).gone();
  695. mLayoutQuery.id(R.id.app_video_center).visible();
  696. mLayoutQuery.id(R.id.app_video_lift).gone();
  697. }
  698. }
  699. /**
  700. * 获取界面方向
  701. */
  702. private int getScreenOrientation() {
  703. int rotation = mActivity.getWindowManager().getDefaultDisplay().getRotation();
  704. DisplayMetrics dm = new DisplayMetrics();
  705. mActivity.getWindowManager().getDefaultDisplay().getMetrics(dm);
  706. int width = dm.widthPixels;
  707. int height = dm.heightPixels;
  708. int orientation;
  709. if ((rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) && height > width ||
  710. (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) && width > height) {
  711. switch (rotation) {
  712. case Surface.ROTATION_0:
  713. orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
  714. break;
  715. case Surface.ROTATION_90:
  716. orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
  717. break;
  718. case Surface.ROTATION_180:
  719. orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
  720. break;
  721. case Surface.ROTATION_270:
  722. orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
  723. break;
  724. default:
  725. orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
  726. break;
  727. }
  728. } else {
  729. switch (rotation) {
  730. case Surface.ROTATION_0:
  731. orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
  732. break;
  733. case Surface.ROTATION_90:
  734. orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
  735. break;
  736. case Surface.ROTATION_180:
  737. orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
  738. break;
  739. case Surface.ROTATION_270:
  740. orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
  741. break;
  742. default:
  743. orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
  744. break;
  745. }
  746. }
  747. return orientation;
  748. }
  749. /**
  750. * 状态改变同步UI
  751. */
  752. private void statusChange(int newStatus) {
  753. if (newStatus == PlayStateParams.STATE_COMPLETED) {
  754. mCurrStatus = PlayStateParams.STATE_COMPLETED;
  755. mCurrPlayPosition = 0;
  756. hideAllUI();
  757. showStatus("播放结束");
  758. } else if (newStatus == PlayStateParams.STATE_PREPARING || newStatus == PlayStateParams.MEDIA_INFO_BUFFERING_START) {
  759. mCurrStatus = PlayStateParams.STATE_PREPARING;
  760. hideStatusUI(); // 视频缓冲
  761. mLayoutQuery.id(R.id.app_video_loading).visible();
  762. } else if (newStatus == PlayStateParams.MEDIA_INFO_VIDEO_RENDERING_START
  763. || newStatus == PlayStateParams.STATE_PLAYING
  764. || newStatus == PlayStateParams.STATE_PREPARED
  765. || newStatus == PlayStateParams.MEDIA_INFO_BUFFERING_END
  766. || newStatus == PlayStateParams.STATE_PAUSED) {
  767. if (mCurrStatus == PlayStateParams.STATE_PAUSED) {
  768. mCurrStatus = PlayStateParams.STATE_PAUSED;
  769. } else {
  770. mCurrStatus = PlayStateParams.STATE_PLAYING;
  771. }
  772. // 视频缓冲结束后隐藏缩列图
  773. mHandler.postDelayed(new Runnable() {
  774. @Override
  775. public void run() {
  776. hideStatusUI();
  777. // 显示控制bar
  778. mIsShowControlPanel = false;
  779. if (!mIsForbidTouch) {
  780. operatorPanel();
  781. }
  782. mLayoutQuery.id(R.id.ll_bg).gone(); // 延迟0.5秒隐藏视频封面隐藏
  783. }
  784. }, 500);
  785. } else if (newStatus == PlayStateParams.MEDIA_INFO_VIDEO_INTERRUPT) {
  786. // 直播停止推流
  787. mCurrStatus = PlayStateParams.STATE_ERROR;
  788. if (!(mIsNetWorkTip && (NetworkUtils.getNetworkType(mActivity) == 4
  789. || NetworkUtils.getNetworkType(mActivity) == 5
  790. || NetworkUtils.getNetworkType(mActivity) == 6))) {
  791. if (mIsCharge && mMaxPlaytime < getCurrPlayPosition()) {
  792. mLayoutQuery.id(R.id.app_video_freeTie).visible();
  793. } else {
  794. hideAllUI();
  795. if (mIsLive) {
  796. showStatus("获取不到直播源");
  797. } else {
  798. showStatus(mActivity.getResources().getString(R.string.small_problem));
  799. }
  800. // 5秒尝试重连
  801. if (!mIsErrorStop && mIsAutoReConnect) {
  802. mHandler.sendEmptyMessageDelayed(MESSAGE_RESTART_PLAY, mAutoConnectTime);
  803. }
  804. }
  805. } else {
  806. mLayoutQuery.id(R.id.app_video_netTie).visible();
  807. }
  808. } else if (newStatus == PlayStateParams.STATE_ERROR
  809. || newStatus == PlayStateParams.MEDIA_INFO_UNKNOWN
  810. || newStatus == PlayStateParams.MEDIA_ERROR_IO
  811. || newStatus == PlayStateParams.MEDIA_ERROR_MALFORMED
  812. || newStatus == PlayStateParams.MEDIA_ERROR_UNSUPPORTED
  813. || newStatus == PlayStateParams.MEDIA_ERROR_TIMED_OUT
  814. || newStatus == PlayStateParams.MEDIA_ERROR_SERVER_DIED) {
  815. mCurrStatus = PlayStateParams.STATE_ERROR;
  816. if (!(mIsNetWorkTip && (NetworkUtils.getNetworkType(mActivity) == 4
  817. || NetworkUtils.getNetworkType(mActivity) == 5
  818. || NetworkUtils.getNetworkType(mActivity) == 6))) {
  819. if (mIsCharge && mMaxPlaytime < getCurrPlayPosition()) {
  820. mLayoutQuery.id(R.id.app_video_freeTie).visible();
  821. } else {
  822. hideStatusUI();
  823. showStatus(mActivity.getResources().getString(R.string.small_problem));
  824. // 秒尝试重连
  825. if (!mIsErrorStop && mIsAutoReConnect) {
  826. mHandler.sendEmptyMessageDelayed(MESSAGE_RESTART_PLAY, mAutoConnectTime);
  827. }
  828. }
  829. } else {
  830. mLayoutQuery.id(R.id.app_video_netTie).visible();
  831. }
  832. }
  833. }
  834. /**
  835. * 显示视频播放状态提示
  836. */
  837. private void showStatus(String statusText) {
  838. mLayoutQuery.id(R.id.app_video_replay).visible();
  839. mLayoutQuery.id(R.id.app_video_status_text).text(statusText);
  840. }
  841. /**
  842. * 界面方向改变时刷新界面
  843. */
  844. private void doOnConfigurationChanged(final boolean portrait) {
  845. if (mIjkView != null && !mIsOnlyFullScreen) {
  846. mHandler.post(new Runnable() {
  847. @Override
  848. public void run() {
  849. tryFullScreen(!portrait);
  850. if (portrait) {
  851. mLayoutQuery.id(R.id.app_video_box).height(mPlayerViewInitHeight, false);
  852. } else {
  853. int heightPixels = mActivity.getResources().getDisplayMetrics().heightPixels;
  854. int widthPixels = mActivity.getResources().getDisplayMetrics().widthPixels;
  855. mLayoutQuery.id(R.id.app_video_box).height(Math.min(heightPixels, widthPixels), false);
  856. }
  857. updateFullScreenButton();
  858. }
  859. });
  860. mOrientationEventListener.enable();
  861. }
  862. }
  863. private void doOnAllScreen() {
  864. setFullScreen(true);
  865. if (mIjkView != null && !mIsOnlyFullScreen) {
  866. mHandler.post(new Runnable() {
  867. @Override
  868. public void run() {
  869. mIsPortrait = false;
  870. int heightPixels = mActivity.getResources().getDisplayMetrics().heightPixels;
  871. int widthPixels = mActivity.getResources().getDisplayMetrics().widthPixels;
  872. mLayoutQuery.id(R.id.app_video_box).height(Math.min(heightPixels, widthPixels), false);
  873. }
  874. });
  875. }
  876. }
  877. private void doOnNoAllScreen() {
  878. setFullScreen(false);
  879. if (mIjkView != null && !mIsOnlyFullScreen) {
  880. mHandler.post(new Runnable() {
  881. @Override
  882. public void run() {
  883. mIsPortrait = true;
  884. mLayoutQuery.id(R.id.app_video_box).height(mPlayerViewInitHeight, false);
  885. }
  886. });
  887. }
  888. }
  889. /**
  890. * 设置界面方向
  891. */
  892. private void setFullScreen(boolean fullScreen) {
  893. if (mActivity != null) {
  894. WindowManager.LayoutParams attrs = mActivity.getWindow().getAttributes();
  895. if (fullScreen) {
  896. attrs.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN;
  897. mActivity.getWindow().setAttributes(attrs);
  898. mActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
  899. } else {
  900. attrs.flags &= (~WindowManager.LayoutParams.FLAG_FULLSCREEN);
  901. mActivity.getWindow().setAttributes(attrs);
  902. mActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
  903. }
  904. toggleProcessDurationOrientation();
  905. }
  906. }
  907. /**
  908. * 设置界面方向带隐藏actionbar
  909. */
  910. private void tryFullScreen(boolean fullScreen) {
  911. if (mActivity instanceof AppCompatActivity) {
  912. ActionBar supportActionBar = ((AppCompatActivity) mActivity).getSupportActionBar();
  913. if (supportActionBar != null) {
  914. if (fullScreen) {
  915. supportActionBar.hide();
  916. } else {
  917. supportActionBar.show();
  918. }
  919. }
  920. }
  921. setFullScreen(fullScreen);
  922. }
  923. /**
  924. * 隐藏状态界面
  925. */
  926. private void hideStatusUI() {
  927. mCenterPlayerImage.setVisibility(View.GONE);
  928. mLayoutQuery.id(R.id.app_video_replay).gone();
  929. mLayoutQuery.id(R.id.app_video_netTie).gone();
  930. mLayoutQuery.id(R.id.app_video_freeTie).gone();
  931. mLayoutQuery.id(R.id.app_video_loading).gone();
  932. if (mOnControlVisibilityChangeListener != null) {
  933. mOnControlVisibilityChangeListener.change(false);
  934. }
  935. }
  936. /**
  937. * 手势结束
  938. */
  939. private void endGesture() {
  940. mCurrVolume = -1;
  941. mCurrBrightness = -1f;
  942. if (mNewPosition >= 0) {
  943. mHandler.removeMessages(MESSAGE_SEEK_NEW_POSITION);
  944. mHandler.sendEmptyMessage(MESSAGE_SEEK_NEW_POSITION);
  945. }
  946. mHandler.removeMessages(MESSAGE_HIDE_CENTER_BOX);
  947. mHandler.sendEmptyMessageDelayed(MESSAGE_HIDE_CENTER_BOX, 500);
  948. if (mAutoPlayRunnable != null) {
  949. mAutoPlayRunnable.start();
  950. }
  951. }
  952. /**
  953. * 同步进度
  954. */
  955. private long syncProgress() {
  956. if (mIsDragging) {
  957. return 0;
  958. }
  959. long position = mIjkView.getCurrentPosition();
  960. long duration = mIjkView.getDuration();
  961. if (mProgressSeekBar != null) {
  962. if (duration > 0) {
  963. long pos = 1000L * position / duration;
  964. mProgressSeekBar.setProgress((int) pos);
  965. }
  966. int percent = mIjkView.getBufferPercentage();
  967. mProgressSeekBar.setSecondaryProgress(percent * 10);
  968. }
  969. if (mIsCharge && mMaxPlaytime + 1000 < getCurrPlayPosition()) {
  970. mLayoutQuery.id(R.id.app_video_freeTie).visible();
  971. pausePlay();
  972. } else {
  973. mLayoutQuery.id(R.id.app_video_currentTime).text(generateTime(position));
  974. mLayoutQuery.id(R.id.app_video_currentTime_full).text(generateTime(position));
  975. mLayoutQuery.id(R.id.app_video_currentTime_left).text(generateTime(position));
  976. mLayoutQuery.id(R.id.app_video_endTime).text(generateTime(duration));
  977. mLayoutQuery.id(R.id.app_video_endTime_full).text(generateTime(duration));
  978. mLayoutQuery.id(R.id.app_video_endTime_left).text(generateTime(duration));
  979. }
  980. return position;
  981. }
  982. /**
  983. * 更新播放、暂停和停止按钮
  984. */
  985. private void updatePausePlay() {
  986. if (mIjkView.isPlaying()) {
  987. if (mIsLive) {
  988. mBottomBarPlayerImage.setImageResource(R.drawable.simple_player_stop_white_24dp);
  989. } else {
  990. mBottomBarPlayerImage.setImageResource(R.drawable.simple_player_icon_media_pause);
  991. mCenterPlayerImage.setImageResource(R.drawable.simple_player_center_pause);
  992. }
  993. } else {
  994. mBottomBarPlayerImage.setImageResource(R.drawable.simple_player_arrow_white_24dp);
  995. mCenterPlayerImage.setImageResource(R.drawable.simple_player_center_play);
  996. }
  997. }
  998. /**
  999. * 更新全屏和半屏按钮
  1000. */
  1001. private void updateFullScreenButton() {
  1002. if (getScreenOrientation() == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
  1003. mFullScreenImage.setImageResource(R.drawable.simple_player_icon_fullscreen_shrink);
  1004. } else {
  1005. mFullScreenImage.setImageResource(R.drawable.simple_player_icon_fullscreen_stretch);
  1006. }
  1007. }
  1008. /**
  1009. * 滑动改变声音大小
  1010. */
  1011. private void onVolumeSlide(float percent) {
  1012. if (mCurrVolume == -1) {
  1013. mCurrVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
  1014. if (mCurrVolume < 0) {
  1015. mCurrVolume = 0;
  1016. }
  1017. }
  1018. int index = (int) (percent * mMaxVolume) + mCurrVolume;
  1019. if (index > mMaxVolume) {
  1020. index = mMaxVolume;
  1021. } else if (index < 0) {
  1022. index = 0;
  1023. }
  1024. // 变更声音
  1025. mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, index, 0);
  1026. // 变更进度条
  1027. int i = (int) (index * 1.0 / mMaxVolume * 100);
  1028. String s = i + "%";
  1029. if (i == 0) {
  1030. s = "off";
  1031. }
  1032. // 显示
  1033. mLayoutQuery.id(R.id.app_video_volume_icon).image(i == 0
  1034. ? R.drawable.simple_player_volume_off_white_36dp : R.drawable.simple_player_volume_up_white_36dp);
  1035. mLayoutQuery.id(R.id.app_video_brightness_box).gone();
  1036. mLayoutQuery.id(R.id.app_video_volume_box).visible();
  1037. mLayoutQuery.id(R.id.app_video_volume_box).visible();
  1038. mLayoutQuery.id(R.id.app_video_volume).text(s).visible();
  1039. }
  1040. /**
  1041. * 快进或者快退滑动改变进度
  1042. */
  1043. private void onProgressSlide(float percent) {
  1044. int position = mIjkView.getCurrentPosition();
  1045. long duration = mIjkView.getDuration();
  1046. long deltaMax = Math.min(100 * 1000, duration - position);
  1047. long delta = (long) (deltaMax * percent);
  1048. mNewPosition = delta + position;
  1049. if (mNewPosition > duration) {
  1050. mNewPosition = duration;
  1051. } else if (mNewPosition <= 0) {
  1052. mNewPosition = 0;
  1053. delta = -position;
  1054. }
  1055. int showDelta = (int) delta / 1000;
  1056. if (showDelta != 0) {
  1057. mLayoutQuery.id(R.id.app_video_fastForward_box).visible();
  1058. String text = showDelta > 0 ? ("+" + showDelta) : "" + showDelta;
  1059. mLayoutQuery.id(R.id.app_video_fastForward).text(text + "s");
  1060. mLayoutQuery.id(R.id.app_video_fastForward_target).text(generateTime(mNewPosition) + "/");
  1061. mLayoutQuery.id(R.id.app_video_fastForward_all).text(generateTime(duration));
  1062. }
  1063. }
  1064. /**
  1065. * 亮度滑动改变亮度
  1066. */
  1067. private void onBrightnessSlide(float percent) {
  1068. if (mCurrBrightness < 0) {
  1069. mCurrBrightness = mActivity.getWindow().getAttributes().screenBrightness;
  1070. if (mCurrBrightness <= 0.00f) {
  1071. mCurrBrightness = 0.50f;
  1072. } else if (mCurrBrightness < 0.01f) {
  1073. mCurrBrightness = 0.01f;
  1074. }
  1075. }
  1076. Log.d(this.getClass().getSimpleName(), "mCurrBrightness:" + mCurrBrightness + ",percent:" + percent);
  1077. mLayoutQuery.id(R.id.app_video_brightness_box).visible();
  1078. WindowManager.LayoutParams lpa = mActivity.getWindow().getAttributes();
  1079. lpa.screenBrightness = mCurrBrightness + percent;
  1080. if (lpa.screenBrightness > 1.0f) {
  1081. lpa.screenBrightness = 1.0f;
  1082. } else if (lpa.screenBrightness < 0.01f) {
  1083. lpa.screenBrightness = 0.01f;
  1084. }
  1085. mLayoutQuery.id(R.id.app_video_brightness).text(((int) (lpa.screenBrightness * 100)) + "%");
  1086. mActivity.getWindow().setAttributes(lpa);
  1087. }
  1088. /**
  1089. * 时长格式化显示
  1090. */
  1091. @SuppressLint("DefaultLocale")
  1092. private String generateTime(long time) {
  1093. int totalSeconds = (int) (time / 1000);
  1094. int seconds = totalSeconds % 60;
  1095. int minutes = (totalSeconds / 60) % 60;
  1096. int hours = totalSeconds / 3600;
  1097. return hours > 0 ? String.format("%02d:%02d:%02d", hours, minutes, seconds) : String.format("%02d:%02d", minutes, seconds);
  1098. }
  1099. /**
  1100. * 下载速度格式化显示
  1101. */
  1102. private String getFormatSize(int size) {
  1103. long fileSize = (long) size;
  1104. String showSize = "";
  1105. if (fileSize >= 0 && fileSize < 1024) {
  1106. showSize = fileSize + "Kb/s";
  1107. } else if (fileSize >= 1024 && fileSize < (1024 * 1024)) {
  1108. showSize = Long.toString(fileSize / 1024) + "KB/s";
  1109. } else if (fileSize >= (1024 * 1024) && fileSize < (1024 * 1024 * 1024)) {
  1110. showSize = Long.toString(fileSize / (1024 * 1024)) + "MB/s";
  1111. }
  1112. return showSize;
  1113. }
  1114. // ========================================== 内部类 ========================================== //
  1115. @SuppressLint("HandlerLeak")
  1116. private class CusUiHandler extends Handler {
  1117. @Override
  1118. public void handleMessage(Message msg) {
  1119. switch (msg.what) {
  1120. // 滑动完成,隐藏滑动提示的box
  1121. case MESSAGE_HIDE_CENTER_BOX:
  1122. mLayoutQuery.id(R.id.app_video_volume_box).gone();
  1123. mLayoutQuery.id(R.id.app_video_brightness_box).gone();
  1124. mLayoutQuery.id(R.id.app_video_fastForward_box).gone();
  1125. break;
  1126. // 滑动完成,设置播放进度
  1127. case MESSAGE_SEEK_NEW_POSITION:
  1128. if (!mIsLive && mNewPosition >= 0) {
  1129. mIjkView.seekTo((int) mNewPosition);
  1130. mNewPosition = -1;
  1131. }
  1132. break;
  1133. // 滑动中,同步播放进度
  1134. case MESSAGE_SHOW_PROGRESS:
  1135. long pos = syncProgress();
  1136. if (!mIsDragging && mIsShowControlPanel) {
  1137. msg = obtainMessage(MESSAGE_SHOW_PROGRESS);
  1138. sendMessageDelayed(msg, 1000 - (pos % 1000));
  1139. updatePausePlay();
  1140. }
  1141. break;
  1142. // 重新去播放
  1143. case MESSAGE_RESTART_PLAY:
  1144. mCurrStatus = PlayStateParams.STATE_ERROR;
  1145. startPlay();
  1146. updatePausePlay();
  1147. break;
  1148. }
  1149. }
  1150. }
  1151. /**
  1152. * 收起控制面板轮询,默认5秒无操作,收起控制面板
  1153. */
  1154. private class AutoPlayRunnable implements Runnable {
  1155. private int AUTO_PLAY_INTERVAL = 5000;
  1156. private boolean mShouldAutoPlay;
  1157. AutoPlayRunnable() {
  1158. mShouldAutoPlay = false;
  1159. }
  1160. void start() {
  1161. if (!mShouldAutoPlay) {
  1162. mShouldAutoPlay = true;
  1163. mHandler.removeCallbacks(this);
  1164. mHandler.postDelayed(this, AUTO_PLAY_INTERVAL);
  1165. }
  1166. }
  1167. void stop() {
  1168. if (mShouldAutoPlay) {
  1169. mHandler.removeCallbacks(this);
  1170. mShouldAutoPlay = false;
  1171. }
  1172. }
  1173. @Override
  1174. public void run() {
  1175. if (mShouldAutoPlay) {
  1176. mHandler.removeCallbacks(this);
  1177. if (!mIsForbidTouch) {
  1178. operatorPanel();
  1179. }
  1180. }
  1181. }
  1182. }
  1183. /**
  1184. * 播放器的手势监听
  1185. */
  1186. public class PlayerGestureListener extends GestureDetector.SimpleOnGestureListener {
  1187. private boolean isDownTouch; // 是否是按下的标识,默认为其他动作,true为按下标识,false为其他动作
  1188. private boolean isVolume; // 是否声音控制,默认为亮度控制,true为声音控制,false为亮度控制
  1189. private boolean isLandscape; // 是否横向滑动,默认为纵向滑动,true为横向滑动,false为纵向滑动
  1190. /**
  1191. * 双击,视频视窗双击事件
  1192. */
  1193. @Override
  1194. public boolean onDoubleTap(MotionEvent e) {
  1195. // if (!mIsForbidTouch && !mIsOnlyFullScreen && !mIsForbidDoubleUp) {
  1196. // toggleFullScreen();
  1197. // }
  1198. return true;
  1199. }
  1200. /**
  1201. * 按下
  1202. */
  1203. @Override
  1204. public boolean onDown(MotionEvent e) {
  1205. isDownTouch = true;
  1206. return super.onDown(e);
  1207. }
  1208. /**
  1209. * 滑动
  1210. */
  1211. @Override
  1212. public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
  1213. if (!mIsForbidTouch) {
  1214. float mOldX = e1.getX(), mOldY = e1.getY();
  1215. float deltaY = mOldY - e2.getY();
  1216. float deltaX = mOldX - e2.getX();
  1217. if (isDownTouch) {
  1218. isLandscape = Math.abs(distanceX) >= Math.abs(distanceY);
  1219. isVolume = mOldX > mScreenWidthPix * 0.5f;
  1220. isDownTouch = false;
  1221. }
  1222. if (isLandscape) {
  1223. if (!mIsLive) {
  1224. // 进度设置
  1225. onProgressSlide(-deltaX / mIjkView.getWidth());
  1226. }
  1227. } else {
  1228. float percent = deltaY / mIjkView.getHeight();
  1229. if (isVolume) {
  1230. // 声音设置
  1231. onVolumeSlide(percent);
  1232. } else {
  1233. // 亮度设置
  1234. onBrightnessSlide(percent);
  1235. }
  1236. }
  1237. }
  1238. return super.onScroll(e1, e2, distanceX, distanceY);
  1239. }
  1240. /**
  1241. * 单击,视频视窗单击事件
  1242. */
  1243. @Override
  1244. public boolean onSingleTapUp(MotionEvent e) {
  1245. if (!mIsForbidTouch) {
  1246. operatorPanel();
  1247. }
  1248. return true;
  1249. }
  1250. }

3.具体的使用:

在你实现播放器的activity使用:

1)首先初始化播放器:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3drX2JlaWNhaQ_size_16_color_FFFFFF_t_70 4

2)对视频进行网络判断

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3drX2JlaWNhaQ_size_16_color_FFFFFF_t_70 5

3)吊起播放:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3drX2JlaWNhaQ_size_16_color_FFFFFF_t_70 6

项目地址:

https://download.csdn.net/download/wk_beicai/11177930

发表评论

表情:
评论列表 (有 0 条评论,808人围观)

还没有评论,来说两句吧...

相关阅读