Commit ac138b3a03d56826adabc3f75241a5370514ad81
0 parents
新建播放SDK demo
Showing
62 changed files
with
3610 additions
and
0 deletions
.gitignore
0 → 100644
.idea/compiler.xml
0 → 100644
| 1 | +++ a/.idea/compiler.xml | ||
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | +<project version="4"> | ||
| 3 | + <component name="CompilerConfiguration"> | ||
| 4 | + <resourceExtensions /> | ||
| 5 | + <wildcardResourcePatterns> | ||
| 6 | + <entry name="!?*.java" /> | ||
| 7 | + <entry name="!?*.form" /> | ||
| 8 | + <entry name="!?*.class" /> | ||
| 9 | + <entry name="!?*.groovy" /> | ||
| 10 | + <entry name="!?*.scala" /> | ||
| 11 | + <entry name="!?*.flex" /> | ||
| 12 | + <entry name="!?*.kt" /> | ||
| 13 | + <entry name="!?*.clj" /> | ||
| 14 | + <entry name="!?*.aj" /> | ||
| 15 | + </wildcardResourcePatterns> | ||
| 16 | + <annotationProcessing> | ||
| 17 | + <profile default="true" name="Default" enabled="false"> | ||
| 18 | + <processorPath useClasspath="true" /> | ||
| 19 | + </profile> | ||
| 20 | + </annotationProcessing> | ||
| 21 | + </component> | ||
| 22 | +</project> | ||
| 0 | \ No newline at end of file | 23 | \ No newline at end of file |
.idea/copyright/profiles_settings.xml
0 → 100644
.idea/gradle.xml
0 → 100644
| 1 | +++ a/.idea/gradle.xml | ||
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | +<project version="4"> | ||
| 3 | + <component name="GradleSettings"> | ||
| 4 | + <option name="linkedExternalProjectsSettings"> | ||
| 5 | + <GradleProjectSettings> | ||
| 6 | + <option name="distributionType" value="LOCAL" /> | ||
| 7 | + <option name="externalProjectPath" value="$PROJECT_DIR$" /> | ||
| 8 | + <option name="gradleHome" value="C:\Program Files\Android\Android Studio\gradle\gradle-2.14.1" /> | ||
| 9 | + <option name="modules"> | ||
| 10 | + <set> | ||
| 11 | + <option value="$PROJECT_DIR$" /> | ||
| 12 | + <option value="$PROJECT_DIR$/app" /> | ||
| 13 | + </set> | ||
| 14 | + </option> | ||
| 15 | + <option name="resolveModulePerSourceSet" value="false" /> | ||
| 16 | + </GradleProjectSettings> | ||
| 17 | + </option> | ||
| 18 | + </component> | ||
| 19 | +</project> | ||
| 0 | \ No newline at end of file | 20 | \ No newline at end of file |
.idea/misc.xml
0 → 100644
| 1 | +++ a/.idea/misc.xml | ||
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | +<project version="4"> | ||
| 3 | + <component name="EntryPointsManager"> | ||
| 4 | + <entry_points version="2.0" /> | ||
| 5 | + </component> | ||
| 6 | + <component name="NullableNotNullManager"> | ||
| 7 | + <option name="myDefaultNullable" value="android.support.annotation.Nullable" /> | ||
| 8 | + <option name="myDefaultNotNull" value="android.support.annotation.NonNull" /> | ||
| 9 | + <option name="myNullables"> | ||
| 10 | + <value> | ||
| 11 | + <list size="4"> | ||
| 12 | + <item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" /> | ||
| 13 | + <item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" /> | ||
| 14 | + <item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" /> | ||
| 15 | + <item index="3" class="java.lang.String" itemvalue="android.support.annotation.Nullable" /> | ||
| 16 | + </list> | ||
| 17 | + </value> | ||
| 18 | + </option> | ||
| 19 | + <option name="myNotNulls"> | ||
| 20 | + <value> | ||
| 21 | + <list size="4"> | ||
| 22 | + <item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" /> | ||
| 23 | + <item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" /> | ||
| 24 | + <item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" /> | ||
| 25 | + <item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" /> | ||
| 26 | + </list> | ||
| 27 | + </value> | ||
| 28 | + </option> | ||
| 29 | + </component> | ||
| 30 | + <component name="ProjectLevelVcsManager" settingsEditedManually="false"> | ||
| 31 | + <OptionsSetting value="true" id="Add" /> | ||
| 32 | + <OptionsSetting value="true" id="Remove" /> | ||
| 33 | + <OptionsSetting value="true" id="Checkout" /> | ||
| 34 | + <OptionsSetting value="true" id="Update" /> | ||
| 35 | + <OptionsSetting value="true" id="Status" /> | ||
| 36 | + <OptionsSetting value="true" id="Edit" /> | ||
| 37 | + <ConfirmationsSetting value="0" id="Add" /> | ||
| 38 | + <ConfirmationsSetting value="0" id="Remove" /> | ||
| 39 | + </component> | ||
| 40 | + <component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" default="true" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK"> | ||
| 41 | + <output url="file://$PROJECT_DIR$/build/classes" /> | ||
| 42 | + </component> | ||
| 43 | + <component name="ProjectType"> | ||
| 44 | + <option name="id" value="Android" /> | ||
| 45 | + </component> | ||
| 46 | +</project> | ||
| 0 | \ No newline at end of file | 47 | \ No newline at end of file |
.idea/modules.xml
0 → 100644
| 1 | +++ a/.idea/modules.xml | ||
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | +<project version="4"> | ||
| 3 | + <component name="ProjectModuleManager"> | ||
| 4 | + <modules> | ||
| 5 | + <module fileurl="file://$PROJECT_DIR$/QNVideoDemo.iml" filepath="$PROJECT_DIR$/QNVideoDemo.iml" /> | ||
| 6 | + <module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" /> | ||
| 7 | + </modules> | ||
| 8 | + </component> | ||
| 9 | +</project> | ||
| 0 | \ No newline at end of file | 10 | \ No newline at end of file |
.idea/runConfigurations.xml
0 → 100644
| 1 | +++ a/.idea/runConfigurations.xml | ||
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | +<project version="4"> | ||
| 3 | + <component name="RunConfigurationProducerService"> | ||
| 4 | + <option name="ignoredProducers"> | ||
| 5 | + <set> | ||
| 6 | + <option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" /> | ||
| 7 | + <option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" /> | ||
| 8 | + <option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" /> | ||
| 9 | + </set> | ||
| 10 | + </option> | ||
| 11 | + </component> | ||
| 12 | +</project> | ||
| 0 | \ No newline at end of file | 13 | \ No newline at end of file |
.idea/vcs.xml
0 → 100644
app/.gitignore
0 → 100644
app/build.gradle
0 → 100644
| 1 | +++ a/app/build.gradle | ||
| 1 | +apply plugin: 'com.android.application' | ||
| 2 | + | ||
| 3 | +android { | ||
| 4 | + compileSdkVersion 24 | ||
| 5 | + buildToolsVersion "24.0.3" | ||
| 6 | + defaultConfig { | ||
| 7 | + applicationId "com.cnlive.demo.video" | ||
| 8 | + minSdkVersion 16 | ||
| 9 | + targetSdkVersion 24 | ||
| 10 | + versionCode 1 | ||
| 11 | + versionName "1.0" | ||
| 12 | + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" | ||
| 13 | + } | ||
| 14 | + buildTypes { | ||
| 15 | + release { | ||
| 16 | + minifyEnabled false | ||
| 17 | + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' | ||
| 18 | + } | ||
| 19 | + } | ||
| 20 | +} | ||
| 21 | + | ||
| 22 | +dependencies { | ||
| 23 | + compile fileTree(include: ['*.jar'], dir: 'libs') | ||
| 24 | + androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { | ||
| 25 | + exclude group: 'com.android.support', module: 'support-annotations' | ||
| 26 | + }) | ||
| 27 | + compile 'com.android.support:appcompat-v7:24.2.1' | ||
| 28 | + testCompile 'junit:junit:4.12' | ||
| 29 | + compile files('libs/libcnlivevideo.jar') | ||
| 30 | + compile files('libs/libcnliveutilplus.jar') | ||
| 31 | +} |
app/libs/libcnliveutilplus.jar
0 → 100644
No preview for this file type
app/libs/libcnlivevideo.jar
0 → 100644
No preview for this file type
app/proguard-rules.pro
0 → 100644
| 1 | +++ a/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:\android_sdk/tools/proguard/proguard-android.txt | ||
| 4 | +# You can edit the include path and order by changing the proguardFiles | ||
| 5 | +# directive in build.gradle. | ||
| 6 | +# | ||
| 7 | +# For more details, see | ||
| 8 | +# http://developer.android.com/guide/developing/tools/proguard.html | ||
| 9 | + | ||
| 10 | +# Add any project specific keep options here: | ||
| 11 | + | ||
| 12 | +# If your project uses WebView with JS, uncomment the following | ||
| 13 | +# and specify the fully qualified class name to the JavaScript interface | ||
| 14 | +# class: | ||
| 15 | +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { | ||
| 16 | +# public *; | ||
| 17 | +#} |
app/src/androidTest/java/com/cnlive/demo/video/ExampleInstrumentedTest.java
0 → 100644
| 1 | +++ a/app/src/androidTest/java/com/cnlive/demo/video/ExampleInstrumentedTest.java | ||
| 1 | +package com.cnlive.demo.video; | ||
| 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 <a href="http://d.android.com/tools/testing">Testing documentation</a> | ||
| 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.cnlive.demo.video", appContext.getPackageName()); | ||
| 25 | + } | ||
| 26 | +} |
app/src/main/AndroidManifest.xml
0 → 100644
| 1 | +++ a/app/src/main/AndroidManifest.xml | ||
| 1 | +<?xml version="1.0" encoding="utf-8"?> | ||
| 2 | +<manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||
| 3 | + package="com.cnlive.demo.video"> | ||
| 4 | + | ||
| 5 | + <uses-permission android:name="android.permission.INTERNET" /> | ||
| 6 | + <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> | ||
| 7 | + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> | ||
| 8 | + <uses-permission android:name="android.permission.WAKE_LOCK" /> | ||
| 9 | + <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> | ||
| 10 | + | ||
| 11 | + <application | ||
| 12 | + android:name=".VideoApplication" | ||
| 13 | + android:allowBackup="true" | ||
| 14 | + android:icon="@mipmap/ic_launcher" | ||
| 15 | + android:label="@string/app_name" | ||
| 16 | + android:supportsRtl="true" | ||
| 17 | + android:theme="@style/AppTheme"> | ||
| 18 | + <activity android:name=".MainActivity"> | ||
| 19 | + <intent-filter> | ||
| 20 | + <action android:name="android.intent.action.MAIN" /> | ||
| 21 | + | ||
| 22 | + <category android:name="android.intent.category.LAUNCHER" /> | ||
| 23 | + </intent-filter> | ||
| 24 | + </activity> | ||
| 25 | + <activity android:name=".CNMediaPlayerActivity" /> | ||
| 26 | + <activity android:name=".CNAudioPlayerActivity" /> | ||
| 27 | + <activity android:name=".CNVideoViewActivity" /> | ||
| 28 | + <activity android:name=".CNVideoTextureActivity" /> | ||
| 29 | + <activity android:name=".VideoViewActivity" /> | ||
| 30 | + <activity android:name=".VideoFileActivity" /> | ||
| 31 | + </application> | ||
| 32 | + | ||
| 33 | +</manifest> | ||
| 0 | \ No newline at end of file | 34 | \ No newline at end of file |
app/src/main/java/com/cnlive/demo/video/CNAudioPlayerActivity.java
0 → 100644
| 1 | +++ a/app/src/main/java/com/cnlive/demo/video/CNAudioPlayerActivity.java | ||
| 1 | +package com.cnlive.demo.video; | ||
| 2 | + | ||
| 3 | +import android.content.Context; | ||
| 4 | +import android.media.AudioManager; | ||
| 5 | +import android.os.Bundle; | ||
| 6 | +import android.os.Handler; | ||
| 7 | +import android.os.Looper; | ||
| 8 | +import android.os.Message; | ||
| 9 | +import android.os.PowerManager; | ||
| 10 | +import android.support.v7.app.AppCompatActivity; | ||
| 11 | +import android.util.Log; | ||
| 12 | +import android.view.View; | ||
| 13 | +import android.widget.Toast; | ||
| 14 | + | ||
| 15 | +import com.cnlive.demo.video.utils.Utils; | ||
| 16 | +import com.cnlive.libs.lib_cnvideo.base.IAVOptions; | ||
| 17 | +import com.cnlive.libs.lib_cnvideo.base.IMediaPlayer; | ||
| 18 | +import com.cnlive.libs.lib_cnvideo.player.AVOptions; | ||
| 19 | +import com.cnlive.libs.lib_cnvideo.player.MediaPlayer; | ||
| 20 | + | ||
| 21 | +import java.io.IOException; | ||
| 22 | + | ||
| 23 | +/** | ||
| 24 | + * This demo shows how to use PLMediaPlayer API playing audio stream | ||
| 25 | + */ | ||
| 26 | +public class CNAudioPlayerActivity extends AppCompatActivity { | ||
| 27 | + | ||
| 28 | + private static final String TAG = CNAudioPlayerActivity.class.getSimpleName(); | ||
| 29 | + | ||
| 30 | + private static final int MESSAGE_ID_RECONNECTING = 0x01; | ||
| 31 | + | ||
| 32 | + private MediaPlayer mMediaPlayer; | ||
| 33 | + private String mAudioPath; | ||
| 34 | + private View mLoadingView; | ||
| 35 | + private AVOptions mAVOptions; | ||
| 36 | + private boolean mIsStopped = false; | ||
| 37 | + private boolean mIsActivityPaused = true; | ||
| 38 | + private Toast mToast = null; | ||
| 39 | + | ||
| 40 | + @Override | ||
| 41 | + protected void onCreate(Bundle savedInstanceState) { | ||
| 42 | + super.onCreate(savedInstanceState); | ||
| 43 | + setContentView(R.layout.activity_audio_player); | ||
| 44 | + mLoadingView = findViewById(R.id.LoadingView); | ||
| 45 | + mAudioPath = getIntent().getStringExtra("videoPath"); | ||
| 46 | + | ||
| 47 | + //配置播放参数 | ||
| 48 | + mAVOptions = new AVOptions(); | ||
| 49 | + | ||
| 50 | + int isLiveStreaming = getIntent().getIntExtra("liveStreaming", 1); | ||
| 51 | + // | ||
| 52 | + mAVOptions.setInteger(IAVOptions.KEY_PREPARE_TIMEOUT, 10 * 1000); | ||
| 53 | + mAVOptions.setInteger(IAVOptions.KEY_GET_AV_FRAME_TIMEOUT, 10 * 1000); | ||
| 54 | + // Some optimization with buffering mechanism when be set to 1 | ||
| 55 | + mAVOptions.setInteger(IAVOptions.KEY_LIVE_STREAMING, isLiveStreaming); | ||
| 56 | + if (isLiveStreaming == 1) { | ||
| 57 | + mAVOptions.setInteger(IAVOptions.KEY_DELAY_OPTIMIZATION, 1); | ||
| 58 | + } | ||
| 59 | + | ||
| 60 | + // 1 -> hw codec enable, 0 -> disable [recommended] | ||
| 61 | + int codec = getIntent().getIntExtra("mediaCodec", 0); | ||
| 62 | + mAVOptions.setInteger(IAVOptions.KEY_MEDIACODEC, codec); | ||
| 63 | + | ||
| 64 | + // whether start play automatically after prepared, default value is 1 | ||
| 65 | + mAVOptions.setInteger(IAVOptions.KEY_START_ON_PREPARED, 0); | ||
| 66 | + | ||
| 67 | + AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); | ||
| 68 | + audioManager.requestAudioFocus(null, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN); | ||
| 69 | + | ||
| 70 | + prepare(); | ||
| 71 | + } | ||
| 72 | + | ||
| 73 | + @Override | ||
| 74 | + protected void onDestroy() { | ||
| 75 | + super.onDestroy(); | ||
| 76 | + release(); | ||
| 77 | + AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); | ||
| 78 | + audioManager.abandonAudioFocus(null); | ||
| 79 | + } | ||
| 80 | + | ||
| 81 | + @Override | ||
| 82 | + protected void onResume() { | ||
| 83 | + super.onResume(); | ||
| 84 | + mIsActivityPaused = false; | ||
| 85 | + // mMediaPlayer.start(); | ||
| 86 | + } | ||
| 87 | + | ||
| 88 | + @Override | ||
| 89 | + protected void onPause() { | ||
| 90 | + super.onPause(); | ||
| 91 | + mIsActivityPaused = true; | ||
| 92 | + // mMediaPlayer.pause(); | ||
| 93 | + } | ||
| 94 | + | ||
| 95 | + public void onClickPlay(View v) { | ||
| 96 | + if (mIsStopped) { | ||
| 97 | + prepare(); | ||
| 98 | + } else { | ||
| 99 | + mMediaPlayer.start(); | ||
| 100 | + } | ||
| 101 | + } | ||
| 102 | + | ||
| 103 | + public void onClickPause(View v) { | ||
| 104 | + if (mMediaPlayer != null) { | ||
| 105 | + mMediaPlayer.pause(); | ||
| 106 | + } | ||
| 107 | + } | ||
| 108 | + | ||
| 109 | + public void onClickResume(View v) { | ||
| 110 | + if (mMediaPlayer != null) { | ||
| 111 | + mMediaPlayer.start(); | ||
| 112 | + } | ||
| 113 | + } | ||
| 114 | + | ||
| 115 | + public void onClickStop(View v) { | ||
| 116 | + if (mMediaPlayer != null) { | ||
| 117 | + mMediaPlayer.stop(); | ||
| 118 | + mMediaPlayer.reset(); | ||
| 119 | + } | ||
| 120 | + mIsStopped = true; | ||
| 121 | + mMediaPlayer = null; | ||
| 122 | + } | ||
| 123 | + | ||
| 124 | + public void release() { | ||
| 125 | + if (mMediaPlayer != null) { | ||
| 126 | + mMediaPlayer.stop(); | ||
| 127 | + mMediaPlayer.release(); | ||
| 128 | + mMediaPlayer = null; | ||
| 129 | + } | ||
| 130 | + } | ||
| 131 | + | ||
| 132 | + private void prepare() { | ||
| 133 | + if (mMediaPlayer == null) { | ||
| 134 | + mMediaPlayer = new MediaPlayer(getApplicationContext(), mAVOptions); | ||
| 135 | + mMediaPlayer.setOnPreparedListener(mOnPreparedListener); | ||
| 136 | + mMediaPlayer.setOnCompletionListener(mOnCompletionListener); | ||
| 137 | + mMediaPlayer.setOnErrorListener(mOnErrorListener); | ||
| 138 | + mMediaPlayer.setOnInfoListener(mOnInfoListener); | ||
| 139 | + mMediaPlayer.setOnBufferingUpdateListener(mOnBufferingUpdateListener); | ||
| 140 | + mMediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK); | ||
| 141 | + } | ||
| 142 | + try { | ||
| 143 | + mMediaPlayer.setDataSource(mAudioPath); | ||
| 144 | + mMediaPlayer.prepareAsync(); | ||
| 145 | + } catch (IllegalArgumentException e) { | ||
| 146 | + e.printStackTrace(); | ||
| 147 | + } catch (IllegalStateException e) { | ||
| 148 | + e.printStackTrace(); | ||
| 149 | + } catch (IOException e) { | ||
| 150 | + e.printStackTrace(); | ||
| 151 | + } | ||
| 152 | + } | ||
| 153 | + | ||
| 154 | + private IMediaPlayer.OnPreparedListener mOnPreparedListener = new IMediaPlayer.OnPreparedListener() { | ||
| 155 | + @Override | ||
| 156 | + public void onPrepared(IMediaPlayer mp) { | ||
| 157 | + Log.i(TAG, "On Prepared !"); | ||
| 158 | + mMediaPlayer.start(); | ||
| 159 | + mIsStopped = false; | ||
| 160 | + } | ||
| 161 | + }; | ||
| 162 | + | ||
| 163 | + private IMediaPlayer.OnInfoListener mOnInfoListener = new IMediaPlayer.OnInfoListener() { | ||
| 164 | + @Override | ||
| 165 | + public boolean onInfo(IMediaPlayer mp, int what, int extra) { | ||
| 166 | + Log.i(TAG, "OnInfo, what = " + what + ", extra = " + extra); | ||
| 167 | + switch (what) { | ||
| 168 | + case IMediaPlayer.MEDIA_INFO_BUFFERING_START: | ||
| 169 | + mLoadingView.setVisibility(View.VISIBLE); | ||
| 170 | + break; | ||
| 171 | + case IMediaPlayer.MEDIA_INFO_BUFFERING_END: | ||
| 172 | + case IMediaPlayer.MEDIA_INFO_AUDIO_RENDERING_START: | ||
| 173 | + mLoadingView.setVisibility(View.GONE); | ||
| 174 | + break; | ||
| 175 | + default: | ||
| 176 | + break; | ||
| 177 | + } | ||
| 178 | + return true; | ||
| 179 | + } | ||
| 180 | + }; | ||
| 181 | + | ||
| 182 | + private IMediaPlayer.OnBufferingUpdateListener mOnBufferingUpdateListener = new IMediaPlayer.OnBufferingUpdateListener() { | ||
| 183 | + @Override | ||
| 184 | + public void onBufferingUpdate(IMediaPlayer mp, int percent) { | ||
| 185 | + Log.d(TAG, "onBufferingUpdate: " + percent + "%"); | ||
| 186 | + } | ||
| 187 | + }; | ||
| 188 | + | ||
| 189 | + /** | ||
| 190 | + * Listen the event of playing complete | ||
| 191 | + * For playing local file, it's called when reading the file EOF | ||
| 192 | + * For playing network stream, it's called when the buffered bytes played over | ||
| 193 | + * | ||
| 194 | + * If setLooping(true) is called, the player will restart automatically | ||
| 195 | + * And `onCompletion` will not be called | ||
| 196 | + */ | ||
| 197 | + private IMediaPlayer.OnCompletionListener mOnCompletionListener = new IMediaPlayer.OnCompletionListener() { | ||
| 198 | + @Override | ||
| 199 | + public void onCompletion(IMediaPlayer mp) { | ||
| 200 | + Log.d(TAG, "Play Completed !"); | ||
| 201 | + } | ||
| 202 | + }; | ||
| 203 | + | ||
| 204 | + private IMediaPlayer.OnErrorListener mOnErrorListener = new IMediaPlayer.OnErrorListener() { | ||
| 205 | + @Override | ||
| 206 | + public boolean onError(IMediaPlayer mp, int errorCode) { | ||
| 207 | + boolean isNeedReconnect = false; | ||
| 208 | + Log.e(TAG, "Error happened, errorCode = " + errorCode); | ||
| 209 | + switch (errorCode) { | ||
| 210 | + case IMediaPlayer.ERROR_CODE_INVALID_URI: | ||
| 211 | + showToastTips("Invalid URL !"); | ||
| 212 | + break; | ||
| 213 | + case IMediaPlayer.ERROR_CODE_404_NOT_FOUND: | ||
| 214 | + showToastTips("404 resource not found !"); | ||
| 215 | + break; | ||
| 216 | + case IMediaPlayer.ERROR_CODE_CONNECTION_REFUSED: | ||
| 217 | + showToastTips("Connection refused !"); | ||
| 218 | + break; | ||
| 219 | + case IMediaPlayer.ERROR_CODE_CONNECTION_TIMEOUT: | ||
| 220 | + showToastTips("Connection timeout !"); | ||
| 221 | + isNeedReconnect = true; | ||
| 222 | + break; | ||
| 223 | + case IMediaPlayer.ERROR_CODE_EMPTY_PLAYLIST: | ||
| 224 | + showToastTips("Empty playlist !"); | ||
| 225 | + break; | ||
| 226 | + case IMediaPlayer.ERROR_CODE_STREAM_DISCONNECTED: | ||
| 227 | + showToastTips("Stream disconnected !"); | ||
| 228 | + isNeedReconnect = true; | ||
| 229 | + break; | ||
| 230 | + case IMediaPlayer.ERROR_CODE_IO_ERROR: | ||
| 231 | + showToastTips("Network IO Error !"); | ||
| 232 | + isNeedReconnect = true; | ||
| 233 | + break; | ||
| 234 | + case IMediaPlayer.ERROR_CODE_UNAUTHORIZED: | ||
| 235 | + showToastTips("Unauthorized Error !"); | ||
| 236 | + break; | ||
| 237 | + case IMediaPlayer.ERROR_CODE_PREPARE_TIMEOUT: | ||
| 238 | + showToastTips("Prepare timeout !"); | ||
| 239 | + isNeedReconnect = true; | ||
| 240 | + break; | ||
| 241 | + case IMediaPlayer.ERROR_CODE_READ_FRAME_TIMEOUT: | ||
| 242 | + showToastTips("Read frame timeout !"); | ||
| 243 | + isNeedReconnect = true; | ||
| 244 | + break; | ||
| 245 | + case IMediaPlayer.MEDIA_ERROR_UNKNOWN: | ||
| 246 | + break; | ||
| 247 | + default: | ||
| 248 | + showToastTips("unknown error !"); | ||
| 249 | + break; | ||
| 250 | + } | ||
| 251 | + // Todo pls handle the error status here, reconnect or call finish() | ||
| 252 | + release(); | ||
| 253 | + if (isNeedReconnect) { | ||
| 254 | + sendReconnectMessage(); | ||
| 255 | + } else { | ||
| 256 | + finish(); | ||
| 257 | + } | ||
| 258 | + // Return true means the error has been handled | ||
| 259 | + // If return false, then `onCompletion` will be called | ||
| 260 | + return true; | ||
| 261 | + } | ||
| 262 | + }; | ||
| 263 | + | ||
| 264 | + private void showToastTips(final String tips) { | ||
| 265 | + if (mIsActivityPaused) { | ||
| 266 | + return; | ||
| 267 | + } | ||
| 268 | + runOnUiThread(new Runnable() { | ||
| 269 | + @Override | ||
| 270 | + public void run() { | ||
| 271 | + if (mToast != null) { | ||
| 272 | + mToast.cancel(); | ||
| 273 | + } | ||
| 274 | + mToast = Toast.makeText(CNAudioPlayerActivity.this, tips, Toast.LENGTH_SHORT); | ||
| 275 | + mToast.show(); | ||
| 276 | + } | ||
| 277 | + }); | ||
| 278 | + } | ||
| 279 | + | ||
| 280 | + private void sendReconnectMessage() { | ||
| 281 | + showToastTips("正在重连..."); | ||
| 282 | + mLoadingView.setVisibility(View.VISIBLE); | ||
| 283 | + mHandler.removeCallbacksAndMessages(null); | ||
| 284 | + mHandler.sendMessageDelayed(mHandler.obtainMessage(MESSAGE_ID_RECONNECTING), 500); | ||
| 285 | + } | ||
| 286 | + | ||
| 287 | + protected Handler mHandler = new Handler(Looper.getMainLooper()) { | ||
| 288 | + @Override | ||
| 289 | + public void handleMessage(Message msg) { | ||
| 290 | + if (msg.what != MESSAGE_ID_RECONNECTING) { | ||
| 291 | + return; | ||
| 292 | + } | ||
| 293 | + if (mIsActivityPaused || !Utils.isLiveStreamingAvailable()) { | ||
| 294 | + finish(); | ||
| 295 | + return; | ||
| 296 | + } | ||
| 297 | + if (!Utils.isNetworkAvailable(CNAudioPlayerActivity.this)) { | ||
| 298 | + sendReconnectMessage(); | ||
| 299 | + return; | ||
| 300 | + } | ||
| 301 | + // The PLMediaPlayer has moved to the Error state, if you want to retry, must reset first ! | ||
| 302 | + prepare(); | ||
| 303 | + } | ||
| 304 | + }; | ||
| 305 | +} |
app/src/main/java/com/cnlive/demo/video/CNMediaPlayerActivity.java
0 → 100644
| 1 | +++ a/app/src/main/java/com/cnlive/demo/video/CNMediaPlayerActivity.java | ||
| 1 | +package com.cnlive.demo.video; | ||
| 2 | + | ||
| 3 | +import android.content.Context; | ||
| 4 | +import android.media.AudioManager; | ||
| 5 | +import android.os.Bundle; | ||
| 6 | +import android.os.Handler; | ||
| 7 | +import android.os.Looper; | ||
| 8 | +import android.os.Message; | ||
| 9 | +import android.os.PowerManager; | ||
| 10 | +import android.util.Log; | ||
| 11 | +import android.view.Gravity; | ||
| 12 | +import android.view.SurfaceHolder; | ||
| 13 | +import android.view.SurfaceView; | ||
| 14 | +import android.view.View; | ||
| 15 | +import android.widget.FrameLayout; | ||
| 16 | +import android.widget.Toast; | ||
| 17 | + | ||
| 18 | +import com.cnlive.demo.video.utils.Utils; | ||
| 19 | +import com.cnlive.libs.lib_cnvideo.base.IAVOptions; | ||
| 20 | +import com.cnlive.libs.lib_cnvideo.base.IDataSource; | ||
| 21 | +import com.cnlive.libs.lib_cnvideo.base.IMediaPlayer; | ||
| 22 | +import com.cnlive.libs.lib_cnvideo.player.AVOptions; | ||
| 23 | +import com.cnlive.libs.lib_cnvideo.player.MediaPlayer; | ||
| 24 | +import com.cnlive.libs.util.Config; | ||
| 25 | + | ||
| 26 | +import java.io.IOException; | ||
| 27 | + | ||
| 28 | +/** | ||
| 29 | + * This demo shows how to use IMediaPlayer API playing video stream | ||
| 30 | + */ | ||
| 31 | +public class CNMediaPlayerActivity extends VideoPlayerBaseActivity { | ||
| 32 | + | ||
| 33 | + private static final String TAG = CNMediaPlayerActivity.class.getSimpleName(); | ||
| 34 | + | ||
| 35 | + private static final int MESSAGE_ID_RECONNECTING = 0x01; | ||
| 36 | + | ||
| 37 | + private SurfaceView mSurfaceView; | ||
| 38 | + private MediaPlayer mMediaPlayer; | ||
| 39 | + private View mLoadingView; | ||
| 40 | + private AVOptions mAVOptions; | ||
| 41 | + | ||
| 42 | + private int mSurfaceWidth = 0; | ||
| 43 | + private int mSurfaceHeight = 0; | ||
| 44 | + | ||
| 45 | + private String mVideoPath = null; | ||
| 46 | + private boolean mIsStopped = false; | ||
| 47 | + private boolean mIsActivityPaused = true; | ||
| 48 | + | ||
| 49 | + private Toast mToast = null; | ||
| 50 | + | ||
| 51 | + @Override | ||
| 52 | + protected void onCreate(Bundle savedInstanceState) { | ||
| 53 | + super.onCreate(savedInstanceState); | ||
| 54 | + setContentView(R.layout.activity_media_player); | ||
| 55 | + mLoadingView = findViewById(R.id.LoadingView); | ||
| 56 | + mSurfaceView = (SurfaceView) findViewById(R.id.SurfaceView); | ||
| 57 | + mSurfaceView.getHolder().addCallback(mCallback); | ||
| 58 | + | ||
| 59 | + mVideoPath = getIntent().getStringExtra("videoPath"); | ||
| 60 | + | ||
| 61 | + mAVOptions = new AVOptions(); | ||
| 62 | + | ||
| 63 | + int isLiveStreaming = getIntent().getIntExtra("liveStreaming", 1); | ||
| 64 | + // the unit of timeout is ms | ||
| 65 | + mAVOptions.setInteger(IAVOptions.KEY_PREPARE_TIMEOUT, 10 * 1000); | ||
| 66 | + mAVOptions.setInteger(IAVOptions.KEY_GET_AV_FRAME_TIMEOUT, 10 * 1000); | ||
| 67 | + // Some optimization with buffering mechanism when be set to 1 | ||
| 68 | + mAVOptions.setInteger(IAVOptions.KEY_LIVE_STREAMING, isLiveStreaming); | ||
| 69 | + if (isLiveStreaming == 1) { | ||
| 70 | + mAVOptions.setInteger(IAVOptions.KEY_DELAY_OPTIMIZATION, 1); | ||
| 71 | + } | ||
| 72 | + | ||
| 73 | + // 1 -> hw codec enable, 0 -> disable [recommended] | ||
| 74 | + int codec = getIntent().getIntExtra("mediaCodec", 0); | ||
| 75 | + mAVOptions.setInteger(IAVOptions.KEY_MEDIACODEC, codec); | ||
| 76 | + | ||
| 77 | + // whether start play automatically after prepared, default value is 1 | ||
| 78 | + mAVOptions.setInteger(IAVOptions.KEY_START_ON_PREPARED, 0); | ||
| 79 | + | ||
| 80 | + AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); | ||
| 81 | + audioManager.requestAudioFocus(null, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN); | ||
| 82 | + } | ||
| 83 | + | ||
| 84 | + @Override | ||
| 85 | + protected void onDestroy() { | ||
| 86 | + super.onDestroy(); | ||
| 87 | + release(); | ||
| 88 | + AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); | ||
| 89 | + audioManager.abandonAudioFocus(null); | ||
| 90 | + } | ||
| 91 | + | ||
| 92 | + @Override | ||
| 93 | + protected void onResume() { | ||
| 94 | + super.onResume(); | ||
| 95 | + mIsActivityPaused = false; | ||
| 96 | + } | ||
| 97 | + | ||
| 98 | + @Override | ||
| 99 | + protected void onPause() { | ||
| 100 | + super.onPause(); | ||
| 101 | + mIsActivityPaused = true; | ||
| 102 | + } | ||
| 103 | + | ||
| 104 | + public void onClickPlay(View v) { | ||
| 105 | + if (mIsStopped) { | ||
| 106 | + prepare(); | ||
| 107 | + } else { | ||
| 108 | + mMediaPlayer.start(); | ||
| 109 | + } | ||
| 110 | + } | ||
| 111 | + | ||
| 112 | + public void onClickPause(View v) { | ||
| 113 | + if (mMediaPlayer != null) { | ||
| 114 | + mMediaPlayer.pause(); | ||
| 115 | + } | ||
| 116 | + } | ||
| 117 | + | ||
| 118 | + public void onClickResume(View v) { | ||
| 119 | + if (mMediaPlayer != null) { | ||
| 120 | + mMediaPlayer.start(); | ||
| 121 | + } | ||
| 122 | + } | ||
| 123 | + | ||
| 124 | + public void onClickStop(View v) { | ||
| 125 | + if (mMediaPlayer != null) { | ||
| 126 | + mMediaPlayer.stop(); | ||
| 127 | + mMediaPlayer.reset(); | ||
| 128 | + } | ||
| 129 | + mIsStopped = true; | ||
| 130 | + mMediaPlayer = null; | ||
| 131 | + } | ||
| 132 | + | ||
| 133 | + public void releaseWithoutStop() { | ||
| 134 | + if (mMediaPlayer != null) { | ||
| 135 | + mMediaPlayer.setDisplay(null); | ||
| 136 | + } | ||
| 137 | + } | ||
| 138 | + | ||
| 139 | + public void release() { | ||
| 140 | + if (mMediaPlayer != null) { | ||
| 141 | + mMediaPlayer.stop(); | ||
| 142 | + mMediaPlayer.release(); | ||
| 143 | + mMediaPlayer = null; | ||
| 144 | + } | ||
| 145 | + } | ||
| 146 | + | ||
| 147 | + private void prepare() { | ||
| 148 | + | ||
| 149 | + if (mMediaPlayer != null) { | ||
| 150 | + mMediaPlayer.setDisplay(mSurfaceView.getHolder()); | ||
| 151 | + return; | ||
| 152 | + } | ||
| 153 | + | ||
| 154 | + try { | ||
| 155 | + mMediaPlayer = new MediaPlayer(this, mAVOptions); | ||
| 156 | + | ||
| 157 | + mMediaPlayer.setOnPreparedListener(mOnPreparedListener); | ||
| 158 | + mMediaPlayer.setOnVideoSizeChangedListener(mOnVideoSizeChangedListener); | ||
| 159 | + mMediaPlayer.setOnCompletionListener(mOnCompletionListener); | ||
| 160 | + mMediaPlayer.setOnErrorListener(mOnErrorListener); | ||
| 161 | + mMediaPlayer.setOnInfoListener(mOnInfoListener); | ||
| 162 | + mMediaPlayer.setOnBufferingUpdateListener(mOnBufferingUpdateListener); | ||
| 163 | + | ||
| 164 | + // set replay if completed | ||
| 165 | + // mMediaPlayer.setLooping(true); | ||
| 166 | + | ||
| 167 | + mMediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK); | ||
| 168 | + | ||
| 169 | + mMediaPlayer.setDataSource(mVideoPath); | ||
| 170 | + //播放视讯云视频 | ||
| 171 | +// mMediaPlayer.setDataSource(new IDataSource() { | ||
| 172 | +// @Override | ||
| 173 | +// public String getId() { | ||
| 174 | +// return "qwert"; | ||
| 175 | +// } | ||
| 176 | +// | ||
| 177 | +// @Override | ||
| 178 | +// public String getType() { | ||
| 179 | +// return Config.TYPE_LIVE; | ||
| 180 | +// } | ||
| 181 | +// | ||
| 182 | +// @Override | ||
| 183 | +// public String getRate() { | ||
| 184 | +// return null; | ||
| 185 | +// } | ||
| 186 | +// }, false); | ||
| 187 | + mMediaPlayer.setDisplay(mSurfaceView.getHolder()); | ||
| 188 | + mMediaPlayer.prepareAsync(); | ||
| 189 | + } catch (UnsatisfiedLinkError e) { | ||
| 190 | + e.printStackTrace(); | ||
| 191 | + } catch (IllegalArgumentException e) { | ||
| 192 | + e.printStackTrace(); | ||
| 193 | + } catch (IllegalStateException e) { | ||
| 194 | + e.printStackTrace(); | ||
| 195 | + } | ||
| 196 | + catch (IOException e) { | ||
| 197 | + e.printStackTrace(); | ||
| 198 | + } | ||
| 199 | + } | ||
| 200 | + | ||
| 201 | + private SurfaceHolder.Callback mCallback = new SurfaceHolder.Callback() { | ||
| 202 | + | ||
| 203 | + @Override | ||
| 204 | + public void surfaceCreated(SurfaceHolder holder) { | ||
| 205 | + prepare(); | ||
| 206 | + } | ||
| 207 | + | ||
| 208 | + @Override | ||
| 209 | + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { | ||
| 210 | + mSurfaceWidth = width; | ||
| 211 | + mSurfaceHeight = height; | ||
| 212 | + } | ||
| 213 | + | ||
| 214 | + @Override | ||
| 215 | + public void surfaceDestroyed(SurfaceHolder holder) { | ||
| 216 | + // release(); | ||
| 217 | + releaseWithoutStop(); | ||
| 218 | + } | ||
| 219 | + }; | ||
| 220 | + | ||
| 221 | + private IMediaPlayer.OnVideoSizeChangedListener mOnVideoSizeChangedListener = new IMediaPlayer.OnVideoSizeChangedListener() { | ||
| 222 | + public void onVideoSizeChanged(IMediaPlayer mp, int width, int height) { | ||
| 223 | + Log.i(TAG, "onVideoSizeChanged, width = "+ width + ",height = " + height); | ||
| 224 | + // resize the display window to fit the screen | ||
| 225 | + if (width != 0 && height != 0) { | ||
| 226 | + float ratioW = (float) width/(float) mSurfaceWidth; | ||
| 227 | + float ratioH = (float) height/(float) mSurfaceHeight; | ||
| 228 | + float ratio = Math.max(ratioW, ratioH); | ||
| 229 | + width = (int) Math.ceil((float)width/ratio); | ||
| 230 | + height = (int) Math.ceil((float)height/ratio); | ||
| 231 | + FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams(width, height); | ||
| 232 | + layout.gravity = Gravity.CENTER; | ||
| 233 | + mSurfaceView.setLayoutParams(layout); | ||
| 234 | + } | ||
| 235 | + } | ||
| 236 | + }; | ||
| 237 | + | ||
| 238 | + private IMediaPlayer.OnPreparedListener mOnPreparedListener = new IMediaPlayer.OnPreparedListener() { | ||
| 239 | + @Override | ||
| 240 | + public void onPrepared(IMediaPlayer mp) { | ||
| 241 | + Log.i(TAG, "On Prepared !"); | ||
| 242 | + mMediaPlayer.start(); | ||
| 243 | + mIsStopped = false; | ||
| 244 | + } | ||
| 245 | + }; | ||
| 246 | + | ||
| 247 | + private IMediaPlayer.OnInfoListener mOnInfoListener = new IMediaPlayer.OnInfoListener() { | ||
| 248 | + @Override | ||
| 249 | + public boolean onInfo(IMediaPlayer mp, int what, int extra) { | ||
| 250 | + Log.i(TAG, "OnInfo, what = " + what + ", extra = " + extra); | ||
| 251 | + switch (what) { | ||
| 252 | + case IMediaPlayer.MEDIA_INFO_BUFFERING_START: | ||
| 253 | + mLoadingView.setVisibility(View.VISIBLE); | ||
| 254 | + break; | ||
| 255 | + case IMediaPlayer.MEDIA_INFO_BUFFERING_END: | ||
| 256 | + case IMediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START: | ||
| 257 | + mLoadingView.setVisibility(View.GONE); | ||
| 258 | + break; | ||
| 259 | + default: | ||
| 260 | + break; | ||
| 261 | + } | ||
| 262 | + return true; | ||
| 263 | + } | ||
| 264 | + }; | ||
| 265 | + | ||
| 266 | + private IMediaPlayer.OnBufferingUpdateListener mOnBufferingUpdateListener = new IMediaPlayer.OnBufferingUpdateListener() { | ||
| 267 | + @Override | ||
| 268 | + public void onBufferingUpdate(IMediaPlayer mp, int percent) { | ||
| 269 | + Log.d(TAG, "onBufferingUpdate: " + percent + "%"); | ||
| 270 | + } | ||
| 271 | + }; | ||
| 272 | + | ||
| 273 | + /** | ||
| 274 | + * Listen the event of playing complete | ||
| 275 | + * For playing local file, it's called when reading the file EOF | ||
| 276 | + * For playing network stream, it's called when the buffered bytes played over | ||
| 277 | + * | ||
| 278 | + * If setLooping(true) is called, the player will restart automatically | ||
| 279 | + * And `onCompletion` will not be called | ||
| 280 | + * | ||
| 281 | + */ | ||
| 282 | + private IMediaPlayer.OnCompletionListener mOnCompletionListener = new IMediaPlayer.OnCompletionListener() { | ||
| 283 | + @Override | ||
| 284 | + public void onCompletion(IMediaPlayer mp) { | ||
| 285 | + Log.d(TAG, "Play Completed !"); | ||
| 286 | + showToastTips("Play Completed !"); | ||
| 287 | + finish(); | ||
| 288 | + } | ||
| 289 | + }; | ||
| 290 | + | ||
| 291 | + private IMediaPlayer.OnErrorListener mOnErrorListener = new IMediaPlayer.OnErrorListener() { | ||
| 292 | + @Override | ||
| 293 | + public boolean onError(IMediaPlayer mp, int errorCode) { | ||
| 294 | + boolean isNeedReconnect = false; | ||
| 295 | + Log.e(TAG, "Error happened, errorCode = " + errorCode); | ||
| 296 | + switch (errorCode) { | ||
| 297 | + case IMediaPlayer.ERROR_CODE_INVALID_URI: | ||
| 298 | + showToastTips("Invalid URL !"); | ||
| 299 | + break; | ||
| 300 | + case IMediaPlayer.ERROR_CODE_404_NOT_FOUND: | ||
| 301 | + showToastTips("404 resource not found !"); | ||
| 302 | + break; | ||
| 303 | + case IMediaPlayer.ERROR_CODE_CONNECTION_REFUSED: | ||
| 304 | + showToastTips("Connection refused !"); | ||
| 305 | + break; | ||
| 306 | + case IMediaPlayer.ERROR_CODE_CONNECTION_TIMEOUT: | ||
| 307 | + showToastTips("Connection timeout !"); | ||
| 308 | + isNeedReconnect = true; | ||
| 309 | + break; | ||
| 310 | + case IMediaPlayer.ERROR_CODE_EMPTY_PLAYLIST: | ||
| 311 | + showToastTips("Empty playlist !"); | ||
| 312 | + break; | ||
| 313 | + case IMediaPlayer.ERROR_CODE_STREAM_DISCONNECTED: | ||
| 314 | + showToastTips("Stream disconnected !"); | ||
| 315 | + isNeedReconnect = true; | ||
| 316 | + break; | ||
| 317 | + case IMediaPlayer.ERROR_CODE_IO_ERROR: | ||
| 318 | + showToastTips("Network IO Error !"); | ||
| 319 | + isNeedReconnect = true; | ||
| 320 | + break; | ||
| 321 | + case IMediaPlayer.ERROR_CODE_UNAUTHORIZED: | ||
| 322 | + showToastTips("Unauthorized Error !"); | ||
| 323 | + break; | ||
| 324 | + case IMediaPlayer.ERROR_CODE_PREPARE_TIMEOUT: | ||
| 325 | + showToastTips("Prepare timeout !"); | ||
| 326 | + isNeedReconnect = true; | ||
| 327 | + break; | ||
| 328 | + case IMediaPlayer.ERROR_CODE_READ_FRAME_TIMEOUT: | ||
| 329 | + showToastTips("Read frame timeout !"); | ||
| 330 | + isNeedReconnect = true; | ||
| 331 | + break; | ||
| 332 | + case IMediaPlayer.MEDIA_ERROR_UNKNOWN: | ||
| 333 | + break; | ||
| 334 | + default: | ||
| 335 | + showToastTips("unknown error !"); | ||
| 336 | + break; | ||
| 337 | + } | ||
| 338 | + // Todo pls handle the error status here, reconnect or call finish() | ||
| 339 | +// release(); | ||
| 340 | +// if (isNeedReconnect) { | ||
| 341 | +// sendReconnectMessage(); | ||
| 342 | +// } else { | ||
| 343 | +// finish(); | ||
| 344 | +// } | ||
| 345 | + // Return true means the error has been handled | ||
| 346 | + // If return false, then `onCompletion` will be called | ||
| 347 | + return true; | ||
| 348 | + } | ||
| 349 | + }; | ||
| 350 | + | ||
| 351 | + private void showToastTips(final String tips) { | ||
| 352 | + if (mIsActivityPaused) { | ||
| 353 | + return; | ||
| 354 | + } | ||
| 355 | + runOnUiThread(new Runnable() { | ||
| 356 | + @Override | ||
| 357 | + public void run() { | ||
| 358 | + if (mToast != null) { | ||
| 359 | + mToast.cancel(); | ||
| 360 | + } | ||
| 361 | + mToast = Toast.makeText(CNMediaPlayerActivity.this, tips, Toast.LENGTH_SHORT); | ||
| 362 | + mToast.show(); | ||
| 363 | + } | ||
| 364 | + }); | ||
| 365 | + } | ||
| 366 | + | ||
| 367 | + private void sendReconnectMessage() { | ||
| 368 | + showToastTips("正在重连..."); | ||
| 369 | + mLoadingView.setVisibility(View.VISIBLE); | ||
| 370 | + mHandler.removeCallbacksAndMessages(null); | ||
| 371 | + mHandler.sendMessageDelayed(mHandler.obtainMessage(MESSAGE_ID_RECONNECTING), 500); | ||
| 372 | + } | ||
| 373 | + | ||
| 374 | + protected Handler mHandler = new Handler(Looper.getMainLooper()) { | ||
| 375 | + @Override | ||
| 376 | + public void handleMessage(Message msg) { | ||
| 377 | + if (msg.what != MESSAGE_ID_RECONNECTING) { | ||
| 378 | + return; | ||
| 379 | + } | ||
| 380 | + if (mIsActivityPaused || !Utils.isLiveStreamingAvailable()) { | ||
| 381 | + finish(); | ||
| 382 | + return; | ||
| 383 | + } | ||
| 384 | + if (!Utils.isNetworkAvailable(CNMediaPlayerActivity.this)) { | ||
| 385 | + sendReconnectMessage(); | ||
| 386 | + return; | ||
| 387 | + } | ||
| 388 | + prepare(); | ||
| 389 | + } | ||
| 390 | + }; | ||
| 391 | +} |
app/src/main/java/com/cnlive/demo/video/CNVideoTextureActivity.java
0 → 100644
| 1 | +++ a/app/src/main/java/com/cnlive/demo/video/CNVideoTextureActivity.java | ||
| 1 | +package com.cnlive.demo.video; | ||
| 2 | + | ||
| 3 | +import android.os.Bundle; | ||
| 4 | +import android.os.Handler; | ||
| 5 | +import android.os.Looper; | ||
| 6 | +import android.os.Message; | ||
| 7 | +import android.view.View; | ||
| 8 | +import android.widget.ImageView; | ||
| 9 | +import android.widget.Toast; | ||
| 10 | + | ||
| 11 | +import com.cnlive.demo.video.utils.Utils; | ||
| 12 | +import com.cnlive.demo.video.widget.MediaController; | ||
| 13 | +import com.cnlive.libs.lib_cnvideo.base.IAVOptions; | ||
| 14 | +import com.cnlive.libs.lib_cnvideo.base.ICNVideoTextureView; | ||
| 15 | +import com.cnlive.libs.lib_cnvideo.base.IMediaPlayer; | ||
| 16 | +import com.cnlive.libs.lib_cnvideo.player.AVOptions; | ||
| 17 | +import com.cnlive.libs.lib_cnvideo.player.widget.CNVideoTextureView; | ||
| 18 | + | ||
| 19 | +/** | ||
| 20 | + * This is a demo activity of PLVideoTextureView | ||
| 21 | + */ | ||
| 22 | +public class CNVideoTextureActivity extends VideoPlayerBaseActivity { | ||
| 23 | + | ||
| 24 | + private static final int MESSAGE_ID_RECONNECTING = 0x01; | ||
| 25 | + | ||
| 26 | + private MediaController mMediaController; | ||
| 27 | + private CNVideoTextureView mVideoView; | ||
| 28 | + private Toast mToast = null; | ||
| 29 | + private String mVideoPath = null; | ||
| 30 | + private int mRotation = 0; | ||
| 31 | + private int mDisplayAspectRatio = ICNVideoTextureView.ASPECT_RATIO_FIT_PARENT; //default | ||
| 32 | + private View mLoadingView; | ||
| 33 | + private View mCoverView = null; | ||
| 34 | + private boolean mIsActivityPaused = true; | ||
| 35 | + | ||
| 36 | + @Override | ||
| 37 | + protected void onCreate(Bundle savedInstanceState) { | ||
| 38 | + super.onCreate(savedInstanceState); | ||
| 39 | + setContentView(R.layout.activity_pl_video_texture); | ||
| 40 | + mVideoView = (CNVideoTextureView) findViewById(R.id.VideoView); | ||
| 41 | + mLoadingView = findViewById(R.id.LoadingView); | ||
| 42 | + mVideoView.setBufferingIndicator(mLoadingView); | ||
| 43 | + mLoadingView.setVisibility(View.VISIBLE); | ||
| 44 | + mCoverView = (ImageView) findViewById(R.id.CoverView); | ||
| 45 | + mVideoView.setCoverView(mCoverView); | ||
| 46 | + | ||
| 47 | + mVideoPath = getIntent().getStringExtra("videoPath"); | ||
| 48 | + | ||
| 49 | + // If you want to fix display orientation such as landscape, you can use the code show as follow | ||
| 50 | + // | ||
| 51 | + // if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { | ||
| 52 | + // mVideoView.setPreviewOrientation(0); | ||
| 53 | + // } | ||
| 54 | + // else if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) { | ||
| 55 | + // mVideoView.setPreviewOrientation(270); | ||
| 56 | + // } | ||
| 57 | + | ||
| 58 | + mVideoPath = getIntent().getStringExtra("videoPath"); | ||
| 59 | + | ||
| 60 | + AVOptions options = new AVOptions(); | ||
| 61 | + | ||
| 62 | + int isLiveStreaming = getIntent().getIntExtra("liveStreaming", 1); | ||
| 63 | + // the unit of timeout is ms | ||
| 64 | + options.setInteger(IAVOptions.KEY_PREPARE_TIMEOUT, 10 * 1000); | ||
| 65 | + options.setInteger(IAVOptions.KEY_GET_AV_FRAME_TIMEOUT, 10 * 1000); | ||
| 66 | + // Some optimization with buffering mechanism when be set to 1 | ||
| 67 | + options.setInteger(IAVOptions.KEY_LIVE_STREAMING, isLiveStreaming); | ||
| 68 | + if (isLiveStreaming == 1) { | ||
| 69 | + options.setInteger(IAVOptions.KEY_DELAY_OPTIMIZATION, 1); | ||
| 70 | + } | ||
| 71 | + | ||
| 72 | + // 1 -> hw codec enable, 0 -> disable [recommended] | ||
| 73 | + int codec = getIntent().getIntExtra("mediaCodec", 0); | ||
| 74 | + options.setInteger(IAVOptions.KEY_MEDIACODEC, codec); | ||
| 75 | + | ||
| 76 | + // whether start play automatically after prepared, default value is 1 | ||
| 77 | + options.setInteger(IAVOptions.KEY_START_ON_PREPARED, 0); | ||
| 78 | + | ||
| 79 | + mVideoView.setAVOptions(options); | ||
| 80 | + | ||
| 81 | + // You can mirror the display | ||
| 82 | + // mVideoView.setMirror(true); | ||
| 83 | + | ||
| 84 | + // You can also use a custom `MediaController` widget | ||
| 85 | + mMediaController = new MediaController(this, false, isLiveStreaming == 1); | ||
| 86 | + mVideoView.setMediaController(mMediaController); | ||
| 87 | + | ||
| 88 | + mVideoView.setOnCompletionListener(mOnCompletionListener); | ||
| 89 | + mVideoView.setOnErrorListener(mOnErrorListener); | ||
| 90 | + | ||
| 91 | + mVideoView.setVideoPath(mVideoPath); | ||
| 92 | + } | ||
| 93 | + | ||
| 94 | + @Override | ||
| 95 | + protected void onPause() { | ||
| 96 | + super.onPause(); | ||
| 97 | + mToast = null; | ||
| 98 | + mIsActivityPaused = true; | ||
| 99 | + mVideoView.pause(); | ||
| 100 | + } | ||
| 101 | + | ||
| 102 | + @Override | ||
| 103 | + protected void onResume() { | ||
| 104 | + super.onResume(); | ||
| 105 | + mIsActivityPaused = false; | ||
| 106 | + } | ||
| 107 | + | ||
| 108 | + @Override | ||
| 109 | + protected void onDestroy() { | ||
| 110 | + super.onDestroy(); | ||
| 111 | + mVideoView.stopPlayback(); | ||
| 112 | + } | ||
| 113 | + | ||
| 114 | + public void onClickRotate(View v) { | ||
| 115 | + mRotation = (mRotation + 90) % 360; | ||
| 116 | + mVideoView.setDisplayOrientation(mRotation); | ||
| 117 | + } | ||
| 118 | + | ||
| 119 | + public void onClickSwitchScreen(View v) { | ||
| 120 | + mDisplayAspectRatio = (mDisplayAspectRatio + 1) % 5; | ||
| 121 | + mVideoView.setDisplayAspectRatio(mDisplayAspectRatio); | ||
| 122 | + switch (mVideoView.getDisplayAspectRatio()) { | ||
| 123 | + case ICNVideoTextureView.ASPECT_RATIO_ORIGIN: | ||
| 124 | + showToastTips("Origin mode"); | ||
| 125 | + break; | ||
| 126 | + case ICNVideoTextureView.ASPECT_RATIO_FIT_PARENT: | ||
| 127 | + showToastTips("Fit parent !"); | ||
| 128 | + break; | ||
| 129 | + case ICNVideoTextureView.ASPECT_RATIO_PAVED_PARENT: | ||
| 130 | + showToastTips("Paved parent !"); | ||
| 131 | + break; | ||
| 132 | + case ICNVideoTextureView.ASPECT_RATIO_16_9: | ||
| 133 | + showToastTips("16 : 9 !"); | ||
| 134 | + break; | ||
| 135 | + case ICNVideoTextureView.ASPECT_RATIO_4_3: | ||
| 136 | + showToastTips("4 : 3 !"); | ||
| 137 | + break; | ||
| 138 | + default: | ||
| 139 | + break; | ||
| 140 | + } | ||
| 141 | + } | ||
| 142 | + | ||
| 143 | + private IMediaPlayer.OnPreparedListener onPreparedListener = new IMediaPlayer.OnPreparedListener() { | ||
| 144 | + @Override | ||
| 145 | + public void onPrepared(IMediaPlayer mp) { | ||
| 146 | + mVideoView.start(); | ||
| 147 | + } | ||
| 148 | + }; | ||
| 149 | + | ||
| 150 | + private IMediaPlayer.OnErrorListener mOnErrorListener = new IMediaPlayer.OnErrorListener() { | ||
| 151 | + @Override | ||
| 152 | + public boolean onError(IMediaPlayer mp, int errorCode) { | ||
| 153 | + boolean isNeedReconnect = false; | ||
| 154 | + switch (errorCode) { | ||
| 155 | + case IMediaPlayer.ERROR_CODE_INVALID_URI: | ||
| 156 | + showToastTips("Invalid URL !"); | ||
| 157 | + break; | ||
| 158 | + case IMediaPlayer.ERROR_CODE_404_NOT_FOUND: | ||
| 159 | + showToastTips("404 resource not found !"); | ||
| 160 | + break; | ||
| 161 | + case IMediaPlayer.ERROR_CODE_CONNECTION_REFUSED: | ||
| 162 | + showToastTips("Connection refused !"); | ||
| 163 | + break; | ||
| 164 | + case IMediaPlayer.ERROR_CODE_CONNECTION_TIMEOUT: | ||
| 165 | + showToastTips("Connection timeout !"); | ||
| 166 | + isNeedReconnect = true; | ||
| 167 | + break; | ||
| 168 | + case IMediaPlayer.ERROR_CODE_EMPTY_PLAYLIST: | ||
| 169 | + showToastTips("Empty playlist !"); | ||
| 170 | + break; | ||
| 171 | + case IMediaPlayer.ERROR_CODE_STREAM_DISCONNECTED: | ||
| 172 | + showToastTips("Stream disconnected !"); | ||
| 173 | + isNeedReconnect = true; | ||
| 174 | + break; | ||
| 175 | + case IMediaPlayer.ERROR_CODE_IO_ERROR: | ||
| 176 | + showToastTips("Network IO Error !"); | ||
| 177 | + isNeedReconnect = true; | ||
| 178 | + break; | ||
| 179 | + case IMediaPlayer.ERROR_CODE_UNAUTHORIZED: | ||
| 180 | + showToastTips("Unauthorized Error !"); | ||
| 181 | + break; | ||
| 182 | + case IMediaPlayer.ERROR_CODE_PREPARE_TIMEOUT: | ||
| 183 | + showToastTips("Prepare timeout !"); | ||
| 184 | + isNeedReconnect = true; | ||
| 185 | + break; | ||
| 186 | + case IMediaPlayer.ERROR_CODE_READ_FRAME_TIMEOUT: | ||
| 187 | + showToastTips("Read frame timeout !"); | ||
| 188 | + isNeedReconnect = true; | ||
| 189 | + break; | ||
| 190 | + case IMediaPlayer.MEDIA_ERROR_UNKNOWN: | ||
| 191 | + break; | ||
| 192 | + default: | ||
| 193 | + showToastTips("unknown error !"); | ||
| 194 | + break; | ||
| 195 | + } | ||
| 196 | + // Todo pls handle the error status here, reconnect or call finish() | ||
| 197 | + if (isNeedReconnect) { | ||
| 198 | + sendReconnectMessage(); | ||
| 199 | + } else { | ||
| 200 | + finish(); | ||
| 201 | + } | ||
| 202 | + // Return true means the error has been handled | ||
| 203 | + // If return false, then `onCompletion` will be called | ||
| 204 | + return true; | ||
| 205 | + } | ||
| 206 | + }; | ||
| 207 | + | ||
| 208 | + private IMediaPlayer.OnCompletionListener mOnCompletionListener = new IMediaPlayer.OnCompletionListener() { | ||
| 209 | + @Override | ||
| 210 | + public void onCompletion(IMediaPlayer plMediaPlayer) { | ||
| 211 | + showToastTips("Play Completed !"); | ||
| 212 | + finish(); | ||
| 213 | + } | ||
| 214 | + }; | ||
| 215 | + | ||
| 216 | + private void showToastTips(final String tips) { | ||
| 217 | + runOnUiThread(new Runnable() { | ||
| 218 | + @Override | ||
| 219 | + public void run() { | ||
| 220 | + if (mToast != null) { | ||
| 221 | + mToast.cancel(); | ||
| 222 | + } | ||
| 223 | + mToast = Toast.makeText(CNVideoTextureActivity.this, tips, Toast.LENGTH_SHORT); | ||
| 224 | + mToast.show(); | ||
| 225 | + } | ||
| 226 | + }); | ||
| 227 | + } | ||
| 228 | + | ||
| 229 | + private void sendReconnectMessage() { | ||
| 230 | + showToastTips("正在重连..."); | ||
| 231 | + mLoadingView.setVisibility(View.VISIBLE); | ||
| 232 | + mHandler.removeCallbacksAndMessages(null); | ||
| 233 | + mHandler.sendMessageDelayed(mHandler.obtainMessage(MESSAGE_ID_RECONNECTING), 500); | ||
| 234 | + } | ||
| 235 | + | ||
| 236 | + protected Handler mHandler = new Handler(Looper.getMainLooper()) { | ||
| 237 | + @Override | ||
| 238 | + public void handleMessage(Message msg) { | ||
| 239 | + if (msg.what != MESSAGE_ID_RECONNECTING) { | ||
| 240 | + return; | ||
| 241 | + } | ||
| 242 | + if (mIsActivityPaused || !Utils.isLiveStreamingAvailable()) { | ||
| 243 | + finish(); | ||
| 244 | + return; | ||
| 245 | + } | ||
| 246 | + if (!Utils.isNetworkAvailable(CNVideoTextureActivity.this)) { | ||
| 247 | + sendReconnectMessage(); | ||
| 248 | + return; | ||
| 249 | + } | ||
| 250 | + mVideoView.setVideoPath(mVideoPath); | ||
| 251 | + mVideoView.start(); | ||
| 252 | + } | ||
| 253 | + }; | ||
| 254 | +} |
app/src/main/java/com/cnlive/demo/video/CNVideoViewActivity.java
0 → 100644
| 1 | +++ a/app/src/main/java/com/cnlive/demo/video/CNVideoViewActivity.java | ||
| 1 | +package com.cnlive.demo.video; | ||
| 2 | + | ||
| 3 | +import android.os.Bundle; | ||
| 4 | +import android.os.Handler; | ||
| 5 | +import android.os.Looper; | ||
| 6 | +import android.os.Message; | ||
| 7 | +import android.util.Log; | ||
| 8 | +import android.view.View; | ||
| 9 | +import android.widget.ImageView; | ||
| 10 | +import android.widget.Toast; | ||
| 11 | + | ||
| 12 | +import com.cnlive.demo.video.utils.Utils; | ||
| 13 | +import com.cnlive.demo.video.widget.MediaController; | ||
| 14 | +import com.cnlive.libs.lib_cnvideo.base.IAVOptions; | ||
| 15 | +import com.cnlive.libs.lib_cnvideo.base.IDataSource; | ||
| 16 | +import com.cnlive.libs.lib_cnvideo.base.IMediaPlayer; | ||
| 17 | +import com.cnlive.libs.lib_cnvideo.base.IVideoView; | ||
| 18 | +import com.cnlive.libs.lib_cnvideo.player.AVOptions; | ||
| 19 | +import com.cnlive.libs.lib_cnvideo.player.widget.CNVideoView; | ||
| 20 | +import com.cnlive.libs.util.Config; | ||
| 21 | + | ||
| 22 | +/** | ||
| 23 | + * This is a demo activity of PLVideoView | ||
| 24 | + */ | ||
| 25 | +public class CNVideoViewActivity extends VideoPlayerBaseActivity { | ||
| 26 | + | ||
| 27 | + private static final String TAG = "CNVideoViewActivity"; | ||
| 28 | + | ||
| 29 | + private static final int MESSAGE_ID_RECONNECTING = 0x01; | ||
| 30 | + | ||
| 31 | + private MediaController mMediaController; | ||
| 32 | + private CNVideoView mVideoView; | ||
| 33 | + private Toast mToast = null; | ||
| 34 | + private String mVideoPath = null; | ||
| 35 | + private int mDisplayAspectRatio = CNVideoView.ASPECT_RATIO_FIT_PARENT; | ||
| 36 | + private boolean mIsActivityPaused = true; | ||
| 37 | + private View mLoadingView; | ||
| 38 | + private View mCoverView = null; | ||
| 39 | + | ||
| 40 | + @Override | ||
| 41 | + protected void onCreate(Bundle savedInstanceState) { | ||
| 42 | + super.onCreate(savedInstanceState); | ||
| 43 | + setContentView(R.layout.activity_pl_video_view); | ||
| 44 | + mVideoView = (CNVideoView) findViewById(R.id.VideoView); | ||
| 45 | + | ||
| 46 | + mCoverView = (ImageView) findViewById(R.id.CoverView); | ||
| 47 | + mVideoView.setCoverView(mCoverView); | ||
| 48 | + mLoadingView = findViewById(R.id.LoadingView); | ||
| 49 | + mVideoView.setBufferingIndicator(mLoadingView); | ||
| 50 | + mLoadingView.setVisibility(View.VISIBLE); | ||
| 51 | + | ||
| 52 | + mVideoPath = getIntent().getStringExtra("videoPath"); | ||
| 53 | + | ||
| 54 | + AVOptions options = new AVOptions(); | ||
| 55 | + | ||
| 56 | + int isLiveStreaming = getIntent().getIntExtra("liveStreaming", 1); | ||
| 57 | + // 准备超时时间,包括创建资源、建立连接、请求码流等,单位是 ms | ||
| 58 | + // 默认值是:无 | ||
| 59 | + options.setInteger(IAVOptions.KEY_PREPARE_TIMEOUT, 10 * 1000); | ||
| 60 | + // 读取视频流超时时间,单位是 ms | ||
| 61 | + // 默认值是:10 * 1000 | ||
| 62 | + options.setInteger(IAVOptions.KEY_GET_AV_FRAME_TIMEOUT, 10 * 1000); | ||
| 63 | + // 当前播放的是否为在线直播,如果是,则底层会有一些播放优化 | ||
| 64 | + // 默认值是:0 | ||
| 65 | + options.setInteger(IAVOptions.KEY_LIVE_STREAMING, isLiveStreaming); | ||
| 66 | + if (isLiveStreaming == 1) { | ||
| 67 | + // 是否开启"延时优化",只在在线直播流中有效 | ||
| 68 | + // 默认值是:0 | ||
| 69 | + options.setInteger(IAVOptions.KEY_DELAY_OPTIMIZATION, 1); | ||
| 70 | + } | ||
| 71 | + int codec = getIntent().getIntExtra("mediaCodec", 0); | ||
| 72 | + // 解码方式,codec=1,硬解; codec=0, 软解 | ||
| 73 | + // 默认值是:0 | ||
| 74 | + options.setInteger(IAVOptions.KEY_MEDIACODEC, codec); | ||
| 75 | + // 是否自动启动播放,如果设置为 1,则在调用 `prepareAsync` 或者 `setVideoPath` 之后自动启动播放,无需调用 `start()` | ||
| 76 | + // 默认值是:1 | ||
| 77 | + options.setInteger(IAVOptions.KEY_START_ON_PREPARED, 0); | ||
| 78 | + mVideoView.setAVOptions(options); | ||
| 79 | + | ||
| 80 | + // Set some listeners | ||
| 81 | + mVideoView.setOnInfoListener(mOnInfoListener); | ||
| 82 | + mVideoView.setOnVideoSizeChangedListener(mOnVideoSizeChangedListener); | ||
| 83 | + mVideoView.setOnBufferingUpdateListener(mOnBufferingUpdateListener); | ||
| 84 | + mVideoView.setOnCompletionListener(mOnCompletionListener); | ||
| 85 | + mVideoView.setOnSeekCompleteListener(mOnSeekCompleteListener); | ||
| 86 | + mVideoView.setOnErrorListener(mOnErrorListener); | ||
| 87 | + mVideoView.setOnPreparedListener(onPreparedListener); | ||
| 88 | + | ||
| 89 | + //播放普通视频 | ||
| 90 | + mVideoView.setVideoPath(mVideoPath); | ||
| 91 | + | ||
| 92 | +// //播放视讯云视频 | ||
| 93 | +// mVideoView.setDataSource(new IDataSource() { | ||
| 94 | +// @Override | ||
| 95 | +// public String getId() { | ||
| 96 | +// return "20161126"; | ||
| 97 | +// } | ||
| 98 | +// | ||
| 99 | +// @Override | ||
| 100 | +// public String getType() { | ||
| 101 | +// return Config.TYPE_VOD; | ||
| 102 | +// } | ||
| 103 | +// | ||
| 104 | +// @Override | ||
| 105 | +// public String getRate() { | ||
| 106 | +// return Config.TYPE_PLAY_RATE_2; | ||
| 107 | +// } | ||
| 108 | +// }, false); | ||
| 109 | + | ||
| 110 | + // You can also use a custom `MediaController` widget | ||
| 111 | + mMediaController = new MediaController(this, false, isLiveStreaming == 1); | ||
| 112 | + mVideoView.setMediaController(mMediaController); | ||
| 113 | + } | ||
| 114 | + | ||
| 115 | + @Override | ||
| 116 | + protected void onResume() { | ||
| 117 | + super.onResume(); | ||
| 118 | + mIsActivityPaused = false; | ||
| 119 | + } | ||
| 120 | + | ||
| 121 | + @Override | ||
| 122 | + protected void onPause() { | ||
| 123 | + super.onPause(); | ||
| 124 | + mToast = null; | ||
| 125 | + mIsActivityPaused = true; | ||
| 126 | + mVideoView.pause(); | ||
| 127 | + } | ||
| 128 | + | ||
| 129 | + @Override | ||
| 130 | + protected void onDestroy() { | ||
| 131 | + super.onDestroy(); | ||
| 132 | + mVideoView.stopPlayback(); | ||
| 133 | + } | ||
| 134 | + | ||
| 135 | + public void onClickSwitchScreen(View v) { | ||
| 136 | + mDisplayAspectRatio = (mDisplayAspectRatio + 1) % 5; | ||
| 137 | + mVideoView.setDisplayAspectRatio(mDisplayAspectRatio); | ||
| 138 | + switch (mVideoView.getDisplayAspectRatio()) { | ||
| 139 | + case IVideoView.ASPECT_RATIO_ORIGIN: | ||
| 140 | + showToastTips("Origin mode"); | ||
| 141 | + break; | ||
| 142 | + case IVideoView.ASPECT_RATIO_FIT_PARENT: | ||
| 143 | + showToastTips("Fit parent !"); | ||
| 144 | + break; | ||
| 145 | + case IVideoView.ASPECT_RATIO_PAVED_PARENT: | ||
| 146 | + showToastTips("Paved parent !"); | ||
| 147 | + break; | ||
| 148 | + case IVideoView.ASPECT_RATIO_16_9: | ||
| 149 | + showToastTips("16 : 9 !"); | ||
| 150 | + break; | ||
| 151 | + case IVideoView.ASPECT_RATIO_4_3: | ||
| 152 | + showToastTips("4 : 3 !"); | ||
| 153 | + break; | ||
| 154 | + default: | ||
| 155 | + break; | ||
| 156 | + } | ||
| 157 | + } | ||
| 158 | + | ||
| 159 | + private IMediaPlayer.OnPreparedListener onPreparedListener = new IMediaPlayer.OnPreparedListener() { | ||
| 160 | + @Override | ||
| 161 | + public void onPrepared(IMediaPlayer mp) { | ||
| 162 | + Log.e("DemoView", "prepared"); | ||
| 163 | + mVideoView.start(); | ||
| 164 | + } | ||
| 165 | + }; | ||
| 166 | + | ||
| 167 | + private IMediaPlayer.OnInfoListener mOnInfoListener = new IMediaPlayer.OnInfoListener() { | ||
| 168 | + @Override | ||
| 169 | + public boolean onInfo(IMediaPlayer plMediaPlayer, int what, int extra) { | ||
| 170 | + Log.d(TAG, "onInfo: " + what + ", " + extra); | ||
| 171 | + return false; | ||
| 172 | + } | ||
| 173 | + }; | ||
| 174 | + | ||
| 175 | + private IMediaPlayer.OnErrorListener mOnErrorListener = new IMediaPlayer.OnErrorListener() { | ||
| 176 | + @Override | ||
| 177 | + public boolean onError(IMediaPlayer plMediaPlayer, int errorCode) { | ||
| 178 | + boolean isNeedReconnect = false; | ||
| 179 | + Log.e(TAG, "Error happened, errorCode = " + errorCode); | ||
| 180 | + switch (errorCode) { | ||
| 181 | + case IMediaPlayer.ERROR_CODE_INVALID_URI: | ||
| 182 | + showToastTips("Invalid URL !"); | ||
| 183 | + break; | ||
| 184 | + case IMediaPlayer.ERROR_CODE_404_NOT_FOUND: | ||
| 185 | + showToastTips("404 resource not found !"); | ||
| 186 | + break; | ||
| 187 | + case IMediaPlayer.ERROR_CODE_CONNECTION_REFUSED: | ||
| 188 | + showToastTips("Connection refused !"); | ||
| 189 | + break; | ||
| 190 | + case IMediaPlayer.ERROR_CODE_CONNECTION_TIMEOUT: | ||
| 191 | + showToastTips("Connection timeout !"); | ||
| 192 | + isNeedReconnect = true; | ||
| 193 | + break; | ||
| 194 | + case IMediaPlayer.ERROR_CODE_EMPTY_PLAYLIST: | ||
| 195 | + showToastTips("Empty playlist !"); | ||
| 196 | + break; | ||
| 197 | + case IMediaPlayer.ERROR_CODE_STREAM_DISCONNECTED: | ||
| 198 | + showToastTips("Stream disconnected !"); | ||
| 199 | + isNeedReconnect = true; | ||
| 200 | + break; | ||
| 201 | + case IMediaPlayer.ERROR_CODE_IO_ERROR: | ||
| 202 | + showToastTips("Network IO Error !"); | ||
| 203 | + isNeedReconnect = true; | ||
| 204 | + break; | ||
| 205 | + case IMediaPlayer.ERROR_CODE_UNAUTHORIZED: | ||
| 206 | + showToastTips("Unauthorized Error !"); | ||
| 207 | + break; | ||
| 208 | + case IMediaPlayer.ERROR_CODE_PREPARE_TIMEOUT: | ||
| 209 | + showToastTips("Prepare timeout !"); | ||
| 210 | + isNeedReconnect = true; | ||
| 211 | + break; | ||
| 212 | + case IMediaPlayer.ERROR_CODE_READ_FRAME_TIMEOUT: | ||
| 213 | + showToastTips("Read frame timeout !"); | ||
| 214 | + isNeedReconnect = true; | ||
| 215 | + break; | ||
| 216 | + case IMediaPlayer.MEDIA_ERROR_UNKNOWN: | ||
| 217 | + break; | ||
| 218 | + default: | ||
| 219 | + showToastTips("unknown error !"); | ||
| 220 | + break; | ||
| 221 | + } | ||
| 222 | + // Todo pls handle the error status here, reconnect or call finish() | ||
| 223 | + if (isNeedReconnect) { | ||
| 224 | + sendReconnectMessage(); | ||
| 225 | + } else { | ||
| 226 | + finish(); | ||
| 227 | + } | ||
| 228 | + // Return true means the error has been handled | ||
| 229 | + // If return false, then `onCompletion` will be called | ||
| 230 | + return true; | ||
| 231 | + } | ||
| 232 | + }; | ||
| 233 | + | ||
| 234 | + private IMediaPlayer.OnCompletionListener mOnCompletionListener = new IMediaPlayer.OnCompletionListener() { | ||
| 235 | + @Override | ||
| 236 | + public void onCompletion(IMediaPlayer plMediaPlayer) { | ||
| 237 | + Log.d(TAG, "Play Completed !"); | ||
| 238 | + showToastTips("Play Completed !"); | ||
| 239 | + finish(); | ||
| 240 | + } | ||
| 241 | + }; | ||
| 242 | + | ||
| 243 | + private IMediaPlayer.OnBufferingUpdateListener mOnBufferingUpdateListener = new IMediaPlayer.OnBufferingUpdateListener() { | ||
| 244 | + @Override | ||
| 245 | + public void onBufferingUpdate(IMediaPlayer plMediaPlayer, int precent) { | ||
| 246 | + Log.d(TAG, "onBufferingUpdate: " + precent); | ||
| 247 | + } | ||
| 248 | + }; | ||
| 249 | + | ||
| 250 | + private IMediaPlayer.OnSeekCompleteListener mOnSeekCompleteListener = new IMediaPlayer.OnSeekCompleteListener() { | ||
| 251 | + @Override | ||
| 252 | + public void onSeekComplete(IMediaPlayer plMediaPlayer) { | ||
| 253 | + Log.d(TAG, "onSeekComplete !"); | ||
| 254 | + } | ||
| 255 | + }; | ||
| 256 | + | ||
| 257 | + private IMediaPlayer.OnVideoSizeChangedListener mOnVideoSizeChangedListener = new IMediaPlayer.OnVideoSizeChangedListener() { | ||
| 258 | + @Override | ||
| 259 | + public void onVideoSizeChanged(IMediaPlayer plMediaPlayer, int width, int height) { | ||
| 260 | + Log.d(TAG, "onVideoSizeChanged: " + width + "," + height); | ||
| 261 | + } | ||
| 262 | + }; | ||
| 263 | + | ||
| 264 | + private void showToastTips(final String tips) { | ||
| 265 | + if (mIsActivityPaused) { | ||
| 266 | + return; | ||
| 267 | + } | ||
| 268 | + runOnUiThread(new Runnable() { | ||
| 269 | + @Override | ||
| 270 | + public void run() { | ||
| 271 | + if (mToast != null) { | ||
| 272 | + mToast.cancel(); | ||
| 273 | + } | ||
| 274 | + mToast = Toast.makeText(CNVideoViewActivity.this, tips, Toast.LENGTH_SHORT); | ||
| 275 | + mToast.show(); | ||
| 276 | + } | ||
| 277 | + }); | ||
| 278 | + } | ||
| 279 | + | ||
| 280 | + protected Handler mHandler = new Handler(Looper.getMainLooper()) { | ||
| 281 | + @Override | ||
| 282 | + public void handleMessage(Message msg) { | ||
| 283 | + if (msg.what != MESSAGE_ID_RECONNECTING) { | ||
| 284 | + return; | ||
| 285 | + } | ||
| 286 | + if (mIsActivityPaused || !Utils.isLiveStreamingAvailable()) { | ||
| 287 | + finish(); | ||
| 288 | + return; | ||
| 289 | + } | ||
| 290 | + if (!Utils.isNetworkAvailable(CNVideoViewActivity.this)) { | ||
| 291 | + sendReconnectMessage(); | ||
| 292 | + return; | ||
| 293 | + } | ||
| 294 | + mVideoView.setVideoPath(mVideoPath); | ||
| 295 | + mVideoView.start(); | ||
| 296 | + } | ||
| 297 | + }; | ||
| 298 | + | ||
| 299 | + private void sendReconnectMessage() { | ||
| 300 | + showToastTips("正在重连..."); | ||
| 301 | + mLoadingView.setVisibility(View.VISIBLE); | ||
| 302 | + mHandler.removeCallbacksAndMessages(null); | ||
| 303 | + mHandler.sendMessageDelayed(mHandler.obtainMessage(MESSAGE_ID_RECONNECTING), 500); | ||
| 304 | + } | ||
| 305 | +} |
app/src/main/java/com/cnlive/demo/video/MainActivity.java
0 → 100644
| 1 | +++ a/app/src/main/java/com/cnlive/demo/video/MainActivity.java | ||
| 1 | +package com.cnlive.demo.video; | ||
| 2 | + | ||
| 3 | +import android.app.Activity; | ||
| 4 | +import android.content.Intent; | ||
| 5 | +import android.os.Bundle; | ||
| 6 | +import android.support.v7.app.AppCompatActivity; | ||
| 7 | +import android.view.View; | ||
| 8 | +import android.widget.ArrayAdapter; | ||
| 9 | +import android.widget.EditText; | ||
| 10 | +import android.widget.RadioGroup; | ||
| 11 | +import android.widget.Spinner; | ||
| 12 | +import android.widget.TextView; | ||
| 13 | + | ||
| 14 | +import com.cnlive.libs.lib_cnvideo.NetworkManager; | ||
| 15 | + | ||
| 16 | +import java.net.UnknownHostException; | ||
| 17 | + | ||
| 18 | +public class MainActivity extends AppCompatActivity { | ||
| 19 | + | ||
| 20 | + private static final String DEFAULT_TEST_URL = "rtmp://live.hkstv.hk.lxdns.com/live/hks"; | ||
| 21 | + | ||
| 22 | + private static final String[] DEFAULT_PLAYBACK_DOMAIN_ARRAY = { | ||
| 23 | + "live.hkstv.hk.lxdns.com" | ||
| 24 | + }; | ||
| 25 | + | ||
| 26 | + private Spinner mActivitySpinner; | ||
| 27 | + private EditText mEditText; | ||
| 28 | + private RadioGroup mStreamingTypeRadioGroup; | ||
| 29 | + private RadioGroup mDecodeTypeRadioGroup; | ||
| 30 | + private NetworkManager networkManager; | ||
| 31 | + | ||
| 32 | + public static final String[] TEST_ACTIVITY_ARRAY = { | ||
| 33 | + "CNMediaPlayerActivity", | ||
| 34 | + "CNAudioPlayerActivity", | ||
| 35 | + "CNVideoViewActivity", | ||
| 36 | + "CNVideoTextureActivity", | ||
| 37 | + "VideoViewActivity" | ||
| 38 | + }; | ||
| 39 | + | ||
| 40 | + @Override | ||
| 41 | + protected void onCreate(Bundle savedInstanceState) { | ||
| 42 | + super.onCreate(savedInstanceState); | ||
| 43 | + setContentView(R.layout.activity_main); | ||
| 44 | + | ||
| 45 | +// try { | ||
| 46 | +// networkManager.startDnsCacheService(this, DEFAULT_PLAYBACK_DOMAIN_ARRAY); | ||
| 47 | +// } catch (UnknownHostException e) { | ||
| 48 | +// e.printStackTrace(); | ||
| 49 | +// } | ||
| 50 | + | ||
| 51 | + TextView mVersionInfoTextView = (TextView) findViewById(R.id.version_info); | ||
| 52 | + mVersionInfoTextView.setText("Version: " + BuildConfig.VERSION_NAME); | ||
| 53 | + | ||
| 54 | + mEditText = (EditText)findViewById(R.id.VideoPathEdit); | ||
| 55 | + mEditText.setText(DEFAULT_TEST_URL); | ||
| 56 | + | ||
| 57 | + mStreamingTypeRadioGroup = (RadioGroup) findViewById(R.id.StreamingTypeRadioGroup); | ||
| 58 | + mDecodeTypeRadioGroup = (RadioGroup) findViewById(R.id.DecodeTypeRadioGroup); | ||
| 59 | + | ||
| 60 | + mActivitySpinner = (Spinner) findViewById(R.id.TestSpinner); | ||
| 61 | + ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, TEST_ACTIVITY_ARRAY); | ||
| 62 | + mActivitySpinner.setAdapter(adapter); | ||
| 63 | + } | ||
| 64 | + | ||
| 65 | + @Override | ||
| 66 | + protected void onDestroy() { | ||
| 67 | + super.onDestroy(); | ||
| 68 | +// networkManager.stopDnsCacheService(this); | ||
| 69 | + } | ||
| 70 | + | ||
| 71 | + public void onClickLocalFile(View v) { | ||
| 72 | + Intent intent = new Intent(this, VideoFileActivity.class); | ||
| 73 | + startActivityForResult(intent, 0); | ||
| 74 | + } | ||
| 75 | + | ||
| 76 | + public void onClickPlay(View v) { | ||
| 77 | + String videopath = mEditText.getText().toString(); | ||
| 78 | + if (!"".equals(videopath)) { | ||
| 79 | + jumpToPlayerActivity(videopath); | ||
| 80 | + } | ||
| 81 | + } | ||
| 82 | + | ||
| 83 | + public void jumpToPlayerActivity(String videopath) { | ||
| 84 | + Class<?> cls = null; | ||
| 85 | + switch (mActivitySpinner.getSelectedItemPosition()) { | ||
| 86 | + case 0: cls = CNMediaPlayerActivity.class; | ||
| 87 | + break; | ||
| 88 | + case 1: cls = CNAudioPlayerActivity.class; | ||
| 89 | + break; | ||
| 90 | + case 2: cls = CNVideoViewActivity.class; | ||
| 91 | + break; | ||
| 92 | + case 3: cls = CNVideoTextureActivity.class; | ||
| 93 | + break; | ||
| 94 | + case 4: cls = VideoViewActivity.class; | ||
| 95 | + break; | ||
| 96 | + default: | ||
| 97 | + return; | ||
| 98 | + } | ||
| 99 | + Intent intent = new Intent(this, cls); | ||
| 100 | + intent.putExtra("videoPath", videopath); | ||
| 101 | + if (mDecodeTypeRadioGroup.getCheckedRadioButtonId() == R.id.RadioHWDecode) { | ||
| 102 | + intent.putExtra("mediaCodec", 1); | ||
| 103 | + } else { | ||
| 104 | + intent.putExtra("mediaCodec", 0); | ||
| 105 | + } | ||
| 106 | + if (mStreamingTypeRadioGroup.getCheckedRadioButtonId() == R.id.RadioLiveStreaming) { | ||
| 107 | + intent.putExtra("liveStreaming", 1); | ||
| 108 | + } else { | ||
| 109 | + intent.putExtra("liveStreaming", 0); | ||
| 110 | + } | ||
| 111 | + startActivity(intent); | ||
| 112 | + } | ||
| 113 | + | ||
| 114 | + @Override | ||
| 115 | + protected void onActivityResult(int requestCode, int resultCode, Intent data) { | ||
| 116 | + if (resultCode != Activity.RESULT_OK) { | ||
| 117 | + return; | ||
| 118 | + } | ||
| 119 | + String videoPath = data.getStringExtra("videoPath"); | ||
| 120 | + mEditText.setText(videoPath, TextView.BufferType.EDITABLE); | ||
| 121 | + } | ||
| 122 | +} |
app/src/main/java/com/cnlive/demo/video/VideoApplication.java
0 → 100644
| 1 | +++ a/app/src/main/java/com/cnlive/demo/video/VideoApplication.java | ||
| 1 | +package com.cnlive.demo.video; | ||
| 2 | + | ||
| 3 | +import android.app.Application; | ||
| 4 | + | ||
| 5 | +import com.cnlive.libs.util.Config; | ||
| 6 | + | ||
| 7 | +/** | ||
| 8 | + * Created by Administrator on 2016/12/2. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | +public class VideoApplication extends Application{ | ||
| 12 | + @Override | ||
| 13 | + public void onCreate() { | ||
| 14 | + super.onCreate(); | ||
| 15 | + Config.init(this, "60_irn9edco29", "69fe70bda67e059286ad509d9f5377f815955645b52f94"); | ||
| 16 | + } | ||
| 17 | +} |
app/src/main/java/com/cnlive/demo/video/VideoFileActivity.java
0 → 100644
| 1 | +++ a/app/src/main/java/com/cnlive/demo/video/VideoFileActivity.java | ||
| 1 | +package com.cnlive.demo.video; | ||
| 2 | + | ||
| 3 | +import android.app.Activity; | ||
| 4 | +import android.content.Context; | ||
| 5 | +import android.content.Intent; | ||
| 6 | +import android.database.Cursor; | ||
| 7 | +import android.os.Bundle; | ||
| 8 | +import android.provider.MediaStore; | ||
| 9 | +import android.support.v4.app.LoaderManager; | ||
| 10 | +import android.support.v4.content.CursorLoader; | ||
| 11 | +import android.support.v4.content.Loader; | ||
| 12 | +import android.support.v4.widget.SimpleCursorAdapter; | ||
| 13 | +import android.support.v7.app.AppCompatActivity; | ||
| 14 | +import android.view.View; | ||
| 15 | +import android.widget.AdapterView; | ||
| 16 | +import android.widget.ListView; | ||
| 17 | + | ||
| 18 | +public class VideoFileActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<Cursor> { | ||
| 19 | + | ||
| 20 | + private ListView mVideoListView; | ||
| 21 | + private VideoAdapter mVideoAdapter; | ||
| 22 | + | ||
| 23 | + @Override | ||
| 24 | + protected void onCreate(Bundle savedInstanceState) { | ||
| 25 | + super.onCreate(savedInstanceState); | ||
| 26 | + setContentView(R.layout.activity_video_file); | ||
| 27 | + | ||
| 28 | + mVideoListView = (ListView) findViewById(R.id.FileListView); | ||
| 29 | + mVideoAdapter = new VideoAdapter(this); | ||
| 30 | + | ||
| 31 | + mVideoListView.setAdapter(mVideoAdapter); | ||
| 32 | + mVideoListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { | ||
| 33 | + @Override | ||
| 34 | + public void onItemClick(AdapterView<?> parent, View view, final int position, final long id) { | ||
| 35 | + Intent intent = new Intent(); | ||
| 36 | + intent.putExtra("videoPath", mVideoAdapter.getVideoPath(position)); | ||
| 37 | + setResult(Activity.RESULT_OK, intent); | ||
| 38 | + finish(); | ||
| 39 | + } | ||
| 40 | + }); | ||
| 41 | + | ||
| 42 | + getSupportLoaderManager().initLoader(1, null, this); | ||
| 43 | + } | ||
| 44 | + | ||
| 45 | + @Override | ||
| 46 | + public Loader<Cursor> onCreateLoader(int id, Bundle args) { | ||
| 47 | + return new CursorLoader(this, MediaStore.Video.Media.getContentUri("external"), null, null, null, | ||
| 48 | + "UPPER(" + MediaStore.Video.Media.DATA + ")"); | ||
| 49 | + } | ||
| 50 | + | ||
| 51 | + @Override | ||
| 52 | + public void onLoadFinished(Loader<Cursor> loader, Cursor data) { | ||
| 53 | + mVideoAdapter.swapCursor(data); | ||
| 54 | + } | ||
| 55 | + | ||
| 56 | + @Override | ||
| 57 | + public void onLoaderReset(Loader<Cursor> loader) { | ||
| 58 | + | ||
| 59 | + } | ||
| 60 | + | ||
| 61 | + public class VideoAdapter extends SimpleCursorAdapter { | ||
| 62 | + | ||
| 63 | + public VideoAdapter(Context context) { | ||
| 64 | + super(context, android.R.layout.simple_list_item_1, null, | ||
| 65 | + new String[]{MediaStore.Video.Media.DISPLAY_NAME, MediaStore.Video.Media.DATA}, | ||
| 66 | + new int[]{android.R.id.text1, android.R.id.text2}, 0); | ||
| 67 | + } | ||
| 68 | + | ||
| 69 | + @Override | ||
| 70 | + public long getItemId(int position) { | ||
| 71 | + final Cursor cursor = getCursor(); | ||
| 72 | + if (cursor == null || cursor.getCount() == 0 || position >= cursor.getCount()) { | ||
| 73 | + return 0; | ||
| 74 | + } | ||
| 75 | + cursor.moveToPosition(position); | ||
| 76 | + return cursor.getLong(0); | ||
| 77 | + } | ||
| 78 | + | ||
| 79 | + public String getVideoPath(int position) { | ||
| 80 | + final Cursor cursor = getCursor(); | ||
| 81 | + if (cursor.getCount() == 0) { | ||
| 82 | + return ""; | ||
| 83 | + } | ||
| 84 | + cursor.moveToPosition(position); | ||
| 85 | + return cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.DATA)); | ||
| 86 | + } | ||
| 87 | + } | ||
| 88 | + | ||
| 89 | +} |
app/src/main/java/com/cnlive/demo/video/VideoPlayerBaseActivity.java
0 → 100644
| 1 | +++ a/app/src/main/java/com/cnlive/demo/video/VideoPlayerBaseActivity.java | ||
| 1 | +package com.cnlive.demo.video; | ||
| 2 | + | ||
| 3 | +import android.os.Build; | ||
| 4 | +import android.os.Bundle; | ||
| 5 | +import android.support.annotation.Nullable; | ||
| 6 | +import android.support.v7.app.AppCompatActivity; | ||
| 7 | +import android.view.View; | ||
| 8 | +import android.view.WindowManager; | ||
| 9 | + | ||
| 10 | +/** | ||
| 11 | + * Created by kongweile on 29/10/2016. | ||
| 12 | + * Auto hide and show navigation bar and status bar for API >= 19. | ||
| 13 | + * Keep screen on. | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | +public class VideoPlayerBaseActivity extends AppCompatActivity { | ||
| 17 | + @Override | ||
| 18 | + protected void onCreate(@Nullable Bundle savedInstanceState) { | ||
| 19 | + super.onCreate(savedInstanceState); | ||
| 20 | + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { | ||
| 21 | + View decorView = getWindow().getDecorView(); | ||
| 22 | + decorView.setSystemUiVisibility( | ||
| 23 | + View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | ||
| 24 | + | View.SYSTEM_UI_FLAG_FULLSCREEN | ||
| 25 | + | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); | ||
| 26 | + } | ||
| 27 | + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); | ||
| 28 | + } | ||
| 29 | +} |
app/src/main/java/com/cnlive/demo/video/VideoViewActivity.java
0 → 100644
| 1 | +++ a/app/src/main/java/com/cnlive/demo/video/VideoViewActivity.java | ||
| 1 | +package com.cnlive.demo.video; | ||
| 2 | + | ||
| 3 | +import android.content.Intent; | ||
| 4 | +import android.content.pm.ActivityInfo; | ||
| 5 | +import android.os.Bundle; | ||
| 6 | +import android.text.TextUtils; | ||
| 7 | +import android.util.Log; | ||
| 8 | +import android.util.Pair; | ||
| 9 | +import android.view.View; | ||
| 10 | +import android.view.ViewGroup; | ||
| 11 | +import android.view.WindowManager; | ||
| 12 | +import android.widget.Button; | ||
| 13 | +import android.widget.Toast; | ||
| 14 | + | ||
| 15 | +import com.cnlive.demo.video.widget.AspectLayout; | ||
| 16 | +import com.cnlive.demo.video.widget.MediaController; | ||
| 17 | +import com.cnlive.libs.lib_cnvideo.base.IAVOptions; | ||
| 18 | +import com.cnlive.libs.lib_cnvideo.base.IMediaPlayer; | ||
| 19 | +import com.cnlive.libs.lib_cnvideo.base.Util; | ||
| 20 | +import com.cnlive.libs.lib_cnvideo.player.AVOptions; | ||
| 21 | +import com.cnlive.libs.lib_cnvideo.player.widget.VideoView; | ||
| 22 | + | ||
| 23 | +public class VideoViewActivity extends VideoPlayerBaseActivity implements | ||
| 24 | + IMediaPlayer.OnCompletionListener, | ||
| 25 | + IMediaPlayer.OnInfoListener, | ||
| 26 | + IMediaPlayer.OnErrorListener, | ||
| 27 | + IMediaPlayer.OnVideoSizeChangedListener, | ||
| 28 | + IMediaPlayer.OnPreparedListener { | ||
| 29 | + | ||
| 30 | + private static final String TAG = "VideoViewActivity"; | ||
| 31 | + | ||
| 32 | + private static final int REQ_DELAY_MILLS = 3000; | ||
| 33 | + | ||
| 34 | + private VideoView mVideoView; | ||
| 35 | + private View mBufferingIndicator; | ||
| 36 | + private MediaController mMediaController; | ||
| 37 | + private AspectLayout mAspectLayout; | ||
| 38 | + private ViewGroup.LayoutParams mLayoutParams; | ||
| 39 | + private Pair<Integer, Integer> mScreenSize; | ||
| 40 | + | ||
| 41 | + private String mVideoPath; | ||
| 42 | + private Button mBackBtn; | ||
| 43 | + private long mLastPosition = 0; | ||
| 44 | + private boolean mIsLiveStream = false; | ||
| 45 | + | ||
| 46 | + private int mReqDelayMills = REQ_DELAY_MILLS; | ||
| 47 | + private boolean mIsCompleted = false; | ||
| 48 | + private Runnable mVideoReconnect; | ||
| 49 | + | ||
| 50 | + @Override | ||
| 51 | + protected void onCreate(Bundle savedInstanceState) { | ||
| 52 | + super.onCreate(savedInstanceState); | ||
| 53 | + setContentView(R.layout.activity_video_view); | ||
| 54 | + | ||
| 55 | + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE); | ||
| 56 | + | ||
| 57 | + mVideoPath = getIntent().getStringExtra("videoPath"); | ||
| 58 | + | ||
| 59 | + Intent intent = getIntent(); | ||
| 60 | + String intentAction = intent.getAction(); | ||
| 61 | + if (!TextUtils.isEmpty(intentAction) && intentAction.equals(Intent.ACTION_VIEW)) { | ||
| 62 | + mVideoPath = intent.getDataString(); | ||
| 63 | + } | ||
| 64 | + | ||
| 65 | + mVideoView = (VideoView) findViewById(R.id.video_view); | ||
| 66 | + mAspectLayout = (AspectLayout) findViewById(R.id.aspect_layout); | ||
| 67 | + mBufferingIndicator = findViewById(R.id.buffering_indicator); | ||
| 68 | + | ||
| 69 | + mBackBtn = (Button) findViewById(R.id.back_btn); | ||
| 70 | + mBackBtn.setOnClickListener(new View.OnClickListener() { | ||
| 71 | + @Override | ||
| 72 | + public void onClick(View view) { | ||
| 73 | + mVideoView.stopPlayback(); | ||
| 74 | + onBackPressed(); | ||
| 75 | + finish(); | ||
| 76 | + } | ||
| 77 | + }); | ||
| 78 | + | ||
| 79 | + boolean useFastForward = true; | ||
| 80 | + boolean disableProgressBar = false; | ||
| 81 | + | ||
| 82 | + Log.i(TAG, "mVideoPath:" + mVideoPath); | ||
| 83 | + | ||
| 84 | + //SharedLibraryNameHelper.getInstance().renameSharedLibrary("pldroidplayer_v7a"); | ||
| 85 | + | ||
| 86 | + AVOptions options = new AVOptions(); | ||
| 87 | + int codec = getIntent().getIntExtra("mediaCodec", 0); | ||
| 88 | + //解码方式,codec=1,硬解;codec=0,软解 | ||
| 89 | + options.setInteger(IAVOptions.KEY_MEDIACODEC, codec); // 1 -> hw codec enable, 0 -> disable | ||
| 90 | + | ||
| 91 | + mIsLiveStream = isLiveStreaming(mVideoPath); | ||
| 92 | + if (mIsLiveStream) { | ||
| 93 | + // 读取视频流超时时间,单位是 ms | ||
| 94 | + // 默认值是:10 * 1000 | ||
| 95 | + options.setInteger(IAVOptions.KEY_GET_AV_FRAME_TIMEOUT, 10 * 1000); | ||
| 96 | + // 当前播放的是否为在线直播,如果是,则底层会有一些播放优化 | ||
| 97 | + // 默认值是:0 | ||
| 98 | + options.setInteger(IAVOptions.KEY_LIVE_STREAMING, 1); | ||
| 99 | + } | ||
| 100 | + // Tip: you can custom the variable depending on your situation | ||
| 101 | + if (mIsLiveStream) { | ||
| 102 | + disableProgressBar = true; | ||
| 103 | + useFastForward = false; | ||
| 104 | + } | ||
| 105 | + mMediaController = new MediaController(this, useFastForward, disableProgressBar); | ||
| 106 | +// mMediaController.setMediaPlayer(mVideoView); | ||
| 107 | + mVideoView.setMediaController(mMediaController); | ||
| 108 | +// mVideoView.setMediaBufferingIndicator(mBufferingIndicator); | ||
| 109 | + | ||
| 110 | + mVideoView.setAVOptions(options); | ||
| 111 | + | ||
| 112 | + mVideoView.setVideoPath(mVideoPath); | ||
| 113 | + | ||
| 114 | + mVideoView.setOnErrorListener(this); | ||
| 115 | + mVideoView.setOnCompletionListener(this); | ||
| 116 | + mVideoView.setOnInfoListener(this); | ||
| 117 | + mVideoView.setOnPreparedListener(this); | ||
| 118 | + mVideoView.setOnVideoSizeChangedListener(this); | ||
| 119 | + | ||
| 120 | + mVideoView.requestFocus(); | ||
| 121 | + | ||
| 122 | + mBufferingIndicator.setVisibility(View.VISIBLE); | ||
| 123 | + } | ||
| 124 | + | ||
| 125 | + @Override | ||
| 126 | + public void onCompletion(IMediaPlayer mp) { | ||
| 127 | + Log.d(TAG, "onCompletion"); | ||
| 128 | + mIsCompleted = true; | ||
| 129 | + mBufferingIndicator.setVisibility(View.GONE); | ||
| 130 | + mVideoView.start(); | ||
| 131 | + } | ||
| 132 | + | ||
| 133 | + @Override | ||
| 134 | + public boolean onError(IMediaPlayer mp, int what) { | ||
| 135 | + Log.d(TAG, "onError what=" + what + ", extra="); | ||
| 136 | + switch (what) { | ||
| 137 | + case IMediaPlayer.ERROR_CODE_INVALID_URI: | ||
| 138 | + Toast.makeText(VideoViewActivity.this, "Invalid URL !", Toast.LENGTH_SHORT); | ||
| 139 | + break; | ||
| 140 | + case IMediaPlayer.ERROR_CODE_404_NOT_FOUND: | ||
| 141 | + Toast.makeText(VideoViewActivity.this, "404 resource not found !", Toast.LENGTH_SHORT); | ||
| 142 | + break; | ||
| 143 | + case IMediaPlayer.ERROR_CODE_CONNECTION_REFUSED: | ||
| 144 | + Toast.makeText(VideoViewActivity.this, "Connection refused !", Toast.LENGTH_SHORT); | ||
| 145 | + break; | ||
| 146 | + case IMediaPlayer.ERROR_CODE_CONNECTION_TIMEOUT: | ||
| 147 | + Toast.makeText(VideoViewActivity.this, "Connection timeout !", Toast.LENGTH_SHORT); | ||
| 148 | + break; | ||
| 149 | + case IMediaPlayer.ERROR_CODE_EMPTY_PLAYLIST: | ||
| 150 | + Toast.makeText(VideoViewActivity.this, "Empty playlist !", Toast.LENGTH_SHORT); | ||
| 151 | + break; | ||
| 152 | + case IMediaPlayer.ERROR_CODE_STREAM_DISCONNECTED: | ||
| 153 | + Toast.makeText(VideoViewActivity.this, "Stream disconnected !", Toast.LENGTH_SHORT); | ||
| 154 | + break; | ||
| 155 | + case IMediaPlayer.ERROR_CODE_IO_ERROR: | ||
| 156 | + Toast.makeText(VideoViewActivity.this, "Network IO Error !", Toast.LENGTH_SHORT); | ||
| 157 | + break; | ||
| 158 | + case IMediaPlayer.ERROR_CODE_UNAUTHORIZED: | ||
| 159 | + Toast.makeText(VideoViewActivity.this, "Unauthorized Error !", Toast.LENGTH_SHORT); | ||
| 160 | + break; | ||
| 161 | + case IMediaPlayer.ERROR_CODE_PREPARE_TIMEOUT: | ||
| 162 | + Toast.makeText(VideoViewActivity.this, "Prepare timeout !", Toast.LENGTH_SHORT); | ||
| 163 | + break; | ||
| 164 | + case IMediaPlayer.ERROR_CODE_READ_FRAME_TIMEOUT: | ||
| 165 | + Toast.makeText(VideoViewActivity.this, "Read frame timeout !", Toast.LENGTH_SHORT); | ||
| 166 | + break; | ||
| 167 | + case IMediaPlayer.MEDIA_ERROR_UNKNOWN: | ||
| 168 | + break; | ||
| 169 | + default: | ||
| 170 | + Toast.makeText(VideoViewActivity.this, "unknown error !", Toast.LENGTH_SHORT); | ||
| 171 | + break; | ||
| 172 | + } | ||
| 173 | + if (what == -10000) { | ||
| 174 | +// if (extra == PlayerCode.EXTRA_CODE_INVALID_URI || extra == PlayerCode.EXTRA_CODE_EOF) { | ||
| 175 | +// if (mBufferingIndicator != null) | ||
| 176 | +// mBufferingIndicator.setVisibility(View.GONE); | ||
| 177 | +// return true; | ||
| 178 | +// } | ||
| 179 | +// if (mIsCompleted && extra == PlayerCode.EXTRA_CODE_EMPTY_PLAYLIST) { | ||
| 180 | +// Log.d(TAG, "mVideoView reconnect!!!"); | ||
| 181 | +// mVideoView.removeCallbacks(mVideoReconnect); | ||
| 182 | +// mVideoReconnect = new Runnable() { | ||
| 183 | +// @Override | ||
| 184 | +// public void run() { | ||
| 185 | +// mVideoView.setVideoPath(mVideoPath); | ||
| 186 | +// } | ||
| 187 | +// }; | ||
| 188 | +// mVideoView.postDelayed(mVideoReconnect, mReqDelayMills); | ||
| 189 | +// mReqDelayMills += 200; | ||
| 190 | +// } else if (extra == PlayerCode.EXTRA_CODE_404_NOT_FOUND) { | ||
| 191 | +// // NO ts exist | ||
| 192 | +// if (mBufferingIndicator != null) | ||
| 193 | +// mBufferingIndicator.setVisibility(View.GONE); | ||
| 194 | +// } else if (extra == PlayerCode.EXTRA_CODE_IO_ERROR) { | ||
| 195 | +// // NO rtmp stream exist | ||
| 196 | +// if (mBufferingIndicator != null) | ||
| 197 | +// mBufferingIndicator.setVisibility(View.GONE); | ||
| 198 | +// } | ||
| 199 | + } | ||
| 200 | + // return true means you handle the onError, hence System wouldn't handle it again(popup a dialog). | ||
| 201 | + return true; | ||
| 202 | + } | ||
| 203 | + | ||
| 204 | + @Override | ||
| 205 | + public boolean onInfo(IMediaPlayer mp, int what, int extra) { | ||
| 206 | + | ||
| 207 | + Log.d(TAG, "onInfo what=" + what + ", extra=" + extra); | ||
| 208 | + | ||
| 209 | + if (what == IMediaPlayer.MEDIA_INFO_BUFFERING_START) { | ||
| 210 | + Log.i(TAG, "onInfo: (MEDIA_INFO_BUFFERING_START)"); | ||
| 211 | + if (mBufferingIndicator != null) | ||
| 212 | + mBufferingIndicator.setVisibility(View.VISIBLE); | ||
| 213 | + } else if (what == IMediaPlayer.MEDIA_INFO_BUFFERING_END) { | ||
| 214 | + Log.i(TAG, "onInfo: (MEDIA_INFO_BUFFERING_END)"); | ||
| 215 | + if (mBufferingIndicator != null) | ||
| 216 | + mBufferingIndicator.setVisibility(View.GONE); | ||
| 217 | + } else if (what == IMediaPlayer.MEDIA_INFO_AUDIO_RENDERING_START) { | ||
| 218 | + Toast.makeText(this, "Audio Start", Toast.LENGTH_LONG).show(); | ||
| 219 | + Log.i(TAG, "duration:" + mVideoView.getDuration()); | ||
| 220 | + } else if (what == IMediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START) { | ||
| 221 | + Toast.makeText(this, "Video Start", Toast.LENGTH_LONG).show(); | ||
| 222 | + Log.i(TAG, "duration:" + mVideoView.getDuration()); | ||
| 223 | + } | ||
| 224 | + | ||
| 225 | + return true; | ||
| 226 | + } | ||
| 227 | + | ||
| 228 | + @Override | ||
| 229 | + public void onPrepared(IMediaPlayer mp) { | ||
| 230 | + Log.d(TAG, "onPrepared"); | ||
| 231 | + mBufferingIndicator.setVisibility(View.GONE); | ||
| 232 | + mReqDelayMills = REQ_DELAY_MILLS; | ||
| 233 | + } | ||
| 234 | + | ||
| 235 | + @Override | ||
| 236 | + public void onResume() { | ||
| 237 | + super.onResume(); | ||
| 238 | + mReqDelayMills = REQ_DELAY_MILLS; | ||
| 239 | + if (mVideoView != null && !mIsLiveStream && mLastPosition != 0) { | ||
| 240 | + mVideoView.seekTo(mLastPosition); | ||
| 241 | + mVideoView.start(); | ||
| 242 | + } | ||
| 243 | + } | ||
| 244 | + | ||
| 245 | + @Override | ||
| 246 | + public void onPause() { | ||
| 247 | + if (mVideoView != null) { | ||
| 248 | + mLastPosition = mVideoView.getCurrentPosition(); | ||
| 249 | + mVideoView.pause(); | ||
| 250 | + } | ||
| 251 | + getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); | ||
| 252 | + super.onPause(); | ||
| 253 | + } | ||
| 254 | + | ||
| 255 | + @Override | ||
| 256 | + public void onVideoSizeChanged(IMediaPlayer iMediaPlayer, int width, int height) { | ||
| 257 | + | ||
| 258 | + Log.i(TAG, "onVideoSizeChanged " + iMediaPlayer.getVideoWidth() + "x" + iMediaPlayer.getVideoHeight() + ",width:" + width + ",height:" + height); | ||
| 259 | + | ||
| 260 | + /* | ||
| 261 | + if (width > height) { | ||
| 262 | + // land video | ||
| 263 | + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE); | ||
| 264 | + } else { | ||
| 265 | + // port video | ||
| 266 | + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT); | ||
| 267 | + }*/ | ||
| 268 | + | ||
| 269 | + mScreenSize = Util.getResolution(this); | ||
| 270 | + | ||
| 271 | + if (width < mScreenSize.first) { | ||
| 272 | + height = mScreenSize.first * height / width; | ||
| 273 | + width = mScreenSize.first; | ||
| 274 | + } | ||
| 275 | + | ||
| 276 | + if (width * height < mScreenSize.first * mScreenSize.second) { | ||
| 277 | + width = mScreenSize.second * width / height; | ||
| 278 | + height = mScreenSize.second; | ||
| 279 | + } | ||
| 280 | + | ||
| 281 | + mLayoutParams = mAspectLayout.getLayoutParams(); | ||
| 282 | + mLayoutParams.width = width; | ||
| 283 | + mLayoutParams.height = height; | ||
| 284 | + mAspectLayout.setLayoutParams(mLayoutParams); | ||
| 285 | + } | ||
| 286 | + | ||
| 287 | + private boolean isLiveStreaming(String url) { | ||
| 288 | + if (url.startsWith("rtmp://") | ||
| 289 | + || (url.startsWith("http://") && url.endsWith(".m3u8")) | ||
| 290 | + || (url.startsWith("http://") && url.endsWith(".flv"))) { | ||
| 291 | + return true; | ||
| 292 | + } | ||
| 293 | + return false; | ||
| 294 | + } | ||
| 295 | +} |
app/src/main/java/com/cnlive/demo/video/utils/Utils.java
0 → 100644
| 1 | +++ a/app/src/main/java/com/cnlive/demo/video/utils/Utils.java | ||
| 1 | +package com.cnlive.demo.video.utils; | ||
| 2 | + | ||
| 3 | +import android.content.Context; | ||
| 4 | +import android.net.ConnectivityManager; | ||
| 5 | +import android.net.NetworkInfo; | ||
| 6 | + | ||
| 7 | +public class Utils { | ||
| 8 | + | ||
| 9 | + public static boolean isNetworkAvailable(Context context) { | ||
| 10 | + ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); | ||
| 11 | + NetworkInfo netInfo = cm.getActiveNetworkInfo(); | ||
| 12 | + return netInfo != null && netInfo.isConnectedOrConnecting(); | ||
| 13 | + } | ||
| 14 | + | ||
| 15 | + /** | ||
| 16 | + * Is the live streaming still available | ||
| 17 | + * @return is the live streaming is available | ||
| 18 | + */ | ||
| 19 | + public static boolean isLiveStreamingAvailable() { | ||
| 20 | + // Todo: Please ask your app server, is the live streaming still available | ||
| 21 | + return true; | ||
| 22 | + } | ||
| 23 | +} |
app/src/main/java/com/cnlive/demo/video/widget/AspectLayout.java
0 → 100644
| 1 | +++ a/app/src/main/java/com/cnlive/demo/video/widget/AspectLayout.java | ||
| 1 | +package com.cnlive.demo.video.widget; | ||
| 2 | + | ||
| 3 | +import android.annotation.TargetApi; | ||
| 4 | +import android.content.Context; | ||
| 5 | +import android.graphics.Rect; | ||
| 6 | +import android.util.AttributeSet; | ||
| 7 | +import android.util.Log; | ||
| 8 | +import android.util.Pair; | ||
| 9 | +import android.widget.RelativeLayout; | ||
| 10 | + | ||
| 11 | +import static com.cnlive.libs.lib_cnvideo.base.Util.getResolution; | ||
| 12 | + | ||
| 13 | +/** | ||
| 14 | + * Created by jerikc on 15/11/22. | ||
| 15 | + */ | ||
| 16 | +public class AspectLayout extends RelativeLayout { | ||
| 17 | + | ||
| 18 | + private static final String TAG = "AspectLayout"; | ||
| 19 | + | ||
| 20 | + private int mWidthMeasureSpec; | ||
| 21 | + | ||
| 22 | + private int mRootHeight = 0; | ||
| 23 | + private int mRootWidth = 0; | ||
| 24 | + | ||
| 25 | + public AspectLayout(Context context) { | ||
| 26 | + super(context); | ||
| 27 | + initialize(context); | ||
| 28 | + } | ||
| 29 | + | ||
| 30 | + public AspectLayout(Context context, AttributeSet attrs) { | ||
| 31 | + super(context, attrs); | ||
| 32 | + initialize(context); | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | + public AspectLayout(Context context, AttributeSet attrs, int defStyleAttr) { | ||
| 36 | + super(context, attrs, defStyleAttr); | ||
| 37 | + initialize(context); | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | + private void initialize(Context ctx) { | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + @TargetApi(21) | ||
| 44 | + public AspectLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { | ||
| 45 | + super(context, attrs, defStyleAttr, defStyleRes); | ||
| 46 | + } | ||
| 47 | + | ||
| 48 | + @Override | ||
| 49 | + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { | ||
| 50 | + Log.d(TAG, "onMeasure" + " width=[" + MeasureSpec.toString(widthMeasureSpec) + | ||
| 51 | + "] height=[" + MeasureSpec.toString(heightMeasureSpec) + "]"); | ||
| 52 | + | ||
| 53 | + Rect r = new Rect(); | ||
| 54 | + getWindowVisibleDisplayFrame(r); | ||
| 55 | + Pair<Integer, Integer> screenSize = getResolution(getContext()); | ||
| 56 | + | ||
| 57 | + if (mRootWidth == 0 && mRootHeight == 0) { | ||
| 58 | + mRootWidth = getRootView().getWidth(); | ||
| 59 | + mRootHeight = getRootView().getHeight(); | ||
| 60 | + } | ||
| 61 | + int totalHeight = 0; | ||
| 62 | + | ||
| 63 | + if (screenSize.first > screenSize.second) { | ||
| 64 | + // land | ||
| 65 | + totalHeight = mRootWidth > mRootHeight ? mRootHeight : mRootWidth; | ||
| 66 | + } else { | ||
| 67 | + // port | ||
| 68 | + totalHeight = mRootWidth < mRootHeight ? mRootHeight : mRootWidth; | ||
| 69 | + } | ||
| 70 | + | ||
| 71 | + int nowHeight = r.bottom - r.top; | ||
| 72 | + | ||
| 73 | + if (totalHeight - nowHeight > totalHeight / 4) { | ||
| 74 | + // soft keyboard show | ||
| 75 | + super.onMeasure(mWidthMeasureSpec, MeasureSpec.makeMeasureSpec(nowHeight + totalHeight - nowHeight, MeasureSpec.EXACTLY)); | ||
| 76 | + return; | ||
| 77 | + } else { | ||
| 78 | + // soft keyboard hide | ||
| 79 | + } | ||
| 80 | + | ||
| 81 | + mWidthMeasureSpec = widthMeasureSpec; | ||
| 82 | + | ||
| 83 | + super.onMeasure(widthMeasureSpec, heightMeasureSpec); | ||
| 84 | + } | ||
| 85 | +} |
app/src/main/java/com/cnlive/demo/video/widget/MediaController.java
0 → 100644
| 1 | +++ a/app/src/main/java/com/cnlive/demo/video/widget/MediaController.java | ||
| 1 | +package com.cnlive.demo.video.widget; | ||
| 2 | + | ||
| 3 | +import android.annotation.SuppressLint; | ||
| 4 | +import android.content.Context; | ||
| 5 | +import android.content.res.Resources; | ||
| 6 | +import android.graphics.Rect; | ||
| 7 | +import android.media.AudioManager; | ||
| 8 | +import android.os.Build; | ||
| 9 | +import android.os.Handler; | ||
| 10 | +import android.os.Message; | ||
| 11 | +import android.util.AttributeSet; | ||
| 12 | +import android.util.Log; | ||
| 13 | +import android.view.Gravity; | ||
| 14 | +import android.view.KeyEvent; | ||
| 15 | +import android.view.LayoutInflater; | ||
| 16 | +import android.view.MotionEvent; | ||
| 17 | +import android.view.View; | ||
| 18 | +import android.widget.FrameLayout; | ||
| 19 | +import android.widget.ImageButton; | ||
| 20 | +import android.widget.PopupWindow; | ||
| 21 | +import android.widget.ProgressBar; | ||
| 22 | +import android.widget.SeekBar; | ||
| 23 | +import android.widget.TextView; | ||
| 24 | + | ||
| 25 | +import com.cnlive.libs.lib_cnvideo.base.ICNMediaController; | ||
| 26 | + | ||
| 27 | +import java.util.Locale; | ||
| 28 | + | ||
| 29 | +/** | ||
| 30 | + * You can write a custom MediaController instead of this class | ||
| 31 | + * A MediaController widget must implement all the interface defined by com.pili.pldroid.player.IMediaController | ||
| 32 | + */ | ||
| 33 | +public class MediaController extends FrameLayout implements ICNMediaController { | ||
| 34 | + | ||
| 35 | + private static final String TAG = "PLMediaController"; | ||
| 36 | + private MediaPlayerControl mPlayer; | ||
| 37 | + private Context mContext; | ||
| 38 | + private PopupWindow mWindow; | ||
| 39 | + private int mAnimStyle; | ||
| 40 | + private View mAnchor; | ||
| 41 | + private View mRoot; | ||
| 42 | + private ProgressBar mProgress; | ||
| 43 | + private TextView mEndTime, mCurrentTime; | ||
| 44 | + private long mDuration; | ||
| 45 | + private boolean mShowing; | ||
| 46 | + private boolean mDragging; | ||
| 47 | + private boolean mInstantSeeking = true; | ||
| 48 | + private static int sDefaultTimeout = 3000; | ||
| 49 | + private static final int SEEK_TO_POST_DELAY_MILLIS = 200; | ||
| 50 | + | ||
| 51 | + private static final int FADE_OUT = 1; | ||
| 52 | + private static final int SHOW_PROGRESS = 2; | ||
| 53 | + private boolean mFromXml = false; | ||
| 54 | + private ImageButton mPauseButton; | ||
| 55 | + private ImageButton mFfwdButton; | ||
| 56 | + private ImageButton mRewButton; | ||
| 57 | + private ImageButton mNextButton; | ||
| 58 | + private ImageButton mPrevButton; | ||
| 59 | + | ||
| 60 | + private boolean mUseFastForward; | ||
| 61 | + | ||
| 62 | + private static final int IC_MEDIA_PAUSE_ID = Resources.getSystem().getIdentifier("ic_media_pause","drawable", "android"); | ||
| 63 | + private static final int IC_MEDIA_PLAY_ID = Resources.getSystem().getIdentifier("ic_media_play","drawable", "android"); | ||
| 64 | + private static final int MEDIA_CONTROLLER_ID = Resources.getSystem().getIdentifier("media_controller", "layout", "android"); | ||
| 65 | + private static final int PRV_BUTTON_ID = Resources.getSystem().getIdentifier("prev","id", "android"); | ||
| 66 | + private static final int FFWD_BUTTON_ID = Resources.getSystem().getIdentifier("ffwd","id", "android"); | ||
| 67 | + private static final int NEXT_BUTTON_ID = Resources.getSystem().getIdentifier("next","id", "android"); | ||
| 68 | + private static final int REW_BUTTON_ID = Resources.getSystem().getIdentifier("rew","id", "android"); | ||
| 69 | + private static final int PAUSE_BUTTON_ID = Resources.getSystem().getIdentifier("pause","id", "android"); | ||
| 70 | + private static final int MEDIACONTROLLER_PROGRESS_ID = Resources.getSystem().getIdentifier("mediacontroller_progress","id", "android"); | ||
| 71 | + private static final int END_TIME_ID = Resources.getSystem().getIdentifier("time","id", "android"); | ||
| 72 | + private static final int CURRENT_TIME_ID = Resources.getSystem().getIdentifier("time_current","id", "android"); | ||
| 73 | + | ||
| 74 | + private AudioManager mAM; | ||
| 75 | + private Runnable mLastSeekBarRunnable; | ||
| 76 | + private boolean mDisableProgress = false; | ||
| 77 | + | ||
| 78 | + public MediaController(Context context, AttributeSet attrs) { | ||
| 79 | + super(context, attrs); | ||
| 80 | + mRoot = this; | ||
| 81 | + mFromXml = true; | ||
| 82 | + initController(context); | ||
| 83 | + } | ||
| 84 | + | ||
| 85 | + public MediaController(Context context) { | ||
| 86 | + super(context); | ||
| 87 | + if (!mFromXml && initController(context)) | ||
| 88 | + initFloatingWindow(); | ||
| 89 | + } | ||
| 90 | + | ||
| 91 | + public MediaController(Context context, boolean useFastForward, boolean disableProgressBar) { | ||
| 92 | + this(context); | ||
| 93 | + mUseFastForward = useFastForward; | ||
| 94 | + mDisableProgress = disableProgressBar; | ||
| 95 | + } | ||
| 96 | + | ||
| 97 | + public MediaController(Context context, boolean useFastForward) { | ||
| 98 | + this(context); | ||
| 99 | + mUseFastForward = useFastForward; | ||
| 100 | + } | ||
| 101 | + | ||
| 102 | + private boolean initController(Context context) { | ||
| 103 | + mUseFastForward = true; | ||
| 104 | + mContext = context.getApplicationContext(); | ||
| 105 | + mAM = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); | ||
| 106 | + return true; | ||
| 107 | + } | ||
| 108 | + | ||
| 109 | + @Override | ||
| 110 | + public void onFinishInflate() { | ||
| 111 | + if (mRoot != null) | ||
| 112 | + initControllerView(mRoot); | ||
| 113 | + super.onFinishInflate(); | ||
| 114 | + } | ||
| 115 | + | ||
| 116 | + private void initFloatingWindow() { | ||
| 117 | + mWindow = new PopupWindow(mContext); | ||
| 118 | + mWindow.setFocusable(false); | ||
| 119 | + mWindow.setBackgroundDrawable(null); | ||
| 120 | + mWindow.setOutsideTouchable(true); | ||
| 121 | + mAnimStyle = android.R.style.Animation; | ||
| 122 | + } | ||
| 123 | + | ||
| 124 | + /** | ||
| 125 | + * Create the view that holds the widgets that control playback. Derived | ||
| 126 | + * classes can override this to create their own. | ||
| 127 | + * | ||
| 128 | + * @return The controller view. | ||
| 129 | + */ | ||
| 130 | + protected View makeControllerView() { | ||
| 131 | + return ((LayoutInflater) mContext | ||
| 132 | + .getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(MEDIA_CONTROLLER_ID, this); | ||
| 133 | + } | ||
| 134 | + | ||
| 135 | + private void initControllerView(View v) { | ||
| 136 | + // By default these are hidden. | ||
| 137 | + mPrevButton = (ImageButton) v.findViewById(PRV_BUTTON_ID); | ||
| 138 | + if (mPrevButton != null) { | ||
| 139 | + mPrevButton.setVisibility(View.GONE); | ||
| 140 | + } | ||
| 141 | + mNextButton = (ImageButton) v.findViewById(NEXT_BUTTON_ID); | ||
| 142 | + if (mNextButton != null) { | ||
| 143 | + mNextButton.setVisibility(View.GONE); | ||
| 144 | + } | ||
| 145 | + | ||
| 146 | + mFfwdButton = (ImageButton) v.findViewById(FFWD_BUTTON_ID); | ||
| 147 | + if (mFfwdButton != null) { | ||
| 148 | + mFfwdButton.setOnClickListener(mFfwdListener); | ||
| 149 | + if (!mFromXml) { | ||
| 150 | + mFfwdButton.setVisibility(mUseFastForward ? View.VISIBLE : View.GONE); | ||
| 151 | + } | ||
| 152 | + } | ||
| 153 | + | ||
| 154 | + mRewButton = (ImageButton) v.findViewById(REW_BUTTON_ID); | ||
| 155 | + if (mRewButton != null) { | ||
| 156 | + mRewButton.setOnClickListener(mRewListener); | ||
| 157 | + if (!mFromXml) { | ||
| 158 | + mRewButton.setVisibility(mUseFastForward ? View.VISIBLE : View.GONE); | ||
| 159 | + } | ||
| 160 | + } | ||
| 161 | + mPauseButton = (ImageButton) v.findViewById(PAUSE_BUTTON_ID); | ||
| 162 | + if (mPauseButton != null) { | ||
| 163 | + mPauseButton.requestFocus(); | ||
| 164 | + mPauseButton.setOnClickListener(mPauseListener); | ||
| 165 | + } | ||
| 166 | + | ||
| 167 | + mProgress = (ProgressBar) v.findViewById(MEDIACONTROLLER_PROGRESS_ID); | ||
| 168 | + if (mProgress != null) { | ||
| 169 | + if (mProgress instanceof SeekBar) { | ||
| 170 | + SeekBar seeker = (SeekBar) mProgress; | ||
| 171 | + seeker.setOnSeekBarChangeListener(mSeekListener); | ||
| 172 | + seeker.setThumbOffset(1); | ||
| 173 | + } | ||
| 174 | + mProgress.setMax(1000); | ||
| 175 | + mProgress.setEnabled(!mDisableProgress); | ||
| 176 | + } | ||
| 177 | + | ||
| 178 | + mEndTime = (TextView) v.findViewById(END_TIME_ID); | ||
| 179 | + mCurrentTime = (TextView) v.findViewById(CURRENT_TIME_ID); | ||
| 180 | + } | ||
| 181 | + | ||
| 182 | + /** | ||
| 183 | + * Control the action when the seekbar dragged by user | ||
| 184 | + * | ||
| 185 | + * @param seekWhenDragging | ||
| 186 | + * True the media will seek periodically | ||
| 187 | + */ | ||
| 188 | + public void setInstantSeeking(boolean seekWhenDragging) { | ||
| 189 | + mInstantSeeking = seekWhenDragging; | ||
| 190 | + } | ||
| 191 | + | ||
| 192 | + private void disableUnsupportedButtons() { | ||
| 193 | + try { | ||
| 194 | + if (mPauseButton != null && !mPlayer.canPause()) | ||
| 195 | + mPauseButton.setEnabled(false); | ||
| 196 | + } catch (IncompatibleClassChangeError ex) { | ||
| 197 | + } | ||
| 198 | + } | ||
| 199 | + | ||
| 200 | + /** | ||
| 201 | + * <p> | ||
| 202 | + * Change the animation style resource for this controller. | ||
| 203 | + * </p> | ||
| 204 | + * | ||
| 205 | + * <p> | ||
| 206 | + * If the controller is showing, calling this method will take effect only | ||
| 207 | + * the next time the controller is shown. | ||
| 208 | + * </p> | ||
| 209 | + * | ||
| 210 | + * @param animationStyle | ||
| 211 | + * animation style to use when the controller appears and disappears. | ||
| 212 | + * Set to -1 for the default animation, 0 for no animation, | ||
| 213 | + * or a resource identifier for an explicit animation. | ||
| 214 | + * | ||
| 215 | + */ | ||
| 216 | + public void setAnimationStyle(int animationStyle) { | ||
| 217 | + mAnimStyle = animationStyle; | ||
| 218 | + } | ||
| 219 | + | ||
| 220 | + public interface OnShownListener { | ||
| 221 | + public void onShown(); | ||
| 222 | + } | ||
| 223 | + | ||
| 224 | + private OnShownListener mShownListener; | ||
| 225 | + | ||
| 226 | + public void setOnShownListener(OnShownListener l) { | ||
| 227 | + mShownListener = l; | ||
| 228 | + } | ||
| 229 | + | ||
| 230 | + public interface OnHiddenListener { | ||
| 231 | + public void onHidden(); | ||
| 232 | + } | ||
| 233 | + | ||
| 234 | + private OnHiddenListener mHiddenListener; | ||
| 235 | + | ||
| 236 | + public void setOnHiddenListener(OnHiddenListener l) { | ||
| 237 | + mHiddenListener = l; | ||
| 238 | + } | ||
| 239 | + | ||
| 240 | + @SuppressLint("HandlerLeak") | ||
| 241 | + private Handler mHandler = new Handler() { | ||
| 242 | + @Override | ||
| 243 | + public void handleMessage(Message msg) { | ||
| 244 | + long pos; | ||
| 245 | + switch (msg.what) { | ||
| 246 | + case FADE_OUT: | ||
| 247 | + hide(); | ||
| 248 | + break; | ||
| 249 | + case SHOW_PROGRESS: | ||
| 250 | + pos = setProgress(); | ||
| 251 | + if (!mDragging && mShowing) { | ||
| 252 | + msg = obtainMessage(SHOW_PROGRESS); | ||
| 253 | + sendMessageDelayed(msg, 1000 - (pos % 1000)); | ||
| 254 | + updatePausePlay(); | ||
| 255 | + } | ||
| 256 | + break; | ||
| 257 | + } | ||
| 258 | + } | ||
| 259 | + }; | ||
| 260 | + | ||
| 261 | + private long setProgress() { | ||
| 262 | + if (mPlayer == null || mDragging) | ||
| 263 | + return 0; | ||
| 264 | + | ||
| 265 | + long position = mPlayer.getCurrentPosition(); | ||
| 266 | + long duration = mPlayer.getDuration(); | ||
| 267 | + if (mProgress != null) { | ||
| 268 | + if (duration > 0) { | ||
| 269 | + long pos = 1000L * position / duration; | ||
| 270 | + mProgress.setProgress((int) pos); | ||
| 271 | + } | ||
| 272 | + int percent = mPlayer.getBufferPercentage(); | ||
| 273 | + mProgress.setSecondaryProgress(percent * 10); | ||
| 274 | + } | ||
| 275 | + | ||
| 276 | + mDuration = duration; | ||
| 277 | + | ||
| 278 | + if (mEndTime != null) | ||
| 279 | + mEndTime.setText(generateTime(mDuration)); | ||
| 280 | + if (mCurrentTime != null) | ||
| 281 | + mCurrentTime.setText(generateTime(position)); | ||
| 282 | + | ||
| 283 | + return position; | ||
| 284 | + } | ||
| 285 | + | ||
| 286 | + private static String generateTime(long position) { | ||
| 287 | + int totalSeconds = (int) (position / 1000); | ||
| 288 | + | ||
| 289 | + int seconds = totalSeconds % 60; | ||
| 290 | + int minutes = (totalSeconds / 60) % 60; | ||
| 291 | + int hours = totalSeconds / 3600; | ||
| 292 | + | ||
| 293 | + if (hours > 0) { | ||
| 294 | + return String.format(Locale.US, "%02d:%02d:%02d", hours, minutes, | ||
| 295 | + seconds).toString(); | ||
| 296 | + } else { | ||
| 297 | + return String.format(Locale.US, "%02d:%02d", minutes, seconds) | ||
| 298 | + .toString(); | ||
| 299 | + } | ||
| 300 | + } | ||
| 301 | + | ||
| 302 | + @Override | ||
| 303 | + public boolean onTouchEvent(MotionEvent event) { | ||
| 304 | + show(sDefaultTimeout); | ||
| 305 | + return true; | ||
| 306 | + } | ||
| 307 | + | ||
| 308 | + @Override | ||
| 309 | + public boolean onTrackballEvent(MotionEvent ev) { | ||
| 310 | + show(sDefaultTimeout); | ||
| 311 | + return false; | ||
| 312 | + } | ||
| 313 | + | ||
| 314 | + @Override | ||
| 315 | + public boolean dispatchKeyEvent(KeyEvent event) { | ||
| 316 | + int keyCode = event.getKeyCode(); | ||
| 317 | + if (event.getRepeatCount() == 0 | ||
| 318 | + && (keyCode == KeyEvent.KEYCODE_HEADSETHOOK | ||
| 319 | + || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE || keyCode == KeyEvent.KEYCODE_SPACE)) { | ||
| 320 | + doPauseResume(); | ||
| 321 | + show(sDefaultTimeout); | ||
| 322 | + if (mPauseButton != null) | ||
| 323 | + mPauseButton.requestFocus(); | ||
| 324 | + return true; | ||
| 325 | + } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP) { | ||
| 326 | + if (mPlayer.isPlaying()) { | ||
| 327 | + mPlayer.pause(); | ||
| 328 | + updatePausePlay(); | ||
| 329 | + } | ||
| 330 | + return true; | ||
| 331 | + } else if (keyCode == KeyEvent.KEYCODE_BACK | ||
| 332 | + || keyCode == KeyEvent.KEYCODE_MENU) { | ||
| 333 | + hide(); | ||
| 334 | + return true; | ||
| 335 | + } else { | ||
| 336 | + show(sDefaultTimeout); | ||
| 337 | + } | ||
| 338 | + return super.dispatchKeyEvent(event); | ||
| 339 | + } | ||
| 340 | + | ||
| 341 | + private OnClickListener mPauseListener = new OnClickListener() { | ||
| 342 | + public void onClick(View v) { | ||
| 343 | + doPauseResume(); | ||
| 344 | + show(sDefaultTimeout); | ||
| 345 | + } | ||
| 346 | + }; | ||
| 347 | + | ||
| 348 | + private void updatePausePlay() { | ||
| 349 | + if (mRoot == null || mPauseButton == null) | ||
| 350 | + return; | ||
| 351 | + | ||
| 352 | + if (mPlayer.isPlaying()) | ||
| 353 | + mPauseButton.setImageResource(IC_MEDIA_PAUSE_ID); | ||
| 354 | + else | ||
| 355 | + mPauseButton.setImageResource(IC_MEDIA_PLAY_ID); | ||
| 356 | + } | ||
| 357 | + | ||
| 358 | + private void doPauseResume() { | ||
| 359 | + if (mPlayer.isPlaying()) | ||
| 360 | + mPlayer.pause(); | ||
| 361 | + else | ||
| 362 | + mPlayer.start(); | ||
| 363 | + updatePausePlay(); | ||
| 364 | + } | ||
| 365 | + | ||
| 366 | + private SeekBar.OnSeekBarChangeListener mSeekListener = new SeekBar.OnSeekBarChangeListener() { | ||
| 367 | + | ||
| 368 | + public void onStartTrackingTouch(SeekBar bar) { | ||
| 369 | + mDragging = true; | ||
| 370 | + show(3600000); | ||
| 371 | + mHandler.removeMessages(SHOW_PROGRESS); | ||
| 372 | + if (mInstantSeeking) | ||
| 373 | + mAM.setStreamMute(AudioManager.STREAM_MUSIC, true); | ||
| 374 | + } | ||
| 375 | + | ||
| 376 | + public void onProgressChanged(SeekBar bar, int progress, boolean fromuser) { | ||
| 377 | + if (!fromuser) | ||
| 378 | + return; | ||
| 379 | + | ||
| 380 | + final long newposition = (long) (mDuration * progress) / 1000; | ||
| 381 | + String time = generateTime(newposition); | ||
| 382 | + if (mInstantSeeking) { | ||
| 383 | + mHandler.removeCallbacks(mLastSeekBarRunnable); | ||
| 384 | + mLastSeekBarRunnable = new Runnable() { | ||
| 385 | + @Override | ||
| 386 | + public void run() { | ||
| 387 | + mPlayer.seekTo(newposition); | ||
| 388 | + } | ||
| 389 | + }; | ||
| 390 | + mHandler.postDelayed(mLastSeekBarRunnable, SEEK_TO_POST_DELAY_MILLIS); | ||
| 391 | + } | ||
| 392 | + if (mCurrentTime != null) | ||
| 393 | + mCurrentTime.setText(time); | ||
| 394 | + } | ||
| 395 | + | ||
| 396 | + public void onStopTrackingTouch(SeekBar bar) { | ||
| 397 | + if (!mInstantSeeking) | ||
| 398 | + mPlayer.seekTo(mDuration * bar.getProgress() / 1000); | ||
| 399 | + | ||
| 400 | + show(sDefaultTimeout); | ||
| 401 | + mHandler.removeMessages(SHOW_PROGRESS); | ||
| 402 | + mAM.setStreamMute(AudioManager.STREAM_MUSIC, false); | ||
| 403 | + mDragging = false; | ||
| 404 | + mHandler.sendEmptyMessageDelayed(SHOW_PROGRESS, 1000); | ||
| 405 | + } | ||
| 406 | + }; | ||
| 407 | + | ||
| 408 | + private OnClickListener mRewListener = new OnClickListener() { | ||
| 409 | + public void onClick(View v) { | ||
| 410 | + long pos = mPlayer.getCurrentPosition(); | ||
| 411 | + pos -= 5000; // milliseconds | ||
| 412 | + mPlayer.seekTo(pos); | ||
| 413 | + setProgress(); | ||
| 414 | + | ||
| 415 | + show(sDefaultTimeout); | ||
| 416 | + } | ||
| 417 | + }; | ||
| 418 | + | ||
| 419 | + private OnClickListener mFfwdListener = new OnClickListener() { | ||
| 420 | + public void onClick(View v) { | ||
| 421 | + long pos = mPlayer.getCurrentPosition(); | ||
| 422 | + pos += 15000; // milliseconds | ||
| 423 | + mPlayer.seekTo(pos); | ||
| 424 | + setProgress(); | ||
| 425 | + | ||
| 426 | + show(sDefaultTimeout); | ||
| 427 | + } | ||
| 428 | + }; | ||
| 429 | + | ||
| 430 | + /** | ||
| 431 | + * Set the view that acts as the anchor for the control view. | ||
| 432 | + * | ||
| 433 | + * - This can for example be a VideoView, or your Activity's main view. | ||
| 434 | + * - AudioPlayer has no anchor view, so the view parameter will be null. | ||
| 435 | + * | ||
| 436 | + * @param view | ||
| 437 | + * The view to which to anchor the controller when it is visible. | ||
| 438 | + */ | ||
| 439 | + @Override | ||
| 440 | + public void setAnchorView(View view) { | ||
| 441 | + mAnchor = view; | ||
| 442 | + if (mAnchor == null) { | ||
| 443 | + sDefaultTimeout = 0; // show forever | ||
| 444 | + } | ||
| 445 | + if (!mFromXml) { | ||
| 446 | + removeAllViews(); | ||
| 447 | + mRoot = makeControllerView(); | ||
| 448 | + mWindow.setContentView(mRoot); | ||
| 449 | + mWindow.setWidth(LayoutParams.MATCH_PARENT); | ||
| 450 | + mWindow.setHeight(LayoutParams.WRAP_CONTENT); | ||
| 451 | + } | ||
| 452 | + initControllerView(mRoot); | ||
| 453 | + } | ||
| 454 | + | ||
| 455 | + @Override | ||
| 456 | + public void setMediaPlayer(MediaPlayerControl player) { | ||
| 457 | + mPlayer = player; | ||
| 458 | + updatePausePlay(); | ||
| 459 | + } | ||
| 460 | + | ||
| 461 | + @Override | ||
| 462 | + public void show() { | ||
| 463 | + show(sDefaultTimeout); | ||
| 464 | + } | ||
| 465 | + | ||
| 466 | + /** | ||
| 467 | + * Show the controller on screen. It will go away automatically after | ||
| 468 | + * 'timeout' milliseconds of inactivity. | ||
| 469 | + * | ||
| 470 | + * @param timeout | ||
| 471 | + * The timeout in milliseconds. Use 0 to show the controller until hide() is called. | ||
| 472 | + */ | ||
| 473 | + @Override | ||
| 474 | + public void show(int timeout) { | ||
| 475 | + if (!mShowing) { | ||
| 476 | + if (mAnchor != null && mAnchor.getWindowToken() != null) { | ||
| 477 | + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { | ||
| 478 | + mAnchor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE); | ||
| 479 | + } | ||
| 480 | + } | ||
| 481 | + if (mPauseButton != null) | ||
| 482 | + mPauseButton.requestFocus(); | ||
| 483 | + disableUnsupportedButtons(); | ||
| 484 | + | ||
| 485 | + if (mFromXml) { | ||
| 486 | + setVisibility(View.VISIBLE); | ||
| 487 | + } else { | ||
| 488 | + int[] location = new int[2]; | ||
| 489 | + | ||
| 490 | + if (mAnchor != null) { | ||
| 491 | + mAnchor.getLocationOnScreen(location); | ||
| 492 | + Rect anchorRect = new Rect(location[0], location[1], | ||
| 493 | + location[0] + mAnchor.getWidth(), location[1] | ||
| 494 | + + mAnchor.getHeight()); | ||
| 495 | + | ||
| 496 | + mWindow.setAnimationStyle(mAnimStyle); | ||
| 497 | + mWindow.showAtLocation(mAnchor, Gravity.BOTTOM, | ||
| 498 | + anchorRect.left, 0); | ||
| 499 | + } else { | ||
| 500 | + Rect anchorRect = new Rect(location[0], location[1], | ||
| 501 | + location[0] + mRoot.getWidth(), location[1] | ||
| 502 | + + mRoot.getHeight()); | ||
| 503 | + | ||
| 504 | + mWindow.setAnimationStyle(mAnimStyle); | ||
| 505 | + mWindow.showAtLocation(mRoot, Gravity.BOTTOM, | ||
| 506 | + anchorRect.left, 0); | ||
| 507 | + } | ||
| 508 | + } | ||
| 509 | + mShowing = true; | ||
| 510 | + if (mShownListener != null) | ||
| 511 | + mShownListener.onShown(); | ||
| 512 | + } | ||
| 513 | + updatePausePlay(); | ||
| 514 | + mHandler.sendEmptyMessage(SHOW_PROGRESS); | ||
| 515 | + | ||
| 516 | + if (timeout != 0) { | ||
| 517 | + mHandler.removeMessages(FADE_OUT); | ||
| 518 | + mHandler.sendMessageDelayed(mHandler.obtainMessage(FADE_OUT), | ||
| 519 | + timeout); | ||
| 520 | + } | ||
| 521 | + } | ||
| 522 | + | ||
| 523 | + @Override | ||
| 524 | + public boolean isShowing() { | ||
| 525 | + return mShowing; | ||
| 526 | + } | ||
| 527 | + | ||
| 528 | + @Override | ||
| 529 | + public void hide() { | ||
| 530 | + if (mShowing) { | ||
| 531 | + if (mAnchor != null) { | ||
| 532 | + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { | ||
| 533 | + //mAnchor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION); | ||
| 534 | + } | ||
| 535 | + } | ||
| 536 | + try { | ||
| 537 | + mHandler.removeMessages(SHOW_PROGRESS); | ||
| 538 | + if (mFromXml) | ||
| 539 | + setVisibility(View.GONE); | ||
| 540 | + else | ||
| 541 | + mWindow.dismiss(); | ||
| 542 | + } catch (IllegalArgumentException ex) { | ||
| 543 | + Log.d(TAG, "MediaController already removed"); | ||
| 544 | + } | ||
| 545 | + mShowing = false; | ||
| 546 | + if (mHiddenListener != null) | ||
| 547 | + mHiddenListener.onHidden(); | ||
| 548 | + } | ||
| 549 | + } | ||
| 550 | + | ||
| 551 | + @Override | ||
| 552 | + public void setEnabled(boolean enabled) { | ||
| 553 | + if (mPauseButton != null) { | ||
| 554 | + mPauseButton.setEnabled(enabled); | ||
| 555 | + } | ||
| 556 | + if (mFfwdButton != null) { | ||
| 557 | + mFfwdButton.setEnabled(enabled); | ||
| 558 | + } | ||
| 559 | + if (mRewButton != null) { | ||
| 560 | + mRewButton.setEnabled(enabled); | ||
| 561 | + } | ||
| 562 | + if (mProgress != null && !mDisableProgress) | ||
| 563 | + mProgress.setEnabled(enabled); | ||
| 564 | + disableUnsupportedButtons(); | ||
| 565 | + super.setEnabled(enabled); | ||
| 566 | + } | ||
| 567 | +} |
app/src/main/jniLibs/arm64-v8a/libpldroidplayer.so
0 → 100644
No preview for this file type
app/src/main/jniLibs/armeabi-v7a/libpldroidplayer.so
0 → 100644
No preview for this file type
app/src/main/jniLibs/armeabi/libpldroidplayer.so
0 → 100644
No preview for this file type
app/src/main/jniLibs/x86/libpldroidplayer.so
0 → 100644
No preview for this file type
app/src/main/res/drawable/audio.jpg
0 → 100644
48.3 KB
app/src/main/res/drawable/ic_rotate_right.png
0 → 100644
1.95 KB
app/src/main/res/drawable/ic_switch_screen.png
0 → 100644
1.77 KB
app/src/main/res/drawable/thumbnail.png
0 → 100644
24.3 KB
app/src/main/res/layout/activity_audio_player.xml
0 → 100644
| 1 | +++ a/app/src/main/res/layout/activity_audio_player.xml | ||
| 1 | +<?xml version="1.0" encoding="utf-8"?> | ||
| 2 | +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||
| 3 | + android:layout_width="match_parent" | ||
| 4 | + android:layout_height="match_parent" | ||
| 5 | + android:orientation="vertical"> | ||
| 6 | + | ||
| 7 | + <ImageView | ||
| 8 | + android:layout_width="wrap_content" | ||
| 9 | + android:layout_height="wrap_content" | ||
| 10 | + android:src="@drawable/audio" | ||
| 11 | + android:layout_margin="20dp"/> | ||
| 12 | + | ||
| 13 | + <LinearLayout | ||
| 14 | + android:id="@+id/LoadingView" | ||
| 15 | + android:layout_width="match_parent" | ||
| 16 | + android:layout_height="wrap_content" | ||
| 17 | + android:layout_gravity="center" | ||
| 18 | + android:gravity="center" | ||
| 19 | + android:orientation="horizontal"> | ||
| 20 | + <ProgressBar | ||
| 21 | + android:layout_width="wrap_content" | ||
| 22 | + android:layout_height="wrap_content" | ||
| 23 | + android:layout_marginRight="5dp"/> | ||
| 24 | + <TextView | ||
| 25 | + android:layout_width="wrap_content" | ||
| 26 | + android:layout_height="wrap_content" | ||
| 27 | + android:layout_marginLeft="5dp" | ||
| 28 | + android:text="buffering" | ||
| 29 | + android:textColor="@android:color/black" | ||
| 30 | + android:textSize="20sp"/> | ||
| 31 | + </LinearLayout> | ||
| 32 | + | ||
| 33 | + <include | ||
| 34 | + layout="@layout/layout_buttons" | ||
| 35 | + android:layout_width="match_parent" | ||
| 36 | + android:layout_height="wrap_content" | ||
| 37 | + android:layout_margin="10dp" | ||
| 38 | + android:layout_gravity="bottom" /> | ||
| 39 | + | ||
| 40 | +</FrameLayout> |
app/src/main/res/layout/activity_main.xml
0 → 100644
| 1 | +++ a/app/src/main/res/layout/activity_main.xml | ||
| 1 | +<?xml version="1.0" encoding="utf-8"?> | ||
| 2 | +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||
| 3 | + android:layout_width="match_parent" | ||
| 4 | + android:layout_height="match_parent" | ||
| 5 | + android:orientation="vertical"> | ||
| 6 | + | ||
| 7 | + <TextView | ||
| 8 | + android:id="@+id/version_info" | ||
| 9 | + android:layout_width="match_parent" | ||
| 10 | + android:layout_height="wrap_content" | ||
| 11 | + android:layout_margin="15dp" | ||
| 12 | + android:textSize="20sp" | ||
| 13 | + android:textStyle="bold" /> | ||
| 14 | + | ||
| 15 | + <TextView | ||
| 16 | + android:layout_width="match_parent" | ||
| 17 | + android:layout_height="wrap_content" | ||
| 18 | + android:layout_margin="15dp" | ||
| 19 | + android:text="@string/pls_choose_activity" | ||
| 20 | + android:textSize="20sp" | ||
| 21 | + android:textStyle="bold" /> | ||
| 22 | + | ||
| 23 | + <Spinner | ||
| 24 | + android:id="@+id/TestSpinner" | ||
| 25 | + android:layout_width="match_parent" | ||
| 26 | + android:layout_height="wrap_content" | ||
| 27 | + android:layout_marginLeft="10dp" /> | ||
| 28 | + | ||
| 29 | + <TextView | ||
| 30 | + android:layout_width="match_parent" | ||
| 31 | + android:layout_height="wrap_content" | ||
| 32 | + android:layout_margin="15dp" | ||
| 33 | + android:text="@string/pls_choose_setting" | ||
| 34 | + android:textSize="20sp" | ||
| 35 | + android:textStyle="bold" /> | ||
| 36 | + | ||
| 37 | + <RadioGroup | ||
| 38 | + android:id="@+id/StreamingTypeRadioGroup" | ||
| 39 | + android:layout_width="match_parent" | ||
| 40 | + android:layout_height="wrap_content" | ||
| 41 | + android:layout_marginLeft="15dp" | ||
| 42 | + android:layout_marginTop="5dp" | ||
| 43 | + android:orientation="horizontal"> | ||
| 44 | + | ||
| 45 | + <RadioButton | ||
| 46 | + android:id="@+id/RadioLiveStreaming" | ||
| 47 | + android:layout_width="wrap_content" | ||
| 48 | + android:layout_height="wrap_content" | ||
| 49 | + android:checked="true" | ||
| 50 | + android:text="@string/live_streaming" /> | ||
| 51 | + | ||
| 52 | + <RadioButton | ||
| 53 | + android:id="@+id/RadioPlayback" | ||
| 54 | + android:layout_width="wrap_content" | ||
| 55 | + android:layout_height="wrap_content" | ||
| 56 | + android:layout_marginLeft="10dp" | ||
| 57 | + android:text="@string/playback" /> | ||
| 58 | + | ||
| 59 | + </RadioGroup> | ||
| 60 | + | ||
| 61 | + <RadioGroup | ||
| 62 | + android:id="@+id/DecodeTypeRadioGroup" | ||
| 63 | + android:layout_width="match_parent" | ||
| 64 | + android:layout_height="wrap_content" | ||
| 65 | + android:layout_marginLeft="15dp" | ||
| 66 | + android:layout_marginTop="5dp" | ||
| 67 | + android:orientation="horizontal"> | ||
| 68 | + | ||
| 69 | + <RadioButton | ||
| 70 | + android:id="@+id/RadioSWDecode" | ||
| 71 | + android:layout_width="wrap_content" | ||
| 72 | + android:layout_height="wrap_content" | ||
| 73 | + android:checked="true" | ||
| 74 | + android:text="@string/sw_decode" /> | ||
| 75 | + | ||
| 76 | + <RadioButton | ||
| 77 | + android:id="@+id/RadioHWDecode" | ||
| 78 | + android:layout_width="wrap_content" | ||
| 79 | + android:layout_height="wrap_content" | ||
| 80 | + android:layout_marginLeft="10dp" | ||
| 81 | + android:text="@string/hw_decode" /> | ||
| 82 | + | ||
| 83 | + </RadioGroup> | ||
| 84 | + | ||
| 85 | + <TextView | ||
| 86 | + android:layout_width="match_parent" | ||
| 87 | + android:layout_height="wrap_content" | ||
| 88 | + android:layout_marginLeft="15dp" | ||
| 89 | + android:layout_marginRight="15dp" | ||
| 90 | + android:layout_marginTop="15dp" | ||
| 91 | + android:text="@string/pls_choose_videopath" | ||
| 92 | + android:textSize="20sp" | ||
| 93 | + android:textStyle="bold" /> | ||
| 94 | + | ||
| 95 | + <EditText | ||
| 96 | + android:id="@+id/VideoPathEdit" | ||
| 97 | + android:layout_width="match_parent" | ||
| 98 | + android:layout_height="wrap_content" | ||
| 99 | + android:layout_margin="10dp" | ||
| 100 | + android:hint="@string/input_video_path" | ||
| 101 | + android:padding="10sp" | ||
| 102 | + android:singleLine="true" | ||
| 103 | + android:textSize="18sp" /> | ||
| 104 | + | ||
| 105 | + <LinearLayout | ||
| 106 | + android:layout_width="match_parent" | ||
| 107 | + android:layout_height="wrap_content" | ||
| 108 | + android:layout_margin="10dp" | ||
| 109 | + android:orientation="horizontal"> | ||
| 110 | + | ||
| 111 | + <Button | ||
| 112 | + android:layout_width="wrap_content" | ||
| 113 | + android:layout_height="wrap_content" | ||
| 114 | + android:onClick="onClickLocalFile" | ||
| 115 | + android:text="@string/btn_local_file" | ||
| 116 | + android:textSize="12sp" /> | ||
| 117 | + | ||
| 118 | + <Button | ||
| 119 | + android:layout_width="wrap_content" | ||
| 120 | + android:layout_height="wrap_content" | ||
| 121 | + android:layout_marginLeft="10dp" | ||
| 122 | + android:onClick="onClickPlay" | ||
| 123 | + android:text="@string/btn_click_play" | ||
| 124 | + android:textSize="12sp" /> | ||
| 125 | + | ||
| 126 | + </LinearLayout> | ||
| 127 | + | ||
| 128 | +</LinearLayout> | ||
| 0 | \ No newline at end of file | 129 | \ No newline at end of file |
app/src/main/res/layout/activity_media_player.xml
0 → 100644
| 1 | +++ a/app/src/main/res/layout/activity_media_player.xml | ||
| 1 | +<?xml version="1.0" encoding="utf-8"?> | ||
| 2 | +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||
| 3 | + android:layout_width="match_parent" | ||
| 4 | + android:layout_height="match_parent"> | ||
| 5 | + | ||
| 6 | + <SurfaceView | ||
| 7 | + android:id="@+id/SurfaceView" | ||
| 8 | + android:layout_width="match_parent" | ||
| 9 | + android:layout_height="match_parent" | ||
| 10 | + android:layout_gravity="center" /> | ||
| 11 | + | ||
| 12 | + <LinearLayout | ||
| 13 | + android:id="@+id/LoadingView" | ||
| 14 | + android:layout_width="match_parent" | ||
| 15 | + android:layout_height="wrap_content" | ||
| 16 | + android:layout_gravity="center" | ||
| 17 | + android:gravity="center" | ||
| 18 | + android:orientation="horizontal"> | ||
| 19 | + <ProgressBar | ||
| 20 | + android:layout_width="wrap_content" | ||
| 21 | + android:layout_height="wrap_content" | ||
| 22 | + android:layout_marginRight="5dp"/> | ||
| 23 | + <TextView | ||
| 24 | + android:layout_width="wrap_content" | ||
| 25 | + android:layout_height="wrap_content" | ||
| 26 | + android:layout_marginLeft="5dp" | ||
| 27 | + android:text="@string/buffering" | ||
| 28 | + android:textColor="@android:color/white" | ||
| 29 | + android:textSize="20sp"/> | ||
| 30 | + </LinearLayout> | ||
| 31 | + | ||
| 32 | + <include | ||
| 33 | + layout="@layout/layout_buttons" | ||
| 34 | + android:layout_width="match_parent" | ||
| 35 | + android:layout_height="wrap_content" | ||
| 36 | + android:layout_margin="10dp" | ||
| 37 | + android:layout_gravity="bottom" /> | ||
| 38 | + | ||
| 39 | +</FrameLayout> |
app/src/main/res/layout/activity_pl_video_texture.xml
0 → 100644
| 1 | +++ a/app/src/main/res/layout/activity_pl_video_texture.xml | ||
| 1 | +<?xml version="1.0" encoding="utf-8"?> | ||
| 2 | +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||
| 3 | + android:layout_width="match_parent" | ||
| 4 | + android:layout_height="match_parent" | ||
| 5 | + android:orientation="vertical"> | ||
| 6 | + | ||
| 7 | + <com.cnlive.libs.lib_cnvideo.player.widget.CNVideoTextureView | ||
| 8 | + android:id="@+id/VideoView" | ||
| 9 | + android:layout_width="match_parent" | ||
| 10 | + android:layout_height="match_parent" | ||
| 11 | + android:layout_gravity="center" /> | ||
| 12 | + | ||
| 13 | + <ImageView | ||
| 14 | + android:id="@+id/CoverView" | ||
| 15 | + android:layout_width="match_parent" | ||
| 16 | + android:layout_height="match_parent" | ||
| 17 | + android:layout_gravity="center" | ||
| 18 | + android:src="@drawable/thumbnail"/> | ||
| 19 | + | ||
| 20 | + <LinearLayout | ||
| 21 | + android:id="@+id/LoadingView" | ||
| 22 | + android:layout_width="match_parent" | ||
| 23 | + android:layout_height="wrap_content" | ||
| 24 | + android:layout_gravity="center" | ||
| 25 | + android:gravity="center" | ||
| 26 | + android:orientation="horizontal"> | ||
| 27 | + | ||
| 28 | + <ProgressBar | ||
| 29 | + android:layout_width="wrap_content" | ||
| 30 | + android:layout_height="wrap_content" | ||
| 31 | + android:layout_marginRight="5dp" /> | ||
| 32 | + | ||
| 33 | + <TextView | ||
| 34 | + android:layout_width="wrap_content" | ||
| 35 | + android:layout_height="wrap_content" | ||
| 36 | + android:layout_marginLeft="5dp" | ||
| 37 | + android:text="@string/buffering" | ||
| 38 | + android:textColor="@android:color/white" | ||
| 39 | + android:textSize="20sp" /> | ||
| 40 | + </LinearLayout> | ||
| 41 | + | ||
| 42 | + <LinearLayout | ||
| 43 | + android:layout_width="wrap_content" | ||
| 44 | + android:layout_height="wrap_content" | ||
| 45 | + android:layout_gravity="top|right" | ||
| 46 | + android:layout_margin="20dp" | ||
| 47 | + android:orientation="horizontal"> | ||
| 48 | + | ||
| 49 | + <ImageButton | ||
| 50 | + android:layout_width="wrap_content" | ||
| 51 | + android:layout_height="wrap_content" | ||
| 52 | + android:layout_margin="5dp" | ||
| 53 | + android:onClick="onClickRotate" | ||
| 54 | + android:src="@drawable/ic_rotate_right" /> | ||
| 55 | + | ||
| 56 | + <ImageButton | ||
| 57 | + android:layout_width="wrap_content" | ||
| 58 | + android:layout_height="wrap_content" | ||
| 59 | + android:layout_margin="5dp" | ||
| 60 | + android:onClick="onClickSwitchScreen" | ||
| 61 | + android:src="@drawable/ic_switch_screen" /> | ||
| 62 | + | ||
| 63 | + </LinearLayout> | ||
| 64 | + | ||
| 65 | +</FrameLayout> |
app/src/main/res/layout/activity_pl_video_view.xml
0 → 100644
| 1 | +++ a/app/src/main/res/layout/activity_pl_video_view.xml | ||
| 1 | +<?xml version="1.0" encoding="utf-8"?> | ||
| 2 | +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||
| 3 | + android:layout_width="match_parent" | ||
| 4 | + android:layout_height="match_parent"> | ||
| 5 | + | ||
| 6 | + <com.cnlive.libs.lib_cnvideo.player.widget.CNVideoView | ||
| 7 | + android:id="@+id/VideoView" | ||
| 8 | + android:layout_width="match_parent" | ||
| 9 | + android:layout_height="match_parent" | ||
| 10 | + android:layout_gravity="center" /> | ||
| 11 | + | ||
| 12 | + <ImageView | ||
| 13 | + android:id="@+id/CoverView" | ||
| 14 | + android:layout_width="match_parent" | ||
| 15 | + android:layout_height="match_parent" | ||
| 16 | + android:layout_gravity="center" | ||
| 17 | + android:src="@drawable/thumbnail" /> | ||
| 18 | + | ||
| 19 | + <LinearLayout | ||
| 20 | + android:id="@+id/LoadingView" | ||
| 21 | + android:layout_width="match_parent" | ||
| 22 | + android:layout_height="wrap_content" | ||
| 23 | + android:layout_gravity="center" | ||
| 24 | + android:gravity="center" | ||
| 25 | + android:orientation="horizontal"> | ||
| 26 | + | ||
| 27 | + <ProgressBar | ||
| 28 | + android:layout_width="wrap_content" | ||
| 29 | + android:layout_height="wrap_content" | ||
| 30 | + android:layout_marginRight="5dp" /> | ||
| 31 | + | ||
| 32 | + <TextView | ||
| 33 | + android:layout_width="wrap_content" | ||
| 34 | + android:layout_height="wrap_content" | ||
| 35 | + android:layout_marginLeft="5dp" | ||
| 36 | + android:text="@string/buffering" | ||
| 37 | + android:textColor="@android:color/white" | ||
| 38 | + android:textSize="20sp" /> | ||
| 39 | + </LinearLayout> | ||
| 40 | + | ||
| 41 | + <LinearLayout | ||
| 42 | + android:layout_width="wrap_content" | ||
| 43 | + android:layout_height="wrap_content" | ||
| 44 | + android:layout_gravity="top|right" | ||
| 45 | + android:layout_margin="20dp" | ||
| 46 | + android:orientation="horizontal"> | ||
| 47 | + | ||
| 48 | + <ImageButton | ||
| 49 | + android:layout_width="wrap_content" | ||
| 50 | + android:layout_height="wrap_content" | ||
| 51 | + android:layout_margin="5dp" | ||
| 52 | + android:onClick="onClickSwitchScreen" | ||
| 53 | + android:src="@drawable/ic_switch_screen" /> | ||
| 54 | + | ||
| 55 | + </LinearLayout> | ||
| 56 | + | ||
| 57 | +</FrameLayout> |
app/src/main/res/layout/activity_video_file.xml
0 → 100644
| 1 | +++ a/app/src/main/res/layout/activity_video_file.xml | ||
| 1 | +<?xml version="1.0" encoding="utf-8"?> | ||
| 2 | +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||
| 3 | + android:orientation="vertical" android:layout_width="match_parent" | ||
| 4 | + android:layout_height="match_parent"> | ||
| 5 | + | ||
| 6 | + <ListView | ||
| 7 | + android:id="@+id/FileListView" | ||
| 8 | + android:layout_width="match_parent" | ||
| 9 | + android:layout_height="wrap_content" | ||
| 10 | + android:choiceMode="singleChoice" | ||
| 11 | + android:layout_margin="10dp" /> | ||
| 12 | + | ||
| 13 | +</LinearLayout> | ||
| 0 | \ No newline at end of file | 14 | \ No newline at end of file |
app/src/main/res/layout/activity_video_view.xml
0 → 100644
| 1 | +++ a/app/src/main/res/layout/activity_video_view.xml | ||
| 1 | +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||
| 2 | + xmlns:tools="http://schemas.android.com/tools" | ||
| 3 | + android:layout_width="match_parent" | ||
| 4 | + android:layout_height="match_parent" | ||
| 5 | + android:background="@color/background_material_light"> | ||
| 6 | + | ||
| 7 | + <com.cnlive.demo.video.widget.AspectLayout | ||
| 8 | + android:id="@+id/aspect_layout" | ||
| 9 | + android:layout_width="match_parent" | ||
| 10 | + android:layout_height="match_parent"> | ||
| 11 | + | ||
| 12 | + <com.cnlive.libs.lib_cnvideo.player.widget.CNVideoView | ||
| 13 | + android:id="@+id/video_view" | ||
| 14 | + android:layout_width="match_parent" | ||
| 15 | + android:layout_height="match_parent" | ||
| 16 | + android:layout_alignParentBottom="true" | ||
| 17 | + android:layout_alignParentLeft="true" | ||
| 18 | + android:layout_alignParentRight="true" | ||
| 19 | + android:layout_alignParentTop="true" | ||
| 20 | + android:layout_centerInParent="true" /> | ||
| 21 | + </com.cnlive.demo.video.widget.AspectLayout> | ||
| 22 | + | ||
| 23 | + <Button | ||
| 24 | + android:id="@+id/back_btn" | ||
| 25 | + android:layout_width="wrap_content" | ||
| 26 | + android:layout_height="wrap_content" | ||
| 27 | + android:layout_gravity="top|left" | ||
| 28 | + android:layout_margin="20dp" | ||
| 29 | + android:background="@color/background_floating_material_dark" | ||
| 30 | + android:text="@string/back_title" /> | ||
| 31 | + | ||
| 32 | + <EditText | ||
| 33 | + android:layout_width="match_parent" | ||
| 34 | + android:layout_height="wrap_content" | ||
| 35 | + android:layout_alignParentBottom="true" /> | ||
| 36 | + | ||
| 37 | + <LinearLayout | ||
| 38 | + android:id="@+id/buffering_indicator" | ||
| 39 | + android:layout_width="wrap_content" | ||
| 40 | + android:layout_height="wrap_content" | ||
| 41 | + android:layout_centerInParent="true" | ||
| 42 | + android:layout_gravity="center" | ||
| 43 | + android:focusable="false" | ||
| 44 | + android:focusableInTouchMode="false" | ||
| 45 | + android:orientation="vertical" | ||
| 46 | + android:visibility="gone"> | ||
| 47 | + | ||
| 48 | + <TextView | ||
| 49 | + android:id="@+id/buffering_msg" | ||
| 50 | + android:layout_width="wrap_content" | ||
| 51 | + android:layout_height="wrap_content" | ||
| 52 | + android:layout_gravity="center" | ||
| 53 | + android:adjustViewBounds="true" | ||
| 54 | + android:ellipsize="marquee" | ||
| 55 | + android:marqueeRepeatLimit="0" | ||
| 56 | + android:scaleType="centerInside" | ||
| 57 | + android:singleLine="true" | ||
| 58 | + android:text="@string/buffering" | ||
| 59 | + android:textSize="12sp" /> | ||
| 60 | + | ||
| 61 | + <ProgressBar | ||
| 62 | + android:id="@+id/buffering_progress" | ||
| 63 | + style="@android:style/Widget.ProgressBar.Large" | ||
| 64 | + android:layout_width="60dip" | ||
| 65 | + android:layout_height="60dip" /> | ||
| 66 | + </LinearLayout> | ||
| 67 | + | ||
| 68 | +</RelativeLayout> | ||
| 0 | \ No newline at end of file | 69 | \ No newline at end of file |
app/src/main/res/layout/dialog_setting.xml
0 → 100644
| 1 | +++ a/app/src/main/res/layout/dialog_setting.xml | ||
| 1 | +<?xml version="1.0" encoding="utf-8"?> | ||
| 2 | +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||
| 3 | + android:orientation="vertical" android:layout_width="match_parent" | ||
| 4 | + android:layout_height="match_parent"> | ||
| 5 | + | ||
| 6 | + <TextView | ||
| 7 | + android:layout_width="match_parent" | ||
| 8 | + android:layout_height="wrap_content" | ||
| 9 | + android:text="@string/decode_config" | ||
| 10 | + android:layout_margin="10dp" | ||
| 11 | + android:textSize="15sp" | ||
| 12 | + android:textStyle="bold" /> | ||
| 13 | + | ||
| 14 | + <Spinner | ||
| 15 | + android:id="@+id/CodecSpinner" | ||
| 16 | + android:layout_width="match_parent" | ||
| 17 | + android:layout_height="wrap_content" | ||
| 18 | + android:layout_marginLeft="10dp" | ||
| 19 | + android:layout_marginBottom="20dp" /> | ||
| 20 | + | ||
| 21 | +</LinearLayout> | ||
| 0 | \ No newline at end of file | 22 | \ No newline at end of file |
app/src/main/res/layout/layout_buttons.xml
0 → 100644
| 1 | +++ a/app/src/main/res/layout/layout_buttons.xml | ||
| 1 | +<?xml version="1.0" encoding="utf-8"?> | ||
| 2 | +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||
| 3 | + android:layout_width="match_parent" | ||
| 4 | + android:layout_height="wrap_content" | ||
| 5 | + android:orientation="horizontal"> | ||
| 6 | + | ||
| 7 | + <Button | ||
| 8 | + android:id="@+id/BtnPlay" | ||
| 9 | + android:layout_width="wrap_content" | ||
| 10 | + android:layout_height="wrap_content" | ||
| 11 | + android:layout_weight="1.0" | ||
| 12 | + android:text="@string/btn_start" | ||
| 13 | + android:onClick="onClickPlay" /> | ||
| 14 | + | ||
| 15 | + <Button | ||
| 16 | + android:id="@+id/BtnPause" | ||
| 17 | + android:layout_width="wrap_content" | ||
| 18 | + android:layout_height="wrap_content" | ||
| 19 | + android:layout_weight="1.0" | ||
| 20 | + android:text="@string/btn_pause" | ||
| 21 | + android:onClick="onClickPause" /> | ||
| 22 | + | ||
| 23 | + <Button | ||
| 24 | + android:id="@+id/BtnResume" | ||
| 25 | + android:layout_width="wrap_content" | ||
| 26 | + android:layout_height="wrap_content" | ||
| 27 | + android:layout_weight="1.0" | ||
| 28 | + android:text="@string/btn_resume" | ||
| 29 | + android:onClick="onClickResume" /> | ||
| 30 | + | ||
| 31 | + <Button | ||
| 32 | + android:id="@+id/BtnStop" | ||
| 33 | + android:layout_width="wrap_content" | ||
| 34 | + android:layout_height="wrap_content" | ||
| 35 | + android:layout_weight="1.0" | ||
| 36 | + android:text="@string/btn_stop" | ||
| 37 | + android:onClick="onClickStop" /> | ||
| 38 | + | ||
| 39 | +</LinearLayout> |
app/src/main/res/mipmap-hdpi/ic_launcher.png
0 → 100644
3.34 KB
app/src/main/res/mipmap-mdpi/ic_launcher.png
0 → 100644
2.15 KB
app/src/main/res/mipmap-xhdpi/ic_launcher.png
0 → 100644
4.73 KB
app/src/main/res/mipmap-xxhdpi/ic_launcher.png
0 → 100644
7.54 KB
app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
0 → 100644
10.2 KB
app/src/main/res/values-w820dp/dimens.xml
0 → 100644
| 1 | +++ a/app/src/main/res/values-w820dp/dimens.xml | ||
| 1 | +<resources> | ||
| 2 | + <!-- Example customization of dimensions originally defined in res/values/dimens.xml | ||
| 3 | + (such as screen margins) for screens with more than 820dp of available width. This | ||
| 4 | + would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). --> | ||
| 5 | + <dimen name="activity_horizontal_margin">64dp</dimen> | ||
| 6 | +</resources> |
app/src/main/res/values/colors.xml
0 → 100644
app/src/main/res/values/dimens.xml
0 → 100644
app/src/main/res/values/strings.xml
0 → 100644
| 1 | +++ a/app/src/main/res/values/strings.xml | ||
| 1 | +<resources> | ||
| 2 | + <string name="app_name">QNVideoDemo</string> | ||
| 3 | + | ||
| 4 | + <string name="choose_ok">OK</string> | ||
| 5 | + <string name="input_title">Input Url:</string> | ||
| 6 | + <string name="back_title">BACK</string> | ||
| 7 | + <string name="change_title">Change</string> | ||
| 8 | + <string name="buffering">Buffering</string> | ||
| 9 | + <string name="title_activity_audio_player">AudioPlayerActivity</string> | ||
| 10 | + <string name="choose_video">Video</string> | ||
| 11 | + <string name="choose_audio">Audio</string> | ||
| 12 | + | ||
| 13 | + <string name="live_streaming">直播</string> | ||
| 14 | + <string name="playback">点播</string> | ||
| 15 | + | ||
| 16 | + <string name="play_setting">播放设置</string> | ||
| 17 | + <string name="decode_config">解码配置</string> | ||
| 18 | + <string name="sw_decode">软解</string> | ||
| 19 | + <string name="hw_decode">硬解</string> | ||
| 20 | + | ||
| 21 | + <string name="dlg_ok">OK</string> | ||
| 22 | + <string name="dlg_cancel">Cancel</string> | ||
| 23 | + | ||
| 24 | + <string name="pls_choose_activity">[请选择测试 Activity]:</string> | ||
| 25 | + <string name="pls_choose_videopath">[输入或选择视频地址]:</string> | ||
| 26 | + <string name="pls_choose_setting">[请选择播放配置]:</string> | ||
| 27 | + <string name="input_video_path">手动输入测试视频地址</string> | ||
| 28 | + <string name="btn_local_file">本地文件</string> | ||
| 29 | + <string name="btn_play_setting">播放配置</string> | ||
| 30 | + <string name="btn_click_play">点击播放</string> | ||
| 31 | + | ||
| 32 | + <string name="btn_start">开始</string> | ||
| 33 | + <string name="btn_pause">暂停</string> | ||
| 34 | + <string name="btn_resume">继续</string> | ||
| 35 | + <string name="btn_stop">停止</string> | ||
| 36 | +</resources> |
app/src/main/res/values/styles.xml
0 → 100644
| 1 | +++ a/app/src/main/res/values/styles.xml | ||
| 1 | +<resources> | ||
| 2 | + | ||
| 3 | + <!-- Base application theme. --> | ||
| 4 | + <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> | ||
| 5 | + <!-- Customize your theme here. --> | ||
| 6 | + <item name="colorPrimary">@color/colorPrimary</item> | ||
| 7 | + <item name="colorPrimaryDark">@color/colorPrimaryDark</item> | ||
| 8 | + <item name="colorAccent">@color/colorAccent</item> | ||
| 9 | + </style> | ||
| 10 | + | ||
| 11 | +</resources> |
app/src/test/java/com/cnlive/demo/video/ExampleUnitTest.java
0 → 100644
| 1 | +++ a/app/src/test/java/com/cnlive/demo/video/ExampleUnitTest.java | ||
| 1 | +package com.cnlive.demo.video; | ||
| 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 <a href="http://d.android.com/tools/testing">Testing documentation</a> | ||
| 11 | + */ | ||
| 12 | +public class ExampleUnitTest { | ||
| 13 | + @Test | ||
| 14 | + public void addition_isCorrect() throws Exception { | ||
| 15 | + assertEquals(4, 2 + 2); | ||
| 16 | + } | ||
| 17 | +} | ||
| 0 | \ No newline at end of file | 18 | \ No newline at end of file |
build.gradle
0 → 100644
| 1 | +++ a/build.gradle | ||
| 1 | +// Top-level build file where you can add configuration options common to all sub-projects/modules. | ||
| 2 | + | ||
| 3 | +buildscript { | ||
| 4 | + repositories { | ||
| 5 | + jcenter() | ||
| 6 | + } | ||
| 7 | + dependencies { | ||
| 8 | + classpath 'com.android.tools.build:gradle:2.2.2' | ||
| 9 | + | ||
| 10 | + // NOTE: Do not place your application dependencies here; they belong | ||
| 11 | + // in the individual module build.gradle files | ||
| 12 | + } | ||
| 13 | +} | ||
| 14 | + | ||
| 15 | +allprojects { | ||
| 16 | + repositories { | ||
| 17 | + jcenter() | ||
| 18 | + } | ||
| 19 | +} | ||
| 20 | + | ||
| 21 | +task clean(type: Delete) { | ||
| 22 | + delete rootProject.buildDir | ||
| 23 | +} |
gradle.properties
0 → 100644
| 1 | +++ a/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 |
gradle/wrapper/gradle-wrapper.jar
0 → 100644
No preview for this file type
gradle/wrapper/gradle-wrapper.properties
0 → 100644
gradlew
0 → 100644
| 1 | +++ a/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 "$@" |
gradlew.bat
0 → 100644
| 1 | +++ a/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 |
settings.gradle
0 → 100644