├── .gitignore
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── allen
│ │ └── supervideoplayer
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── allen
│ │ │ └── supervideoplayer
│ │ │ ├── App.java
│ │ │ ├── MainActivity.java
│ │ │ └── VideoPlayerActivity.java
│ └── res
│ │ ├── layout
│ │ ├── activity_main.xml
│ │ └── activity_video_player.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── raw
│ │ ├── local_video.mp4
│ │ └── loginvideo.mp4
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── allen
│ └── supervideoplayer
│ └── ExampleUnitTest.java
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── playerview
├── .gitignore
├── build.gradle
├── libs
│ └── pldroid-player-1.5.1.jar
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── allen
│ │ └── playerview
│ │ ├── VideoFileUtils.java
│ │ └── VideoPlayerView.java
│ ├── jniLibs
│ ├── arm64-v8a
│ │ └── libpldroidplayer.so
│ ├── armeabi-v7a
│ │ └── libpldroidplayer.so
│ ├── armeabi
│ │ └── libpldroidplayer.so
│ └── x86
│ │ └── libpldroidplayer.so
│ └── res
│ ├── drawable
│ ├── default_seekbar.xml
│ └── default_seekbar_thumb.xml
│ ├── layout
│ └── video_player_view_layout.xml
│ ├── mipmap-xhdpi
│ ├── default_play_img.png
│ ├── default_service_video_dot.png
│ ├── default_service_video_play_xiao.png
│ ├── default_service_video_suspend.png
│ └── default_service_xuanzhuan.png
│ ├── mipmap-xxhdpi
│ ├── default_play_img.png
│ ├── default_service_video_dot.png
│ ├── default_service_video_play_xiao.png
│ ├── default_service_video_suspend.png
│ └── default_service_xuanzhuan.png
│ └── values
│ ├── attrs.xml
│ ├── colors.xml
│ └── strings.xml
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle
2 | /local.properties
3 | .DS_Store
4 | /build
5 | /captures
6 | ### Android template
7 | # Built application files
8 | *.apk
9 | *.ap_
10 |
11 | # Files for the ART/Dalvik VM
12 | *.dex
13 |
14 | # Java class files
15 | *.class
16 |
17 | # Generated files
18 | bin/
19 | gen/
20 | out/
21 |
22 | # Gradle files
23 | .gradle/
24 | build/
25 |
26 | .idea
27 | # Local configuration file (sdk path, etc)
28 | local.properties
29 |
30 | # Proguard folder generated by Eclipse
31 | proguard/
32 |
33 | # Log Files
34 | *.log
35 |
36 | # Android Studio Navigation editor temp files
37 | .navigation/
38 |
39 | # Android Studio captures folder
40 | captures/
41 |
42 | # Intellij
43 | *.iml
44 | .idea/workspace.xml
45 | .idea/tasks.xml
46 | .idea/gradle.xml
47 | .idea/dictionaries
48 | .idea/libraries
49 |
50 | # Keystore files
51 | *.jks
52 |
53 | # External native build folder generated in Android Studio 2.2 and later
54 | .externalNativeBuild
55 |
56 | # Google Services (e.g. APIs or Firebase)
57 | google-services.json
58 |
59 | # Freeline
60 | freeline.py
61 | freeline/
62 | freeline_project_description.json
63 |
64 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SuperVideoPlayer
2 | 基于 ijkplayer 和PLDroidPlayer( based on ffplay )二次封装的视频播放器
3 |
4 |
5 | #playerView使用说明
6 |
7 | **1、在XML中使用**
8 |
9 | a、引入VideoPlayerView
10 |
11 |
16 | * 处理视频文件工具类 17 | */ 18 | 19 | public class VideoFileUtils { 20 | 21 | 22 | /** 23 | * 获取SDCard卡或者手机内存的根路径(优先获取SDCard卡的根路径) 24 | * 25 | * @param context Context 26 | * @return SDCard卡或者手机内存的根路径 27 | */ 28 | public static String getRootDir(Context context) { 29 | String rootDir = null; 30 | if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { 31 | rootDir = Environment.getExternalStorageDirectory().getAbsolutePath(); 32 | } else { 33 | rootDir = context.getCacheDir().getAbsolutePath(); 34 | } 35 | return rootDir; 36 | } 37 | 38 | 39 | /** 40 | * 检查本地是否存在某个文件 41 | * 42 | * @param filePath 43 | * @return 44 | */ 45 | public static boolean checkFileExists(String filePath) { 46 | File file = new File(filePath); 47 | Log.i("playerView", "checkFileExists file = " + file.getAbsolutePath()); 48 | return file.exists() && file.isFile(); 49 | } 50 | 51 | 52 | /** 53 | * 获取raw视频文件拷贝到sd卡后的路径 用于启动页播放本视频 54 | * 55 | * @param context 上下文对象 56 | * @param rawResId R.raw.yourVideo 57 | * @param fileSuffix 例如 .mp4 58 | * @return 返回filePath 59 | */ 60 | public static String getCopyRawResToSdcardPath(Context context, int rawResId, String fileSuffix) { 61 | String filePath = null; 62 | 63 | filePath = getRootDir(context) + "/" + rawResId + fileSuffix; 64 | 65 | if (checkFileExists(filePath)) { 66 | Log.i("playerView", "fileExists"); 67 | return filePath; 68 | } 69 | File file = new File(filePath); 70 | InputStream inputStream = null; 71 | FileOutputStream outputStream = null; 72 | try { 73 | int len; 74 | byte[] buffer = new byte[1024]; 75 | inputStream = context.getResources().openRawResource(rawResId); 76 | outputStream = new FileOutputStream(file); 77 | while ((len = inputStream.read(buffer)) != -1) { 78 | outputStream.write(buffer, 0, len); 79 | } 80 | outputStream.flush(); 81 | 82 | return filePath; 83 | 84 | } catch (IOException e) { 85 | Log.i("playerView", "getCopyRawResToSdcardPath: " + e.toString()); 86 | } finally { 87 | try { 88 | if (inputStream != null) { 89 | inputStream.close(); 90 | } 91 | if (outputStream != null) { 92 | outputStream.close(); 93 | } 94 | } catch (IOException e) { 95 | Log.i("playerView", "getCopyRawResToSdcardPath: " + e.getMessage()); 96 | } 97 | } 98 | 99 | return filePath; 100 | 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /playerview/src/main/java/com/allen/playerview/VideoPlayerView.java: -------------------------------------------------------------------------------- 1 | package com.allen.playerview; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.content.pm.ActivityInfo; 6 | import android.content.res.Configuration; 7 | import android.content.res.TypedArray; 8 | import android.graphics.drawable.Drawable; 9 | import android.net.Uri; 10 | import android.os.Handler; 11 | import android.os.Message; 12 | import android.util.AttributeSet; 13 | import android.util.Log; 14 | import android.view.LayoutInflater; 15 | import android.view.View; 16 | import android.widget.FrameLayout; 17 | import android.widget.ImageView; 18 | import android.widget.LinearLayout; 19 | import android.widget.ProgressBar; 20 | import android.widget.SeekBar; 21 | import android.widget.TextView; 22 | import android.widget.Toast; 23 | 24 | import com.pili.pldroid.player.AVOptions; 25 | import com.pili.pldroid.player.PLMediaPlayer; 26 | import com.pili.pldroid.player.PLNetworkManager; 27 | import com.pili.pldroid.player.widget.PLVideoTextureView; 28 | 29 | import java.net.UnknownHostException; 30 | 31 | /** 32 | * Created by Allen on 2017/3/24. 33 | *
34 | * 视频播放组件
35 | */
36 |
37 | public class VideoPlayerView extends LinearLayout {
38 |
39 |
40 | private Context mContext;
41 | private Activity mActivity;
42 | private Toast mToast = null;
43 |
44 | private PLVideoTextureView playerView;
45 |
46 | /**
47 | * 视频地址
48 | */
49 | private String videoPath = null;
50 | /**
51 | * 是否自动播放
52 | */
53 | private boolean isStartVideo = false;
54 | /**
55 | * 是否重复播放
56 | */
57 | private boolean repeatPlay = false;
58 | /**
59 | * 是否显示底部播放控制布局
60 | */
61 | private boolean isShowPlayerBottomBar = false;
62 | /**
63 | * 是否显示加载loading
64 | */
65 | private boolean isShowLoadingView = true;
66 | /**
67 | * 是否显示视频全屏按钮
68 | */
69 | private boolean isShowFullScreenBtn = false;
70 | /**
71 | * 是否允许点击屏幕暂停播放
72 | */
73 | private boolean isTouchForPause = true;
74 |
75 | /**
76 | * 是否显示中间视频播放按钮
77 | */
78 | private boolean isShowCenterPlayerBtn = true;
79 |
80 | /**
81 | * 封面图片
82 | */
83 | private Drawable coverViewDrawable = null;
84 | /**
85 | * 视频画面预览模式 16:9 4:3 适应屏幕 等等
86 | */
87 | private int aspectRatio;
88 |
89 |
90 | private LinearLayout mPlayerBottomBarLayout;
91 | private ImageView mCoverView;
92 | private ProgressBar mLoadingView;
93 |
94 | private ImageView mPlayControllerIv;
95 | private ImageView mCenterPlayerBtnIv;
96 | private ImageView mFullScreenBtnIv;
97 |
98 | private TextView mCurrentTimeTv, mTotalTimeTv;
99 | private SeekBar mSeekBar;
100 |
101 | private FrameLayout mTouchForPauseView;
102 |
103 | private static final int UPDATE_UI = 1;
104 |
105 | public static final int ORIGIN = 0;
106 | public static final int FIT_PARENT = 1;
107 | public static final int PAVED_PARENT = 2;
108 | public static final int IS_16_9 = 3;
109 | public static final int FIS_4_3 = 4;
110 |
111 |
112 | /**
113 | * 视频播放完毕回调
114 | */
115 | public OnVideoCompletionListener mOnVideoCompletionListener;
116 |
117 | /**
118 | * 进度变化回调
119 | */
120 | public OnCurrentProgressChange mOnCurrentProgressChange;
121 |
122 |
123 | public VideoPlayerView(Context context) {
124 | this(context, null);
125 | }
126 |
127 | public VideoPlayerView(Context context, AttributeSet attrs) {
128 | super(context, attrs);
129 | mContext = context;
130 | getAttr(attrs);
131 | initView();
132 | initPlayerView();
133 | }
134 |
135 |
136 | /**
137 | * 获取自定义属性
138 | *
139 | * @param attributeSet
140 | */
141 | private void getAttr(AttributeSet attributeSet) {
142 | TypedArray typedArray = mContext.obtainStyledAttributes(attributeSet, R.styleable.VideoPlayerView);
143 | videoPath = typedArray.getString(R.styleable.VideoPlayerView_player_videoPath);
144 | isStartVideo = typedArray.getBoolean(R.styleable.VideoPlayerView_player_isStart, false);
145 | repeatPlay = typedArray.getBoolean(R.styleable.VideoPlayerView_player_repeatPlay, false);
146 | isShowPlayerBottomBar = typedArray.getBoolean(R.styleable.VideoPlayerView_player_isShowPlayerBottomBar, false);
147 | isShowLoadingView = typedArray.getBoolean(R.styleable.VideoPlayerView_player_isShowLoadingView, true);
148 | isShowFullScreenBtn = typedArray.getBoolean(R.styleable.VideoPlayerView_player_isShowFullScreenBtn, false);
149 | isTouchForPause = typedArray.getBoolean(R.styleable.VideoPlayerView_player_isTouchForPause, true);
150 | coverViewDrawable = typedArray.getDrawable(R.styleable.VideoPlayerView_player_coverViewDrawableId);
151 | aspectRatio = typedArray.getInt(R.styleable.VideoPlayerView_player_aspectRatio, 1);
152 |
153 | typedArray.recycle();
154 | }
155 |
156 |
157 | /**
158 | * 初始化view
159 | */
160 | private void initView() {
161 | LayoutInflater.from(mContext).inflate(R.layout.video_player_view_layout, this);
162 |
163 | mPlayerBottomBarLayout = (LinearLayout) findViewById(R.id.player_view_bottom_bar_layout_ll);
164 |
165 | playerView = (PLVideoTextureView) findViewById(R.id.VideoView);
166 | mCoverView = (ImageView) findViewById(R.id.player_view_cover_view);
167 | mLoadingView = (ProgressBar) findViewById(R.id.player_view_loading_view);
168 |
169 | mTouchForPauseView = (FrameLayout) findViewById(R.id.touch_for_pause_view);
170 | mPlayControllerIv = (ImageView) findViewById(R.id.player_view_play_controller_iv);
171 | mCurrentTimeTv = (TextView) findViewById(R.id.player_view_current_time_tv);
172 | mTotalTimeTv = (TextView) findViewById(R.id.player_view_total_time_tv);
173 | mSeekBar = (SeekBar) findViewById(R.id.player_view_current_seekBar_sb);
174 | mCenterPlayerBtnIv = (ImageView) findViewById(R.id.player_view_center_play_iv);
175 | mFullScreenBtnIv = (ImageView) findViewById(R.id.player_view_play_full_iv);
176 |
177 | //设置按钮事件
178 | setClickListenerEvent();
179 |
180 | }
181 |
182 | /**
183 | * 设置按钮事件
184 | */
185 | private void setClickListenerEvent() {
186 | mTouchForPauseView.setOnClickListener(new OnClickListener() {
187 | @Override
188 | public void onClick(View v) {
189 | if (isShowCenterPlayerBtn) {
190 | if (isTouchForPause) {
191 | playController();
192 | }
193 | }
194 | }
195 | });
196 | mPlayControllerIv.setOnClickListener(new OnClickListener() {
197 | @Override
198 | public void onClick(View v) {
199 | playController();
200 | }
201 | });
202 |
203 | mCenterPlayerBtnIv.setOnClickListener(new OnClickListener() {
204 | @Override
205 | public void onClick(View v) {
206 | start();
207 | }
208 | });
209 |
210 |
211 | if (isShowFullScreenBtn) {
212 |
213 | mFullScreenBtnIv.setVisibility(VISIBLE);
214 |
215 | mFullScreenBtnIv.setOnClickListener(new OnClickListener() {
216 | @Override
217 | public void onClick(View v) {
218 |
219 | if (mActivity != null) {
220 | if (mActivity.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
221 | mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
222 | } else {
223 | mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
224 | }
225 | }
226 | }
227 | });
228 |
229 | } else {
230 | mFullScreenBtnIv.setVisibility(GONE);
231 | }
232 |
233 |
234 | mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
235 | @Override
236 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
237 | updateTextViewWithTimeFormat(mCurrentTimeTv, progress);
238 | }
239 |
240 | @Override
241 | public void onStartTrackingTouch(SeekBar seekBar) {
242 | playerView.pause();
243 | TimeUIHandler.removeMessages(UPDATE_UI);
244 | }
245 |
246 | @Override
247 | public void onStopTrackingTouch(SeekBar seekBar) {
248 | int progress = seekBar.getProgress();
249 | playerView.seekTo(progress);
250 |
251 | if (mCenterPlayerBtnIv.getVisibility() == GONE) {
252 | playerView.start();
253 | TimeUIHandler.sendEmptyMessage(UPDATE_UI);
254 | }
255 |
256 | }
257 | });
258 |
259 | }
260 |
261 | /**
262 | * 播放控制方法
263 | */
264 | private void playController() {
265 | if (playerView != null) {
266 | if (playerView.isPlaying()) {
267 | pause();
268 | } else {
269 | start();
270 | }
271 | }
272 | }
273 |
274 |
275 | /**
276 | * 初始化播放器信息
277 | */
278 | private void initPlayerView() {
279 |
280 | try {
281 | PLNetworkManager.getInstance().startDnsCacheService(mContext);
282 | } catch (UnknownHostException e) {
283 | e.printStackTrace();
284 | }
285 |
286 | setOptions();
287 | playerView.setOnErrorListener(mOnErrorListener);
288 | playerView.setOnCompletionListener(mOnCompletionListener);
289 | playerView.setOnPreparedListener(new PLMediaPlayer.OnPreparedListener() {
290 | @Override
291 | public void onPrepared(PLMediaPlayer plMediaPlayer) {
292 | if (isShowPlayerBottomBar) {
293 | mPlayerBottomBarLayout.setVisibility(VISIBLE);
294 | }
295 | }
296 | });
297 | setCoverView(coverViewDrawable);
298 |
299 | //是否显示加载时候的loading框
300 | if (isShowLoadingView) {
301 | mLoadingView.setVisibility(VISIBLE);
302 | playerView.setBufferingIndicator(mLoadingView);
303 | }
304 |
305 |
306 | //设置显示比例 16:9 4:3 等等
307 | playerView.setDisplayAspectRatio(aspectRatio);
308 |
309 | //支持画面的镜像变换
310 | playerView.setMirror(false);
311 |
312 | //是否重复播放
313 | if (repeatPlay) {
314 | playerView.setLooping(true);
315 | }
316 |
317 | setVideoPlayerPath(videoPath);
318 | isStartVideo(isStartVideo);
319 |
320 |
321 | }
322 |
323 |
324 | /**
325 | * 初始化播放封面
326 | */
327 | private VideoPlayerView setCoverView(Drawable drawable) {
328 | if (drawable != null) {
329 | mCoverView.setImageDrawable(drawable);
330 | playerView.setCoverView(mCoverView);
331 | }
332 | return this;
333 | }
334 |
335 | /**
336 | * 是否开始播放
337 | *
338 | * @param isStartVideo
339 | */
340 | private void isStartVideo(boolean isStartVideo) {
341 | if (isStartVideo && videoPath != null && !"".equals(videoPath)) {
342 | playerView.start();
343 | TimeUIHandler.sendEmptyMessage(UPDATE_UI);
344 | }
345 | }
346 |
347 | /**
348 | * 设置视频播放地址
349 | *
350 | * @param videoPath
351 | */
352 | private void setVideoPlayerPath(String videoPath) {
353 | if (this.videoPath != null) {
354 | playerView.setVideoPath(videoPath);
355 | }
356 | }
357 |
358 | /**
359 | * 配置视频播放器器的参数
360 | */
361 | private void setOptions() {
362 | AVOptions options = new AVOptions();
363 |
364 | // the unit of timeout is ms
365 | options.setInteger(AVOptions.KEY_PREPARE_TIMEOUT, 10 * 1000);
366 | options.setInteger(AVOptions.KEY_GET_AV_FRAME_TIMEOUT, 10 * 1000);
367 | options.setInteger(AVOptions.KEY_PROBESIZE, 128 * 1024);
368 |
369 | // 1 -> hw codec enable, 0 -> disable [recommended]
370 | options.setInteger(AVOptions.KEY_MEDIACODEC, AVOptions.MEDIA_CODEC_AUTO);
371 |
372 | // whether start play automatically after prepared, default value is 1
373 | options.setInteger(AVOptions.KEY_START_ON_PREPARED, 0);
374 |
375 | playerView.setAVOptions(options);
376 | }
377 |
378 | /**
379 | * 播放结束回调
380 | */
381 | private PLMediaPlayer.OnCompletionListener mOnCompletionListener = new PLMediaPlayer.OnCompletionListener() {
382 | @Override
383 | public void onCompletion(PLMediaPlayer plMediaPlayer) {
384 | if (mOnCurrentProgressChange != null) {
385 | mOnCurrentProgressChange.onCurrentProgressChange(100, getTotalTime());
386 | }
387 | if (repeatPlay) {
388 | repeatPlay();
389 | } else {
390 |
391 | mPlayControllerIv.setImageResource(R.mipmap.default_service_video_play_xiao);
392 | if (isShowCenterPlayerBtn) {
393 | mCenterPlayerBtnIv.setVisibility(VISIBLE);
394 | }
395 | TimeUIHandler.removeMessages(UPDATE_UI);
396 |
397 | if (mOnVideoCompletionListener != null) {
398 | mOnVideoCompletionListener.doOnVideoCompletionListener();
399 | }
400 | }
401 |
402 |
403 | }
404 | };
405 |
406 | /**
407 | * 播放错误回调
408 | */
409 | private PLMediaPlayer.OnErrorListener mOnErrorListener = new PLMediaPlayer.OnErrorListener() {
410 | @Override
411 | public boolean onError(PLMediaPlayer mp, int errorCode) {
412 | switch (errorCode) {
413 | case PLMediaPlayer.ERROR_CODE_INVALID_URI:
414 | showToastTips("无效的 URL!");
415 | break;
416 | case PLMediaPlayer.ERROR_CODE_404_NOT_FOUND:
417 | showToastTips("播放资源不存在!");
418 | break;
419 | case PLMediaPlayer.ERROR_CODE_CONNECTION_REFUSED:
420 | showToastTips("服务器拒绝连接!");
421 | break;
422 | case PLMediaPlayer.ERROR_CODE_CONNECTION_TIMEOUT:
423 | showToastTips("连接超时!");
424 | break;
425 | case PLMediaPlayer.ERROR_CODE_EMPTY_PLAYLIST:
426 | showToastTips("空的播放列表!");
427 | break;
428 | case PLMediaPlayer.ERROR_CODE_STREAM_DISCONNECTED:
429 | showToastTips("与服务器连接断开!");
430 | break;
431 | case PLMediaPlayer.ERROR_CODE_IO_ERROR:
432 | showToastTips("网络异常!");
433 | break;
434 | case PLMediaPlayer.ERROR_CODE_UNAUTHORIZED:
435 | showToastTips("未授权,播放一个禁播的流!");
436 | break;
437 | case PLMediaPlayer.ERROR_CODE_PREPARE_TIMEOUT:
438 | showToastTips("播放器准备超时!");
439 | break;
440 | case PLMediaPlayer.ERROR_CODE_READ_FRAME_TIMEOUT:
441 | showToastTips("读取数据超时!");
442 | break;
443 | case PLMediaPlayer.ERROR_CODE_HW_DECODE_FAILURE:
444 | showToastTips("硬解失败!");
445 | break;
446 | case PLMediaPlayer.MEDIA_ERROR_UNKNOWN:
447 | break;
448 | default:
449 | showToastTips("未知错误!");
450 | break;
451 | }
452 |
453 | // Return true means the error has been handled
454 | // If return false, then `onCompletion` will be called
455 | return true;
456 | }
457 | };
458 |
459 |
460 | /**
461 | * 格式化视频播放时间
462 | *
463 | * @param textView
464 | * @param millisecond
465 | */
466 | private void updateTextViewWithTimeFormat(TextView textView, int millisecond) {
467 |
468 | textView.setText(getFormatTime(millisecond));
469 | }
470 |
471 | /**
472 | * 格式化时间
473 | *
474 | * @param millisecond
475 | * @return
476 | */
477 | private String getFormatTime(int millisecond) {
478 | int second = millisecond / 1000;
479 | int hh = second / 3600;
480 | int mm = second % 3600 / 60;
481 | int ss = second % 60;
482 |
483 | String str = null;
484 | if (hh != 0) {
485 | str = String.format("%02d:%02d:%02d", hh, mm, ss);
486 | } else {
487 | str = String.format("%02d:%02d", mm, ss);
488 | }
489 |
490 | return str;
491 | }
492 |
493 | /**
494 | * 更新时间及播放进度UI
495 | */
496 | private Handler TimeUIHandler = new Handler() {
497 | @Override
498 | public void handleMessage(Message msg) {
499 | super.handleMessage(msg);
500 | if (msg.what == UPDATE_UI) {
501 | //获取当前播放进度和总时间
502 | int currentPosition = (int) playerView.getCurrentPosition();
503 | int totalDuration = (int) playerView.getDuration();
504 |
505 | // Log.d("allen", "currentPosition: " + currentPosition + "-----" + "totalDuration=" + totalDuration);
506 | //格式化时间SocketService
507 | updateTextViewWithTimeFormat(mCurrentTimeTv, currentPosition);
508 | updateTextViewWithTimeFormat(mTotalTimeTv, totalDuration);
509 |
510 | //设置进度参数
511 | mSeekBar.setMax(totalDuration);
512 | mSeekBar.setProgress(currentPosition);
513 |
514 | if (mOnCurrentProgressChange != null) {
515 | // Log.e("onCurrentProgressChange", getCurrentProgress() + "");
516 | mOnCurrentProgressChange.onCurrentProgressChange(getCurrentProgress(), getFormatTime(currentPosition));
517 | }
518 |
519 | //调取自己实现自动刷新
520 | TimeUIHandler.sendEmptyMessageDelayed(UPDATE_UI, 100);
521 |
522 |
523 | }
524 | }
525 | };
526 |
527 |
528 | private void showToastTips(String tips) {
529 | if (mToast != null) {
530 | mToast.cancel();
531 | }
532 | mToast = Toast.makeText(mContext, tips, Toast.LENGTH_SHORT);
533 | mToast.show();
534 | }
535 |
536 | /////////////////生命周期方法///////////////////
537 | public void onResume() {
538 | start();
539 | }
540 |
541 | public void onPause() {
542 | //这里不直接使用pause方法是为了解决欢迎页退出的时候不显示暂停播放的图标
543 | if (playerView != null) {
544 | playerView.pause();
545 | TimeUIHandler.removeMessages(UPDATE_UI);
546 | mPlayControllerIv.setImageResource(R.mipmap.default_service_video_play_xiao);
547 | }
548 | }
549 |
550 | public void onDestroy() {
551 | stop();
552 | }
553 |
554 |
555 | ////////////对外暴露的方法/////////////
556 |
557 | /**
558 | * 开始播放
559 | *
560 | * @return
561 | */
562 | public VideoPlayerView start() {
563 | if (playerView != null) {
564 | playerView.start();
565 | TimeUIHandler.sendEmptyMessage(UPDATE_UI);
566 | mPlayControllerIv.setImageResource(R.mipmap.default_service_video_suspend);
567 |
568 | if (isShowCenterPlayerBtn) {
569 | mCenterPlayerBtnIv.setVisibility(GONE);
570 | }
571 | }
572 |
573 | return this;
574 | }
575 |
576 | /**
577 | * 暂停播放
578 | *
579 | * @return
580 | */
581 | public VideoPlayerView pause() {
582 | if (playerView != null) {
583 | playerView.pause();
584 | TimeUIHandler.removeMessages(UPDATE_UI);
585 | mPlayControllerIv.setImageResource(R.mipmap.default_service_video_play_xiao);
586 | if (isShowCenterPlayerBtn) {
587 | mCenterPlayerBtnIv.setVisibility(VISIBLE);
588 | }
589 | }
590 | return this;
591 | }
592 |
593 | /**
594 | * 停止播放
595 | */
596 | public void stop() {
597 | if (playerView != null) {
598 | playerView.stopPlayback();
599 | TimeUIHandler.removeMessages(UPDATE_UI);
600 | }
601 | }
602 |
603 | /**
604 | * 循环播放
605 | */
606 | public void repeatPlay() {
607 | if (playerView != null) {
608 | playerView.setVideoPath(videoPath);
609 | start();
610 | }
611 | }
612 |
613 | /**
614 | * 设置视频播放地址
615 | *
616 | * @param videoPath
617 | * @return
618 | */
619 | public VideoPlayerView setPlayerPath(String videoPath) {
620 | if (videoPath != null) {
621 | this.videoPath = videoPath;
622 | playerView.setVideoPath(videoPath);
623 | }
624 | return this;
625 | }
626 |
627 | /**
628 | * 设置播放地址Uri
629 | *
630 | * @param videoPath
631 | * @return
632 | */
633 | public VideoPlayerView setPlayerUriPath(Uri videoPath) {
634 | if (videoPath != null) {
635 | playerView.setVideoURI(videoPath);
636 | }
637 | return this;
638 | }
639 |
640 | /**
641 | * 设置静音
642 | *
643 | * @return
644 | */
645 | public VideoPlayerView setSoundOff() {
646 | playerView.setVolume(0.0f, 0.0f);
647 | return this;
648 | }
649 |
650 | /**
651 | * 开启音量
652 | *
653 | * @return
654 | */
655 | public VideoPlayerView setSoundOpen() {
656 | playerView.setVolume(0.0f, 1f);
657 | return this;
658 | }
659 |
660 | /**
661 | * 设置音量大小
662 | *
663 | * @param sound 0.0---1.0
664 | * @return
665 | */
666 | public VideoPlayerView setSound(float sound) {
667 | playerView.setVolume(0.0f, sound);
668 | return this;
669 | }
670 |
671 | /**
672 | * 设置是否显示底部控制布局
673 | *
674 | * @param isShowPlayerBottomBar
675 | * @return
676 | */
677 | public VideoPlayerView isShowPlayerBottomBar(boolean isShowPlayerBottomBar) {
678 |
679 | this.isShowPlayerBottomBar = isShowPlayerBottomBar;
680 |
681 | if (isShowPlayerBottomBar) {
682 | mPlayerBottomBarLayout.setVisibility(VISIBLE);
683 | } else {
684 | mPlayerBottomBarLayout.setVisibility(GONE);
685 | }
686 |
687 | return this;
688 | }
689 |
690 | /**
691 | * 设置是否显示横屏按钮
692 | *
693 | * @param isShowFullScreenBtn
694 | * @return
695 | */
696 | public VideoPlayerView isShowFullScreenBtn(boolean isShowFullScreenBtn) {
697 |
698 | if (isShowFullScreenBtn) {
699 | mFullScreenBtnIv.setVisibility(VISIBLE);
700 | } else {
701 | mFullScreenBtnIv.setVisibility(GONE);
702 | }
703 | return this;
704 | }
705 |
706 | /**
707 | * 设置视频显示比例
708 | *
709 | * @param screenRatio
710 | * @return
711 | */
712 | public VideoPlayerView setScreenRatio(int screenRatio) {
713 | playerView.setDisplayAspectRatio(screenRatio);
714 | return this;
715 | }
716 |
717 | /**
718 | * 是否显示播放loading
719 | *
720 | * @param isShowLoadingView
721 | * @return
722 | */
723 | public VideoPlayerView isShowLoadingView(boolean isShowLoadingView) {
724 | if (isShowLoadingView) {
725 | mLoadingView.setVisibility(VISIBLE);
726 | } else {
727 | mLoadingView.setVisibility(GONE);
728 | }
729 | return this;
730 | }
731 |
732 | /**
733 | * 是否允许点击屏幕暂停播放
734 | *
735 | * @param isTouchForPause
736 | * @return
737 | */
738 | public VideoPlayerView isTouchForPause(boolean isTouchForPause) {
739 | this.isTouchForPause = isTouchForPause;
740 | return this;
741 | }
742 |
743 | /**
744 | * 是否允许循环播放
745 | *
746 | * @param repeatPlay
747 | * @return
748 | */
749 | public VideoPlayerView isRepeatPlay(boolean repeatPlay) {
750 | this.repeatPlay = repeatPlay;
751 | return this;
752 | }
753 |
754 | /**
755 | * 是否显示中间播放按钮 默认显示 不显示的就屏蔽触摸屏幕暂停
756 | *
757 | * @param isShowCenterPlayerBtn
758 | * @return
759 | */
760 | public VideoPlayerView isShowCenterPlayerBtn(boolean isShowCenterPlayerBtn) {
761 | this.isShowCenterPlayerBtn = isShowCenterPlayerBtn;
762 | if (!isShowCenterPlayerBtn) {
763 | mCenterPlayerBtnIv.setVisibility(GONE);
764 | }
765 | return this;
766 | }
767 |
768 |
769 | /**
770 | * 是否正在播放
771 | *
772 | * @return
773 | */
774 | public boolean isPlaying() {
775 | return playerView.isPlaying();
776 | }
777 |
778 | /**
779 | * 获取当前播放进度百分比
780 | *
781 | * @return currentProgress
782 | */
783 | public int getCurrentProgress() {
784 |
785 | int mCurrentProgress = 0;
786 |
787 | //获取当前播放进度和总时间
788 | double currentPosition = (double) playerView.getCurrentPosition();
789 | double totalDuration = (double) playerView.getDuration();
790 |
791 | if (totalDuration != -1) {
792 | mCurrentProgress = (int) (currentPosition / (totalDuration) * 100);
793 | }
794 |
795 | return mCurrentProgress;
796 | }
797 |
798 | /**
799 | * 获取播放总时间
800 | *
801 | * @return
802 | */
803 | public String getTotalTime() {
804 | int duration = 0;
805 | if (playerView != null) {
806 | duration = (int) playerView.getDuration();
807 | }
808 | return getFormatTime(duration);
809 | }
810 |
811 | /**
812 | * 开启全屏按钮点击事件监听
813 | *
814 | * @param activity
815 | * @return
816 | */
817 | public VideoPlayerView setOnFullScreenClickListener(Activity activity) {
818 | mActivity = activity;
819 | return this;
820 | }
821 |
822 | public VideoPlayerView setOnCurrentProgressChange(OnCurrentProgressChange mOnCurrentProgressChange) {
823 | this.mOnCurrentProgressChange = mOnCurrentProgressChange;
824 | return this;
825 | }
826 |
827 | public VideoPlayerView setOnVideoCompletionListener(OnVideoCompletionListener mOnVideoCompletionListener) {
828 | this.mOnVideoCompletionListener = mOnVideoCompletionListener;
829 | return this;
830 | }
831 |
832 | public interface OnVideoCompletionListener {
833 | void doOnVideoCompletionListener();
834 | }
835 |
836 | public interface OnCurrentProgressChange {
837 | void onCurrentProgressChange(int currentProgress, String currentTime);
838 |
839 | }
840 |
841 | }
842 |
--------------------------------------------------------------------------------
/playerview/src/main/jniLibs/arm64-v8a/libpldroidplayer.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lygttpod/SuperVideoPlayer/b8bd05e539fea3ee6ab5b3df5441b8c100bb6d0e/playerview/src/main/jniLibs/arm64-v8a/libpldroidplayer.so
--------------------------------------------------------------------------------
/playerview/src/main/jniLibs/armeabi-v7a/libpldroidplayer.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lygttpod/SuperVideoPlayer/b8bd05e539fea3ee6ab5b3df5441b8c100bb6d0e/playerview/src/main/jniLibs/armeabi-v7a/libpldroidplayer.so
--------------------------------------------------------------------------------
/playerview/src/main/jniLibs/armeabi/libpldroidplayer.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lygttpod/SuperVideoPlayer/b8bd05e539fea3ee6ab5b3df5441b8c100bb6d0e/playerview/src/main/jniLibs/armeabi/libpldroidplayer.so
--------------------------------------------------------------------------------
/playerview/src/main/jniLibs/x86/libpldroidplayer.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lygttpod/SuperVideoPlayer/b8bd05e539fea3ee6ab5b3df5441b8c100bb6d0e/playerview/src/main/jniLibs/x86/libpldroidplayer.so
--------------------------------------------------------------------------------
/playerview/src/main/res/drawable/default_seekbar.xml:
--------------------------------------------------------------------------------
1 |
2 |