├── list ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ ├── mipmap-hdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── btn_title_back.png │ │ │ │ ├── btn_title_black_back.png │ │ │ │ └── btn_title_back_pressed.png │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── drawable │ │ │ │ ├── btn_title_back_bg.xml │ │ │ │ ├── btn_shape.xml │ │ │ │ ├── text_selector.xml │ │ │ │ └── selector_text.xml │ │ │ ├── values-v21 │ │ │ │ └── styles.xml │ │ │ ├── values-w820dp │ │ │ │ └── dimens.xml │ │ │ ├── menu │ │ │ │ └── menu_main.xml │ │ │ ├── layout │ │ │ │ ├── table_media_info.xml │ │ │ │ ├── table_media_info_section.xml │ │ │ │ ├── table_media_info_row1.xml │ │ │ │ ├── activity_main.xml │ │ │ │ ├── table_media_info_row2.xml │ │ │ │ ├── content_main.xml │ │ │ │ └── playactivity_main.xml │ │ │ ├── values │ │ │ │ ├── colors.xml │ │ │ │ ├── dimens.xml │ │ │ │ ├── styles.xml │ │ │ │ ├── strings.xml │ │ │ │ └── strings_pref.xml │ │ │ └── color │ │ │ │ └── selector.xml │ │ ├── java │ │ │ └── voole │ │ │ │ └── example │ │ │ │ └── com │ │ │ │ └── myapplication │ │ │ │ ├── data │ │ │ │ └── liveData.java │ │ │ │ ├── media │ │ │ │ ├── IMediaController.java │ │ │ │ ├── IRenderView.java │ │ │ │ ├── MediaPlayerCompat.java │ │ │ │ ├── Settings.java │ │ │ │ ├── TableLayoutBinder.java │ │ │ │ ├── AndroidMediaController.java │ │ │ │ ├── InfoHudViewHolder.java │ │ │ │ ├── SurfaceRenderView.java │ │ │ │ ├── MeasureHelper.java │ │ │ │ ├── TextureRenderView.java │ │ │ │ └── IjkVideoView.java │ │ │ │ ├── services │ │ │ │ └── MediaPlayerService.java │ │ │ │ ├── adapter │ │ │ │ └── newAdapter.java │ │ │ │ ├── network │ │ │ │ └── HttpHelp.java │ │ │ │ └── activity │ │ │ │ ├── PlayerActivity.java │ │ │ │ └── MainActivity.java │ │ └── AndroidManifest.xml │ ├── test │ │ └── java │ │ │ └── voole │ │ │ └── example │ │ │ └── com │ │ │ └── myapplication │ │ │ └── ExampleUnitTest.java │ └── androidTest │ │ └── java │ │ └── voole │ │ └── example │ │ └── com │ │ └── myapplication │ │ └── ApplicationTest.java ├── proguard-rules.pro └── build.gradle └── README.md /list/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Player 2 | 直播项目的demo,仅供参考,不得用于商业用途 3 | 本demo是基于ijkplayer播放框架下实现的。 4 | -------------------------------------------------------------------------------- /list/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangzexin/Player_Demo/HEAD/list/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /list/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangzexin/Player_Demo/HEAD/list/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /list/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangzexin/Player_Demo/HEAD/list/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /list/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangzexin/Player_Demo/HEAD/list/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /list/src/main/res/mipmap-hdpi/btn_title_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangzexin/Player_Demo/HEAD/list/src/main/res/mipmap-hdpi/btn_title_back.png -------------------------------------------------------------------------------- /list/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangzexin/Player_Demo/HEAD/list/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /list/src/main/res/mipmap-hdpi/btn_title_black_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangzexin/Player_Demo/HEAD/list/src/main/res/mipmap-hdpi/btn_title_black_back.png -------------------------------------------------------------------------------- /list/src/main/res/mipmap-hdpi/btn_title_back_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangzexin/Player_Demo/HEAD/list/src/main/res/mipmap-hdpi/btn_title_back_pressed.png -------------------------------------------------------------------------------- /list/src/main/res/drawable/btn_title_back_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /list/src/main/res/drawable/btn_shape.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /list/src/main/res/values-v21/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | -------------------------------------------------------------------------------- /list/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /list/src/test/java/voole/example/com/myapplication/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package voole.example.com.myapplication; 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 | } -------------------------------------------------------------------------------- /list/src/main/res/menu/menu_main.xml: -------------------------------------------------------------------------------- 1 | 5 | 10 | 11 | -------------------------------------------------------------------------------- /list/src/androidTest/java/voole/example/com/myapplication/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package voole.example.com.myapplication; 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 | } -------------------------------------------------------------------------------- /list/src/main/res/layout/table_media_info.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /list/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | #009688 7 | #00796b 8 | #eeff41 9 | #F4FF81 10 | 11 | 12 | 13 | #77000000 14 | 15 | -------------------------------------------------------------------------------- /list/src/main/res/color/selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /list/src/main/res/layout/table_media_info_section.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 14 | 15 | -------------------------------------------------------------------------------- /list/src/main/res/drawable/text_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /list/src/main/res/drawable/selector_text.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /list/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 /Users/zhangzexin/Library/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 | -------------------------------------------------------------------------------- /list/src/main/res/layout/table_media_info_row1.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 13 | 20 | 21 | -------------------------------------------------------------------------------- /list/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /list/src/main/res/layout/table_media_info_row2.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 15 | 16 | 23 | 24 | -------------------------------------------------------------------------------- /list/src/main/java/voole/example/com/myapplication/data/liveData.java: -------------------------------------------------------------------------------- 1 | package voole.example.com.myapplication.data; 2 | 3 | /** 4 | * Created by zhangzexin on 16/2/18. 5 | */ 6 | public class liveData { 7 | String id;//房间ID 8 | String name;//房间名 9 | String flv;//flv 10 | String imgerURl;//图片地址 11 | 12 | public String getId() { 13 | return id; 14 | } 15 | 16 | public String getName() { 17 | return name; 18 | } 19 | 20 | public String getFlv() { 21 | return flv; 22 | } 23 | 24 | public String getImgerURl() { 25 | return imgerURl; 26 | } 27 | 28 | public void setId(String id) { 29 | this.id = id; 30 | } 31 | 32 | public void setName(String name) { 33 | this.name = name; 34 | } 35 | 36 | public void setFlv(String flv) { 37 | this.flv = flv; 38 | } 39 | 40 | public void setImgerURl(String imgerURl) { 41 | this.imgerURl = imgerURl; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /list/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16dp 5 | 6 | 16dp 7 | 16dp 8 | 9 | 16dp 10 | 8dp 11 | 4dp 12 | 13 | 4dp 14 | 15 | 72dp 16 | 72dp 17 | 240dp 18 | 120dp 19 | 20 | 180dp 21 | 48dp 22 | 23 | 72dp 24 | 16dp 25 | 26 | 32dp 27 | 4dp 28 | 29 | -------------------------------------------------------------------------------- /list/src/main/java/voole/example/com/myapplication/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 voole.example.com.myapplication.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 | -------------------------------------------------------------------------------- /list/src/main/res/layout/content_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 19 | 20 | 30 | 31 | -------------------------------------------------------------------------------- /list/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion '23.0.2' 6 | 7 | defaultConfig { 8 | applicationId "voole.example.com.myapplication" 9 | minSdkVersion 9 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(dir: 'libs', include: ['*.jar']) 24 | testCompile 'junit:junit:4.12' 25 | compile 'com.android.support:appcompat-v7:23.1.1' 26 | compile 'com.android.support:design:23.1.1' 27 | compile 'com.facebook.fresco:fresco:0.9.0' 28 | //required, enough for most devices. 29 | compile 'tv.danmaku.ijk.media:ijkplayer-java:0.4.5.1' 30 | compile 'tv.danmaku.ijk.media:ijkplayer-armv7a:0.4.5.1' 31 | // Other ABIs: optional 32 | compile 'tv.danmaku.ijk.media:ijkplayer-armv5:0.4.5.1' 33 | compile 'tv.danmaku.ijk.media:ijkplayer-x86:0.4.5.1' 34 | compile 'tv.danmaku.ijk.media:ijkplayer-exo:0.4.4.1' 35 | compile 'com.github.ksoichiro:android-observablescrollview:1.6.0' 36 | compile 'com.melnykov:floatingactionbutton:1.3.0' 37 | compile 'com.nineoldandroids:library:2.4.0' 38 | } 39 | allprojects { 40 | gradle.projectsEvaluated { 41 | tasks.withType(JavaCompile) { 42 | options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /list/src/main/res/layout/playactivity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 36 | 37 | -------------------------------------------------------------------------------- /list/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 13 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /list/src/main/java/voole/example/com/myapplication/services/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 voole.example.com.myapplication.services; 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 | -------------------------------------------------------------------------------- /list/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 15 | 16 | 23 | 24 | 29 | 30 | 36 | 41 | 42 | 50 | 53 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /list/src/main/java/voole/example/com/myapplication/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 voole.example.com.myapplication.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 | static final int AR_ASPECT_FIT_PARENT = 0; // without clip 30 | static final int AR_ASPECT_FILL_PARENT = 1; // may clip 31 | static final int AR_ASPECT_WRAP_CONTENT = 2; 32 | static final int AR_MATCH_PARENT = 3; 33 | static final int AR_16_9_FIT_PARENT = 4; 34 | static final 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 | public 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 | -------------------------------------------------------------------------------- /list/src/main/java/voole/example/com/myapplication/adapter/newAdapter.java: -------------------------------------------------------------------------------- 1 | package voole.example.com.myapplication.adapter; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.net.Uri; 6 | import android.util.Log; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | import android.widget.BaseAdapter; 11 | import android.widget.RelativeLayout; 12 | import android.widget.TextView; 13 | 14 | import com.facebook.drawee.view.SimpleDraweeView; 15 | 16 | import java.util.ArrayList; 17 | 18 | import voole.example.com.myapplication.R; 19 | import voole.example.com.myapplication.activity.PlayerActivity; 20 | import voole.example.com.myapplication.data.liveData; 21 | 22 | /** 23 | * Created by zhangzexin on 16/1/29. 24 | */ 25 | public class newAdapter extends BaseAdapter { 26 | String url = "http://pl3.live.panda.tv/live_panda/"; 27 | Context context; 28 | ArrayList data; 29 | public newAdapter(Context context,ArrayList data) { 30 | super(); 31 | this.data = data; 32 | this.context = context; 33 | } 34 | 35 | @Override 36 | public int getCount() { 37 | return data.size(); 38 | } 39 | 40 | @Override 41 | public liveData getItem(int position) { 42 | return data.get(position); 43 | } 44 | 45 | @Override 46 | public long getItemId(int position) { 47 | return position; 48 | } 49 | 50 | @Override 51 | public View getView(final int position, View convertView, ViewGroup parent) { 52 | final liveData item = getItem(position); 53 | ViewHolder holder; 54 | if(convertView == null) { 55 | holder = new ViewHolder(); 56 | holder.layout = (RelativeLayout) LayoutInflater.from(context).inflate(R.layout.content_main,null); 57 | holder.imagerview = (SimpleDraweeView) holder.layout.findViewById(R.id.my_image_view); 58 | holder.textView = (TextView) holder.layout.findViewById(R.id.text); 59 | convertView = holder.layout; 60 | convertView.setTag(holder); 61 | }else{ 62 | holder = (ViewHolder) convertView.getTag(); 63 | } 64 | holder.textView.setText(item.getName()); 65 | Uri uri = Uri.parse(item.getImgerURl()); 66 | holder.imagerview.setImageURI(uri); 67 | convertView.setOnClickListener(new View.OnClickListener() { 68 | @Override 69 | public void onClick(View view) { 70 | // Intent intent = new Intent(context,PlayerActivity.class); 71 | String flv = item.getFlv(); 72 | // intent.putExtra("url",url+flv+".flv?sign=sign&time=ts"); 73 | PlayerActivity.intentTo(context,url+flv+".flv?sign=sign&time=ts"); 74 | // context.startActivity(intent); 75 | Log.d("Adapter","点几了第"+position); 76 | } 77 | }); 78 | return holder.layout; 79 | } 80 | static class ViewHolder{ 81 | public SimpleDraweeView imagerview; 82 | public RelativeLayout layout; 83 | public TextView textView; 84 | } 85 | 86 | 87 | 88 | } 89 | -------------------------------------------------------------------------------- /list/src/main/java/voole/example/com/myapplication/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 voole.example.com.myapplication.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 | -------------------------------------------------------------------------------- /list/src/main/java/voole/example/com/myapplication/network/HttpHelp.java: -------------------------------------------------------------------------------- 1 | package voole.example.com.myapplication.network; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.ByteArrayOutputStream; 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | import java.io.InputStreamReader; 8 | import java.io.OutputStream; 9 | import java.net.HttpURLConnection; 10 | import java.net.MalformedURLException; 11 | import java.net.URL; 12 | import java.net.URLConnection; 13 | import java.net.URLEncoder; 14 | import java.util.Map; 15 | 16 | 17 | public class HttpHelp { 18 | 19 | public String Get(String url) throws IOException { 20 | URL mUrl = new URL(url); 21 | HttpURLConnection connection = (HttpURLConnection) mUrl 22 | .openConnection(); 23 | connection.setConnectTimeout(5000); 24 | connection.setReadTimeout(5000); 25 | connection.setRequestMethod("GET"); 26 | connection.connect(); 27 | int responseCode = connection.getResponseCode(); 28 | if (responseCode<=299&&200 <=responseCode) { 29 | InputStream inputStream = connection.getInputStream(); 30 | BufferedReader br = new BufferedReader(new InputStreamReader( 31 | inputStream)); 32 | String response = ""; 33 | String readLine = null; 34 | while ((readLine = br.readLine()) != null) { 35 | // response = br.readLine(); 36 | response = response + readLine; 37 | } 38 | inputStream.close(); 39 | br.close(); 40 | connection.disconnect(); 41 | return response; 42 | } else { 43 | throw new IOException("Unexpected code " 44 | + connection.getResponseCode()); 45 | } 46 | } 47 | public String Post(String url,Map data,String ecode) throws IOException { 48 | byte[] mData = getRequestData(data,ecode).toString().getBytes(); 49 | URL mUrl = new URL(url); 50 | HttpURLConnection connection = (HttpURLConnection)mUrl.openConnection(); 51 | connection.setConnectTimeout(3000); 52 | connection.setReadTimeout(5000); 53 | connection.setDoInput(true); 54 | connection.setDoOutput(true); 55 | connection.setRequestMethod("POST"); 56 | connection.setUseCaches(true); 57 | // connection.setRequestProperty("Content-Type","application/text/html;charset=utf-8"); 58 | // connection.setRequestProperty("Content-Length",String.valueOf(mData.length)); 59 | OutputStream outputStream = connection.getOutputStream(); 60 | outputStream.write(mData); 61 | int responseCode = connection.getResponseCode(); 62 | if(responseCode == connection.HTTP_OK){ 63 | InputStream inputStream = connection.getInputStream(); 64 | return dealResponseResult(inputStream); 65 | } 66 | 67 | return "-1"; 68 | } 69 | public static StringBuffer getRequestData(Map data, String encode) { 70 | StringBuffer stringBuffer = new StringBuffer(); //存储封装好的请求体信息 71 | try { 72 | for(Map.Entry entry : data.entrySet()) { 73 | stringBuffer.append(entry.getKey()) 74 | .append("=") 75 | .append(URLEncoder.encode(entry.getValue(), encode)) 76 | .append("&"); 77 | } 78 | stringBuffer.deleteCharAt(stringBuffer.length() - 1); //删除最后的一个"&" 79 | } catch (Exception e) { 80 | e.printStackTrace(); 81 | } 82 | return stringBuffer; 83 | } 84 | public static String dealResponseResult(InputStream inputStream) { 85 | String resultData = null; //存储处理结果 86 | ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); 87 | byte[] data = new byte[1024]; 88 | int len = 0; 89 | try { 90 | while((len = inputStream.read(data)) != -1) { 91 | byteArrayOutputStream.write(data, 0, len); 92 | } 93 | } catch (IOException e) { 94 | e.printStackTrace(); 95 | } 96 | resultData = new String(byteArrayOutputStream.toByteArray()); 97 | return resultData; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /list/src/main/java/voole/example/com/myapplication/activity/PlayerActivity.java: -------------------------------------------------------------------------------- 1 | package voole.example.com.myapplication.activity; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.os.Bundle; 7 | import android.support.v7.app.ActionBar; 8 | import android.support.v7.app.AppCompatActivity; 9 | import android.support.v7.widget.Toolbar; 10 | import android.util.Log; 11 | import android.view.View; 12 | import android.widget.ImageButton; 13 | 14 | import tv.danmaku.ijk.media.player.IjkMediaPlayer; 15 | import voole.example.com.myapplication.R; 16 | import voole.example.com.myapplication.media.AndroidMediaController; 17 | import voole.example.com.myapplication.media.IjkVideoView; 18 | 19 | /** 20 | * Created by Administrator on 2016/2/22 0022. 21 | */ 22 | public class PlayerActivity extends AppCompatActivity { 23 | 24 | private IjkVideoView mVideoView; 25 | private boolean mBackPressed; 26 | private AndroidMediaController mMediaController; 27 | 28 | public static Intent newIntent(Context context,String url){ 29 | Intent intent = new Intent(context, PlayerActivity.class); 30 | intent.putExtra("url",url); 31 | return intent; 32 | } 33 | 34 | public static void intentTo(Context context, String Url){ 35 | context.startActivity(newIntent(context,Url)); 36 | } 37 | 38 | @Override 39 | protected void onCreate(Bundle savedInstanceState) { 40 | String TAG = "PlayerActivty"; 41 | 42 | 43 | super.onCreate(savedInstanceState); 44 | setContentView(R.layout.playactivity_main); 45 | Intent intent = getIntent(); 46 | String url = intent.getStringExtra("url"); 47 | 48 | //initButton 49 | ImageButton button = (ImageButton) findViewById(R.id.button); 50 | button.setOnClickListener(new View.OnClickListener() { 51 | @Override 52 | public void onClick(View v) { 53 | onBackPressed(); 54 | } 55 | }); 56 | 57 | //// android.support.v7.widget.Toolbar toolbar = (android.support.v7.widget.Toolbar) findViewById(R.id.toolbar); 58 | // Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 59 | // setSupportActionBar(toolbar); 60 | // ActionBar actionBar = getSupportActionBar(); 61 | // 62 | // AndroidMediaController androidMediaController = new AndroidMediaController(this, false); 63 | // androidMediaController.setSupportActionBar(actionBar); 64 | // ActionBar actionBar = getSupportActionBar(); 65 | mMediaController = new AndroidMediaController(this, false); 66 | mMediaController.setSupportBackPressed(button); 67 | // mMediaController.setSupportActionBar(actionBar); 68 | 69 | 70 | // init player 71 | IjkMediaPlayer.loadLibrariesOnce(null); 72 | IjkMediaPlayer.native_profileBegin("libijkplayer.so"); 73 | 74 | mVideoView = (IjkVideoView) findViewById(R.id.video_view); 75 | mVideoView.setMediaController(mMediaController); 76 | // mVideoView.setHudView(mHudView); 77 | if (url != null) 78 | mVideoView.setVideoPath(url); 79 | else { 80 | Log.e(TAG, "Null Data Source\n"); 81 | finish(); 82 | return; 83 | } 84 | mVideoView.start(); 85 | 86 | } 87 | 88 | @Override 89 | public void onBackPressed() { 90 | mBackPressed = true; 91 | super.onBackPressed(); 92 | } 93 | 94 | @Override 95 | protected void onStop() { 96 | super.onStop(); 97 | if (mBackPressed) { 98 | mVideoView.stopPlayback(); 99 | mVideoView.release(true); 100 | mVideoView.stopBackgroundPlay(); 101 | } 102 | IjkMediaPlayer.native_profileEnd(); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /list/src/main/java/voole/example/com/myapplication/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 voole.example.com.myapplication.media; 18 | 19 | import android.content.Context; 20 | import android.content.SharedPreferences; 21 | import android.preference.PreferenceManager; 22 | 23 | import voole.example.com.myapplication.R; 24 | 25 | public class Settings { 26 | private Context mAppContext; 27 | private SharedPreferences mSharedPreferences; 28 | 29 | public static final int PV_PLAYER__Auto = 0; 30 | public static final int PV_PLAYER__AndroidMediaPlayer = 1; 31 | public static final int PV_PLAYER__IjkMediaPlayer = 2; 32 | public static final int PV_PLAYER__IjkExoMediaPlayer = 3; 33 | 34 | public Settings(Context context) { 35 | mAppContext = context.getApplicationContext(); 36 | mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(mAppContext); 37 | } 38 | 39 | public boolean getEnableBackgroundPlay() { 40 | String key = mAppContext.getString(R.string.pref_key_enable_background_play); 41 | return mSharedPreferences.getBoolean(key, false); 42 | } 43 | 44 | public int getPlayer() { 45 | String key = mAppContext.getString(R.string.pref_key_player); 46 | String value = mSharedPreferences.getString(key, ""); 47 | try { 48 | return Integer.valueOf(value).intValue(); 49 | } catch (NumberFormatException e) { 50 | return 0; 51 | } 52 | } 53 | 54 | public boolean getUsingMediaCodec() { 55 | String key = mAppContext.getString(R.string.pref_key_using_media_codec); 56 | return mSharedPreferences.getBoolean(key, false); 57 | } 58 | 59 | public boolean getUsingMediaCodecAutoRotate() { 60 | String key = mAppContext.getString(R.string.pref_key_using_media_codec_auto_rotate); 61 | return mSharedPreferences.getBoolean(key, false); 62 | } 63 | 64 | public boolean getUsingOpenSLES() { 65 | String key = mAppContext.getString(R.string.pref_key_using_opensl_es); 66 | return mSharedPreferences.getBoolean(key, false); 67 | } 68 | 69 | public String getPixelFormat() { 70 | String key = mAppContext.getString(R.string.pref_key_pixel_format); 71 | return mSharedPreferences.getString(key, ""); 72 | } 73 | 74 | public boolean getEnableNoView() { 75 | String key = mAppContext.getString(R.string.pref_key_enable_no_view); 76 | return mSharedPreferences.getBoolean(key, false); 77 | } 78 | 79 | public boolean getEnableSurfaceView() { 80 | String key = mAppContext.getString(R.string.pref_key_enable_surface_view); 81 | return mSharedPreferences.getBoolean(key, false); 82 | } 83 | 84 | public boolean getEnableTextureView() { 85 | String key = mAppContext.getString(R.string.pref_key_enable_texture_view); 86 | return mSharedPreferences.getBoolean(key, false); 87 | } 88 | 89 | public boolean getEnableDetachedSurfaceTextureView() { 90 | String key = mAppContext.getString(R.string.pref_key_enable_detached_surface_texture); 91 | return mSharedPreferences.getBoolean(key, false); 92 | } 93 | 94 | public String getLastDirectory() { 95 | String key = mAppContext.getString(R.string.pref_key_last_directory); 96 | return mSharedPreferences.getString(key, "/"); 97 | } 98 | 99 | public void setLastDirectory(String path) { 100 | String key = mAppContext.getString(R.string.pref_key_last_directory); 101 | mSharedPreferences.edit().putString(key, path).commit(); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /list/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | My Application 3 | Settings 4 | 5 | 6 | Sliding up effect with GridView 7 | N/A 8 | Close 9 | Exit 10 | Sample 11 | Recent 12 | Settings 13 | Tracks 14 | Player 15 | Render 16 | Scale 17 | Info 18 | vdec 19 | fps 20 | v-cache 21 | a-cache 22 | 23 | Media Information 24 | Player 25 | Media 26 | Profile level 27 | Pixel format 28 | Resolution 29 | Length 30 | Stream #%d 31 | Type 32 | Language 33 | Codec 34 | Frame rate 35 | Bit rate 36 | Sample rate 37 | Channels 38 | * 39 | * 40 | 41 | Video 42 | Audio 43 | Subtitle 44 | Timed text 45 | Meta data 46 | Unknown 47 | 48 | Invalid progressive playback 49 | Unknown 50 | OK 51 | 52 | Aspect / Fit parent 53 | Aspect / Fill parent 54 | Aspect / Wrap content 55 | Free / Fill parent 56 | 16:9 / Fit parent 57 | 4:3 / Fit parent 58 | 59 | Render: None 60 | Render: SurfaceView 61 | Render: TextureView 62 | 63 | Player: None 64 | Player: AndroidMediaPlayer 65 | Player: IjkMediaPlayer 66 | Player: IjkExoMediaPlayer 67 | 68 | 69 | Previous track 70 | 71 | Next track 72 | 73 | Pause 74 | 75 | Play 76 | 77 | Stop 78 | 79 | Rewind 80 | 81 | -------------------------------------------------------------------------------- /list/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 | -------------------------------------------------------------------------------- /list/src/main/java/voole/example/com/myapplication/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 voole.example.com.myapplication.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 voole.example.com.myapplication.R; 28 | 29 | public class TableLayoutBinder { 30 | private Context mContext; 31 | public ViewGroup mTableView; 32 | public TableLayout mTableLayout; 33 | 34 | public TableLayoutBinder(Context context) { 35 | this(context, R.layout.table_media_info); 36 | } 37 | 38 | public TableLayoutBinder(Context context, int layoutResourceId) { 39 | mContext = context; 40 | mTableView = (ViewGroup) LayoutInflater.from(mContext).inflate(layoutResourceId, null); 41 | mTableLayout = (TableLayout) mTableView.findViewById(R.id.table); 42 | } 43 | 44 | public TableLayoutBinder(Context context, TableLayout tableLayout) { 45 | mContext = context; 46 | mTableView = tableLayout; 47 | mTableLayout = tableLayout; 48 | } 49 | 50 | public View appendRow1(String name, String value) { 51 | return appendRow(R.layout.table_media_info_row1, name, value); 52 | } 53 | 54 | public View appendRow1(int nameId, String value) { 55 | return appendRow1(mContext.getString(nameId), value); 56 | } 57 | 58 | public View appendRow2(String name, String value) { 59 | return appendRow(R.layout.table_media_info_row2, name, value); 60 | } 61 | 62 | public View appendRow2(int nameId, String value) { 63 | return appendRow2(mContext.getString(nameId), value); 64 | } 65 | 66 | public View appendSection(String name) { 67 | return appendRow(R.layout.table_media_info_section, name, null); 68 | } 69 | 70 | public View appendSection(int nameId) { 71 | return appendSection(mContext.getString(nameId)); 72 | } 73 | 74 | public View appendRow(int layoutId, String name, String value) { 75 | ViewGroup rowView = (ViewGroup) LayoutInflater.from(mContext).inflate(layoutId, mTableLayout, false); 76 | setNameValueText(rowView, name, value); 77 | 78 | mTableLayout.addView(rowView); 79 | return rowView; 80 | } 81 | 82 | public ViewHolder obtainViewHolder(View rowView) { 83 | ViewHolder viewHolder = (ViewHolder) rowView.getTag(); 84 | if (viewHolder == null) { 85 | viewHolder = new ViewHolder(); 86 | viewHolder.mNameTextView = (TextView) rowView.findViewById(R.id.name); 87 | viewHolder.mValueTextView = (TextView) rowView.findViewById(R.id.value); 88 | rowView.setTag(viewHolder); 89 | } 90 | return viewHolder; 91 | } 92 | 93 | public void setNameValueText(View rowView, String name, String value) { 94 | ViewHolder viewHolder = obtainViewHolder(rowView); 95 | viewHolder.setName(name); 96 | viewHolder.setValue(value); 97 | } 98 | 99 | public void setValueText(View rowView, String value) { 100 | ViewHolder viewHolder = obtainViewHolder(rowView); 101 | viewHolder.setValue(value); 102 | } 103 | 104 | public ViewGroup buildLayout() { 105 | return mTableView; 106 | } 107 | 108 | public AlertDialog.Builder buildAlertDialogBuilder() { 109 | AlertDialog.Builder dlgBuilder = new AlertDialog.Builder(mContext); 110 | dlgBuilder.setView(buildLayout()); 111 | return dlgBuilder; 112 | } 113 | 114 | private static class ViewHolder { 115 | public TextView mNameTextView; 116 | public TextView mValueTextView; 117 | 118 | public void setName(String name) { 119 | if (mNameTextView != null) { 120 | mNameTextView.setText(name); 121 | } 122 | } 123 | 124 | public void setValue(String value) { 125 | if (mValueTextView != null) { 126 | mValueTextView.setText(value); 127 | } 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /list/src/main/java/voole/example/com/myapplication/media/AndroidMediaController.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 voole.example.com.myapplication.media; 18 | 19 | import android.content.Context; 20 | import android.support.annotation.NonNull; 21 | import android.support.annotation.Nullable; 22 | import android.support.v7.app.ActionBar; 23 | import android.util.AttributeSet; 24 | import android.view.View; 25 | import android.view.ViewGroup; 26 | import android.widget.ImageButton; 27 | import android.widget.MediaController; 28 | import android.widget.SeekBar; 29 | 30 | import java.lang.reflect.Field; 31 | import java.util.ArrayList; 32 | 33 | public class AndroidMediaController extends MediaController implements IMediaController { 34 | private ActionBar mActionBar; 35 | private ImageButton mButton; 36 | private Context mContext; 37 | private Field mRoot; 38 | 39 | public AndroidMediaController(Context context, AttributeSet attrs) { 40 | super(context, attrs); 41 | this.mContext =context; 42 | initView(context); 43 | } 44 | 45 | public AndroidMediaController(Context context, boolean useFastForward) { 46 | super(context, useFastForward); 47 | this.mContext =context; 48 | initView(context); 49 | } 50 | 51 | public AndroidMediaController(Context context) { 52 | super(context); 53 | this.mContext =context; 54 | initView(context); 55 | } 56 | 57 | private void initView(Context context) { 58 | } 59 | 60 | @Override 61 | public void setAnchorView(View view) { 62 | super.setAnchorView(view); 63 | try { 64 | mRoot = MediaController.class.getDeclaredField("mRoot"); 65 | mRoot.setAccessible(true); 66 | ViewGroup mRootVg = (ViewGroup) mRoot.get(this); 67 | ViewGroup vg = findSeekBarParent(mRootVg); 68 | vg.setVisibility(INVISIBLE); 69 | } catch (NoSuchFieldException e) { 70 | e.printStackTrace(); 71 | } catch (IllegalAccessException e) { 72 | e.printStackTrace(); 73 | } 74 | 75 | } 76 | private ViewGroup findSeekBarParent(ViewGroup vg) { 77 | ViewGroup viewGroup = null; 78 | for (int i = 0; i < vg.getChildCount(); i++) { 79 | View view = vg.getChildAt(i); 80 | if (view instanceof SeekBar) { 81 | viewGroup = (ViewGroup) view.getParent(); 82 | break; 83 | } else if (view instanceof ViewGroup) { 84 | viewGroup = findSeekBarParent((ViewGroup) view); 85 | } else { 86 | continue; 87 | } 88 | } 89 | return viewGroup; 90 | } 91 | 92 | 93 | 94 | 95 | public void setSupportActionBar(@Nullable ActionBar actionBar) { 96 | mActionBar = actionBar; 97 | if (isShowing()) { 98 | actionBar.show(); 99 | } else { 100 | actionBar.hide(); 101 | } 102 | } 103 | public void setSupportBackPressed(@Nullable ImageButton imageButton){ 104 | mButton = imageButton; 105 | if(isShowing()){ 106 | imageButton.setVisibility(VISIBLE); 107 | }else{ 108 | imageButton.setVisibility(INVISIBLE); 109 | } 110 | } 111 | 112 | @Override 113 | public void show() { 114 | super.show(); 115 | if (mActionBar != null) 116 | mActionBar.show(); 117 | if (mButton != null) 118 | mButton.setVisibility(VISIBLE); 119 | } 120 | 121 | @Override 122 | public void hide() { 123 | super.hide(); 124 | if (mActionBar != null) 125 | mActionBar.hide(); 126 | if (mButton != null) 127 | mButton.setVisibility(INVISIBLE); 128 | for (View view : mShowOnceArray) 129 | view.setVisibility(View.GONE); 130 | mShowOnceArray.clear();; 131 | } 132 | 133 | //---------- 134 | // Extends 135 | //---------- 136 | private ArrayList mShowOnceArray = new ArrayList(); 137 | 138 | public void showOnce(@NonNull View view) { 139 | mShowOnceArray.add(view); 140 | view.setVisibility(View.VISIBLE); 141 | show(); 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /list/src/main/java/voole/example/com/myapplication/media/InfoHudViewHolder.java: -------------------------------------------------------------------------------- 1 | package voole.example.com.myapplication.media; 2 | 3 | import android.content.Context; 4 | import android.os.Handler; 5 | import android.os.Message; 6 | import android.view.View; 7 | import android.widget.TableLayout; 8 | 9 | import java.util.HashMap; 10 | import java.util.Locale; 11 | 12 | import tv.danmaku.ijk.media.player.IMediaPlayer; 13 | import tv.danmaku.ijk.media.player.IjkMediaPlayer; 14 | import tv.danmaku.ijk.media.player.MediaPlayerProxy; 15 | import voole.example.com.myapplication.R; 16 | 17 | public class InfoHudViewHolder { 18 | private TableLayoutBinder mTableLayoutBinder; 19 | private HashMap mRowMap = new HashMap(); 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 | -------------------------------------------------------------------------------- /list/src/main/java/voole/example/com/myapplication/activity/MainActivity.java: -------------------------------------------------------------------------------- 1 | package voole.example.com.myapplication.activity; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.os.Handler; 7 | import android.os.Message; 8 | import android.support.v7.app.AppCompatActivity; 9 | import android.util.Log; 10 | import android.view.View; 11 | import android.widget.AbsListView; 12 | import android.widget.AdapterView; 13 | import android.widget.GridView; 14 | import android.widget.Toast; 15 | 16 | import com.facebook.drawee.backends.pipeline.Fresco; 17 | 18 | import org.json.JSONArray; 19 | import org.json.JSONException; 20 | import org.json.JSONObject; 21 | 22 | import java.io.IOException; 23 | import java.util.ArrayList; 24 | import java.util.HashMap; 25 | import java.util.LinkedHashMap; 26 | import java.util.List; 27 | import java.util.Map; 28 | 29 | import voole.example.com.myapplication.network.HttpHelp; 30 | import voole.example.com.myapplication.R; 31 | import voole.example.com.myapplication.data.liveData; 32 | import voole.example.com.myapplication.adapter.newAdapter; 33 | 34 | 35 | public class MainActivity extends AppCompatActivity implements AbsListView.OnScrollListener { 36 | String url = "http://pl3.live.panda.tv/live_panda/"; 37 | int pageno = 1; 38 | ArrayList arrayList = new ArrayList(); 39 | Handler handler = new Handler(){ 40 | @Override 41 | public void handleMessage(Message msg) { 42 | switch (msg.what){ 43 | case 0: 44 | Toast.makeText(MainActivity.this,(String)msg.obj,Toast.LENGTH_LONG).show(); 45 | break; 46 | case 1: 47 | ArrayList arrayList = (ArrayList) msg.obj; 48 | adapter = new newAdapter(MainActivity.this, arrayList); 49 | view.setAdapter(adapter); 50 | view.setOnItemClickListener(new AdapterView.OnItemClickListener() { 51 | @Override 52 | public void onItemClick(AdapterView parent, View view, int position, long id) { 53 | liveData item = adapter.getItem(position); 54 | Intent intent = new Intent(MainActivity.this,PlayerActivity.class); 55 | String flv = item.getFlv(); 56 | intent.putExtra("url",url+flv+".flv?sign=sign&time=ts"); 57 | MainActivity.this.startActivity(intent); 58 | } 59 | }); 60 | break; 61 | case 2: 62 | adapter.notifyDataSetChanged(); 63 | break; 64 | } 65 | super.handleMessage(msg); 66 | } 67 | }; 68 | 69 | 70 | List> data; 71 | GridView view; 72 | String Http = "Http"; 73 | private newAdapter adapter; 74 | 75 | @Override 76 | protected void onCreate(Bundle savedInstanceState) { 77 | super.onCreate(savedInstanceState); 78 | Fresco.initialize(this); 79 | setContentView(R.layout.activity_main); 80 | // data = getData(); 81 | view = (GridView) findViewById(R.id.gridview); 82 | view.setOnScrollListener(this); 83 | loadData(); 84 | 85 | // view.setItemsCanFocus(true); 86 | } 87 | 88 | private void loadData() { 89 | new Thread(new Runnable() { 90 | @Override 91 | public void run() { 92 | String time = System.currentTimeMillis()+""; 93 | Log.d(Http,"http====>Time======> "+time); 94 | // String url = "http://capi.douyucdn.cn/api/v1/live?aid=ios&client_sys=ios&limit=20&offset=0&time="+time+"&auth=20125743bcc2114d1abcbca5afac55c7"; 95 | String url = "http://api.m.panda.tv/ajax_get_live_list_by_cate"; 96 | Map map = new LinkedHashMap(); 97 | map.put("plat","android"); 98 | map.put("version","1.0.5.109"); 99 | map.put("cate","lol"); 100 | map.put("order","person_num"); 101 | map.put("pageno",String.valueOf(pageno)); 102 | map.put("pagenum","10"); 103 | map.put("status","2"); 104 | HttpHelp help = new HttpHelp(); 105 | Log.d(Http,"Url ===>"+url); 106 | 107 | try { 108 | String json = help.Post(url,map,"UTF-8"); 109 | Log.d(Http,""+json); 110 | try { 111 | JSONObject jsonObject = new JSONObject(json); 112 | JSONObject data = jsonObject.getJSONObject("data"); 113 | JSONArray items = data.getJSONArray("items"); 114 | int length = items.length(); 115 | Log.d("JsonObject","length====>JSONObject======> "+length); 116 | Log.d("JsonObject","items====>JSONObject======> "+items.toString()); 117 | 118 | for (int i = 0;iJSONObject======> " + mData.getFlv()); 127 | Log.d("JsonObject", "imgUrl====>JSONObject======> " + mData.getImgerURl()); 128 | Log.d("JsonObject", "name====>JSONObject======> " + mData.getName()); 129 | Log.d("JsonObject", "Id====>JSONObject======> " + mData.getId()); 130 | arrayList.add(mData); 131 | } 132 | Message message = new Message(); 133 | if(pageno == 1) { 134 | message.what = 1; 135 | message.obj = arrayList; 136 | }else{ 137 | message.what = 2; 138 | } 139 | pageno++; 140 | handler.sendMessage(message); 141 | } catch (JSONException e) { 142 | Message message = new Message(); 143 | message.what = 0; 144 | message.obj = "解析出错"; 145 | handler.sendMessage(message); 146 | // Toast.makeText(MainActivity.this,"解析出错",Toast.LENGTH_LONG).show(); 147 | e.printStackTrace(); 148 | } 149 | } catch (IOException e) { 150 | Message message = new Message(); 151 | message.what = 0; 152 | message.obj = "服务器繁忙,请稍后再试"; 153 | handler.sendMessage(message); 154 | 155 | e.printStackTrace(); 156 | } 157 | 158 | } 159 | }).start(); 160 | } 161 | 162 | 163 | @Override 164 | public void onScrollStateChanged(AbsListView view, int scrollState) { 165 | if(scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE){ 166 | if(view.getLastVisiblePosition() == view.getCount() -1){ 167 | loadData(); 168 | } 169 | } 170 | } 171 | 172 | @Override 173 | public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { 174 | 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /list/src/main/java/voole/example/com/myapplication/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 voole.example.com.myapplication.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 | -------------------------------------------------------------------------------- /list/src/main/java/voole/example/com/myapplication/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 voole.example.com.myapplication.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 voole.example.com.myapplication.R; 26 | 27 | public final class MeasureHelper { 28 | private WeakReference mWeakView; 29 | 30 | private int mVideoWidth; 31 | private int mVideoHeight; 32 | private int mVideoSarNum; 33 | private int mVideoSarDen; 34 | 35 | private int mVideoRotationDegree; 36 | 37 | private int mMeasuredWidth; 38 | private int mMeasuredHeight; 39 | 40 | private int mCurrentAspectRatio = IRenderView.AR_ASPECT_FIT_PARENT; 41 | 42 | public MeasureHelper(View view) { 43 | mWeakView = new WeakReference(view); 44 | } 45 | 46 | public View getView() { 47 | if (mWeakView == null) 48 | return null; 49 | return mWeakView.get(); 50 | } 51 | 52 | public void setVideoSize(int videoWidth, int videoHeight) { 53 | mVideoWidth = videoWidth; 54 | mVideoHeight = videoHeight; 55 | } 56 | 57 | public void setVideoSampleAspectRatio(int videoSarNum, int videoSarDen) { 58 | mVideoSarNum = videoSarNum; 59 | mVideoSarDen = videoSarDen; 60 | } 61 | 62 | public void setVideoRotation(int videoRotationDegree) { 63 | mVideoRotationDegree = videoRotationDegree; 64 | } 65 | 66 | /** 67 | * Must be called by View.onMeasure(int, int) 68 | * 69 | * @param widthMeasureSpec 70 | * @param heightMeasureSpec 71 | */ 72 | public void doMeasure(int widthMeasureSpec, int heightMeasureSpec) { 73 | //Log.i("@@@@", "onMeasure(" + MeasureSpec.toString(widthMeasureSpec) + ", " 74 | // + MeasureSpec.toString(heightMeasureSpec) + ")"); 75 | if (mVideoRotationDegree == 90 || mVideoRotationDegree == 270) { 76 | int tempSpec = widthMeasureSpec; 77 | widthMeasureSpec = heightMeasureSpec; 78 | heightMeasureSpec = tempSpec; 79 | } 80 | 81 | int width = View.getDefaultSize(mVideoWidth, widthMeasureSpec); 82 | int height = View.getDefaultSize(mVideoHeight, heightMeasureSpec); 83 | if (mCurrentAspectRatio == IRenderView.AR_MATCH_PARENT) { 84 | width = widthMeasureSpec; 85 | height = heightMeasureSpec; 86 | } else if (mVideoWidth > 0 && mVideoHeight > 0) { 87 | int widthSpecMode = View.MeasureSpec.getMode(widthMeasureSpec); 88 | int widthSpecSize = View.MeasureSpec.getSize(widthMeasureSpec); 89 | int heightSpecMode = View.MeasureSpec.getMode(heightMeasureSpec); 90 | int heightSpecSize = View.MeasureSpec.getSize(heightMeasureSpec); 91 | 92 | if (widthSpecMode == View.MeasureSpec.AT_MOST && heightSpecMode == View.MeasureSpec.AT_MOST) { 93 | float specAspectRatio = (float) widthSpecSize / (float) heightSpecSize; 94 | float displayAspectRatio; 95 | switch (mCurrentAspectRatio) { 96 | case IRenderView.AR_16_9_FIT_PARENT: 97 | displayAspectRatio = 16.0f / 9.0f; 98 | if (mVideoRotationDegree == 90 || mVideoRotationDegree == 270) 99 | displayAspectRatio = 1.0f / displayAspectRatio; 100 | break; 101 | case IRenderView.AR_4_3_FIT_PARENT: 102 | displayAspectRatio = 4.0f / 3.0f; 103 | if (mVideoRotationDegree == 90 || mVideoRotationDegree == 270) 104 | displayAspectRatio = 1.0f / displayAspectRatio; 105 | break; 106 | case IRenderView.AR_ASPECT_FIT_PARENT: 107 | case IRenderView.AR_ASPECT_FILL_PARENT: 108 | case IRenderView.AR_ASPECT_WRAP_CONTENT: 109 | default: 110 | displayAspectRatio = (float) mVideoWidth / (float) mVideoHeight; 111 | if (mVideoSarNum > 0 && mVideoSarDen > 0) 112 | displayAspectRatio = displayAspectRatio * mVideoSarNum / mVideoSarDen; 113 | break; 114 | } 115 | boolean shouldBeWider = displayAspectRatio > specAspectRatio; 116 | 117 | switch (mCurrentAspectRatio) { 118 | case IRenderView.AR_ASPECT_FIT_PARENT: 119 | case IRenderView.AR_16_9_FIT_PARENT: 120 | case IRenderView.AR_4_3_FIT_PARENT: 121 | if (shouldBeWider) { 122 | // too wide, fix width 123 | width = widthSpecSize; 124 | height = (int) (width / displayAspectRatio); 125 | } else { 126 | // too high, fix height 127 | height = heightSpecSize; 128 | width = (int) (height * displayAspectRatio); 129 | } 130 | break; 131 | case IRenderView.AR_ASPECT_FILL_PARENT: 132 | if (shouldBeWider) { 133 | // not high enough, fix height 134 | height = heightSpecSize; 135 | width = (int) (height * displayAspectRatio); 136 | } else { 137 | // not wide enough, fix width 138 | width = widthSpecSize; 139 | height = (int) (width / displayAspectRatio); 140 | } 141 | break; 142 | case IRenderView.AR_ASPECT_WRAP_CONTENT: 143 | default: 144 | if (shouldBeWider) { 145 | // too wide, fix width 146 | width = Math.min(mVideoWidth, widthSpecSize); 147 | height = (int) (width / displayAspectRatio); 148 | } else { 149 | // too high, fix height 150 | height = Math.min(mVideoHeight, heightSpecSize); 151 | width = (int) (height * displayAspectRatio); 152 | } 153 | break; 154 | } 155 | } else if (widthSpecMode == View.MeasureSpec.EXACTLY && heightSpecMode == View.MeasureSpec.EXACTLY) { 156 | // the size is fixed 157 | width = widthSpecSize; 158 | height = heightSpecSize; 159 | 160 | // for compatibility, we adjust size based on aspect ratio 161 | if (mVideoWidth * height < width * mVideoHeight) { 162 | //Log.i("@@@", "image too wide, correcting"); 163 | width = height * mVideoWidth / mVideoHeight; 164 | } else if (mVideoWidth * height > width * mVideoHeight) { 165 | //Log.i("@@@", "image too tall, correcting"); 166 | height = width * mVideoHeight / mVideoWidth; 167 | } 168 | } else if (widthSpecMode == View.MeasureSpec.EXACTLY) { 169 | // only the width is fixed, adjust the height to match aspect ratio if possible 170 | width = widthSpecSize; 171 | height = width * mVideoHeight / mVideoWidth; 172 | if (heightSpecMode == View.MeasureSpec.AT_MOST && height > heightSpecSize) { 173 | // couldn't match aspect ratio within the constraints 174 | height = heightSpecSize; 175 | } 176 | } else if (heightSpecMode == View.MeasureSpec.EXACTLY) { 177 | // only the height is fixed, adjust the width to match aspect ratio if possible 178 | height = heightSpecSize; 179 | width = height * mVideoWidth / mVideoHeight; 180 | if (widthSpecMode == View.MeasureSpec.AT_MOST && width > widthSpecSize) { 181 | // couldn't match aspect ratio within the constraints 182 | width = widthSpecSize; 183 | } 184 | } else { 185 | // neither the width nor the height are fixed, try to use actual video size 186 | width = mVideoWidth; 187 | height = mVideoHeight; 188 | if (heightSpecMode == View.MeasureSpec.AT_MOST && height > heightSpecSize) { 189 | // too tall, decrease both width and height 190 | height = heightSpecSize; 191 | width = height * mVideoWidth / mVideoHeight; 192 | } 193 | if (widthSpecMode == View.MeasureSpec.AT_MOST && width > widthSpecSize) { 194 | // too wide, decrease both width and height 195 | width = widthSpecSize; 196 | height = width * mVideoHeight / mVideoWidth; 197 | } 198 | } 199 | } else { 200 | // no size yet, just adopt the given spec sizes 201 | } 202 | 203 | mMeasuredWidth = width; 204 | mMeasuredHeight = height; 205 | } 206 | 207 | public int getMeasuredWidth() { 208 | return mMeasuredWidth; 209 | } 210 | 211 | public int getMeasuredHeight() { 212 | return mMeasuredHeight; 213 | } 214 | 215 | public void setAspectRatio(int aspectRatio) { 216 | mCurrentAspectRatio = aspectRatio; 217 | } 218 | 219 | @NonNull 220 | public static String getAspectRatioText(Context context, int aspectRatio) { 221 | String text; 222 | switch (aspectRatio) { 223 | case IRenderView.AR_ASPECT_FIT_PARENT: 224 | text = context.getString(R.string.VideoView_ar_aspect_fit_parent); 225 | break; 226 | case IRenderView.AR_ASPECT_FILL_PARENT: 227 | text = context.getString(R.string.VideoView_ar_aspect_fill_parent); 228 | break; 229 | case IRenderView.AR_ASPECT_WRAP_CONTENT: 230 | text = context.getString(R.string.VideoView_ar_aspect_wrap_content); 231 | break; 232 | case IRenderView.AR_MATCH_PARENT: 233 | text = context.getString(R.string.VideoView_ar_match_parent); 234 | break; 235 | case IRenderView.AR_16_9_FIT_PARENT: 236 | text = context.getString(R.string.VideoView_ar_16_9_fit_parent); 237 | break; 238 | case IRenderView.AR_4_3_FIT_PARENT: 239 | text = context.getString(R.string.VideoView_ar_4_3_fit_parent); 240 | break; 241 | default: 242 | text = context.getString(R.string.N_A); 243 | break; 244 | } 245 | return text; 246 | } 247 | } 248 | -------------------------------------------------------------------------------- /list/src/main/java/voole/example/com/myapplication/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 voole.example.com.myapplication.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 | return; 311 | } else if (mDidDetachFromWindow) { 312 | if (surfaceTexture != mSurfaceTexture) { 313 | Log.d(TAG, "releaseSurfaceTexture: didDetachFromWindow(): release different SurfaceTexture"); 314 | surfaceTexture.release(); 315 | } else if (!mOwnSurfaceTexture) { 316 | Log.d(TAG, "releaseSurfaceTexture: didDetachFromWindow(): release detached SurfaceTexture"); 317 | surfaceTexture.release(); 318 | } else { 319 | Log.d(TAG, "releaseSurfaceTexture: didDetachFromWindow(): already released by TextureView"); 320 | } 321 | } else if (mWillDetachFromWindow) { 322 | if (surfaceTexture != mSurfaceTexture) { 323 | Log.d(TAG, "releaseSurfaceTexture: willDetachFromWindow(): release different SurfaceTexture"); 324 | surfaceTexture.release(); 325 | } else if (!mOwnSurfaceTexture) { 326 | Log.d(TAG, "releaseSurfaceTexture: willDetachFromWindow(): re-attach SurfaceTexture to TextureView"); 327 | setOwnSurfaceTexture(true); 328 | } else { 329 | Log.d(TAG, "releaseSurfaceTexture: willDetachFromWindow(): will released by TextureView"); 330 | } 331 | } else { 332 | if (surfaceTexture != mSurfaceTexture) { 333 | Log.d(TAG, "releaseSurfaceTexture: alive: release different SurfaceTexture"); 334 | surfaceTexture.release(); 335 | } else if (!mOwnSurfaceTexture) { 336 | Log.d(TAG, "releaseSurfaceTexture: alive: re-attach SurfaceTexture to TextureView"); 337 | setOwnSurfaceTexture(true); 338 | } else { 339 | Log.d(TAG, "releaseSurfaceTexture: alive: will released by TextureView"); 340 | } 341 | } 342 | } 343 | 344 | public void willDetachFromWindow() { 345 | Log.d(TAG, "willDetachFromWindow()"); 346 | mWillDetachFromWindow = true; 347 | } 348 | 349 | public void didDetachFromWindow() { 350 | Log.d(TAG, "didDetachFromWindow()"); 351 | mDidDetachFromWindow = true; 352 | } 353 | } 354 | 355 | //-------------------- 356 | // Accessibility 357 | //-------------------- 358 | 359 | @Override 360 | public void onInitializeAccessibilityEvent(AccessibilityEvent event) { 361 | super.onInitializeAccessibilityEvent(event); 362 | event.setClassName(TextureRenderView.class.getName()); 363 | } 364 | 365 | @Override 366 | public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { 367 | super.onInitializeAccessibilityNodeInfo(info); 368 | info.setClassName(TextureRenderView.class.getName()); 369 | } 370 | } 371 | -------------------------------------------------------------------------------- /list/src/main/java/voole/example/com/myapplication/media/IjkVideoView.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 voole.example.com.myapplication.media; 18 | 19 | import android.annotation.TargetApi; 20 | import android.content.Context; 21 | import android.content.DialogInterface; 22 | import android.content.res.Resources; 23 | import android.media.AudioManager; 24 | import android.media.MediaPlayer; 25 | import android.net.Uri; 26 | import android.os.Build; 27 | import android.support.annotation.NonNull; 28 | import android.support.v7.app.AlertDialog; 29 | import android.text.TextUtils; 30 | import android.util.AttributeSet; 31 | import android.util.Log; 32 | import android.view.Gravity; 33 | import android.view.KeyEvent; 34 | import android.view.MotionEvent; 35 | import android.view.View; 36 | import android.widget.FrameLayout; 37 | import android.widget.MediaController; 38 | import android.widget.TableLayout; 39 | 40 | import java.io.IOException; 41 | import java.util.ArrayList; 42 | import java.util.List; 43 | import java.util.Locale; 44 | import java.util.Map; 45 | 46 | import tv.danmaku.ijk.media.exo.IjkExoMediaPlayer; 47 | import tv.danmaku.ijk.media.player.AndroidMediaPlayer; 48 | import tv.danmaku.ijk.media.player.IMediaPlayer; 49 | import tv.danmaku.ijk.media.player.IjkMediaPlayer; 50 | import tv.danmaku.ijk.media.player.TextureMediaPlayer; 51 | import tv.danmaku.ijk.media.player.misc.IMediaFormat; 52 | import tv.danmaku.ijk.media.player.misc.ITrackInfo; 53 | import tv.danmaku.ijk.media.player.misc.IjkMediaFormat; 54 | import voole.example.com.myapplication.R; 55 | import voole.example.com.myapplication.services.MediaPlayerService; 56 | //import tv.danmaku.ijk.media.sample.services.MediaPlayerService; 57 | 58 | public class IjkVideoView extends FrameLayout implements MediaController.MediaPlayerControl { 59 | private String TAG = "IjkVideoView"; 60 | // settable by the client 61 | private Uri mUri; 62 | private Map mHeaders; 63 | private String mPath; 64 | 65 | // all possible internal states 66 | private static final int STATE_ERROR = -1; 67 | private static final int STATE_IDLE = 0; 68 | private static final int STATE_PREPARING = 1; 69 | private static final int STATE_PREPARED = 2; 70 | private static final int STATE_PLAYING = 3; 71 | private static final int STATE_PAUSED = 4; 72 | private static final int STATE_PLAYBACK_COMPLETED = 5; 73 | 74 | // mCurrentState is a VideoView object's current state. 75 | // mTargetState is the state that a method caller intends to reach. 76 | // For instance, regardless the VideoView object's current state, 77 | // calling pause() intends to bring the object to a target state 78 | // of STATE_PAUSED. 79 | private int mCurrentState = STATE_IDLE; 80 | private int mTargetState = STATE_IDLE; 81 | 82 | // All the stuff we need for playing and showing a video 83 | private IRenderView.ISurfaceHolder mSurfaceHolder = null; 84 | private IMediaPlayer mMediaPlayer = null; 85 | // private int mAudioSession; 86 | private int mVideoWidth; 87 | private int mVideoHeight; 88 | private int mSurfaceWidth; 89 | private int mSurfaceHeight; 90 | private int mVideoRotationDegree; 91 | private IMediaController mMediaController; 92 | private IMediaPlayer.OnCompletionListener mOnCompletionListener; 93 | private IMediaPlayer.OnPreparedListener mOnPreparedListener; 94 | private int mCurrentBufferPercentage; 95 | private IMediaPlayer.OnErrorListener mOnErrorListener; 96 | private IMediaPlayer.OnInfoListener mOnInfoListener; 97 | private int mSeekWhenPrepared; // recording the seek position while preparing 98 | private boolean mCanPause = true; 99 | private boolean mCanSeekBack = true; 100 | private boolean mCanSeekForward = true; 101 | 102 | /** Subtitle rendering widget overlaid on top of the video. */ 103 | // private RenderingWidget mSubtitleWidget; 104 | 105 | /** 106 | * Listener for changes to subtitle data, used to redraw when needed. 107 | */ 108 | // private RenderingWidget.OnChangedListener mSubtitlesChangedListener; 109 | 110 | private Context mAppContext; 111 | private Settings mSettings; 112 | private IRenderView mRenderView; 113 | private int mVideoSarNum; 114 | private int mVideoSarDen; 115 | 116 | private InfoHudViewHolder mHudViewHolder; 117 | private long mTime; 118 | 119 | public IjkVideoView(Context context) { 120 | super(context); 121 | initVideoView(context); 122 | } 123 | 124 | public IjkVideoView(Context context, AttributeSet attrs) { 125 | super(context, attrs); 126 | initVideoView(context); 127 | } 128 | 129 | public IjkVideoView(Context context, AttributeSet attrs, int defStyleAttr) { 130 | super(context, attrs, defStyleAttr); 131 | initVideoView(context); 132 | } 133 | 134 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) 135 | public IjkVideoView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 136 | super(context, attrs, defStyleAttr, defStyleRes); 137 | initVideoView(context); 138 | } 139 | 140 | // REMOVED: onMeasure 141 | // REMOVED: onInitializeAccessibilityEvent 142 | // REMOVED: onInitializeAccessibilityNodeInfo 143 | // REMOVED: resolveAdjustedSize 144 | 145 | private void initVideoView(Context context) { 146 | mAppContext = context.getApplicationContext(); 147 | mSettings = new Settings(mAppContext); 148 | 149 | initBackground(); 150 | initRenders(); 151 | 152 | mVideoWidth = 0; 153 | mVideoHeight = 0; 154 | // REMOVED: getHolder().addCallback(mSHCallback); 155 | // REMOVED: getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); 156 | setFocusable(true); 157 | setFocusableInTouchMode(true); 158 | requestFocus(); 159 | // REMOVED: mPendingSubtitleTracks = new Vector>(); 160 | mCurrentState = STATE_IDLE; 161 | mTargetState = STATE_IDLE; 162 | } 163 | 164 | public void setRenderView(IRenderView renderView) { 165 | if (mRenderView != null) { 166 | if (mMediaPlayer != null) 167 | mMediaPlayer.setDisplay(null); 168 | 169 | View renderUIView = mRenderView.getView(); 170 | mRenderView.removeRenderCallback(mSHCallback); 171 | mRenderView = null; 172 | removeView(renderUIView); 173 | } 174 | 175 | if (renderView == null) 176 | return; 177 | 178 | mRenderView = renderView; 179 | renderView.setAspectRatio(mCurrentAspectRatio); 180 | if (mVideoWidth > 0 && mVideoHeight > 0) 181 | renderView.setVideoSize(mVideoWidth, mVideoHeight); 182 | if (mVideoSarNum > 0 && mVideoSarDen > 0) 183 | renderView.setVideoSampleAspectRatio(mVideoSarNum, mVideoSarDen); 184 | 185 | View renderUIView = mRenderView.getView(); 186 | LayoutParams lp = new LayoutParams( 187 | LayoutParams.WRAP_CONTENT, 188 | LayoutParams.WRAP_CONTENT, 189 | Gravity.CENTER); 190 | renderUIView.setLayoutParams(lp); 191 | addView(renderUIView); 192 | 193 | mRenderView.addRenderCallback(mSHCallback); 194 | mRenderView.setVideoRotation(mVideoRotationDegree); 195 | } 196 | 197 | public void setRender(int render) { 198 | switch (render) { 199 | case RENDER_NONE: 200 | setRenderView(null); 201 | break; 202 | case RENDER_TEXTURE_VIEW: { 203 | TextureRenderView renderView = new TextureRenderView(getContext()); 204 | if (mMediaPlayer != null) { 205 | renderView.getSurfaceHolder().bindToMediaPlayer(mMediaPlayer); 206 | renderView.setVideoSize(mMediaPlayer.getVideoWidth(), mMediaPlayer.getVideoHeight()); 207 | renderView.setVideoSampleAspectRatio(mMediaPlayer.getVideoSarNum(), mMediaPlayer.getVideoSarDen()); 208 | renderView.setAspectRatio(mCurrentAspectRatio); 209 | } 210 | setRenderView(renderView); 211 | break; 212 | } 213 | case RENDER_SURFACE_VIEW: { 214 | SurfaceRenderView renderView = new SurfaceRenderView(getContext()); 215 | setRenderView(renderView); 216 | break; 217 | } 218 | default: 219 | Log.e(TAG, String.format(Locale.getDefault(), "invalid render %d\n", render)); 220 | break; 221 | } 222 | } 223 | 224 | public void setHudView(TableLayout tableLayout) { 225 | mHudViewHolder = new InfoHudViewHolder(getContext(), tableLayout); 226 | } 227 | 228 | /** 229 | * Sets video path. 230 | * 231 | * @param path the path of the video. 232 | */ 233 | public void setVideoPath(String path) { 234 | this.mPath = path; 235 | setVideoURI(Uri.parse(path)); 236 | } 237 | 238 | /** 239 | * Sets video URI. 240 | * 241 | * @param uri the URI of the video. 242 | */ 243 | public void setVideoURI(Uri uri) { 244 | setVideoURI(uri, null); 245 | } 246 | 247 | /** 248 | * Sets video URI using specific headers. 249 | * 250 | * @param uri the URI of the video. 251 | * @param headers the headers for the URI request. 252 | * Note that the cross domain redirection is allowed by default, but that can be 253 | * changed with key/value pairs through the headers parameter with 254 | * "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value 255 | * to disallow or allow cross domain redirection. 256 | */ 257 | private void setVideoURI(Uri uri, Map headers) { 258 | mUri = uri; 259 | mHeaders = headers; 260 | mSeekWhenPrepared = 0; 261 | openVideo(); 262 | requestLayout(); 263 | invalidate(); 264 | } 265 | 266 | // REMOVED: addSubtitleSource 267 | // REMOVED: mPendingSubtitleTracks 268 | 269 | public void stopPlayback() { 270 | if (mMediaPlayer != null) { 271 | mMediaPlayer.stop(); 272 | mMediaPlayer.release(); 273 | mMediaPlayer = null; 274 | if (mHudViewHolder != null) 275 | mHudViewHolder.setMediaPlayer(null); 276 | mCurrentState = STATE_IDLE; 277 | mTargetState = STATE_IDLE; 278 | AudioManager am = (AudioManager) mAppContext.getSystemService(Context.AUDIO_SERVICE); 279 | am.abandonAudioFocus(null); 280 | } 281 | } 282 | 283 | @TargetApi(Build.VERSION_CODES.M) 284 | private void openVideo() { 285 | if (mUri == null || mSurfaceHolder == null) { 286 | // not ready for playback just yet, will try again later 287 | return; 288 | } 289 | // we shouldn't clear the target state, because somebody might have 290 | // called start() previously 291 | release(false); 292 | 293 | AudioManager am = (AudioManager) mAppContext.getSystemService(Context.AUDIO_SERVICE); 294 | am.requestAudioFocus(null, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN); 295 | 296 | try { 297 | mMediaPlayer = createPlayer(mSettings.getPlayer()); 298 | 299 | // TODO: create SubtitleController in MediaPlayer, but we need 300 | // a context for the subtitle renderers 301 | final Context context = getContext(); 302 | // REMOVED: SubtitleController 303 | 304 | // REMOVED: mAudioSession 305 | mMediaPlayer.setOnPreparedListener(mPreparedListener); 306 | mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener); 307 | mMediaPlayer.setOnCompletionListener(mCompletionListener); 308 | mMediaPlayer.setOnErrorListener(mErrorListener); 309 | mMediaPlayer.setOnInfoListener(mInfoListener); 310 | mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener); 311 | mCurrentBufferPercentage = 0; 312 | if (Build.VERSION.SDK_INT > Build.VERSION_CODES.ICE_CREAM_SANDWICH) { 313 | mMediaPlayer.setDataSource(mAppContext, mUri, mHeaders); 314 | } else { 315 | mMediaPlayer.setDataSource(mUri.toString()); 316 | } 317 | bindSurfaceHolder(mMediaPlayer, mSurfaceHolder); 318 | mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); 319 | mMediaPlayer.setScreenOnWhilePlaying(true); 320 | mMediaPlayer.prepareAsync(); 321 | if (mHudViewHolder != null) 322 | mHudViewHolder.setMediaPlayer(mMediaPlayer); 323 | 324 | // REMOVED: mPendingSubtitleTracks 325 | 326 | // we don't set the target state here either, but preserve the 327 | // target state that was there before. 328 | mCurrentState = STATE_PREPARING; 329 | attachMediaController(); 330 | } catch (IOException ex) { 331 | Log.w(TAG, "Unable to open content: " + mUri, ex); 332 | mCurrentState = STATE_ERROR; 333 | mTargetState = STATE_ERROR; 334 | mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); 335 | return; 336 | } catch (IllegalArgumentException ex) { 337 | Log.w(TAG, "Unable to open content: " + mUri, ex); 338 | mCurrentState = STATE_ERROR; 339 | mTargetState = STATE_ERROR; 340 | mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); 341 | return; 342 | } finally { 343 | // REMOVED: mPendingSubtitleTracks.clear(); 344 | } 345 | } 346 | 347 | public void setMediaController(IMediaController controller) { 348 | if (mMediaController != null) { 349 | mMediaController.hide(); 350 | } 351 | mMediaController = controller; 352 | attachMediaController(); 353 | } 354 | 355 | private void attachMediaController() { 356 | if (mMediaPlayer != null && mMediaController != null) { 357 | mMediaController.setMediaPlayer(this); 358 | View anchorView = this.getParent() instanceof View ? 359 | (View) this.getParent() : this; 360 | mMediaController.setAnchorView(anchorView); 361 | mMediaController.setEnabled(isInPlaybackState()); 362 | } 363 | } 364 | 365 | IMediaPlayer.OnVideoSizeChangedListener mSizeChangedListener = 366 | new IMediaPlayer.OnVideoSizeChangedListener() { 367 | public void onVideoSizeChanged(IMediaPlayer mp, int width, int height, int sarNum, int sarDen) { 368 | mVideoWidth = mp.getVideoWidth(); 369 | mVideoHeight = mp.getVideoHeight(); 370 | mVideoSarNum = mp.getVideoSarNum(); 371 | mVideoSarDen = mp.getVideoSarDen(); 372 | if (mVideoWidth != 0 && mVideoHeight != 0) { 373 | if (mRenderView != null) { 374 | mRenderView.setVideoSize(mVideoWidth, mVideoHeight); 375 | mRenderView.setVideoSampleAspectRatio(mVideoSarNum, mVideoSarDen); 376 | } 377 | // REMOVED: getHolder().setFixedSize(mVideoWidth, mVideoHeight); 378 | requestLayout(); 379 | } 380 | } 381 | }; 382 | 383 | IMediaPlayer.OnPreparedListener mPreparedListener = new IMediaPlayer.OnPreparedListener() { 384 | public void onPrepared(IMediaPlayer mp) { 385 | mCurrentState = STATE_PREPARED; 386 | 387 | // Get the capabilities of the player for this stream 388 | // REMOVED: Metadata 389 | 390 | if (mOnPreparedListener != null) { 391 | mOnPreparedListener.onPrepared(mMediaPlayer); 392 | } 393 | if (mMediaController != null) { 394 | mMediaController.setEnabled(true); 395 | } 396 | mVideoWidth = mp.getVideoWidth(); 397 | mVideoHeight = mp.getVideoHeight(); 398 | 399 | int seekToPosition = mSeekWhenPrepared; // mSeekWhenPrepared may be changed after seekTo() call 400 | if (seekToPosition != 0) { 401 | seekTo(seekToPosition); 402 | } 403 | if (mVideoWidth != 0 && mVideoHeight != 0) { 404 | //Log.i("@@@@", "video size: " + mVideoWidth +"/"+ mVideoHeight); 405 | // REMOVED: getHolder().setFixedSize(mVideoWidth, mVideoHeight); 406 | if (mRenderView != null) { 407 | mRenderView.setVideoSize(mVideoWidth, mVideoHeight); 408 | mRenderView.setVideoSampleAspectRatio(mVideoSarNum, mVideoSarDen); 409 | if (!mRenderView.shouldWaitForResize() || mSurfaceWidth == mVideoWidth && mSurfaceHeight == mVideoHeight) { 410 | // We didn't actually change the size (it was already at the size 411 | // we need), so we won't get a "surface changed" callback, so 412 | // start the video here instead of in the callback. 413 | if (mTargetState == STATE_PLAYING) { 414 | start(); 415 | if (mMediaController != null) { 416 | mMediaController.show(); 417 | } 418 | } else if (!isPlaying() && 419 | (seekToPosition != 0 || getCurrentPosition() > 0)) { 420 | if (mMediaController != null) { 421 | // Show the media controls when we're paused into a video and make 'em stick. 422 | mMediaController.show(0); 423 | } 424 | } 425 | } 426 | } 427 | } else { 428 | // We don't know the video size yet, but should start anyway. 429 | // The video size might be reported to us later. 430 | if (mTargetState == STATE_PLAYING) { 431 | start(); 432 | } 433 | } 434 | } 435 | }; 436 | 437 | private IMediaPlayer.OnCompletionListener mCompletionListener = 438 | new IMediaPlayer.OnCompletionListener() { 439 | public void onCompletion(IMediaPlayer mp) { 440 | mCurrentState = STATE_PLAYBACK_COMPLETED; 441 | mTargetState = STATE_PLAYBACK_COMPLETED; 442 | if (mMediaController != null) { 443 | mMediaController.hide(); 444 | } 445 | if (mOnCompletionListener != null) { 446 | mOnCompletionListener.onCompletion(mMediaPlayer); 447 | } 448 | } 449 | }; 450 | 451 | private IMediaPlayer.OnInfoListener mInfoListener = 452 | new IMediaPlayer.OnInfoListener() { 453 | public boolean onInfo(IMediaPlayer mp, int arg1, int arg2) { 454 | if (mOnInfoListener != null) { 455 | mOnInfoListener.onInfo(mp, arg1, arg2); 456 | } 457 | switch (arg1) { 458 | case IMediaPlayer.MEDIA_INFO_VIDEO_TRACK_LAGGING: 459 | Log.d(TAG, "MEDIA_INFO_VIDEO_TRACK_LAGGING:"); 460 | break; 461 | case IMediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START: 462 | Log.d(TAG, "MEDIA_INFO_VIDEO_RENDERING_START:"); 463 | break; 464 | case IMediaPlayer.MEDIA_INFO_BUFFERING_START: 465 | Log.d(TAG, "MEDIA_INFO_BUFFERING_START:"); 466 | break; 467 | case IMediaPlayer.MEDIA_INFO_BUFFERING_END: 468 | Log.d(TAG, "MEDIA_INFO_BUFFERING_END:"); 469 | break; 470 | case IMediaPlayer.MEDIA_INFO_NETWORK_BANDWIDTH: 471 | Log.d(TAG, "MEDIA_INFO_NETWORK_BANDWIDTH: " + arg2); 472 | break; 473 | case IMediaPlayer.MEDIA_INFO_BAD_INTERLEAVING: 474 | Log.d(TAG, "MEDIA_INFO_BAD_INTERLEAVING:"); 475 | break; 476 | case IMediaPlayer.MEDIA_INFO_NOT_SEEKABLE: 477 | Log.d(TAG, "MEDIA_INFO_NOT_SEEKABLE:"); 478 | break; 479 | case IMediaPlayer.MEDIA_INFO_METADATA_UPDATE: 480 | Log.d(TAG, "MEDIA_INFO_METADATA_UPDATE:"); 481 | break; 482 | case IMediaPlayer.MEDIA_INFO_UNSUPPORTED_SUBTITLE: 483 | Log.d(TAG, "MEDIA_INFO_UNSUPPORTED_SUBTITLE:"); 484 | break; 485 | case IMediaPlayer.MEDIA_INFO_SUBTITLE_TIMED_OUT: 486 | Log.d(TAG, "MEDIA_INFO_SUBTITLE_TIMED_OUT:"); 487 | break; 488 | case IMediaPlayer.MEDIA_INFO_VIDEO_ROTATION_CHANGED: 489 | mVideoRotationDegree = arg2; 490 | Log.d(TAG, "MEDIA_INFO_VIDEO_ROTATION_CHANGED: " + arg2); 491 | if (mRenderView != null) 492 | mRenderView.setVideoRotation(arg2); 493 | break; 494 | case IMediaPlayer.MEDIA_INFO_AUDIO_RENDERING_START: 495 | Log.d(TAG, "MEDIA_INFO_AUDIO_RENDERING_START:"); 496 | break; 497 | } 498 | return true; 499 | } 500 | }; 501 | 502 | private IMediaPlayer.OnErrorListener mErrorListener = 503 | new IMediaPlayer.OnErrorListener() { 504 | public boolean onError(IMediaPlayer mp, int framework_err, int impl_err) { 505 | Log.d(TAG, "Error: " + framework_err + "," + impl_err); 506 | mCurrentState = STATE_ERROR; 507 | mTargetState = STATE_ERROR; 508 | if (mMediaController != null) { 509 | mMediaController.hide(); 510 | } 511 | 512 | /* If an error handler has been supplied, use it and finish. */ 513 | if (mOnErrorListener != null) { 514 | if (mOnErrorListener.onError(mMediaPlayer, framework_err, impl_err)) { 515 | return true; 516 | } 517 | } 518 | 519 | /* Otherwise, pop up an error dialog so the user knows that 520 | * something bad has happened. Only try and pop up the dialog 521 | * if we're attached to a window. When we're going away and no 522 | * longer have a window, don't bother showing the user an error. 523 | */ 524 | if (getWindowToken() != null) { 525 | Resources r = mAppContext.getResources(); 526 | int messageId; 527 | 528 | if (framework_err == MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK) { 529 | messageId = R.string.VideoView_error_text_invalid_progressive_playback; 530 | } else { 531 | messageId = R.string.VideoView_error_text_unknown; 532 | } 533 | 534 | new AlertDialog.Builder(getContext()) 535 | .setMessage(messageId) 536 | .setPositiveButton(R.string.VideoView_error_button, 537 | new DialogInterface.OnClickListener() { 538 | public void onClick(DialogInterface dialog, int whichButton) { 539 | /* If we get here, there is no onError listener, so 540 | * at least inform them that the video is over. 541 | */ 542 | if (mOnCompletionListener != null) { 543 | mOnCompletionListener.onCompletion(mMediaPlayer); 544 | } 545 | } 546 | }) 547 | .setCancelable(false) 548 | .show(); 549 | } 550 | return true; 551 | } 552 | }; 553 | 554 | private IMediaPlayer.OnBufferingUpdateListener mBufferingUpdateListener = 555 | new IMediaPlayer.OnBufferingUpdateListener() { 556 | public void onBufferingUpdate(IMediaPlayer mp, int percent) { 557 | mCurrentBufferPercentage = percent; 558 | } 559 | }; 560 | 561 | /** 562 | * Register a callback to be invoked when the media file 563 | * is loaded and ready to go. 564 | * 565 | * @param l The callback that will be run 566 | */ 567 | public void setOnPreparedListener(IMediaPlayer.OnPreparedListener l) { 568 | mOnPreparedListener = l; 569 | } 570 | 571 | /** 572 | * Register a callback to be invoked when the end of a media file 573 | * has been reached during playback. 574 | * 575 | * @param l The callback that will be run 576 | */ 577 | public void setOnCompletionListener(IMediaPlayer.OnCompletionListener l) { 578 | mOnCompletionListener = l; 579 | } 580 | 581 | /** 582 | * Register a callback to be invoked when an error occurs 583 | * during playback or setup. If no listener is specified, 584 | * or if the listener returned false, VideoView will inform 585 | * the user of any errors. 586 | * 587 | * @param l The callback that will be run 588 | */ 589 | public void setOnErrorListener(IMediaPlayer.OnErrorListener l) { 590 | mOnErrorListener = l; 591 | } 592 | 593 | /** 594 | * Register a callback to be invoked when an informational event 595 | * occurs during playback or setup. 596 | * 597 | * @param l The callback that will be run 598 | */ 599 | public void setOnInfoListener(IMediaPlayer.OnInfoListener l) { 600 | mOnInfoListener = l; 601 | } 602 | 603 | // REMOVED: mSHCallback 604 | private void bindSurfaceHolder(IMediaPlayer mp, IRenderView.ISurfaceHolder holder) { 605 | if (mp == null) 606 | return; 607 | 608 | if (holder == null) { 609 | mp.setDisplay(null); 610 | return; 611 | } 612 | 613 | holder.bindToMediaPlayer(mp); 614 | } 615 | 616 | IRenderView.IRenderCallback mSHCallback = new IRenderView.IRenderCallback() { 617 | @Override 618 | public void onSurfaceChanged(@NonNull IRenderView.ISurfaceHolder holder, int format, int w, int h) { 619 | if (holder.getRenderView() != mRenderView) { 620 | Log.e(TAG, "onSurfaceChanged: unmatched render callback\n"); 621 | return; 622 | } 623 | 624 | mSurfaceWidth = w; 625 | mSurfaceHeight = h; 626 | boolean isValidState = (mTargetState == STATE_PLAYING); 627 | boolean hasValidSize = !mRenderView.shouldWaitForResize() || (mVideoWidth == w && mVideoHeight == h); 628 | if (mMediaPlayer != null && isValidState && hasValidSize) { 629 | if (mSeekWhenPrepared != 0) { 630 | seekTo(mSeekWhenPrepared); 631 | } 632 | start(); 633 | } 634 | } 635 | 636 | @Override 637 | public void onSurfaceCreated(@NonNull IRenderView.ISurfaceHolder holder, int width, int height) { 638 | if (holder.getRenderView() != mRenderView) { 639 | Log.e(TAG, "onSurfaceCreated: unmatched render callback\n"); 640 | return; 641 | } 642 | 643 | mSurfaceHolder = holder; 644 | if (mMediaPlayer != null) 645 | bindSurfaceHolder(mMediaPlayer, holder); 646 | else 647 | openVideo(); 648 | } 649 | 650 | @Override 651 | public void onSurfaceDestroyed(@NonNull IRenderView.ISurfaceHolder holder) { 652 | if (holder.getRenderView() != mRenderView) { 653 | Log.e(TAG, "onSurfaceDestroyed: unmatched render callback\n"); 654 | return; 655 | } 656 | 657 | // after we return from this we can't use the surface any more 658 | mSurfaceHolder = null; 659 | // REMOVED: if (mMediaController != null) mMediaController.hide(); 660 | // REMOVED: release(true); 661 | releaseWithoutStop(); 662 | } 663 | }; 664 | 665 | public void releaseWithoutStop() { 666 | if (mMediaPlayer != null) 667 | mMediaPlayer.setDisplay(null); 668 | } 669 | 670 | /* 671 | * release the media player in any state 672 | */ 673 | public void release(boolean cleartargetstate) { 674 | if (mMediaPlayer != null) { 675 | mMediaPlayer.reset(); 676 | mMediaPlayer.release(); 677 | mMediaPlayer = null; 678 | // REMOVED: mPendingSubtitleTracks.clear(); 679 | mCurrentState = STATE_IDLE; 680 | if (cleartargetstate) { 681 | mTargetState = STATE_IDLE; 682 | } 683 | AudioManager am = (AudioManager) mAppContext.getSystemService(Context.AUDIO_SERVICE); 684 | am.abandonAudioFocus(null); 685 | } 686 | } 687 | 688 | @Override 689 | public boolean onTouchEvent(MotionEvent ev) { 690 | if (isInPlaybackState() && mMediaController != null) { 691 | toggleMediaControlsVisiblity(); 692 | } 693 | return false; 694 | } 695 | 696 | @Override 697 | public boolean onTrackballEvent(MotionEvent ev) { 698 | if (isInPlaybackState() && mMediaController != null) { 699 | toggleMediaControlsVisiblity(); 700 | } 701 | return false; 702 | } 703 | 704 | @Override 705 | public boolean onKeyDown(int keyCode, KeyEvent event) { 706 | boolean isKeyCodeSupported = keyCode != KeyEvent.KEYCODE_BACK && 707 | keyCode != KeyEvent.KEYCODE_VOLUME_UP && 708 | keyCode != KeyEvent.KEYCODE_VOLUME_DOWN && 709 | keyCode != KeyEvent.KEYCODE_VOLUME_MUTE && 710 | keyCode != KeyEvent.KEYCODE_MENU && 711 | keyCode != KeyEvent.KEYCODE_CALL && 712 | keyCode != KeyEvent.KEYCODE_ENDCALL; 713 | if (isInPlaybackState() && isKeyCodeSupported && mMediaController != null) { 714 | if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK || 715 | keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) { 716 | if (mMediaPlayer.isPlaying()) { 717 | pause(); 718 | mMediaController.show(); 719 | } else { 720 | start(); 721 | mMediaController.hide(); 722 | } 723 | return true; 724 | } else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) { 725 | if (!mMediaPlayer.isPlaying()) { 726 | start(); 727 | mMediaController.hide(); 728 | } 729 | return true; 730 | } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP 731 | || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) { 732 | if (mMediaPlayer.isPlaying()) { 733 | pause(); 734 | mMediaController.show(); 735 | } 736 | return true; 737 | } else { 738 | toggleMediaControlsVisiblity(); 739 | } 740 | } 741 | 742 | return super.onKeyDown(keyCode, event); 743 | } 744 | 745 | private void toggleMediaControlsVisiblity() { 746 | if (mMediaController.isShowing()) { 747 | mMediaController.hide(); 748 | } else { 749 | mMediaController.show(); 750 | } 751 | } 752 | boolean isFirst = true; 753 | @Override 754 | public void start() { 755 | if (isInPlaybackState()) { 756 | if (!isFirst && STATE_PAUSED == mTargetState && System.currentTimeMillis()-mTime >(60*1000)){ 757 | mMediaPlayer.reset(); 758 | setVideoPath(mPath); 759 | } 760 | mMediaPlayer.start(); 761 | mCurrentState = STATE_PLAYING; 762 | } 763 | mTargetState = STATE_PLAYING; 764 | } 765 | 766 | @Override 767 | public void pause() { 768 | if (isInPlaybackState()) { 769 | if (mMediaPlayer.isPlaying()) { 770 | mMediaPlayer.pause(); 771 | //获取当前时间 772 | mTime = System.currentTimeMillis(); 773 | if(isFirst == true){ 774 | isFirst = false; 775 | } 776 | mCurrentState = STATE_PAUSED; 777 | } 778 | } 779 | mTargetState = STATE_PAUSED; 780 | } 781 | 782 | public void suspend() { 783 | release(false); 784 | } 785 | 786 | public void resume() { 787 | openVideo(); 788 | } 789 | 790 | @Override 791 | public int getDuration() { 792 | if (isInPlaybackState()) { 793 | return (int) mMediaPlayer.getDuration(); 794 | } 795 | 796 | return -1; 797 | } 798 | 799 | @Override 800 | public int getCurrentPosition() { 801 | if (isInPlaybackState()) { 802 | return (int) mMediaPlayer.getCurrentPosition(); 803 | } 804 | return 0; 805 | } 806 | 807 | @Override 808 | public void seekTo(int msec) { 809 | if (isInPlaybackState()) { 810 | mMediaPlayer.seekTo(msec); 811 | mSeekWhenPrepared = 0; 812 | } else { 813 | mSeekWhenPrepared = msec; 814 | } 815 | } 816 | 817 | @Override 818 | public boolean isPlaying() { 819 | return isInPlaybackState() && mMediaPlayer.isPlaying(); 820 | } 821 | 822 | @Override 823 | public int getBufferPercentage() { 824 | if (mMediaPlayer != null) { 825 | return mCurrentBufferPercentage; 826 | } 827 | return 0; 828 | } 829 | 830 | private boolean isInPlaybackState() { 831 | return (mMediaPlayer != null && 832 | mCurrentState != STATE_ERROR && 833 | mCurrentState != STATE_IDLE && 834 | mCurrentState != STATE_PREPARING); 835 | } 836 | 837 | @Override 838 | public boolean canPause() { 839 | return mCanPause; 840 | } 841 | 842 | @Override 843 | public boolean canSeekBackward() { 844 | return mCanSeekBack; 845 | } 846 | 847 | @Override 848 | public boolean canSeekForward() { 849 | return mCanSeekForward; 850 | } 851 | 852 | @Override 853 | public int getAudioSessionId() { 854 | return 0; 855 | } 856 | 857 | // REMOVED: getAudioSessionId(); 858 | // REMOVED: onAttachedToWindow(); 859 | // REMOVED: onDetachedFromWindow(); 860 | // REMOVED: onLayout(); 861 | // REMOVED: draw(); 862 | // REMOVED: measureAndLayoutSubtitleWidget(); 863 | // REMOVED: setSubtitleWidget(); 864 | // REMOVED: getSubtitleLooper(); 865 | 866 | //------------------------- 867 | // Extend: Aspect Ratio 868 | //------------------------- 869 | 870 | private static final int[] s_allAspectRatio = { 871 | IRenderView.AR_ASPECT_FIT_PARENT, 872 | IRenderView.AR_ASPECT_FILL_PARENT, 873 | IRenderView.AR_ASPECT_WRAP_CONTENT, 874 | // IRenderView.AR_MATCH_PARENT, 875 | IRenderView.AR_16_9_FIT_PARENT, 876 | IRenderView.AR_4_3_FIT_PARENT}; 877 | private int mCurrentAspectRatioIndex = 0; 878 | private int mCurrentAspectRatio = s_allAspectRatio[0]; 879 | 880 | public int toggleAspectRatio() { 881 | mCurrentAspectRatioIndex++; 882 | mCurrentAspectRatioIndex %= s_allAspectRatio.length; 883 | 884 | mCurrentAspectRatio = s_allAspectRatio[mCurrentAspectRatioIndex]; 885 | if (mRenderView != null) 886 | mRenderView.setAspectRatio(mCurrentAspectRatio); 887 | return mCurrentAspectRatio; 888 | } 889 | 890 | //------------------------- 891 | // Extend: Render 892 | //------------------------- 893 | public static final int RENDER_NONE = 0; 894 | public static final int RENDER_SURFACE_VIEW = 1; 895 | public static final int RENDER_TEXTURE_VIEW = 2; 896 | 897 | private List mAllRenders = new ArrayList(); 898 | private int mCurrentRenderIndex = 0; 899 | private int mCurrentRender = RENDER_NONE; 900 | 901 | private void initRenders() { 902 | mAllRenders.clear(); 903 | 904 | if (mSettings.getEnableSurfaceView()) 905 | mAllRenders.add(RENDER_SURFACE_VIEW); 906 | if (mSettings.getEnableTextureView() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) 907 | mAllRenders.add(RENDER_TEXTURE_VIEW); 908 | if (mSettings.getEnableNoView()) 909 | mAllRenders.add(RENDER_NONE); 910 | 911 | if (mAllRenders.isEmpty()) 912 | mAllRenders.add(RENDER_SURFACE_VIEW); 913 | mCurrentRender = mAllRenders.get(mCurrentRenderIndex); 914 | setRender(mCurrentRender); 915 | } 916 | 917 | public int toggleRender() { 918 | mCurrentRenderIndex++; 919 | mCurrentRenderIndex %= mAllRenders.size(); 920 | 921 | mCurrentRender = mAllRenders.get(mCurrentRenderIndex); 922 | setRender(mCurrentRender); 923 | return mCurrentRender; 924 | } 925 | 926 | @NonNull 927 | public static String getRenderText(Context context, int render) { 928 | String text; 929 | switch (render) { 930 | case RENDER_NONE: 931 | text = context.getString(R.string.VideoView_render_none); 932 | break; 933 | case RENDER_SURFACE_VIEW: 934 | text = context.getString(R.string.VideoView_render_surface_view); 935 | break; 936 | case RENDER_TEXTURE_VIEW: 937 | text = context.getString(R.string.VideoView_render_texture_view); 938 | break; 939 | default: 940 | text = context.getString(R.string.N_A); 941 | break; 942 | } 943 | return text; 944 | } 945 | 946 | //------------------------- 947 | // Extend: Player 948 | //------------------------- 949 | public int togglePlayer() { 950 | if (mMediaPlayer != null) 951 | mMediaPlayer.release(); 952 | 953 | if (mRenderView != null) 954 | mRenderView.getView().invalidate(); 955 | openVideo(); 956 | return mSettings.getPlayer(); 957 | } 958 | 959 | @NonNull 960 | public static String getPlayerText(Context context, int player) { 961 | String text; 962 | switch (player) { 963 | case Settings.PV_PLAYER__AndroidMediaPlayer: 964 | text = context.getString(R.string.VideoView_player_AndroidMediaPlayer); 965 | break; 966 | case Settings.PV_PLAYER__IjkMediaPlayer: 967 | text = context.getString(R.string.VideoView_player_IjkMediaPlayer); 968 | break; 969 | case Settings.PV_PLAYER__IjkExoMediaPlayer: 970 | text = context.getString(R.string.VideoView_player_IjkExoMediaPlayer); 971 | break; 972 | default: 973 | text = context.getString(R.string.N_A); 974 | break; 975 | } 976 | return text; 977 | } 978 | 979 | public IMediaPlayer createPlayer(int playerType) { 980 | IMediaPlayer mediaPlayer = null; 981 | 982 | switch (playerType) { 983 | case Settings.PV_PLAYER__IjkExoMediaPlayer: { 984 | IjkExoMediaPlayer IjkExoMediaPlayer = new IjkExoMediaPlayer(mAppContext); 985 | mediaPlayer = IjkExoMediaPlayer; 986 | } 987 | break; 988 | case Settings.PV_PLAYER__AndroidMediaPlayer: { 989 | AndroidMediaPlayer androidMediaPlayer = new AndroidMediaPlayer(); 990 | mediaPlayer = androidMediaPlayer; 991 | } 992 | break; 993 | case Settings.PV_PLAYER__IjkMediaPlayer: 994 | default: { 995 | IjkMediaPlayer ijkMediaPlayer = null; 996 | if (mUri != null) { 997 | ijkMediaPlayer = new IjkMediaPlayer(); 998 | ijkMediaPlayer.native_setLogLevel(IjkMediaPlayer.IJK_LOG_DEBUG); 999 | 1000 | if (mSettings.getUsingMediaCodec()) { 1001 | ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 1); 1002 | if (mSettings.getUsingMediaCodecAutoRotate()) { 1003 | ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 1); 1004 | } else { 1005 | ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 0); 1006 | } 1007 | } else { 1008 | ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 0); 1009 | } 1010 | 1011 | if (mSettings.getUsingOpenSLES()) { 1012 | ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "opensles", 1); 1013 | } else { 1014 | ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "opensles", 0); 1015 | } 1016 | 1017 | String pixelFormat = mSettings.getPixelFormat(); 1018 | if (TextUtils.isEmpty(pixelFormat)) { 1019 | ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "overlay-format", IjkMediaPlayer.SDL_FCC_RV32); 1020 | } else { 1021 | ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "overlay-format", pixelFormat); 1022 | } 1023 | ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "framedrop", 1); 1024 | ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", 0); 1025 | 1026 | ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "http-detect-range-support", 0); 1027 | 1028 | ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_loop_filter", 48); 1029 | } 1030 | mediaPlayer = ijkMediaPlayer; 1031 | } 1032 | break; 1033 | } 1034 | 1035 | if (mSettings.getEnableDetachedSurfaceTextureView()) { 1036 | mediaPlayer = new TextureMediaPlayer(mediaPlayer); 1037 | } 1038 | 1039 | return mediaPlayer; 1040 | } 1041 | 1042 | //------------------------- 1043 | // Extend: Background 1044 | //------------------------- 1045 | 1046 | private boolean mEnableBackgroundPlay = false; 1047 | 1048 | private void initBackground() { 1049 | mEnableBackgroundPlay = mSettings.getEnableBackgroundPlay(); 1050 | if (mEnableBackgroundPlay) { 1051 | MediaPlayerService.intentToStart(getContext()); 1052 | mMediaPlayer = MediaPlayerService.getMediaPlayer(); 1053 | if (mHudViewHolder != null) 1054 | mHudViewHolder.setMediaPlayer(mMediaPlayer); 1055 | } 1056 | } 1057 | 1058 | public boolean isBackgroundPlayEnabled() { 1059 | return mEnableBackgroundPlay; 1060 | } 1061 | 1062 | public void enterBackground() { 1063 | MediaPlayerService.setMediaPlayer(mMediaPlayer); 1064 | } 1065 | 1066 | public void stopBackgroundPlay() { 1067 | MediaPlayerService.setMediaPlayer(null); 1068 | } 1069 | 1070 | //------------------------- 1071 | // Extend: Background 1072 | //------------------------- 1073 | public void showMediaInfo() { 1074 | if (mMediaPlayer == null) 1075 | return; 1076 | 1077 | int selectedVideoTrack = MediaPlayerCompat.getSelectedTrack(mMediaPlayer, ITrackInfo.MEDIA_TRACK_TYPE_VIDEO); 1078 | int selectedAudioTrack = MediaPlayerCompat.getSelectedTrack(mMediaPlayer, ITrackInfo.MEDIA_TRACK_TYPE_AUDIO); 1079 | 1080 | TableLayoutBinder builder = new TableLayoutBinder(getContext()); 1081 | builder.appendSection(R.string.mi_player); 1082 | builder.appendRow2(R.string.mi_player, MediaPlayerCompat.getName(mMediaPlayer)); 1083 | builder.appendSection(R.string.mi_media); 1084 | builder.appendRow2(R.string.mi_resolution, buildResolution(mVideoWidth, mVideoHeight, mVideoSarNum, mVideoSarDen)); 1085 | builder.appendRow2(R.string.mi_length, buildTimeMilli(mMediaPlayer.getDuration())); 1086 | 1087 | ITrackInfo trackInfos[] = mMediaPlayer.getTrackInfo(); 1088 | if (trackInfos != null) { 1089 | int index = -1; 1090 | for (ITrackInfo trackInfo : trackInfos) { 1091 | index++; 1092 | 1093 | int trackType = trackInfo.getTrackType(); 1094 | if (index == selectedVideoTrack) { 1095 | builder.appendSection(getContext().getString(R.string.mi_stream_fmt1, index) + " " + getContext().getString(R.string.mi__selected_video_track)); 1096 | } else if (index == selectedAudioTrack) { 1097 | builder.appendSection(getContext().getString(R.string.mi_stream_fmt1, index) + " " + getContext().getString(R.string.mi__selected_audio_track)); 1098 | } else { 1099 | builder.appendSection(getContext().getString(R.string.mi_stream_fmt1, index)); 1100 | } 1101 | builder.appendRow2(R.string.mi_type, buildTrackType(trackType)); 1102 | builder.appendRow2(R.string.mi_language, buildLanguage(trackInfo.getLanguage())); 1103 | 1104 | IMediaFormat mediaFormat = trackInfo.getFormat(); 1105 | if (mediaFormat == null) { 1106 | } else if (mediaFormat instanceof IjkMediaFormat) { 1107 | switch (trackType) { 1108 | case ITrackInfo.MEDIA_TRACK_TYPE_VIDEO: 1109 | builder.appendRow2(R.string.mi_codec, mediaFormat.getString(IjkMediaFormat.KEY_IJK_CODEC_LONG_NAME_UI)); 1110 | builder.appendRow2(R.string.mi_profile_level, mediaFormat.getString(IjkMediaFormat.KEY_IJK_CODEC_PROFILE_LEVEL_UI)); 1111 | builder.appendRow2(R.string.mi_pixel_format, mediaFormat.getString(IjkMediaFormat.KEY_IJK_CODEC_PIXEL_FORMAT_UI)); 1112 | builder.appendRow2(R.string.mi_resolution, mediaFormat.getString(IjkMediaFormat.KEY_IJK_RESOLUTION_UI)); 1113 | builder.appendRow2(R.string.mi_frame_rate, mediaFormat.getString(IjkMediaFormat.KEY_IJK_FRAME_RATE_UI)); 1114 | builder.appendRow2(R.string.mi_bit_rate, mediaFormat.getString(IjkMediaFormat.KEY_IJK_BIT_RATE_UI)); 1115 | break; 1116 | case ITrackInfo.MEDIA_TRACK_TYPE_AUDIO: 1117 | builder.appendRow2(R.string.mi_codec, mediaFormat.getString(IjkMediaFormat.KEY_IJK_CODEC_LONG_NAME_UI)); 1118 | builder.appendRow2(R.string.mi_profile_level, mediaFormat.getString(IjkMediaFormat.KEY_IJK_CODEC_PROFILE_LEVEL_UI)); 1119 | builder.appendRow2(R.string.mi_sample_rate, mediaFormat.getString(IjkMediaFormat.KEY_IJK_SAMPLE_RATE_UI)); 1120 | builder.appendRow2(R.string.mi_channels, mediaFormat.getString(IjkMediaFormat.KEY_IJK_CHANNEL_UI)); 1121 | builder.appendRow2(R.string.mi_bit_rate, mediaFormat.getString(IjkMediaFormat.KEY_IJK_BIT_RATE_UI)); 1122 | break; 1123 | default: 1124 | break; 1125 | } 1126 | } 1127 | } 1128 | } 1129 | 1130 | AlertDialog.Builder adBuilder = builder.buildAlertDialogBuilder(); 1131 | adBuilder.setTitle(R.string.media_information); 1132 | adBuilder.setNegativeButton(R.string.close, null); 1133 | adBuilder.show(); 1134 | } 1135 | 1136 | private String buildResolution(int width, int height, int sarNum, int sarDen) { 1137 | StringBuilder sb = new StringBuilder(); 1138 | sb.append(width); 1139 | sb.append(" x "); 1140 | sb.append(height); 1141 | 1142 | if (sarNum > 1 || sarDen > 1) { 1143 | sb.append("["); 1144 | sb.append(sarNum); 1145 | sb.append(":"); 1146 | sb.append(sarDen); 1147 | sb.append("]"); 1148 | } 1149 | 1150 | return sb.toString(); 1151 | } 1152 | 1153 | private String buildTimeMilli(long duration) { 1154 | long total_seconds = duration / 1000; 1155 | long hours = total_seconds / 3600; 1156 | long minutes = (total_seconds % 3600) / 60; 1157 | long seconds = total_seconds % 60; 1158 | if (duration <= 0) { 1159 | return "--:--"; 1160 | } 1161 | if (hours >= 100) { 1162 | return String.format(Locale.US, "%d:%02d:%02d", hours, minutes, seconds); 1163 | } else if (hours > 0) { 1164 | return String.format(Locale.US, "%02d:%02d:%02d", hours, minutes, seconds); 1165 | } else { 1166 | return String.format(Locale.US, "%02d:%02d", minutes, seconds); 1167 | } 1168 | } 1169 | 1170 | private String buildTrackType(int type) { 1171 | Context context = getContext(); 1172 | switch (type) { 1173 | case ITrackInfo.MEDIA_TRACK_TYPE_VIDEO: 1174 | return context.getString(R.string.TrackType_video); 1175 | case ITrackInfo.MEDIA_TRACK_TYPE_AUDIO: 1176 | return context.getString(R.string.TrackType_audio); 1177 | case ITrackInfo.MEDIA_TRACK_TYPE_SUBTITLE: 1178 | return context.getString(R.string.TrackType_subtitle); 1179 | case ITrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT: 1180 | return context.getString(R.string.TrackType_timedtext); 1181 | case ITrackInfo.MEDIA_TRACK_TYPE_METADATA: 1182 | return context.getString(R.string.TrackType_metadata); 1183 | case ITrackInfo.MEDIA_TRACK_TYPE_UNKNOWN: 1184 | default: 1185 | return context.getString(R.string.TrackType_unknown); 1186 | } 1187 | } 1188 | 1189 | private String buildLanguage(String language) { 1190 | if (TextUtils.isEmpty(language)) 1191 | return "und"; 1192 | return language; 1193 | } 1194 | 1195 | public ITrackInfo[] getTrackInfo() { 1196 | if (mMediaPlayer == null) 1197 | return null; 1198 | 1199 | return mMediaPlayer.getTrackInfo(); 1200 | } 1201 | 1202 | public void selectTrack(int stream) { 1203 | MediaPlayerCompat.selectTrack(mMediaPlayer, stream); 1204 | } 1205 | 1206 | public void deselectTrack(int stream) { 1207 | MediaPlayerCompat.deselectTrack(mMediaPlayer, stream); 1208 | } 1209 | 1210 | public int getSelectedTrack(int trackType) { 1211 | return MediaPlayerCompat.getSelectedTrack(mMediaPlayer, trackType); 1212 | } 1213 | } 1214 | --------------------------------------------------------------------------------