Browse Source

1,升级ijk播放器

许林 4 years ago
parent
commit
cc4fc60076
44 changed files with 4429 additions and 11 deletions
  1. 9 0
      app/src/main/java/com/haochuan/hciptvbasic/BaseWebActivity.java
  2. 2 0
      app/src/main/java/com/haochuan/hciptvbasic/webview/UtilToJS.java
  3. 3 3
      build.gradle
  4. 1 0
      ijkplayer-java/.gitignore
  5. 27 0
      ijkplayer-java/build.gradle
  6. 3 0
      ijkplayer-java/gradle.properties
  7. 20 0
      ijkplayer-java/proguard-rules.pro
  8. 13 0
      ijkplayer-java/src/androidTest/java/tv/danmaku/ijk/media/player/ApplicationTest.java
  9. 10 0
      ijkplayer-java/src/main/.classpath
  10. 33 0
      ijkplayer-java/src/main/.project
  11. 4 0
      ijkplayer-java/src/main/.settings/org.eclipse.jdt.core.prefs
  12. 5 0
      ijkplayer-java/src/main/AndroidManifest.xml
  13. 121 0
      ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/AbstractMediaPlayer.java
  14. 437 0
      ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/AndroidMediaPlayer.java
  15. 208 0
      ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/IMediaPlayer.java
  16. 28 0
      ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/ISurfaceTextureHolder.java
  17. 24 0
      ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/ISurfaceTextureHost.java
  18. 23 0
      ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/IjkLibLoader.java
  19. 293 0
      ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/IjkMediaCodecInfo.java
  20. 401 0
      ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/IjkMediaMeta.java
  21. 1263 0
      ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/IjkMediaPlayer.java
  22. 39 0
      ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/IjkTimedText.java
  23. 30 0
      ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/MediaInfo.java
  24. 339 0
      ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/MediaPlayerProxy.java
  25. 100 0
      ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/TextureMediaPlayer.java
  26. 32 0
      ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/annotations/AccessedByNative.java
  27. 36 0
      ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/annotations/CalledByNative.java
  28. 22 0
      ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/exceptions/IjkMediaException.java
  29. 5 0
      ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/ffmpeg/FFmpegApi.java
  30. 63 0
      ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/misc/AndroidMediaFormat.java
  31. 109 0
      ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/misc/AndroidTrackInfo.java
  32. 28 0
      ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/misc/IIjkIOHttp.java
  33. 29 0
      ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/misc/IMediaDataSource.java
  34. 31 0
      ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/misc/IMediaFormat.java
  35. 35 0
      ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/misc/ITrackInfo.java
  36. 259 0
      ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/misc/IjkMediaFormat.java
  37. 99 0
      ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/misc/IjkTrackInfo.java
  38. 143 0
      ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/pragma/DebugLog.java
  39. 24 0
      ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/pragma/Pragma.java
  40. 15 0
      ijkplayer-java/src/main/project.properties
  41. 6 0
      ijkplayer-java/src/main/res/values/strings.xml
  42. 11 6
      ijkvideo/src/main/java/com/haochuan/ijk/IjkVideoPlayer.java
  43. 1 0
      systemvideo/build.gradle
  44. 45 2
      systemvideo/src/main/java/com/haochuan/systemvideo/SystemVideoPlayer.java

+ 9 - 0
app/src/main/java/com/haochuan/hciptvbasic/BaseWebActivity.java

@@ -232,10 +232,12 @@ public abstract class BaseWebActivity extends AppCompatActivity {
             case 2:
                 openVr = true;
                 mHCPlayer = new SystemVideoPlayer(this);
+                backFocusToWebView();
                 break;
             case 3:
                 openVr = false;
                 mHCPlayer = new SystemVideoPlayer(this);
+                backFocusToWebView();
                 break;
             default:
                 mHCPlayer = new IjkVideoPlayer(this);
@@ -414,6 +416,13 @@ public abstract class BaseWebActivity extends AppCompatActivity {
         System.exit(0);
     }
 
+    /**
+     * 将焦点返还给webview
+     */
+    private void backFocusToWebView(){
+        webView.requestFocus();
+    }
+
     /*------------------------子类获取实例接口------------------------------*/
 
     /**

+ 2 - 0
app/src/main/java/com/haochuan/hciptvbasic/webview/UtilToJS.java

@@ -4,6 +4,7 @@ import android.app.Activity;
 import android.content.Context;
 import android.database.Cursor;
 import android.net.Uri;
+import android.os.Build;
 import android.text.TextUtils;
 import android.webkit.JavascriptInterface;
 import android.webkit.WebView;
@@ -101,6 +102,7 @@ public class UtilToJS {
             localParamsJson.put("version_name",BuildConfig.VERSION_NAME);
             localParamsJson.put("mac",MacUtil.getMac(context));
             localParamsJson.put("user_id",getUserName());
+            localParamsJson.put("model", Build.MODEL);
             String paramJsonStr = localParamsJson.toString();
             Logger.d("UtilToJS,getLocalParamsByJson(),paramJsonStr:" + paramJsonStr);
             return paramJsonStr;

+ 3 - 3
build.gradle

@@ -14,13 +14,13 @@ buildscript {
     }
 
     ext.versions = [
-            'minSdk'                : 19,
+            'minSdk'                : 17,
             'targetSdk'             : 28,
             'compileSdk'            : 28,
             'Java'                  : JavaVersion.VERSION_1_8,
             'supportLibrary'        : '28.0.0',
-            'versionCode'           : 4,
-            'versionName'           : "1.0.4"
+            'versionCode'           : 1,
+            'versionName'           : "1.0.1"
     ]
 
     // 引用的lib

+ 1 - 0
ijkplayer-java/.gitignore

@@ -0,0 +1 @@
+/build

+ 27 - 0
ijkplayer-java/build.gradle

@@ -0,0 +1,27 @@
+apply plugin: 'com.android.library'
+
+android {
+    // http://tools.android.com/tech-docs/new-build-system/tips
+    //noinspection GroovyAssignabilityCheck
+    compileSdkVersion versions.compileSdk
+
+    lintOptions {
+        abortOnError false
+    }
+    defaultConfig {
+        minSdkVersion versions.minSdk
+        targetSdkVersion versions.targetSdk
+        versionCode versions.versionCode
+        versionName versions.versionName
+    }
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+    }
+}
+
+dependencies {
+    implementation fileTree(dir: 'libs', include: ['*.jar'])
+}

+ 3 - 0
ijkplayer-java/gradle.properties

@@ -0,0 +1,3 @@
+POM_NAME=ijkplayer-java
+POM_ARTIFACT_ID=ijkplayer-java
+POM_PACKAGING=aar

+ 20 - 0
ijkplayer-java/proguard-rules.pro

@@ -0,0 +1,20 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /opt/android/ADK/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+-keep class tv.danmaku.ijk.media.player.** {*;}
+-keep class tv.danmaku.ijk.media.player.IjkMediaPlayer{*;}
+-keep class tv.danmaku.ijk.media.player.ffmpeg.FFmpegApi{*;}

+ 13 - 0
ijkplayer-java/src/androidTest/java/tv/danmaku/ijk/media/player/ApplicationTest.java

@@ -0,0 +1,13 @@
+package tv.danmaku.ijk.media.player;
+
+import android.app.Application;
+import android.test.ApplicationTestCase;
+
+/**
+ * <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
+ */
+public class ApplicationTest extends ApplicationTestCase<Application> {
+    public ApplicationTest() {
+        super(Application.class);
+    }
+}

+ 10 - 0
ijkplayer-java/src/main/.classpath

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="java"/>
+	<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
+	<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
+	<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="src" path="gen"/>
+	<classpathentry kind="output" path="bin/classes"/>
+</classpath>

+ 33 - 0
ijkplayer-java/src/main/.project

@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>ijkplayer-java</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>com.android.ide.eclipse.adt.ApkBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>

+ 4 - 0
ijkplayer-java/src/main/.settings/org.eclipse.jdt.core.prefs

@@ -0,0 +1,4 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.source=1.6

+ 5 - 0
ijkplayer-java/src/main/AndroidManifest.xml

@@ -0,0 +1,5 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="tv.danmaku.ijk.media.player" >
+
+
+</manifest>

+ 121 - 0
ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/AbstractMediaPlayer.java

@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2013-2014 Bilibili
+ * Copyright (C) 2013-2014 Zhang Rui <bbcallen@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package tv.danmaku.ijk.media.player;
+
+import tv.danmaku.ijk.media.player.misc.IMediaDataSource;
+
+@SuppressWarnings("WeakerAccess")
+public abstract class AbstractMediaPlayer implements IMediaPlayer {
+    private OnPreparedListener mOnPreparedListener;
+    private OnCompletionListener mOnCompletionListener;
+    private OnBufferingUpdateListener mOnBufferingUpdateListener;
+    private OnSeekCompleteListener mOnSeekCompleteListener;
+    private OnVideoSizeChangedListener mOnVideoSizeChangedListener;
+    private OnErrorListener mOnErrorListener;
+    private OnInfoListener mOnInfoListener;
+    private OnTimedTextListener mOnTimedTextListener;
+
+    public final void setOnPreparedListener(OnPreparedListener listener) {
+        mOnPreparedListener = listener;
+    }
+
+    public final void setOnCompletionListener(OnCompletionListener listener) {
+        mOnCompletionListener = listener;
+    }
+
+    public final void setOnBufferingUpdateListener(
+            OnBufferingUpdateListener listener) {
+        mOnBufferingUpdateListener = listener;
+    }
+
+    public final void setOnSeekCompleteListener(OnSeekCompleteListener listener) {
+        mOnSeekCompleteListener = listener;
+    }
+
+    public final void setOnVideoSizeChangedListener(
+            OnVideoSizeChangedListener listener) {
+        mOnVideoSizeChangedListener = listener;
+    }
+
+    public final void setOnErrorListener(OnErrorListener listener) {
+        mOnErrorListener = listener;
+    }
+
+    public final void setOnInfoListener(OnInfoListener listener) {
+        mOnInfoListener = listener;
+    }
+
+    public final void setOnTimedTextListener(OnTimedTextListener listener) {
+        mOnTimedTextListener = listener;
+    }
+
+    public void resetListeners() {
+        mOnPreparedListener = null;
+        mOnBufferingUpdateListener = null;
+        mOnCompletionListener = null;
+        mOnSeekCompleteListener = null;
+        mOnVideoSizeChangedListener = null;
+        mOnErrorListener = null;
+        mOnInfoListener = null;
+        mOnTimedTextListener = null;
+    }
+
+    protected final void notifyOnPrepared() {
+        if (mOnPreparedListener != null)
+            mOnPreparedListener.onPrepared(this);
+    }
+
+    protected final void notifyOnCompletion() {
+        if (mOnCompletionListener != null)
+            mOnCompletionListener.onCompletion(this);
+    }
+
+    protected final void notifyOnBufferingUpdate(int percent) {
+        if (mOnBufferingUpdateListener != null)
+            mOnBufferingUpdateListener.onBufferingUpdate(this, percent);
+    }
+
+    protected final void notifyOnSeekComplete() {
+        if (mOnSeekCompleteListener != null)
+            mOnSeekCompleteListener.onSeekComplete(this);
+    }
+
+    protected final void notifyOnVideoSizeChanged(int width, int height,
+                                                  int sarNum, int sarDen) {
+        if (mOnVideoSizeChangedListener != null)
+            mOnVideoSizeChangedListener.onVideoSizeChanged(this, width, height,
+                    sarNum, sarDen);
+    }
+
+    protected final boolean notifyOnError(int what, int extra) {
+        return mOnErrorListener != null && mOnErrorListener.onError(this, what, extra);
+    }
+
+    protected final boolean notifyOnInfo(int what, int extra) {
+        return mOnInfoListener != null && mOnInfoListener.onInfo(this, what, extra);
+    }
+
+    protected final void notifyOnTimedText(IjkTimedText text) {
+        if (mOnTimedTextListener != null)
+            mOnTimedTextListener.onTimedText(this, text);
+    }
+
+    public void setDataSource(IMediaDataSource mediaDataSource) {
+        throw new UnsupportedOperationException();
+    }
+}

+ 437 - 0
ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/AndroidMediaPlayer.java

@@ -0,0 +1,437 @@
+/*
+ * Copyright (C) 2006 Bilibili
+ * Copyright (C) 2006 The Android Open Source Project
+ * Copyright (C) 2013 Zhang Rui <bbcallen@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package tv.danmaku.ijk.media.player;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.media.AudioManager;
+import android.media.MediaDataSource;
+import android.media.MediaPlayer;
+import android.media.TimedText;
+import android.net.Uri;
+import android.os.Build;
+import android.text.TextUtils;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.lang.ref.WeakReference;
+import java.util.Map;
+
+import tv.danmaku.ijk.media.player.misc.AndroidTrackInfo;
+import tv.danmaku.ijk.media.player.misc.IMediaDataSource;
+import tv.danmaku.ijk.media.player.misc.ITrackInfo;
+import tv.danmaku.ijk.media.player.pragma.DebugLog;
+
+public class AndroidMediaPlayer extends AbstractMediaPlayer {
+    private final MediaPlayer mInternalMediaPlayer;
+    private final AndroidMediaPlayerListenerHolder mInternalListenerAdapter;
+    private String mDataSource;
+    private MediaDataSource mMediaDataSource;
+
+    private final Object mInitLock = new Object();
+    private boolean mIsReleased;
+
+    private static MediaInfo sMediaInfo;
+
+    public AndroidMediaPlayer() {
+        synchronized (mInitLock) {
+            mInternalMediaPlayer = new MediaPlayer();
+        }
+        mInternalMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
+        mInternalListenerAdapter = new AndroidMediaPlayerListenerHolder(this);
+        attachInternalListeners();
+    }
+
+    public MediaPlayer getInternalMediaPlayer() {
+        return mInternalMediaPlayer;
+    }
+
+    @Override
+    public void setDisplay(SurfaceHolder sh) {
+        synchronized (mInitLock) {
+            if (!mIsReleased) {
+                mInternalMediaPlayer.setDisplay(sh);
+            }
+        }
+    }
+
+    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+    @Override
+    public void setSurface(Surface surface) {
+        mInternalMediaPlayer.setSurface(surface);
+    }
+
+    @Override
+    public void setDataSource(Context context, Uri uri)
+            throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
+        mInternalMediaPlayer.setDataSource(context, uri);
+    }
+
+    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+    @Override
+    public void setDataSource(Context context, Uri uri, Map<String, String> headers)
+            throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
+        mInternalMediaPlayer.setDataSource(context, uri, headers);
+    }
+
+    @Override
+    public void setDataSource(FileDescriptor fd)
+            throws IOException, IllegalArgumentException, IllegalStateException {
+        mInternalMediaPlayer.setDataSource(fd);
+    }
+
+    @Override
+    public void setDataSource(String path) throws IOException,
+            IllegalArgumentException, SecurityException, IllegalStateException {
+        mDataSource = path;
+
+        Uri uri = Uri.parse(path);
+        String scheme = uri.getScheme();
+        if (!TextUtils.isEmpty(scheme) && scheme.equalsIgnoreCase("file")) {
+            mInternalMediaPlayer.setDataSource(uri.getPath());
+        } else {
+            mInternalMediaPlayer.setDataSource(path);
+        }
+    }
+
+    @TargetApi(Build.VERSION_CODES.M)
+    @Override
+    public void setDataSource(IMediaDataSource mediaDataSource) {
+        releaseMediaDataSource();
+
+        mMediaDataSource = new MediaDataSourceProxy(mediaDataSource);
+        mInternalMediaPlayer.setDataSource(mMediaDataSource);
+    }
+
+    @TargetApi(Build.VERSION_CODES.M)
+    private static class MediaDataSourceProxy extends MediaDataSource {
+        private final IMediaDataSource mMediaDataSource;
+
+        public MediaDataSourceProxy(IMediaDataSource mediaDataSource) {
+            mMediaDataSource = mediaDataSource;
+        }
+
+        @Override
+        public int readAt(long position, byte[] buffer, int offset, int size) throws IOException {
+            return mMediaDataSource.readAt(position, buffer, offset, size);
+        }
+
+        @Override
+        public long getSize() throws IOException {
+            return mMediaDataSource.getSize();
+        }
+
+        @Override
+        public void close() throws IOException {
+            mMediaDataSource.close();
+        }
+    }
+
+    @Override
+    public String getDataSource() {
+        return mDataSource;
+    }
+
+    private void releaseMediaDataSource() {
+        if (mMediaDataSource != null) {
+            try {
+                mMediaDataSource.close();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+            mMediaDataSource = null;
+        }
+    }
+
+    @Override
+    public void prepareAsync() throws IllegalStateException {
+        mInternalMediaPlayer.prepareAsync();
+    }
+
+    @Override
+    public void start() throws IllegalStateException {
+        mInternalMediaPlayer.start();
+    }
+
+    @Override
+    public void stop() throws IllegalStateException {
+        mInternalMediaPlayer.stop();
+    }
+
+    @Override
+    public void pause() throws IllegalStateException {
+        mInternalMediaPlayer.pause();
+    }
+
+    @Override
+    public void setScreenOnWhilePlaying(boolean screenOn) {
+        mInternalMediaPlayer.setScreenOnWhilePlaying(screenOn);
+    }
+
+    @Override
+    public ITrackInfo[] getTrackInfo() {
+        return AndroidTrackInfo.fromMediaPlayer(mInternalMediaPlayer);
+    }
+
+    @Override
+    public int getVideoWidth() {
+        return mInternalMediaPlayer.getVideoWidth();
+    }
+
+    @Override
+    public int getVideoHeight() {
+        return mInternalMediaPlayer.getVideoHeight();
+    }
+
+    @Override
+    public int getVideoSarNum() {
+        return 1;
+    }
+
+    @Override
+    public int getVideoSarDen() {
+        return 1;
+    }
+
+    @Override
+    public boolean isPlaying() {
+        try {
+            return mInternalMediaPlayer.isPlaying();
+        } catch (IllegalStateException e) {
+            DebugLog.printStackTrace(e);
+            return false;
+        }
+    }
+
+    @Override
+    public void seekTo(long msec) throws IllegalStateException {
+        mInternalMediaPlayer.seekTo((int) msec);
+    }
+
+    @Override
+    public long getCurrentPosition() {
+        try {
+            return mInternalMediaPlayer.getCurrentPosition();
+        } catch (IllegalStateException e) {
+            DebugLog.printStackTrace(e);
+            return 0;
+        }
+    }
+
+    @Override
+    public long getDuration() {
+        try {
+            return mInternalMediaPlayer.getDuration();
+        } catch (IllegalStateException e) {
+            DebugLog.printStackTrace(e);
+            return 0;
+        }
+    }
+
+    @Override
+    public void release() {
+        mIsReleased = true;
+        mInternalMediaPlayer.release();
+        releaseMediaDataSource();
+        resetListeners();
+        attachInternalListeners();
+    }
+
+    @Override
+    public void reset() {
+        try {
+            mInternalMediaPlayer.reset();
+        } catch (IllegalStateException e) {
+            DebugLog.printStackTrace(e);
+        }
+        releaseMediaDataSource();
+        resetListeners();
+        attachInternalListeners();
+    }
+
+    @Override
+    public void setLooping(boolean looping) {
+        mInternalMediaPlayer.setLooping(looping);
+    }
+
+    @Override
+    public boolean isLooping() {
+        return mInternalMediaPlayer.isLooping();
+    }
+
+    @Override
+    public void setVolume(float leftVolume, float rightVolume) {
+        mInternalMediaPlayer.setVolume(leftVolume, rightVolume);
+    }
+
+    @Override
+    public int getAudioSessionId() {
+        return mInternalMediaPlayer.getAudioSessionId();
+    }
+
+    @Override
+    public MediaInfo getMediaInfo() {
+        if (sMediaInfo == null) {
+            MediaInfo module = new MediaInfo();
+
+            module.mVideoDecoder = "android";
+            module.mVideoDecoderImpl = "HW";
+
+            module.mAudioDecoder = "android";
+            module.mAudioDecoderImpl = "HW";
+
+            sMediaInfo = module;
+        }
+
+        return sMediaInfo;
+    }
+
+    @Override
+    public void setLogEnabled(boolean enable) {
+    }
+
+    @Override
+    public boolean isPlayable() {
+        return true;
+    }
+
+    /*--------------------
+     * misc
+     */
+    @Override
+    public void setWakeMode(Context context, int mode) {
+        mInternalMediaPlayer.setWakeMode(context, mode);
+    }
+
+    @Override
+    public void setAudioStreamType(int streamtype) {
+        mInternalMediaPlayer.setAudioStreamType(streamtype);
+    }
+
+    @Override
+    public void setKeepInBackground(boolean keepInBackground) {
+    }
+
+    /*--------------------
+     * Listeners adapter
+     */
+    private void attachInternalListeners() {
+        mInternalMediaPlayer.setOnPreparedListener(mInternalListenerAdapter);
+        mInternalMediaPlayer
+                .setOnBufferingUpdateListener(mInternalListenerAdapter);
+        mInternalMediaPlayer.setOnCompletionListener(mInternalListenerAdapter);
+        mInternalMediaPlayer
+                .setOnSeekCompleteListener(mInternalListenerAdapter);
+        mInternalMediaPlayer
+                .setOnVideoSizeChangedListener(mInternalListenerAdapter);
+        mInternalMediaPlayer.setOnErrorListener(mInternalListenerAdapter);
+        mInternalMediaPlayer.setOnInfoListener(mInternalListenerAdapter);
+        mInternalMediaPlayer.setOnTimedTextListener(mInternalListenerAdapter);
+    }
+
+    private class AndroidMediaPlayerListenerHolder implements
+            MediaPlayer.OnPreparedListener, MediaPlayer.OnCompletionListener,
+            MediaPlayer.OnBufferingUpdateListener,
+            MediaPlayer.OnSeekCompleteListener,
+            MediaPlayer.OnVideoSizeChangedListener,
+            MediaPlayer.OnErrorListener, MediaPlayer.OnInfoListener,
+            MediaPlayer.OnTimedTextListener {
+        public final WeakReference<AndroidMediaPlayer> mWeakMediaPlayer;
+
+        public AndroidMediaPlayerListenerHolder(AndroidMediaPlayer mp) {
+            mWeakMediaPlayer = new WeakReference<AndroidMediaPlayer>(mp);
+        }
+
+        @Override
+        public boolean onInfo(MediaPlayer mp, int what, int extra) {
+            AndroidMediaPlayer self = mWeakMediaPlayer.get();
+            return self != null && notifyOnInfo(what, extra);
+
+        }
+
+        @Override
+        public boolean onError(MediaPlayer mp, int what, int extra) {
+            AndroidMediaPlayer self = mWeakMediaPlayer.get();
+            return self != null && notifyOnError(what, extra);
+
+        }
+
+        @Override
+        public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
+            AndroidMediaPlayer self = mWeakMediaPlayer.get();
+            if (self == null)
+                return;
+
+            notifyOnVideoSizeChanged(width, height, 1, 1);
+        }
+
+        @Override
+        public void onSeekComplete(MediaPlayer mp) {
+            AndroidMediaPlayer self = mWeakMediaPlayer.get();
+            if (self == null)
+                return;
+
+            notifyOnSeekComplete();
+        }
+
+        @Override
+        public void onBufferingUpdate(MediaPlayer mp, int percent) {
+            AndroidMediaPlayer self = mWeakMediaPlayer.get();
+            if (self == null)
+                return;
+
+            notifyOnBufferingUpdate(percent);
+        }
+
+        @Override
+        public void onCompletion(MediaPlayer mp) {
+            AndroidMediaPlayer self = mWeakMediaPlayer.get();
+            if (self == null)
+                return;
+
+            notifyOnCompletion();
+        }
+
+        @Override
+        public void onPrepared(MediaPlayer mp) {
+            AndroidMediaPlayer self = mWeakMediaPlayer.get();
+            if (self == null)
+                return;
+
+            notifyOnPrepared();
+        }
+
+        @Override
+        public void onTimedText(MediaPlayer mp, TimedText text) {
+            AndroidMediaPlayer self = mWeakMediaPlayer.get();
+            if (self == null)
+                return;
+
+            IjkTimedText ijkText = null;
+
+            if (text != null) {
+                ijkText = new IjkTimedText(text.getBounds(), text.getText());
+            }
+
+            notifyOnTimedText(ijkText);
+        }
+    }
+}

+ 208 - 0
ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/IMediaPlayer.java

@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2013-2014 Bilibili
+ * Copyright (C) 2013-2014 Zhang Rui <bbcallen@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package tv.danmaku.ijk.media.player;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.net.Uri;
+import android.os.Build;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.util.Map;
+
+import tv.danmaku.ijk.media.player.misc.IMediaDataSource;
+import tv.danmaku.ijk.media.player.misc.ITrackInfo;
+
+public interface IMediaPlayer {
+    /*
+     * Do not change these values without updating their counterparts in native
+     */
+    int MEDIA_INFO_UNKNOWN = 1;
+    int MEDIA_INFO_STARTED_AS_NEXT = 2;
+    int MEDIA_INFO_VIDEO_RENDERING_START = 3;
+    int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700;
+    int MEDIA_INFO_BUFFERING_START = 701;
+    int MEDIA_INFO_BUFFERING_END = 702;
+    int MEDIA_INFO_NETWORK_BANDWIDTH = 703;
+    int MEDIA_INFO_BAD_INTERLEAVING = 800;
+    int MEDIA_INFO_NOT_SEEKABLE = 801;
+    int MEDIA_INFO_METADATA_UPDATE = 802;
+    int MEDIA_INFO_TIMED_TEXT_ERROR = 900;
+    int MEDIA_INFO_UNSUPPORTED_SUBTITLE = 901;
+    int MEDIA_INFO_SUBTITLE_TIMED_OUT = 902;
+
+    int MEDIA_INFO_VIDEO_ROTATION_CHANGED = 10001;
+    int MEDIA_INFO_AUDIO_RENDERING_START = 10002;
+    int MEDIA_INFO_MEDIA_ACCURATE_SEEK_COMPLETE = 10100;
+
+    int MEDIA_ERROR_UNKNOWN = 1;
+    int MEDIA_ERROR_SERVER_DIED = 100;
+    int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200;
+    int MEDIA_ERROR_IO = -1004;
+    int MEDIA_ERROR_MALFORMED = -1007;
+    int MEDIA_ERROR_UNSUPPORTED = -1010;
+    int MEDIA_ERROR_TIMED_OUT = -110;
+
+    void setDisplay(SurfaceHolder sh);
+
+    void setDataSource(Context context, Uri uri)
+            throws IOException, IllegalArgumentException, SecurityException, IllegalStateException;
+
+    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+    void setDataSource(Context context, Uri uri, Map<String, String> headers)
+            throws IOException, IllegalArgumentException, SecurityException, IllegalStateException;
+
+    void setDataSource(FileDescriptor fd)
+            throws IOException, IllegalArgumentException, IllegalStateException;
+
+    void setDataSource(String path)
+            throws IOException, IllegalArgumentException, SecurityException, IllegalStateException;
+
+    String getDataSource();
+
+    void prepareAsync() throws IllegalStateException;
+
+    void start() throws IllegalStateException;
+
+    void stop() throws IllegalStateException;
+
+    void pause() throws IllegalStateException;
+
+    void setScreenOnWhilePlaying(boolean screenOn);
+
+    int getVideoWidth();
+
+    int getVideoHeight();
+
+    boolean isPlaying();
+
+    void seekTo(long msec) throws IllegalStateException;
+
+    long getCurrentPosition();
+
+    long getDuration();
+
+    void release();
+
+    void reset();
+
+    void setVolume(float leftVolume, float rightVolume);
+
+    int getAudioSessionId();
+
+    MediaInfo getMediaInfo();
+
+    @SuppressWarnings("EmptyMethod")
+    @Deprecated
+    void setLogEnabled(boolean enable);
+
+    @Deprecated
+    boolean isPlayable();
+
+    void setOnPreparedListener(OnPreparedListener listener);
+
+    void setOnCompletionListener(OnCompletionListener listener);
+
+    void setOnBufferingUpdateListener(
+            OnBufferingUpdateListener listener);
+
+    void setOnSeekCompleteListener(
+            OnSeekCompleteListener listener);
+
+    void setOnVideoSizeChangedListener(
+            OnVideoSizeChangedListener listener);
+
+    void setOnErrorListener(OnErrorListener listener);
+
+    void setOnInfoListener(OnInfoListener listener);
+
+    void setOnTimedTextListener(OnTimedTextListener listener);
+
+    /*--------------------
+     * Listeners
+     */
+    interface OnPreparedListener {
+        void onPrepared(IMediaPlayer mp);
+    }
+
+    interface OnCompletionListener {
+        void onCompletion(IMediaPlayer mp);
+    }
+
+    interface OnBufferingUpdateListener {
+        void onBufferingUpdate(IMediaPlayer mp, int percent);
+    }
+
+    interface OnSeekCompleteListener {
+        void onSeekComplete(IMediaPlayer mp);
+    }
+
+    interface OnVideoSizeChangedListener {
+        void onVideoSizeChanged(IMediaPlayer mp, int width, int height,
+                                int sar_num, int sar_den);
+    }
+
+    interface OnErrorListener {
+        boolean onError(IMediaPlayer mp, int what, int extra);
+    }
+
+    interface OnInfoListener {
+        boolean onInfo(IMediaPlayer mp, int what, int extra);
+    }
+
+    interface OnTimedTextListener {
+        void onTimedText(IMediaPlayer mp, IjkTimedText text);
+    }
+
+    /*--------------------
+     * Optional
+     */
+    void setAudioStreamType(int streamtype);
+
+    @Deprecated
+    void setKeepInBackground(boolean keepInBackground);
+
+    int getVideoSarNum();
+
+    int getVideoSarDen();
+
+    @Deprecated
+    void setWakeMode(Context context, int mode);
+
+    void setLooping(boolean looping);
+
+    boolean isLooping();
+
+    /*--------------------
+     * AndroidMediaPlayer: JELLY_BEAN
+     */
+    ITrackInfo[] getTrackInfo();
+
+    /*--------------------
+     * AndroidMediaPlayer: ICE_CREAM_SANDWICH:
+     */
+    void setSurface(Surface surface);
+
+    /*--------------------
+     * AndroidMediaPlayer: M:
+     */
+    void setDataSource(IMediaDataSource mediaDataSource);
+}

+ 28 - 0
ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/ISurfaceTextureHolder.java

@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2015 Bilibili
+ * Copyright (C) 2015 Zhang Rui <bbcallen@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package tv.danmaku.ijk.media.player;
+
+import android.graphics.SurfaceTexture;
+
+public interface ISurfaceTextureHolder {
+    void setSurfaceTexture(SurfaceTexture surfaceTexture);
+
+    SurfaceTexture getSurfaceTexture();
+
+    void setSurfaceTextureHost(ISurfaceTextureHost surfaceTextureHost);
+}

+ 24 - 0
ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/ISurfaceTextureHost.java

@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2015 Bilibili
+ * Copyright (C) 2015 Zhang Rui <bbcallen@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package tv.danmaku.ijk.media.player;
+
+import android.graphics.SurfaceTexture;
+
+public interface ISurfaceTextureHost {
+    void releaseSurfaceTexture(SurfaceTexture surfaceTexture);
+}

+ 23 - 0
ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/IjkLibLoader.java

@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2013-2014 Bilibili
+ * Copyright (C) 2013-2014 Zhang Rui <bbcallen@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package tv.danmaku.ijk.media.player;
+
+public interface IjkLibLoader {
+    void loadLibrary(String libName) throws UnsatisfiedLinkError,
+            SecurityException;
+}

+ 293 - 0
ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/IjkMediaCodecInfo.java

@@ -0,0 +1,293 @@
+package tv.danmaku.ijk.media.player;
+
+import android.annotation.TargetApi;
+import android.media.MediaCodecInfo;
+import android.media.MediaCodecInfo.CodecCapabilities;
+import android.media.MediaCodecInfo.CodecProfileLevel;
+import android.os.Build;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.util.Locale;
+import java.util.Map;
+import java.util.TreeMap;
+
+public class IjkMediaCodecInfo {
+    private final static String TAG = "IjkMediaCodecInfo";
+
+    public static final int RANK_MAX = 1000;
+    public static final int RANK_TESTED = 800;
+    public static final int RANK_ACCEPTABLE = 700;
+    public static final int RANK_LAST_CHANCE = 600;
+    public static final int RANK_SECURE = 300;
+    public static final int RANK_SOFTWARE = 200;
+    public static final int RANK_NON_STANDARD = 100;
+    public static final int RANK_NO_SENSE = 0;
+
+    public MediaCodecInfo mCodecInfo;
+    public int mRank = 0;
+    public String mMimeType;
+
+    private static Map<String, Integer> sKnownCodecList;
+
+    private static synchronized Map<String, Integer> getKnownCodecList() {
+        if (sKnownCodecList != null)
+            return sKnownCodecList;
+
+        sKnownCodecList = new TreeMap<String, Integer>(
+                String.CASE_INSENSITIVE_ORDER);
+
+        // ----- Nvidia -----
+        // Tegra3
+        // Nexus 7 (2012)
+        // Tegra K1
+        // Nexus 9
+        sKnownCodecList.put("OMX.Nvidia.h264.decode", RANK_TESTED);
+        sKnownCodecList.put("OMX.Nvidia.h264.decode.secure", RANK_SECURE);
+
+        // ----- Intel -----
+        // Atom Z3735
+        // Teclast X98 Air
+        sKnownCodecList.put("OMX.Intel.hw_vd.h264", RANK_TESTED + 1);
+        // Atom Z2560
+        // Dell Venue 7 3730
+        sKnownCodecList.put("OMX.Intel.VideoDecoder.AVC", RANK_TESTED);
+
+        // ----- Qualcomm -----
+        // MSM8260
+        // Xiaomi MI 1S
+        sKnownCodecList.put("OMX.qcom.video.decoder.avc", RANK_TESTED);
+        sKnownCodecList.put("OMX.ittiam.video.decoder.avc", RANK_NO_SENSE);
+
+        // ----- Samsung -----
+        // Exynos 3110
+        // Nexus S
+        sKnownCodecList.put("OMX.SEC.avc.dec", RANK_TESTED);
+        sKnownCodecList.put("OMX.SEC.AVC.Decoder", RANK_TESTED - 1);
+        // OMX.SEC.avcdec doesn't reorder output pictures on GT-9100
+        sKnownCodecList.put("OMX.SEC.avcdec", RANK_TESTED - 2);
+        sKnownCodecList.put("OMX.SEC.avc.sw.dec", RANK_SOFTWARE);
+        // Exynos 5 ?
+        sKnownCodecList.put("OMX.Exynos.avc.dec", RANK_TESTED);
+        sKnownCodecList.put("OMX.Exynos.AVC.Decoder", RANK_TESTED - 1);
+
+        // ------ Huawei hisilicon ------
+        // Kirin 910, Mali 450 MP
+        // Huawei HONOR 3C (H30-L01)
+        sKnownCodecList.put("OMX.k3.video.decoder.avc", RANK_TESTED);
+        // Kirin 920, Mali T624
+        // Huawei HONOR 6
+        sKnownCodecList.put("OMX.IMG.MSVDX.Decoder.AVC", RANK_TESTED);
+
+        // ----- TI -----
+        // TI OMAP4460
+        // Galaxy Nexus
+        sKnownCodecList.put("OMX.TI.DUCATI1.VIDEO.DECODER", RANK_TESTED);
+
+        // ------ RockChip ------
+        // Youku TVBox
+        sKnownCodecList.put("OMX.rk.video_decoder.avc", RANK_TESTED);
+
+        // ------ AMLogic -----
+        // MiBox1, 1s, 2
+        sKnownCodecList.put("OMX.amlogic.avc.decoder.awesome", RANK_TESTED);
+
+        // ------ Marvell ------
+        // Lenovo A788t
+        sKnownCodecList.put("OMX.MARVELL.VIDEO.HW.CODA7542DECODER", RANK_TESTED);
+        sKnownCodecList.put("OMX.MARVELL.VIDEO.H264DECODER", RANK_SOFTWARE);
+
+        // ----- TODO: need test -----
+        sKnownCodecList.remove("OMX.Action.Video.Decoder");
+        sKnownCodecList.remove("OMX.allwinner.video.decoder.avc");
+        sKnownCodecList.remove("OMX.BRCM.vc4.decoder.avc");
+        sKnownCodecList.remove("OMX.brcm.video.h264.hw.decoder");
+        sKnownCodecList.remove("OMX.brcm.video.h264.decoder");
+        sKnownCodecList.remove("OMX.cosmo.video.decoder.avc");
+        sKnownCodecList.remove("OMX.duos.h264.decoder");
+        sKnownCodecList.remove("OMX.hantro.81x0.video.decoder");
+        sKnownCodecList.remove("OMX.hantro.G1.video.decoder");
+        sKnownCodecList.remove("OMX.hisi.video.decoder");
+        sKnownCodecList.remove("OMX.LG.decoder.video.avc");
+        sKnownCodecList.remove("OMX.MS.AVC.Decoder");
+        sKnownCodecList.remove("OMX.RENESAS.VIDEO.DECODER.H264");
+        sKnownCodecList.remove("OMX.RTK.video.decoder");
+        sKnownCodecList.remove("OMX.sprd.h264.decoder");
+        sKnownCodecList.remove("OMX.ST.VFM.H264Dec");
+        sKnownCodecList.remove("OMX.vpu.video_decoder.avc");
+        sKnownCodecList.remove("OMX.WMT.decoder.avc");
+
+        // Really ?
+        sKnownCodecList.remove("OMX.bluestacks.hw.decoder");
+
+        // ---------------
+        // Useless codec
+        // ----- google -----
+        sKnownCodecList.put("OMX.google.h264.decoder", RANK_SOFTWARE);
+        sKnownCodecList.put("OMX.google.h264.lc.decoder", RANK_SOFTWARE);
+        // ----- huawei k920 -----
+        sKnownCodecList.put("OMX.k3.ffmpeg.decoder", RANK_SOFTWARE);
+        sKnownCodecList.put("OMX.ffmpeg.video.decoder", RANK_SOFTWARE);
+        // ----- unknown -----
+        sKnownCodecList.put("OMX.sprd.soft.h264.decoder", RANK_SOFTWARE);
+
+        return sKnownCodecList;
+    }
+
+    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+    public static IjkMediaCodecInfo setupCandidate(MediaCodecInfo codecInfo,
+            String mimeType) {
+        if (codecInfo == null
+                || Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN)
+            return null;
+
+        String name = codecInfo.getName();
+        if (TextUtils.isEmpty(name))
+            return null;
+
+        name = name.toLowerCase(Locale.US);
+        int rank = RANK_NO_SENSE;
+        if (!name.startsWith("omx.")) {
+            rank = RANK_NON_STANDARD;
+        } else if (name.startsWith("omx.pv")) {
+            rank = RANK_SOFTWARE;
+        } else if (name.startsWith("omx.google.")) {
+            rank = RANK_SOFTWARE;
+        } else if (name.startsWith("omx.ffmpeg.")) {
+            rank = RANK_SOFTWARE;
+        } else if (name.startsWith("omx.k3.ffmpeg.")) {
+            rank = RANK_SOFTWARE;
+        } else if (name.startsWith("omx.avcodec.")) {
+            rank = RANK_SOFTWARE;
+        } else if (name.startsWith("omx.ittiam.")) {
+            // unknown codec in qualcomm SoC
+            rank = RANK_NO_SENSE;
+        } else if (name.startsWith("omx.mtk.")) {
+            // 1. MTK only works on 4.3 and above
+            // 2. MTK works on MIUI 6 (4.2.1)
+            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2)
+                rank = RANK_NO_SENSE;
+            else
+                rank = RANK_TESTED;
+        } else {
+            Integer knownRank = getKnownCodecList().get(name);
+            if (knownRank != null) {
+                rank = knownRank;
+            } else {
+                try {
+                    CodecCapabilities cap = codecInfo
+                            .getCapabilitiesForType(mimeType);
+                    if (cap != null)
+                        rank = RANK_ACCEPTABLE;
+                    else
+                        rank = RANK_LAST_CHANCE;
+                } catch (Throwable e) {
+                    rank = RANK_LAST_CHANCE;
+                }
+            }
+        }
+
+        IjkMediaCodecInfo candidate = new IjkMediaCodecInfo();
+        candidate.mCodecInfo = codecInfo;
+        candidate.mRank = rank;
+        candidate.mMimeType = mimeType;
+        return candidate;
+    }
+
+    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+    public void dumpProfileLevels(String mimeType) {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN)
+            return;
+
+        try {
+            CodecCapabilities caps = mCodecInfo
+                    .getCapabilitiesForType(mimeType);
+            int maxProfile = 0;
+            int maxLevel = 0;
+            if (caps != null) {
+                if (caps.profileLevels != null) {
+                    for (CodecProfileLevel profileLevel : caps.profileLevels) {
+                        if (profileLevel == null)
+                            continue;
+
+                        maxProfile = Math.max(maxProfile, profileLevel.profile);
+                        maxLevel = Math.max(maxLevel, profileLevel.level);
+                    }
+                }
+            }
+
+            Log.i(TAG,
+                    String.format(Locale.US, "%s",
+                            getProfileLevelName(maxProfile, maxLevel)));
+        } catch (Throwable e) {
+            Log.i(TAG, "profile-level: exception");
+        }
+    }
+
+    public static String getProfileLevelName(int profile, int level) {
+        return String.format(Locale.US, " %s Profile Level %s (%d,%d)",
+                getProfileName(profile), getLevelName(level), profile, level);
+    }
+
+    public static String getProfileName(int profile) {
+        switch (profile) {
+        case CodecProfileLevel.AVCProfileBaseline:
+            return "Baseline";
+        case CodecProfileLevel.AVCProfileMain:
+            return "Main";
+        case CodecProfileLevel.AVCProfileExtended:
+            return "Extends";
+        case CodecProfileLevel.AVCProfileHigh:
+            return "High";
+        case CodecProfileLevel.AVCProfileHigh10:
+            return "High10";
+        case CodecProfileLevel.AVCProfileHigh422:
+            return "High422";
+        case CodecProfileLevel.AVCProfileHigh444:
+            return "High444";
+        default:
+            return "Unknown";
+        }
+    }
+
+    public static String getLevelName(int level) {
+        switch (level) {
+        case CodecProfileLevel.AVCLevel1:
+            return "1";
+        case CodecProfileLevel.AVCLevel1b:
+            return "1b";
+        case CodecProfileLevel.AVCLevel11:
+            return "11";
+        case CodecProfileLevel.AVCLevel12:
+            return "12";
+        case CodecProfileLevel.AVCLevel13:
+            return "13";
+        case CodecProfileLevel.AVCLevel2:
+            return "2";
+        case CodecProfileLevel.AVCLevel21:
+            return "21";
+        case CodecProfileLevel.AVCLevel22:
+            return "22";
+        case CodecProfileLevel.AVCLevel3:
+            return "3";
+        case CodecProfileLevel.AVCLevel31:
+            return "31";
+        case CodecProfileLevel.AVCLevel32:
+            return "32";
+        case CodecProfileLevel.AVCLevel4:
+            return "4";
+        case CodecProfileLevel.AVCLevel41:
+            return "41";
+        case CodecProfileLevel.AVCLevel42:
+            return "42";
+        case CodecProfileLevel.AVCLevel5:
+            return "5";
+        case CodecProfileLevel.AVCLevel51:
+            return "51";
+        case 65536: // CodecProfileLevel.AVCLevel52:
+            return "52";
+        default:
+            return "0";
+        }
+    }
+}

+ 401 - 0
ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/IjkMediaMeta.java

@@ -0,0 +1,401 @@
+package tv.danmaku.ijk.media.player;
+
+import android.os.Bundle;
+import android.text.TextUtils;
+
+import java.util.ArrayList;
+import java.util.Locale;
+
+@SuppressWarnings("SameParameterValue")
+public class IjkMediaMeta {
+    // media meta
+    public static final String IJKM_KEY_FORMAT = "format";
+    public static final String IJKM_KEY_DURATION_US = "duration_us";
+    public static final String IJKM_KEY_START_US = "start_us";
+    public static final String IJKM_KEY_BITRATE = "bitrate";
+    public static final String IJKM_KEY_VIDEO_STREAM = "video";
+    public static final String IJKM_KEY_AUDIO_STREAM = "audio";
+    public static final String IJKM_KEY_TIMEDTEXT_STREAM = "timedtext";
+
+    // stream meta
+    public static final String IJKM_KEY_TYPE = "type";
+    public static final String IJKM_VAL_TYPE__VIDEO = "video";
+    public static final String IJKM_VAL_TYPE__AUDIO = "audio";
+    public static final String IJKM_VAL_TYPE__TIMEDTEXT = "timedtext";
+    public static final String IJKM_VAL_TYPE__UNKNOWN = "unknown";
+    public static final String IJKM_KEY_LANGUAGE = "language";
+
+    public static final String IJKM_KEY_CODEC_NAME = "codec_name";
+    public static final String IJKM_KEY_CODEC_PROFILE = "codec_profile";
+    public static final String IJKM_KEY_CODEC_LEVEL = "codec_level";
+    public static final String IJKM_KEY_CODEC_LONG_NAME = "codec_long_name";
+    public static final String IJKM_KEY_CODEC_PIXEL_FORMAT = "codec_pixel_format";
+    public static final String IJKM_KEY_CODEC_PROFILE_ID = "codec_profile_id";
+
+    // stream: video
+    public static final String IJKM_KEY_WIDTH = "width";
+    public static final String IJKM_KEY_HEIGHT = "height";
+    public static final String IJKM_KEY_FPS_NUM = "fps_num";
+    public static final String IJKM_KEY_FPS_DEN = "fps_den";
+    public static final String IJKM_KEY_TBR_NUM = "tbr_num";
+    public static final String IJKM_KEY_TBR_DEN = "tbr_den";
+    public static final String IJKM_KEY_SAR_NUM = "sar_num";
+    public static final String IJKM_KEY_SAR_DEN = "sar_den";
+    // stream: audio
+    public static final String IJKM_KEY_SAMPLE_RATE = "sample_rate";
+    public static final String IJKM_KEY_CHANNEL_LAYOUT = "channel_layout";
+
+    public static final String IJKM_KEY_STREAMS = "streams";
+
+    public static final long AV_CH_FRONT_LEFT = 0x00000001;
+    public static final long AV_CH_FRONT_RIGHT = 0x00000002;
+    public static final long AV_CH_FRONT_CENTER = 0x00000004;
+    public static final long AV_CH_LOW_FREQUENCY = 0x00000008;
+    public static final long AV_CH_BACK_LEFT = 0x00000010;
+    public static final long AV_CH_BACK_RIGHT = 0x00000020;
+    public static final long AV_CH_FRONT_LEFT_OF_CENTER = 0x00000040;
+    public static final long AV_CH_FRONT_RIGHT_OF_CENTER = 0x00000080;
+    public static final long AV_CH_BACK_CENTER = 0x00000100;
+    public static final long AV_CH_SIDE_LEFT = 0x00000200;
+    public static final long AV_CH_SIDE_RIGHT = 0x00000400;
+    public static final long AV_CH_TOP_CENTER = 0x00000800;
+    public static final long AV_CH_TOP_FRONT_LEFT = 0x00001000;
+    public static final long AV_CH_TOP_FRONT_CENTER = 0x00002000;
+    public static final long AV_CH_TOP_FRONT_RIGHT = 0x00004000;
+    public static final long AV_CH_TOP_BACK_LEFT = 0x00008000;
+    public static final long AV_CH_TOP_BACK_CENTER = 0x00010000;
+    public static final long AV_CH_TOP_BACK_RIGHT = 0x00020000;
+    public static final long AV_CH_STEREO_LEFT = 0x20000000;
+    public static final long AV_CH_STEREO_RIGHT = 0x40000000;
+    public static final long AV_CH_WIDE_LEFT = 0x0000000080000000L;
+    public static final long AV_CH_WIDE_RIGHT = 0x0000000100000000L;
+    public static final long AV_CH_SURROUND_DIRECT_LEFT = 0x0000000200000000L;
+    public static final long AV_CH_SURROUND_DIRECT_RIGHT = 0x0000000400000000L;
+    public static final long AV_CH_LOW_FREQUENCY_2 = 0x0000000800000000L;
+
+    public static final long AV_CH_LAYOUT_MONO = (AV_CH_FRONT_CENTER);
+    public static final long AV_CH_LAYOUT_STEREO = (AV_CH_FRONT_LEFT | AV_CH_FRONT_RIGHT);
+    public static final long AV_CH_LAYOUT_2POINT1 = (AV_CH_LAYOUT_STEREO | AV_CH_LOW_FREQUENCY);
+    public static final long AV_CH_LAYOUT_2_1 = (AV_CH_LAYOUT_STEREO | AV_CH_BACK_CENTER);
+    public static final long AV_CH_LAYOUT_SURROUND = (AV_CH_LAYOUT_STEREO | AV_CH_FRONT_CENTER);
+    public static final long AV_CH_LAYOUT_3POINT1 = (AV_CH_LAYOUT_SURROUND | AV_CH_LOW_FREQUENCY);
+    public static final long AV_CH_LAYOUT_4POINT0 = (AV_CH_LAYOUT_SURROUND | AV_CH_BACK_CENTER);
+    public static final long AV_CH_LAYOUT_4POINT1 = (AV_CH_LAYOUT_4POINT0 | AV_CH_LOW_FREQUENCY);
+    public static final long AV_CH_LAYOUT_2_2 = (AV_CH_LAYOUT_STEREO
+            | AV_CH_SIDE_LEFT | AV_CH_SIDE_RIGHT);
+    public static final long AV_CH_LAYOUT_QUAD = (AV_CH_LAYOUT_STEREO
+            | AV_CH_BACK_LEFT | AV_CH_BACK_RIGHT);
+    public static final long AV_CH_LAYOUT_5POINT0 = (AV_CH_LAYOUT_SURROUND
+            | AV_CH_SIDE_LEFT | AV_CH_SIDE_RIGHT);
+    public static final long AV_CH_LAYOUT_5POINT1 = (AV_CH_LAYOUT_5POINT0 | AV_CH_LOW_FREQUENCY);
+    public static final long AV_CH_LAYOUT_5POINT0_BACK = (AV_CH_LAYOUT_SURROUND
+            | AV_CH_BACK_LEFT | AV_CH_BACK_RIGHT);
+    public static final long AV_CH_LAYOUT_5POINT1_BACK = (AV_CH_LAYOUT_5POINT0_BACK | AV_CH_LOW_FREQUENCY);
+    public static final long AV_CH_LAYOUT_6POINT0 = (AV_CH_LAYOUT_5POINT0 | AV_CH_BACK_CENTER);
+    public static final long AV_CH_LAYOUT_6POINT0_FRONT = (AV_CH_LAYOUT_2_2
+            | AV_CH_FRONT_LEFT_OF_CENTER | AV_CH_FRONT_RIGHT_OF_CENTER);
+    public static final long AV_CH_LAYOUT_HEXAGONAL = (AV_CH_LAYOUT_5POINT0_BACK | AV_CH_BACK_CENTER);
+    public static final long AV_CH_LAYOUT_6POINT1 = (AV_CH_LAYOUT_5POINT1 | AV_CH_BACK_CENTER);
+    public static final long AV_CH_LAYOUT_6POINT1_BACK = (AV_CH_LAYOUT_5POINT1_BACK | AV_CH_BACK_CENTER);
+    public static final long AV_CH_LAYOUT_6POINT1_FRONT = (AV_CH_LAYOUT_6POINT0_FRONT | AV_CH_LOW_FREQUENCY);
+    public static final long AV_CH_LAYOUT_7POINT0 = (AV_CH_LAYOUT_5POINT0
+            | AV_CH_BACK_LEFT | AV_CH_BACK_RIGHT);
+    public static final long AV_CH_LAYOUT_7POINT0_FRONT = (AV_CH_LAYOUT_5POINT0
+            | AV_CH_FRONT_LEFT_OF_CENTER | AV_CH_FRONT_RIGHT_OF_CENTER);
+    public static final long AV_CH_LAYOUT_7POINT1 = (AV_CH_LAYOUT_5POINT1
+            | AV_CH_BACK_LEFT | AV_CH_BACK_RIGHT);
+    public static final long AV_CH_LAYOUT_7POINT1_WIDE = (AV_CH_LAYOUT_5POINT1
+            | AV_CH_FRONT_LEFT_OF_CENTER | AV_CH_FRONT_RIGHT_OF_CENTER);
+    public static final long AV_CH_LAYOUT_7POINT1_WIDE_BACK = (AV_CH_LAYOUT_5POINT1_BACK
+            | AV_CH_FRONT_LEFT_OF_CENTER | AV_CH_FRONT_RIGHT_OF_CENTER);
+    public static final long AV_CH_LAYOUT_OCTAGONAL = (AV_CH_LAYOUT_5POINT0
+            | AV_CH_BACK_LEFT | AV_CH_BACK_CENTER | AV_CH_BACK_RIGHT);
+    public static final long AV_CH_LAYOUT_STEREO_DOWNMIX = (AV_CH_STEREO_LEFT | AV_CH_STEREO_RIGHT);
+
+    public static final int FF_PROFILE_H264_CONSTRAINED = (1<<9);  // 8+1; constraint_set1_flag
+    public static final int FF_PROFILE_H264_INTRA = (1<<11);       // 8+3; constraint_set3_flag
+
+    public static final int FF_PROFILE_H264_BASELINE = 66;
+    public static final int FF_PROFILE_H264_CONSTRAINED_BASELINE = (66|FF_PROFILE_H264_CONSTRAINED);
+    public static final int FF_PROFILE_H264_MAIN = 77;
+    public static final int FF_PROFILE_H264_EXTENDED = 88;
+    public static final int FF_PROFILE_H264_HIGH = 100;
+    public static final int FF_PROFILE_H264_HIGH_10 = 110;
+    public static final int FF_PROFILE_H264_HIGH_10_INTRA = (110|FF_PROFILE_H264_INTRA);
+    public static final int FF_PROFILE_H264_HIGH_422 = 122;
+    public static final int FF_PROFILE_H264_HIGH_422_INTRA = (122|FF_PROFILE_H264_INTRA);
+    public static final int FF_PROFILE_H264_HIGH_444 = 144;
+    public static final int FF_PROFILE_H264_HIGH_444_PREDICTIVE = 244;
+    public static final int FF_PROFILE_H264_HIGH_444_INTRA = (244|FF_PROFILE_H264_INTRA);
+    public static final int FF_PROFILE_H264_CAVLC_444 = 44;
+
+    public Bundle mMediaMeta;
+
+    public String mFormat;
+    public long mDurationUS;
+    public long mStartUS;
+    public long mBitrate;
+
+    public final ArrayList<IjkStreamMeta> mStreams = new ArrayList<IjkStreamMeta>();
+    public IjkStreamMeta mVideoStream;
+    public IjkStreamMeta mAudioStream;
+
+    public String getString(String key) {
+        return mMediaMeta.getString(key);
+    }
+
+    public int getInt(String key) {
+        return getInt(key, 0);
+    }
+
+    public int getInt(String key, int defaultValue) {
+        String value = getString(key);
+        if (TextUtils.isEmpty(value))
+            return defaultValue;
+
+        try {
+            return Integer.parseInt(value);
+        } catch (NumberFormatException e) {
+            return defaultValue;
+        }
+    }
+
+    public long getLong(String key) {
+        return getLong(key, 0);
+    }
+
+    public long getLong(String key, long defaultValue) {
+        String value = getString(key);
+        if (TextUtils.isEmpty(value))
+            return defaultValue;
+
+        try {
+            return Long.parseLong(value);
+        } catch (NumberFormatException e) {
+            return defaultValue;
+        }
+    }
+
+    public ArrayList<Bundle> getParcelableArrayList(String key) {
+        return mMediaMeta.getParcelableArrayList(key);
+    }
+
+    public String getDurationInline() {
+        long duration = mDurationUS + 5000;
+        long secs = duration / 1000000;
+        long mins = secs / 60;
+        secs %= 60;
+        long hours = mins / 60;
+        mins %= 60;
+        return String.format(Locale.US, "%02d:%02d:%02d", hours, mins, secs);
+    }
+
+    public static IjkMediaMeta parse(Bundle mediaMeta) {
+        if (mediaMeta == null)
+            return null;
+
+        IjkMediaMeta meta = new IjkMediaMeta();
+        meta.mMediaMeta = mediaMeta;
+
+        meta.mFormat = meta.getString(IJKM_KEY_FORMAT);
+        meta.mDurationUS = meta.getLong(IJKM_KEY_DURATION_US);
+        meta.mStartUS = meta.getLong(IJKM_KEY_START_US);
+        meta.mBitrate = meta.getLong(IJKM_KEY_BITRATE);
+
+        int videoStreamIndex = meta.getInt(IJKM_KEY_VIDEO_STREAM, -1);
+        int audioStreamIndex = meta.getInt(IJKM_KEY_AUDIO_STREAM, -1);
+        int subtitleStreamIndex = meta.getInt(IJKM_KEY_TIMEDTEXT_STREAM, -1);
+
+        ArrayList<Bundle> streams = meta
+                .getParcelableArrayList(IJKM_KEY_STREAMS);
+        if (streams == null)
+            return meta;
+
+        int index = -1;
+        for (Bundle streamBundle : streams) {
+            index++;
+
+            if (streamBundle == null) {
+                continue;
+            }
+
+            IjkStreamMeta streamMeta = new IjkStreamMeta(index);
+            streamMeta.mMeta = streamBundle;
+            streamMeta.mType = streamMeta.getString(IJKM_KEY_TYPE);
+            streamMeta.mLanguage = streamMeta.getString(IJKM_KEY_LANGUAGE);
+            if (TextUtils.isEmpty(streamMeta.mType))
+                continue;
+
+            streamMeta.mCodecName = streamMeta.getString(IJKM_KEY_CODEC_NAME);
+            streamMeta.mCodecProfile = streamMeta
+                    .getString(IJKM_KEY_CODEC_PROFILE);
+            streamMeta.mCodecLongName = streamMeta
+                    .getString(IJKM_KEY_CODEC_LONG_NAME);
+            streamMeta.mBitrate = streamMeta.getInt(IJKM_KEY_BITRATE);
+
+            if (streamMeta.mType.equalsIgnoreCase(IJKM_VAL_TYPE__VIDEO)) {
+                streamMeta.mWidth = streamMeta.getInt(IJKM_KEY_WIDTH);
+                streamMeta.mHeight = streamMeta.getInt(IJKM_KEY_HEIGHT);
+                streamMeta.mFpsNum = streamMeta.getInt(IJKM_KEY_FPS_NUM);
+                streamMeta.mFpsDen = streamMeta.getInt(IJKM_KEY_FPS_DEN);
+                streamMeta.mTbrNum = streamMeta.getInt(IJKM_KEY_TBR_NUM);
+                streamMeta.mTbrDen = streamMeta.getInt(IJKM_KEY_TBR_DEN);
+                streamMeta.mSarNum = streamMeta.getInt(IJKM_KEY_SAR_NUM);
+                streamMeta.mSarDen = streamMeta.getInt(IJKM_KEY_SAR_DEN);
+
+                if (videoStreamIndex == index) {
+                    meta.mVideoStream = streamMeta;
+                }
+            } else if (streamMeta.mType.equalsIgnoreCase(IJKM_VAL_TYPE__AUDIO)) {
+                streamMeta.mSampleRate = streamMeta
+                        .getInt(IJKM_KEY_SAMPLE_RATE);
+                streamMeta.mChannelLayout = streamMeta
+                        .getLong(IJKM_KEY_CHANNEL_LAYOUT);
+
+                if (audioStreamIndex == index) {
+                    meta.mAudioStream = streamMeta;
+                }
+            }
+            meta.mStreams.add(streamMeta);
+        }
+
+        return meta;
+    }
+
+    public static class IjkStreamMeta {
+        public Bundle mMeta;
+
+        public final int mIndex;
+        public String mType;
+        public String mLanguage;
+
+        // common
+        public String mCodecName;
+        public String mCodecProfile;
+        public String mCodecLongName;
+        public long mBitrate;
+
+        // video
+        public int mWidth;
+        public int mHeight;
+        public int mFpsNum;
+        public int mFpsDen;
+        public int mTbrNum;
+        public int mTbrDen;
+        public int mSarNum;
+        public int mSarDen;
+
+        // audio
+        public int mSampleRate;
+        public long mChannelLayout;
+
+        public IjkStreamMeta(int index) {
+            mIndex = index;
+        }
+
+        public String getString(String key) {
+            return mMeta.getString(key);
+        }
+
+        public int getInt(String key) {
+            return getInt(key, 0);
+        }
+
+        public int getInt(String key, int defaultValue) {
+            String value = getString(key);
+            if (TextUtils.isEmpty(value))
+                return defaultValue;
+
+            try {
+                return Integer.parseInt(value);
+            } catch (NumberFormatException e) {
+                return defaultValue;
+            }
+        }
+
+        public long getLong(String key) {
+            return getLong(key, 0);
+        }
+
+        public long getLong(String key, long defaultValue) {
+            String value = getString(key);
+            if (TextUtils.isEmpty(value))
+                return defaultValue;
+
+            try {
+                return Long.parseLong(value);
+            } catch (NumberFormatException e) {
+                return defaultValue;
+            }
+        }
+
+        public String getCodecLongNameInline() {
+            if (!TextUtils.isEmpty(mCodecLongName)) {
+                return mCodecLongName;
+            } else if (!TextUtils.isEmpty(mCodecName)) {
+                return mCodecName;
+            } else {
+                return "N/A";
+            }
+        }
+
+        public String getCodecShortNameInline() {
+            if (!TextUtils.isEmpty(mCodecName)) {
+                return mCodecName;
+            } else {
+                return "N/A";
+            }
+        }
+
+        public String getResolutionInline() {
+            if (mWidth <= 0 || mHeight <= 0) {
+                return "N/A";
+            } else if (mSarNum <= 0 || mSarDen <= 0) {
+                return String.format(Locale.US, "%d x %d", mWidth, mHeight);
+            } else {
+                return String.format(Locale.US, "%d x %d [SAR %d:%d]", mWidth,
+                        mHeight, mSarNum, mSarDen);
+            }
+        }
+
+        public String getFpsInline() {
+            if (mFpsNum <= 0 || mFpsDen <= 0) {
+                return "N/A";
+            } else {
+                return String.valueOf(((float) (mFpsNum)) / mFpsDen);
+            }
+        }
+
+        public String getBitrateInline() {
+            if (mBitrate <= 0) {
+                return "N/A";
+            } else if (mBitrate < 1000) {
+                return String.format(Locale.US, "%d bit/s", mBitrate);
+            } else {
+                return String.format(Locale.US, "%d kb/s", mBitrate / 1000);
+            }
+        }
+
+        public String getSampleRateInline() {
+            if (mSampleRate <= 0) {
+                return "N/A";
+            } else {
+                return String.format(Locale.US, "%d Hz", mSampleRate);
+            }
+        }
+
+        public String getChannelLayoutInline() {
+            if (mChannelLayout <= 0) {
+                return "N/A";
+            } else {
+                if (mChannelLayout == AV_CH_LAYOUT_MONO) {
+                    return "mono";
+                } else if (mChannelLayout == AV_CH_LAYOUT_STEREO) {
+                    return "stereo";
+                } else {
+                    return String.format(Locale.US, "%x", mChannelLayout);
+                }
+            }
+        }
+    }
+}

File diff suppressed because it is too large
+ 1263 - 0
ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/IjkMediaPlayer.java


+ 39 - 0
ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/IjkTimedText.java

@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 Zheng Yuan <zhengyuan10503@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package tv.danmaku.ijk.media.player;
+
+import android.graphics.Rect;
+import java.lang.String;
+
+public final class IjkTimedText {
+
+    private Rect mTextBounds = null;
+    private String mTextChars = null;
+
+    public IjkTimedText(Rect bounds, String text) {
+        mTextBounds = bounds;
+        mTextChars = text;
+    }
+
+    public Rect getBounds() {
+        return mTextBounds;
+    }
+
+    public String getText() {
+        return mTextChars;
+    }
+}

+ 30 - 0
ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/MediaInfo.java

@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2013-2014 Bilibili
+ * Copyright (C) 2013-2014 Zhang Rui <bbcallen@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package tv.danmaku.ijk.media.player;
+
+public class MediaInfo {
+    public String mMediaPlayerName;
+
+    public String mVideoDecoder;
+    public String mVideoDecoderImpl;
+
+    public String mAudioDecoder;
+    public String mAudioDecoderImpl;
+
+    public IjkMediaMeta mMeta;
+}

+ 339 - 0
ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/MediaPlayerProxy.java

@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2015 Bilibili
+ * Copyright (C) 2015 Zhang Rui <bbcallen@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package tv.danmaku.ijk.media.player;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.net.Uri;
+import android.os.Build;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.util.Map;
+
+import tv.danmaku.ijk.media.player.misc.IMediaDataSource;
+import tv.danmaku.ijk.media.player.misc.ITrackInfo;
+
+public class MediaPlayerProxy implements IMediaPlayer {
+    protected final IMediaPlayer mBackEndMediaPlayer;
+
+    public MediaPlayerProxy(IMediaPlayer backEndMediaPlayer) {
+        mBackEndMediaPlayer = backEndMediaPlayer;
+    }
+
+    public IMediaPlayer getInternalMediaPlayer() {
+        return mBackEndMediaPlayer;
+    }
+
+    @Override
+    public void setDisplay(SurfaceHolder sh) {
+        mBackEndMediaPlayer.setDisplay(sh);
+    }
+
+    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+    @Override
+    public void setSurface(Surface surface) {
+        mBackEndMediaPlayer.setSurface(surface);
+    }
+
+    @Override
+    public void setDataSource(Context context, Uri uri)
+            throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
+        mBackEndMediaPlayer.setDataSource(context, uri);
+    }
+
+    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+    @Override
+    public void setDataSource(Context context, Uri uri, Map<String, String> headers)
+            throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
+        mBackEndMediaPlayer.setDataSource(context, uri, headers);
+    }
+
+    @Override
+    public void setDataSource(FileDescriptor fd)
+            throws IOException, IllegalArgumentException, IllegalStateException {
+        mBackEndMediaPlayer.setDataSource(fd);
+    }
+
+    @Override
+    public void setDataSource(String path) throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
+        mBackEndMediaPlayer.setDataSource(path);
+    }
+
+    @Override
+    public void setDataSource(IMediaDataSource mediaDataSource)  {
+        mBackEndMediaPlayer.setDataSource(mediaDataSource);
+    }
+
+    @Override
+    public String getDataSource() {
+        return mBackEndMediaPlayer.getDataSource();
+    }
+
+    @Override
+    public void prepareAsync() throws IllegalStateException {
+        mBackEndMediaPlayer.prepareAsync();
+    }
+
+    @Override
+    public void start() throws IllegalStateException {
+        mBackEndMediaPlayer.start();
+    }
+
+    @Override
+    public void stop() throws IllegalStateException {
+        mBackEndMediaPlayer.stop();
+    }
+
+    @Override
+    public void pause() throws IllegalStateException {
+        mBackEndMediaPlayer.pause();
+    }
+
+    @Override
+    public void setScreenOnWhilePlaying(boolean screenOn) {
+        mBackEndMediaPlayer.setScreenOnWhilePlaying(screenOn);
+    }
+
+    @Override
+    public int getVideoWidth() {
+        return mBackEndMediaPlayer.getVideoWidth();
+    }
+
+    @Override
+    public int getVideoHeight() {
+        return mBackEndMediaPlayer.getVideoHeight();
+    }
+
+    @Override
+    public boolean isPlaying() {
+        return mBackEndMediaPlayer.isPlaying();
+    }
+
+    @Override
+    public void seekTo(long msec) throws IllegalStateException {
+        mBackEndMediaPlayer.seekTo(msec);
+    }
+
+    @Override
+    public long getCurrentPosition() {
+        return mBackEndMediaPlayer.getCurrentPosition();
+    }
+
+    @Override
+    public long getDuration() {
+        return mBackEndMediaPlayer.getDuration();
+    }
+
+    @Override
+    public void release() {
+        mBackEndMediaPlayer.release();
+    }
+
+    @Override
+    public void reset() {
+        mBackEndMediaPlayer.reset();
+    }
+
+    @Override
+    public void setVolume(float leftVolume, float rightVolume) {
+        mBackEndMediaPlayer.setVolume(leftVolume, rightVolume);
+    }
+
+    @Override
+    public int getAudioSessionId() {
+        return mBackEndMediaPlayer.getAudioSessionId();
+    }
+
+    @Override
+    public MediaInfo getMediaInfo() {
+        return mBackEndMediaPlayer.getMediaInfo();
+    }
+
+    @Override
+    public void setLogEnabled(boolean enable) {
+
+    }
+
+    @Override
+    public boolean isPlayable() {
+        return false;
+    }
+
+    @Override
+    public void setOnPreparedListener(OnPreparedListener listener) {
+        if (listener != null) {
+            final OnPreparedListener finalListener = listener;
+            mBackEndMediaPlayer.setOnPreparedListener(new OnPreparedListener() {
+                @Override
+                public void onPrepared(IMediaPlayer mp) {
+                    finalListener.onPrepared(MediaPlayerProxy.this);
+                }
+            });
+        } else {
+            mBackEndMediaPlayer.setOnPreparedListener(null);
+        }
+    }
+
+    @Override
+    public void setOnCompletionListener(OnCompletionListener listener) {
+        if (listener != null) {
+            final OnCompletionListener finalListener = listener;
+            mBackEndMediaPlayer.setOnCompletionListener(new OnCompletionListener() {
+                @Override
+                public void onCompletion(IMediaPlayer mp) {
+                    finalListener.onCompletion(MediaPlayerProxy.this);
+                }
+            });
+        } else {
+            mBackEndMediaPlayer.setOnCompletionListener(null);
+        }
+    }
+
+    @Override
+    public void setOnBufferingUpdateListener(OnBufferingUpdateListener listener) {
+        if (listener != null) {
+            final OnBufferingUpdateListener finalListener = listener;
+            mBackEndMediaPlayer.setOnBufferingUpdateListener(new OnBufferingUpdateListener() {
+                @Override
+                public void onBufferingUpdate(IMediaPlayer mp, int percent) {
+                    finalListener.onBufferingUpdate(MediaPlayerProxy.this, percent);
+                }
+            });
+        } else {
+            mBackEndMediaPlayer.setOnBufferingUpdateListener(null);
+        }
+    }
+
+    @Override
+    public void setOnSeekCompleteListener(OnSeekCompleteListener listener) {
+        if (listener != null) {
+            final OnSeekCompleteListener finalListener = listener;
+            mBackEndMediaPlayer.setOnSeekCompleteListener(new OnSeekCompleteListener() {
+                @Override
+                public void onSeekComplete(IMediaPlayer mp) {
+                    finalListener.onSeekComplete(MediaPlayerProxy.this);
+                }
+            });
+        } else {
+            mBackEndMediaPlayer.setOnSeekCompleteListener(null);
+        }
+    }
+
+    @Override
+    public void setOnVideoSizeChangedListener(OnVideoSizeChangedListener listener) {
+        if (listener != null) {
+            final OnVideoSizeChangedListener finalListener = listener;
+            mBackEndMediaPlayer.setOnVideoSizeChangedListener(new OnVideoSizeChangedListener() {
+                @Override
+                public void onVideoSizeChanged(IMediaPlayer mp, int width, int height, int sar_num, int sar_den) {
+                    finalListener.onVideoSizeChanged(MediaPlayerProxy.this, width, height, sar_num, sar_den);
+                }
+            });
+        } else {
+            mBackEndMediaPlayer.setOnVideoSizeChangedListener(null);
+        }
+    }
+
+    @Override
+    public void setOnErrorListener(OnErrorListener listener) {
+        if (listener != null) {
+            final OnErrorListener finalListener = listener;
+            mBackEndMediaPlayer.setOnErrorListener(new OnErrorListener() {
+                @Override
+                public boolean onError(IMediaPlayer mp, int what, int extra) {
+                    return finalListener.onError(MediaPlayerProxy.this, what, extra);
+                }
+            });
+        } else {
+            mBackEndMediaPlayer.setOnErrorListener(null);
+        }
+    }
+
+    @Override
+    public void setOnInfoListener(OnInfoListener listener) {
+        if (listener != null) {
+            final OnInfoListener finalListener = listener;
+            mBackEndMediaPlayer.setOnInfoListener(new OnInfoListener() {
+                @Override
+                public boolean onInfo(IMediaPlayer mp, int what, int extra) {
+                    return finalListener.onInfo(MediaPlayerProxy.this, what, extra);
+                }
+            });
+        } else {
+            mBackEndMediaPlayer.setOnInfoListener(null);
+        }
+    }
+
+    @Override
+    public void setOnTimedTextListener(OnTimedTextListener listener) {
+        if (listener != null) {
+            final OnTimedTextListener finalListener = listener;
+            mBackEndMediaPlayer.setOnTimedTextListener(new OnTimedTextListener() {
+                @Override
+                public void onTimedText(IMediaPlayer mp, IjkTimedText text) {
+                    finalListener.onTimedText(MediaPlayerProxy.this, text);
+                }
+            });
+        } else {
+            mBackEndMediaPlayer.setOnTimedTextListener(null);
+        }
+    }
+
+    @Override
+    public void setAudioStreamType(int streamtype) {
+        mBackEndMediaPlayer.setAudioStreamType(streamtype);
+    }
+
+    @Override
+    public void setKeepInBackground(boolean keepInBackground) {
+        mBackEndMediaPlayer.setKeepInBackground(keepInBackground);
+    }
+
+    @Override
+    public int getVideoSarNum() {
+        return mBackEndMediaPlayer.getVideoSarNum();
+    }
+
+    @Override
+    public int getVideoSarDen() {
+        return mBackEndMediaPlayer.getVideoSarDen();
+    }
+
+    @Override
+    public void setWakeMode(Context context, int mode) {
+        mBackEndMediaPlayer.setWakeMode(context, mode);
+    }
+
+    @Override
+    public ITrackInfo[] getTrackInfo() {
+        return mBackEndMediaPlayer.getTrackInfo();
+    }
+
+    @Override
+    public void setLooping(boolean looping) {
+        mBackEndMediaPlayer.setLooping(looping);
+    }
+
+    @Override
+    public boolean isLooping() {
+        return mBackEndMediaPlayer.isLooping();
+    }
+}

+ 100 - 0
ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/TextureMediaPlayer.java

@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2015 Bilibili
+ * Copyright (C) 2015 Zhang Rui <bbcallen@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package tv.danmaku.ijk.media.player;
+
+import android.annotation.TargetApi;
+import android.graphics.SurfaceTexture;
+import android.os.Build;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+
+@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+public class TextureMediaPlayer extends MediaPlayerProxy implements IMediaPlayer, ISurfaceTextureHolder {
+    private SurfaceTexture mSurfaceTexture;
+    private ISurfaceTextureHost mSurfaceTextureHost;
+
+    public TextureMediaPlayer(IMediaPlayer backEndMediaPlayer) {
+        super(backEndMediaPlayer);
+    }
+
+    public void releaseSurfaceTexture() {
+        if (mSurfaceTexture != null) {
+            if (mSurfaceTextureHost != null) {
+                mSurfaceTextureHost.releaseSurfaceTexture(mSurfaceTexture);
+            } else {
+                mSurfaceTexture.release();
+            }
+            mSurfaceTexture = null;
+        }
+    }
+
+    //--------------------
+    // IMediaPlayer
+    //--------------------
+    @Override
+    public void reset() {
+        super.reset();
+        releaseSurfaceTexture();
+    }
+
+    @Override
+    public void release() {
+        super.release();
+        releaseSurfaceTexture();
+    }
+
+    @Override
+    public void setDisplay(SurfaceHolder sh) {
+        if (mSurfaceTexture == null)
+            super.setDisplay(sh);
+    }
+
+    @Override
+    public void setSurface(Surface surface) {
+        if (mSurfaceTexture == null)
+            super.setSurface(surface);
+    }
+
+    //--------------------
+    // ISurfaceTextureHolder
+    //--------------------
+
+    @Override
+    public void setSurfaceTexture(SurfaceTexture surfaceTexture) {
+        if (mSurfaceTexture == surfaceTexture)
+            return;
+
+        releaseSurfaceTexture();
+        mSurfaceTexture = surfaceTexture;
+        if (surfaceTexture == null) {
+            super.setSurface(null);
+        } else {
+            super.setSurface(new Surface(surfaceTexture));
+        }
+    }
+
+    @Override
+    public SurfaceTexture getSurfaceTexture() {
+        return mSurfaceTexture;
+    }
+
+    @Override
+    public void setSurfaceTextureHost(ISurfaceTextureHost surfaceTextureHost) {
+        mSurfaceTextureHost = surfaceTextureHost;
+    }
+}

+ 32 - 0
ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/annotations/AccessedByNative.java

@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2013-2014 Bilibili
+ * Copyright (C) 2013-2014 Zhang Rui <bbcallen@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package tv.danmaku.ijk.media.player.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * is used by the JNI generator to create the necessary JNI
+ * bindings and expose this method to native code.
+ */
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.CLASS)
+public @interface AccessedByNative {
+}

+ 36 - 0
ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/annotations/CalledByNative.java

@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2013-2014 Bilibili
+ * Copyright (C) 2013-2014 Zhang Rui <bbcallen@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package tv.danmaku.ijk.media.player.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * is used by the JNI generator to create the necessary JNI
+ * bindings and expose this method to native code.
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.CLASS)
+public @interface CalledByNative {
+    /*
+     * If present, tells which inner class the method belongs to.
+     */
+    String value() default "";
+}

+ 22 - 0
ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/exceptions/IjkMediaException.java

@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2013-2014 Bilibili
+ * Copyright (C) 2013-2014 Zhang Rui <bbcallen@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package tv.danmaku.ijk.media.player.exceptions;
+
+public class IjkMediaException extends Exception {
+    private static final long serialVersionUID = 7234796519009099506L;
+}

+ 5 - 0
ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/ffmpeg/FFmpegApi.java

@@ -0,0 +1,5 @@
+package tv.danmaku.ijk.media.player.ffmpeg;
+
+public class FFmpegApi {
+    public static native String av_base64_encode(byte in[]);
+}

+ 63 - 0
ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/misc/AndroidMediaFormat.java

@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2015 Bilibili
+ * Copyright (C) 2015 Zhang Rui <bbcallen@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package tv.danmaku.ijk.media.player.misc;
+
+import android.annotation.TargetApi;
+import android.media.MediaFormat;
+import android.os.Build;
+
+public class AndroidMediaFormat implements IMediaFormat {
+    private final MediaFormat mMediaFormat;
+
+    public AndroidMediaFormat(MediaFormat mediaFormat) {
+        mMediaFormat = mediaFormat;
+    }
+
+    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+    @Override
+    public int getInteger(String name) {
+        if (mMediaFormat == null)
+            return 0;
+
+        return mMediaFormat.getInteger(name);
+    }
+
+    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+    @Override
+    public String getString(String name) {
+        if (mMediaFormat == null)
+            return null;
+
+        return mMediaFormat.getString(name);
+    }
+
+    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+    @Override
+    public String toString() {
+        StringBuilder out = new StringBuilder(128);
+        out.append(getClass().getName());
+        out.append('{');
+        if (mMediaFormat != null) {
+            out.append(mMediaFormat.toString());
+        } else {
+            out.append("null");
+        }
+        out.append('}');
+        return out.toString();
+    }
+}

+ 109 - 0
ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/misc/AndroidTrackInfo.java

@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2015 Bilibili
+ * Copyright (C) 2015 Zhang Rui <bbcallen@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package tv.danmaku.ijk.media.player.misc;
+
+import android.annotation.TargetApi;
+import android.media.MediaFormat;
+import android.media.MediaPlayer;
+import android.os.Build;
+
+public class AndroidTrackInfo implements ITrackInfo {
+    private final MediaPlayer.TrackInfo mTrackInfo;
+
+    public static AndroidTrackInfo[] fromMediaPlayer(MediaPlayer mp) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
+            return fromTrackInfo(mp.getTrackInfo());
+
+        return null;
+    }
+
+    private static AndroidTrackInfo[] fromTrackInfo(MediaPlayer.TrackInfo[] trackInfos) {
+        if (trackInfos == null)
+            return null;
+
+        AndroidTrackInfo androidTrackInfo[] = new AndroidTrackInfo[trackInfos.length];
+        for (int i = 0; i < trackInfos.length; ++i) {
+            androidTrackInfo[i] = new AndroidTrackInfo(trackInfos[i]);
+        }
+
+        return androidTrackInfo;
+    }
+
+    private AndroidTrackInfo(MediaPlayer.TrackInfo trackInfo) {
+        mTrackInfo = trackInfo;
+    }
+
+    @TargetApi(Build.VERSION_CODES.KITKAT)
+    @Override
+    public IMediaFormat getFormat() {
+        if (mTrackInfo == null)
+            return null;
+
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT)
+            return null;
+
+        MediaFormat mediaFormat = mTrackInfo.getFormat();
+        if (mediaFormat == null)
+            return null;
+
+        return new AndroidMediaFormat(mediaFormat);
+    }
+
+    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+    @Override
+    public String getLanguage() {
+        if (mTrackInfo == null)
+            return "und";
+
+        return mTrackInfo.getLanguage();
+    }
+
+    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+    @Override
+    public int getTrackType() {
+        if (mTrackInfo == null)
+            return MEDIA_TRACK_TYPE_UNKNOWN;
+
+        return mTrackInfo.getTrackType();
+    }
+
+    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+    @Override
+    public String toString() {
+        StringBuilder out = new StringBuilder(128);
+        out.append(getClass().getSimpleName());
+        out.append('{');
+        if (mTrackInfo != null) {
+            out.append(mTrackInfo.toString());
+        } else {
+            out.append("null");
+        }
+        out.append('}');
+        return out.toString();
+    }
+
+    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+    @Override
+    public String getInfoInline() {
+        if (mTrackInfo != null) {
+            return mTrackInfo.toString();
+        } else {
+            return "null";
+        }
+    }
+}

+ 28 - 0
ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/misc/IIjkIOHttp.java

@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2016 Bilibili
+ * Copyright (C) 2016 Raymond Zheng <raymondzheng1412@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package tv.danmaku.ijk.media.player.misc;
+
+import java.io.IOException;
+
+@SuppressWarnings("RedundantThrows")
+public interface IIjkIOHttp {
+    int  open() throws IOException;
+    int  read(byte[] buffer, int size) throws IOException;
+    long seek(long offset, int whence) throws IOException;
+    int  close() throws IOException;
+}

+ 29 - 0
ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/misc/IMediaDataSource.java

@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2015 Bilibili
+ * Copyright (C) 2015 Zhang Rui <bbcallen@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package tv.danmaku.ijk.media.player.misc;
+
+import java.io.IOException;
+
+@SuppressWarnings("RedundantThrows")
+public interface IMediaDataSource {
+    int	 readAt(long position, byte[] buffer, int offset, int size) throws IOException;
+
+    long getSize() throws IOException;
+
+    void close() throws IOException;
+}

+ 31 - 0
ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/misc/IMediaFormat.java

@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2015 Bilibili
+ * Copyright (C) 2015 Zhang Rui <bbcallen@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package tv.danmaku.ijk.media.player.misc;
+
+public interface IMediaFormat {
+    // Common keys
+    String KEY_MIME = "mime";
+
+    // Video Keys
+    String KEY_WIDTH = "width";
+    String KEY_HEIGHT = "height";
+
+    String getString(String name);
+
+    int getInteger(String name);
+}

+ 35 - 0
ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/misc/ITrackInfo.java

@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2015 Bilibili
+ * Copyright (C) 2015 Zhang Rui <bbcallen@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package tv.danmaku.ijk.media.player.misc;
+
+public interface ITrackInfo {
+    int MEDIA_TRACK_TYPE_AUDIO = 2;
+    int MEDIA_TRACK_TYPE_METADATA = 5;
+    int MEDIA_TRACK_TYPE_SUBTITLE = 4;
+    int MEDIA_TRACK_TYPE_TIMEDTEXT = 3;
+    int MEDIA_TRACK_TYPE_UNKNOWN = 0;
+    int MEDIA_TRACK_TYPE_VIDEO = 1;
+
+    IMediaFormat getFormat();
+
+    String getLanguage();
+
+    int getTrackType();
+
+    String getInfoInline();
+}

+ 259 - 0
ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/misc/IjkMediaFormat.java

@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2015 Bilibili
+ * Copyright (C) 2015 Zhang Rui <bbcallen@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package tv.danmaku.ijk.media.player.misc;
+
+import android.annotation.TargetApi;
+import android.os.Build;
+import android.text.TextUtils;
+
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+import tv.danmaku.ijk.media.player.IjkMediaMeta;
+
+public class IjkMediaFormat implements IMediaFormat {
+    // Common
+    public static final String KEY_IJK_CODEC_LONG_NAME_UI = "ijk-codec-long-name-ui";
+    public static final String KEY_IJK_CODEC_NAME_UI = "ijk-codec-name-ui";
+    public static final String KEY_IJK_BIT_RATE_UI = "ijk-bit-rate-ui";
+
+    // Video
+    public static final String KEY_IJK_CODEC_PROFILE_LEVEL_UI = "ijk-profile-level-ui";
+    public static final String KEY_IJK_CODEC_PIXEL_FORMAT_UI = "ijk-pixel-format-ui";
+    public static final String KEY_IJK_RESOLUTION_UI = "ijk-resolution-ui";
+    public static final String KEY_IJK_FRAME_RATE_UI = "ijk-frame-rate-ui";
+
+    // Audio
+    public static final String KEY_IJK_SAMPLE_RATE_UI = "ijk-sample-rate-ui";
+    public static final String KEY_IJK_CHANNEL_UI = "ijk-channel-ui";
+
+    // Codec
+    public static final String CODEC_NAME_H264 = "h264";
+
+    public final IjkMediaMeta.IjkStreamMeta mMediaFormat;
+
+    public IjkMediaFormat(IjkMediaMeta.IjkStreamMeta streamMeta) {
+        mMediaFormat = streamMeta;
+    }
+
+    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+    @Override
+    public int getInteger(String name) {
+        if (mMediaFormat == null)
+            return 0;
+
+        return mMediaFormat.getInt(name);
+    }
+
+    @Override
+    public String getString(String name) {
+        if (mMediaFormat == null)
+            return null;
+
+        if (sFormatterMap.containsKey(name)) {
+            Formatter formatter = sFormatterMap.get(name);
+            return formatter.format(this);
+        }
+
+        return mMediaFormat.getString(name);
+    }
+
+    //-------------------------
+    // Formatter
+    //-------------------------
+
+    private static abstract class Formatter {
+        public String format(IjkMediaFormat mediaFormat) {
+            String value = doFormat(mediaFormat);
+            if (TextUtils.isEmpty(value))
+                return getDefaultString();
+            return value;
+        }
+
+        protected abstract String doFormat(IjkMediaFormat mediaFormat);
+
+        @SuppressWarnings("SameReturnValue")
+        protected String getDefaultString() {
+            return "N/A";
+        }
+    }
+
+    private static final Map<String, Formatter> sFormatterMap = new HashMap<String, Formatter>();
+
+    {
+        sFormatterMap.put(KEY_IJK_CODEC_LONG_NAME_UI, new Formatter() {
+            @Override
+            public String doFormat(IjkMediaFormat mediaFormat) {
+                return mMediaFormat.getString(IjkMediaMeta.IJKM_KEY_CODEC_LONG_NAME);
+            }
+        });
+        sFormatterMap.put(KEY_IJK_CODEC_NAME_UI, new Formatter() {
+            @Override
+            public String doFormat(IjkMediaFormat mediaFormat) {
+                return mMediaFormat.getString(IjkMediaMeta.IJKM_KEY_CODEC_NAME);
+            }
+        });
+        sFormatterMap.put(KEY_IJK_BIT_RATE_UI, new Formatter() {
+            @Override
+            protected String doFormat(IjkMediaFormat mediaFormat) {
+                int bitRate = mediaFormat.getInteger(IjkMediaMeta.IJKM_KEY_BITRATE);
+                if (bitRate <= 0) {
+                    return null;
+                } else if (bitRate < 1000) {
+                    return String.format(Locale.US, "%d bit/s", bitRate);
+                } else {
+                    return String.format(Locale.US, "%d kb/s", bitRate / 1000);
+                }
+            }
+        });
+        sFormatterMap.put(KEY_IJK_CODEC_PROFILE_LEVEL_UI, new Formatter() {
+            @Override
+            protected String doFormat(IjkMediaFormat mediaFormat) {
+                int profileIndex = mediaFormat.getInteger(IjkMediaMeta.IJKM_KEY_CODEC_PROFILE_ID);
+                String profile;
+                switch (profileIndex) {
+                    case IjkMediaMeta.FF_PROFILE_H264_BASELINE:
+                        profile = "Baseline";
+                        break;
+                    case IjkMediaMeta.FF_PROFILE_H264_CONSTRAINED_BASELINE:
+                        profile = "Constrained Baseline";
+                        break;
+                    case IjkMediaMeta.FF_PROFILE_H264_MAIN:
+                        profile = "Main";
+                        break;
+                    case IjkMediaMeta.FF_PROFILE_H264_EXTENDED:
+                        profile = "Extended";
+                        break;
+                    case IjkMediaMeta.FF_PROFILE_H264_HIGH:
+                        profile = "High";
+                        break;
+                    case IjkMediaMeta.FF_PROFILE_H264_HIGH_10:
+                        profile = "High 10";
+                        break;
+                    case IjkMediaMeta.FF_PROFILE_H264_HIGH_10_INTRA:
+                        profile = "High 10 Intra";
+                        break;
+                    case IjkMediaMeta.FF_PROFILE_H264_HIGH_422:
+                        profile = "High 4:2:2";
+                        break;
+                    case IjkMediaMeta.FF_PROFILE_H264_HIGH_422_INTRA:
+                        profile = "High 4:2:2 Intra";
+                        break;
+                    case IjkMediaMeta.FF_PROFILE_H264_HIGH_444:
+                        profile = "High 4:4:4";
+                        break;
+                    case IjkMediaMeta.FF_PROFILE_H264_HIGH_444_PREDICTIVE:
+                        profile = "High 4:4:4 Predictive";
+                        break;
+                    case IjkMediaMeta.FF_PROFILE_H264_HIGH_444_INTRA:
+                        profile = "High 4:4:4 Intra";
+                        break;
+                    case IjkMediaMeta.FF_PROFILE_H264_CAVLC_444:
+                        profile = "CAVLC 4:4:4";
+                        break;
+                    default:
+                        return null;
+                }
+
+                StringBuilder sb = new StringBuilder();
+                sb.append(profile);
+
+                String codecName = mediaFormat.getString(IjkMediaMeta.IJKM_KEY_CODEC_NAME);
+                if (!TextUtils.isEmpty(codecName) && codecName.equalsIgnoreCase(CODEC_NAME_H264)) {
+                    int level = mediaFormat.getInteger(IjkMediaMeta.IJKM_KEY_CODEC_LEVEL);
+                    if (level < 10)
+                        return sb.toString();
+
+                    sb.append(" Profile Level ");
+                    sb.append((level / 10) % 10);
+                    if ((level % 10) != 0) {
+                        sb.append(".");
+                        sb.append(level % 10);
+                    }
+                }
+
+                return sb.toString();
+            }
+        });
+        sFormatterMap.put(KEY_IJK_CODEC_PIXEL_FORMAT_UI, new Formatter() {
+            @Override
+            protected String doFormat(IjkMediaFormat mediaFormat) {
+                return mediaFormat.getString(IjkMediaMeta.IJKM_KEY_CODEC_PIXEL_FORMAT);
+            }
+        });
+        sFormatterMap.put(KEY_IJK_RESOLUTION_UI, new Formatter() {
+            @Override
+            protected String doFormat(IjkMediaFormat mediaFormat) {
+                int width = mediaFormat.getInteger(KEY_WIDTH);
+                int height = mediaFormat.getInteger(KEY_HEIGHT);
+                int sarNum = mediaFormat.getInteger(IjkMediaMeta.IJKM_KEY_SAR_NUM);
+                int sarDen = mediaFormat.getInteger(IjkMediaMeta.IJKM_KEY_SAR_DEN);
+
+                if (width <= 0 || height <= 0) {
+                    return null;
+                } else if (sarNum <= 0 || sarDen <= 0) {
+                    return String.format(Locale.US, "%d x %d", width, height);
+                } else {
+                    return String.format(Locale.US, "%d x %d [SAR %d:%d]", width,
+                            height, sarNum, sarDen);
+                }
+            }
+        });
+        sFormatterMap.put(KEY_IJK_FRAME_RATE_UI, new Formatter() {
+            @Override
+            protected String doFormat(IjkMediaFormat mediaFormat) {
+                int fpsNum = mediaFormat.getInteger(IjkMediaMeta.IJKM_KEY_FPS_NUM);
+                int fpsDen = mediaFormat.getInteger(IjkMediaMeta.IJKM_KEY_FPS_DEN);
+                if (fpsNum <= 0 || fpsDen <= 0) {
+                    return null;
+                } else {
+                    return String.valueOf(((float) (fpsNum)) / fpsDen);
+                }
+            }
+        });
+        sFormatterMap.put(KEY_IJK_SAMPLE_RATE_UI, new Formatter() {
+            @Override
+            protected String doFormat(IjkMediaFormat mediaFormat) {
+                int sampleRate = mediaFormat.getInteger(IjkMediaMeta.IJKM_KEY_SAMPLE_RATE);
+                if (sampleRate <= 0) {
+                    return null;
+                } else {
+                    return String.format(Locale.US, "%d Hz", sampleRate);
+                }
+            }
+        });
+        sFormatterMap.put(KEY_IJK_CHANNEL_UI, new Formatter() {
+            @Override
+            protected String doFormat(IjkMediaFormat mediaFormat) {
+                int channelLayout = mediaFormat.getInteger(IjkMediaMeta.IJKM_KEY_CHANNEL_LAYOUT);
+                if (channelLayout <= 0) {
+                    return null;
+                } else {
+                    if (channelLayout == IjkMediaMeta.AV_CH_LAYOUT_MONO) {
+                        return "mono";
+                    } else if (channelLayout == IjkMediaMeta.AV_CH_LAYOUT_STEREO) {
+                        return "stereo";
+                    } else {
+                        return String.format(Locale.US, "%x", channelLayout);
+                    }
+                }
+            }
+        });
+    }
+}

+ 99 - 0
ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/misc/IjkTrackInfo.java

@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2015 Bilibili
+ * Copyright (C) 2015 Zhang Rui <bbcallen@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package tv.danmaku.ijk.media.player.misc;
+
+import android.text.TextUtils;
+
+import tv.danmaku.ijk.media.player.IjkMediaMeta;
+
+public class IjkTrackInfo implements ITrackInfo {
+    private int mTrackType = MEDIA_TRACK_TYPE_UNKNOWN;
+    private IjkMediaMeta.IjkStreamMeta mStreamMeta;
+
+    public IjkTrackInfo(IjkMediaMeta.IjkStreamMeta streamMeta) {
+        mStreamMeta = streamMeta;
+    }
+
+    public void setMediaMeta(IjkMediaMeta.IjkStreamMeta streamMeta) {
+        mStreamMeta = streamMeta;
+    }
+
+    @Override
+    public IMediaFormat getFormat() {
+        return new IjkMediaFormat(mStreamMeta);
+    }
+
+    @Override
+    public String getLanguage() {
+        if (mStreamMeta == null || TextUtils.isEmpty(mStreamMeta.mLanguage))
+            return "und";
+
+        return mStreamMeta.mLanguage;
+    }
+
+    @Override
+    public int getTrackType() {
+        return mTrackType;
+    }
+
+    public void setTrackType(int trackType) {
+        mTrackType = trackType;
+    }
+
+    @Override
+    public String toString() {
+        return getClass().getSimpleName() + '{' + getInfoInline() + "}";
+    }
+
+    @Override
+    public String getInfoInline() {
+        StringBuilder out = new StringBuilder(128);
+        switch (mTrackType) {
+            case MEDIA_TRACK_TYPE_VIDEO:
+                out.append("VIDEO");
+                out.append(", ");
+                out.append(mStreamMeta.getCodecShortNameInline());
+                out.append(", ");
+                out.append(mStreamMeta.getBitrateInline());
+                out.append(", ");
+                out.append(mStreamMeta.getResolutionInline());
+                break;
+            case MEDIA_TRACK_TYPE_AUDIO:
+                out.append("AUDIO");
+                out.append(", ");
+                out.append(mStreamMeta.getCodecShortNameInline());
+                out.append(", ");
+                out.append(mStreamMeta.getBitrateInline());
+                out.append(", ");
+                out.append(mStreamMeta.getSampleRateInline());
+                break;
+            case MEDIA_TRACK_TYPE_TIMEDTEXT:
+                out.append("TIMEDTEXT");
+                out.append(", ");
+                out.append(mStreamMeta.mLanguage);
+                break;
+            case MEDIA_TRACK_TYPE_SUBTITLE:
+                out.append("SUBTITLE");
+                break;
+            default:
+                out.append("UNKNOWN");
+                break;
+        }
+        return out.toString();
+    }
+}

+ 143 - 0
ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/pragma/DebugLog.java

@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2013 Bilibili
+ * Copyright (C) 2013 Zhang Rui <bbcallen@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package tv.danmaku.ijk.media.player.pragma;
+
+import java.util.Locale;
+
+
+import android.util.Log;
+
+@SuppressWarnings({"SameParameterValue", "WeakerAccess"})
+public class DebugLog {
+    public static final boolean ENABLE_ERROR = Pragma.ENABLE_VERBOSE;
+    public static final boolean ENABLE_INFO = Pragma.ENABLE_VERBOSE;
+    public static final boolean ENABLE_WARN = Pragma.ENABLE_VERBOSE;
+    public static final boolean ENABLE_DEBUG = Pragma.ENABLE_VERBOSE;
+    public static final boolean ENABLE_VERBOSE = Pragma.ENABLE_VERBOSE;
+
+    public static void e(String tag, String msg) {
+        if (ENABLE_ERROR) {
+            Log.e(tag, msg);
+        }
+    }
+
+    public static void e(String tag, String msg, Throwable tr) {
+        if (ENABLE_ERROR) {
+            Log.e(tag, msg, tr);
+        }
+    }
+
+    public static void efmt(String tag, String fmt, Object... args) {
+        if (ENABLE_ERROR) {
+            String msg = String.format(Locale.US, fmt, args);
+            Log.e(tag, msg);
+        }
+    }
+
+    public static void i(String tag, String msg) {
+        if (ENABLE_INFO) {
+            Log.i(tag, msg);
+        }
+    }
+
+    public static void i(String tag, String msg, Throwable tr) {
+        if (ENABLE_INFO) {
+            Log.i(tag, msg, tr);
+        }
+    }
+
+    public static void ifmt(String tag, String fmt, Object... args) {
+        if (ENABLE_INFO) {
+            String msg = String.format(Locale.US, fmt, args);
+            Log.i(tag, msg);
+        }
+    }
+
+    public static void w(String tag, String msg) {
+        if (ENABLE_WARN) {
+            Log.w(tag, msg);
+        }
+    }
+
+    public static void w(String tag, String msg, Throwable tr) {
+        if (ENABLE_WARN) {
+            Log.w(tag, msg, tr);
+        }
+    }
+
+    public static void wfmt(String tag, String fmt, Object... args) {
+        if (ENABLE_WARN) {
+            String msg = String.format(Locale.US, fmt, args);
+            Log.w(tag, msg);
+        }
+    }
+
+    public static void d(String tag, String msg) {
+        if (ENABLE_DEBUG) {
+            Log.d(tag, msg);
+        }
+    }
+
+    public static void d(String tag, String msg, Throwable tr) {
+        if (ENABLE_DEBUG) {
+            Log.d(tag, msg, tr);
+        }
+    }
+
+    public static void dfmt(String tag, String fmt, Object... args) {
+        if (ENABLE_DEBUG) {
+            String msg = String.format(Locale.US, fmt, args);
+            Log.d(tag, msg);
+        }
+    }
+
+    public static void v(String tag, String msg) {
+        if (ENABLE_VERBOSE) {
+            Log.v(tag, msg);
+        }
+    }
+
+    public static void v(String tag, String msg, Throwable tr) {
+        if (ENABLE_VERBOSE) {
+            Log.v(tag, msg, tr);
+        }
+    }
+
+    public static void vfmt(String tag, String fmt, Object... args) {
+        if (ENABLE_VERBOSE) {
+            String msg = String.format(Locale.US, fmt, args);
+            Log.v(tag, msg);
+        }
+    }
+
+    public static void printStackTrace(Throwable e) {
+        if (ENABLE_WARN) {
+            e.printStackTrace();
+        }
+    }
+
+    public static void printCause(Throwable e) {
+        if (ENABLE_WARN) {
+            Throwable cause = e.getCause();
+            if (cause != null)
+                e = cause;
+
+            printStackTrace(e);
+        }
+    }
+}

+ 24 - 0
ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/pragma/Pragma.java

@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2013 Bilibili
+ * Copyright (C) 2013 Zhang Rui <bbcallen@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package tv.danmaku.ijk.media.player.pragma;
+
+/*-
+ * configurated by app project
+ */
+public class Pragma {
+    public static final boolean ENABLE_VERBOSE = true;
+}

+ 15 - 0
ijkplayer-java/src/main/project.properties

@@ -0,0 +1,15 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-22
+android.library=true

+ 6 - 0
ijkplayer-java/src/main/res/values/strings.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+    <string name="ijkplayer_dummy"></string>
+
+</resources>

+ 11 - 6
ijkvideo/src/main/java/com/haochuan/ijk/IjkVideoPlayer.java

@@ -29,11 +29,7 @@ public class IjkVideoPlayer extends BaseMediaPlayer {
     protected boolean mHadPrepared = false;                 //Prepared
     private int startTime = 0;//播放器开始的时间,单位毫秒
 
-
-
-
     //ijk player播放器全局参数
-
     public IjkVideoPlayer(@NonNull Context context) {
         super(context);
         init(context);
@@ -68,13 +64,22 @@ public class IjkVideoPlayer extends BaseMediaPlayer {
             player.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_loop_filter", 48);
             player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-hevc", 1);*/
 
-            player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 1);      //关闭硬解码
+            /*player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 1);      //关闭硬解码
             player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 1);
             player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "overlay-format", IjkMediaPlayer.SDL_FCC_RV32);
             player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "framedrop", 60);
             player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "max-fps", 0);
             player.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_loop_filter", 48);
-            player.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "rtsp_transport", "tcp");
+            player.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "rtsp_transport", "tcp");*/
+
+            //黑屏解决测试方案
+            player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 1);
+            player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-avc", 1);
+            player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-mpeg2", 1);
+            player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-mpeg4", 1);
+            player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-hevc", 1);
+            player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 1);
+            player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-handle-resolution-change", 1);
         }
     }
 

+ 1 - 0
systemvideo/build.gradle

@@ -36,4 +36,5 @@ dependencies {
     androidTestImplementation lib.test.runner
     androidTestImplementation lib.test.espresso
     implementation project(path: ':core')
+    compile project(path: ':vrlib')
 }

+ 45 - 2
systemvideo/src/main/java/com/haochuan/systemvideo/SystemVideoPlayer.java

@@ -1,17 +1,24 @@
 package com.haochuan.systemvideo;
+import android.app.Activity;
 import android.content.Context;
 import android.media.MediaPlayer;
 import android.util.AttributeSet;
+import android.view.Gravity;
 import android.view.Surface;
 import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
 import android.widget.VideoView;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import com.asha.vrlib.MDVRLibrary;
+import com.asha.vrlib.common.VRUtil;
 import com.haochuan.core.BaseMediaPlayer;
 import com.haochuan.core.IVideoPlayer;
 import com.haochuan.core.Logger;
+import com.haochuan.core.util.HandlerUtil;
 import com.haochuan.core.util.MediaStatusCode;
 
 import static com.haochuan.core.util.MessageCode.EXCEPTION_ERROR;
@@ -26,10 +33,13 @@ public class SystemVideoPlayer extends BaseMediaPlayer {
     private int playerStatus = MediaStatusCode.STOP;
     protected boolean mHadPrepared = false;                 //Prepared
     private int startTime = 0;//播放器开始的时间,单位毫秒
+    private Context mContext;
 
     //添加surface参数,以便替换vr模块传递过来的surface
     private Surface surface;
 
+
+
     public SystemVideoPlayer(@NonNull Context context) {
         super(context);
         init(context);
@@ -47,8 +57,9 @@ public class SystemVideoPlayer extends BaseMediaPlayer {
 
     private void init(Context context){
         Logger.d("SystemVideoPlayer,init()");
-        View videoGroup = View.inflate(getContext(), R.layout.layout_system_video, this);
-        videoView = videoGroup.findViewById(R.id.sys_video);
+        this.mContext = context;
+
+        addRenderToViewGroup();
         initVideoView();
         videoView.resume();
     }
@@ -103,6 +114,8 @@ public class SystemVideoPlayer extends BaseMediaPlayer {
         });
     }
 
+
+
     /*-------------------------BaseMediaPlayer继承函数----------------------*/
 
     @Override
@@ -182,6 +195,8 @@ public class SystemVideoPlayer extends BaseMediaPlayer {
         }
         videoView.stopPlayback();
         videoView.suspend();
+        videoView = null;
+        removeRenderToViewGroup();
         mHadPrepared = false;
         playerStatus = MediaStatusCode.STOP;   //暂停中;
         iVideoPlayer.onDestroy();
@@ -264,4 +279,32 @@ public class SystemVideoPlayer extends BaseMediaPlayer {
         }
     }
 
+    public void addRenderToViewGroup(){
+        videoView = new VideoView(mContext);
+        //videoView创建之后,会自动获取焦点,其实不需要,所以要将检点返回给webview
+
+        FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
+        params.gravity = Gravity.CENTER;
+        videoView.setLayoutParams(params);
+
+        if (videoView.getParent() == null) {
+            Activity activity = (Activity)mContext;
+            ViewGroup viewGroup = (ViewGroup)activity.getWindow().getDecorView();
+            viewGroup.addView(videoView, 0);
+        }
+    }
+
+    public void removeRenderToViewGroup(){
+        HandlerUtil.runOnUiThread(()->{
+            Activity activity = (Activity)mContext;
+            if(videoView != null && videoView.getParent() != null){
+                ViewGroup viewGroup = (ViewGroup) activity.getWindow().getDecorView();
+                viewGroup.removeView(videoView);
+            }else{
+                Logger.e(PLAYER_OBJ_NULL,"videoView 不能从父组件移除");
+            }
+        });
+
+    }
+
 }