├── app
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ ├── colors.xml
│ │ │ │ └── styles.xml
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── close.png
│ │ │ │ ├── timg.jpg
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── close.png
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── lobby_qidongye.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ └── layout
│ │ │ │ ├── activity_first.xml
│ │ │ │ ├── activity_fplm.xml
│ │ │ │ └── activity_main.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ │ └── com
│ │ │ └── ws
│ │ │ └── ijk
│ │ │ └── ws_ijk
│ │ │ ├── FirstActivity.java
│ │ │ ├── MainActivity.java
│ │ │ ├── FPLMActivity.java
│ │ │ └── CameraUtil.java
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── ws
│ │ │ └── ijk
│ │ │ └── ws_ijk
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── ws
│ │ └── ijk
│ │ └── ws_ijk
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
├── wsqj_ijkplayer
├── .gitignore
├── libs
│ └── armeabi-v7a
│ │ ├── libijksdl.so
│ │ ├── libijkffmpeg.so
│ │ └── libijkplayer.so
├── src
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── ws
│ │ │ └── ijk
│ │ │ └── wsqj_ijkplayer
│ │ │ └── ExampleUnitTest.java
│ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── ws
│ │ │ │ └── ijk
│ │ │ │ └── wsqj_ijkplayer
│ │ │ │ └── widget
│ │ │ │ ├── LianMaiSplitScreen
│ │ │ │ ├── LMFlowGroupView.java
│ │ │ │ ├── LMVideoView.java
│ │ │ │ └── AspectTextureView.java
│ │ │ │ ├── media
│ │ │ │ ├── IMediaController.java
│ │ │ │ ├── FileMediaDataSource.java
│ │ │ │ ├── AndroidMediaController.java
│ │ │ │ ├── IRenderView.java
│ │ │ │ ├── MediaPlayerCompat.java
│ │ │ │ ├── Settings.java
│ │ │ │ ├── SurfaceRenderView.java
│ │ │ │ ├── MeasureHelper.java
│ │ │ │ ├── TextureRenderView.java
│ │ │ │ └── IjkVideoView.java
│ │ │ │ ├── LianMai
│ │ │ │ ├── LianMaiVideoView.java
│ │ │ │ └── LianMaiViewGroup.java
│ │ │ │ ├── content
│ │ │ │ ├── PathCursorLoader.java
│ │ │ │ ├── RecentMediaStorage.java
│ │ │ │ └── PathCursor.java
│ │ │ │ └── services
│ │ │ │ └── MediaPlayerService.java
│ │ └── res
│ │ │ ├── layout
│ │ │ └── lm_videoview.xml
│ │ │ └── values
│ │ │ ├── strings.xml
│ │ │ └── strings_pref.xml
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── ws
│ │ └── ijk
│ │ └── wsqj_ijkplayer
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
├── settings.gradle
├── .idea
├── copyright
│ └── profiles_settings.xml
├── encodings.xml
├── vcs.xml
├── modules.xml
├── runConfigurations.xml
├── gradle.xml
├── compiler.xml
└── misc.xml
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .gitignore
├── gradle.properties
├── gradlew.bat
└── gradlew
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/wsqj_ijkplayer/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':wsqj_ijkplayer'
2 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | WS_IJK
3 |
4 |
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WangShuo1143368701/WS_IJK/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WangShuo1143368701/WS_IJK/HEAD/app/src/main/res/mipmap-hdpi/close.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/timg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WangShuo1143368701/WS_IJK/HEAD/app/src/main/res/mipmap-hdpi/timg.jpg
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WangShuo1143368701/WS_IJK/HEAD/app/src/main/res/mipmap-xhdpi/close.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WangShuo1143368701/WS_IJK/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WangShuo1143368701/WS_IJK/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WangShuo1143368701/WS_IJK/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WangShuo1143368701/WS_IJK/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/wsqj_ijkplayer/libs/armeabi-v7a/libijksdl.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WangShuo1143368701/WS_IJK/HEAD/wsqj_ijkplayer/libs/armeabi-v7a/libijksdl.so
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/lobby_qidongye.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WangShuo1143368701/WS_IJK/HEAD/app/src/main/res/mipmap-xhdpi/lobby_qidongye.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WangShuo1143368701/WS_IJK/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/wsqj_ijkplayer/libs/armeabi-v7a/libijkffmpeg.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WangShuo1143368701/WS_IJK/HEAD/wsqj_ijkplayer/libs/armeabi-v7a/libijkffmpeg.so
--------------------------------------------------------------------------------
/wsqj_ijkplayer/libs/armeabi-v7a/libijkplayer.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WangShuo1143368701/WS_IJK/HEAD/wsqj_ijkplayer/libs/armeabi-v7a/libijkplayer.so
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WangShuo1143368701/WS_IJK/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WangShuo1143368701/WS_IJK/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WangShuo1143368701/WS_IJK/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WangShuo1143368701/WS_IJK/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WangShuo1143368701/WS_IJK/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 | .externalNativeBuild
10 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Thu Jun 15 11:44:22 CST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 | //背景色
7 | #e6e8f2
8 |
9 |
--------------------------------------------------------------------------------
/app/src/test/java/com/ws/ijk/ws_ijk/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.ws.ijk.ws_ijk;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/wsqj_ijkplayer/src/test/java/com/ws/ijk/wsqj_ijkplayer/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.ws.ijk.wsqj_ijkplayer;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/wsqj_ijkplayer/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
7 |
8 |
9 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
13 |
14 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/ws/ijk/ws_ijk/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.ws.ijk.ws_ijk;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumentation test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.ws.ijk.ws_ijk", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/wsqj_ijkplayer/src/androidTest/java/com/ws/ijk/wsqj_ijkplayer/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.ws.ijk.wsqj_ijkplayer;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumentation test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.ws.ijk.wsqj_ijkplayer.test", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in D:\androidstudio\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 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/wsqj_ijkplayer/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 D:\androidstudio\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 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/wsqj_ijkplayer/src/main/java/com/ws/ijk/wsqj_ijkplayer/widget/LianMaiSplitScreen/LMFlowGroupView.java:
--------------------------------------------------------------------------------
1 | package com.ws.ijk.wsqj_ijkplayer.widget.LianMaiSplitScreen;
2 |
3 | import android.content.Context;
4 | import android.support.annotation.Nullable;
5 | import android.util.AttributeSet;
6 | import android.widget.LinearLayout;
7 |
8 | /**
9 | * Created by WangShuo on 2017/7/21.
10 | */
11 |
12 | public class LMFlowGroupView extends LinearLayout {
13 |
14 |
15 | public LMFlowGroupView(Context context) {
16 | super(context);
17 | init(context);
18 | }
19 |
20 | public LMFlowGroupView(Context context, @Nullable AttributeSet attrs) {
21 | super(context, attrs);
22 | init(context);
23 | }
24 |
25 | public LMFlowGroupView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
26 | super(context, attrs, defStyleAttr);
27 | init(context);
28 | }
29 |
30 | private void init(Context context) {
31 | setOrientation(LinearLayout.HORIZONTAL);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_first.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
16 |
17 |
22 |
23 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/wsqj_ijkplayer/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 25
5 | buildToolsVersion "25.0.0"
6 |
7 | defaultConfig {
8 | minSdkVersion 15
9 | targetSdkVersion 22
10 | versionCode 1
11 | versionName "1.0"
12 |
13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
14 |
15 | }
16 | buildTypes {
17 | release {
18 | minifyEnabled false
19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
20 | }
21 | }
22 | sourceSets {
23 | main {
24 | jniLibs.srcDirs = ['libs']
25 | }
26 | }
27 | }
28 |
29 | dependencies {
30 | compile fileTree(dir: 'libs', include: ['*.jar'])
31 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
32 | exclude group: 'com.android.support', module: 'support-annotations'
33 | })
34 | compile 'com.android.support:appcompat-v7:25.+'
35 | compile 'tv.danmaku.ijk.media:ijkplayer-java:0.8.0'
36 | testCompile 'junit:junit:4.12'
37 | }
38 |
--------------------------------------------------------------------------------
/wsqj_ijkplayer/src/main/res/layout/lm_videoview.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
11 |
17 |
25 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'android-apt'
3 |
4 | android {
5 | compileSdkVersion 25
6 | buildToolsVersion "25.0.0"
7 | defaultConfig {
8 | applicationId "com.ws.ijk.ws_ijk"
9 | minSdkVersion 15
10 | targetSdkVersion 22
11 | versionCode 1
12 | versionName "1.0"
13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
14 | }
15 | buildTypes {
16 | release {
17 | minifyEnabled false
18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
19 | }
20 | }
21 |
22 | }
23 |
24 | dependencies {
25 | compile fileTree(include: ['*.jar'], dir: 'libs')
26 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
27 | exclude group: 'com.android.support', module: 'support-annotations'
28 | })
29 | compile project(':wsqj_ijkplayer')
30 | compile 'com.android.support:appcompat-v7:25.3.1'
31 | compile 'com.android.support.constraint:constraint-layout:1.0.2'
32 | compile 'com.jakewharton:butterknife:8.6.0'
33 | testCompile 'junit:junit:4.12'
34 | apt 'com.jakewharton:butterknife-compiler:8.6.0'
35 | }
36 |
--------------------------------------------------------------------------------
/wsqj_ijkplayer/src/main/java/com/ws/ijk/wsqj_ijkplayer/widget/media/IMediaController.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2015 Bilibili
3 | * Copyright (C) 2015 Zhang Rui
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.ws.ijk.wsqj_ijkplayer.widget.media;
19 |
20 | import android.view.View;
21 | import android.widget.MediaController;
22 |
23 | public interface IMediaController {
24 | void hide();
25 |
26 | boolean isShowing();
27 |
28 | void setAnchorView(View view);
29 |
30 | void setEnabled(boolean enabled);
31 |
32 | void setMediaPlayer(MediaController.MediaPlayerControl player);
33 |
34 | void show(int timeout);
35 |
36 | void show();
37 |
38 | //----------
39 | // Extends
40 | //----------
41 | void showOnce(View view);
42 | }
43 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_fplm.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
18 |
19 |
20 |
21 |
27 |
28 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ws/ijk/ws_ijk/FirstActivity.java:
--------------------------------------------------------------------------------
1 | package com.ws.ijk.ws_ijk;
2 |
3 | import android.content.Intent;
4 | import android.os.Bundle;
5 | import android.support.v7.app.AppCompatActivity;
6 | import android.view.View;
7 | import android.widget.Button;
8 |
9 | import butterknife.BindView;
10 | import butterknife.ButterKnife;
11 | import butterknife.OnClick;
12 |
13 | public class FirstActivity extends AppCompatActivity {
14 |
15 |
16 | @BindView(R.id.XFLM)
17 | Button XFLM;
18 | @BindView(R.id.FPLM)
19 | Button FPLM;
20 | @BindView(R.id.FPLM2)
21 | Button FPLM2;
22 |
23 |
24 | @Override
25 | protected void onCreate(Bundle savedInstanceState) {
26 | super.onCreate(savedInstanceState);
27 | setContentView(R.layout.activity_first);
28 | ButterKnife.bind(this);
29 | }
30 |
31 | @OnClick({R.id.XFLM, R.id.FPLM, R.id.FPLM2})
32 | public void onViewClicked(View view) {
33 | switch (view.getId()) {
34 | case R.id.XFLM:
35 | Intent intent = new Intent(FirstActivity.this, MainActivity.class);
36 | startActivity(intent);
37 | break;
38 | case R.id.FPLM:
39 | Intent intent2 = new Intent(FirstActivity.this, FPLMActivity.class);
40 | startActivity(intent2);
41 | break;
42 | case R.id.FPLM2:
43 | Intent intent3 = new Intent(FirstActivity.this, FPLMActivity.class);
44 | startActivity(intent3);
45 | break;
46 |
47 |
48 | }
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/wsqj_ijkplayer/src/main/java/com/ws/ijk/wsqj_ijkplayer/widget/LianMai/LianMaiVideoView.java:
--------------------------------------------------------------------------------
1 | package com.ws.ijk.wsqj_ijkplayer.widget.LianMai;
2 |
3 | import android.content.Context;
4 | import android.graphics.Color;
5 | import android.support.annotation.AttrRes;
6 | import android.support.annotation.NonNull;
7 | import android.support.annotation.Nullable;
8 | import android.util.AttributeSet;
9 | import android.view.View;
10 | import android.widget.FrameLayout;
11 |
12 | import com.ws.ijk.wsqj_ijkplayer.widget.media.LiveIjkVideoView;
13 |
14 | /**
15 | * Created by WangShuo on 2017/7/20.
16 | */
17 |
18 | public class LianMaiVideoView extends FrameLayout {
19 |
20 | private View mLiveVideoView;
21 |
22 | public LianMaiVideoView(@NonNull Context context) {
23 | super(context);
24 | init();
25 | }
26 |
27 | public LianMaiVideoView(@NonNull Context context, @Nullable AttributeSet attrs) {
28 | super(context, attrs);
29 | init();
30 | }
31 |
32 | public LianMaiVideoView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
33 | super(context, attrs, defStyleAttr);
34 | init();
35 | }
36 |
37 | private void init() {
38 | setBackgroundColor(Color.TRANSPARENT);
39 | }
40 |
41 | @Override
42 | protected void onFinishInflate() {
43 | super.onFinishInflate();
44 |
45 | mLiveVideoView = getChildAt(0);
46 | if(mLiveVideoView instanceof LiveIjkVideoView) {
47 | ((LiveIjkVideoView) mLiveVideoView).setRender(LiveIjkVideoView.RENDER_TEXTURE_VIEW);
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/wsqj_ijkplayer/src/main/java/com/ws/ijk/wsqj_ijkplayer/widget/content/PathCursorLoader.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2015 Bilibili
3 | * Copyright (C) 2015 Zhang Rui
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.ws.ijk.wsqj_ijkplayer.widget.content;
19 |
20 | import android.content.Context;
21 | import android.database.Cursor;
22 | import android.os.Environment;
23 | import android.support.v4.content.AsyncTaskLoader;
24 |
25 | import java.io.File;
26 |
27 | public class PathCursorLoader extends AsyncTaskLoader {
28 | private File mPath;
29 |
30 | public PathCursorLoader(Context context) {
31 | this(context, Environment.getExternalStorageDirectory());
32 | }
33 |
34 | public PathCursorLoader(Context context, String path) {
35 | super(context);
36 | mPath = new File(path).getAbsoluteFile();
37 | }
38 |
39 | public PathCursorLoader(Context context, File path) {
40 | super(context);
41 | mPath = path;
42 | }
43 |
44 | @Override
45 | public Cursor loadInBackground() {
46 | File[] file_list = mPath.listFiles();
47 | return new PathCursor(mPath, file_list);
48 | }
49 |
50 | @Override
51 | protected void onStartLoading() {
52 | forceLoad();
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/wsqj_ijkplayer/src/main/java/com/ws/ijk/wsqj_ijkplayer/widget/media/FileMediaDataSource.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2015 Bilibili
3 | * Copyright (C) 2015 Zhang Rui
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.ws.ijk.wsqj_ijkplayer.widget.media;
19 |
20 | import java.io.File;
21 | import java.io.IOException;
22 | import java.io.RandomAccessFile;
23 |
24 | import tv.danmaku.ijk.media.player.misc.IMediaDataSource;
25 |
26 | public class FileMediaDataSource implements IMediaDataSource {
27 | private RandomAccessFile mFile;
28 | private long mFileSize;
29 |
30 | public FileMediaDataSource(File file) throws IOException {
31 | mFile = new RandomAccessFile(file, "r");
32 | mFileSize = mFile.length();
33 | }
34 |
35 | @Override
36 | public int readAt(long position, byte[] buffer, int offset, int size) throws IOException {
37 | if (mFile.getFilePointer() != position)
38 | mFile.seek(position);
39 |
40 | if (size == 0)
41 | return 0;
42 |
43 | return mFile.read(buffer, 0, size);
44 | }
45 |
46 | @Override
47 | public long getSize() throws IOException {
48 | return mFileSize;
49 | }
50 |
51 | @Override
52 | public void close() throws IOException {
53 | mFileSize = 0;
54 | mFile.close();
55 | mFile = null;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/wsqj_ijkplayer/src/main/java/com/ws/ijk/wsqj_ijkplayer/widget/services/MediaPlayerService.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2015 Bilibili
3 | * Copyright (C) 2015 Zhang Rui
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.ws.ijk.wsqj_ijkplayer.widget.services;
19 |
20 | import android.app.Service;
21 | import android.content.Context;
22 | import android.content.Intent;
23 | import android.os.IBinder;
24 | import android.support.annotation.Nullable;
25 |
26 | import tv.danmaku.ijk.media.player.IMediaPlayer;
27 |
28 | /**
29 | * Created by Wang.shuo on 2017/6/15.
30 | */
31 |
32 | public class MediaPlayerService extends Service {
33 | private static IMediaPlayer sMediaPlayer;
34 |
35 | public static Intent newIntent(Context context) {
36 | Intent intent = new Intent(context, MediaPlayerService.class);
37 | return intent;
38 | }
39 |
40 | public static void intentToStart(Context context) {
41 | context.startService(newIntent(context));
42 | }
43 |
44 | public static void intentToStop(Context context) {
45 | context.stopService(newIntent(context));
46 | }
47 |
48 | @Nullable
49 | @Override
50 | public IBinder onBind(Intent intent) {
51 | return null;
52 | }
53 |
54 | public static void setMediaPlayer(IMediaPlayer mp) {
55 | if (sMediaPlayer != null && sMediaPlayer != mp) {
56 | if (sMediaPlayer.isPlaying())
57 | sMediaPlayer.stop();
58 | sMediaPlayer.release();
59 | sMediaPlayer = null;
60 | }
61 | sMediaPlayer = mp;
62 | }
63 |
64 | public static IMediaPlayer getMediaPlayer() {
65 | return sMediaPlayer;
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
16 |
22 |
23 |
27 |
33 |
34 |
39 |
45 |
54 |
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/wsqj_ijkplayer/src/main/java/com/ws/ijk/wsqj_ijkplayer/widget/LianMaiSplitScreen/LMVideoView.java:
--------------------------------------------------------------------------------
1 | package com.ws.ijk.wsqj_ijkplayer.widget.LianMaiSplitScreen;
2 |
3 | import android.content.Context;
4 | import android.graphics.Color;
5 | import android.support.annotation.AttrRes;
6 | import android.support.annotation.NonNull;
7 | import android.support.annotation.Nullable;
8 | import android.util.AttributeSet;
9 | import android.view.LayoutInflater;
10 | import android.view.View;
11 | import android.widget.FrameLayout;
12 | import android.widget.ImageView;
13 |
14 | import com.ws.ijk.wsqj_ijkplayer.R;
15 | import com.ws.ijk.wsqj_ijkplayer.widget.media.LiveIjkVideoView;
16 |
17 | /**
18 | * Created by WangShuo on 2017/7/20.
19 | */
20 |
21 | public class LMVideoView extends FrameLayout {
22 |
23 | private Context mContext;
24 | private String mPath;
25 |
26 | private View view;
27 | private LiveIjkVideoView mLiveVideoView;
28 | private ImageView mCoverView;
29 |
30 |
31 | public LMVideoView(@NonNull Context context) {
32 | super(context);
33 | init(context);
34 | }
35 |
36 | public LMVideoView(@NonNull Context context, @Nullable AttributeSet attrs) {
37 | super(context, attrs);
38 | init(context);
39 | }
40 |
41 | public LMVideoView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
42 | super(context, attrs, defStyleAttr);
43 | init(context);
44 | }
45 |
46 | public LMVideoView(@NonNull Context context ,String path) {
47 | super(context);
48 | this.mPath = path;
49 | init(context);
50 | }
51 |
52 | private void init(Context context) {
53 | this.mContext = context;
54 | setBackgroundColor(Color.TRANSPARENT);
55 | LayoutInflater layoutInflater = LayoutInflater.from(context);
56 | view = layoutInflater.inflate(R.layout.lm_videoview, null);
57 | addView(view);
58 |
59 | initView();
60 | }
61 |
62 | private void initView() {
63 |
64 | if(view != null) {
65 | mLiveVideoView = (LiveIjkVideoView) view.findViewById(R.id.lm_video_view);
66 | mCoverView = (ImageView) view.findViewById(R.id.lm_play_coverview);
67 |
68 | mLiveVideoView.setRender(LiveIjkVideoView.RENDER_TEXTURE_VIEW);
69 | mLiveVideoView.setDisplayAspectRatio(LiveIjkVideoView.s_allAspectRatio[1]);
70 | mLiveVideoView.setCoverView(mCoverView);
71 | if(mPath != null){
72 | mLiveVideoView.setVideoPath(mPath);
73 | }
74 | }
75 | }
76 |
77 |
78 | public LiveIjkVideoView getLMijkVideoView(){
79 | return mLiveVideoView;
80 | }
81 |
82 | public ImageView getCoverView(){
83 | return mCoverView;
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/wsqj_ijkplayer/src/main/java/com/ws/ijk/wsqj_ijkplayer/widget/media/AndroidMediaController.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2015 Bilibili
3 | * Copyright (C) 2015 Zhang Rui
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.ws.ijk.wsqj_ijkplayer.widget.media;
19 |
20 | import android.content.Context;
21 | import android.support.annotation.NonNull;
22 | import android.support.annotation.Nullable;
23 | import android.support.v7.app.ActionBar;
24 | import android.util.AttributeSet;
25 | import android.view.View;
26 | import android.widget.MediaController;
27 |
28 | import java.util.ArrayList;
29 |
30 | public class AndroidMediaController extends MediaController implements IMediaController {
31 | private ActionBar mActionBar;
32 |
33 | public AndroidMediaController(Context context, AttributeSet attrs) {
34 | super(context, attrs);
35 | initView(context);
36 | }
37 |
38 | public AndroidMediaController(Context context, boolean useFastForward) {
39 | super(context, useFastForward);
40 | initView(context);
41 | }
42 |
43 | public AndroidMediaController(Context context) {
44 | super(context);
45 | initView(context);
46 | }
47 |
48 | private void initView(Context context) {
49 | }
50 |
51 | public void setSupportActionBar(@Nullable ActionBar actionBar) {
52 | mActionBar = actionBar;
53 | if (isShowing()) {
54 | actionBar.show();
55 | } else {
56 | actionBar.hide();
57 | }
58 | }
59 |
60 | @Override
61 | public void show() {
62 | super.show();
63 | if (mActionBar != null)
64 | mActionBar.show();
65 | }
66 |
67 | @Override
68 | public void hide() {
69 | super.hide();
70 | if (mActionBar != null)
71 | mActionBar.hide();
72 | for (View view : mShowOnceArray)
73 | view.setVisibility(View.GONE);
74 | mShowOnceArray.clear();
75 | }
76 |
77 | //----------
78 | // Extends
79 | //----------
80 | private ArrayList mShowOnceArray = new ArrayList();
81 |
82 | public void showOnce(@NonNull View view) {
83 | mShowOnceArray.add(view);
84 | view.setVisibility(View.VISIBLE);
85 | show();
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/wsqj_ijkplayer/src/main/java/com/ws/ijk/wsqj_ijkplayer/widget/media/IRenderView.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2015 Bilibili
3 | * Copyright (C) 2015 Zhang Rui
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.ws.ijk.wsqj_ijkplayer.widget.media;
19 |
20 | import android.graphics.SurfaceTexture;
21 | import android.support.annotation.NonNull;
22 | import android.support.annotation.Nullable;
23 | import android.view.Surface;
24 | import android.view.SurfaceHolder;
25 | import android.view.View;
26 |
27 | import tv.danmaku.ijk.media.player.IMediaPlayer;
28 |
29 | /**
30 | * Created by Wang.shuo on 2017/6/15.
31 | */
32 |
33 | public interface IRenderView {
34 | int AR_ASPECT_FIT_PARENT = 0; // without clip
35 | int AR_ASPECT_FILL_PARENT = 1; // may clip
36 | int AR_ASPECT_WRAP_CONTENT = 2;
37 | int AR_MATCH_PARENT = 3;
38 | int AR_16_9_FIT_PARENT = 4;
39 | int AR_4_3_FIT_PARENT = 5;
40 |
41 | View getView();
42 |
43 | boolean shouldWaitForResize();
44 |
45 | void setVideoSize(int videoWidth, int videoHeight);
46 |
47 | void setVideoSampleAspectRatio(int videoSarNum, int videoSarDen);
48 |
49 | void setVideoRotation(int degree);
50 |
51 | void setAspectRatio(int aspectRatio);
52 |
53 | void addRenderCallback(@NonNull IRenderCallback callback);
54 |
55 | void removeRenderCallback(@NonNull IRenderCallback callback);
56 |
57 | interface ISurfaceHolder {
58 | void bindToMediaPlayer(IMediaPlayer mp);
59 |
60 | @NonNull
61 | IRenderView getRenderView();
62 |
63 | @Nullable
64 | SurfaceHolder getSurfaceHolder();
65 |
66 | @Nullable
67 | Surface openSurface();
68 |
69 | @Nullable
70 | SurfaceTexture getSurfaceTexture();
71 | }
72 |
73 | interface IRenderCallback {
74 | /**
75 | * @param holder
76 | * @param width could be 0
77 | * @param height could be 0
78 | */
79 | void onSurfaceCreated(@NonNull ISurfaceHolder holder, int width, int height);
80 |
81 | /**
82 | * @param holder
83 | * @param format could be 0
84 | * @param width
85 | * @param height
86 | */
87 | void onSurfaceChanged(@NonNull ISurfaceHolder holder, int format, int width, int height);
88 |
89 | void onSurfaceDestroyed(@NonNull ISurfaceHolder holder);
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | C:\Users\shuo.wang\AppData\Roaming\Subversion
48 |
49 |
50 |
51 |
52 |
53 | 1.8
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/wsqj_ijkplayer/src/main/java/com/ws/ijk/wsqj_ijkplayer/widget/media/MediaPlayerCompat.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2015 Bilibili
3 | * Copyright (C) 2015 Zhang Rui
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.ws.ijk.wsqj_ijkplayer.widget.media;
19 |
20 | import tv.danmaku.ijk.media.player.IMediaPlayer;
21 | import tv.danmaku.ijk.media.player.IjkMediaPlayer;
22 | import tv.danmaku.ijk.media.player.MediaPlayerProxy;
23 | import tv.danmaku.ijk.media.player.TextureMediaPlayer;
24 |
25 | public class MediaPlayerCompat {
26 | public static String getName(IMediaPlayer mp) {
27 | if (mp == null) {
28 | return "null";
29 | } else if (mp instanceof TextureMediaPlayer) {
30 | StringBuilder sb = new StringBuilder("TextureMediaPlayer <");
31 | IMediaPlayer internalMediaPlayer = ((TextureMediaPlayer) mp).getInternalMediaPlayer();
32 | if (internalMediaPlayer == null) {
33 | sb.append("null>");
34 | } else {
35 | sb.append(internalMediaPlayer.getClass().getSimpleName());
36 | sb.append(">");
37 | }
38 | return sb.toString();
39 | } else {
40 | return mp.getClass().getSimpleName();
41 | }
42 | }
43 |
44 | public static IjkMediaPlayer getIjkMediaPlayer(IMediaPlayer mp) {
45 | IjkMediaPlayer ijkMediaPlayer = null;
46 | if (mp == null) {
47 | return null;
48 | } if (mp instanceof IjkMediaPlayer) {
49 | ijkMediaPlayer = (IjkMediaPlayer) mp;
50 | } else if (mp instanceof MediaPlayerProxy && ((MediaPlayerProxy) mp).getInternalMediaPlayer() instanceof IjkMediaPlayer) {
51 | ijkMediaPlayer = (IjkMediaPlayer) ((MediaPlayerProxy) mp).getInternalMediaPlayer();
52 | }
53 | return ijkMediaPlayer;
54 | }
55 |
56 | public static void selectTrack(IMediaPlayer mp, int stream) {
57 | IjkMediaPlayer ijkMediaPlayer = getIjkMediaPlayer(mp);
58 | if (ijkMediaPlayer == null)
59 | return;
60 | ijkMediaPlayer.selectTrack(stream);
61 | }
62 |
63 | public static void deselectTrack(IMediaPlayer mp, int stream) {
64 | IjkMediaPlayer ijkMediaPlayer = getIjkMediaPlayer(mp);
65 | if (ijkMediaPlayer == null)
66 | return;
67 | ijkMediaPlayer.deselectTrack(stream);
68 | }
69 |
70 | public static int getSelectedTrack(IMediaPlayer mp, int trackType) {
71 | IjkMediaPlayer ijkMediaPlayer = getIjkMediaPlayer(mp);
72 | if (ijkMediaPlayer == null)
73 | return -1;
74 | return ijkMediaPlayer.getSelectedTrack(trackType);
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/wsqj_ijkplayer/src/main/java/com/ws/ijk/wsqj_ijkplayer/widget/LianMai/LianMaiViewGroup.java:
--------------------------------------------------------------------------------
1 | package com.ws.ijk.wsqj_ijkplayer.widget.LianMai;
2 |
3 | import android.content.Context;
4 | import android.graphics.Color;
5 | import android.support.annotation.AttrRes;
6 | import android.support.annotation.NonNull;
7 | import android.support.annotation.Nullable;
8 | import android.support.v4.widget.ViewDragHelper;
9 | import android.util.AttributeSet;
10 | import android.view.MotionEvent;
11 | import android.view.View;
12 | import android.widget.FrameLayout;
13 |
14 | /**
15 | * Created by WangShuo on 2017/7/20.
16 | */
17 |
18 | public class LianMaiViewGroup extends FrameLayout {
19 |
20 | private ViewDragHelper mDragger;
21 | private View mLMvideoGroup;
22 |
23 | public LianMaiViewGroup(@NonNull Context context) {
24 | super(context);
25 | init();
26 | }
27 |
28 | public LianMaiViewGroup(@NonNull Context context, @Nullable AttributeSet attrs) {
29 | super(context, attrs);
30 | init();
31 | }
32 |
33 | public LianMaiViewGroup(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
34 | super(context, attrs, defStyleAttr);
35 | init();
36 | }
37 |
38 | private void init() {
39 | mDragger = ViewDragHelper.create(this, 1f, new MyHelperCallback());
40 | setBackgroundColor(Color.TRANSPARENT);
41 | }
42 |
43 |
44 |
45 | private class MyHelperCallback extends ViewDragHelper.Callback{
46 |
47 | @Override
48 | public boolean tryCaptureView(View child, int pointerId) {
49 | return true;
50 | }
51 |
52 | @Override
53 | public int clampViewPositionHorizontal(View child, int left, int dx)
54 | {
55 | final int leftBound = getPaddingLeft();
56 | final int rightBound = getWidth() - mLMvideoGroup.getWidth() - leftBound;
57 |
58 | final int newLeft = Math.min(Math.max(left, leftBound), rightBound);
59 |
60 | return newLeft;
61 | }
62 |
63 | @Override
64 | public int clampViewPositionVertical(View child, int top, int dy)
65 | {
66 | final int topBound = getPaddingTop();
67 | final int bottomBound = getHeight() - mLMvideoGroup.getHeight() - topBound;
68 |
69 | final int newTop = Math.min(Math.max(top, topBound), bottomBound);
70 | return newTop;
71 | }
72 |
73 | @Override
74 | public int getViewHorizontalDragRange(View child)
75 | {
76 | return getMeasuredWidth()-child.getMeasuredWidth();
77 | }
78 |
79 | @Override
80 | public int getViewVerticalDragRange(View child)
81 | {
82 | return getMeasuredHeight()-child.getMeasuredHeight();
83 | }
84 | }
85 |
86 | @Override
87 | public boolean onInterceptTouchEvent(MotionEvent event)
88 | {
89 | return mDragger.shouldInterceptTouchEvent(event);
90 | }
91 |
92 | @Override
93 | public boolean onTouchEvent(MotionEvent event)
94 | {
95 | mDragger.processTouchEvent(event);
96 | return true;
97 | }
98 |
99 | @Override
100 | public void computeScroll()
101 | {
102 | if(mDragger.continueSettling(true))
103 | {
104 | invalidate();
105 | }
106 | }
107 | @Override
108 | protected void onFinishInflate() {
109 | super.onFinishInflate();
110 | mLMvideoGroup=getChildAt(0);
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/wsqj_ijkplayer/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | wsqj_ijkplayer
3 |
4 | N/A
5 | Close
6 | Exit
7 | Sample
8 | Recent
9 | Settings
10 | Tracks
11 | Player
12 | Render
13 | Scale
14 | Info
15 | vdec
16 | fps
17 | v-cache
18 | a-cache
19 | load-cost
20 | seek_cost
21 | seek_load_cost
22 | tcp_speed
23 | bit_rate
24 |
25 | Media Information
26 | Player
27 | Media
28 | Profile level
29 | Pixel format
30 | Resolution
31 | Length
32 | Stream #%d
33 | Type
34 | Language
35 | Codec
36 | Frame rate
37 | Bit rate
38 | Sample rate
39 | Channels
40 | *
41 | *
42 | *
43 |
44 | Video
45 | Audio
46 | Subtitle
47 | Timed text
48 | Meta data
49 | Unknown
50 |
51 | Invalid progressive playback
52 | Unknown
53 | OK
54 |
55 | Aspect / Fit parent
56 | Aspect / Fill parent
57 | Aspect / Wrap content
58 | Free / Fill parent
59 | 16:9 / Fit parent
60 | 4:3 / Fit parent
61 |
62 | Render: None
63 | Render: SurfaceView
64 | Render: TextureView
65 |
66 | Player: None
67 | Player: AndroidMediaPlayer
68 | Player: IjkMediaPlayer
69 | Player: IjkExoMediaPlayer
70 |
71 |
--------------------------------------------------------------------------------
/wsqj_ijkplayer/src/main/java/com/ws/ijk/wsqj_ijkplayer/widget/LianMaiSplitScreen/AspectTextureView.java:
--------------------------------------------------------------------------------
1 | package com.ws.ijk.wsqj_ijkplayer.widget.LianMaiSplitScreen;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.TextureView;
6 |
7 | /**
8 | * Created by WangShuo on 2017/07/21.
9 | */
10 | public class AspectTextureView extends TextureView {
11 | public static final int MODE_FITXY = 0;
12 | public static final int MODE_INSIDE = 1;
13 | public static final int MODE_OUTSIDE = 2;
14 | private double targetAspect = -1;
15 | private int aspectMode = MODE_OUTSIDE;
16 |
17 | public AspectTextureView(Context context) {
18 | super(context);
19 | }
20 |
21 | public AspectTextureView(Context context, AttributeSet attrs) {
22 | super(context, attrs);
23 | }
24 |
25 | public AspectTextureView(Context context, AttributeSet attrs, int defStyleAttr) {
26 | super(context, attrs, defStyleAttr);
27 | }
28 |
29 | /**
30 | * @param mode {@link #MODE_FITXY},{@link #MODE_INSIDE},{@link #MODE_OUTSIDE}
31 | * @param aspectRatio width/height
32 | */
33 | public void setAspectRatio(int mode, double aspectRatio) {
34 | if (mode != MODE_INSIDE && mode != MODE_OUTSIDE && mode != MODE_FITXY) {
35 | throw new IllegalArgumentException("illegal mode");
36 | }
37 | if (aspectRatio < 0) {
38 | throw new IllegalArgumentException("illegal aspect ratio");
39 | }
40 | if (targetAspect != aspectRatio || aspectMode != mode) {
41 | targetAspect = aspectRatio;
42 | aspectMode = mode;
43 | requestLayout();
44 | }
45 | }
46 |
47 | @Override
48 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
49 | if (targetAspect > 0) {
50 | int initialWidth = MeasureSpec.getSize(widthMeasureSpec);
51 | int initialHeight = MeasureSpec.getSize(heightMeasureSpec);
52 |
53 | double viewAspectRatio = (double) initialWidth / initialHeight;
54 | double aspectDiff = targetAspect / viewAspectRatio - 1;
55 |
56 | if (Math.abs(aspectDiff) > 0.01 && aspectMode != MODE_FITXY) {
57 | if (aspectMode == MODE_INSIDE) {
58 | if (aspectDiff > 0) {
59 | initialHeight = (int) (initialWidth / targetAspect);
60 | } else {
61 | initialWidth = (int) (initialHeight * targetAspect);
62 | }
63 | } else if (aspectMode == MODE_OUTSIDE) {
64 | if (aspectDiff > 0) {
65 | initialWidth = (int) (initialHeight * targetAspect);
66 | } else {
67 | initialHeight = (int) (initialWidth / targetAspect);
68 | }
69 | }
70 | widthMeasureSpec = MeasureSpec.makeMeasureSpec(initialWidth, MeasureSpec.EXACTLY);
71 | heightMeasureSpec = MeasureSpec.makeMeasureSpec(initialHeight, MeasureSpec.EXACTLY);
72 | }
73 | }
74 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
75 | }
76 |
77 | /* @Override
78 | public void layout(int l, int t, int r, int b) {
79 | View p = (View) getParent();
80 | if (p != null) {
81 | int pw = p.getMeasuredWidth();
82 | int ph = p.getMeasuredHeight();
83 | int w = getMeasuredWidth();
84 | int h = getMeasuredHeight();
85 | t = (ph - h) / 2;
86 | l = (pw - w) / 2;
87 | r += l;
88 | b += t;
89 | }
90 | super.layout(l, t, r, b);
91 | }*/
92 | }
93 |
--------------------------------------------------------------------------------
/wsqj_ijkplayer/src/main/java/com/ws/ijk/wsqj_ijkplayer/widget/media/Settings.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2015 Bilibili
3 | * Copyright (C) 2015 Zhang Rui
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.ws.ijk.wsqj_ijkplayer.widget.media;
19 |
20 | import android.content.Context;
21 | import android.content.SharedPreferences;
22 | import android.preference.PreferenceManager;
23 |
24 | import com.ws.ijk.wsqj_ijkplayer.R;
25 |
26 | /**
27 | * Created by Wang.shuo on 2017/6/15.
28 | */
29 |
30 | public class Settings {
31 | private Context mAppContext;
32 | private SharedPreferences mSharedPreferences;
33 |
34 | public static final int PV_PLAYER__Auto = 0;
35 | public static final int PV_PLAYER__AndroidMediaPlayer = 1;
36 | public static final int PV_PLAYER__IjkMediaPlayer = 2;
37 | public static final int PV_PLAYER__IjkExoMediaPlayer = 3;
38 |
39 | public Settings(Context context) {
40 | mAppContext = context.getApplicationContext();
41 | mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(mAppContext);
42 | }
43 |
44 | public boolean getEnableBackgroundPlay() {
45 | String key = mAppContext.getString(R.string.pref_key_enable_background_play);
46 | return mSharedPreferences.getBoolean(key, false);
47 | }
48 |
49 | public int getPlayer() {
50 | String key = mAppContext.getString(R.string.pref_key_player);
51 | String value = mSharedPreferences.getString(key, "");
52 | try {
53 | return Integer.valueOf(value).intValue();
54 | } catch (NumberFormatException e) {
55 | return 0;
56 | }
57 | }
58 |
59 | public boolean getUsingMediaCodec() {
60 | String key = mAppContext.getString(R.string.pref_key_using_media_codec);
61 | return mSharedPreferences.getBoolean(key, false);
62 | }
63 |
64 | public boolean getUsingMediaCodecAutoRotate() {
65 | String key = mAppContext.getString(R.string.pref_key_using_media_codec_auto_rotate);
66 | return mSharedPreferences.getBoolean(key, false);
67 | }
68 |
69 | public boolean getMediaCodecHandleResolutionChange() {
70 | String key = mAppContext.getString(R.string.pref_key_media_codec_handle_resolution_change);
71 | return mSharedPreferences.getBoolean(key, false);
72 | }
73 |
74 | public boolean getUsingOpenSLES() {
75 | String key = mAppContext.getString(R.string.pref_key_using_opensl_es);
76 | return mSharedPreferences.getBoolean(key, false);
77 | }
78 |
79 | public String getPixelFormat() {
80 | String key = mAppContext.getString(R.string.pref_key_pixel_format);
81 | return mSharedPreferences.getString(key, "");
82 | }
83 |
84 | public boolean getEnableNoView() {
85 | String key = mAppContext.getString(R.string.pref_key_enable_no_view);
86 | return mSharedPreferences.getBoolean(key, false);
87 | }
88 |
89 | public boolean getEnableSurfaceView() {
90 | String key = mAppContext.getString(R.string.pref_key_enable_surface_view);
91 | return mSharedPreferences.getBoolean(key, false);
92 | }
93 |
94 | public boolean getEnableTextureView() {
95 | String key = mAppContext.getString(R.string.pref_key_enable_texture_view);
96 | return mSharedPreferences.getBoolean(key, false);
97 | }
98 |
99 | public boolean getEnableDetachedSurfaceTextureView() {
100 | String key = mAppContext.getString(R.string.pref_key_enable_detached_surface_texture);
101 | return mSharedPreferences.getBoolean(key, false);
102 | }
103 |
104 | public boolean getUsingMediaDataSource() {
105 | String key = mAppContext.getString(R.string.pref_key_using_mediadatasource);
106 | return mSharedPreferences.getBoolean(key, false);
107 | }
108 |
109 | public String getLastDirectory() {
110 | String key = mAppContext.getString(R.string.pref_key_last_directory);
111 | return mSharedPreferences.getString(key, "/");
112 | }
113 |
114 | public void setLastDirectory(String path) {
115 | String key = mAppContext.getString(R.string.pref_key_last_directory);
116 | mSharedPreferences.edit().putString(key, path).apply();
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/wsqj_ijkplayer/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.media_codec_handle_resolution_change
47 | MediaCodec handle resolution change
48 |
49 |
50 | pref.pixel_format
51 | Pixel Format
52 |
53 | - Auto Select
54 | - RGB 565
55 | - RGB 888
56 | - RGBX 8888
57 | - YV12
58 | - OpenGL ES2
59 |
60 |
61 |
62 | - fcc-rv16
63 | - fcc-rv24
64 | - fcc-rv32
65 | - fcc-yv12
66 | - fcc-_es2
67 |
68 |
69 | - Auto Select
70 | - RGB 565
71 | - RGB 888
72 | - RGBX 8888
73 | - YV12
74 | - OpenGL ES2
75 |
76 |
77 |
78 | Audio: ijkplayer
79 |
80 | pref.using_opensl_es
81 | Using OpenSL ES
82 |
83 |
84 |
85 | RenderView
86 |
87 | pref.enable_no_view
88 | Enable NoView
89 |
90 |
91 | pref.enable_surface_view
92 | Enable SurfaceView
93 |
94 |
95 | pref.enable_texture_view
96 | Enable TextureView
97 |
98 |
99 | pref.enable_detached_surface_texture
100 | Enable detached SurfaceTexture
101 |
102 |
103 |
104 | Misc
105 | pref.using_mediadatasource
106 | Using MediaDataSource
107 |
108 |
109 |
110 |
111 |
112 |
113 |
--------------------------------------------------------------------------------
/wsqj_ijkplayer/src/main/java/com/ws/ijk/wsqj_ijkplayer/widget/content/RecentMediaStorage.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2015 Bilibili
3 | * Copyright (C) 2015 Zhang Rui
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.ws.ijk.wsqj_ijkplayer.widget.content;
19 |
20 | import android.content.ContentValues;
21 | import android.content.Context;
22 | import android.database.Cursor;
23 | import android.database.sqlite.SQLiteDatabase;
24 | import android.database.sqlite.SQLiteOpenHelper;
25 | import android.os.AsyncTask;
26 | import android.support.v4.content.AsyncTaskLoader;
27 | import android.text.TextUtils;
28 |
29 | public class RecentMediaStorage {
30 | private Context mAppContext;
31 |
32 | public RecentMediaStorage(Context context) {
33 | mAppContext = context.getApplicationContext();
34 | }
35 |
36 | public void saveUrlAsync(String url) {
37 | new AsyncTask() {
38 | @Override
39 | protected Void doInBackground(String... params) {
40 | saveUrl(params[0]);
41 | return null;
42 | }
43 | }.execute(url);
44 | }
45 |
46 | public void saveUrl(String url) {
47 | ContentValues cv = new ContentValues();
48 | cv.putNull(Entry.COLUMN_NAME_ID);
49 | cv.put(Entry.COLUMN_NAME_URL, url);
50 | cv.put(Entry.COLUMN_NAME_LAST_ACCESS, System.currentTimeMillis());
51 | cv.put(Entry.COLUMN_NAME_NAME, getNameOfUrl(url));
52 | save(cv);
53 | }
54 |
55 | public void save(ContentValues contentValue) {
56 | OpenHelper openHelper = new OpenHelper(mAppContext);
57 | SQLiteDatabase db = openHelper.getWritableDatabase();
58 | db.replace(Entry.TABLE_NAME, null, contentValue);
59 | }
60 |
61 | public static String getNameOfUrl(String url) {
62 | return getNameOfUrl(url, "");
63 | }
64 |
65 | public static String getNameOfUrl(String url, String defaultName) {
66 | String name = null;
67 | int pos = url.lastIndexOf('/');
68 | if (pos >= 0)
69 | name = url.substring(pos + 1);
70 |
71 | if (TextUtils.isEmpty(name))
72 | name = defaultName;
73 |
74 | return name;
75 | }
76 |
77 | public static class Entry {
78 | public static final String TABLE_NAME = "RecentMedia";
79 | public static final String COLUMN_NAME_ID = "id";
80 | public static final String COLUMN_NAME_URL = "url";
81 | public static final String COLUMN_NAME_NAME = "name";
82 | public static final String COLUMN_NAME_LAST_ACCESS = "last_access";
83 | }
84 |
85 | public static final String ALL_COLUMNS[] = new String[]{
86 | Entry.COLUMN_NAME_ID + " as _id",
87 | Entry.COLUMN_NAME_ID,
88 | Entry.COLUMN_NAME_URL,
89 | Entry.COLUMN_NAME_NAME,
90 | Entry.COLUMN_NAME_LAST_ACCESS};
91 |
92 | public static class OpenHelper extends SQLiteOpenHelper {
93 | private static final int DATABASE_VERSION = 1;
94 | private static final String DATABASE_NAME = "RecentMedia.db";
95 | private static final String SQL_CREATE_ENTRIES =
96 | " CREATE TABLE IF NOT EXISTS " + Entry.TABLE_NAME + " (" +
97 | Entry.COLUMN_NAME_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
98 | Entry.COLUMN_NAME_URL + " VARCHAR UNIQUE, " +
99 | Entry.COLUMN_NAME_NAME + " VARCHAR, " +
100 | Entry.COLUMN_NAME_LAST_ACCESS + " INTEGER) ";
101 |
102 | public OpenHelper(Context context) {
103 | super(context, DATABASE_NAME, null, DATABASE_VERSION);
104 | }
105 |
106 | @Override
107 | public void onCreate(SQLiteDatabase db) {
108 | db.execSQL(SQL_CREATE_ENTRIES);
109 | }
110 |
111 | @Override
112 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
113 | }
114 | }
115 |
116 | public static class CursorLoader extends AsyncTaskLoader {
117 | public CursorLoader(Context context) {
118 | super(context);
119 | }
120 |
121 | @Override
122 | public Cursor loadInBackground() {
123 | Context context = getContext();
124 | OpenHelper openHelper = new OpenHelper(context);
125 | SQLiteDatabase db = openHelper.getReadableDatabase();
126 |
127 | return db.query(Entry.TABLE_NAME, ALL_COLUMNS, null, null, null, null,
128 | Entry.COLUMN_NAME_LAST_ACCESS + " DESC",
129 | "100");
130 | }
131 |
132 | @Override
133 | protected void onStartLoading() {
134 | forceLoad();
135 | }
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ws/ijk/ws_ijk/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.ws.ijk.ws_ijk;
2 |
3 | import android.os.Bundle;
4 | import android.support.v7.app.AppCompatActivity;
5 | import android.util.Log;
6 | import android.view.View;
7 | import android.widget.ImageView;
8 | import android.widget.Toast;
9 |
10 | import com.ws.ijk.wsqj_ijkplayer.widget.media.LiveIjkVideoView;
11 |
12 | import butterknife.ButterKnife;
13 | import tv.danmaku.ijk.media.player.IMediaPlayer;
14 |
15 | public class MainActivity extends AppCompatActivity implements IMediaPlayer.OnInfoListener, IMediaPlayer.OnPreparedListener, IMediaPlayer.OnCompletionListener, IMediaPlayer.OnErrorListener, View.OnClickListener {
16 |
17 | private LiveIjkVideoView liveIjkVideoView ,lmIjkVideoView;
18 | private ImageView imageView,lmCoverView,lmCloseView;
19 |
20 |
21 | @Override
22 | protected void onCreate(Bundle savedInstanceState) {
23 | super.onCreate(savedInstanceState);
24 | setContentView(R.layout.activity_main);
25 | ButterKnife.bind(this);
26 | initView();
27 | init();
28 |
29 | /* DisplayMetrics dm=new DisplayMetrics();
30 | getWindowManager().getDefaultDisplay().getMetrics(dm);
31 | String s="屏幕的分辨率为:"+dm.widthPixels+"*"+dm.heightPixels;
32 | Log.e("wangshuo",s);
33 |
34 | Camera.Size size = CameraUtil.getInstance().getBestSize(CameraUtil.getFrontCameraPreviewSize(),640);
35 |
36 | if(size==null){
37 | Log.e("wangshuo", "size==null");
38 | }else{
39 | Log.e("wangshuo","size : "+size.width+"*"+size.height);
40 | }
41 |
42 | Camera.Size size2 = CameraUtil.getInstance().getBestSize(CameraUtil.getFrontCameraVideoSize(),640);
43 | if(size2==null){
44 | Log.e("wangshuo", "size2==null");
45 | }else{
46 | Log.e("wangshuo","size : "+size2.width+"*"+size2.height);
47 | }
48 |
49 |
50 | boolean b = CameraUtil.isTablet(this);
51 | Log.e("wangshuo","b : "+b);*/
52 | }
53 |
54 |
55 |
56 | private void initView() {
57 | liveIjkVideoView= (LiveIjkVideoView) findViewById(R.id.ijkvideo_view);
58 | imageView = (ImageView) findViewById(R.id.live_play_coverview);
59 | liveIjkVideoView.setOnInfoListener(this);
60 | liveIjkVideoView.setOnPreparedListener(this);
61 | liveIjkVideoView.setOnCompletionListener(this);
62 | liveIjkVideoView.setOnErrorListener(this);
63 |
64 | //added LM start
65 | lmIjkVideoView = (LiveIjkVideoView) findViewById(R.id.lm_video_view);
66 | lmCoverView = (ImageView) findViewById(R.id.lm_play_coverview);
67 | lmCloseView = (ImageView) findViewById(R.id.lm_play_closeview);
68 | lmCloseView.setOnClickListener(this);
69 | //added LM end
70 | }
71 | private void init() {
72 | liveIjkVideoView.setVideoPath("http://9890.vod.myqcloud.com/9890_4e292f9a3dd011e6b4078980237cc3d3.f30.mp4");
73 | liveIjkVideoView.start();
74 |
75 |
76 | imageView.setImageResource(R.mipmap.timg);
77 | liveIjkVideoView.setCoverView(imageView);
78 |
79 | liveIjkVideoView.setRender(LiveIjkVideoView.RENDER_TEXTURE_VIEW);//这里不生效,是info监听自动改了
80 | liveIjkVideoView.setPlayerRotation(120);
81 |
82 | liveIjkVideoView.setDisplayAspectRatio(LiveIjkVideoView.s_allAspectRatio[1]);
83 |
84 |
85 | //added LM start
86 | lmIjkVideoView.setVideoPath("https://raw.githubusercontent.com/danikula/AndroidVideoCache/master/files/orange1.mp4");
87 | lmIjkVideoView.start();
88 |
89 | lmCoverView.setImageResource(R.mipmap.timg);
90 | lmIjkVideoView.setCoverView(lmCoverView);
91 | lmIjkVideoView.setDisplayAspectRatio(LiveIjkVideoView.s_allAspectRatio[1]);
92 | //added LM end
93 | }
94 |
95 | @Override
96 | protected void onStop() {
97 | super.onStop();
98 | liveIjkVideoView.stopPlayback();
99 | lmIjkVideoView.stopPlayback(); //added LM
100 | }
101 |
102 | @Override
103 | protected void onPause() {
104 | super.onPause();
105 | liveIjkVideoView.pause();
106 | lmIjkVideoView.pause(); //added LM
107 | }
108 |
109 | @Override
110 | protected void onResume() {
111 | super.onResume();
112 | liveIjkVideoView.start();
113 | lmIjkVideoView.start(); //added LM
114 |
115 |
116 | }
117 |
118 | @Override
119 | protected void onDestroy() {
120 | super.onDestroy();
121 | }
122 |
123 | @Override
124 | public boolean onInfo(IMediaPlayer iMediaPlayer, int i, int i1) {
125 | Log.e("wangshuo","onInfo= "+i +" onInfo= "+i1);
126 | return false;
127 | }
128 |
129 | @Override
130 | public void onPrepared(IMediaPlayer iMediaPlayer) {
131 |
132 | }
133 |
134 | @Override
135 | public void onCompletion(IMediaPlayer iMediaPlayer) {
136 |
137 | }
138 |
139 | @Override
140 | public boolean onError(IMediaPlayer iMediaPlayer, int i, int i1) {
141 | Log.e("wangshuo","onError= "+i +" onError= "+i1);
142 | return true;
143 | }
144 |
145 | @Override
146 | public void onClick(View view) {
147 | switch (view.getId()){
148 | case R.id.lm_play_closeview:
149 | Toast.makeText(MainActivity.this,"close",Toast.LENGTH_LONG).show();
150 | break;
151 | }
152 | }
153 | }
154 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ws/ijk/ws_ijk/FPLMActivity.java:
--------------------------------------------------------------------------------
1 | package com.ws.ijk.ws_ijk;
2 |
3 | import android.graphics.SurfaceTexture;
4 | import android.hardware.Camera;
5 | import android.os.Bundle;
6 | import android.support.v7.app.AppCompatActivity;
7 | import android.util.Log;
8 | import android.view.TextureView;
9 | import android.view.View;
10 | import android.view.ViewGroup;
11 | import android.widget.Button;
12 |
13 | import com.ws.ijk.wsqj_ijkplayer.widget.LianMaiSplitScreen.AspectTextureView;
14 | import com.ws.ijk.wsqj_ijkplayer.widget.LianMaiSplitScreen.LMVideoView;
15 |
16 | import java.io.IOException;
17 | import java.util.ArrayList;
18 |
19 | import butterknife.BindView;
20 | import butterknife.ButterKnife;
21 | import butterknife.OnClick;
22 |
23 | public class FPLMActivity extends AppCompatActivity implements TextureView.SurfaceTextureListener {
24 |
25 | private static final String TAG = "FPLMActivity";
26 |
27 | @BindView(R.id.txv_preview)
28 | AspectTextureView mTextureView;
29 | @BindView(R.id.LM)
30 | Button LM;
31 | @BindView(R.id.LMFlowGroupView)
32 | com.ws.ijk.wsqj_ijkplayer.widget.LianMaiSplitScreen.LMFlowGroupView LMFlowGroupView;
33 | @BindView(R.id.LMHF)
34 | Button LMHF;
35 |
36 | private Camera mCamera;
37 |
38 | private int mChildWidth;
39 | private int mChildHeight;
40 | private int mChildCount;
41 |
42 | private ArrayList LMVideoViewLists;
43 |
44 | @Override
45 | protected void onCreate(Bundle savedInstanceState) {
46 | super.onCreate(savedInstanceState);
47 | setContentView(R.layout.activity_fplm);
48 | ButterKnife.bind(this);
49 |
50 | mTextureView.setSurfaceTextureListener(this);
51 | LMVideoViewLists = new ArrayList();
52 | }
53 |
54 |
55 | @OnClick({R.id.LM, R.id.LMHF})
56 | public void onViewClicked(View view) {
57 | switch (view.getId()) {
58 | case R.id.LM:
59 | addLMVideoView();
60 | break;
61 | case R.id.LMHF:
62 | recoverLive();
63 | break;
64 |
65 | }
66 |
67 | }
68 |
69 | private void recoverLive() {
70 | for(LMVideoView lv :LMVideoViewLists){
71 | LMFlowGroupView.removeView(lv);
72 | }
73 | ViewGroup.LayoutParams params = mTextureView.getLayoutParams();
74 | params.width = ViewGroup.LayoutParams.MATCH_PARENT;
75 | params.height = ViewGroup.LayoutParams.MATCH_PARENT;
76 | mTextureView.setLayoutParams(params);
77 | }
78 |
79 | private void addLMVideoView() {
80 | LMVideoView lmVideoView = new LMVideoView(this);
81 | LMFlowGroupView.addView(lmVideoView);
82 | LMVideoViewLists.add(lmVideoView);
83 |
84 | lmVideoView.getLMijkVideoView().setVideoPath("http://9890.vod.myqcloud.com/9890_4e292f9a3dd011e6b4078980237cc3d3.f30.mp4");
85 | lmVideoView.getLMijkVideoView().start();
86 | lmVideoView.getCoverView().setImageResource(R.mipmap.timg);
87 |
88 | updateLMVideoViewLayout(lmVideoView);
89 | updateAspectTextureLayout(mTextureView);
90 | }
91 |
92 | private void updateLMVideoViewLayout(LMVideoView view) {
93 | getLMFlowViewData();
94 | ViewGroup.LayoutParams params = view.getLayoutParams();
95 | params.width = mChildWidth;
96 | params.height = mChildHeight;
97 | view.setLayoutParams(params);
98 | }
99 |
100 |
101 | private void updateAspectTextureLayout(AspectTextureView view) {
102 | getLMFlowViewData();
103 | ViewGroup.LayoutParams params = view.getLayoutParams();
104 | params.width = mChildWidth;
105 | params.height = mChildHeight;
106 | view.setLayoutParams(params);
107 | }
108 |
109 |
110 | private void getLMFlowViewData() {
111 | mChildCount = LMFlowGroupView.getChildCount();
112 | mChildWidth = LMFlowGroupView.getWidth() / mChildCount;
113 | mChildHeight = (int) (mChildWidth * 1.6);
114 | Log.e("wangshuo", "mChildCount = " + mChildCount);
115 | }
116 |
117 |
118 | @Override
119 | public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
120 | mCamera = Camera.open(1);
121 | if (mCamera == null) {
122 | // Seeing this on Nexus 7 2012 -- I guess it wants a rear-facing camera, but
123 | // there isn't one. TODO: fix
124 | throw new RuntimeException("Default camera not available");
125 | }
126 |
127 | try {
128 | mCamera.setPreviewTexture(surface);
129 | mCamera.setDisplayOrientation(90);//将相机试图旋转90度,因为默认是横屏显示
130 | mCamera.startPreview();
131 | } catch (IOException ioe) {
132 | // Something bad happened
133 | }
134 | }
135 |
136 | @Override
137 | public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
138 | // Ignored, Camera does all the work for us
139 | }
140 |
141 | @Override
142 | public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
143 | mCamera.stopPreview();
144 | mCamera.release();
145 | return true;
146 | }
147 |
148 | @Override
149 | public void onSurfaceTextureUpdated(SurfaceTexture surface) {
150 | // Invoked every time there's a new Camera preview frame
151 | //Log.d(TAG, "updated, ts=" + surface.getTimestamp());
152 | }
153 |
154 |
155 | }
156 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/wsqj_ijkplayer/src/main/java/com/ws/ijk/wsqj_ijkplayer/widget/content/PathCursor.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2015 Bilibili
3 | * Copyright (C) 2015 Zhang Rui
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.ws.ijk.wsqj_ijkplayer.widget.content;
19 |
20 | import android.database.AbstractCursor;
21 | import android.provider.BaseColumns;
22 | import android.text.TextUtils;
23 |
24 | import java.io.File;
25 | import java.util.ArrayList;
26 | import java.util.Collections;
27 | import java.util.Comparator;
28 | import java.util.List;
29 | import java.util.Set;
30 | import java.util.TreeSet;
31 |
32 | public class PathCursor extends AbstractCursor {
33 | private List mFileList = new ArrayList<>();
34 |
35 | public static final String CN_ID = BaseColumns._ID;
36 | public static final String CN_FILE_NAME = "file_name";
37 | public static final String CN_FILE_PATH = "file_path";
38 | public static final String CN_IS_DIRECTORY = "is_directory";
39 | public static final String CN_IS_VIDEO = "is_video";
40 | public static final String[] columnNames = new String[]{CN_ID, CN_FILE_NAME, CN_FILE_PATH, CN_IS_DIRECTORY, CN_IS_VIDEO};
41 | public static final int CI_ID = 0;
42 | public static final int CI_FILE_NAME = 1;
43 | public static final int CI_FILE_PATH = 2;
44 | public static final int CI_IS_DIRECTORY = 3;
45 | public static final int CI_IS_VIDEO = 4;
46 |
47 | PathCursor(File parentDirectory, File[] fileList) {
48 | if (parentDirectory.getParent() != null) {
49 | FileItem parentFile = new FileItem(new File(parentDirectory, ".."));
50 | parentFile.isDirectory = true;
51 | mFileList.add(parentFile);
52 | }
53 |
54 | if (fileList != null) {
55 | for (File file : fileList) {
56 | mFileList.add(new FileItem(file));
57 | }
58 | Collections.sort(this.mFileList, sComparator);
59 | }
60 | }
61 |
62 | @Override
63 | public int getCount() {
64 | return mFileList.size();
65 | }
66 |
67 | @Override
68 | public String[] getColumnNames() {
69 | return columnNames;
70 | }
71 |
72 | @Override
73 | public String getString(int column) {
74 | switch (column) {
75 | case CI_FILE_NAME:
76 | return mFileList.get(getPosition()).file.getName();
77 | case CI_FILE_PATH:
78 | return mFileList.get(getPosition()).file.toString();
79 | }
80 | return null;
81 | }
82 |
83 | @Override
84 | public short getShort(int column) {
85 | return (short) getLong(column);
86 | }
87 |
88 | @Override
89 | public int getInt(int column) {
90 | return (int) getLong(column);
91 | }
92 |
93 | @Override
94 | public long getLong(int column) {
95 | switch (column) {
96 | case CI_ID:
97 | return getPosition();
98 | case CI_IS_DIRECTORY:
99 | return mFileList.get(getPosition()).isDirectory ? 1 : 0;
100 | case CI_IS_VIDEO:
101 | return mFileList.get(getPosition()).isVideo ? 1 : 0;
102 | }
103 | return 0;
104 | }
105 |
106 | @Override
107 | public float getFloat(int column) {
108 | return 0;
109 | }
110 |
111 | @Override
112 | public double getDouble(int column) {
113 | return 0;
114 | }
115 |
116 | @Override
117 | public boolean isNull(int column) {
118 | return mFileList == null;
119 | }
120 |
121 | public static Comparator sComparator = new Comparator() {
122 | @Override
123 | public int compare(FileItem lhs, FileItem rhs) {
124 | if (lhs.isDirectory && !rhs.isDirectory)
125 | return -1;
126 | else if (!lhs.isDirectory && rhs.isDirectory)
127 | return 1;
128 | return lhs.file.getName().compareToIgnoreCase(rhs.file.getName());
129 | }
130 | };
131 |
132 | private static Set sMediaExtSet = new TreeSet(String.CASE_INSENSITIVE_ORDER);
133 |
134 | static {
135 | sMediaExtSet.add("flv");
136 | sMediaExtSet.add("mp4");
137 | }
138 |
139 | private class FileItem {
140 | public File file;
141 | public boolean isDirectory;
142 | public boolean isVideo;
143 |
144 | public FileItem(String file) {
145 | this(new File(file));
146 | }
147 |
148 | public FileItem(File file) {
149 | this.file = file;
150 | this.isDirectory = file.isDirectory();
151 |
152 | String fileName = file.getName();
153 | if (!TextUtils.isEmpty(fileName)) {
154 | int extPos = fileName.lastIndexOf('.');
155 | if (extPos >= 0) {
156 | String ext = fileName.substring(extPos + 1);
157 | if (!TextUtils.isEmpty(ext) && sMediaExtSet.contains(ext)) {
158 | this.isVideo = true;
159 | }
160 | }
161 | }
162 | }
163 | }
164 | }
165 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ws/ijk/ws_ijk/CameraUtil.java:
--------------------------------------------------------------------------------
1 | package com.ws.ijk.ws_ijk;
2 |
3 | import android.content.Context;
4 | import android.content.res.Configuration;
5 | import android.hardware.Camera;
6 | import android.hardware.Camera.Size;
7 | import android.util.Log;
8 |
9 | import java.util.Collections;
10 | import java.util.Comparator;
11 | import java.util.List;
12 |
13 | /**
14 | * Created by WangShuo on 2017/6/30.
15 | */
16 |
17 | public class CameraUtil {
18 | private static final String tag = "wangshuo";
19 | private CameraSizeComparator sizeComparator = new CameraSizeComparator();
20 | private static CameraUtil cameraUtil = null;
21 | private CameraUtil(){
22 |
23 | }
24 | public static CameraUtil getInstance(){
25 | if(cameraUtil == null){
26 | cameraUtil = new CameraUtil();
27 | return cameraUtil;
28 | }
29 | else{
30 | return cameraUtil;
31 | }
32 | }
33 |
34 | public Size getBestSize(List list, int th){
35 | if(list == null || list.size() < 1){
36 | return null;
37 | }
38 | boolean bool= false;
39 |
40 | Collections.sort(list, sizeComparator);
41 | int i = 0;
42 | for(Size s:list){
43 | if((s.width >= th) && equalRate(s, 1.3333f)){
44 | Log.i(tag, "最终设置Video尺寸:w = " + s.width + "h = " + s.height);
45 | bool = true;
46 | break;
47 | }
48 | i++;
49 | }
50 | if(bool){
51 | return list.get(i);
52 | }
53 | return null;
54 | }
55 |
56 | public Size getPictureSize(List list, int th){
57 | Collections.sort(list, sizeComparator);
58 |
59 | int i = 0;
60 | for(Size s:list){
61 | if((s.width >= th) && equalRate(s, 1.333f)){
62 | Log.i(tag, "最终设置图片尺寸:w = " + s.width + "h = " + s.height);
63 | break;
64 | }
65 | i++;
66 | }
67 |
68 | return list.get(i);
69 | }
70 |
71 | public boolean equalRate(Size s, float rate){
72 | float r = (float)(s.width)/(float)(s.height);
73 | if(Math.abs(r - rate) <= 0.2)
74 | {
75 | return true;
76 | }
77 | else{
78 | return false;
79 | }
80 | }
81 |
82 | public class CameraSizeComparator implements Comparator {
83 | //按升序排列
84 | public int compare(Size lhs, Size rhs) {
85 | // TODO Auto-generated method stub
86 | if(lhs.width == rhs.width){
87 | return 0;
88 | }
89 | else if(lhs.width > rhs.width){
90 | return 1;
91 | }
92 | else{
93 | return -1;
94 | }
95 | }
96 |
97 | }
98 |
99 | public static List getBackCameraPreviewSize(){
100 | Camera back = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK);
101 | List backSizeList = back.getParameters().getSupportedPreviewSizes();
102 | back.release();
103 | return backSizeList;
104 | }
105 |
106 | public static List getFrontCameraPreviewSize(){
107 | Camera front = Camera.open(Camera.CameraInfo.CAMERA_FACING_FRONT);
108 | List frontSizeList = front.getParameters().getSupportedPreviewSizes();
109 | //added by log
110 | StringBuilder str = new StringBuilder();
111 | str.append("如使用前置摄像头您可以输入的宽度有Preview:");
112 | for (Camera.Size fSize : frontSizeList) {
113 | str.append(fSize.width+"*"+fSize.height + "、;");
114 | }
115 | Log.e(tag,str.toString());
116 | //added by log
117 | front.release();
118 | return frontSizeList;
119 | }
120 |
121 | public static List getBackCameraVideoSize(){
122 | Camera back = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK);
123 | List backSizeList = back.getParameters().getSupportedVideoSizes();
124 | if(backSizeList == null){
125 | return null;
126 | }
127 | back.release();
128 | return backSizeList;
129 | }
130 |
131 | public static List getFrontCameraVideoSize(){
132 | Camera front = Camera.open(Camera.CameraInfo.CAMERA_FACING_FRONT);
133 | List frontSizeList = front.getParameters().getSupportedVideoSizes();
134 | if(frontSizeList == null){
135 | return null;
136 | }
137 | //added by log
138 | StringBuilder str = new StringBuilder();
139 | str.append("如使用前置摄像头您可以输入的宽度有Video:");
140 | for (Camera.Size fSize : frontSizeList) {
141 | str.append(fSize.width+"*"+fSize.height + "、;");
142 | }
143 | Log.e(tag,str.toString());
144 | //added by log
145 | front.release();
146 | return frontSizeList;
147 | }
148 |
149 | public static List getFrontCameraSize(){
150 | Camera front = Camera.open(Camera.CameraInfo.CAMERA_FACING_FRONT);
151 | List frontSizeList = front.getParameters().getSupportedVideoSizes();
152 | if(null == frontSizeList || frontSizeList.size()<=0){
153 | frontSizeList = front.getParameters().getSupportedPreviewSizes();
154 | Log.e(tag,"getSupportedVideoSizes==null");
155 | }
156 | //added by log
157 | StringBuilder str = new StringBuilder();
158 | str.append("如使用前置摄像头您可以输入的宽度有Preview:");
159 | for (Camera.Size fSize : frontSizeList) {
160 | str.append(fSize.width+"*"+fSize.height + "、;");
161 | }
162 | Log.e(tag,str.toString());
163 | //added by log
164 | front.release();
165 | return frontSizeList;
166 | }
167 |
168 |
169 | /**
170 | * 判断当前设备是手机还是平板,代码来自 Google I/O App for Android
171 | * @param context
172 | * @return 平板返回 True,手机返回 False
173 | */
174 | public static boolean isTablet(Context context) {
175 | return (context.getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE;
176 | }
177 | }
178 |
--------------------------------------------------------------------------------
/wsqj_ijkplayer/src/main/java/com/ws/ijk/wsqj_ijkplayer/widget/media/SurfaceRenderView.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2015 Bilibili
3 | * Copyright (C) 2015 Zhang Rui
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.ws.ijk.wsqj_ijkplayer.widget.media;
19 |
20 | import android.annotation.TargetApi;
21 | import android.content.Context;
22 | import android.graphics.PixelFormat;
23 | import android.graphics.SurfaceTexture;
24 | import android.os.Build;
25 | import android.support.annotation.NonNull;
26 | import android.support.annotation.Nullable;
27 | import android.util.AttributeSet;
28 | import android.util.Log;
29 | import android.view.Surface;
30 | import android.view.SurfaceHolder;
31 | import android.view.SurfaceView;
32 | import android.view.View;
33 | import android.view.accessibility.AccessibilityEvent;
34 | import android.view.accessibility.AccessibilityNodeInfo;
35 |
36 | import java.lang.ref.WeakReference;
37 | import java.util.Map;
38 | import java.util.concurrent.ConcurrentHashMap;
39 |
40 | import tv.danmaku.ijk.media.player.IMediaPlayer;
41 | import tv.danmaku.ijk.media.player.ISurfaceTextureHolder;
42 |
43 | /**
44 | * Created by Wang.shuo on 2017/6/15.
45 | */
46 |
47 | public class SurfaceRenderView extends SurfaceView implements IRenderView {
48 | private MeasureHelper mMeasureHelper;
49 |
50 | public SurfaceRenderView(Context context) {
51 | super(context);
52 | initView(context);
53 | }
54 |
55 | public SurfaceRenderView(Context context, AttributeSet attrs) {
56 | super(context, attrs);
57 | initView(context);
58 | }
59 |
60 | public SurfaceRenderView(Context context, AttributeSet attrs, int defStyleAttr) {
61 | super(context, attrs, defStyleAttr);
62 | initView(context);
63 | }
64 |
65 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
66 | public SurfaceRenderView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
67 | super(context, attrs, defStyleAttr, defStyleRes);
68 | initView(context);
69 | }
70 |
71 | private void initView(Context context) {
72 | mMeasureHelper = new MeasureHelper(this);
73 | mSurfaceCallback = new SurfaceCallback(this);
74 | getHolder().addCallback(mSurfaceCallback);
75 | //noinspection deprecation
76 | getHolder().setType(SurfaceHolder.SURFACE_TYPE_NORMAL);
77 | getHolder().setFormat(PixelFormat.TRANSLUCENT);//added by wangshuo for Flash black
78 | }
79 |
80 | @Override
81 | public View getView() {
82 | return this;
83 | }
84 |
85 | @Override
86 | public boolean shouldWaitForResize() {
87 | return true;
88 | }
89 |
90 | //--------------------
91 | // Layout & Measure
92 | //--------------------
93 | @Override
94 | public void setVideoSize(int videoWidth, int videoHeight) {
95 | if (videoWidth > 0 && videoHeight > 0) {
96 | mMeasureHelper.setVideoSize(videoWidth, videoHeight);
97 | getHolder().setFixedSize(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 | Log.e("", "SurfaceView doesn't support rotation (" + degree + ")!\n");
113 | }
114 |
115 | @Override
116 | public void setAspectRatio(int aspectRatio) {
117 | mMeasureHelper.setAspectRatio(aspectRatio);
118 | requestLayout();
119 | }
120 |
121 | @Override
122 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
123 | mMeasureHelper.doMeasure(widthMeasureSpec, heightMeasureSpec);
124 | setMeasuredDimension(mMeasureHelper.getMeasuredWidth(), mMeasureHelper.getMeasuredHeight());
125 | }
126 |
127 | //--------------------
128 | // SurfaceViewHolder
129 | //--------------------
130 |
131 | private static final class InternalSurfaceHolder implements IRenderView.ISurfaceHolder {
132 | private SurfaceRenderView mSurfaceView;
133 | private SurfaceHolder mSurfaceHolder;
134 |
135 | public InternalSurfaceHolder(@NonNull SurfaceRenderView surfaceView,
136 | @Nullable SurfaceHolder surfaceHolder) {
137 | mSurfaceView = surfaceView;
138 | mSurfaceHolder = surfaceHolder;
139 | }
140 |
141 | public void bindToMediaPlayer(IMediaPlayer mp) {
142 | if (mp != null) {
143 | if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) &&
144 | (mp instanceof ISurfaceTextureHolder)) {
145 | ISurfaceTextureHolder textureHolder = (ISurfaceTextureHolder) mp;
146 | textureHolder.setSurfaceTexture(null);
147 | }
148 | mp.setDisplay(mSurfaceHolder);
149 | }
150 | }
151 |
152 | @NonNull
153 | @Override
154 | public IRenderView getRenderView() {
155 | return mSurfaceView;
156 | }
157 |
158 | @Nullable
159 | @Override
160 | public SurfaceHolder getSurfaceHolder() {
161 | return mSurfaceHolder;
162 | }
163 |
164 | @Nullable
165 | @Override
166 | public SurfaceTexture getSurfaceTexture() {
167 | return null;
168 | }
169 |
170 | @Nullable
171 | @Override
172 | public Surface openSurface() {
173 | if (mSurfaceHolder == null)
174 | return null;
175 | return mSurfaceHolder.getSurface();
176 | }
177 | }
178 |
179 | //-------------------------
180 | // SurfaceHolder.Callback
181 | //-------------------------
182 |
183 | @Override
184 | public void addRenderCallback(IRenderCallback callback) {
185 | mSurfaceCallback.addRenderCallback(callback);
186 | }
187 |
188 | @Override
189 | public void removeRenderCallback(IRenderCallback callback) {
190 | mSurfaceCallback.removeRenderCallback(callback);
191 | }
192 |
193 | private SurfaceCallback mSurfaceCallback;
194 |
195 | private static final class SurfaceCallback implements SurfaceHolder.Callback {
196 | private SurfaceHolder mSurfaceHolder;
197 | private boolean mIsFormatChanged;
198 | private int mFormat;
199 | private int mWidth;
200 | private int mHeight;
201 |
202 | private WeakReference mWeakSurfaceView;
203 | private Map mRenderCallbackMap = new ConcurrentHashMap();
204 |
205 | public SurfaceCallback(@NonNull SurfaceRenderView surfaceView) {
206 | mWeakSurfaceView = new WeakReference(surfaceView);
207 | }
208 |
209 | public void addRenderCallback(@NonNull IRenderCallback callback) {
210 | mRenderCallbackMap.put(callback, callback);
211 |
212 | ISurfaceHolder surfaceHolder = null;
213 | if (mSurfaceHolder != null) {
214 | if (surfaceHolder == null)
215 | surfaceHolder = new InternalSurfaceHolder(mWeakSurfaceView.get(), mSurfaceHolder);
216 | callback.onSurfaceCreated(surfaceHolder, mWidth, mHeight);
217 | }
218 |
219 | if (mIsFormatChanged) {
220 | if (surfaceHolder == null)
221 | surfaceHolder = new InternalSurfaceHolder(mWeakSurfaceView.get(), mSurfaceHolder);
222 | callback.onSurfaceChanged(surfaceHolder, mFormat, mWidth, mHeight);
223 | }
224 | }
225 |
226 | public void removeRenderCallback(@NonNull IRenderCallback callback) {
227 | mRenderCallbackMap.remove(callback);
228 | }
229 |
230 | @Override
231 | public void surfaceCreated(SurfaceHolder holder) {
232 | mSurfaceHolder = holder;
233 | mIsFormatChanged = false;
234 | mFormat = 0;
235 | mWidth = 0;
236 | mHeight = 0;
237 |
238 | ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakSurfaceView.get(), mSurfaceHolder);
239 | for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) {
240 | renderCallback.onSurfaceCreated(surfaceHolder, 0, 0);
241 | }
242 | }
243 |
244 | @Override
245 | public void surfaceDestroyed(SurfaceHolder holder) {
246 | mSurfaceHolder = null;
247 | mIsFormatChanged = false;
248 | mFormat = 0;
249 | mWidth = 0;
250 | mHeight = 0;
251 |
252 | ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakSurfaceView.get(), mSurfaceHolder);
253 | for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) {
254 | renderCallback.onSurfaceDestroyed(surfaceHolder);
255 | }
256 | }
257 |
258 | @Override
259 | public void surfaceChanged(SurfaceHolder holder, int format,
260 | int width, int height) {
261 | mSurfaceHolder = holder;
262 | mIsFormatChanged = true;
263 | mFormat = format;
264 | mWidth = width;
265 | mHeight = height;
266 |
267 | // mMeasureHelper.setVideoSize(width, height);
268 |
269 | ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakSurfaceView.get(), mSurfaceHolder);
270 | for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) {
271 | renderCallback.onSurfaceChanged(surfaceHolder, format, width, height);
272 | }
273 | }
274 | }
275 |
276 | //--------------------
277 | // Accessibility
278 | //--------------------
279 |
280 | @Override
281 | public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
282 | super.onInitializeAccessibilityEvent(event);
283 | event.setClassName(SurfaceRenderView.class.getName());
284 | }
285 |
286 | @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
287 | @Override
288 | public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
289 | super.onInitializeAccessibilityNodeInfo(info);
290 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
291 | info.setClassName(SurfaceRenderView.class.getName());
292 | }
293 | }
294 | }
295 |
--------------------------------------------------------------------------------
/wsqj_ijkplayer/src/main/java/com/ws/ijk/wsqj_ijkplayer/widget/media/MeasureHelper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2015 Bilibili
3 | * Copyright (C) 2015 Zhang Rui
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.ws.ijk.wsqj_ijkplayer.widget.media;
19 |
20 | import android.content.Context;
21 | import android.support.annotation.NonNull;
22 | import android.view.View;
23 |
24 | import com.ws.ijk.wsqj_ijkplayer.R;
25 |
26 | import java.lang.ref.WeakReference;
27 |
28 |
29 |
30 | public final class MeasureHelper {
31 | private WeakReference mWeakView;
32 |
33 | private int mVideoWidth;
34 | private int mVideoHeight;
35 | private int mVideoSarNum;
36 | private int mVideoSarDen;
37 |
38 | private int mVideoRotationDegree;
39 |
40 | private int mMeasuredWidth;
41 | private int mMeasuredHeight;
42 |
43 | private int mCurrentAspectRatio = IRenderView.AR_ASPECT_FIT_PARENT;
44 |
45 | public MeasureHelper(View view) {
46 | mWeakView = new WeakReference(view);
47 | }
48 |
49 | public View getView() {
50 | if (mWeakView == null)
51 | return null;
52 | return mWeakView.get();
53 | }
54 |
55 | public void setVideoSize(int videoWidth, int videoHeight) {
56 | mVideoWidth = videoWidth;
57 | mVideoHeight = videoHeight;
58 | }
59 |
60 | public void setVideoSampleAspectRatio(int videoSarNum, int videoSarDen) {
61 | mVideoSarNum = videoSarNum;
62 | mVideoSarDen = videoSarDen;
63 | }
64 |
65 | public void setVideoRotation(int videoRotationDegree) {
66 | mVideoRotationDegree = videoRotationDegree;
67 | }
68 |
69 | /**
70 | * Must be called by View.onMeasure(int, int)
71 | *
72 | * @param widthMeasureSpec
73 | * @param heightMeasureSpec
74 | */
75 | public void doMeasure(int widthMeasureSpec, int heightMeasureSpec) {
76 | //Log.i("@@@@", "onMeasure(" + MeasureSpec.toString(widthMeasureSpec) + ", "
77 | // + MeasureSpec.toString(heightMeasureSpec) + ")");
78 | if (mVideoRotationDegree == 90 || mVideoRotationDegree == 270) {
79 | int tempSpec = widthMeasureSpec;
80 | widthMeasureSpec = heightMeasureSpec;
81 | heightMeasureSpec = tempSpec;
82 | }
83 |
84 | int width = View.getDefaultSize(mVideoWidth, widthMeasureSpec);
85 | int height = View.getDefaultSize(mVideoHeight, heightMeasureSpec);
86 | if (mCurrentAspectRatio == IRenderView.AR_MATCH_PARENT) {
87 | width = widthMeasureSpec;
88 | height = heightMeasureSpec;
89 | } else if (mVideoWidth > 0 && mVideoHeight > 0) {
90 | int widthSpecMode = View.MeasureSpec.getMode(widthMeasureSpec);
91 | int widthSpecSize = View.MeasureSpec.getSize(widthMeasureSpec);
92 | int heightSpecMode = View.MeasureSpec.getMode(heightMeasureSpec);
93 | int heightSpecSize = View.MeasureSpec.getSize(heightMeasureSpec);
94 |
95 | if (widthSpecMode == View.MeasureSpec.AT_MOST && heightSpecMode == View.MeasureSpec.AT_MOST) {
96 | float specAspectRatio = (float) widthSpecSize / (float) heightSpecSize;
97 | float displayAspectRatio;
98 | switch (mCurrentAspectRatio) {
99 | case IRenderView.AR_16_9_FIT_PARENT:
100 | displayAspectRatio = 16.0f / 9.0f;
101 | if (mVideoRotationDegree == 90 || mVideoRotationDegree == 270)
102 | displayAspectRatio = 1.0f / displayAspectRatio;
103 | break;
104 | case IRenderView.AR_4_3_FIT_PARENT:
105 | displayAspectRatio = 4.0f / 3.0f;
106 | if (mVideoRotationDegree == 90 || mVideoRotationDegree == 270)
107 | displayAspectRatio = 1.0f / displayAspectRatio;
108 | break;
109 | case IRenderView.AR_ASPECT_FIT_PARENT:
110 | case IRenderView.AR_ASPECT_FILL_PARENT:
111 | case IRenderView.AR_ASPECT_WRAP_CONTENT:
112 | default:
113 | displayAspectRatio = (float) mVideoWidth / (float) mVideoHeight;
114 | if (mVideoSarNum > 0 && mVideoSarDen > 0)
115 | displayAspectRatio = displayAspectRatio * mVideoSarNum / mVideoSarDen;
116 | break;
117 | }
118 | boolean shouldBeWider = displayAspectRatio > specAspectRatio;
119 |
120 | switch (mCurrentAspectRatio) {
121 | case IRenderView.AR_ASPECT_FIT_PARENT:
122 | case IRenderView.AR_16_9_FIT_PARENT:
123 | case IRenderView.AR_4_3_FIT_PARENT:
124 | if (shouldBeWider) {
125 | // too wide, fix width
126 | width = widthSpecSize;
127 | height = (int) (width / displayAspectRatio);
128 | } else {
129 | // too high, fix height
130 | height = heightSpecSize;
131 | width = (int) (height * displayAspectRatio);
132 | }
133 | break;
134 | case IRenderView.AR_ASPECT_FILL_PARENT:
135 | if (shouldBeWider) {
136 | // not high enough, fix height
137 | height = heightSpecSize;
138 | width = (int) (height * displayAspectRatio);
139 | } else {
140 | // not wide enough, fix width
141 | width = widthSpecSize;
142 | height = (int) (width / displayAspectRatio);
143 | }
144 | break;
145 | case IRenderView.AR_ASPECT_WRAP_CONTENT:
146 | default:
147 | if (shouldBeWider) {
148 | // too wide, fix width
149 | width = Math.min(mVideoWidth, widthSpecSize);
150 | height = (int) (width / displayAspectRatio);
151 | } else {
152 | // too high, fix height
153 | height = Math.min(mVideoHeight, heightSpecSize);
154 | width = (int) (height * displayAspectRatio);
155 | }
156 | break;
157 | }
158 | } else if (widthSpecMode == View.MeasureSpec.EXACTLY && heightSpecMode == View.MeasureSpec.EXACTLY) {
159 | // the size is fixed
160 | width = widthSpecSize;
161 | height = heightSpecSize;
162 |
163 | // for compatibility, we adjust size based on aspect ratio
164 | if (mVideoWidth * height < width * mVideoHeight) {
165 | //Log.i("@@@", "image too wide, correcting");
166 | width = height * mVideoWidth / mVideoHeight;
167 | } else if (mVideoWidth * height > width * mVideoHeight) {
168 | //Log.i("@@@", "image too tall, correcting");
169 | height = width * mVideoHeight / mVideoWidth;
170 | }
171 | } else if (widthSpecMode == View.MeasureSpec.EXACTLY) {
172 | // only the width is fixed, adjust the height to match aspect ratio if possible
173 | width = widthSpecSize;
174 | height = width * mVideoHeight / mVideoWidth;
175 | if (heightSpecMode == View.MeasureSpec.AT_MOST && height > heightSpecSize) {
176 | // couldn't match aspect ratio within the constraints
177 | height = heightSpecSize;
178 | }
179 | } else if (heightSpecMode == View.MeasureSpec.EXACTLY) {
180 | // only the height is fixed, adjust the width to match aspect ratio if possible
181 | height = heightSpecSize;
182 | width = height * mVideoWidth / mVideoHeight;
183 | if (widthSpecMode == View.MeasureSpec.AT_MOST && width > widthSpecSize) {
184 | // couldn't match aspect ratio within the constraints
185 | width = widthSpecSize;
186 | }
187 | } else {
188 | // neither the width nor the height are fixed, try to use actual video size
189 | width = mVideoWidth;
190 | height = mVideoHeight;
191 | if (heightSpecMode == View.MeasureSpec.AT_MOST && height > heightSpecSize) {
192 | // too tall, decrease both width and height
193 | height = heightSpecSize;
194 | width = height * mVideoWidth / mVideoHeight;
195 | }
196 | if (widthSpecMode == View.MeasureSpec.AT_MOST && width > widthSpecSize) {
197 | // too wide, decrease both width and height
198 | width = widthSpecSize;
199 | height = width * mVideoHeight / mVideoWidth;
200 | }
201 | }
202 | } else {
203 | // no size yet, just adopt the given spec sizes
204 | }
205 |
206 | mMeasuredWidth = width;
207 | mMeasuredHeight = height;
208 | }
209 |
210 | public int getMeasuredWidth() {
211 | return mMeasuredWidth;
212 | }
213 |
214 | public int getMeasuredHeight() {
215 | return mMeasuredHeight;
216 | }
217 |
218 | public void setAspectRatio(int aspectRatio) {
219 | mCurrentAspectRatio = aspectRatio;
220 | }
221 |
222 | @NonNull
223 | public static String getAspectRatioText(Context context, int aspectRatio) {
224 | String text;
225 | switch (aspectRatio) {
226 | case IRenderView.AR_ASPECT_FIT_PARENT:
227 | text = context.getString(R.string.VideoView_ar_aspect_fit_parent);
228 | break;
229 | case IRenderView.AR_ASPECT_FILL_PARENT:
230 | text = context.getString(R.string.VideoView_ar_aspect_fill_parent);
231 | break;
232 | case IRenderView.AR_ASPECT_WRAP_CONTENT:
233 | text = context.getString(R.string.VideoView_ar_aspect_wrap_content);
234 | break;
235 | case IRenderView.AR_MATCH_PARENT:
236 | text = context.getString(R.string.VideoView_ar_match_parent);
237 | break;
238 | case IRenderView.AR_16_9_FIT_PARENT:
239 | text = context.getString(R.string.VideoView_ar_16_9_fit_parent);
240 | break;
241 | case IRenderView.AR_4_3_FIT_PARENT:
242 | text = context.getString(R.string.VideoView_ar_4_3_fit_parent);
243 | break;
244 | default:
245 | text = context.getString(R.string.N_A);
246 | break;
247 | }
248 | return text;
249 | }
250 | }
251 |
--------------------------------------------------------------------------------
/wsqj_ijkplayer/src/main/java/com/ws/ijk/wsqj_ijkplayer/widget/media/TextureRenderView.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2015 Bilibili
3 | * Copyright (C) 2015 Zhang Rui
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.ws.ijk.wsqj_ijkplayer.widget.media;
19 |
20 | import android.annotation.TargetApi;
21 | import android.content.Context;
22 | import android.graphics.Color;
23 | import android.graphics.SurfaceTexture;
24 | import android.os.Build;
25 | import android.support.annotation.NonNull;
26 | import android.support.annotation.Nullable;
27 | import android.util.AttributeSet;
28 | import android.util.Log;
29 | import android.view.Surface;
30 | import android.view.SurfaceHolder;
31 | import android.view.TextureView;
32 | import android.view.View;
33 | import android.view.accessibility.AccessibilityEvent;
34 | import android.view.accessibility.AccessibilityNodeInfo;
35 |
36 | import java.lang.ref.WeakReference;
37 | import java.util.Map;
38 | import java.util.concurrent.ConcurrentHashMap;
39 |
40 | import tv.danmaku.ijk.media.player.IMediaPlayer;
41 | import tv.danmaku.ijk.media.player.ISurfaceTextureHolder;
42 | import tv.danmaku.ijk.media.player.ISurfaceTextureHost;
43 |
44 | /**
45 | * Created by Wang.shuo on 2017/6/15.
46 | */
47 |
48 | @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
49 | public class TextureRenderView extends TextureView implements IRenderView {
50 | private static final String TAG = "TextureRenderView";
51 | private MeasureHelper mMeasureHelper;
52 |
53 | public TextureRenderView(Context context) {
54 | super(context);
55 | initView(context);
56 | }
57 |
58 | public TextureRenderView(Context context, AttributeSet attrs) {
59 | super(context, attrs);
60 | initView(context);
61 | }
62 |
63 | public TextureRenderView(Context context, AttributeSet attrs, int defStyleAttr) {
64 | super(context, attrs, defStyleAttr);
65 | initView(context);
66 | }
67 |
68 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
69 | public TextureRenderView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
70 | super(context, attrs, defStyleAttr, defStyleRes);
71 | initView(context);
72 | }
73 |
74 | private void initView(Context context) {
75 | mMeasureHelper = new MeasureHelper(this);
76 | mSurfaceCallback = new SurfaceCallback(this);
77 | setSurfaceTextureListener(mSurfaceCallback);
78 | setBackgroundColor(Color.parseColor("#00000000"));//added by wangshuo for Flash black
79 | }
80 |
81 | @Override
82 | public View getView() {
83 | return this;
84 | }
85 |
86 | @Override
87 | public boolean shouldWaitForResize() {
88 | return false;
89 | }
90 |
91 | @Override
92 | protected void onDetachedFromWindow() {
93 | mSurfaceCallback.willDetachFromWindow();
94 | super.onDetachedFromWindow();
95 | mSurfaceCallback.didDetachFromWindow();
96 | }
97 |
98 | //--------------------
99 | // Layout & Measure
100 | //--------------------
101 | @Override
102 | public void setVideoSize(int videoWidth, int videoHeight) {
103 | if (videoWidth > 0 && videoHeight > 0) {
104 | mMeasureHelper.setVideoSize(videoWidth, videoHeight);
105 | requestLayout();
106 | }
107 | }
108 |
109 | @Override
110 | public void setVideoSampleAspectRatio(int videoSarNum, int videoSarDen) {
111 | if (videoSarNum > 0 && videoSarDen > 0) {
112 | mMeasureHelper.setVideoSampleAspectRatio(videoSarNum, videoSarDen);
113 | requestLayout();
114 | }
115 | }
116 |
117 | @Override
118 | public void setVideoRotation(int degree) {
119 | mMeasureHelper.setVideoRotation(degree);
120 | setRotation(degree);
121 | }
122 |
123 | @Override
124 | public void setAspectRatio(int aspectRatio) {
125 | mMeasureHelper.setAspectRatio(aspectRatio);
126 | requestLayout();
127 | }
128 |
129 | @Override
130 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
131 | mMeasureHelper.doMeasure(widthMeasureSpec, heightMeasureSpec);
132 | setMeasuredDimension(mMeasureHelper.getMeasuredWidth(), mMeasureHelper.getMeasuredHeight());
133 | }
134 |
135 | //--------------------
136 | // TextureViewHolder
137 | //--------------------
138 |
139 | public IRenderView.ISurfaceHolder getSurfaceHolder() {
140 | return new InternalSurfaceHolder(this, mSurfaceCallback.mSurfaceTexture, mSurfaceCallback);
141 | }
142 |
143 | private static final class InternalSurfaceHolder implements IRenderView.ISurfaceHolder {
144 | private TextureRenderView mTextureView;
145 | private SurfaceTexture mSurfaceTexture;
146 | private ISurfaceTextureHost mSurfaceTextureHost;
147 |
148 | public InternalSurfaceHolder(@NonNull TextureRenderView textureView,
149 | @Nullable SurfaceTexture surfaceTexture,
150 | @NonNull ISurfaceTextureHost surfaceTextureHost) {
151 | mTextureView = textureView;
152 | mSurfaceTexture = surfaceTexture;
153 | mSurfaceTextureHost = surfaceTextureHost;
154 | }
155 |
156 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
157 | public void bindToMediaPlayer(IMediaPlayer mp) {
158 | if (mp == null)
159 | return;
160 |
161 | if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) &&
162 | (mp instanceof ISurfaceTextureHolder)) {
163 | ISurfaceTextureHolder textureHolder = (ISurfaceTextureHolder) mp;
164 | mTextureView.mSurfaceCallback.setOwnSurfaceTexture(false);
165 |
166 | SurfaceTexture surfaceTexture = textureHolder.getSurfaceTexture();
167 | if (surfaceTexture != null) {
168 | mTextureView.setSurfaceTexture(surfaceTexture);
169 | } else {
170 | textureHolder.setSurfaceTexture(mSurfaceTexture);
171 | textureHolder.setSurfaceTextureHost(mTextureView.mSurfaceCallback);
172 | }
173 | } else {
174 | mp.setSurface(openSurface());
175 | }
176 | }
177 |
178 | @NonNull
179 | @Override
180 | public IRenderView getRenderView() {
181 | return mTextureView;
182 | }
183 |
184 | @Nullable
185 | @Override
186 | public SurfaceHolder getSurfaceHolder() {
187 | return null;
188 | }
189 |
190 | @Nullable
191 | @Override
192 | public SurfaceTexture getSurfaceTexture() {
193 | return mSurfaceTexture;
194 | }
195 |
196 | @Nullable
197 | @Override
198 | public Surface openSurface() {
199 | if (mSurfaceTexture == null)
200 | return null;
201 | return new Surface(mSurfaceTexture);
202 | }
203 | }
204 |
205 | //-------------------------
206 | // SurfaceHolder.Callback
207 | //-------------------------
208 |
209 | @Override
210 | public void addRenderCallback(IRenderCallback callback) {
211 | mSurfaceCallback.addRenderCallback(callback);
212 | }
213 |
214 | @Override
215 | public void removeRenderCallback(IRenderCallback callback) {
216 | mSurfaceCallback.removeRenderCallback(callback);
217 | }
218 |
219 | private SurfaceCallback mSurfaceCallback;
220 |
221 | private static final class SurfaceCallback implements TextureView.SurfaceTextureListener, ISurfaceTextureHost {
222 | private SurfaceTexture mSurfaceTexture;
223 | private boolean mIsFormatChanged;
224 | private int mWidth;
225 | private int mHeight;
226 |
227 | private boolean mOwnSurfaceTexture = true;
228 | private boolean mWillDetachFromWindow = false;
229 | private boolean mDidDetachFromWindow = false;
230 |
231 | private WeakReference mWeakRenderView;
232 | private Map mRenderCallbackMap = new ConcurrentHashMap();
233 |
234 | public SurfaceCallback(@NonNull TextureRenderView renderView) {
235 | mWeakRenderView = new WeakReference(renderView);
236 | }
237 |
238 | public void setOwnSurfaceTexture(boolean ownSurfaceTexture) {
239 | mOwnSurfaceTexture = ownSurfaceTexture;
240 | }
241 |
242 | public void addRenderCallback(@NonNull IRenderCallback callback) {
243 | mRenderCallbackMap.put(callback, callback);
244 |
245 | ISurfaceHolder surfaceHolder = null;
246 | if (mSurfaceTexture != null) {
247 | if (surfaceHolder == null)
248 | surfaceHolder = new InternalSurfaceHolder(mWeakRenderView.get(), mSurfaceTexture, this);
249 | callback.onSurfaceCreated(surfaceHolder, mWidth, mHeight);
250 | }
251 |
252 | if (mIsFormatChanged) {
253 | if (surfaceHolder == null)
254 | surfaceHolder = new InternalSurfaceHolder(mWeakRenderView.get(), mSurfaceTexture, this);
255 | callback.onSurfaceChanged(surfaceHolder, 0, mWidth, mHeight);
256 | }
257 | }
258 |
259 | public void removeRenderCallback(@NonNull IRenderCallback callback) {
260 | mRenderCallbackMap.remove(callback);
261 | }
262 |
263 | @Override
264 | public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
265 | mSurfaceTexture = surface;
266 | mIsFormatChanged = false;
267 | mWidth = 0;
268 | mHeight = 0;
269 |
270 | ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakRenderView.get(), surface, this);
271 | for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) {
272 | renderCallback.onSurfaceCreated(surfaceHolder, 0, 0);
273 | }
274 | }
275 |
276 | @Override
277 | public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
278 | mSurfaceTexture = surface;
279 | mIsFormatChanged = true;
280 | mWidth = width;
281 | mHeight = height;
282 |
283 | ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakRenderView.get(), surface, this);
284 | for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) {
285 | renderCallback.onSurfaceChanged(surfaceHolder, 0, width, height);
286 | }
287 | }
288 |
289 | @Override
290 | public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
291 | mSurfaceTexture = surface;
292 | mIsFormatChanged = false;
293 | mWidth = 0;
294 | mHeight = 0;
295 |
296 | ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakRenderView.get(), surface, this);
297 | for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) {
298 | renderCallback.onSurfaceDestroyed(surfaceHolder);
299 | }
300 |
301 | Log.d(TAG, "onSurfaceTextureDestroyed: destroy: " + mOwnSurfaceTexture);
302 | return mOwnSurfaceTexture;
303 | }
304 |
305 | @Override
306 | public void onSurfaceTextureUpdated(SurfaceTexture surface) {
307 | }
308 |
309 | //-------------------------
310 | // ISurfaceTextureHost
311 | //-------------------------
312 |
313 | @Override
314 | public void releaseSurfaceTexture(SurfaceTexture surfaceTexture) {
315 | if (surfaceTexture == null) {
316 | Log.d(TAG, "releaseSurfaceTexture: null");
317 | } else if (mDidDetachFromWindow) {
318 | if (surfaceTexture != mSurfaceTexture) {
319 | Log.d(TAG, "releaseSurfaceTexture: didDetachFromWindow(): release different SurfaceTexture");
320 | surfaceTexture.release();
321 | } else if (!mOwnSurfaceTexture) {
322 | Log.d(TAG, "releaseSurfaceTexture: didDetachFromWindow(): release detached SurfaceTexture");
323 | surfaceTexture.release();
324 | } else {
325 | Log.d(TAG, "releaseSurfaceTexture: didDetachFromWindow(): already released by TextureView");
326 | }
327 | } else if (mWillDetachFromWindow) {
328 | if (surfaceTexture != mSurfaceTexture) {
329 | Log.d(TAG, "releaseSurfaceTexture: willDetachFromWindow(): release different SurfaceTexture");
330 | surfaceTexture.release();
331 | } else if (!mOwnSurfaceTexture) {
332 | Log.d(TAG, "releaseSurfaceTexture: willDetachFromWindow(): re-attach SurfaceTexture to TextureView");
333 | setOwnSurfaceTexture(true);
334 | } else {
335 | Log.d(TAG, "releaseSurfaceTexture: willDetachFromWindow(): will released by TextureView");
336 | }
337 | } else {
338 | if (surfaceTexture != mSurfaceTexture) {
339 | Log.d(TAG, "releaseSurfaceTexture: alive: release different SurfaceTexture");
340 | surfaceTexture.release();
341 | } else if (!mOwnSurfaceTexture) {
342 | Log.d(TAG, "releaseSurfaceTexture: alive: re-attach SurfaceTexture to TextureView");
343 | setOwnSurfaceTexture(true);
344 | } else {
345 | Log.d(TAG, "releaseSurfaceTexture: alive: will released by TextureView");
346 | }
347 | }
348 | }
349 |
350 | public void willDetachFromWindow() {
351 | Log.d(TAG, "willDetachFromWindow()");
352 | mWillDetachFromWindow = true;
353 | }
354 |
355 | public void didDetachFromWindow() {
356 | Log.d(TAG, "didDetachFromWindow()");
357 | mDidDetachFromWindow = true;
358 | }
359 | }
360 |
361 | //--------------------
362 | // Accessibility
363 | //--------------------
364 |
365 | @Override
366 | public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
367 | super.onInitializeAccessibilityEvent(event);
368 | event.setClassName(TextureRenderView.class.getName());
369 | }
370 |
371 | @Override
372 | public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
373 | super.onInitializeAccessibilityNodeInfo(info);
374 | info.setClassName(TextureRenderView.class.getName());
375 | }
376 | }
377 |
--------------------------------------------------------------------------------
/wsqj_ijkplayer/src/main/java/com/ws/ijk/wsqj_ijkplayer/widget/media/IjkVideoView.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2015 Bilibili
3 | * Copyright (C) 2015 Zhang Rui
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.ws.ijk.wsqj_ijkplayer.widget.media;
19 |
20 | import android.annotation.TargetApi;
21 | import android.content.Context;
22 | import android.content.DialogInterface;
23 | import android.content.res.Resources;
24 | import android.media.AudioManager;
25 | import android.media.MediaPlayer;
26 | import android.net.Uri;
27 | import android.os.Build;
28 | import android.support.annotation.NonNull;
29 | import android.support.v7.app.AlertDialog;
30 | import android.text.TextUtils;
31 | import android.util.AttributeSet;
32 | import android.util.Log;
33 | import android.view.Gravity;
34 | import android.view.KeyEvent;
35 | import android.view.MotionEvent;
36 | import android.view.View;
37 | import android.widget.FrameLayout;
38 | import android.widget.MediaController;
39 |
40 | import com.ws.ijk.wsqj_ijkplayer.R;
41 | import com.ws.ijk.wsqj_ijkplayer.widget.services.MediaPlayerService;
42 |
43 | import java.io.File;
44 | import java.io.IOException;
45 | import java.util.ArrayList;
46 | import java.util.List;
47 | import java.util.Locale;
48 | import java.util.Map;
49 |
50 | import tv.danmaku.ijk.media.player.AndroidMediaPlayer;
51 | import tv.danmaku.ijk.media.player.IMediaPlayer;
52 | import tv.danmaku.ijk.media.player.IjkMediaPlayer;
53 | import tv.danmaku.ijk.media.player.IjkTimedText;
54 | import tv.danmaku.ijk.media.player.TextureMediaPlayer;
55 | import tv.danmaku.ijk.media.player.misc.IMediaDataSource;
56 | import tv.danmaku.ijk.media.player.misc.ITrackInfo;
57 |
58 | /**
59 | * Created by Wang.shuo on 2017/6/15.
60 | */
61 | public class IjkVideoView extends FrameLayout implements MediaController.MediaPlayerControl {
62 | private String TAG = "IjkVideoView";
63 | // settable by the client
64 | private Uri mUri;
65 | private Map mHeaders;
66 |
67 | // all possible internal states
68 | private static final int STATE_ERROR = -1;
69 | private static final int STATE_IDLE = 0;
70 | private static final int STATE_PREPARING = 1;
71 | private static final int STATE_PREPARED = 2;
72 | private static final int STATE_PLAYING = 3;
73 | private static final int STATE_PAUSED = 4;
74 | private static final int STATE_PLAYBACK_COMPLETED = 5;
75 |
76 | // mCurrentState is a VideoView object's current state.
77 | // mTargetState is the state that a method caller intends to reach.
78 | // For instance, regardless the VideoView object's current state,
79 | // calling pause() intends to bring the object to a target state
80 | // of STATE_PAUSED.
81 | private int mCurrentState = STATE_IDLE;
82 | private int mTargetState = STATE_IDLE;
83 |
84 | // All the stuff we need for playing and showing a video
85 | private IRenderView.ISurfaceHolder mSurfaceHolder = null;
86 | private IMediaPlayer mMediaPlayer = null;
87 | // private int mAudioSession;
88 | private int mVideoWidth;
89 | private int mVideoHeight;
90 | private int mSurfaceWidth;
91 | private int mSurfaceHeight;
92 | private int mVideoRotationDegree;
93 | private IMediaController mMediaController;
94 | private IMediaPlayer.OnCompletionListener mOnCompletionListener;
95 | private IMediaPlayer.OnPreparedListener mOnPreparedListener;
96 | private int mCurrentBufferPercentage;
97 | private IMediaPlayer.OnErrorListener mOnErrorListener;
98 | private IMediaPlayer.OnInfoListener mOnInfoListener;
99 | private int mSeekWhenPrepared; // recording the seek position while preparing
100 | private boolean mCanPause = true;
101 | private boolean mCanSeekBack = true;
102 | private boolean mCanSeekForward = true;
103 |
104 | /** Subtitle rendering widget overlaid on top of the video. */
105 | // private RenderingWidget mSubtitleWidget;
106 |
107 | /**
108 | * Listener for changes to subtitle data, used to redraw when needed.
109 | */
110 | // private RenderingWidget.OnChangedListener mSubtitlesChangedListener;
111 |
112 | private Context mAppContext;
113 | private Settings mSettings;
114 | private IRenderView mRenderView;
115 | private int mVideoSarNum;
116 | private int mVideoSarDen;
117 |
118 | public IjkVideoView(Context context) {
119 | super(context);
120 | initVideoView(context);
121 | }
122 |
123 | public IjkVideoView(Context context, AttributeSet attrs) {
124 | super(context, attrs);
125 | initVideoView(context);
126 | }
127 |
128 | public IjkVideoView(Context context, AttributeSet attrs, int defStyleAttr) {
129 | super(context, attrs, defStyleAttr);
130 | initVideoView(context);
131 | }
132 |
133 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
134 | public IjkVideoView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
135 | super(context, attrs, defStyleAttr, defStyleRes);
136 | initVideoView(context);
137 | }
138 |
139 | // REMOVED: onMeasure
140 | // REMOVED: onInitializeAccessibilityEvent
141 | // REMOVED: onInitializeAccessibilityNodeInfo
142 | // REMOVED: resolveAdjustedSize
143 |
144 | private void initVideoView(Context context) {
145 | mAppContext = context.getApplicationContext();
146 | mSettings = new Settings(mAppContext);
147 |
148 | initBackground();
149 | initRenders();
150 |
151 | mVideoWidth = 0;
152 | mVideoHeight = 0;
153 | // REMOVED: getHolder().addCallback(mSHCallback);
154 | // REMOVED: getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
155 | setFocusable(true);
156 | setFocusableInTouchMode(true);
157 | requestFocus();
158 | // REMOVED: mPendingSubtitleTracks = new Vector>();
159 | mCurrentState = STATE_IDLE;
160 | mTargetState = STATE_IDLE;
161 | }
162 |
163 | public void setRenderView(IRenderView renderView) {
164 | if (mRenderView != null) {
165 | if (mMediaPlayer != null)
166 | mMediaPlayer.setDisplay(null);
167 |
168 | View renderUIView = mRenderView.getView();
169 | mRenderView.removeRenderCallback(mSHCallback);
170 | mRenderView = null;
171 | removeView(renderUIView);
172 | }
173 |
174 | if (renderView == null)
175 | return;
176 |
177 | mRenderView = renderView;
178 | renderView.setAspectRatio(mCurrentAspectRatio);
179 | if (mVideoWidth > 0 && mVideoHeight > 0)
180 | renderView.setVideoSize(mVideoWidth, mVideoHeight);
181 | if (mVideoSarNum > 0 && mVideoSarDen > 0)
182 | renderView.setVideoSampleAspectRatio(mVideoSarNum, mVideoSarDen);
183 |
184 | View renderUIView = mRenderView.getView();
185 | LayoutParams lp = new LayoutParams(
186 | LayoutParams.WRAP_CONTENT,
187 | LayoutParams.WRAP_CONTENT,
188 | Gravity.CENTER);
189 | renderUIView.setLayoutParams(lp);
190 | addView(renderUIView);
191 |
192 | mRenderView.addRenderCallback(mSHCallback);
193 | mRenderView.setVideoRotation(mVideoRotationDegree);
194 | }
195 |
196 | public void setRender(int render) {
197 | switch (render) {
198 | case RENDER_NONE:
199 | setRenderView(null);
200 | break;
201 | case RENDER_TEXTURE_VIEW: {
202 | TextureRenderView renderView = new TextureRenderView(getContext());
203 | if (mMediaPlayer != null) {
204 | renderView.getSurfaceHolder().bindToMediaPlayer(mMediaPlayer);
205 | renderView.setVideoSize(mMediaPlayer.getVideoWidth(), mMediaPlayer.getVideoHeight());
206 | renderView.setVideoSampleAspectRatio(mMediaPlayer.getVideoSarNum(), mMediaPlayer.getVideoSarDen());
207 | renderView.setAspectRatio(mCurrentAspectRatio);
208 | }
209 | setRenderView(renderView);
210 | break;
211 | }
212 | case RENDER_SURFACE_VIEW: {
213 | SurfaceRenderView renderView = new SurfaceRenderView(getContext());
214 | setRenderView(renderView);
215 | break;
216 | }
217 | default:
218 | Log.e(TAG, String.format(Locale.getDefault(), "invalid render %d\n", render));
219 | break;
220 | }
221 | }
222 |
223 |
224 |
225 | /**
226 | * Sets video path.
227 | *
228 | * @param path the path of the video.
229 | */
230 | public void setVideoPath(String path) {
231 | setVideoURI(Uri.parse(path));
232 | }
233 |
234 | /**
235 | * Sets video URI.
236 | *
237 | * @param uri the URI of the video.
238 | */
239 | public void setVideoURI(Uri uri) {
240 | setVideoURI(uri, null);
241 | }
242 |
243 | /**
244 | * Sets video URI using specific headers.
245 | *
246 | * @param uri the URI of the video.
247 | * @param headers the headers for the URI request.
248 | * Note that the cross domain redirection is allowed by default, but that can be
249 | * changed with key/value pairs through the headers parameter with
250 | * "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value
251 | * to disallow or allow cross domain redirection.
252 | */
253 | private void setVideoURI(Uri uri, Map headers) {
254 | mUri = uri;
255 | mHeaders = headers;
256 | mSeekWhenPrepared = 0;
257 | openVideo();
258 | requestLayout();
259 | invalidate();
260 | }
261 |
262 | // REMOVED: addSubtitleSource
263 | // REMOVED: mPendingSubtitleTracks
264 |
265 | public void stopPlayback() {
266 | if (mMediaPlayer != null) {
267 | mMediaPlayer.stop();
268 | mMediaPlayer.release();
269 | mMediaPlayer = null;
270 |
271 | mCurrentState = STATE_IDLE;
272 | mTargetState = STATE_IDLE;
273 | AudioManager am = (AudioManager) mAppContext.getSystemService(Context.AUDIO_SERVICE);
274 | am.abandonAudioFocus(null);
275 | }
276 | }
277 |
278 | @TargetApi(Build.VERSION_CODES.M)
279 | private void openVideo() {
280 | if (mUri == null || mSurfaceHolder == null) {
281 | // not ready for playback just yet, will try again later
282 | return;
283 | }
284 | // we shouldn't clear the target state, because somebody might have
285 | // called start() previously
286 | release(false);
287 |
288 | AudioManager am = (AudioManager) mAppContext.getSystemService(Context.AUDIO_SERVICE);
289 | am.requestAudioFocus(null, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
290 |
291 | try {
292 | mMediaPlayer = createPlayer(Settings.PV_PLAYER__IjkMediaPlayer);/*mSettings.getPlayer()*/
293 |
294 | // TODO: create SubtitleController in MediaPlayer, but we need
295 | // a context for the subtitle renderers
296 | final Context context = getContext();
297 | // REMOVED: SubtitleController
298 |
299 | // REMOVED: mAudioSession
300 | mMediaPlayer.setOnPreparedListener(mPreparedListener);
301 | mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener);
302 | mMediaPlayer.setOnCompletionListener(mCompletionListener);
303 | mMediaPlayer.setOnErrorListener(mErrorListener);
304 | mMediaPlayer.setOnInfoListener(mInfoListener);
305 | mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener);
306 | mMediaPlayer.setOnSeekCompleteListener(mSeekCompleteListener);
307 | mMediaPlayer.setOnTimedTextListener(mOnTimedTextListener);
308 | mCurrentBufferPercentage = 0;
309 | String scheme = mUri.getScheme();
310 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
311 | mSettings.getUsingMediaDataSource() &&
312 | (TextUtils.isEmpty(scheme) || scheme.equalsIgnoreCase("file"))) {
313 | IMediaDataSource dataSource = new FileMediaDataSource(new File(mUri.toString()));
314 | mMediaPlayer.setDataSource(dataSource);
315 | } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
316 | mMediaPlayer.setDataSource(mAppContext, mUri, mHeaders);
317 | } else {
318 | mMediaPlayer.setDataSource(mUri.toString());
319 | }
320 | bindSurfaceHolder(mMediaPlayer, mSurfaceHolder);
321 | mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
322 | mMediaPlayer.setScreenOnWhilePlaying(true);
323 | mMediaPlayer.prepareAsync();
324 |
325 | // REMOVED: mPendingSubtitleTracks
326 |
327 | // we don't set the target state here either, but preserve the
328 | // target state that was there before.
329 | mCurrentState = STATE_PREPARING;
330 | attachMediaController();
331 | } catch (IOException ex) {
332 | Log.w(TAG, "Unable to open content: " + mUri, ex);
333 | mCurrentState = STATE_ERROR;
334 | mTargetState = STATE_ERROR;
335 | mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0);
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 | } finally {
342 | // REMOVED: mPendingSubtitleTracks.clear();
343 | }
344 | }
345 |
346 | public void setMediaController(IMediaController controller) {
347 | if (mMediaController != null) {
348 | mMediaController.hide();
349 | }
350 | mMediaController = controller;
351 | attachMediaController();
352 | }
353 |
354 | private void attachMediaController() {
355 | if (mMediaPlayer != null && mMediaController != null) {
356 | mMediaController.setMediaPlayer(this);
357 | View anchorView = this.getParent() instanceof View ?
358 | (View) this.getParent() : this;
359 | mMediaController.setAnchorView(anchorView);
360 | mMediaController.setEnabled(isInPlaybackState());
361 | }
362 | }
363 |
364 | IMediaPlayer.OnVideoSizeChangedListener mSizeChangedListener =
365 | new IMediaPlayer.OnVideoSizeChangedListener() {
366 | public void onVideoSizeChanged(IMediaPlayer mp, int width, int height, int sarNum, int sarDen) {
367 | mVideoWidth = mp.getVideoWidth();
368 | mVideoHeight = mp.getVideoHeight();
369 | mVideoSarNum = mp.getVideoSarNum();
370 | mVideoSarDen = mp.getVideoSarDen();
371 | if (mVideoWidth != 0 && mVideoHeight != 0) {
372 | if (mRenderView != null) {
373 | mRenderView.setVideoSize(mVideoWidth, mVideoHeight);
374 | mRenderView.setVideoSampleAspectRatio(mVideoSarNum, mVideoSarDen);
375 | }
376 | // REMOVED: getHolder().setFixedSize(mVideoWidth, mVideoHeight);
377 | requestLayout();
378 | }
379 | }
380 | };
381 |
382 | IMediaPlayer.OnPreparedListener mPreparedListener = new IMediaPlayer.OnPreparedListener() {
383 | public void onPrepared(IMediaPlayer mp) {
384 | mCurrentState = STATE_PREPARED;
385 |
386 | // Get the capabilities of the player for this stream
387 | // REMOVED: Metadata
388 |
389 | if (mOnPreparedListener != null) {
390 | mOnPreparedListener.onPrepared(mMediaPlayer);
391 | }
392 | if (mMediaController != null) {
393 | mMediaController.setEnabled(true);
394 | }
395 | mVideoWidth = mp.getVideoWidth();
396 | mVideoHeight = mp.getVideoHeight();
397 |
398 | int seekToPosition = mSeekWhenPrepared; // mSeekWhenPrepared may be changed after seekTo() call
399 | if (seekToPosition != 0) {
400 | seekTo(seekToPosition);
401 | }
402 | if (mVideoWidth != 0 && mVideoHeight != 0) {
403 | //Log.i("@@@@", "video size: " + mVideoWidth +"/"+ mVideoHeight);
404 | // REMOVED: getHolder().setFixedSize(mVideoWidth, mVideoHeight);
405 | if (mRenderView != null) {
406 | mRenderView.setVideoSize(mVideoWidth, mVideoHeight);
407 | mRenderView.setVideoSampleAspectRatio(mVideoSarNum, mVideoSarDen);
408 | if (!mRenderView.shouldWaitForResize() || mSurfaceWidth == mVideoWidth && mSurfaceHeight == mVideoHeight) {
409 | // We didn't actually change the size (it was already at the size
410 | // we need), so we won't get a "surface changed" callback, so
411 | // start the video here instead of in the callback.
412 | if (mTargetState == STATE_PLAYING) {
413 | start();
414 | if (mMediaController != null) {
415 | mMediaController.show();
416 | }
417 | } else if (!isPlaying() &&
418 | (seekToPosition != 0 || getCurrentPosition() > 0)) {
419 | if (mMediaController != null) {
420 | // Show the media controls when we're paused into a video and make 'em stick.
421 | mMediaController.show(0);
422 | }
423 | }
424 | }
425 | }
426 | } else {
427 | // We don't know the video size yet, but should start anyway.
428 | // The video size might be reported to us later.
429 | if (mTargetState == STATE_PLAYING) {
430 | start();
431 | }
432 | }
433 | }
434 | };
435 |
436 | private IMediaPlayer.OnCompletionListener mCompletionListener =
437 | new IMediaPlayer.OnCompletionListener() {
438 | public void onCompletion(IMediaPlayer mp) {
439 | mCurrentState = STATE_PLAYBACK_COMPLETED;
440 | mTargetState = STATE_PLAYBACK_COMPLETED;
441 | if (mMediaController != null) {
442 | mMediaController.hide();
443 | }
444 | if (mOnCompletionListener != null) {
445 | mOnCompletionListener.onCompletion(mMediaPlayer);
446 | }
447 | }
448 | };
449 |
450 | private IMediaPlayer.OnInfoListener mInfoListener =
451 | new IMediaPlayer.OnInfoListener() {
452 | public boolean onInfo(IMediaPlayer mp, int arg1, int arg2) {
453 | if (mOnInfoListener != null) {
454 | mOnInfoListener.onInfo(mp, arg1, arg2);
455 | }
456 | switch (arg1) {
457 | case IMediaPlayer.MEDIA_INFO_VIDEO_TRACK_LAGGING:
458 | Log.d(TAG, "MEDIA_INFO_VIDEO_TRACK_LAGGING:");
459 | break;
460 | case IMediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START:
461 | Log.d(TAG, "MEDIA_INFO_VIDEO_RENDERING_START:");
462 | break;
463 | case IMediaPlayer.MEDIA_INFO_BUFFERING_START:
464 | Log.d(TAG, "MEDIA_INFO_BUFFERING_START:");
465 | break;
466 | case IMediaPlayer.MEDIA_INFO_BUFFERING_END:
467 | Log.d(TAG, "MEDIA_INFO_BUFFERING_END:");
468 | break;
469 | case IMediaPlayer.MEDIA_INFO_NETWORK_BANDWIDTH:
470 | Log.d(TAG, "MEDIA_INFO_NETWORK_BANDWIDTH: " + arg2);
471 | break;
472 | case IMediaPlayer.MEDIA_INFO_BAD_INTERLEAVING:
473 | Log.d(TAG, "MEDIA_INFO_BAD_INTERLEAVING:");
474 | break;
475 | case IMediaPlayer.MEDIA_INFO_NOT_SEEKABLE:
476 | Log.d(TAG, "MEDIA_INFO_NOT_SEEKABLE:");
477 | break;
478 | case IMediaPlayer.MEDIA_INFO_METADATA_UPDATE:
479 | Log.d(TAG, "MEDIA_INFO_METADATA_UPDATE:");
480 | break;
481 | case IMediaPlayer.MEDIA_INFO_UNSUPPORTED_SUBTITLE:
482 | Log.d(TAG, "MEDIA_INFO_UNSUPPORTED_SUBTITLE:");
483 | break;
484 | case IMediaPlayer.MEDIA_INFO_SUBTITLE_TIMED_OUT:
485 | Log.d(TAG, "MEDIA_INFO_SUBTITLE_TIMED_OUT:");
486 | break;
487 | case IMediaPlayer.MEDIA_INFO_VIDEO_ROTATION_CHANGED:
488 | mVideoRotationDegree = arg2;
489 | Log.d(TAG, "MEDIA_INFO_VIDEO_ROTATION_CHANGED: " + arg2);
490 | if (mRenderView != null)
491 | mRenderView.setVideoRotation(arg2);
492 | break;
493 | case IMediaPlayer.MEDIA_INFO_AUDIO_RENDERING_START:
494 | Log.d(TAG, "MEDIA_INFO_AUDIO_RENDERING_START:");
495 | break;
496 | }
497 | return true;
498 | }
499 | };
500 |
501 | private IMediaPlayer.OnErrorListener mErrorListener =
502 | new IMediaPlayer.OnErrorListener() {
503 | public boolean onError(IMediaPlayer mp, int framework_err, int impl_err) {
504 | Log.d(TAG, "Error: " + framework_err + "," + impl_err);
505 | mCurrentState = STATE_ERROR;
506 | mTargetState = STATE_ERROR;
507 | if (mMediaController != null) {
508 | mMediaController.hide();
509 | }
510 |
511 | /* If an error handler has been supplied, use it and finish. */
512 | if (mOnErrorListener != null) {
513 | if (mOnErrorListener.onError(mMediaPlayer, framework_err, impl_err)) {
514 | return true;
515 | }
516 | }
517 |
518 | /* Otherwise, pop up an error dialog so the user knows that
519 | * something bad has happened. Only try and pop up the dialog
520 | * if we're attached to a window. When we're going away and no
521 | * longer have a window, don't bother showing the user an error.
522 | */
523 | if (getWindowToken() != null) {
524 | Resources r = mAppContext.getResources();
525 | int messageId;
526 |
527 | if (framework_err == MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK) {
528 | messageId = R.string.VideoView_error_text_invalid_progressive_playback;
529 | } else {
530 | messageId = R.string.VideoView_error_text_unknown;
531 | }
532 |
533 | new AlertDialog.Builder(getContext())
534 | .setMessage(messageId)
535 | .setPositiveButton(R.string.VideoView_error_button,
536 | new DialogInterface.OnClickListener() {
537 | public void onClick(DialogInterface dialog, int whichButton) {
538 | /* If we get here, there is no onError listener, so
539 | * at least inform them that the video is over.
540 | */
541 | if (mOnCompletionListener != null) {
542 | mOnCompletionListener.onCompletion(mMediaPlayer);
543 | }
544 | }
545 | })
546 | .setCancelable(false)
547 | .show();
548 | }
549 | return true;
550 | }
551 | };
552 |
553 | private IMediaPlayer.OnBufferingUpdateListener mBufferingUpdateListener =
554 | new IMediaPlayer.OnBufferingUpdateListener() {
555 | public void onBufferingUpdate(IMediaPlayer mp, int percent) {
556 | mCurrentBufferPercentage = percent;
557 | }
558 | };
559 |
560 | private IMediaPlayer.OnSeekCompleteListener mSeekCompleteListener = new IMediaPlayer.OnSeekCompleteListener() {
561 |
562 | @Override
563 | public void onSeekComplete(IMediaPlayer mp) {
564 |
565 | }
566 | };
567 |
568 | private IMediaPlayer.OnTimedTextListener mOnTimedTextListener = new IMediaPlayer.OnTimedTextListener() {
569 | @Override
570 | public void onTimedText(IMediaPlayer mp, IjkTimedText text) {
571 |
572 | }
573 | };
574 |
575 | /**
576 | * Register a callback to be invoked when the media file
577 | * is loaded and ready to go.
578 | *
579 | * @param l The callback that will be run
580 | */
581 | public void setOnPreparedListener(IMediaPlayer.OnPreparedListener l) {
582 | mOnPreparedListener = l;
583 | }
584 |
585 | /**
586 | * Register a callback to be invoked when the end of a media file
587 | * has been reached during playback.
588 | *
589 | * @param l The callback that will be run
590 | */
591 | public void setOnCompletionListener(IMediaPlayer.OnCompletionListener l) {
592 | mOnCompletionListener = l;
593 | }
594 |
595 | /**
596 | * Register a callback to be invoked when an error occurs
597 | * during playback or setup. If no listener is specified,
598 | * or if the listener returned false, VideoView will inform
599 | * the user of any errors.
600 | *
601 | * @param l The callback that will be run
602 | */
603 | public void setOnErrorListener(IMediaPlayer.OnErrorListener l) {
604 | mOnErrorListener = l;
605 | }
606 |
607 | /**
608 | * Register a callback to be invoked when an informational event
609 | * occurs during playback or setup.
610 | *
611 | * @param l The callback that will be run
612 | */
613 | public void setOnInfoListener(IMediaPlayer.OnInfoListener l) {
614 | mOnInfoListener = l;
615 | }
616 |
617 | // REMOVED: mSHCallback
618 | private void bindSurfaceHolder(IMediaPlayer mp, IRenderView.ISurfaceHolder holder) {
619 | if (mp == null)
620 | return;
621 |
622 | if (holder == null) {
623 | mp.setDisplay(null);
624 | return;
625 | }
626 |
627 | holder.bindToMediaPlayer(mp);
628 | }
629 |
630 | IRenderView.IRenderCallback mSHCallback = new IRenderView.IRenderCallback() {
631 | @Override
632 | public void onSurfaceChanged(@NonNull IRenderView.ISurfaceHolder holder, int format, int w, int h) {
633 | if (holder.getRenderView() != mRenderView) {
634 | Log.e(TAG, "onSurfaceChanged: unmatched render callback\n");
635 | return;
636 | }
637 |
638 | mSurfaceWidth = w;
639 | mSurfaceHeight = h;
640 | boolean isValidState = (mTargetState == STATE_PLAYING);
641 | boolean hasValidSize = !mRenderView.shouldWaitForResize() || (mVideoWidth == w && mVideoHeight == h);
642 | if (mMediaPlayer != null && isValidState && hasValidSize) {
643 | if (mSeekWhenPrepared != 0) {
644 | seekTo(mSeekWhenPrepared);
645 | }
646 | start();
647 | }
648 | }
649 |
650 | @Override
651 | public void onSurfaceCreated(@NonNull IRenderView.ISurfaceHolder holder, int width, int height) {
652 | if (holder.getRenderView() != mRenderView) {
653 | Log.e(TAG, "onSurfaceCreated: unmatched render callback\n");
654 | return;
655 | }
656 |
657 | mSurfaceHolder = holder;
658 | if (mMediaPlayer != null)
659 | bindSurfaceHolder(mMediaPlayer, holder);
660 | else
661 | openVideo();
662 | }
663 |
664 | @Override
665 | public void onSurfaceDestroyed(@NonNull IRenderView.ISurfaceHolder holder) {
666 | if (holder.getRenderView() != mRenderView) {
667 | Log.e(TAG, "onSurfaceDestroyed: unmatched render callback\n");
668 | return;
669 | }
670 |
671 | // after we return from this we can't use the surface any more
672 | mSurfaceHolder = null;
673 | // REMOVED: if (mMediaController != null) mMediaController.hide();
674 | // REMOVED: release(true);
675 | releaseWithoutStop();
676 | }
677 | };
678 |
679 | public void releaseWithoutStop() {
680 | if (mMediaPlayer != null)
681 | mMediaPlayer.setDisplay(null);
682 | }
683 |
684 | /*
685 | * release the media player in any state
686 | */
687 | public void release(boolean cleartargetstate) {
688 | if (mMediaPlayer != null) {
689 | mMediaPlayer.reset();
690 | mMediaPlayer.release();
691 | mMediaPlayer = null;
692 | // REMOVED: mPendingSubtitleTracks.clear();
693 | mCurrentState = STATE_IDLE;
694 | if (cleartargetstate) {
695 | mTargetState = STATE_IDLE;
696 | }
697 | AudioManager am = (AudioManager) mAppContext.getSystemService(Context.AUDIO_SERVICE);
698 | am.abandonAudioFocus(null);
699 | }
700 | }
701 |
702 | @Override
703 | public boolean onTouchEvent(MotionEvent ev) {
704 | if (isInPlaybackState() && mMediaController != null) {
705 | toggleMediaControlsVisiblity();
706 | }
707 | return false;
708 | }
709 |
710 | @Override
711 | public boolean onTrackballEvent(MotionEvent ev) {
712 | if (isInPlaybackState() && mMediaController != null) {
713 | toggleMediaControlsVisiblity();
714 | }
715 | return false;
716 | }
717 |
718 | @Override
719 | public boolean onKeyDown(int keyCode, KeyEvent event) {
720 | boolean isKeyCodeSupported = keyCode != KeyEvent.KEYCODE_BACK &&
721 | keyCode != KeyEvent.KEYCODE_VOLUME_UP &&
722 | keyCode != KeyEvent.KEYCODE_VOLUME_DOWN &&
723 | keyCode != KeyEvent.KEYCODE_VOLUME_MUTE &&
724 | keyCode != KeyEvent.KEYCODE_MENU &&
725 | keyCode != KeyEvent.KEYCODE_CALL &&
726 | keyCode != KeyEvent.KEYCODE_ENDCALL;
727 | if (isInPlaybackState() && isKeyCodeSupported && mMediaController != null) {
728 | if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK ||
729 | keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) {
730 | if (mMediaPlayer.isPlaying()) {
731 | pause();
732 | mMediaController.show();
733 | } else {
734 | start();
735 | mMediaController.hide();
736 | }
737 | return true;
738 | } else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) {
739 | if (!mMediaPlayer.isPlaying()) {
740 | start();
741 | mMediaController.hide();
742 | }
743 | return true;
744 | } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP
745 | || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) {
746 | if (mMediaPlayer.isPlaying()) {
747 | pause();
748 | mMediaController.show();
749 | }
750 | return true;
751 | } else {
752 | toggleMediaControlsVisiblity();
753 | }
754 | }
755 |
756 | return super.onKeyDown(keyCode, event);
757 | }
758 |
759 | private void toggleMediaControlsVisiblity() {
760 | if (mMediaController.isShowing()) {
761 | mMediaController.hide();
762 | } else {
763 | mMediaController.show();
764 | }
765 | }
766 |
767 | @Override
768 | public void start() {
769 | if (isInPlaybackState()) {
770 | mMediaPlayer.start();
771 | mCurrentState = STATE_PLAYING;
772 | }
773 | mTargetState = STATE_PLAYING;
774 | }
775 |
776 | @Override
777 | public void pause() {
778 | if (isInPlaybackState()) {
779 | if (mMediaPlayer.isPlaying()) {
780 | mMediaPlayer.pause();
781 | mCurrentState = STATE_PAUSED;
782 | }
783 | }
784 | mTargetState = STATE_PAUSED;
785 | }
786 |
787 | public void suspend() {
788 | release(false);
789 | }
790 |
791 | public void resume() {
792 | openVideo();
793 | }
794 |
795 | @Override
796 | public int getDuration() {
797 | if (isInPlaybackState()) {
798 | return (int) mMediaPlayer.getDuration();
799 | }
800 |
801 | return -1;
802 | }
803 |
804 | @Override
805 | public int getCurrentPosition() {
806 | if (isInPlaybackState()) {
807 | return (int) mMediaPlayer.getCurrentPosition();
808 | }
809 | return 0;
810 | }
811 |
812 | @Override
813 | public void seekTo(int msec) {
814 | if (isInPlaybackState()) {
815 | mMediaPlayer.seekTo(msec);
816 | mSeekWhenPrepared = 0;
817 | } else {
818 | mSeekWhenPrepared = msec;
819 | }
820 | }
821 |
822 | @Override
823 | public boolean isPlaying() {
824 | return isInPlaybackState() && mMediaPlayer.isPlaying();
825 | }
826 |
827 | @Override
828 | public int getBufferPercentage() {
829 | if (mMediaPlayer != null) {
830 | return mCurrentBufferPercentage;
831 | }
832 | return 0;
833 | }
834 |
835 | private boolean isInPlaybackState() {
836 | return (mMediaPlayer != null &&
837 | mCurrentState != STATE_ERROR &&
838 | mCurrentState != STATE_IDLE &&
839 | mCurrentState != STATE_PREPARING);
840 | }
841 |
842 | @Override
843 | public boolean canPause() {
844 | return mCanPause;
845 | }
846 |
847 | @Override
848 | public boolean canSeekBackward() {
849 | return mCanSeekBack;
850 | }
851 |
852 | @Override
853 | public boolean canSeekForward() {
854 | return mCanSeekForward;
855 | }
856 |
857 | @Override
858 | public int getAudioSessionId() {
859 | return 0;
860 | }
861 |
862 | // REMOVED: getAudioSessionId();
863 | // REMOVED: onAttachedToWindow();
864 | // REMOVED: onDetachedFromWindow();
865 | // REMOVED: onLayout();
866 | // REMOVED: draw();
867 | // REMOVED: measureAndLayoutSubtitleWidget();
868 | // REMOVED: setSubtitleWidget();
869 | // REMOVED: getSubtitleLooper();
870 |
871 | //-------------------------
872 | // Extend: Aspect Ratio
873 | //-------------------------
874 |
875 | private static final int[] s_allAspectRatio = {
876 | IRenderView.AR_ASPECT_FIT_PARENT,
877 | IRenderView.AR_ASPECT_FILL_PARENT,
878 | IRenderView.AR_ASPECT_WRAP_CONTENT,
879 | IRenderView.AR_MATCH_PARENT,
880 | IRenderView.AR_16_9_FIT_PARENT,
881 | IRenderView.AR_4_3_FIT_PARENT};
882 | private int mCurrentAspectRatioIndex = 0;
883 | private int mCurrentAspectRatio = s_allAspectRatio[1];
884 |
885 | public int toggleAspectRatio() {
886 | mCurrentAspectRatioIndex++;
887 | mCurrentAspectRatioIndex %= s_allAspectRatio.length;
888 |
889 | mCurrentAspectRatio = s_allAspectRatio[mCurrentAspectRatioIndex];
890 | if (mRenderView != null)
891 | mRenderView.setAspectRatio(mCurrentAspectRatio);
892 | return mCurrentAspectRatio;
893 | }
894 |
895 | //-------------------------
896 | // Extend: Render
897 | //-------------------------
898 | public static final int RENDER_NONE = 0;
899 | public static final int RENDER_SURFACE_VIEW = 1;
900 | public static final int RENDER_TEXTURE_VIEW = 2;
901 |
902 | private List mAllRenders = new ArrayList();
903 | private int mCurrentRenderIndex = 0;
904 | private int mCurrentRender = RENDER_NONE;
905 |
906 | private void initRenders() {
907 | mAllRenders.clear();
908 |
909 | if (mSettings.getEnableSurfaceView())
910 | mAllRenders.add(RENDER_SURFACE_VIEW);
911 | if (mSettings.getEnableTextureView() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
912 | mAllRenders.add(RENDER_TEXTURE_VIEW);
913 | if (mSettings.getEnableNoView())
914 | mAllRenders.add(RENDER_NONE);
915 |
916 | if (mAllRenders.isEmpty())
917 | mAllRenders.add(RENDER_SURFACE_VIEW);
918 | mCurrentRender = mAllRenders.get(mCurrentRenderIndex);
919 | setRender(mCurrentRender);
920 | }
921 |
922 | public int toggleRender() {
923 | mCurrentRenderIndex++;
924 | mCurrentRenderIndex %= mAllRenders.size();
925 |
926 | mCurrentRender = mAllRenders.get(mCurrentRenderIndex);
927 | setRender(mCurrentRender);
928 | return mCurrentRender;
929 | }
930 |
931 | @NonNull
932 | public static String getRenderText(Context context, int render) {
933 | String text;
934 | switch (render) {
935 | case RENDER_NONE:
936 | text = context.getString(R.string.VideoView_render_none);
937 | break;
938 | case RENDER_SURFACE_VIEW:
939 | text = context.getString(R.string.VideoView_render_surface_view);
940 | break;
941 | case RENDER_TEXTURE_VIEW:
942 | text = context.getString(R.string.VideoView_render_texture_view);
943 | break;
944 | default:
945 | text = context.getString(R.string.N_A);
946 | break;
947 | }
948 | return text;
949 | }
950 |
951 | //-------------------------
952 | // Extend: Player
953 | //-------------------------
954 | public int togglePlayer() {
955 | if (mMediaPlayer != null)
956 | mMediaPlayer.release();
957 |
958 | if (mRenderView != null)
959 | mRenderView.getView().invalidate();
960 | openVideo();
961 | return mSettings.getPlayer();
962 | }
963 |
964 | @NonNull
965 | public static String getPlayerText(Context context, int player) {
966 | String text;
967 | switch (player) {
968 | case Settings.PV_PLAYER__AndroidMediaPlayer:
969 | text = context.getString(R.string.VideoView_player_AndroidMediaPlayer);
970 | break;
971 | case Settings.PV_PLAYER__IjkMediaPlayer:
972 | text = context.getString(R.string.VideoView_player_IjkMediaPlayer);
973 | break;
974 | case Settings.PV_PLAYER__IjkExoMediaPlayer:
975 | text = context.getString(R.string.VideoView_player_IjkExoMediaPlayer);
976 | break;
977 | default:
978 | text = context.getString(R.string.N_A);
979 | break;
980 | }
981 | return text;
982 | }
983 |
984 | public IMediaPlayer createPlayer(int playerType) {
985 | IMediaPlayer mediaPlayer = null;
986 |
987 | switch (playerType) {
988 | case Settings.PV_PLAYER__IjkExoMediaPlayer: {
989 | /* IjkExoMediaPlayer IjkExoMediaPlayer = new IjkExoMediaPlayer(mAppContext);
990 | mediaPlayer = IjkExoMediaPlayer;*/
991 | AndroidMediaPlayer androidMediaPlayer = new AndroidMediaPlayer();
992 | mediaPlayer = androidMediaPlayer;
993 | }
994 | break;
995 | case Settings.PV_PLAYER__AndroidMediaPlayer: {
996 | AndroidMediaPlayer androidMediaPlayer = new AndroidMediaPlayer();
997 | mediaPlayer = androidMediaPlayer;
998 | }
999 | break;
1000 | case Settings.PV_PLAYER__IjkMediaPlayer:
1001 | default: {
1002 | IjkMediaPlayer ijkMediaPlayer = null;
1003 | if (mUri != null) {
1004 | ijkMediaPlayer = new IjkMediaPlayer();
1005 | ijkMediaPlayer.native_setLogLevel(IjkMediaPlayer.IJK_LOG_DEBUG);
1006 |
1007 | if (mSettings.getUsingMediaCodec()) {
1008 | ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 1);
1009 | if (mSettings.getUsingMediaCodecAutoRotate()) {
1010 | ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 1);
1011 | } else {
1012 | ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 0);
1013 | }
1014 | if (mSettings.getMediaCodecHandleResolutionChange()) {
1015 | ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-handle-resolution-change", 1);
1016 | } else {
1017 | ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-handle-resolution-change", 0);
1018 | }
1019 | } else {
1020 | ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 0);
1021 | }
1022 |
1023 | if (mSettings.getUsingOpenSLES()) {
1024 | ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "opensles", 1);
1025 | } else {
1026 | ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "opensles", 0);
1027 | }
1028 |
1029 | String pixelFormat = mSettings.getPixelFormat();
1030 | if (TextUtils.isEmpty(pixelFormat)) {
1031 | ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "overlay-format", IjkMediaPlayer.SDL_FCC_RV32);
1032 | } else {
1033 | ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "overlay-format", pixelFormat);
1034 | }
1035 | ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "framedrop", 1);
1036 | ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", 0);
1037 |
1038 | ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "http-detect-range-support", 0);
1039 |
1040 | ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_loop_filter", 48);
1041 | }
1042 | mediaPlayer = ijkMediaPlayer;
1043 | }
1044 | break;
1045 | }
1046 |
1047 | if (mSettings.getEnableDetachedSurfaceTextureView()) {
1048 | mediaPlayer = new TextureMediaPlayer(mediaPlayer);
1049 | }
1050 |
1051 | return mediaPlayer;
1052 | }
1053 |
1054 | //-------------------------
1055 | // Extend: Background
1056 | //-------------------------
1057 |
1058 | private boolean mEnableBackgroundPlay = false;
1059 |
1060 | private void initBackground() {
1061 | mEnableBackgroundPlay = mSettings.getEnableBackgroundPlay();
1062 | if (mEnableBackgroundPlay) {
1063 | MediaPlayerService.intentToStart(getContext());
1064 | mMediaPlayer = MediaPlayerService.getMediaPlayer();
1065 |
1066 | }
1067 | }
1068 |
1069 | public boolean isBackgroundPlayEnabled() {
1070 | return mEnableBackgroundPlay;
1071 | }
1072 |
1073 | public void enterBackground() {
1074 | MediaPlayerService.setMediaPlayer(mMediaPlayer);
1075 | }
1076 |
1077 | public void stopBackgroundPlay() {
1078 | MediaPlayerService.setMediaPlayer(null);
1079 | }
1080 |
1081 | //-------------------------
1082 | // Extend: Background
1083 | //-------------------------
1084 |
1085 |
1086 | private String buildResolution(int width, int height, int sarNum, int sarDen) {
1087 | StringBuilder sb = new StringBuilder();
1088 | sb.append(width);
1089 | sb.append(" x ");
1090 | sb.append(height);
1091 |
1092 | if (sarNum > 1 || sarDen > 1) {
1093 | sb.append("[");
1094 | sb.append(sarNum);
1095 | sb.append(":");
1096 | sb.append(sarDen);
1097 | sb.append("]");
1098 | }
1099 |
1100 | return sb.toString();
1101 | }
1102 |
1103 | private String buildTimeMilli(long duration) {
1104 | long total_seconds = duration / 1000;
1105 | long hours = total_seconds / 3600;
1106 | long minutes = (total_seconds % 3600) / 60;
1107 | long seconds = total_seconds % 60;
1108 | if (duration <= 0) {
1109 | return "--:--";
1110 | }
1111 | if (hours >= 100) {
1112 | return String.format(Locale.US, "%d:%02d:%02d", hours, minutes, seconds);
1113 | } else if (hours > 0) {
1114 | return String.format(Locale.US, "%02d:%02d:%02d", hours, minutes, seconds);
1115 | } else {
1116 | return String.format(Locale.US, "%02d:%02d", minutes, seconds);
1117 | }
1118 | }
1119 |
1120 | private String buildTrackType(int type) {
1121 | Context context = getContext();
1122 | switch (type) {
1123 | case ITrackInfo.MEDIA_TRACK_TYPE_VIDEO:
1124 | return context.getString(R.string.TrackType_video);
1125 | case ITrackInfo.MEDIA_TRACK_TYPE_AUDIO:
1126 | return context.getString(R.string.TrackType_audio);
1127 | case ITrackInfo.MEDIA_TRACK_TYPE_SUBTITLE:
1128 | return context.getString(R.string.TrackType_subtitle);
1129 | case ITrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT:
1130 | return context.getString(R.string.TrackType_timedtext);
1131 | case ITrackInfo.MEDIA_TRACK_TYPE_METADATA:
1132 | return context.getString(R.string.TrackType_metadata);
1133 | case ITrackInfo.MEDIA_TRACK_TYPE_UNKNOWN:
1134 | default:
1135 | return context.getString(R.string.TrackType_unknown);
1136 | }
1137 | }
1138 |
1139 | private String buildLanguage(String language) {
1140 | if (TextUtils.isEmpty(language))
1141 | return "und";
1142 | return language;
1143 | }
1144 |
1145 | public ITrackInfo[] getTrackInfo() {
1146 | if (mMediaPlayer == null)
1147 | return null;
1148 |
1149 | return mMediaPlayer.getTrackInfo();
1150 | }
1151 |
1152 | public void selectTrack(int stream) {
1153 | MediaPlayerCompat.selectTrack(mMediaPlayer, stream);
1154 | }
1155 |
1156 | public void deselectTrack(int stream) {
1157 | MediaPlayerCompat.deselectTrack(mMediaPlayer, stream);
1158 | }
1159 |
1160 | public int getSelectedTrack(int trackType) {
1161 | return MediaPlayerCompat.getSelectedTrack(mMediaPlayer, trackType);
1162 | }
1163 |
1164 | //added by wangshuo start
1165 | /**
1166 | * 设置旋转角度
1167 | */
1168 | public void setPlayerRotation(int rotation) {
1169 | mVideoRotationDegree = rotation;
1170 | if (mRenderView != null) {
1171 | mRenderView.setVideoRotation(mVideoRotationDegree);
1172 | }
1173 | }
1174 |
1175 | /**
1176 | * 设置屏幕铺满方式 873行 AR_ASPECT_FIT_PARENT
1177 | 1. fitParent:可能会剪裁,保持原视频的大小,显示在中心,当原视频的大小超过 view 的大小超过部分裁剪处理
1178 | 2. fillParent:可能会剪裁,等比例放大视频,直到填满 View 为止,超过 View 的部分作裁剪处理
1179 | 3. wrapcontent:将视频的内容完整居中显示,如果视频大于 view,则按比例缩视频直到完全显示在 view 中
1180 | 4. fitXY:不剪裁,非等比例拉伸画面填满整个 View
1181 | 5. f16_9:不剪裁,非等比例拉伸画面到 16:9,并完全显示在 View 中
1182 | 6. f4_3:不剪裁,非等比例拉伸画面到 4:3,并完全显示在 View 中
1183 | */
1184 | public void setDisplayAspectRatio(int AspectRatio) {
1185 | mCurrentAspectRatio = AspectRatio;
1186 | if (mRenderView != null) {
1187 | mRenderView.setAspectRatio(mCurrentAspectRatio);
1188 | }
1189 | }
1190 |
1191 | /***
1192 | * 获得IMediaPlayer
1193 | */
1194 | public IMediaPlayer getMediaPlayer(){
1195 | if(mMediaPlayer == null){
1196 | Log.e(TAG,"mMediaPlayer = null");
1197 | return null;
1198 | }
1199 | return mMediaPlayer;
1200 | }
1201 | //added by wangshuo end
1202 | }
1203 |
--------------------------------------------------------------------------------