├── .gitignore
├── .idea
├── .name
├── compiler.xml
├── copyright
│ └── profiles_settings.xml
├── dictionaries
│ └── JUSFOUN.xml
├── encodings.xml
├── gradle.xml
├── misc.xml
├── modules.xml
├── runConfigurations.xml
└── vcs.xml
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── cn
│ │ └── demo
│ │ └── videolist
│ │ └── ApplicationTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── cn
│ │ │ └── demo
│ │ │ └── videolist
│ │ │ ├── CustomMediaContoller.java
│ │ │ ├── MainActivity.java
│ │ │ ├── VSeekBar.java
│ │ │ ├── VideoItemData.java
│ │ │ ├── VideoListData.java
│ │ │ ├── VideoListLayout.java
│ │ │ ├── VideoPlayView.java
│ │ │ └── media
│ │ │ ├── IMediaController.java
│ │ │ ├── IRenderView.java
│ │ │ ├── IjkVideoView.java
│ │ │ ├── InfoHudViewHolder.java
│ │ │ ├── MeasureHelper.java
│ │ │ ├── MediaPlayerCompat.java
│ │ │ ├── MediaPlayerService.java
│ │ │ ├── Settings.java
│ │ │ ├── SurfaceRenderView.java
│ │ │ ├── TableLayoutBinder.java
│ │ │ ├── TextureRenderView.java
│ │ │ └── VideoAdapter.java
│ ├── jniLibs
│ │ ├── arm64-v8a
│ │ │ ├── libijkffmpeg.so
│ │ │ ├── libijkplayer.so
│ │ │ └── libijksdl.so
│ │ └── x86_64
│ │ │ ├── libijkffmpeg.so
│ │ │ ├── libijkplayer.so
│ │ │ └── libijksdl.so
│ └── res
│ │ ├── drawable
│ │ ├── app_video_center_bg.xml
│ │ ├── bg_seek.xml
│ │ └── bg_thumb.xml
│ │ ├── layout
│ │ ├── activity_main.xml
│ │ ├── item_video.xml
│ │ ├── layout_video_list.xml
│ │ ├── media_contoller.xml
│ │ ├── table_media_info.xml
│ │ ├── table_media_info_row1.xml
│ │ ├── table_media_info_row2.xml
│ │ ├── table_media_info_section.xml
│ │ └── view_video_item.xml
│ │ ├── mipmap-hdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-mdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xhdpi
│ │ ├── full_screen_icon.png
│ │ ├── ic_launcher.png
│ │ ├── sound_mult_icon.png
│ │ ├── sound_open_icon.png
│ │ ├── video_play_btn.png
│ │ └── video_stop_btn.png
│ │ ├── mipmap-xxhdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xxxhdpi
│ │ └── ic_launcher.png
│ │ ├── raw
│ │ └── video_list.json
│ │ ├── values-w820dp
│ │ └── dimens.xml
│ │ └── values
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ ├── strings_pref.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── cn
│ └── demo
│ └── videolist
│ └── ExampleUnitTest.java
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── screenshots
└── vidiolist.gif
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 |
--------------------------------------------------------------------------------
/.idea/.name:
--------------------------------------------------------------------------------
1 | VideoListDemo
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.idea/dictionaries/JUSFOUN.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
23 |
24 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | Android Lint
39 |
40 |
41 | Java
42 |
43 |
44 | Java language level migration aidsJava
45 |
46 |
47 | Performance issuesJava
48 |
49 |
50 | Portability issuesJava
51 |
52 |
53 | Probable bugsJava
54 |
55 |
56 |
57 |
58 | Android
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 | 1.8
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # VideoListDemo
2 | 一个视频播放列表demo,视频播放使用的ijkplayer,正在播放视频拖动到不可见区域小窗口播放,可全屏切换
3 | 
4 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 23
5 | buildToolsVersion "23.0.3"
6 |
7 | defaultConfig {
8 | applicationId "cn.demo.videolist"
9 | minSdkVersion 15
10 | targetSdkVersion 23
11 | versionCode 1
12 | versionName "1.0"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | }
21 |
22 | dependencies {
23 | compile fileTree(include: ['*.jar'], dir: 'libs')
24 | testCompile 'junit:junit:4.12'
25 | compile 'com.android.support:appcompat-v7:23.3.0'
26 | compile 'com.android.support:recyclerview-v7:23.+'
27 | compile 'com.google.code.gson:gson:2.6.2'
28 |
29 | compile 'tv.danmaku.ijk.media:ijkplayer-java:0.5.1'
30 | compile 'tv.danmaku.ijk.media:ijkplayer-armv7a:0.5.1'
31 | compile 'tv.danmaku.ijk.media:ijkplayer-exo:0.5.1'
32 | compile 'tv.danmaku.ijk.media:ijkplayer-x86:0.5.1'
33 | compile 'tv.danmaku.ijk.media:ijkplayer-armv5:0.5.1'
34 | }
35 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in C:\Users\JUSFOUN\AppData\Local\Android\sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/cn/demo/videolist/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package cn.demo.videolist;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
12 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/demo/videolist/CustomMediaContoller.java:
--------------------------------------------------------------------------------
1 | package cn.demo.videolist;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.content.pm.ActivityInfo;
6 | import android.graphics.Bitmap;
7 | import android.graphics.PointF;
8 | import android.graphics.Rect;
9 | import android.media.AudioManager;
10 | import android.os.Handler;
11 | import android.os.Message;
12 | import android.util.DisplayMetrics;
13 | import android.util.Log;
14 | import android.view.GestureDetector;
15 | import android.view.MotionEvent;
16 | import android.view.Surface;
17 | import android.view.View;
18 | import android.view.WindowManager;
19 | import android.widget.ImageView;
20 | import android.widget.LinearLayout;
21 | import android.widget.MediaController;
22 | import android.widget.ProgressBar;
23 | import android.widget.RelativeLayout;
24 | import android.widget.SeekBar;
25 | import android.widget.TextView;
26 |
27 | import cn.demo.videolist.media.IMediaController;
28 | import cn.demo.videolist.media.IjkVideoView;
29 | import tv.danmaku.ijk.media.player.IMediaPlayer;
30 |
31 | /**
32 | * @Description ${控制器}
33 | */
34 | public class CustomMediaContoller implements IMediaController {
35 |
36 | private static final int SET_VIEW_HIDE = 1;
37 | private static final int TIME_OUT = 5000;
38 | private static final int MESSAGE_SHOW_PROGRESS = 2;
39 | private static final int PAUSE_IMAGE_HIDE = 3;
40 | private static final int MESSAGE_SEEK_NEW_POSITION = 4;
41 | private static final int MESSAGE_HIDE_CONTOLL = 5;
42 | private View itemView;
43 | private View view;
44 | private boolean isShow;
45 | private IjkVideoView videoView;
46 | private boolean isScroll;
47 |
48 | private SeekBar seekBar;
49 | AudioManager audioManager;
50 | private ProgressBar progressBar;
51 |
52 | private boolean isSound;
53 | private boolean isDragging;
54 |
55 | private boolean isPause;
56 |
57 | private boolean isShowContoller;
58 | private ImageView sound, full, play;
59 | private TextView time, allTime,seekTxt;
60 | private PointF lastPoint;
61 | private Context context;
62 | private ImageView pauseImage;
63 | private Bitmap bitmap;
64 | private GestureDetector detector;
65 |
66 | private RelativeLayout show;
67 | private VSeekBar brightness_seek, sound_seek;
68 | private LinearLayout brightness_layout, sound_layout;
69 | private Handler handler = new Handler() {
70 | @Override
71 | public void handleMessage(Message msg) {
72 | super.handleMessage(msg);
73 | switch (msg.what) {
74 | case SET_VIEW_HIDE:
75 | isShow = false;
76 | itemView.setVisibility(View.GONE);
77 | break;
78 | case MESSAGE_SHOW_PROGRESS:
79 | setProgress();
80 | if (!isDragging && isShow) {
81 | msg = obtainMessage(MESSAGE_SHOW_PROGRESS);
82 | sendMessageDelayed(msg, 1000);
83 | }
84 | break;
85 | case PAUSE_IMAGE_HIDE:
86 | pauseImage.setVisibility(View.GONE);
87 | break;
88 | case MESSAGE_SEEK_NEW_POSITION:
89 | if (newPosition >= 0) {
90 | videoView.seekTo((int) newPosition);
91 | newPosition = -1;
92 | }
93 | break;
94 | case MESSAGE_HIDE_CONTOLL:
95 | seekTxt.setVisibility(View.GONE);
96 | brightness_layout.setVisibility(View.GONE);
97 | sound_layout.setVisibility(View.GONE);
98 | break;
99 | }
100 | }
101 | };
102 |
103 | public CustomMediaContoller(Context context, View view) {
104 | this.view = view;
105 | itemView = view.findViewById(R.id.media_contoller);
106 | this.videoView = (IjkVideoView) view.findViewById(R.id.main_video);
107 | itemView.setVisibility(View.GONE);
108 | isShow = false;
109 | isDragging = false;
110 |
111 | isShowContoller = true;
112 | this.context = context;
113 | audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
114 | initView();
115 | initAction();
116 | }
117 |
118 | public void initView() {
119 | progressBar = (ProgressBar) view.findViewById(R.id.loading);
120 | seekBar = (SeekBar) itemView.findViewById(R.id.seekbar);
121 | allTime = (TextView) itemView.findViewById(R.id.all_time);
122 | time = (TextView) itemView.findViewById(R.id.time);
123 | full = (ImageView) itemView.findViewById(R.id.full);
124 | sound = (ImageView) itemView.findViewById(R.id.sound);
125 | play = (ImageView) itemView.findViewById(R.id.player_btn);
126 | pauseImage = (ImageView) view.findViewById(R.id.pause_image);
127 |
128 | brightness_layout = (LinearLayout) view.findViewById(R.id.brightness_layout);
129 | brightness_seek = (VSeekBar) view.findViewById(R.id.brightness_seek);
130 | sound_layout = (LinearLayout) view.findViewById(R.id.sound_layout);
131 | sound_seek = (VSeekBar) view.findViewById(R.id.sound_seek);
132 | show = (RelativeLayout) view.findViewById(R.id.show);
133 | seekTxt= (TextView) view.findViewById(R.id.seekTxt);
134 | }
135 |
136 | private void initAction() {
137 |
138 | sound_seek.setEnabled(false);
139 | brightness_seek.setEnabled(false);
140 | isSound = false;
141 | detector = new GestureDetector(context, new PlayGestureListener());
142 | mMaxVolume = ((AudioManager) context.getSystemService(Context.AUDIO_SERVICE))
143 | .getStreamMaxVolume(AudioManager.STREAM_MUSIC);
144 | seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
145 | @Override
146 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
147 | String string = generateTime((long) (duration * progress * 1.0f / 100));
148 | time.setText(string);
149 | }
150 |
151 | @Override
152 | public void onStartTrackingTouch(SeekBar seekBar) {
153 | setProgress();
154 | isDragging = true;
155 | audioManager.setStreamMute(AudioManager.STREAM_MUSIC, true);
156 | handler.removeMessages(MESSAGE_SHOW_PROGRESS);
157 | show();
158 | handler.removeMessages(SET_VIEW_HIDE);
159 | }
160 |
161 | @Override
162 | public void onStopTrackingTouch(SeekBar seekBar) {
163 | isDragging = false;
164 | videoView.seekTo((int) (duration * seekBar.getProgress() * 1.0f / 100));
165 | handler.removeMessages(MESSAGE_SHOW_PROGRESS);
166 | audioManager.setStreamMute(AudioManager.STREAM_MUSIC, false);
167 | isDragging = false;
168 | handler.sendEmptyMessageDelayed(MESSAGE_SHOW_PROGRESS, 1000);
169 | show();
170 | }
171 | });
172 |
173 | view.setOnTouchListener(new View.OnTouchListener() {
174 | @Override
175 | public boolean onTouch(View v, MotionEvent event) {
176 | if (detector.onTouchEvent(event))
177 | return true;
178 |
179 | // 处理手势结束
180 | switch (event.getAction() & MotionEvent.ACTION_MASK) {
181 | case MotionEvent.ACTION_UP:
182 | endGesture();
183 | break;
184 | }
185 | return false;
186 | }
187 | });
188 |
189 | itemView.setOnTouchListener(new View.OnTouchListener() {
190 | @Override
191 | public boolean onTouch(View v, MotionEvent event) {
192 | Log.e("custommedia", "event");
193 |
194 | Rect seekRect = new Rect();
195 | seekBar.getHitRect(seekRect);
196 |
197 | if ((event.getY() >= (seekRect.top - 50)) && (event.getY() <= (seekRect.bottom + 50))) {
198 |
199 | float y = seekRect.top + seekRect.height() / 2;
200 | //seekBar only accept relative x
201 | float x = event.getX() - seekRect.left;
202 | if (x < 0) {
203 | x = 0;
204 | } else if (x > seekRect.width()) {
205 | x = seekRect.width();
206 | }
207 | MotionEvent me = MotionEvent.obtain(event.getDownTime(), event.getEventTime(),
208 | event.getAction(), x, y, event.getMetaState());
209 | return seekBar.onTouchEvent(me);
210 |
211 | }
212 | return false;
213 | }
214 | });
215 |
216 | videoView.setOnInfoListener(new IMediaPlayer.OnInfoListener() {
217 | @Override
218 | public boolean onInfo(IMediaPlayer mp, int what, int extra) {
219 |
220 | Log.e("setOnInfoListener", what + "");
221 | switch (what) {
222 | case IMediaPlayer.MEDIA_INFO_BUFFERING_START:
223 | //开始缓冲
224 | if (progressBar.getVisibility() == View.GONE)
225 | progressBar.setVisibility(View.VISIBLE);
226 | break;
227 | case IMediaPlayer.MEDIA_INFO_BUFFERING_END:
228 | //开始播放
229 | progressBar.setVisibility(View.GONE);
230 | break;
231 |
232 | case IMediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START:
233 | // statusChange(STATUS_PLAYING);
234 | progressBar.setVisibility(View.GONE);
235 | break;
236 |
237 | case IMediaPlayer.MEDIA_INFO_AUDIO_RENDERING_START:
238 | progressBar.setVisibility(View.GONE);
239 | break;
240 | }
241 | return false;
242 | }
243 | });
244 |
245 | sound.setOnClickListener(new View.OnClickListener() {
246 | @Override
247 | public void onClick(View v) {
248 | if (isSound) {
249 | //静音
250 | sound.setImageResource(R.mipmap.sound_mult_icon);
251 | audioManager.setStreamMute(AudioManager.STREAM_MUSIC, true);
252 | } else {
253 | //取消静音
254 | sound.setImageResource(R.mipmap.sound_open_icon);
255 | audioManager.setStreamMute(AudioManager.STREAM_MUSIC, false);
256 | }
257 | isSound = !isSound;
258 | }
259 | });
260 |
261 |
262 | play.setOnClickListener(new View.OnClickListener() {
263 | @Override
264 | public void onClick(View v) {
265 | if (videoView.isPlaying()) {
266 | pause();
267 | } else {
268 | reStart();
269 | }
270 | }
271 | });
272 |
273 | full.setOnClickListener(new View.OnClickListener() {
274 | @Override
275 | public void onClick(View v) {
276 | Log.e("full", "full");
277 | if (getScreenOrientation((Activity) context) == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
278 | ((Activity) context).setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
279 | } else {
280 | ((Activity) context).setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
281 | }
282 | }
283 |
284 | });
285 | }
286 |
287 | public void start() {
288 | pauseImage.setVisibility(View.GONE);
289 | itemView.setVisibility(View.GONE);
290 | play.setImageResource(R.mipmap.video_stop_btn);
291 | progressBar.setVisibility(View.VISIBLE);
292 | }
293 |
294 | public void pause() {
295 | play.setImageResource(R.mipmap.video_play_btn);
296 | videoView.pause();
297 | bitmap = videoView.getBitmap();
298 | if (bitmap != null) {
299 | pauseImage.setImageBitmap(bitmap);
300 | pauseImage.setVisibility(View.VISIBLE);
301 | }
302 | }
303 |
304 | public void reStart() {
305 | play.setImageResource(R.mipmap.video_stop_btn);
306 | videoView.start();
307 | if (bitmap != null) {
308 | handler.sendEmptyMessageDelayed(PAUSE_IMAGE_HIDE, 100);
309 | bitmap.recycle();
310 | bitmap = null;
311 | // pauseImage.setVisibility(View.GONE);
312 | }
313 | }
314 |
315 | private long duration;
316 |
317 |
318 | public void setShowContoller(boolean isShowContoller) {
319 | this.isShowContoller = isShowContoller;
320 | handler.removeMessages(SET_VIEW_HIDE);
321 | itemView.setVisibility(View.GONE);
322 | }
323 |
324 | public int getScreenOrientation(Activity activity) {
325 | int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
326 | DisplayMetrics dm = new DisplayMetrics();
327 | activity.getWindowManager().getDefaultDisplay().getMetrics(dm);
328 | int width = dm.widthPixels;
329 | int height = dm.heightPixels;
330 | int orientation;
331 | // if the device's natural orientation is portrait:
332 | if ((rotation == Surface.ROTATION_0
333 | || rotation == Surface.ROTATION_180) && height > width ||
334 | (rotation == Surface.ROTATION_90
335 | || rotation == Surface.ROTATION_270) && width > height) {
336 | switch (rotation) {
337 | case Surface.ROTATION_0:
338 | orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
339 | break;
340 | case Surface.ROTATION_90:
341 | orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
342 | break;
343 | case Surface.ROTATION_180:
344 | orientation =
345 | ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
346 | break;
347 | case Surface.ROTATION_270:
348 | orientation =
349 | ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
350 | break;
351 | default:
352 | orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
353 | break;
354 | }
355 | }
356 | // if the device's natural orientation is landscape or if the device
357 | // is square:
358 | else {
359 | switch (rotation) {
360 | case Surface.ROTATION_0:
361 | orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
362 | break;
363 | case Surface.ROTATION_90:
364 | orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
365 | break;
366 | case Surface.ROTATION_180:
367 | orientation =
368 | ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
369 | break;
370 | case Surface.ROTATION_270:
371 | orientation =
372 | ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
373 | break;
374 | default:
375 | orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
376 | break;
377 | }
378 | }
379 |
380 | return orientation;
381 | }
382 |
383 | @Override
384 | public void hide() {
385 | if (isShow) {
386 | handler.removeMessages(MESSAGE_SHOW_PROGRESS);
387 | isShow = false;
388 | handler.removeMessages(SET_VIEW_HIDE);
389 | itemView.setVisibility(View.GONE);
390 | }
391 | }
392 |
393 | @Override
394 | public boolean isShowing() {
395 | return isShow;
396 | }
397 |
398 | @Override
399 | public void setAnchorView(View view) {
400 | }
401 |
402 | @Override
403 | public void setEnabled(boolean enabled) {
404 | }
405 |
406 | @Override
407 | public void setMediaPlayer(MediaController.MediaPlayerControl player) {
408 | }
409 |
410 | @Override
411 | public void show(int timeout) {
412 | handler.sendEmptyMessageDelayed(SET_VIEW_HIDE, timeout);
413 | }
414 |
415 | @Override
416 | public void show() {
417 | if (!isShowContoller)
418 | return;
419 | isShow = true;
420 | progressBar.setVisibility(View.GONE);
421 | itemView.setVisibility(View.VISIBLE);
422 | handler.sendEmptyMessage(MESSAGE_SHOW_PROGRESS);
423 | show(TIME_OUT);
424 | }
425 |
426 | @Override
427 | public void showOnce(View view) {
428 | }
429 |
430 | private String generateTime(long time) {
431 | int totalSeconds = (int) (time / 1000);
432 | int seconds = totalSeconds % 60;
433 | int minutes = (totalSeconds / 60) % 60;
434 | int hours = totalSeconds / 3600;
435 | return hours > 0 ? String.format("%02d:%02d:%02d", hours, minutes, seconds) : String.format("%02d:%02d", minutes, seconds);
436 | }
437 |
438 | public void setVisiable() {
439 | show();
440 | }
441 |
442 | private long setProgress() {
443 | if (isDragging) {
444 | return 0;
445 | }
446 |
447 | long position = videoView.getCurrentPosition();
448 | long duration = videoView.getDuration();
449 | this.duration = duration;
450 | if (!generateTime(duration).equals(allTime.getText().toString()))
451 | allTime.setText(generateTime(duration));
452 | if (seekBar != null) {
453 | if (duration > 0) {
454 | long pos = 100L * position / duration;
455 | seekBar.setProgress((int) pos);
456 | }
457 | int percent = videoView.getBufferPercentage();
458 | seekBar.setSecondaryProgress(percent);
459 | }
460 | String string = generateTime((long) (duration * seekBar.getProgress() * 1.0f / 100));
461 | time.setText(string);
462 | return position;
463 | }
464 |
465 | private VedioIsPause vedioIsPause;
466 |
467 | public interface VedioIsPause {
468 | void pause(boolean pause);
469 | }
470 |
471 | public void setPauseImageHide() {
472 | pauseImage.setVisibility(View.GONE);
473 | }
474 |
475 | public class PlayGestureListener extends GestureDetector.SimpleOnGestureListener {
476 |
477 | private boolean firstTouch;
478 | private boolean volumeControl;
479 | private boolean seek;
480 |
481 | @Override
482 | public boolean onDown(MotionEvent e) {
483 | firstTouch = true;
484 | handler.removeMessages(SET_VIEW_HIDE);
485 | //横屏下拦截事件
486 | if (getScreenOrientation((Activity) context) == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
487 | return true;
488 | }else {
489 | return super.onDown(e);
490 | }
491 | }
492 |
493 | @Override
494 | public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
495 | float x = e1.getX() - e2.getX();
496 | float y = e1.getY() - e2.getY();
497 | if (firstTouch) {
498 | seek = Math.abs(distanceX) >= Math.abs(distanceY);
499 | volumeControl = e1.getX() < view.getMeasuredWidth() * 0.5;
500 | firstTouch = false;
501 | }
502 |
503 | if (seek) {
504 | onProgressSlide(-x / view.getWidth(),e1.getX()/view.getWidth());
505 | } else {
506 | float percent = y / view.getHeight();
507 | if (volumeControl) {
508 | onVolumeSlide(percent);
509 | } else {
510 | onBrightnessSlide(percent);
511 | }
512 | }
513 | return super.onScroll(e1, e2, distanceX, distanceY);
514 | }
515 | }
516 |
517 | private int volume = -1;
518 | private float brightness = -1;
519 | private long newPosition = 1;
520 | private int mMaxVolume;
521 |
522 | /**
523 | * 手势结束
524 | */
525 | private void endGesture() {
526 | volume = -1;
527 | brightness = -1f;
528 | if (newPosition >= 0) {
529 | handler.removeMessages(MESSAGE_SEEK_NEW_POSITION);
530 | handler.sendEmptyMessage(MESSAGE_SEEK_NEW_POSITION);
531 | }
532 | handler.removeMessages(MESSAGE_HIDE_CONTOLL);
533 | handler.sendEmptyMessageDelayed(MESSAGE_HIDE_CONTOLL, 500);
534 |
535 | }
536 |
537 | /**
538 | * 滑动改变声音大小
539 | *
540 | * @param percent
541 | */
542 | private void onVolumeSlide(float percent) {
543 | if (volume == -1) {
544 | volume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
545 | if (volume < 0)
546 | volume = 0;
547 | }
548 | // hide();
549 |
550 | int index = (int) (percent * mMaxVolume) + volume;
551 | if (index > mMaxVolume)
552 | index = mMaxVolume;
553 | else if (index < 0)
554 | index = 0;
555 |
556 | // 变更声音
557 | audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, index, 0);
558 |
559 | if (sound_layout.getVisibility()==View.GONE)
560 | sound_layout.setVisibility(View.VISIBLE);
561 | // 变更进度条
562 | int i = (int) (index * 1.0f / mMaxVolume * 100);
563 | if (i == 0) {
564 | sound.setImageResource(R.mipmap.sound_mult_icon);
565 | } else {
566 | sound.setImageResource(R.mipmap.sound_open_icon);
567 | }
568 | sound_seek.setProgress(i);
569 | }
570 |
571 | /**
572 | * 滑动跳转
573 | *
574 | * @param percent 移动比例
575 | * @param downPer 按下比例
576 | */
577 | private void onProgressSlide(float percent,float downPer) {
578 | long position = videoView.getCurrentPosition();
579 | long duration = videoView.getDuration();
580 | long deltaMax = Math.min(100 * 1000, duration - position);
581 | long delta = (long) (deltaMax * percent);
582 |
583 | newPosition = delta + position;
584 | if (newPosition > duration) {
585 | newPosition = duration;
586 | } else if (newPosition <= 0) {
587 | newPosition = 0;
588 | delta = -position;
589 | }
590 | int showDelta = (int) delta / 1000;
591 | Log.e("showdelta", ((downPer +percent)*100) + "");
592 | if (showDelta != 0) {
593 | if (seekTxt.getVisibility()==View.GONE)
594 | seekTxt.setVisibility(View.VISIBLE);
595 | String current=generateTime(newPosition);
596 | seekTxt.setText(current+"/"+allTime.getText());
597 | }
598 | }
599 |
600 | /**
601 | * 滑动改变亮度
602 | *
603 | * @param percent
604 | */
605 | private void onBrightnessSlide(float percent) {
606 | if (brightness < 0) {
607 | brightness = ((Activity) context).getWindow().getAttributes().screenBrightness;
608 | if (brightness <= 0.00f) {
609 | brightness = 0.50f;
610 | } else if (brightness < 0.01f) {
611 | brightness = 0.01f;
612 | }
613 | }
614 | Log.d(this.getClass().getSimpleName(), "brightness:" + brightness + ",percent:" + percent);
615 | WindowManager.LayoutParams lpa = ((Activity) context).getWindow().getAttributes();
616 | lpa.screenBrightness = brightness + percent;
617 | if (lpa.screenBrightness > 1.0f) {
618 | lpa.screenBrightness = 1.0f;
619 | } else if (lpa.screenBrightness < 0.01f) {
620 | lpa.screenBrightness = 0.01f;
621 | }
622 |
623 | if (brightness_layout.getVisibility()==View.GONE)
624 | brightness_layout.setVisibility(View.VISIBLE);
625 |
626 | brightness_seek.setProgress((int) (lpa.screenBrightness * 100));
627 | ((Activity) context).getWindow().setAttributes(lpa);
628 |
629 | }
630 | }
631 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/demo/videolist/MainActivity.java:
--------------------------------------------------------------------------------
1 | package cn.demo.videolist;
2 |
3 | import android.content.pm.ActivityInfo;
4 | import android.content.res.Configuration;
5 | import android.os.Bundle;
6 | import android.support.v7.app.AppCompatActivity;
7 | import android.view.KeyEvent;
8 | import android.view.Window;
9 |
10 | public class MainActivity extends AppCompatActivity {
11 |
12 | @Override
13 | protected void onCreate(Bundle savedInstanceState) {
14 | requestWindowFeature(Window.FEATURE_NO_TITLE);
15 | super.onCreate(savedInstanceState);
16 | setContentView(R.layout.activity_main);
17 | initActions();
18 | }
19 |
20 | private void initActions() {
21 |
22 | }
23 |
24 | @Override
25 | public boolean onKeyUp(int keyCode, KeyEvent event) {
26 | if (keyCode == KeyEvent.KEYCODE_BACK) {
27 | if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
28 | setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
29 | return true;
30 | }
31 | }
32 | return super.onKeyUp(keyCode, event);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/demo/videolist/VSeekBar.java:
--------------------------------------------------------------------------------
1 | package cn.demo.videolist;
2 |
3 | import android.annotation.TargetApi;
4 | import android.content.Context;
5 | import android.graphics.Canvas;
6 | import android.os.Build;
7 | import android.util.AttributeSet;
8 | import android.widget.SeekBar;
9 |
10 | /**
11 | * Author wangchenchen
12 | * CreateDate 2016/8/9.
13 | * Email wcc@jusfoun.com
14 | * Description 垂直seekbar
15 | */
16 | public class VSeekBar extends SeekBar {
17 | public VSeekBar(Context context, AttributeSet attrs) {
18 | super(context, attrs);
19 | }
20 |
21 | public VSeekBar(Context context) {
22 | super(context);
23 | }
24 |
25 | public VSeekBar(Context context, AttributeSet attrs, int defStyleAttr) {
26 | super(context, attrs, defStyleAttr);
27 | }
28 |
29 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
30 | public VSeekBar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
31 | super(context, attrs, defStyleAttr, defStyleRes);
32 | }
33 |
34 | @Override
35 | protected synchronized void onDraw(Canvas canvas) {
36 | canvas.rotate(-90);
37 | canvas.translate(-getHeight(),0);
38 | super.onDraw(canvas);
39 | }
40 |
41 | @Override
42 | protected void onSizeChanged(int w, int h, int oldw, int oldh) {
43 | super.onSizeChanged(h, w, oldw, oldh);
44 | }
45 |
46 | @Override
47 | protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
48 | super.onMeasure(heightMeasureSpec, widthMeasureSpec);
49 | setMeasuredDimension(getMeasuredHeight(),getMeasuredWidth());
50 | }
51 |
52 | @Override
53 | public synchronized void setProgress(int progress) {
54 | super.setProgress(progress);
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/demo/videolist/VideoItemData.java:
--------------------------------------------------------------------------------
1 | package cn.demo.videolist;
2 |
3 | import java.io.Serializable;
4 |
5 | /**
6 | * Author wangchenchen
7 | * Description video model
8 | */
9 | public class VideoItemData implements Serializable {
10 | int type;
11 | String cover;
12 | int playCount;
13 | String title;
14 | String mp4_url;
15 | String ptime;
16 | String vid;
17 | int length;
18 | String videosource;
19 |
20 | public int getType() {
21 | return type;
22 | }
23 |
24 | public void setType(int type) {
25 | this.type = type;
26 | }
27 |
28 | public String getCover() {
29 | return cover;
30 | }
31 |
32 | public void setCover(String cover) {
33 | this.cover = cover;
34 | }
35 |
36 | public int getPlayCount() {
37 | return playCount;
38 | }
39 |
40 | public void setPlayCount(int playCount) {
41 | this.playCount = playCount;
42 | }
43 |
44 | public String getTitle() {
45 | return title;
46 | }
47 |
48 | public void setTitle(String title) {
49 | this.title = title;
50 | }
51 |
52 | public String getMp4_url() {
53 | return mp4_url;
54 | }
55 |
56 | public void setMp4_url(String mp4_url) {
57 | this.mp4_url = mp4_url;
58 | }
59 |
60 | public String getPtime() {
61 | return ptime;
62 | }
63 |
64 | public void setPtime(String ptime) {
65 | this.ptime = ptime;
66 | }
67 |
68 | public String getVid() {
69 | return vid;
70 | }
71 |
72 | public void setVid(String vid) {
73 | this.vid = vid;
74 | }
75 |
76 | public int getLength() {
77 | return length;
78 | }
79 |
80 | public void setLength(int length) {
81 | this.length = length;
82 | }
83 |
84 | public String getVideosource() {
85 | return videosource;
86 | }
87 |
88 | public void setVideosource(String videosource) {
89 | this.videosource = videosource;
90 | }
91 |
92 | boolean isPlaying;
93 |
94 | public boolean isPlaying() {
95 | return isPlaying;
96 | }
97 |
98 | public void setPlaying(boolean playing) {
99 | isPlaying = playing;
100 | }
101 |
102 | int currentPositon;
103 |
104 | public int getCurrentPositon() {
105 | return currentPositon;
106 | }
107 |
108 | public void setCurrentPositon(int currentPositon) {
109 | this.currentPositon = currentPositon;
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/demo/videolist/VideoListData.java:
--------------------------------------------------------------------------------
1 | package cn.demo.videolist;
2 |
3 | import java.io.Serializable;
4 | import java.util.List;
5 |
6 | /**
7 | * Author wangchenchen
8 | * Description
9 | */
10 | public class VideoListData implements Serializable {
11 |
12 | private List list;
13 |
14 | public List getList() {
15 | return list;
16 | }
17 |
18 | public void setList(List list) {
19 | this.list = list;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/demo/videolist/VideoListLayout.java:
--------------------------------------------------------------------------------
1 | package cn.demo.videolist;
2 |
3 | import android.annotation.TargetApi;
4 | import android.app.Activity;
5 | import android.content.Context;
6 | import android.content.pm.ActivityInfo;
7 | import android.content.res.Configuration;
8 | import android.os.Build;
9 | import android.support.v7.widget.LinearLayoutManager;
10 | import android.support.v7.widget.RecyclerView;
11 | import android.util.AttributeSet;
12 | import android.util.Log;
13 | import android.view.LayoutInflater;
14 | import android.view.View;
15 | import android.view.ViewGroup;
16 | import android.widget.FrameLayout;
17 | import android.widget.ImageView;
18 | import android.widget.RelativeLayout;
19 |
20 | import com.google.gson.Gson;
21 |
22 | import java.io.BufferedReader;
23 | import java.io.InputStreamReader;
24 |
25 | import cn.demo.videolist.media.IjkVideoView;
26 | import cn.demo.videolist.media.VideoAdapter;
27 | import tv.danmaku.ijk.media.player.IMediaPlayer;
28 |
29 | /**
30 | * Author wangchenchen
31 | * CreateDate 2016/8/23.
32 | * Email wcc@jusfoun.com
33 | * Description
34 | */
35 | public class VideoListLayout extends RelativeLayout {
36 |
37 | private RecyclerView videoList;
38 | private LinearLayoutManager mLayoutManager;
39 | private VideoAdapter adapter;
40 | private FrameLayout videoLayout;
41 |
42 | private int postion = -1;
43 | private int lastPostion = -1;
44 | private Context context;
45 | private VideoPlayView videoItemView;
46 |
47 | private FrameLayout fullScreen;
48 | private VideoListData listData;
49 | private RelativeLayout smallLayout;
50 | private ImageView close;
51 |
52 | public VideoListLayout(Context context) {
53 | super(context);
54 | initView(context);
55 | initActions();
56 | }
57 |
58 | public VideoListLayout(Context context, AttributeSet attrs) {
59 | super(context, attrs);
60 | initView(context);
61 | initActions();
62 | }
63 |
64 | public VideoListLayout(Context context, AttributeSet attrs, int defStyleAttr) {
65 | super(context, attrs, defStyleAttr);
66 | initView(context);
67 | initActions();
68 | }
69 |
70 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
71 | public VideoListLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
72 | super(context, attrs, defStyleAttr, defStyleRes);
73 | initView(context);
74 | initActions();
75 | }
76 |
77 | private void initView(Context context){
78 | LayoutInflater.from(context).inflate(R.layout.layout_video_list,this,true);
79 | this.context = context;
80 | mLayoutManager = new LinearLayoutManager(context);
81 | videoList = (RecyclerView) findViewById(R.id.video_list);
82 | videoList.setLayoutManager(mLayoutManager);
83 |
84 | adapter = new VideoAdapter(context);
85 | videoList.setAdapter(adapter);
86 | fullScreen = (FrameLayout) findViewById(R.id.full_screen);
87 | videoLayout = (FrameLayout) findViewById(R.id.layout_video);
88 | videoItemView = new VideoPlayView(context);
89 |
90 | String data = readTextFileFromRawResourceId(context, R.raw.video_list);
91 | listData = new Gson().fromJson(data, VideoListData.class);
92 | adapter.refresh(listData.getList());
93 | smallLayout = (RelativeLayout) findViewById(R.id.small_layout);
94 | close = (ImageView) findViewById(R.id.close);
95 |
96 | }
97 |
98 | private void initActions() {
99 |
100 | close.setOnClickListener(new View.OnClickListener() {
101 | @Override
102 | public void onClick(View v) {
103 | if (videoItemView.isPlay()) {
104 | videoItemView.stop();
105 | postion = -1;
106 | lastPostion = -1;
107 | videoLayout.removeAllViews();
108 | smallLayout.setVisibility(View.GONE);
109 | }
110 | }
111 | });
112 |
113 | smallLayout.setOnClickListener(new View.OnClickListener() {
114 | @Override
115 | public void onClick(View v) {
116 | smallLayout.setVisibility(View.GONE);
117 | ((Activity)context).setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
118 | }
119 | });
120 |
121 | videoItemView.setCompletionListener(new VideoPlayView.CompletionListener() {
122 | @Override
123 | public void completion(IMediaPlayer mp) {
124 |
125 | //播放完还原播放界面
126 | if (smallLayout.getVisibility() == View.VISIBLE) {
127 | videoLayout.removeAllViews();
128 | smallLayout.setVisibility(View.GONE);
129 | videoItemView.setShowContoller(true);
130 | }
131 |
132 | FrameLayout frameLayout = (FrameLayout) videoItemView.getParent();
133 | videoItemView.release();
134 | if (frameLayout != null && frameLayout.getChildCount() > 0) {
135 | frameLayout.removeAllViews();
136 | View itemView = (View) frameLayout.getParent();
137 |
138 | if (itemView != null) {
139 | itemView.findViewById(R.id.showview).setVisibility(View.VISIBLE);
140 | }
141 | }
142 |
143 | lastPostion = -1;
144 | }
145 | });
146 |
147 | adapter.setClick(new VideoAdapter.onClick() {
148 | @Override
149 | public void onclick(int position) {
150 | VideoListLayout.this.postion = position;
151 |
152 | if (videoItemView.VideoStatus() == IjkVideoView.STATE_PAUSED) {
153 | if (position != lastPostion) {
154 |
155 | videoItemView.stop();
156 | videoItemView.release();
157 | }
158 | }
159 |
160 | if (smallLayout.getVisibility() == View.VISIBLE)
161 |
162 | {
163 | smallLayout.setVisibility(View.GONE);
164 | videoLayout.removeAllViews();
165 | videoItemView.setShowContoller(true);
166 | }
167 |
168 | if (lastPostion != -1)
169 |
170 | {
171 | ViewGroup last = (ViewGroup) videoItemView.getParent();//找到videoitemview的父类,然后remove
172 | if (last != null) {
173 | last.removeAllViews();
174 | View itemView = (View) last.getParent();
175 | if (itemView != null) {
176 | itemView.findViewById(R.id.showview).setVisibility(View.VISIBLE);
177 | }
178 | }
179 | }
180 |
181 | if (videoItemView.getParent() != null) {
182 | ((ViewGroup) videoItemView.getParent()).removeAllViews();
183 | }
184 |
185 | View view = videoList.findViewHolderForAdapterPosition(postion).itemView;
186 | FrameLayout frameLayout = (FrameLayout) view.findViewById(R.id.layout_video);
187 | frameLayout.removeAllViews();
188 | frameLayout.addView(videoItemView);
189 | videoItemView.start(listData.getList().get(position).getMp4_url());
190 | lastPostion = position;
191 | }
192 | });
193 |
194 | videoList.addOnChildAttachStateChangeListener(new RecyclerView.OnChildAttachStateChangeListener() {
195 | @Override
196 | public void onChildViewAttachedToWindow(View view) {
197 | int index = videoList.getChildAdapterPosition(view);
198 | view.findViewById(R.id.showview).setVisibility(View.VISIBLE);
199 | if (index == postion) {
200 | FrameLayout frameLayout = (FrameLayout) view.findViewById(R.id.layout_video);
201 | frameLayout.removeAllViews();
202 | if (videoItemView != null &&
203 | ((videoItemView.isPlay()) || videoItemView.VideoStatus() == IjkVideoView.STATE_PAUSED)) {
204 | view.findViewById(R.id.showview).setVisibility(View.GONE);
205 | }
206 |
207 | if (videoItemView.VideoStatus() == IjkVideoView.STATE_PAUSED) {
208 | if (videoItemView.getParent() != null)
209 | ((ViewGroup) videoItemView.getParent()).removeAllViews();
210 | frameLayout.addView(videoItemView);
211 | return;
212 | }
213 |
214 | if (smallLayout.getVisibility() == View.VISIBLE && videoItemView != null && videoItemView.isPlay()) {
215 | smallLayout.setVisibility(View.GONE);
216 | videoLayout.removeAllViews();
217 | videoItemView.setShowContoller(true);
218 | frameLayout.addView(videoItemView);
219 | }
220 | }
221 | }
222 |
223 | @Override
224 | public void onChildViewDetachedFromWindow(View view) {
225 | int index = videoList.getChildAdapterPosition(view);
226 | if (index == postion) {
227 | FrameLayout frameLayout = (FrameLayout) view.findViewById(R.id.layout_video);
228 | frameLayout.removeAllViews();
229 | if (smallLayout.getVisibility() == View.GONE && videoItemView != null
230 | && videoItemView.isPlay()) {
231 | videoLayout.removeAllViews();
232 | videoItemView.setShowContoller(false);
233 | videoLayout.addView(videoItemView);
234 | smallLayout.setVisibility(View.VISIBLE);
235 | }
236 | }
237 | }
238 | });
239 | }
240 |
241 | @Override
242 | protected void onConfigurationChanged(Configuration newConfig) {
243 | super.onConfigurationChanged(newConfig);
244 |
245 | if (videoItemView != null) {
246 | videoItemView.onChanged(newConfig);
247 | if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
248 | fullScreen.setVisibility(View.GONE);
249 | videoList.setVisibility(View.VISIBLE);
250 | fullScreen.removeAllViews();
251 | if (postion <= mLayoutManager.findLastVisibleItemPosition()
252 | && postion >= mLayoutManager.findFirstVisibleItemPosition()) {
253 | View view = videoList.findViewHolderForAdapterPosition(postion).itemView;
254 | FrameLayout frameLayout = (FrameLayout) view.findViewById(R.id.layout_video);
255 | frameLayout.removeAllViews();
256 | frameLayout.addView(videoItemView);
257 | videoItemView.setShowContoller(true);
258 | } else {
259 | videoLayout.removeAllViews();
260 | videoLayout.addView(videoItemView);
261 | videoItemView.setShowContoller(false);
262 | smallLayout.setVisibility(View.VISIBLE);
263 | }
264 | videoItemView.setContorllerVisiable();
265 | } else {
266 | ViewGroup viewGroup = (ViewGroup) videoItemView.getParent();
267 | if (viewGroup == null)
268 | return;
269 | viewGroup.removeAllViews();
270 | fullScreen.addView(videoItemView);
271 | smallLayout.setVisibility(View.GONE);
272 | videoList.setVisibility(View.GONE);
273 | fullScreen.setVisibility(View.VISIBLE);
274 | }
275 | } else {
276 | adapter.notifyDataSetChanged();
277 | videoList.setVisibility(View.VISIBLE);
278 | fullScreen.setVisibility(View.GONE);
279 | }
280 | }
281 |
282 | @Override
283 | protected void onAttachedToWindow() {
284 | super.onAttachedToWindow();
285 | if (videoItemView==null)
286 | videoItemView=new VideoPlayView(context);
287 | }
288 |
289 | @Override
290 | protected void onDetachedFromWindow() {
291 | super.onDetachedFromWindow();
292 | if (videoLayout == null)
293 | return;
294 | if (smallLayout.getVisibility() == View.VISIBLE) {
295 | smallLayout.setVisibility(View.GONE);
296 | videoLayout.removeAllViews();
297 | }
298 |
299 | if (postion != -1) {
300 | ViewGroup view = (ViewGroup) videoItemView.getParent();
301 | if (view != null) {
302 | view.removeAllViews();
303 | }
304 | }
305 | videoItemView.stop();
306 | videoItemView.release();
307 | videoItemView.onDestroy();
308 | videoItemView = null;
309 | }
310 |
311 | public String readTextFileFromRawResourceId(Context context, int resourceId) {
312 | StringBuilder builder = new StringBuilder();
313 |
314 | BufferedReader reader = new BufferedReader(new InputStreamReader(context.getResources().openRawResource(
315 | resourceId)));
316 |
317 | try {
318 | for (String line = reader.readLine(); line != null; line = reader.readLine()) {
319 | builder.append(line).append("\n");
320 | }
321 | } catch (Exception e) {
322 | throw new RuntimeException(e);
323 | }
324 |
325 | return builder.toString();
326 | }
327 | }
328 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/demo/videolist/VideoPlayView.java:
--------------------------------------------------------------------------------
1 | package cn.demo.videolist;
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.media.MediaPlayer;
8 | import android.net.Uri;
9 | import android.os.Handler;
10 | import android.util.AttributeSet;
11 | import android.util.Log;
12 | import android.view.LayoutInflater;
13 | import android.view.OrientationEventListener;
14 | import android.view.View;
15 | import android.view.ViewGroup;
16 | import android.view.WindowManager;
17 | import android.widget.RelativeLayout;
18 |
19 | import cn.demo.videolist.media.IjkVideoView;
20 | import tv.danmaku.ijk.media.player.IMediaPlayer;
21 |
22 | /**
23 | * Description 播放view
24 | */
25 | public class VideoPlayView extends RelativeLayout implements MediaPlayer.OnInfoListener, MediaPlayer.OnBufferingUpdateListener {
26 |
27 |
28 | private CustomMediaContoller mediaController;
29 | private View player_btn, view;
30 | private IjkVideoView mVideoView;
31 | private Handler handler = new Handler();
32 | private boolean isPause;
33 |
34 | private View rView;
35 | private Context mContext;
36 | private boolean portrait;
37 | // private OrientationEventListener orientationEventListener;
38 |
39 | public VideoPlayView(Context context) {
40 | super(context);
41 | mContext = context;
42 | initData();
43 | initViews();
44 | initActions();
45 | }
46 |
47 | private void initData() {
48 |
49 | }
50 |
51 | private void initViews() {
52 |
53 | rView = LayoutInflater.from(mContext).inflate(R.layout.view_video_item, this, true);
54 | view = findViewById(R.id.media_contoller);
55 | mVideoView = (IjkVideoView) findViewById(R.id.main_video);
56 | mediaController = new CustomMediaContoller(mContext, rView);
57 | mVideoView.setMediaController(mediaController);
58 |
59 | mVideoView.setOnCompletionListener(new IMediaPlayer.OnCompletionListener() {
60 | @Override
61 | public void onCompletion(IMediaPlayer mp) {
62 | view.setVisibility(View.GONE);
63 | if (mediaController.getScreenOrientation((Activity) mContext)
64 | == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
65 | //横屏播放完毕,重置
66 | ((Activity) mContext).setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
67 | ViewGroup.LayoutParams layoutParams = getLayoutParams();
68 | layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT;
69 | layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
70 | setLayoutParams(layoutParams);
71 | }
72 | if (completionListener != null)
73 | completionListener.completion(mp);
74 | }
75 | });
76 |
77 | }
78 |
79 | private void initActions() {
80 |
81 | /*orientationEventListener = new OrientationEventListener(mContext) {
82 | @Override
83 | public void onOrientationChanged(int orientation) {
84 | Log.e("onOrientationChanged", "orientation");
85 | if (orientation >= 0 && orientation <= 30 || orientation >= 330 || (orientation >= 150 && orientation <= 210)) {
86 | //竖屏
87 | if (portrait) {
88 | ((Activity) mContext).setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
89 | orientationEventListener.disable();
90 | }
91 | } else if ((orientation >= 90 && orientation <= 120) || (orientation >= 240 && orientation <= 300)) {
92 | if (!portrait) {
93 | ((Activity) mContext).setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
94 | orientationEventListener.disable();
95 | }
96 | }
97 | }
98 | };*/
99 | }
100 |
101 | public boolean isPlay() {
102 | return mVideoView.isPlaying();
103 | }
104 |
105 | public void pause() {
106 | if (mVideoView.isPlaying()) {
107 | mVideoView.pause();
108 | } else {
109 | mVideoView.start();
110 | }
111 | }
112 |
113 | public void start(String path) {
114 | Uri uri = Uri.parse(path);
115 | if (mediaController != null)
116 | mediaController.start();
117 | if (!mVideoView.isPlaying()) {
118 | mVideoView.setVideoURI(uri);
119 | mVideoView.start();
120 | } else {
121 | mVideoView.stopPlayback();
122 | mVideoView.setVideoURI(uri);
123 | mVideoView.start();
124 | }
125 | }
126 |
127 | public void start(){
128 | if (mVideoView.isPlaying()){
129 | mVideoView.start();
130 | }
131 | }
132 |
133 | public void setContorllerVisiable(){
134 | mediaController.setVisiable();
135 | }
136 |
137 | public void seekTo(int msec){
138 | mVideoView.seekTo(msec);
139 | }
140 |
141 | public void onChanged(Configuration configuration) {
142 | portrait = configuration.orientation == Configuration.ORIENTATION_PORTRAIT;
143 | doOnConfigurationChanged(portrait);
144 | }
145 |
146 | public void doOnConfigurationChanged(final boolean portrait) {
147 | if (mVideoView != null) {
148 | handler.post(new Runnable() {
149 | @Override
150 | public void run() {
151 | setFullScreen(!portrait);
152 | if (portrait) {
153 | ViewGroup.LayoutParams layoutParams = getLayoutParams();
154 | layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT;
155 | layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
156 | Log.e("handler", "400");
157 | setLayoutParams(layoutParams);
158 | requestLayout();
159 | } else {
160 | int heightPixels = ((Activity) mContext).getResources().getDisplayMetrics().heightPixels;
161 | int widthPixels = ((Activity) mContext).getResources().getDisplayMetrics().widthPixels;
162 | ViewGroup.LayoutParams layoutParams = getLayoutParams();
163 | layoutParams.height = heightPixels;
164 | layoutParams.width = widthPixels;
165 | Log.e("handler", "height==" + heightPixels + "\nwidth==" + widthPixels);
166 | setLayoutParams(layoutParams);
167 | }
168 | }
169 | });
170 | // orientationEventListener.enable();
171 | }
172 | }
173 |
174 | public void stop() {
175 | if (mVideoView.isPlaying()) {
176 | mVideoView.stopPlayback();
177 | }
178 | }
179 |
180 | public void onDestroy() {
181 | handler.removeCallbacksAndMessages(null);
182 | // orientationEventListener.disable();
183 | }
184 |
185 | private void setFullScreen(boolean fullScreen) {
186 | if (mContext != null && mContext instanceof Activity) {
187 | WindowManager.LayoutParams attrs = ((Activity) mContext).getWindow().getAttributes();
188 | if (fullScreen) {
189 | attrs.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN;
190 | ((Activity) mContext).getWindow().setAttributes(attrs);
191 | ((Activity) mContext).getWindow().addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
192 | } else {
193 | attrs.flags &= (~WindowManager.LayoutParams.FLAG_FULLSCREEN);
194 | ((Activity) mContext).getWindow().setAttributes(attrs);
195 | ((Activity) mContext).getWindow().clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
196 | }
197 | }
198 |
199 | }
200 |
201 | public void setShowContoller(boolean isShowContoller) {
202 | mediaController.setShowContoller(isShowContoller);
203 | }
204 |
205 | @Override
206 | public void onBufferingUpdate(MediaPlayer mp, int percent) {
207 |
208 | }
209 |
210 | @Override
211 | public boolean onInfo(MediaPlayer mp, int what, int extra) {
212 | return false;
213 | }
214 |
215 | public long getPalyPostion() {
216 | return mVideoView.getCurrentPosition();
217 | }
218 |
219 | public void release() {
220 | mVideoView.release(true);
221 | }
222 |
223 | public int VideoStatus() {
224 | return mVideoView.getCurrentStatue();
225 | }
226 |
227 | private CompletionListener completionListener;
228 |
229 | public void setCompletionListener(CompletionListener completionListener) {
230 | this.completionListener = completionListener;
231 | }
232 |
233 | public interface CompletionListener {
234 | void completion(IMediaPlayer mp);
235 | }
236 | }
237 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/demo/videolist/media/IMediaController.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2015 Zhang Rui
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package cn.demo.videolist.media;
18 |
19 | import android.view.View;
20 | import android.widget.MediaController;
21 |
22 | public interface IMediaController {
23 | void hide();
24 |
25 | boolean isShowing();
26 |
27 | void setAnchorView(View view);
28 |
29 | void setEnabled(boolean enabled);
30 |
31 | void setMediaPlayer(MediaController.MediaPlayerControl player);
32 |
33 | void show(int timeout);
34 |
35 | void show();
36 |
37 | //----------
38 | // Extends
39 | //----------
40 | void showOnce(View view);
41 | }
42 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/demo/videolist/media/IRenderView.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2015 Zhang Rui
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package cn.demo.videolist.media;
18 |
19 | import android.graphics.SurfaceTexture;
20 | import android.support.annotation.NonNull;
21 | import android.support.annotation.Nullable;
22 | import android.view.Surface;
23 | import android.view.SurfaceHolder;
24 | import android.view.View;
25 |
26 | import tv.danmaku.ijk.media.player.IMediaPlayer;
27 |
28 | public interface IRenderView {
29 | int AR_ASPECT_FIT_PARENT = 0; // without clip
30 | int AR_ASPECT_FILL_PARENT = 1; // may clip
31 | int AR_ASPECT_WRAP_CONTENT = 2;
32 | int AR_MATCH_PARENT = 3;
33 | int AR_16_9_FIT_PARENT = 4;
34 | int AR_4_3_FIT_PARENT = 5;
35 |
36 | View getView();
37 |
38 | boolean shouldWaitForResize();
39 |
40 | void setVideoSize(int videoWidth, int videoHeight);
41 |
42 | void setVideoSampleAspectRatio(int videoSarNum, int videoSarDen);
43 |
44 | void setVideoRotation(int degree);
45 |
46 | void setAspectRatio(int aspectRatio);
47 |
48 | void addRenderCallback(@NonNull IRenderCallback callback);
49 |
50 | void removeRenderCallback(@NonNull IRenderCallback callback);
51 |
52 | interface ISurfaceHolder {
53 | void bindToMediaPlayer(IMediaPlayer mp);
54 |
55 | @NonNull
56 | IRenderView getRenderView();
57 |
58 | @Nullable
59 | SurfaceHolder getSurfaceHolder();
60 |
61 | @Nullable
62 | Surface openSurface();
63 |
64 | @Nullable
65 | SurfaceTexture getSurfaceTexture();
66 | }
67 |
68 | interface IRenderCallback {
69 | /**
70 | * @param holder
71 | * @param width could be 0
72 | * @param height could be 0
73 | */
74 | void onSurfaceCreated(@NonNull ISurfaceHolder holder, int width, int height);
75 |
76 | /**
77 | * @param holder
78 | * @param format could be 0
79 | * @param width
80 | * @param height
81 | */
82 | void onSurfaceChanged(@NonNull ISurfaceHolder holder, int format, int width, int height);
83 |
84 | void onSurfaceDestroyed(@NonNull ISurfaceHolder holder);
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/demo/videolist/media/InfoHudViewHolder.java:
--------------------------------------------------------------------------------
1 | package cn.demo.videolist.media;
2 |
3 | import android.content.Context;
4 | import android.os.Handler;
5 | import android.os.Message;
6 | import android.util.SparseArray;
7 | import android.view.View;
8 | import android.widget.TableLayout;
9 |
10 | import java.util.Locale;
11 |
12 | import cn.demo.videolist.R;
13 | import tv.danmaku.ijk.media.player.IMediaPlayer;
14 | import tv.danmaku.ijk.media.player.IjkMediaPlayer;
15 | import tv.danmaku.ijk.media.player.MediaPlayerProxy;
16 |
17 | public class InfoHudViewHolder {
18 | private TableLayoutBinder mTableLayoutBinder;
19 | private SparseArray mRowMap = new SparseArray();
20 | private IMediaPlayer mMediaPlayer;
21 |
22 | public InfoHudViewHolder(Context context, TableLayout tableLayout) {
23 | mTableLayoutBinder = new TableLayoutBinder(context, tableLayout);
24 | }
25 |
26 | private void appendSection(int nameId) {
27 | mTableLayoutBinder.appendSection(nameId);
28 | }
29 |
30 | private void appendRow(int nameId) {
31 | View rowView = mTableLayoutBinder.appendRow2(nameId, null);
32 | mRowMap.put(nameId, rowView);
33 | }
34 |
35 | private void setRowValue(int id, String value) {
36 | View rowView = mRowMap.get(id);
37 | if (rowView == null) {
38 | rowView = mTableLayoutBinder.appendRow2(id, value);
39 | mRowMap.put(id, rowView);
40 | } else {
41 | mTableLayoutBinder.setValueText(rowView, value);
42 | }
43 | }
44 |
45 | public void setMediaPlayer(IMediaPlayer mp) {
46 | mMediaPlayer = mp;
47 | if (mMediaPlayer != null) {
48 | mHandler.sendEmptyMessageDelayed(MSG_UPDATE_HUD, 500);
49 | } else {
50 | mHandler.removeMessages(MSG_UPDATE_HUD);
51 | }
52 | }
53 |
54 | private static String formatedDurationMilli(long duration) {
55 | if (duration >= 1000) {
56 | return String.format(Locale.US, "%.2f sec", ((float)duration) / 1000);
57 | } else {
58 | return String.format(Locale.US, "%d msec", duration);
59 | }
60 | }
61 |
62 | private static String formatedSize(long bytes) {
63 | if (bytes >= 100 * 1000) {
64 | return String.format(Locale.US, "%.2f MB", ((float)bytes) / 1000 / 1000);
65 | } else if (bytes >= 100) {
66 | return String.format(Locale.US, "%.1f KB", ((float)bytes) / 1000);
67 | } else {
68 | return String.format(Locale.US, "%d B", bytes);
69 | }
70 | }
71 |
72 | private static final int MSG_UPDATE_HUD = 1;
73 | private Handler mHandler = new Handler() {
74 | @Override
75 | public void handleMessage(Message msg) {
76 | switch (msg.what) {
77 | case MSG_UPDATE_HUD: {
78 | InfoHudViewHolder holder = InfoHudViewHolder.this;
79 | IjkMediaPlayer mp = null;
80 | if (mMediaPlayer == null)
81 | break;
82 | if (mMediaPlayer instanceof IjkMediaPlayer) {
83 | mp = (IjkMediaPlayer) mMediaPlayer;
84 | } else if (mMediaPlayer instanceof MediaPlayerProxy) {
85 | MediaPlayerProxy proxy = (MediaPlayerProxy) mMediaPlayer;
86 | IMediaPlayer internal = proxy.getInternalMediaPlayer();
87 | if (internal != null && internal instanceof IjkMediaPlayer)
88 | mp = (IjkMediaPlayer) internal;
89 | }
90 | if (mp == null)
91 | break;
92 |
93 | int vdec = mp.getVideoDecoder();
94 | switch (vdec) {
95 | case IjkMediaPlayer.FFP_PROPV_DECODER_AVCODEC:
96 | setRowValue(R.string.vdec, "avcodec");
97 | break;
98 | case IjkMediaPlayer.FFP_PROPV_DECODER_MEDIACODEC:
99 | setRowValue(R.string.vdec, "MediaCodec");
100 | break;
101 | default:
102 | setRowValue(R.string.vdec, "");
103 | break;
104 | }
105 |
106 | float fpsOutput = mp.getVideoOutputFramesPerSecond();
107 | float fpsDecode = mp.getVideoDecodeFramesPerSecond();
108 | setRowValue(R.string.fps, String.format(Locale.US, "%.2f / %.2f", fpsDecode, fpsOutput));
109 |
110 | long videoCachedDuration = mp.getVideoCachedDuration();
111 | long audioCachedDuration = mp.getAudioCachedDuration();
112 | long videoCachedBytes = mp.getVideoCachedBytes();
113 | long audioCachedBytes = mp.getAudioCachedBytes();
114 |
115 | setRowValue(R.string.v_cache, String.format(Locale.US, "%s, %s", formatedDurationMilli(videoCachedDuration), formatedSize(videoCachedBytes)));
116 | setRowValue(R.string.a_cache, String.format(Locale.US, "%s, %s", formatedDurationMilli(audioCachedDuration), formatedSize(audioCachedBytes)));
117 |
118 | mHandler.removeMessages(MSG_UPDATE_HUD);
119 | mHandler.sendEmptyMessageDelayed(MSG_UPDATE_HUD, 500);
120 | }
121 | }
122 | }
123 | };
124 | }
125 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/demo/videolist/media/MeasureHelper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2015 Zhang Rui
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package cn.demo.videolist.media;
18 |
19 | import android.content.Context;
20 | import android.support.annotation.NonNull;
21 | import android.view.View;
22 |
23 | import java.lang.ref.WeakReference;
24 |
25 | import cn.demo.videolist.R;
26 |
27 |
28 | public final class MeasureHelper {
29 | private WeakReference mWeakView;
30 |
31 | private int mVideoWidth;
32 | private int mVideoHeight;
33 | private int mVideoSarNum;
34 | private int mVideoSarDen;
35 |
36 | private int mVideoRotationDegree;
37 |
38 | private int mMeasuredWidth;
39 | private int mMeasuredHeight;
40 |
41 | private int mCurrentAspectRatio = IRenderView.AR_ASPECT_FIT_PARENT;
42 |
43 | public MeasureHelper(View view) {
44 | mWeakView = new WeakReference(view);
45 | }
46 |
47 | public View getView() {
48 | if (mWeakView == null)
49 | return null;
50 | return mWeakView.get();
51 | }
52 |
53 | public void setVideoSize(int videoWidth, int videoHeight) {
54 | mVideoWidth = videoWidth;
55 | mVideoHeight = videoHeight;
56 | }
57 |
58 | public void setVideoSampleAspectRatio(int videoSarNum, int videoSarDen) {
59 | mVideoSarNum = videoSarNum;
60 | mVideoSarDen = videoSarDen;
61 | }
62 |
63 | public void setVideoRotation(int videoRotationDegree) {
64 | mVideoRotationDegree = videoRotationDegree;
65 | }
66 |
67 | /**
68 | * Must be called by View.onMeasure(int, int)
69 | *
70 | * @param widthMeasureSpec
71 | * @param heightMeasureSpec
72 | */
73 | public void doMeasure(int widthMeasureSpec, int heightMeasureSpec) {
74 | //Log.i("@@@@", "onMeasure(" + MeasureSpec.toString(widthMeasureSpec) + ", "
75 | // + MeasureSpec.toString(heightMeasureSpec) + ")");
76 | if (mVideoRotationDegree == 90 || mVideoRotationDegree == 270) {
77 | int tempSpec = widthMeasureSpec;
78 | widthMeasureSpec = heightMeasureSpec;
79 | heightMeasureSpec = tempSpec;
80 | }
81 |
82 | int width = View.getDefaultSize(mVideoWidth, widthMeasureSpec);
83 | int height = View.getDefaultSize(mVideoHeight, heightMeasureSpec);
84 | if (mCurrentAspectRatio == IRenderView.AR_MATCH_PARENT) {
85 | width = widthMeasureSpec;
86 | height = heightMeasureSpec;
87 | } else if (mVideoWidth > 0 && mVideoHeight > 0) {
88 | int widthSpecMode = View.MeasureSpec.getMode(widthMeasureSpec);
89 | int widthSpecSize = View.MeasureSpec.getSize(widthMeasureSpec);
90 | int heightSpecMode = View.MeasureSpec.getMode(heightMeasureSpec);
91 | int heightSpecSize = View.MeasureSpec.getSize(heightMeasureSpec);
92 |
93 | if (widthSpecMode == View.MeasureSpec.AT_MOST && heightSpecMode == View.MeasureSpec.AT_MOST) {
94 | float specAspectRatio = (float) widthSpecSize / (float) heightSpecSize;
95 | float displayAspectRatio;
96 | switch (mCurrentAspectRatio) {
97 | case IRenderView.AR_16_9_FIT_PARENT:
98 | displayAspectRatio = 16.0f / 9.0f;
99 | if (mVideoRotationDegree == 90 || mVideoRotationDegree == 270)
100 | displayAspectRatio = 1.0f / displayAspectRatio;
101 | break;
102 | case IRenderView.AR_4_3_FIT_PARENT:
103 | displayAspectRatio = 4.0f / 3.0f;
104 | if (mVideoRotationDegree == 90 || mVideoRotationDegree == 270)
105 | displayAspectRatio = 1.0f / displayAspectRatio;
106 | break;
107 | case IRenderView.AR_ASPECT_FIT_PARENT:
108 | case IRenderView.AR_ASPECT_FILL_PARENT:
109 | case IRenderView.AR_ASPECT_WRAP_CONTENT:
110 | default:
111 | displayAspectRatio = (float) mVideoWidth / (float) mVideoHeight;
112 | if (mVideoSarNum > 0 && mVideoSarDen > 0)
113 | displayAspectRatio = displayAspectRatio * mVideoSarNum / mVideoSarDen;
114 | break;
115 | }
116 | boolean shouldBeWider = displayAspectRatio > specAspectRatio;
117 |
118 | switch (mCurrentAspectRatio) {
119 | case IRenderView.AR_ASPECT_FIT_PARENT:
120 | case IRenderView.AR_16_9_FIT_PARENT:
121 | case IRenderView.AR_4_3_FIT_PARENT:
122 | if (shouldBeWider) {
123 | // too wide, fix width
124 | width = widthSpecSize;
125 | height = (int) (width / displayAspectRatio);
126 | } else {
127 | // too high, fix height
128 | height = heightSpecSize;
129 | width = (int) (height * displayAspectRatio);
130 | }
131 | break;
132 | case IRenderView.AR_ASPECT_FILL_PARENT:
133 | if (shouldBeWider) {
134 | // not high enough, fix height
135 | height = heightSpecSize;
136 | width = (int) (height * displayAspectRatio);
137 | } else {
138 | // not wide enough, fix width
139 | width = widthSpecSize;
140 | height = (int) (width / displayAspectRatio);
141 | }
142 | break;
143 | case IRenderView.AR_ASPECT_WRAP_CONTENT:
144 | default:
145 | if (shouldBeWider) {
146 | // too wide, fix width
147 | width = Math.min(mVideoWidth, widthSpecSize);
148 | height = (int) (width / displayAspectRatio);
149 | } else {
150 | // too high, fix height
151 | height = Math.min(mVideoHeight, heightSpecSize);
152 | width = (int) (height * displayAspectRatio);
153 | }
154 | break;
155 | }
156 | } else if (widthSpecMode == View.MeasureSpec.EXACTLY && heightSpecMode == View.MeasureSpec.EXACTLY) {
157 | // the size is fixed
158 | width = widthSpecSize;
159 | height = heightSpecSize;
160 |
161 | // for compatibility, we adjust size based on aspect ratio
162 | if (mVideoWidth * height < width * mVideoHeight) {
163 | //Log.i("@@@", "image too wide, correcting");
164 | width = height * mVideoWidth / mVideoHeight;
165 | } else if (mVideoWidth * height > width * mVideoHeight) {
166 | //Log.i("@@@", "image too tall, correcting");
167 | height = width * mVideoHeight / mVideoWidth;
168 | }
169 | } else if (widthSpecMode == View.MeasureSpec.EXACTLY) {
170 | // only the width is fixed, adjust the height to match aspect ratio if possible
171 | width = widthSpecSize;
172 | height = width * mVideoHeight / mVideoWidth;
173 | if (heightSpecMode == View.MeasureSpec.AT_MOST && height > heightSpecSize) {
174 | // couldn't match aspect ratio within the constraints
175 | height = heightSpecSize;
176 | }
177 | } else if (heightSpecMode == View.MeasureSpec.EXACTLY) {
178 | // only the height is fixed, adjust the width to match aspect ratio if possible
179 | height = heightSpecSize;
180 | width = height * mVideoWidth / mVideoHeight;
181 | if (widthSpecMode == View.MeasureSpec.AT_MOST && width > widthSpecSize) {
182 | // couldn't match aspect ratio within the constraints
183 | width = widthSpecSize;
184 | }
185 | } else {
186 | // neither the width nor the height are fixed, try to use actual video size
187 | width = mVideoWidth;
188 | height = mVideoHeight;
189 | if (heightSpecMode == View.MeasureSpec.AT_MOST && height > heightSpecSize) {
190 | // too tall, decrease both width and height
191 | height = heightSpecSize;
192 | width = height * mVideoWidth / mVideoHeight;
193 | }
194 | if (widthSpecMode == View.MeasureSpec.AT_MOST && width > widthSpecSize) {
195 | // too wide, decrease both width and height
196 | width = widthSpecSize;
197 | height = width * mVideoHeight / mVideoWidth;
198 | }
199 | }
200 | } else {
201 | // no size yet, just adopt the given spec sizes
202 | }
203 |
204 | mMeasuredWidth = width;
205 | mMeasuredHeight = height;
206 | }
207 |
208 | public int getMeasuredWidth() {
209 | return mMeasuredWidth;
210 | }
211 |
212 | public int getMeasuredHeight() {
213 | return mMeasuredHeight;
214 | }
215 |
216 | public void setAspectRatio(int aspectRatio) {
217 | mCurrentAspectRatio = aspectRatio;
218 | }
219 |
220 | @NonNull
221 | public static String getAspectRatioText(Context context, int aspectRatio) {
222 | String text;
223 | switch (aspectRatio) {
224 | case IRenderView.AR_ASPECT_FIT_PARENT:
225 | text = context.getString(R.string.VideoView_ar_aspect_fit_parent);
226 | break;
227 | case IRenderView.AR_ASPECT_FILL_PARENT:
228 | text = context.getString(R.string.VideoView_ar_aspect_fill_parent);
229 | break;
230 | case IRenderView.AR_ASPECT_WRAP_CONTENT:
231 | text = context.getString(R.string.VideoView_ar_aspect_wrap_content);
232 | break;
233 | case IRenderView.AR_MATCH_PARENT:
234 | text = context.getString(R.string.VideoView_ar_match_parent);
235 | break;
236 | case IRenderView.AR_16_9_FIT_PARENT:
237 | text = context.getString(R.string.VideoView_ar_16_9_fit_parent);
238 | break;
239 | case IRenderView.AR_4_3_FIT_PARENT:
240 | text = context.getString(R.string.VideoView_ar_4_3_fit_parent);
241 | break;
242 | default:
243 | text = context.getString(R.string.N_A);
244 | break;
245 | }
246 | return text;
247 | }
248 | }
249 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/demo/videolist/media/MediaPlayerCompat.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2015 Zhang Rui
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package cn.demo.videolist.media;
18 |
19 | import tv.danmaku.ijk.media.player.IMediaPlayer;
20 | import tv.danmaku.ijk.media.player.IjkMediaPlayer;
21 | import tv.danmaku.ijk.media.player.MediaPlayerProxy;
22 | import tv.danmaku.ijk.media.player.TextureMediaPlayer;
23 |
24 | public class MediaPlayerCompat {
25 | public static String getName(IMediaPlayer mp) {
26 | if (mp == null) {
27 | return "null";
28 | } else if (mp instanceof TextureMediaPlayer) {
29 | StringBuilder sb = new StringBuilder("TextureMediaPlayer <");
30 | IMediaPlayer internalMediaPlayer = ((TextureMediaPlayer) mp).getInternalMediaPlayer();
31 | if (internalMediaPlayer == null) {
32 | sb.append("null>");
33 | } else {
34 | sb.append(internalMediaPlayer.getClass().getSimpleName());
35 | sb.append(">");
36 | }
37 | return sb.toString();
38 | } else {
39 | return mp.getClass().getSimpleName();
40 | }
41 | }
42 |
43 | public static IjkMediaPlayer getIjkMediaPlayer(IMediaPlayer mp) {
44 | IjkMediaPlayer ijkMediaPlayer = null;
45 | if (mp == null) {
46 | return null;
47 | } if (mp instanceof IjkMediaPlayer) {
48 | ijkMediaPlayer = (IjkMediaPlayer) mp;
49 | } else if (mp instanceof MediaPlayerProxy && ((MediaPlayerProxy) mp).getInternalMediaPlayer() instanceof IjkMediaPlayer) {
50 | ijkMediaPlayer = (IjkMediaPlayer) ((MediaPlayerProxy) mp).getInternalMediaPlayer();
51 | }
52 | return ijkMediaPlayer;
53 | }
54 |
55 | public static void selectTrack(IMediaPlayer mp, int stream) {
56 | IjkMediaPlayer ijkMediaPlayer = getIjkMediaPlayer(mp);
57 | if (ijkMediaPlayer == null)
58 | return;
59 | ijkMediaPlayer.selectTrack(stream);
60 | }
61 |
62 | public static void deselectTrack(IMediaPlayer mp, int stream) {
63 | IjkMediaPlayer ijkMediaPlayer = getIjkMediaPlayer(mp);
64 | if (ijkMediaPlayer == null)
65 | return;
66 | ijkMediaPlayer.deselectTrack(stream);
67 | }
68 |
69 | public static int getSelectedTrack(IMediaPlayer mp, int trackType) {
70 | IjkMediaPlayer ijkMediaPlayer = getIjkMediaPlayer(mp);
71 | if (ijkMediaPlayer == null)
72 | return -1;
73 | return ijkMediaPlayer.getSelectedTrack(trackType);
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/demo/videolist/media/MediaPlayerService.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2015 Zhang Rui
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package cn.demo.videolist.media;
18 |
19 | import android.app.Service;
20 | import android.content.Context;
21 | import android.content.Intent;
22 | import android.os.IBinder;
23 | import android.support.annotation.Nullable;
24 |
25 | import tv.danmaku.ijk.media.player.IMediaPlayer;
26 |
27 | public class MediaPlayerService extends Service {
28 | private static IMediaPlayer sMediaPlayer;
29 |
30 | public static Intent newIntent(Context context) {
31 | Intent intent = new Intent(context, MediaPlayerService.class);
32 | return intent;
33 | }
34 |
35 | public static void intentToStart(Context context) {
36 | context.startService(newIntent(context));
37 | }
38 |
39 | public static void intentToStop(Context context) {
40 | context.stopService(newIntent(context));
41 | }
42 |
43 | @Nullable
44 | @Override
45 | public IBinder onBind(Intent intent) {
46 | return null;
47 | }
48 |
49 | public static void setMediaPlayer(IMediaPlayer mp) {
50 | if (sMediaPlayer != null && sMediaPlayer != mp) {
51 | if (sMediaPlayer.isPlaying())
52 | sMediaPlayer.stop();
53 | sMediaPlayer.release();
54 | sMediaPlayer = null;
55 | }
56 | sMediaPlayer = mp;
57 | }
58 |
59 | public static IMediaPlayer getMediaPlayer() {
60 | return sMediaPlayer;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/demo/videolist/media/Settings.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2015 Zhang Rui
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package cn.demo.videolist.media;
18 |
19 | import android.content.Context;
20 | import android.content.SharedPreferences;
21 | import android.preference.PreferenceManager;
22 |
23 | import cn.demo.videolist.R;
24 |
25 |
26 | public class Settings {
27 | private Context mAppContext;
28 | private SharedPreferences mSharedPreferences;
29 |
30 | public static final int PV_PLAYER__Auto = 0;
31 | public static final int PV_PLAYER__AndroidMediaPlayer = 1;
32 | public static final int PV_PLAYER__IjkMediaPlayer = 2;
33 | public static final int PV_PLAYER__IjkExoMediaPlayer = 3;
34 |
35 | public Settings(Context context) {
36 | mAppContext = context.getApplicationContext();
37 | mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(mAppContext);
38 | }
39 |
40 | public boolean getEnableBackgroundPlay() {
41 | String key = mAppContext.getString(R.string.pref_key_enable_background_play);
42 | return mSharedPreferences.getBoolean(key, false);
43 | }
44 |
45 | public int getPlayer() {
46 | String key = mAppContext.getString(R.string.pref_key_player);
47 | String value = mSharedPreferences.getString(key, "");
48 | try {
49 | return Integer.valueOf(value).intValue();
50 | } catch (NumberFormatException e) {
51 | return 0;
52 | }
53 | }
54 |
55 | public boolean getUsingMediaCodec() {
56 | String key = mAppContext.getString(R.string.pref_key_using_media_codec);
57 | return mSharedPreferences.getBoolean(key, false);
58 | }
59 |
60 | public boolean getUsingMediaCodecAutoRotate() {
61 | String key = mAppContext.getString(R.string.pref_key_using_media_codec_auto_rotate);
62 | return mSharedPreferences.getBoolean(key, false);
63 | }
64 |
65 | public boolean getUsingOpenSLES() {
66 | String key = mAppContext.getString(R.string.pref_key_using_opensl_es);
67 | return mSharedPreferences.getBoolean(key, false);
68 | }
69 |
70 | public String getPixelFormat() {
71 | String key = mAppContext.getString(R.string.pref_key_pixel_format);
72 | return mSharedPreferences.getString(key, "");
73 | }
74 |
75 | public boolean getEnableNoView() {
76 | String key = mAppContext.getString(R.string.pref_key_enable_no_view);
77 | return mSharedPreferences.getBoolean(key, false);
78 | }
79 |
80 | public boolean getEnableSurfaceView() {
81 | String key = mAppContext.getString(R.string.pref_key_enable_surface_view);
82 | return mSharedPreferences.getBoolean(key, false);
83 | }
84 |
85 | public boolean getEnableTextureView() {
86 | String key = mAppContext.getString(R.string.pref_key_enable_texture_view);
87 | return mSharedPreferences.getBoolean(key, false);
88 | }
89 |
90 | public boolean getEnableDetachedSurfaceTextureView() {
91 | String key = mAppContext.getString(R.string.pref_key_enable_detached_surface_texture);
92 | return mSharedPreferences.getBoolean(key, false);
93 | }
94 |
95 | public String getLastDirectory() {
96 | String key = mAppContext.getString(R.string.pref_key_last_directory);
97 | return mSharedPreferences.getString(key, "/");
98 | }
99 |
100 | public void setLastDirectory(String path) {
101 | String key = mAppContext.getString(R.string.pref_key_last_directory);
102 | mSharedPreferences.edit().putString(key, path).apply();
103 | }
104 |
105 | public void setEnableTextureView(boolean enable){
106 | String key = mAppContext.getString(R.string.pref_key_enable_texture_view);
107 | SharedPreferences.Editor editor=mSharedPreferences.edit();
108 | editor.putBoolean(key,enable);
109 | editor.commit();
110 | }
111 |
112 | public void setEnableSurfaceView(boolean enable){
113 | String key = mAppContext.getString(R.string.pref_key_enable_surface_view);
114 | SharedPreferences.Editor editor=mSharedPreferences.edit();
115 | editor.putBoolean(key,enable);
116 | editor.commit();
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/demo/videolist/media/SurfaceRenderView.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2015 Zhang Rui
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package cn.demo.videolist.media;
18 |
19 | import android.annotation.TargetApi;
20 | import android.content.Context;
21 | import android.graphics.SurfaceTexture;
22 | import android.os.Build;
23 | import android.support.annotation.NonNull;
24 | import android.support.annotation.Nullable;
25 | import android.util.AttributeSet;
26 | import android.util.Log;
27 | import android.view.Surface;
28 | import android.view.SurfaceHolder;
29 | import android.view.SurfaceView;
30 | import android.view.View;
31 | import android.view.accessibility.AccessibilityEvent;
32 | import android.view.accessibility.AccessibilityNodeInfo;
33 |
34 | import java.lang.ref.WeakReference;
35 | import java.util.Map;
36 | import java.util.concurrent.ConcurrentHashMap;
37 |
38 | import tv.danmaku.ijk.media.player.IMediaPlayer;
39 | import tv.danmaku.ijk.media.player.ISurfaceTextureHolder;
40 |
41 | public class SurfaceRenderView extends SurfaceView implements IRenderView {
42 | private MeasureHelper mMeasureHelper;
43 |
44 | public SurfaceRenderView(Context context) {
45 | super(context);
46 | initView(context);
47 | }
48 |
49 | public SurfaceRenderView(Context context, AttributeSet attrs) {
50 | super(context, attrs);
51 | initView(context);
52 | }
53 |
54 | public SurfaceRenderView(Context context, AttributeSet attrs, int defStyleAttr) {
55 | super(context, attrs, defStyleAttr);
56 | initView(context);
57 | }
58 |
59 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
60 | public SurfaceRenderView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
61 | super(context, attrs, defStyleAttr, defStyleRes);
62 | initView(context);
63 | }
64 |
65 | private void initView(Context context) {
66 | mMeasureHelper = new MeasureHelper(this);
67 | mSurfaceCallback = new SurfaceCallback(this);
68 | getHolder().addCallback(mSurfaceCallback);
69 | //noinspection deprecation
70 | getHolder().setType(SurfaceHolder.SURFACE_TYPE_NORMAL);
71 | }
72 |
73 | @Override
74 | public View getView() {
75 | return this;
76 | }
77 |
78 | @Override
79 | public boolean shouldWaitForResize() {
80 | return true;
81 | }
82 |
83 | //--------------------
84 | // Layout & Measure
85 | //--------------------
86 | @Override
87 | public void setVideoSize(int videoWidth, int videoHeight) {
88 | if (videoWidth > 0 && videoHeight > 0) {
89 | mMeasureHelper.setVideoSize(videoWidth, videoHeight);
90 | getHolder().setFixedSize(videoWidth, videoHeight);
91 | requestLayout();
92 | }
93 | }
94 |
95 | @Override
96 | public void setVideoSampleAspectRatio(int videoSarNum, int videoSarDen) {
97 | if (videoSarNum > 0 && videoSarDen > 0) {
98 | mMeasureHelper.setVideoSampleAspectRatio(videoSarNum, videoSarDen);
99 | requestLayout();
100 | }
101 | }
102 |
103 | @Override
104 | public void setVideoRotation(int degree) {
105 | Log.e("", "SurfaceView doesn't support rotation (" + degree + ")!\n");
106 | }
107 |
108 | @Override
109 | public void setAspectRatio(int aspectRatio) {
110 | mMeasureHelper.setAspectRatio(aspectRatio);
111 | requestLayout();
112 | }
113 |
114 | @Override
115 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
116 | mMeasureHelper.doMeasure(widthMeasureSpec, heightMeasureSpec);
117 | setMeasuredDimension(mMeasureHelper.getMeasuredWidth(), mMeasureHelper.getMeasuredHeight());
118 | }
119 |
120 | //--------------------
121 | // SurfaceViewHolder
122 | //--------------------
123 |
124 | private static final class InternalSurfaceHolder implements IRenderView.ISurfaceHolder {
125 | private SurfaceRenderView mSurfaceView;
126 | private SurfaceHolder mSurfaceHolder;
127 |
128 | public InternalSurfaceHolder(@NonNull SurfaceRenderView surfaceView,
129 | @Nullable SurfaceHolder surfaceHolder) {
130 | mSurfaceView = surfaceView;
131 | mSurfaceHolder = surfaceHolder;
132 | }
133 |
134 | public void bindToMediaPlayer(IMediaPlayer mp) {
135 | if (mp != null) {
136 | if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) &&
137 | (mp instanceof ISurfaceTextureHolder)) {
138 | ISurfaceTextureHolder textureHolder = (ISurfaceTextureHolder) mp;
139 | textureHolder.setSurfaceTexture(null);
140 | }
141 | mp.setDisplay(mSurfaceHolder);
142 | }
143 | }
144 |
145 | @NonNull
146 | @Override
147 | public IRenderView getRenderView() {
148 | return mSurfaceView;
149 | }
150 |
151 | @Nullable
152 | @Override
153 | public SurfaceHolder getSurfaceHolder() {
154 | return mSurfaceHolder;
155 | }
156 |
157 | @Nullable
158 | @Override
159 | public SurfaceTexture getSurfaceTexture() {
160 | return null;
161 | }
162 |
163 | @Nullable
164 | @Override
165 | public Surface openSurface() {
166 | if (mSurfaceHolder == null)
167 | return null;
168 | return mSurfaceHolder.getSurface();
169 | }
170 | }
171 |
172 | //-------------------------
173 | // SurfaceHolder.Callback
174 | //-------------------------
175 |
176 | @Override
177 | public void addRenderCallback(IRenderCallback callback) {
178 | mSurfaceCallback.addRenderCallback(callback);
179 | }
180 |
181 | @Override
182 | public void removeRenderCallback(IRenderCallback callback) {
183 | mSurfaceCallback.removeRenderCallback(callback);
184 | }
185 |
186 | private SurfaceCallback mSurfaceCallback;
187 |
188 | private static final class SurfaceCallback implements SurfaceHolder.Callback {
189 | private SurfaceHolder mSurfaceHolder;
190 | private boolean mIsFormatChanged;
191 | private int mFormat;
192 | private int mWidth;
193 | private int mHeight;
194 |
195 | private WeakReference mWeakSurfaceView;
196 | private Map mRenderCallbackMap = new ConcurrentHashMap();
197 |
198 | public SurfaceCallback(@NonNull SurfaceRenderView surfaceView) {
199 | mWeakSurfaceView = new WeakReference(surfaceView);
200 | }
201 |
202 | public void addRenderCallback(@NonNull IRenderCallback callback) {
203 | mRenderCallbackMap.put(callback, callback);
204 |
205 | ISurfaceHolder surfaceHolder = null;
206 | if (mSurfaceHolder != null) {
207 | if (surfaceHolder == null)
208 | surfaceHolder = new InternalSurfaceHolder(mWeakSurfaceView.get(), mSurfaceHolder);
209 | callback.onSurfaceCreated(surfaceHolder, mWidth, mHeight);
210 | }
211 |
212 | if (mIsFormatChanged) {
213 | if (surfaceHolder == null)
214 | surfaceHolder = new InternalSurfaceHolder(mWeakSurfaceView.get(), mSurfaceHolder);
215 | callback.onSurfaceChanged(surfaceHolder, mFormat, mWidth, mHeight);
216 | }
217 | }
218 |
219 | public void removeRenderCallback(@NonNull IRenderCallback callback) {
220 | mRenderCallbackMap.remove(callback);
221 | }
222 |
223 | @Override
224 | public void surfaceCreated(SurfaceHolder holder) {
225 | mSurfaceHolder = holder;
226 | mIsFormatChanged = false;
227 | mFormat = 0;
228 | mWidth = 0;
229 | mHeight = 0;
230 |
231 | ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakSurfaceView.get(), mSurfaceHolder);
232 | for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) {
233 | renderCallback.onSurfaceCreated(surfaceHolder, 0, 0);
234 | }
235 | }
236 |
237 | @Override
238 | public void surfaceDestroyed(SurfaceHolder holder) {
239 | mSurfaceHolder = null;
240 | mIsFormatChanged = false;
241 | mFormat = 0;
242 | mWidth = 0;
243 | mHeight = 0;
244 |
245 | ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakSurfaceView.get(), mSurfaceHolder);
246 | for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) {
247 | renderCallback.onSurfaceDestroyed(surfaceHolder);
248 | }
249 | }
250 |
251 | @Override
252 | public void surfaceChanged(SurfaceHolder holder, int format,
253 | int width, int height) {
254 | mSurfaceHolder = holder;
255 | mIsFormatChanged = true;
256 | mFormat = format;
257 | mWidth = width;
258 | mHeight = height;
259 |
260 | // mMeasureHelper.setVideoSize(width, height);
261 |
262 | ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakSurfaceView.get(), mSurfaceHolder);
263 | for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) {
264 | renderCallback.onSurfaceChanged(surfaceHolder, format, width, height);
265 | }
266 | }
267 | }
268 |
269 | //--------------------
270 | // Accessibility
271 | //--------------------
272 |
273 | @Override
274 | public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
275 | super.onInitializeAccessibilityEvent(event);
276 | event.setClassName(SurfaceRenderView.class.getName());
277 | }
278 |
279 | @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
280 | @Override
281 | public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
282 | super.onInitializeAccessibilityNodeInfo(info);
283 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
284 | info.setClassName(SurfaceRenderView.class.getName());
285 | }
286 | }
287 | }
288 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/demo/videolist/media/TableLayoutBinder.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2015 Zhang Rui
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package cn.demo.videolist.media;
18 |
19 | import android.content.Context;
20 | import android.support.v7.app.AlertDialog;
21 | import android.view.LayoutInflater;
22 | import android.view.View;
23 | import android.view.ViewGroup;
24 | import android.widget.TableLayout;
25 | import android.widget.TextView;
26 |
27 | import cn.demo.videolist.R;
28 |
29 |
30 | public class TableLayoutBinder {
31 | private Context mContext;
32 | public ViewGroup mTableView;
33 | public TableLayout mTableLayout;
34 |
35 | public TableLayoutBinder(Context context) {
36 | this(context, R.layout.table_media_info);
37 | }
38 |
39 | public TableLayoutBinder(Context context, int layoutResourceId) {
40 | mContext = context;
41 | mTableView = (ViewGroup) LayoutInflater.from(mContext).inflate(layoutResourceId, null);
42 | mTableLayout = (TableLayout) mTableView.findViewById(R.id.table);
43 | }
44 |
45 | public TableLayoutBinder(Context context, TableLayout tableLayout) {
46 | mContext = context;
47 | mTableView = tableLayout;
48 | mTableLayout = tableLayout;
49 | }
50 |
51 | public View appendRow1(String name, String value) {
52 | return appendRow(R.layout.table_media_info_row1, name, value);
53 | }
54 |
55 | public View appendRow1(int nameId, String value) {
56 | return appendRow1(mContext.getString(nameId), value);
57 | }
58 |
59 | public View appendRow2(String name, String value) {
60 | return appendRow(R.layout.table_media_info_row2, name, value);
61 | }
62 |
63 | public View appendRow2(int nameId, String value) {
64 | return appendRow2(mContext.getString(nameId), value);
65 | }
66 |
67 | public View appendSection(String name) {
68 | return appendRow(R.layout.table_media_info_section, name, null);
69 | }
70 |
71 | public View appendSection(int nameId) {
72 | return appendSection(mContext.getString(nameId));
73 | }
74 |
75 | public View appendRow(int layoutId, String name, String value) {
76 | ViewGroup rowView = (ViewGroup) LayoutInflater.from(mContext).inflate(layoutId, mTableLayout, false);
77 | setNameValueText(rowView, name, value);
78 |
79 | mTableLayout.addView(rowView);
80 | return rowView;
81 | }
82 |
83 | public ViewHolder obtainViewHolder(View rowView) {
84 | ViewHolder viewHolder = (ViewHolder) rowView.getTag();
85 | if (viewHolder == null) {
86 | viewHolder = new ViewHolder();
87 | viewHolder.mNameTextView = (TextView) rowView.findViewById(R.id.name);
88 | viewHolder.mValueTextView = (TextView) rowView.findViewById(R.id.value);
89 | rowView.setTag(viewHolder);
90 | }
91 | return viewHolder;
92 | }
93 |
94 | public void setNameValueText(View rowView, String name, String value) {
95 | ViewHolder viewHolder = obtainViewHolder(rowView);
96 | viewHolder.setName(name);
97 | viewHolder.setValue(value);
98 | }
99 |
100 | public void setValueText(View rowView, String value) {
101 | ViewHolder viewHolder = obtainViewHolder(rowView);
102 | viewHolder.setValue(value);
103 | }
104 |
105 | public ViewGroup buildLayout() {
106 | return mTableView;
107 | }
108 |
109 | public AlertDialog.Builder buildAlertDialogBuilder() {
110 | AlertDialog.Builder dlgBuilder = new AlertDialog.Builder(mContext);
111 | dlgBuilder.setView(buildLayout());
112 | return dlgBuilder;
113 | }
114 |
115 | private static class ViewHolder {
116 | public TextView mNameTextView;
117 | public TextView mValueTextView;
118 |
119 | public void setName(String name) {
120 | if (mNameTextView != null) {
121 | mNameTextView.setText(name);
122 | }
123 | }
124 |
125 | public void setValue(String value) {
126 | if (mValueTextView != null) {
127 | mValueTextView.setText(value);
128 | }
129 | }
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/demo/videolist/media/TextureRenderView.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2015 Zhang Rui
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package cn.demo.videolist.media;
18 |
19 | import android.annotation.TargetApi;
20 | import android.content.Context;
21 | import android.graphics.SurfaceTexture;
22 | import android.os.Build;
23 | import android.support.annotation.NonNull;
24 | import android.support.annotation.Nullable;
25 | import android.util.AttributeSet;
26 | import android.util.Log;
27 | import android.view.Surface;
28 | import android.view.SurfaceHolder;
29 | import android.view.TextureView;
30 | import android.view.View;
31 | import android.view.accessibility.AccessibilityEvent;
32 | import android.view.accessibility.AccessibilityNodeInfo;
33 |
34 | import java.lang.ref.WeakReference;
35 | import java.util.Map;
36 | import java.util.concurrent.ConcurrentHashMap;
37 |
38 | import tv.danmaku.ijk.media.player.IMediaPlayer;
39 | import tv.danmaku.ijk.media.player.ISurfaceTextureHolder;
40 | import tv.danmaku.ijk.media.player.ISurfaceTextureHost;
41 |
42 | @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
43 | public class TextureRenderView extends TextureView implements IRenderView {
44 | private static final String TAG = "TextureRenderView";
45 | private MeasureHelper mMeasureHelper;
46 |
47 | public TextureRenderView(Context context) {
48 | super(context);
49 | initView(context);
50 | }
51 |
52 | public TextureRenderView(Context context, AttributeSet attrs) {
53 | super(context, attrs);
54 | initView(context);
55 | }
56 |
57 | public TextureRenderView(Context context, AttributeSet attrs, int defStyleAttr) {
58 | super(context, attrs, defStyleAttr);
59 | initView(context);
60 | }
61 |
62 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
63 | public TextureRenderView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
64 | super(context, attrs, defStyleAttr, defStyleRes);
65 | initView(context);
66 | }
67 |
68 | private void initView(Context context) {
69 | mMeasureHelper = new MeasureHelper(this);
70 | mSurfaceCallback = new SurfaceCallback(this);
71 | setSurfaceTextureListener(mSurfaceCallback);
72 | }
73 |
74 | @Override
75 | public View getView() {
76 | return this;
77 | }
78 |
79 | @Override
80 | public boolean shouldWaitForResize() {
81 | return false;
82 | }
83 |
84 | @Override
85 | protected void onDetachedFromWindow() {
86 | mSurfaceCallback.willDetachFromWindow();
87 | super.onDetachedFromWindow();
88 | mSurfaceCallback.didDetachFromWindow();
89 | }
90 |
91 | //--------------------
92 | // Layout & Measure
93 | //--------------------
94 | @Override
95 | public void setVideoSize(int videoWidth, int videoHeight) {
96 | if (videoWidth > 0 && videoHeight > 0) {
97 | mMeasureHelper.setVideoSize(videoWidth, videoHeight);
98 | requestLayout();
99 | }
100 | }
101 |
102 | @Override
103 | public void setVideoSampleAspectRatio(int videoSarNum, int videoSarDen) {
104 | if (videoSarNum > 0 && videoSarDen > 0) {
105 | mMeasureHelper.setVideoSampleAspectRatio(videoSarNum, videoSarDen);
106 | requestLayout();
107 | }
108 | }
109 |
110 | @Override
111 | public void setVideoRotation(int degree) {
112 | mMeasureHelper.setVideoRotation(degree);
113 | setRotation(degree);
114 | }
115 |
116 | @Override
117 | public void setAspectRatio(int aspectRatio) {
118 | mMeasureHelper.setAspectRatio(aspectRatio);
119 | requestLayout();
120 | }
121 |
122 | @Override
123 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
124 | mMeasureHelper.doMeasure(widthMeasureSpec, heightMeasureSpec);
125 | setMeasuredDimension(mMeasureHelper.getMeasuredWidth(), mMeasureHelper.getMeasuredHeight());
126 | }
127 |
128 | //--------------------
129 | // TextureViewHolder
130 | //--------------------
131 |
132 | public IRenderView.ISurfaceHolder getSurfaceHolder() {
133 | return new InternalSurfaceHolder(this, mSurfaceCallback.mSurfaceTexture, mSurfaceCallback);
134 | }
135 |
136 | private static final class InternalSurfaceHolder implements IRenderView.ISurfaceHolder {
137 | private TextureRenderView mTextureView;
138 | private SurfaceTexture mSurfaceTexture;
139 | private ISurfaceTextureHost mSurfaceTextureHost;
140 |
141 | public InternalSurfaceHolder(@NonNull TextureRenderView textureView,
142 | @Nullable SurfaceTexture surfaceTexture,
143 | @NonNull ISurfaceTextureHost surfaceTextureHost) {
144 | mTextureView = textureView;
145 | mSurfaceTexture = surfaceTexture;
146 | mSurfaceTextureHost = surfaceTextureHost;
147 | }
148 |
149 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
150 | public void bindToMediaPlayer(IMediaPlayer mp) {
151 | if (mp == null)
152 | return;
153 |
154 | if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) &&
155 | (mp instanceof ISurfaceTextureHolder)) {
156 | ISurfaceTextureHolder textureHolder = (ISurfaceTextureHolder) mp;
157 | mTextureView.mSurfaceCallback.setOwnSurfaceTexture(false);
158 |
159 | SurfaceTexture surfaceTexture = textureHolder.getSurfaceTexture();
160 | if (surfaceTexture != null) {
161 | mTextureView.setSurfaceTexture(surfaceTexture);
162 | } else {
163 | textureHolder.setSurfaceTexture(mSurfaceTexture);
164 | textureHolder.setSurfaceTextureHost(mTextureView.mSurfaceCallback);
165 | }
166 | } else {
167 | mp.setSurface(openSurface());
168 | }
169 | }
170 |
171 | @NonNull
172 | @Override
173 | public IRenderView getRenderView() {
174 | return mTextureView;
175 | }
176 |
177 | @Nullable
178 | @Override
179 | public SurfaceHolder getSurfaceHolder() {
180 | return null;
181 | }
182 |
183 | @Nullable
184 | @Override
185 | public SurfaceTexture getSurfaceTexture() {
186 | return mSurfaceTexture;
187 | }
188 |
189 | @Nullable
190 | @Override
191 | public Surface openSurface() {
192 | if (mSurfaceTexture == null)
193 | return null;
194 | return new Surface(mSurfaceTexture);
195 | }
196 | }
197 |
198 | //-------------------------
199 | // SurfaceHolder.Callback
200 | //-------------------------
201 |
202 | @Override
203 | public void addRenderCallback(IRenderCallback callback) {
204 | mSurfaceCallback.addRenderCallback(callback);
205 | }
206 |
207 | @Override
208 | public void removeRenderCallback(IRenderCallback callback) {
209 | mSurfaceCallback.removeRenderCallback(callback);
210 | }
211 |
212 | private SurfaceCallback mSurfaceCallback;
213 |
214 | private static final class SurfaceCallback implements SurfaceTextureListener, ISurfaceTextureHost {
215 | private SurfaceTexture mSurfaceTexture;
216 | private boolean mIsFormatChanged;
217 | private int mWidth;
218 | private int mHeight;
219 |
220 | private boolean mOwnSurfaceTexture = true;
221 | private boolean mWillDetachFromWindow = false;
222 | private boolean mDidDetachFromWindow = false;
223 |
224 | private WeakReference mWeakRenderView;
225 | private Map mRenderCallbackMap = new ConcurrentHashMap();
226 |
227 | public SurfaceCallback(@NonNull TextureRenderView renderView) {
228 | mWeakRenderView = new WeakReference(renderView);
229 | }
230 |
231 | public void setOwnSurfaceTexture(boolean ownSurfaceTexture) {
232 | mOwnSurfaceTexture = ownSurfaceTexture;
233 | }
234 |
235 | public void addRenderCallback(@NonNull IRenderCallback callback) {
236 | mRenderCallbackMap.put(callback, callback);
237 |
238 | ISurfaceHolder surfaceHolder = null;
239 | if (mSurfaceTexture != null) {
240 | if (surfaceHolder == null)
241 | surfaceHolder = new InternalSurfaceHolder(mWeakRenderView.get(), mSurfaceTexture, this);
242 | callback.onSurfaceCreated(surfaceHolder, mWidth, mHeight);
243 | }
244 |
245 | if (mIsFormatChanged) {
246 | if (surfaceHolder == null)
247 | surfaceHolder = new InternalSurfaceHolder(mWeakRenderView.get(), mSurfaceTexture, this);
248 | callback.onSurfaceChanged(surfaceHolder, 0, mWidth, mHeight);
249 | }
250 | }
251 |
252 | public void removeRenderCallback(@NonNull IRenderCallback callback) {
253 | mRenderCallbackMap.remove(callback);
254 | }
255 |
256 | @Override
257 | public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
258 | mSurfaceTexture = surface;
259 | mIsFormatChanged = false;
260 | mWidth = 0;
261 | mHeight = 0;
262 |
263 | ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakRenderView.get(), surface, this);
264 | for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) {
265 | renderCallback.onSurfaceCreated(surfaceHolder, 0, 0);
266 | }
267 | }
268 |
269 | @Override
270 | public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
271 | mSurfaceTexture = surface;
272 | mIsFormatChanged = true;
273 | mWidth = width;
274 | mHeight = height;
275 |
276 | ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakRenderView.get(), surface, this);
277 | for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) {
278 | renderCallback.onSurfaceChanged(surfaceHolder, 0, width, height);
279 | }
280 | }
281 |
282 | @Override
283 | public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
284 | mSurfaceTexture = surface;
285 | mIsFormatChanged = false;
286 | mWidth = 0;
287 | mHeight = 0;
288 |
289 | ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakRenderView.get(), surface, this);
290 | for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) {
291 | renderCallback.onSurfaceDestroyed(surfaceHolder);
292 | }
293 |
294 | Log.d(TAG, "onSurfaceTextureDestroyed: destroy: " + mOwnSurfaceTexture);
295 | return mOwnSurfaceTexture;
296 | }
297 |
298 | @Override
299 | public void onSurfaceTextureUpdated(SurfaceTexture surface) {
300 | }
301 |
302 | //-------------------------
303 | // ISurfaceTextureHost
304 | //-------------------------
305 |
306 | @Override
307 | public void releaseSurfaceTexture(SurfaceTexture surfaceTexture) {
308 | if (surfaceTexture == null) {
309 | Log.d(TAG, "releaseSurfaceTexture: null");
310 | } else if (mDidDetachFromWindow) {
311 | if (surfaceTexture != mSurfaceTexture) {
312 | Log.d(TAG, "releaseSurfaceTexture: didDetachFromWindow(): release different SurfaceTexture");
313 | surfaceTexture.release();
314 | } else if (!mOwnSurfaceTexture) {
315 | Log.d(TAG, "releaseSurfaceTexture: didDetachFromWindow(): release detached SurfaceTexture");
316 | surfaceTexture.release();
317 | } else {
318 | Log.d(TAG, "releaseSurfaceTexture: didDetachFromWindow(): already released by TextureView");
319 | }
320 | } else if (mWillDetachFromWindow) {
321 | if (surfaceTexture != mSurfaceTexture) {
322 | Log.d(TAG, "releaseSurfaceTexture: willDetachFromWindow(): release different SurfaceTexture");
323 | surfaceTexture.release();
324 | } else if (!mOwnSurfaceTexture) {
325 | Log.d(TAG, "releaseSurfaceTexture: willDetachFromWindow(): re-attach SurfaceTexture to TextureView");
326 | setOwnSurfaceTexture(true);
327 | } else {
328 | Log.d(TAG, "releaseSurfaceTexture: willDetachFromWindow(): will released by TextureView");
329 | }
330 | } else {
331 | if (surfaceTexture != mSurfaceTexture) {
332 | Log.d(TAG, "releaseSurfaceTexture: alive: release different SurfaceTexture");
333 | surfaceTexture.release();
334 | } else if (!mOwnSurfaceTexture) {
335 | Log.d(TAG, "releaseSurfaceTexture: alive: re-attach SurfaceTexture to TextureView");
336 | setOwnSurfaceTexture(true);
337 | } else {
338 | Log.d(TAG, "releaseSurfaceTexture: alive: will released by TextureView");
339 | }
340 | }
341 | }
342 |
343 | public void willDetachFromWindow() {
344 | Log.d(TAG, "willDetachFromWindow()");
345 | mWillDetachFromWindow = true;
346 | }
347 |
348 | public void didDetachFromWindow() {
349 | Log.d(TAG, "didDetachFromWindow()");
350 | mDidDetachFromWindow = true;
351 | }
352 | }
353 |
354 | //--------------------
355 | // Accessibility
356 | //--------------------
357 |
358 | @Override
359 | public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
360 | super.onInitializeAccessibilityEvent(event);
361 | event.setClassName(TextureRenderView.class.getName());
362 | }
363 |
364 | @Override
365 | public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
366 | super.onInitializeAccessibilityNodeInfo(info);
367 | info.setClassName(TextureRenderView.class.getName());
368 | }
369 | }
370 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/demo/videolist/media/VideoAdapter.java:
--------------------------------------------------------------------------------
1 | package cn.demo.videolist.media;
2 |
3 | import android.content.Context;
4 | import android.support.v7.widget.RecyclerView;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.FrameLayout;
9 | import android.widget.RelativeLayout;
10 | import android.widget.TextView;
11 |
12 | import java.util.ArrayList;
13 | import java.util.List;
14 |
15 | import cn.demo.videolist.R;
16 | import cn.demo.videolist.VideoItemData;
17 |
18 | /**
19 | * Author wangchenchen
20 | * Description video列表adapter
21 | */
22 | public class VideoAdapter extends RecyclerView.Adapter {
23 |
24 | private List list;
25 | public VideoAdapter(Context context){
26 | list=new ArrayList<>();
27 | }
28 |
29 | @Override
30 | public VideoAdapter.VideoViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
31 |
32 | View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_video, parent, false);
33 | VideoAdapter.VideoViewHolder holder = new VideoAdapter.VideoViewHolder(view);
34 | view.setTag(holder);
35 | return new VideoAdapter.VideoViewHolder(view);
36 | }
37 |
38 | @Override
39 | public void onBindViewHolder(VideoAdapter.VideoViewHolder holder, int position) {
40 | holder.update(position);
41 | }
42 |
43 | @Override
44 | public int getItemCount() {
45 | return list.size();
46 | }
47 |
48 | public void refresh(List list){
49 | this.list.clear();
50 | this.list.addAll(list);
51 | notifyDataSetChanged();
52 | }
53 |
54 | public class VideoViewHolder extends RecyclerView.ViewHolder {
55 | private FrameLayout videoLayout;
56 | private int position;
57 | private RelativeLayout showView;
58 | private TextView title,from;
59 |
60 |
61 | public VideoViewHolder(View itemView) {
62 | super(itemView);
63 | videoLayout = (FrameLayout) itemView.findViewById(R.id.layout_video);
64 | showView= (RelativeLayout) itemView.findViewById(R.id.showview);
65 | title= (TextView) itemView.findViewById(R.id.title);
66 | from= (TextView) itemView.findViewById(R.id.from);
67 | }
68 |
69 | public void update(final int position) {
70 | this.position = position;
71 | title.setText(list.get(position).getTitle());
72 | title.setText(list.get(position).getVideosource());
73 | showView.setOnClickListener(new View.OnClickListener() {
74 | @Override
75 | public void onClick(View v) {
76 | showView.setVisibility(View.GONE);
77 | if (click != null)
78 | click.onclick(position);
79 | }
80 | });
81 | }
82 | }
83 |
84 | private onClick click;
85 |
86 | public void setClick(onClick click){
87 | this.click=click;
88 | }
89 |
90 | public static interface onClick{
91 | void onclick(int position);
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/app/src/main/jniLibs/arm64-v8a/libijkffmpeg.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/w1123440793/VideoListDemo/e0b124d398016d81b4105e0d05b5df14a179bcfb/app/src/main/jniLibs/arm64-v8a/libijkffmpeg.so
--------------------------------------------------------------------------------
/app/src/main/jniLibs/arm64-v8a/libijkplayer.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/w1123440793/VideoListDemo/e0b124d398016d81b4105e0d05b5df14a179bcfb/app/src/main/jniLibs/arm64-v8a/libijkplayer.so
--------------------------------------------------------------------------------
/app/src/main/jniLibs/arm64-v8a/libijksdl.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/w1123440793/VideoListDemo/e0b124d398016d81b4105e0d05b5df14a179bcfb/app/src/main/jniLibs/arm64-v8a/libijksdl.so
--------------------------------------------------------------------------------
/app/src/main/jniLibs/x86_64/libijkffmpeg.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/w1123440793/VideoListDemo/e0b124d398016d81b4105e0d05b5df14a179bcfb/app/src/main/jniLibs/x86_64/libijkffmpeg.so
--------------------------------------------------------------------------------
/app/src/main/jniLibs/x86_64/libijkplayer.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/w1123440793/VideoListDemo/e0b124d398016d81b4105e0d05b5df14a179bcfb/app/src/main/jniLibs/x86_64/libijkplayer.so
--------------------------------------------------------------------------------
/app/src/main/jniLibs/x86_64/libijksdl.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/w1123440793/VideoListDemo/e0b124d398016d81b4105e0d05b5df14a179bcfb/app/src/main/jniLibs/x86_64/libijksdl.so
--------------------------------------------------------------------------------
/app/src/main/res/drawable/app_video_center_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_seek.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -
5 |
6 |
7 |
8 |
9 |
10 | -
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | -
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_thumb.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_video.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
17 |
18 |
23 |
24 |
28 |
29 |
30 |
31 |
35 |
36 |
41 |
42 |
48 |
49 |
50 |
51 |
52 |
57 |
58 |
67 |
68 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_video_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
16 |
17 |
24 |
25 |
29 |
30 |
37 |
38 |
39 |
40 |
45 |
46 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/media_contoller.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
14 |
15 |
21 |
22 |
32 |
33 |
39 |
40 |
48 |
49 |
61 |
62 |
69 |
70 |
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/table_media_info.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/table_media_info_row1.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/table_media_info_row2.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
15 |
16 |
23 |
24 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/table_media_info_section.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/view_video_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
15 |
16 |
23 |
24 |
29 |
30 |
36 |
37 |
46 |
47 |
52 |
53 |
63 |
64 |
65 |
74 |
75 |
80 |
81 |
91 |
92 |
93 |
103 |
104 |
105 |
106 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/w1123440793/VideoListDemo/e0b124d398016d81b4105e0d05b5df14a179bcfb/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/w1123440793/VideoListDemo/e0b124d398016d81b4105e0d05b5df14a179bcfb/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/full_screen_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/w1123440793/VideoListDemo/e0b124d398016d81b4105e0d05b5df14a179bcfb/app/src/main/res/mipmap-xhdpi/full_screen_icon.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/w1123440793/VideoListDemo/e0b124d398016d81b4105e0d05b5df14a179bcfb/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/sound_mult_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/w1123440793/VideoListDemo/e0b124d398016d81b4105e0d05b5df14a179bcfb/app/src/main/res/mipmap-xhdpi/sound_mult_icon.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/sound_open_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/w1123440793/VideoListDemo/e0b124d398016d81b4105e0d05b5df14a179bcfb/app/src/main/res/mipmap-xhdpi/sound_open_icon.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/video_play_btn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/w1123440793/VideoListDemo/e0b124d398016d81b4105e0d05b5df14a179bcfb/app/src/main/res/mipmap-xhdpi/video_play_btn.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/video_stop_btn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/w1123440793/VideoListDemo/e0b124d398016d81b4105e0d05b5df14a179bcfb/app/src/main/res/mipmap-xhdpi/video_stop_btn.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/w1123440793/VideoListDemo/e0b124d398016d81b4105e0d05b5df14a179bcfb/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/w1123440793/VideoListDemo/e0b124d398016d81b4105e0d05b5df14a179bcfb/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/raw/video_list.json:
--------------------------------------------------------------------------------
1 | {
2 | "list":[{
3 | "topicImg": "http://vimg1.ws.126.net/image/snapshot/2016/4/7/G/VBILRDA7G.jpg",
4 | "replyCount": 0,
5 | "videosource": "新媒体",
6 | "mp4Hd_url": null,
7 | "topicDesc": "此诚哥非彼诚哥",
8 | "topicSid": "VBILRDA7E",
9 | "cover": "http://vimg1.ws.126.net/image/snapshot/2016/4/3/7/VBKQP3437.jpg",
10 | "title": "我看过魔术之中最牛逼的一个",
11 | "playCount": 12336,
12 | "replyBoard": "video_bbs",
13 | "videoTopic": {
14 | "alias": "此诚哥非彼诚哥",
15 | "tname": "诚哥讲笑话",
16 | "ename": "T1460515708449",
17 | "tid": "T1460515708449"
18 | },
19 | "sectiontitle": "",
20 | "description": "我的天,他是怎么做到的。。。",
21 | "replyid": "BKQP3436008535RB",
22 | "mp4_url": "http://flv2.bn.netease.com/videolib3/1604/28/fVobI0704/SD/fVobI0704-mobile.mp4",
23 | "length": 65,
24 | "playersize": 1,
25 | "m3u8Hd_url": null,
26 | "vid": "VBKQP3436",
27 | "m3u8_url": "http://flv2.bn.netease.com/videolib3/1604/28/fVobI0704/SD/movie_index.m3u8",
28 | "ptime": "2016-04-28 16:04:50",
29 | "topicName": "诚哥讲笑话"
30 | }, {
31 | "topicImg": "http://vimg2.ws.126.net/image/snapshot/2016/2/I/M/VBG7HE1IM.jpg",
32 | "replyCount": 0,
33 | "videosource": "新媒体",
34 | "mp4Hd_url": null,
35 | "topicDesc": "不开心?来这寻找最快乐的自己",
36 | "topicSid": "VBG2BBJ7H",
37 | "cover": "http://vimg3.ws.126.net/image/snapshot/2016/4/U/0/VBKQOPUU0.jpg",
38 | "title": "逗比美女:养我也是为了吃肉吗",
39 | "playCount": 9448,
40 | "replyBoard": "video_bbs",
41 | "videoTopic": {
42 | "alias": "不开心?来这寻找最快乐的自己",
43 | "tname": "逗比对嘴表演",
44 | "ename": "T1460515712745",
45 | "tid": "T1460515712745"
46 | },
47 | "sectiontitle": "",
48 | "description": "小咖秀合作",
49 | "replyid": "BKQOAO51008535RB",
50 | "mp4_url": "http://flv2.bn.netease.com/tvmrepo/2016/4/G/O/EBKQOA8GO/SD/EBKQOA8GO-mobile.mp4",
51 | "length": 17,
52 | "playersize": 0,
53 | "m3u8Hd_url": null,
54 | "vid": "VBKQOAO51",
55 | "m3u8_url": "http://flv2.bn.netease.com/tvmrepo/2016/4/G/O/EBKQOA8GO/SD/movie_index.m3u8",
56 | "ptime": "2016-04-28 15:51:15",
57 | "topicName": "逗比对嘴表演"
58 | }, {
59 | "topicImg": "http://vimg2.ws.126.net/image/snapshot/2016/3/5/S/VBHT6285S.jpg",
60 | "replyCount": 0,
61 | "videosource": "新媒体",
62 | "mp4Hd_url": null,
63 | "topicDesc": "让你每天笑不停。",
64 | "topicSid": "VBHT6285Q",
65 | "cover": "http://vimg3.ws.126.net/image/snapshot/2016/4/0/R/VBKQOOU0R.jpg",
66 | "title": "女蛇精病唱民歌:把这段话念一遍",
67 | "playCount": 9600,
68 | "replyBoard": "video_bbs",
69 | "videoTopic": {
70 | "alias": "让你每天笑不停。",
71 | "tname": "十秒你就笑",
72 | "ename": "T1460515715472",
73 | "tid": "T1460515715472"
74 | },
75 | "sectiontitle": "",
76 | "description": "小咖秀合作",
77 | "replyid": "BKQO9M84008535RB",
78 | "mp4_url": "http://flv2.bn.netease.com/tvmrepo/2016/4/M/Q/EBKQO95MQ/SD/EBKQO95MQ-mobile.mp4",
79 | "length": 32,
80 | "playersize": 0,
81 | "m3u8Hd_url": null,
82 | "vid": "VBKQO9M84",
83 | "m3u8_url": "http://flv2.bn.netease.com/tvmrepo/2016/4/M/Q/EBKQO95MQ/SD/movie_index.m3u8",
84 | "ptime": "2016-04-28 15:50:40",
85 | "topicName": "十秒你就笑"
86 | }, {
87 | "topicImg": "http://vimg1.ws.126.net/image/snapshot/2016/4/C/0/VBJ4774C0.jpg",
88 | "replyCount": 0,
89 | "videosource": "新媒体",
90 | "mp4Hd_url": null,
91 | "topicDesc": "这里有世界各地的奇葩事",
92 | "topicSid": "VBJ4774BU",
93 | "cover": "http://vimg1.ws.126.net/image/snapshot/2016/4/C/L/VBKQOOPCL.jpg",
94 | "title": "呆萌妹子实力出演:可爱皮卡丘",
95 | "playCount": 9117,
96 | "replyBoard": "video_bbs",
97 | "videoTopic": {
98 | "alias": "这里有世界各地的奇葩事",
99 | "tname": "阿拉伯飞毯",
100 | "ename": "T1460515711614",
101 | "tid": "T1460515711614"
102 | },
103 | "sectiontitle": "",
104 | "description": "小咖秀合作",
105 | "replyid": "BKQO86Q5008535RB",
106 | "mp4_url": "http://flv2.bn.netease.com/tvmrepo/2016/4/G/S/EBKQO7TGS/SD/EBKQO7TGS-mobile.mp4",
107 | "length": 9,
108 | "playersize": 0,
109 | "m3u8Hd_url": null,
110 | "vid": "VBKQO86Q5",
111 | "m3u8_url": "http://flv2.bn.netease.com/tvmrepo/2016/4/G/S/EBKQO7TGS/SD/movie_index.m3u8",
112 | "ptime": "2016-04-28 15:49:59",
113 | "topicName": "阿拉伯飞毯"
114 | }, {
115 | "topicImg": "http://vimg3.ws.126.net/image/snapshot/2016/3/C/N/VBG7AA2CN.jpg",
116 | "replyCount": 0,
117 | "videosource": "新媒体",
118 | "mp4Hd_url": null,
119 | "topicDesc": "你是猴子请来的逗比么?",
120 | "topicSid": "VBG7AA2CK",
121 | "cover": "http://vimg1.ws.126.net/image/snapshot/2016/4/7/E/VBKQOA87E.jpg",
122 | "title": "史上最诱惑的广告,基本广告词说完大家也不知道在说什么",
123 | "playCount": 9448,
124 | "replyBoard": "video_bbs",
125 | "videoTopic": {
126 | "alias": "你是猴子请来的逗比么?",
127 | "tname": "有一种美女叫逗比",
128 | "ename": "T1460515706915",
129 | "tid": "T1460515706915"
130 | },
131 | "sectiontitle": "",
132 | "description": "小咖秀合作",
133 | "replyid": "BKQO3HRO008535RB",
134 | "mp4_url": "http://flv2.bn.netease.com/tvmrepo/2016/4/0/K/EBKQO3C0K-mobile.mp4",
135 | "length": 17,
136 | "playersize": 0,
137 | "m3u8Hd_url": null,
138 | "vid": "VBKQO3HRO",
139 | "m3u8_url": "http://flv2.bn.netease.com/tvmrepo/2016/4/0/K/EBKQO3C0K-mobile.mp4",
140 | "ptime": "2016-04-28 15:47:30",
141 | "topicName": "有一种美女叫逗比"
142 | }, {
143 | "topicImg": "http://vimg2.ws.126.net/image/snapshot/2016/2/U/L/VBG7H0SUL.jpg",
144 | "replyCount": 0,
145 | "videosource": "新媒体",
146 | "mp4Hd_url": null,
147 | "topicDesc": "不说不笑不热闹",
148 | "topicSid": "VBFGARMFR",
149 | "cover": "http://vimg2.ws.126.net/image/snapshot/2016/4/I/V/VBKQOCKIV.jpg",
150 | "title": "男子逗比搞笑自语:下雨了出去洗个头",
151 | "playCount": 19721,
152 | "replyBoard": "video_bbs",
153 | "videoTopic": {
154 | "alias": "不说不笑不热闹",
155 | "tname": "搞笑一箩筐",
156 | "ename": "T1460515708679",
157 | "tid": "T1460515708679"
158 | },
159 | "sectiontitle": "",
160 | "description": "小咖秀合作",
161 | "replyid": "BKQO37QV008535RB",
162 | "mp4_url": "http://flv2.bn.netease.com/tvmrepo/2016/4/I/G/EBKQO2SIG/SD/EBKQO2SIG-mobile.mp4",
163 | "length": 39,
164 | "playersize": 0,
165 | "m3u8Hd_url": null,
166 | "vid": "VBKQO37QV",
167 | "m3u8_url": "http://flv2.bn.netease.com/tvmrepo/2016/4/I/G/EBKQO2SIG/SD/movie_index.m3u8",
168 | "ptime": "2016-04-28 15:47:14",
169 | "topicName": "搞笑一箩筐"
170 | }, {
171 | "topicImg": "http://vimg1.ws.126.net/image/snapshot/2016/2/I/M/VBG7HE1IM.jpg",
172 | "replyCount": 0,
173 | "videosource": "新媒体",
174 | "mp4Hd_url": null,
175 | "topicDesc": "不开心?来这寻找最快乐的自己",
176 | "topicSid": "VBG2BBJ7H",
177 | "cover": "http://vimg3.ws.126.net/image/snapshot/2016/4/C/4/VBKQO4JC4.jpg",
178 | "title": "逗逼制服美女之旅客朋友们请注意啦",
179 | "playCount": 9768,
180 | "replyBoard": "video_bbs",
181 | "videoTopic": {
182 | "alias": "不开心?来这寻找最快乐的自己",
183 | "tname": "逗比对嘴表演",
184 | "ename": "T1460515712745",
185 | "tid": "T1460515712745"
186 | },
187 | "sectiontitle": "",
188 | "description": "小咖秀合作",
189 | "replyid": "BKQO0LN8008535RB",
190 | "mp4_url": "http://flv2.bn.netease.com/tvmrepo/2016/4/5/G/EBKQO085G/SD/EBKQO085G-mobile.mp4",
191 | "length": 40,
192 | "playersize": 0,
193 | "m3u8Hd_url": null,
194 | "vid": "VBKQO0LN8",
195 | "m3u8_url": "http://flv2.bn.netease.com/tvmrepo/2016/4/5/G/EBKQO085G/SD/movie_index.m3u8",
196 | "ptime": "2016-04-28 15:45:47",
197 | "topicName": "逗比对嘴表演"
198 | }, {
199 | "topicImg": "http://vimg2.ws.126.net/image/snapshot/2016/2/B/5/VBG7H5IB5.jpg",
200 | "replyCount": 0,
201 | "videosource": "新媒体",
202 | "mp4Hd_url": null,
203 | "topicDesc": "探索世界,捕捉甜虾奇闻怪诞。",
204 | "topicSid": "VBFGGQAN8",
205 | "cover": "http://vimg3.ws.126.net/image/snapshot/2016/4/I/V/VBKQNCCIV.jpg",
206 | "title": "我家一条巷相隔六尺宽包容无线大",
207 | "playCount": 9847,
208 | "replyBoard": "video_bbs",
209 | "videoTopic": {
210 | "alias": "探索世界,捕捉甜虾奇闻怪诞。",
211 | "tname": "奇闻异事",
212 | "ename": "T1460515709001",
213 | "tid": "T1460515709001"
214 | },
215 | "sectiontitle": "",
216 | "description": "六尺巷",
217 | "replyid": "BKQN40G0008535RB",
218 | "mp4_url": "http://flv2.bn.netease.com/tvmrepo/2016/4/U/4/EBKQN3GU4/SD/EBKQN3GU4-mobile.mp4",
219 | "length": 36,
220 | "playersize": 0,
221 | "m3u8Hd_url": null,
222 | "vid": "VBKQN40G0",
223 | "m3u8_url": "http://flv2.bn.netease.com/tvmrepo/2016/4/U/4/EBKQN3GU4/SD/movie_index.m3u8",
224 | "ptime": "2016-04-28 15:30:06",
225 | "topicName": "奇闻异事"
226 | }, {
227 | "topicImg": "http://vimg1.ws.126.net/image/snapshot/2016/3/B/0/VBI02ISB0.jpg",
228 | "replyCount": 0,
229 | "videosource": "新媒体",
230 | "mp4Hd_url": null,
231 | "topicDesc": "狗哥出品,必属精品。",
232 | "topicSid": "VBI02ISAT",
233 | "cover": "http://vimg3.ws.126.net/image/snapshot/2016/4/4/O/VBKQMQE4O.jpg",
234 | "title": "教你如何当一位完美的男友",
235 | "playCount": 18049,
236 | "replyBoard": "video_bbs",
237 | "videoTopic": {
238 | "alias": "狗哥出品,必属精品。",
239 | "tname": "东北李二狗",
240 | "ename": "T1460515706478",
241 | "tid": "T1460515706478"
242 | },
243 | "sectiontitle": "",
244 | "description": "单身狗哭晕,这样的女友还不珍惜。",
245 | "replyid": "BKQMKH2P008535RB",
246 | "mp4_url": "http://flv2.bn.netease.com/videolib3/1604/28/Dgwkr8165/SD/Dgwkr8165-mobile.mp4",
247 | "length": 70,
248 | "playersize": 0,
249 | "m3u8Hd_url": null,
250 | "vid": "VBKQMKH2P",
251 | "m3u8_url": "http://flv2.bn.netease.com/videolib3/1604/28/Dgwkr8165/SD/movie_index.m3u8",
252 | "ptime": "2016-04-28 15:21:55",
253 | "topicName": "东北李二狗"
254 | }, {
255 | "topicImg": "http://vimg1.ws.126.net/image/snapshot/2016/3/C/6/VBI02GKC6.jpg",
256 | "replyCount": 0,
257 | "videosource": "新媒体",
258 | "mp4Hd_url": null,
259 | "topicDesc": "让你开心每一天。",
260 | "topicSid": "VBI02GKC3",
261 | "cover": "http://vimg1.ws.126.net/image/snapshot/2016/4/S/R/VBKQMQFSR.jpg",
262 | "title": "逗比美女的逗比故事:和老外打招呼",
263 | "playCount": 9511,
264 | "replyBoard": "video_bbs",
265 | "videoTopic": {
266 | "alias": "让你开心每一天。",
267 | "tname": "劲爆搞笑",
268 | "ename": "T1460515706582",
269 | "tid": "T1460515706582"
270 | },
271 | "sectiontitle": "",
272 | "description": "小咖秀合作",
273 | "replyid": "BKQMD062008535RB",
274 | "mp4_url": "http://flv2.bn.netease.com/tvmrepo/2016/4/N/C/EBKQMCMNC/SD/EBKQMCMNC-mobile.mp4",
275 | "length": 20,
276 | "playersize": 0,
277 | "m3u8Hd_url": null,
278 | "vid": "VBKQMD062",
279 | "m3u8_url": "http://flv2.bn.netease.com/tvmrepo/2016/4/N/C/EBKQMCMNC/SD/movie_index.m3u8",
280 | "ptime": "2016-04-28 15:17:38",
281 | "topicName": "劲爆搞笑"
282 | }]
283 |
284 | }
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 10dp
4 | 10dp
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | VideoListDemo
3 |
4 | 本地视频
5 | 在线视频
6 |
7 |
8 | loading...
9 | Click to load more
10 | All the data has been loaded
11 | It is empty. :(
12 | Load error, click to retry
13 | N/A
14 | Close
15 | Exit
16 | Sample
17 | Recent
18 | Settings
19 | Tracks
20 | Player
21 | Render
22 | Scale
23 | Info
24 | vdec
25 | fps
26 | v-cache
27 | a-cache
28 |
29 | Media Information
30 | Player
31 | Media
32 | Profile level
33 | Pixel format
34 | Resolution
35 | Length
36 | Stream #%d
37 | Type
38 | Language
39 | Codec
40 | Frame rate
41 | Bit rate
42 | Sample rate
43 | Channels
44 | *
45 | *
46 |
47 | Video
48 | Audio
49 | Subtitle
50 | Timed text
51 | Meta data
52 | Unknown
53 |
54 | Invalid progressive playback
55 | Unknown
56 | OK
57 |
58 | Aspect / Fit parent
59 | Aspect / Fill parent
60 | Aspect / Wrap content
61 | Free / Fill parent
62 | 16:9 / Fit parent
63 | 4:3 / Fit parent
64 |
65 | Render: None
66 | Render: SurfaceView
67 | Render: TextureView
68 |
69 | Player: None
70 | Player: AndroidMediaPlayer
71 | Player: IjkMediaPlayer
72 | Player: IjkExoMediaPlayer
73 |
74 |
75 | Please specify the address of the video playback
76 |
77 | Player encountered a small problem
78 |
79 | can not play this video
80 |
81 | player not support this device
82 |
83 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings_pref.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | General
5 |
6 | pref.enable_background_play
7 | Enable background play
8 | need Android 4.0+
9 |
10 | pref.using_android_player
11 | Using system player
12 |
13 |
14 | pref.player
15 | Choose Player
16 |
17 | - Auto Select
18 | - AndroidMediaPlayer
19 | - IjkMediaPlayer
20 | - IjkExoMediaPlayer
21 |
22 |
23 | - 0
24 | - 1
25 | - 2
26 | - 3
27 |
28 |
29 | - Auto Select
30 | - AndroidMediaPlayer
31 | - IjkMediaPlayer
32 | - IjkExoMediaPlayer
33 |
34 |
35 |
36 | Video: ijkplayer
37 |
38 | pref.using_media_codec
39 | Using MediaCodec
40 |
41 |
42 | pref.using_media_codec_auto_rotate
43 | Using MediaCodec auto rotate
44 |
45 |
46 | pref.pixel_format
47 | Pixel Format
48 |
49 | - Auto Select
50 | - RGB 565
51 | - RGB 888
52 | - RGBX 8888
53 | - YV12
54 | - OpenGL ES2
55 |
56 |
57 |
58 | - fcc-rv16
59 | - fcc-rv24
60 | - fcc-rv32
61 | - fcc-yv12
62 | - fcc-_es2
63 |
64 |
65 | - Auto Select
66 | - RGB 565
67 | - RGB 888
68 | - RGBX 8888
69 | - YV12
70 | - OpenGL ES2
71 |
72 |
73 |
74 | Audio: ijkplayer
75 |
76 | pref.using_opensl_es
77 | Using OpenSL ES
78 |
79 |
80 |
81 | RenderView
82 |
83 | pref.enable_no_view
84 | Enable NoView
85 |
86 |
87 | pref.enable_surface_view
88 | Enable SurfaceView
89 |
90 |
91 | pref.enable_texture_view
92 | Enable TextureView
93 |
94 |
95 | pref.enable_detached_surface_texture
96 | Enable detached SurfaceTexture
97 |
98 |
99 |
100 |
101 |
102 |
103 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/cn/demo/videolist/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package cn.demo.videolist;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * To work on unit tests, switch the Test Artifact in the Build Variants view.
9 | */
10 | public class ExampleUnitTest {
11 | @Test
12 | public void addition_isCorrect() throws Exception {
13 | assertEquals(4, 2 + 2);
14 | }
15 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:1.5.0'
9 |
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | jcenter()
18 | }
19 | }
20 |
21 | task clean(type: Delete) {
22 | delete rootProject.buildDir
23 | }
24 |
25 | ext {
26 | compileSdkVersion = 23
27 | buildToolsVersion = "23.0.3"
28 | targetSdkVersion = 21
29 | }
30 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/w1123440793/VideoListDemo/e0b124d398016d81b4105e0d05b5df14a179bcfb/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Oct 21 11:34:03 PDT 2015
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/screenshots/vidiolist.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/w1123440793/VideoListDemo/e0b124d398016d81b4105e0d05b5df14a179bcfb/screenshots/vidiolist.gif
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------