desc:移植player,播放本地视频
@ -0,0 +1 @@
|
||||
/build
|
@ -0,0 +1,27 @@
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion 26
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
}
|
||||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 26
|
||||
consumerProguardFiles 'proguard-rules.pro'
|
||||
versionCode 38
|
||||
versionName "1.1.17.1124"
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled true
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
buildToolsVersion '28.0.3'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||
}
|
||||
|
@ -0,0 +1,31 @@
|
||||
# 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 *;
|
||||
#}
|
||||
-keepparameternames
|
||||
-renamesourcefileattribute SourceFile
|
||||
-keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,*Annotation*,EnclosingMethod
|
||||
|
||||
-keep class tv.danmaku.ijk.media.widget.media.IjkVideoView {
|
||||
public protected *;
|
||||
}
|
||||
-keep class tv.danmaku.ijk.media.widget.media.IMediaController {
|
||||
public protected *;
|
||||
}
|
||||
|
||||
-keep class tv.danmaku.ijk.media.player.** {
|
||||
*;
|
||||
}
|
@ -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>
|
@ -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>
|
@ -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
|
@ -0,0 +1,4 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="tv.danmaku.ijk.media.player" >
|
||||
|
||||
</manifest>
|
@ -0,0 +1,113 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
|
||||
import tv.danmaku.ijk.media.player.R;
|
||||
|
||||
public class Settings {
|
||||
private Context mAppContext;
|
||||
private SharedPreferences mSharedPreferences;
|
||||
|
||||
public static final int PV_PLAYER__Auto = 0;
|
||||
public static final int PV_PLAYER__AndroidMediaPlayer = 1;
|
||||
public static final int PV_PLAYER__IjkMediaPlayer = 2;
|
||||
public static final int PV_PLAYER__IjkExoMediaPlayer = 3;
|
||||
|
||||
public Settings(Context context) {
|
||||
mAppContext = context.getApplicationContext();
|
||||
mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(mAppContext);
|
||||
}
|
||||
|
||||
public boolean getEnableBackgroundPlay() {
|
||||
String key = mAppContext.getString(R.string.pref_key_enable_background_play);
|
||||
return mSharedPreferences.getBoolean(key, false);
|
||||
}
|
||||
|
||||
public int getPlayer() {
|
||||
String key = mAppContext.getString(R.string.pref_key_player);
|
||||
String value = mSharedPreferences.getString(key, "");
|
||||
try {
|
||||
return Integer.valueOf(value).intValue();
|
||||
} catch (NumberFormatException e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getUsingMediaCodec() {
|
||||
String key = mAppContext.getString(R.string.pref_key_using_media_codec);
|
||||
return mSharedPreferences.getBoolean(key, true);
|
||||
}
|
||||
|
||||
public boolean getUsingMediaCodecAutoRotate() {
|
||||
String key = mAppContext.getString(R.string.pref_key_using_media_codec_auto_rotate);
|
||||
return mSharedPreferences.getBoolean(key, false);
|
||||
}
|
||||
|
||||
public boolean getMediaCodecHandleResolutionChange() {
|
||||
String key = mAppContext.getString(R.string.pref_key_media_codec_handle_resolution_change);
|
||||
return mSharedPreferences.getBoolean(key, false);
|
||||
}
|
||||
|
||||
public boolean getUsingOpenSLES() {
|
||||
String key = mAppContext.getString(R.string.pref_key_using_opensl_es);
|
||||
return mSharedPreferences.getBoolean(key, false);
|
||||
}
|
||||
|
||||
public String getPixelFormat() {
|
||||
String key = mAppContext.getString(R.string.pref_key_pixel_format);
|
||||
return mSharedPreferences.getString(key, "");
|
||||
}
|
||||
|
||||
public boolean getEnableNoView() {
|
||||
String key = mAppContext.getString(R.string.pref_key_enable_no_view);
|
||||
return mSharedPreferences.getBoolean(key, false);
|
||||
}
|
||||
|
||||
public boolean getEnableSurfaceView() {
|
||||
String key = mAppContext.getString(R.string.pref_key_enable_surface_view);
|
||||
return mSharedPreferences.getBoolean(key, true);
|
||||
}
|
||||
|
||||
public boolean getEnableTextureView() {
|
||||
String key = mAppContext.getString(R.string.pref_key_enable_texture_view);
|
||||
return mSharedPreferences.getBoolean(key, true);
|
||||
}
|
||||
|
||||
public boolean getEnableDetachedSurfaceTextureView() {
|
||||
String key = mAppContext.getString(R.string.pref_key_enable_detached_surface_texture);
|
||||
return mSharedPreferences.getBoolean(key, false);
|
||||
}
|
||||
|
||||
public boolean getUsingMediaDataSource() {
|
||||
String key = mAppContext.getString(R.string.pref_key_using_mediadatasource);
|
||||
return mSharedPreferences.getBoolean(key, false);
|
||||
}
|
||||
|
||||
public String getLastDirectory() {
|
||||
String key = mAppContext.getString(R.string.pref_key_last_directory);
|
||||
return mSharedPreferences.getString(key, "/");
|
||||
}
|
||||
|
||||
public void setLastDirectory(String path) {
|
||||
String key = mAppContext.getString(R.string.pref_key_last_directory);
|
||||
mSharedPreferences.edit().putString(key, path).apply();
|
||||
}
|
||||
}
|
@ -0,0 +1,126 @@
|
||||
/*
|
||||
* 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;
|
||||
protected boolean mCompleted;
|
||||
|
||||
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() {
|
||||
mCompleted = true;
|
||||
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();
|
||||
}
|
||||
|
||||
public boolean isCompleted(){
|
||||
return mCompleted;
|
||||
}
|
||||
}
|
@ -0,0 +1,436 @@
|
||||
/*
|
||||
* 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 record(String path, int seconds) {
|
||||
|
||||
}
|
||||
|
||||
@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 = new IjkTimedText(text.getBounds(), text.getText());
|
||||
notifyOnTimedText(ijkText);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,208 @@
|
||||
/*
|
||||
* 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_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 record(String path, int seconds);
|
||||
|
||||
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);
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
@ -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_TESTED);
|
||||
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 (false && 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";
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
@ -0,0 +1,343 @@
|
||||
/*
|
||||
* 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 record(String path, int seconds) {
|
||||
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package tv.danmaku.ijk.media.player;
|
||||
|
||||
import tv.danmaku.ijk.media.player.annotations.AccessedByNative;
|
||||
|
||||
/**
|
||||
* Created by jiaozebo on 2017/2/23.
|
||||
*/
|
||||
|
||||
public class TxtOverlayAdd {
|
||||
|
||||
@AccessedByNative
|
||||
private long mNativeMediaPlayer;
|
||||
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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 {
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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 "";
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package tv.danmaku.ijk.media.player.ffmpeg;
|
||||
|
||||
public class FFmpegApi {
|
||||
public static native String av_base64_encode(byte in[]);
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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();
|
||||
}
|
||||
}
|
@ -0,0 +1,108 @@
|
||||
/*
|
||||
* 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";
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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();
|
||||
}
|
@ -0,0 +1,258 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* 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();
|
||||
}
|
||||
}
|
@ -0,0 +1,142 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.widget.media;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
|
||||
import tv.danmaku.ijk.media.player.misc.IMediaDataSource;
|
||||
|
||||
public class FileMediaDataSource implements IMediaDataSource {
|
||||
private RandomAccessFile mFile;
|
||||
private long mFileSize;
|
||||
|
||||
public FileMediaDataSource(File file) throws IOException {
|
||||
mFile = new RandomAccessFile(file, "r");
|
||||
mFileSize = mFile.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readAt(long position, byte[] buffer, int offset, int size) throws IOException {
|
||||
if (mFile.getFilePointer() != position)
|
||||
mFile.seek(position);
|
||||
|
||||
if (size == 0)
|
||||
return 0;
|
||||
|
||||
return mFile.read(buffer, 0, size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSize() throws IOException {
|
||||
return mFileSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
mFileSize = 0;
|
||||
mFile.close();
|
||||
mFile = null;
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.widget.media;
|
||||
|
||||
import android.view.View;
|
||||
import android.widget.MediaController;
|
||||
|
||||
public interface IMediaController {
|
||||
void hide();
|
||||
|
||||
boolean isShowing();
|
||||
|
||||
void setAnchorView(View view);
|
||||
|
||||
void setEnabled(boolean enabled);
|
||||
|
||||
void setMediaPlayer(MediaController.MediaPlayerControl player);
|
||||
|
||||
void show(int timeout);
|
||||
|
||||
void show();
|
||||
|
||||
//----------
|
||||
// Extends
|
||||
//----------
|
||||
void showOnce(View view);
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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.widget.media;
|
||||
|
||||
import android.graphics.SurfaceTexture;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.View;
|
||||
|
||||
import tv.danmaku.ijk.media.player.IMediaPlayer;
|
||||
|
||||
public interface IRenderView {
|
||||
int AR_ASPECT_FIT_PARENT = 0; // without clip
|
||||
int AR_ASPECT_FILL_PARENT = 1; // may clip
|
||||
int AR_ASPECT_WRAP_CONTENT = 2;
|
||||
int AR_MATCH_PARENT = 3;
|
||||
int AR_16_9_FIT_PARENT = 4;
|
||||
int AR_4_3_FIT_PARENT = 5;
|
||||
|
||||
int AR_1_1 = 6;
|
||||
|
||||
View getView();
|
||||
|
||||
boolean shouldWaitForResize();
|
||||
|
||||
void setVideoSize(int videoWidth, int videoHeight);
|
||||
|
||||
void setVideoSampleAspectRatio(int videoSarNum, int videoSarDen);
|
||||
|
||||
void setVideoRotation(int degree);
|
||||
|
||||
void setAspectRatio(int aspectRatio);
|
||||
|
||||
void addRenderCallback( IRenderCallback callback);
|
||||
|
||||
void removeRenderCallback( IRenderCallback callback);
|
||||
|
||||
interface ISurfaceHolder {
|
||||
void bindToMediaPlayer(IMediaPlayer mp);
|
||||
|
||||
IRenderView getRenderView();
|
||||
|
||||
SurfaceHolder getSurfaceHolder();
|
||||
|
||||
Surface openSurface();
|
||||
|
||||
SurfaceTexture getSurfaceTexture();
|
||||
}
|
||||
|
||||
interface IRenderCallback {
|
||||
/**
|
||||
* @param holder
|
||||
* @param width could be 0
|
||||
* @param height could be 0
|
||||
*/
|
||||
void onSurfaceCreated(ISurfaceHolder holder, int width, int height);
|
||||
|
||||
/**
|
||||
* @param holder
|
||||
* @param format could be 0
|
||||
* @param width
|
||||
* @param height
|
||||
*/
|
||||
void onSurfaceChanged( ISurfaceHolder holder, int format, int width, int height);
|
||||
|
||||
void onSurfaceDestroyed( ISurfaceHolder holder);
|
||||
}
|
||||
}
|
@ -0,0 +1,251 @@
|
||||
/*
|
||||
* 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.widget.media;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
import tv.danmaku.ijk.media.player.R;
|
||||
|
||||
|
||||
public final class MeasureHelper {
|
||||
private WeakReference<View> mWeakView;
|
||||
|
||||
private int mVideoWidth;
|
||||
private int mVideoHeight;
|
||||
private int mVideoSarNum;
|
||||
private int mVideoSarDen;
|
||||
|
||||
private int mVideoRotationDegree;
|
||||
|
||||
private int mMeasuredWidth;
|
||||
private int mMeasuredHeight;
|
||||
|
||||
private int mCurrentAspectRatio = IRenderView.AR_ASPECT_FIT_PARENT;
|
||||
|
||||
public MeasureHelper(View view) {
|
||||
mWeakView = new WeakReference<View>(view);
|
||||
}
|
||||
|
||||
public View getView() {
|
||||
if (mWeakView == null)
|
||||
return null;
|
||||
return mWeakView.get();
|
||||
}
|
||||
|
||||
public void setVideoSize(int videoWidth, int videoHeight) {
|
||||
mVideoWidth = videoWidth;
|
||||
mVideoHeight = videoHeight;
|
||||
}
|
||||
|
||||
public void setVideoSampleAspectRatio(int videoSarNum, int videoSarDen) {
|
||||
mVideoSarNum = videoSarNum;
|
||||
mVideoSarDen = videoSarDen;
|
||||
}
|
||||
|
||||
public void setVideoRotation(int videoRotationDegree) {
|
||||
mVideoRotationDegree = videoRotationDegree;
|
||||
}
|
||||
|
||||
/**
|
||||
* Must be called by View.onMeasure(int, int)
|
||||
*
|
||||
* @param widthMeasureSpec
|
||||
* @param heightMeasureSpec
|
||||
*/
|
||||
public void doMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
//Log.i("@@@@", "onMeasure(" + MeasureSpec.toString(widthMeasureSpec) + ", "
|
||||
// + MeasureSpec.toString(heightMeasureSpec) + ")");
|
||||
if (mVideoRotationDegree == 90 || mVideoRotationDegree == 270) {
|
||||
int tempSpec = widthMeasureSpec;
|
||||
widthMeasureSpec = heightMeasureSpec;
|
||||
heightMeasureSpec = tempSpec;
|
||||
}
|
||||
|
||||
int width = View.getDefaultSize(mVideoWidth, widthMeasureSpec);
|
||||
int height = View.getDefaultSize(mVideoHeight, heightMeasureSpec);
|
||||
if (mCurrentAspectRatio == IRenderView.AR_MATCH_PARENT) {
|
||||
width = widthMeasureSpec;
|
||||
height = heightMeasureSpec;
|
||||
} else if (mVideoWidth > 0 && mVideoHeight > 0) {
|
||||
int widthSpecMode = View.MeasureSpec.getMode(widthMeasureSpec);
|
||||
int widthSpecSize = View.MeasureSpec.getSize(widthMeasureSpec);
|
||||
int heightSpecMode = View.MeasureSpec.getMode(heightMeasureSpec);
|
||||
int heightSpecSize = View.MeasureSpec.getSize(heightMeasureSpec);
|
||||
|
||||
if (widthSpecMode == View.MeasureSpec.AT_MOST && heightSpecMode == View.MeasureSpec.AT_MOST) {
|
||||
float specAspectRatio = (float) widthSpecSize / (float) heightSpecSize;
|
||||
float displayAspectRatio;
|
||||
switch (mCurrentAspectRatio) {
|
||||
case IRenderView.AR_16_9_FIT_PARENT:
|
||||
displayAspectRatio = 16.0f / 9.0f;
|
||||
if (mVideoRotationDegree == 90 || mVideoRotationDegree == 270)
|
||||
displayAspectRatio = 1.0f / displayAspectRatio;
|
||||
break;
|
||||
case IRenderView.AR_4_3_FIT_PARENT:
|
||||
displayAspectRatio = 4.0f / 3.0f;
|
||||
if (mVideoRotationDegree == 90 || mVideoRotationDegree == 270)
|
||||
displayAspectRatio = 1.0f / displayAspectRatio;
|
||||
break;
|
||||
case IRenderView.AR_ASPECT_FIT_PARENT:
|
||||
case IRenderView.AR_ASPECT_FILL_PARENT:
|
||||
case IRenderView.AR_ASPECT_WRAP_CONTENT:
|
||||
default:
|
||||
displayAspectRatio = (float) mVideoWidth / (float) mVideoHeight;
|
||||
if (mVideoSarNum > 0 && mVideoSarDen > 0)
|
||||
displayAspectRatio = displayAspectRatio * mVideoSarNum / mVideoSarDen;
|
||||
break;
|
||||
}
|
||||
boolean shouldBeWider = displayAspectRatio > specAspectRatio;
|
||||
|
||||
switch (mCurrentAspectRatio) {
|
||||
case IRenderView.AR_ASPECT_FIT_PARENT:
|
||||
case IRenderView.AR_16_9_FIT_PARENT:
|
||||
case IRenderView.AR_4_3_FIT_PARENT:
|
||||
if (shouldBeWider) {
|
||||
// too wide, fix width
|
||||
width = widthSpecSize;
|
||||
height = (int) (width / displayAspectRatio);
|
||||
} else {
|
||||
// too high, fix height
|
||||
height = heightSpecSize;
|
||||
width = (int) (height * displayAspectRatio);
|
||||
}
|
||||
break;
|
||||
case IRenderView.AR_ASPECT_FILL_PARENT:
|
||||
if (shouldBeWider) {
|
||||
// not high enough, fix height
|
||||
height = heightSpecSize;
|
||||
width = (int) (height * displayAspectRatio);
|
||||
} else {
|
||||
// not wide enough, fix width
|
||||
width = widthSpecSize;
|
||||
height = (int) (width / displayAspectRatio);
|
||||
}
|
||||
break;
|
||||
case IRenderView.AR_1_1:
|
||||
width = height = 1;
|
||||
break;
|
||||
case IRenderView.AR_ASPECT_WRAP_CONTENT:
|
||||
default:
|
||||
if (shouldBeWider) {
|
||||
// too wide, fix width
|
||||
width = Math.min(mVideoWidth, widthSpecSize);
|
||||
height = (int) (width / displayAspectRatio);
|
||||
} else {
|
||||
// too high, fix height
|
||||
height = Math.min(mVideoHeight, heightSpecSize);
|
||||
width = (int) (height * displayAspectRatio);
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else if (widthSpecMode == View.MeasureSpec.EXACTLY && heightSpecMode == View.MeasureSpec.EXACTLY) {
|
||||
// the size is fixed
|
||||
width = widthSpecSize;
|
||||
height = heightSpecSize;
|
||||
|
||||
// for compatibility, we adjust size based on aspect ratio
|
||||
if (mVideoWidth * height < width * mVideoHeight) {
|
||||
//Log.i("@@@", "image too wide, correcting");
|
||||
width = height * mVideoWidth / mVideoHeight;
|
||||
} else if (mVideoWidth * height > width * mVideoHeight) {
|
||||
//Log.i("@@@", "image too tall, correcting");
|
||||
height = width * mVideoHeight / mVideoWidth;
|
||||
}
|
||||
} else if (widthSpecMode == View.MeasureSpec.EXACTLY) {
|
||||
// only the width is fixed, adjust the height to match aspect ratio if possible
|
||||
width = widthSpecSize;
|
||||
height = width * mVideoHeight / mVideoWidth;
|
||||
if (heightSpecMode == View.MeasureSpec.AT_MOST && height > heightSpecSize) {
|
||||
// couldn't match aspect ratio within the constraints
|
||||
height = heightSpecSize;
|
||||
}
|
||||
} else if (heightSpecMode == View.MeasureSpec.EXACTLY) {
|
||||
// only the height is fixed, adjust the width to match aspect ratio if possible
|
||||
height = heightSpecSize;
|
||||
width = height * mVideoWidth / mVideoHeight;
|
||||
if (widthSpecMode == View.MeasureSpec.AT_MOST && width > widthSpecSize) {
|
||||
// couldn't match aspect ratio within the constraints
|
||||
width = widthSpecSize;
|
||||
}
|
||||
} else {
|
||||
// neither the width nor the height are fixed, try to use actual video size
|
||||
width = mVideoWidth;
|
||||
height = mVideoHeight;
|
||||
if (heightSpecMode == View.MeasureSpec.AT_MOST && height > heightSpecSize) {
|
||||
// too tall, decrease both width and height
|
||||
height = heightSpecSize;
|
||||
width = height * mVideoWidth / mVideoHeight;
|
||||
}
|
||||
if (widthSpecMode == View.MeasureSpec.AT_MOST && width > widthSpecSize) {
|
||||
// too wide, decrease both width and height
|
||||
width = widthSpecSize;
|
||||
height = width * mVideoHeight / mVideoWidth;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// no size yet, just adopt the given spec sizes
|
||||
}
|
||||
|
||||
mMeasuredWidth = width;
|
||||
mMeasuredHeight = height;
|
||||
}
|
||||
|
||||
public int getMeasuredWidth() {
|
||||
return mMeasuredWidth;
|
||||
}
|
||||
|
||||
public int getMeasuredHeight() {
|
||||
return mMeasuredHeight;
|
||||
}
|
||||
|
||||
public void setAspectRatio(int aspectRatio) {
|
||||
mCurrentAspectRatio = aspectRatio;
|
||||
}
|
||||
|
||||
|
||||
public static String getAspectRatioText(Context context, int aspectRatio) {
|
||||
String text;
|
||||
switch (aspectRatio) {
|
||||
case IRenderView.AR_ASPECT_FIT_PARENT:
|
||||
text = context.getString(R.string.VideoView_ar_aspect_fit_parent);
|
||||
break;
|
||||
case IRenderView.AR_ASPECT_FILL_PARENT:
|
||||
text = context.getString(R.string.VideoView_ar_aspect_fill_parent);
|
||||
break;
|
||||
case IRenderView.AR_ASPECT_WRAP_CONTENT:
|
||||
text = context.getString(R.string.VideoView_ar_aspect_wrap_content);
|
||||
break;
|
||||
case IRenderView.AR_MATCH_PARENT:
|
||||
text = context.getString(R.string.VideoView_ar_match_parent);
|
||||
break;
|
||||
case IRenderView.AR_16_9_FIT_PARENT:
|
||||
text = context.getString(R.string.VideoView_ar_16_9_fit_parent);
|
||||
break;
|
||||
case IRenderView.AR_4_3_FIT_PARENT:
|
||||
text = context.getString(R.string.VideoView_ar_4_3_fit_parent);
|
||||
break;
|
||||
default:
|
||||
text = context.getString(R.string.N_A);
|
||||
break;
|
||||
}
|
||||
return text;
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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.widget.media;
|
||||
|
||||
import tv.danmaku.ijk.media.player.IMediaPlayer;
|
||||
import tv.danmaku.ijk.media.player.IjkMediaPlayer;
|
||||
import tv.danmaku.ijk.media.player.MediaPlayerProxy;
|
||||
import tv.danmaku.ijk.media.player.TextureMediaPlayer;
|
||||
|
||||
public class MediaPlayerCompat {
|
||||
public static String getName(IMediaPlayer mp) {
|
||||
if (mp == null) {
|
||||
return "null";
|
||||
} else if (mp instanceof TextureMediaPlayer) {
|
||||
StringBuilder sb = new StringBuilder("TextureMediaPlayer <");
|
||||
IMediaPlayer internalMediaPlayer = ((TextureMediaPlayer) mp).getInternalMediaPlayer();
|
||||
if (internalMediaPlayer == null) {
|
||||
sb.append("null>");
|
||||
} else {
|
||||
sb.append(internalMediaPlayer.getClass().getSimpleName());
|
||||
sb.append(">");
|
||||
}
|
||||
return sb.toString();
|
||||
} else {
|
||||
return mp.getClass().getSimpleName();
|
||||
}
|
||||
}
|
||||
|
||||
public static IjkMediaPlayer getIjkMediaPlayer(IMediaPlayer mp) {
|
||||
IjkMediaPlayer ijkMediaPlayer = null;
|
||||
if (mp == null) {
|
||||
return null;
|
||||
} if (mp instanceof IjkMediaPlayer) {
|
||||
ijkMediaPlayer = (IjkMediaPlayer) mp;
|
||||
} else if (mp instanceof MediaPlayerProxy && ((MediaPlayerProxy) mp).getInternalMediaPlayer() instanceof IjkMediaPlayer) {
|
||||
ijkMediaPlayer = (IjkMediaPlayer) ((MediaPlayerProxy) mp).getInternalMediaPlayer();
|
||||
}
|
||||
return ijkMediaPlayer;
|
||||
}
|
||||
|
||||
public static void selectTrack(IMediaPlayer mp, int stream) {
|
||||
IjkMediaPlayer ijkMediaPlayer = getIjkMediaPlayer(mp);
|
||||
if (ijkMediaPlayer == null)
|
||||
return;
|
||||
ijkMediaPlayer.selectTrack(stream);
|
||||
}
|
||||
|
||||
public static void deselectTrack(IMediaPlayer mp, int stream) {
|
||||
IjkMediaPlayer ijkMediaPlayer = getIjkMediaPlayer(mp);
|
||||
if (ijkMediaPlayer == null)
|
||||
return;
|
||||
ijkMediaPlayer.deselectTrack(stream);
|
||||
}
|
||||
|
||||
public static int getSelectedTrack(IMediaPlayer mp, int trackType) {
|
||||
IjkMediaPlayer ijkMediaPlayer = getIjkMediaPlayer(mp);
|
||||
if (ijkMediaPlayer == null)
|
||||
return -1;
|
||||
return ijkMediaPlayer.getSelectedTrack(trackType);
|
||||
}
|
||||
}
|
@ -0,0 +1,282 @@
|
||||
/*
|
||||
* 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.widget.media;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.graphics.SurfaceTexture;
|
||||
import android.os.Build;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.View;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import tv.danmaku.ijk.media.player.IMediaPlayer;
|
||||
import tv.danmaku.ijk.media.player.ISurfaceTextureHolder;
|
||||
import tv.danmaku.ijk.media.widget.media.IRenderView;
|
||||
|
||||
public class SurfaceRenderView extends SurfaceView implements IRenderView {
|
||||
private MeasureHelper mMeasureHelper;
|
||||
|
||||
public SurfaceRenderView(Context context) {
|
||||
super(context);
|
||||
initView(context);
|
||||
}
|
||||
|
||||
public SurfaceRenderView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
initView(context);
|
||||
}
|
||||
|
||||
public SurfaceRenderView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
initView(context);
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
public SurfaceRenderView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
initView(context);
|
||||
}
|
||||
|
||||
private void initView(Context context) {
|
||||
mMeasureHelper = new MeasureHelper(this);
|
||||
mSurfaceCallback = new SurfaceCallback(this);
|
||||
getHolder().addCallback(mSurfaceCallback);
|
||||
//noinspection deprecation
|
||||
getHolder().setType(SurfaceHolder.SURFACE_TYPE_NORMAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldWaitForResize() {
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------
|
||||
// Layout & Measure
|
||||
//--------------------
|
||||
@Override
|
||||
public void setVideoSize(int videoWidth, int videoHeight) {
|
||||
if (videoWidth > 0 && videoHeight > 0) {
|
||||
mMeasureHelper.setVideoSize(videoWidth, videoHeight);
|
||||
getHolder().setFixedSize(videoWidth, videoHeight);
|
||||
requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVideoSampleAspectRatio(int videoSarNum, int videoSarDen) {
|
||||
if (videoSarNum > 0 && videoSarDen > 0) {
|
||||
mMeasureHelper.setVideoSampleAspectRatio(videoSarNum, videoSarDen);
|
||||
requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVideoRotation(int degree) {
|
||||
Log.e("", "SurfaceView doesn't support rotation (" + degree + ")!\n");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAspectRatio(int aspectRatio) {
|
||||
mMeasureHelper.setAspectRatio(aspectRatio);
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
mMeasureHelper.doMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
setMeasuredDimension(mMeasureHelper.getMeasuredWidth(), mMeasureHelper.getMeasuredHeight());
|
||||
}
|
||||
|
||||
//--------------------
|
||||
// SurfaceViewHolder
|
||||
//--------------------
|
||||
|
||||
private static final class InternalSurfaceHolder implements IRenderView.ISurfaceHolder {
|
||||
private SurfaceRenderView mSurfaceView;
|
||||
private SurfaceHolder mSurfaceHolder;
|
||||
|
||||
public InternalSurfaceHolder( SurfaceRenderView surfaceView,
|
||||
SurfaceHolder surfaceHolder) {
|
||||
mSurfaceView = surfaceView;
|
||||
mSurfaceHolder = surfaceHolder;
|
||||
}
|
||||
|
||||
public void bindToMediaPlayer(IMediaPlayer mp) {
|
||||
if (mp != null) {
|
||||
if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) &&
|
||||
(mp instanceof ISurfaceTextureHolder)) {
|
||||
ISurfaceTextureHolder textureHolder = (ISurfaceTextureHolder) mp;
|
||||
textureHolder.setSurfaceTexture(null);
|
||||
}
|
||||
mp.setDisplay(mSurfaceHolder);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IRenderView getRenderView() {
|
||||
return mSurfaceView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SurfaceHolder getSurfaceHolder() {
|
||||
return mSurfaceHolder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SurfaceTexture getSurfaceTexture() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Surface openSurface() {
|
||||
if (mSurfaceHolder == null)
|
||||
return null;
|
||||
return mSurfaceHolder.getSurface();
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------
|
||||
// SurfaceHolder.Callback
|
||||
//-------------------------
|
||||
|
||||
@Override
|
||||
public void addRenderCallback(IRenderCallback callback) {
|
||||
mSurfaceCallback.addRenderCallback(callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeRenderCallback(IRenderCallback callback) {
|
||||
mSurfaceCallback.removeRenderCallback(callback);
|
||||
}
|
||||
|
||||
private SurfaceCallback mSurfaceCallback;
|
||||
|
||||
private static final class SurfaceCallback implements SurfaceHolder.Callback {
|
||||
private SurfaceHolder mSurfaceHolder;
|
||||
private boolean mIsFormatChanged;
|
||||
private int mFormat;
|
||||
private int mWidth;
|
||||
private int mHeight;
|
||||
|
||||
private WeakReference<SurfaceRenderView> mWeakSurfaceView;
|
||||
private Map<IRenderCallback, Object> mRenderCallbackMap = new ConcurrentHashMap<IRenderCallback, Object>();
|
||||
|
||||
public SurfaceCallback(SurfaceRenderView surfaceView) {
|
||||
mWeakSurfaceView = new WeakReference<SurfaceRenderView>(surfaceView);
|
||||
}
|
||||
|
||||
public void addRenderCallback(IRenderCallback callback) {
|
||||
mRenderCallbackMap.put(callback, callback);
|
||||
|
||||
ISurfaceHolder surfaceHolder = null;
|
||||
if (mSurfaceHolder != null) {
|
||||
if (surfaceHolder == null)
|
||||
surfaceHolder = new InternalSurfaceHolder(mWeakSurfaceView.get(), mSurfaceHolder);
|
||||
callback.onSurfaceCreated(surfaceHolder, mWidth, mHeight);
|
||||
}
|
||||
|
||||
if (mIsFormatChanged) {
|
||||
if (surfaceHolder == null)
|
||||
surfaceHolder = new InternalSurfaceHolder(mWeakSurfaceView.get(), mSurfaceHolder);
|
||||
callback.onSurfaceChanged(surfaceHolder, mFormat, mWidth, mHeight);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeRenderCallback(IRenderCallback callback) {
|
||||
mRenderCallbackMap.remove(callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceCreated(SurfaceHolder holder) {
|
||||
mSurfaceHolder = holder;
|
||||
mIsFormatChanged = false;
|
||||
mFormat = 0;
|
||||
mWidth = 0;
|
||||
mHeight = 0;
|
||||
|
||||
ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakSurfaceView.get(), mSurfaceHolder);
|
||||
for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) {
|
||||
renderCallback.onSurfaceCreated(surfaceHolder, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceDestroyed(SurfaceHolder holder) {
|
||||
mSurfaceHolder = null;
|
||||
mIsFormatChanged = false;
|
||||
mFormat = 0;
|
||||
mWidth = 0;
|
||||
mHeight = 0;
|
||||
|
||||
ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakSurfaceView.get(), mSurfaceHolder);
|
||||
for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) {
|
||||
renderCallback.onSurfaceDestroyed(surfaceHolder);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceChanged(SurfaceHolder holder, int format,
|
||||
int width, int height) {
|
||||
mSurfaceHolder = holder;
|
||||
mIsFormatChanged = true;
|
||||
mFormat = format;
|
||||
mWidth = width;
|
||||
mHeight = height;
|
||||
|
||||
// mMeasureHelper.setVideoSize(width, height);
|
||||
|
||||
ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakSurfaceView.get(), mSurfaceHolder);
|
||||
for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) {
|
||||
renderCallback.onSurfaceChanged(surfaceHolder, format, width, height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------
|
||||
// Accessibility
|
||||
//--------------------
|
||||
|
||||
@Override
|
||||
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
|
||||
super.onInitializeAccessibilityEvent(event);
|
||||
event.setClassName(SurfaceRenderView.class.getName());
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
|
||||
@Override
|
||||
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
|
||||
super.onInitializeAccessibilityNodeInfo(info);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||
info.setClassName(SurfaceRenderView.class.getName());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,368 @@
|
||||
/*
|
||||
* 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.widget.media;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.SurfaceTexture;
|
||||
import android.os.Build;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.TextureView;
|
||||
import android.view.View;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import tv.danmaku.ijk.media.player.IMediaPlayer;
|
||||
import tv.danmaku.ijk.media.player.ISurfaceTextureHolder;
|
||||
import tv.danmaku.ijk.media.player.ISurfaceTextureHost;
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
|
||||
public class TextureRenderView extends TextureView implements IRenderView {
|
||||
private static final String TAG = "TextureRenderView";
|
||||
private MeasureHelper mMeasureHelper;
|
||||
|
||||
public TextureRenderView(Context context) {
|
||||
super(context);
|
||||
initView(context);
|
||||
}
|
||||
|
||||
public TextureRenderView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
initView(context);
|
||||
}
|
||||
|
||||
public TextureRenderView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
initView(context);
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
public TextureRenderView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
initView(context);
|
||||
}
|
||||
|
||||
private void initView(Context context) {
|
||||
mMeasureHelper = new MeasureHelper(this);
|
||||
mSurfaceCallback = new SurfaceCallback(this);
|
||||
setSurfaceTextureListener(mSurfaceCallback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldWaitForResize() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
mSurfaceCallback.willDetachFromWindow();
|
||||
super.onDetachedFromWindow();
|
||||
mSurfaceCallback.didDetachFromWindow();
|
||||
}
|
||||
|
||||
//--------------------
|
||||
// Layout & Measure
|
||||
//--------------------
|
||||
@Override
|
||||
public void setVideoSize(int videoWidth, int videoHeight) {
|
||||
if (videoWidth > 0 && videoHeight > 0) {
|
||||
mMeasureHelper.setVideoSize(videoWidth, videoHeight);
|
||||
requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVideoSampleAspectRatio(int videoSarNum, int videoSarDen) {
|
||||
if (videoSarNum > 0 && videoSarDen > 0) {
|
||||
mMeasureHelper.setVideoSampleAspectRatio(videoSarNum, videoSarDen);
|
||||
requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVideoRotation(int degree) {
|
||||
mMeasureHelper.setVideoRotation(degree);
|
||||
setRotation(degree);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAspectRatio(int aspectRatio) {
|
||||
mMeasureHelper.setAspectRatio(aspectRatio);
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
mMeasureHelper.doMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
setMeasuredDimension(mMeasureHelper.getMeasuredWidth(), mMeasureHelper.getMeasuredHeight());
|
||||
}
|
||||
|
||||
//--------------------
|
||||
// TextureViewHolder
|
||||
//--------------------
|
||||
|
||||
public IRenderView.ISurfaceHolder getSurfaceHolder() {
|
||||
return new InternalSurfaceHolder(this, mSurfaceCallback.mSurfaceTexture, mSurfaceCallback);
|
||||
}
|
||||
|
||||
private static final class InternalSurfaceHolder implements IRenderView.ISurfaceHolder {
|
||||
private TextureRenderView mTextureView;
|
||||
private SurfaceTexture mSurfaceTexture;
|
||||
private ISurfaceTextureHost mSurfaceTextureHost;
|
||||
|
||||
public InternalSurfaceHolder( TextureRenderView textureView,
|
||||
SurfaceTexture surfaceTexture,
|
||||
ISurfaceTextureHost surfaceTextureHost) {
|
||||
mTextureView = textureView;
|
||||
mSurfaceTexture = surfaceTexture;
|
||||
mSurfaceTextureHost = surfaceTextureHost;
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
|
||||
public void bindToMediaPlayer(IMediaPlayer mp) {
|
||||
if (mp == null)
|
||||
return;
|
||||
|
||||
if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) &&
|
||||
(mp instanceof ISurfaceTextureHolder)) {
|
||||
ISurfaceTextureHolder textureHolder = (ISurfaceTextureHolder) mp;
|
||||
mTextureView.mSurfaceCallback.setOwnSurfaceTexture(false);
|
||||
|
||||
SurfaceTexture surfaceTexture = textureHolder.getSurfaceTexture();
|
||||
if (surfaceTexture != null) {
|
||||
mTextureView.setSurfaceTexture(surfaceTexture);
|
||||
} else {
|
||||
textureHolder.setSurfaceTexture(mSurfaceTexture);
|
||||
textureHolder.setSurfaceTextureHost(mTextureView.mSurfaceCallback);
|
||||
}
|
||||
} else {
|
||||
mp.setSurface(openSurface());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public IRenderView getRenderView() {
|
||||
return mTextureView;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public SurfaceHolder getSurfaceHolder() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public SurfaceTexture getSurfaceTexture() {
|
||||
return mSurfaceTexture;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Surface openSurface() {
|
||||
if (mSurfaceTexture == null)
|
||||
return null;
|
||||
return new Surface(mSurfaceTexture);
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------
|
||||
// SurfaceHolder.Callback
|
||||
//-------------------------
|
||||
|
||||
@Override
|
||||
public void addRenderCallback(IRenderCallback callback) {
|
||||
mSurfaceCallback.addRenderCallback(callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeRenderCallback(IRenderCallback callback) {
|
||||
mSurfaceCallback.removeRenderCallback(callback);
|
||||
}
|
||||
|
||||
private SurfaceCallback mSurfaceCallback;
|
||||
|
||||
private static final class SurfaceCallback implements SurfaceTextureListener, ISurfaceTextureHost {
|
||||
private SurfaceTexture mSurfaceTexture;
|
||||
private boolean mIsFormatChanged;
|
||||
private int mWidth;
|
||||
private int mHeight;
|
||||
|
||||
private boolean mOwnSurfaceTexture = true;
|
||||
private boolean mWillDetachFromWindow = false;
|
||||
private boolean mDidDetachFromWindow = false;
|
||||
|
||||
private WeakReference<TextureRenderView> mWeakRenderView;
|
||||
private Map<IRenderCallback, Object> mRenderCallbackMap = new ConcurrentHashMap<IRenderCallback, Object>();
|
||||
|
||||
public SurfaceCallback( TextureRenderView renderView) {
|
||||
mWeakRenderView = new WeakReference<TextureRenderView>(renderView);
|
||||
}
|
||||
|
||||
public void setOwnSurfaceTexture(boolean ownSurfaceTexture) {
|
||||
mOwnSurfaceTexture = ownSurfaceTexture;
|
||||
}
|
||||
|
||||
public void addRenderCallback( IRenderCallback callback) {
|
||||
mRenderCallbackMap.put(callback, callback);
|
||||
|
||||
ISurfaceHolder surfaceHolder = null;
|
||||
if (mSurfaceTexture != null) {
|
||||
if (surfaceHolder == null)
|
||||
surfaceHolder = new InternalSurfaceHolder(mWeakRenderView.get(), mSurfaceTexture, this);
|
||||
callback.onSurfaceCreated(surfaceHolder, mWidth, mHeight);
|
||||
}
|
||||
|
||||
if (mIsFormatChanged) {
|
||||
if (surfaceHolder == null)
|
||||
surfaceHolder = new InternalSurfaceHolder(mWeakRenderView.get(), mSurfaceTexture, this);
|
||||
callback.onSurfaceChanged(surfaceHolder, 0, mWidth, mHeight);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeRenderCallback( IRenderCallback callback) {
|
||||
mRenderCallbackMap.remove(callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
|
||||
mSurfaceTexture = surface;
|
||||
mIsFormatChanged = false;
|
||||
mWidth = 0;
|
||||
mHeight = 0;
|
||||
|
||||
ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakRenderView.get(), surface, this);
|
||||
for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) {
|
||||
renderCallback.onSurfaceCreated(surfaceHolder, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
|
||||
mSurfaceTexture = surface;
|
||||
mIsFormatChanged = true;
|
||||
mWidth = width;
|
||||
mHeight = height;
|
||||
|
||||
ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakRenderView.get(), surface, this);
|
||||
for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) {
|
||||
renderCallback.onSurfaceChanged(surfaceHolder, 0, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
|
||||
mSurfaceTexture = surface;
|
||||
mIsFormatChanged = false;
|
||||
mWidth = 0;
|
||||
mHeight = 0;
|
||||
|
||||
ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakRenderView.get(), surface, this);
|
||||
for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) {
|
||||
renderCallback.onSurfaceDestroyed(surfaceHolder);
|
||||
}
|
||||
|
||||
Log.d(TAG, "onSurfaceTextureDestroyed: destroy: " + mOwnSurfaceTexture);
|
||||
return mOwnSurfaceTexture;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
|
||||
}
|
||||
|
||||
//-------------------------
|
||||
// ISurfaceTextureHost
|
||||
//-------------------------
|
||||
|
||||
@Override
|
||||
public void releaseSurfaceTexture(SurfaceTexture surfaceTexture) {
|
||||
if (surfaceTexture == null) {
|
||||
Log.d(TAG, "releaseSurfaceTexture: null");
|
||||
} else if (mDidDetachFromWindow) {
|
||||
if (surfaceTexture != mSurfaceTexture) {
|
||||
Log.d(TAG, "releaseSurfaceTexture: didDetachFromWindow(): release different SurfaceTexture");
|
||||
surfaceTexture.release();
|
||||
} else if (!mOwnSurfaceTexture) {
|
||||
Log.d(TAG, "releaseSurfaceTexture: didDetachFromWindow(): release detached SurfaceTexture");
|
||||
surfaceTexture.release();
|
||||
} else {
|
||||
Log.d(TAG, "releaseSurfaceTexture: didDetachFromWindow(): already released by TextureView");
|
||||
}
|
||||
} else if (mWillDetachFromWindow) {
|
||||
if (surfaceTexture != mSurfaceTexture) {
|
||||
Log.d(TAG, "releaseSurfaceTexture: willDetachFromWindow(): release different SurfaceTexture");
|
||||
surfaceTexture.release();
|
||||
} else if (!mOwnSurfaceTexture) {
|
||||
Log.d(TAG, "releaseSurfaceTexture: willDetachFromWindow(): re-attach SurfaceTexture to TextureView");
|
||||
setOwnSurfaceTexture(true);
|
||||
} else {
|
||||
Log.d(TAG, "releaseSurfaceTexture: willDetachFromWindow(): will released by TextureView");
|
||||
}
|
||||
} else {
|
||||
if (surfaceTexture != mSurfaceTexture) {
|
||||
Log.d(TAG, "releaseSurfaceTexture: alive: release different SurfaceTexture");
|
||||
surfaceTexture.release();
|
||||
} else if (!mOwnSurfaceTexture) {
|
||||
Log.d(TAG, "releaseSurfaceTexture: alive: re-attach SurfaceTexture to TextureView");
|
||||
setOwnSurfaceTexture(true);
|
||||
} else {
|
||||
Log.d(TAG, "releaseSurfaceTexture: alive: will released by TextureView");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void willDetachFromWindow() {
|
||||
Log.d(TAG, "willDetachFromWindow()");
|
||||
mWillDetachFromWindow = true;
|
||||
}
|
||||
|
||||
public void didDetachFromWindow() {
|
||||
Log.d(TAG, "didDetachFromWindow()");
|
||||
mDidDetachFromWindow = true;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------
|
||||
// Accessibility
|
||||
//--------------------
|
||||
|
||||
@Override
|
||||
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
|
||||
super.onInitializeAccessibilityEvent(event);
|
||||
event.setClassName(TextureRenderView.class.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
|
||||
super.onInitializeAccessibilityNodeInfo(info);
|
||||
info.setClassName(TextureRenderView.class.getName());
|
||||
}
|
||||
}
|
@ -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
|
@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<!-- -->
|
||||
<string name="N_A">N/A</string>
|
||||
|
||||
<string name="TrackType_video">Video</string>
|
||||
<string name="TrackType_audio">Audio</string>
|
||||
<string name="TrackType_subtitle">Subtitle</string>
|
||||
<string name="TrackType_timedtext">Timed text</string>
|
||||
<string name="TrackType_metadata">Meta data</string>
|
||||
<string name="TrackType_unknown">Unknown</string>
|
||||
|
||||
<string name="VideoView_ar_aspect_fit_parent">Aspect / Fit parent</string>
|
||||
<string name="VideoView_ar_aspect_fill_parent">Aspect / Fill parent</string>
|
||||
<string name="VideoView_ar_aspect_wrap_content">Aspect / Wrap content</string>
|
||||
<string name="VideoView_ar_match_parent">Free / Fill parent</string>
|
||||
<string name="VideoView_ar_16_9_fit_parent">16:9 / Fit parent</string>
|
||||
<string name="VideoView_ar_4_3_fit_parent">4:3 / Fit parent</string>
|
||||
|
||||
<string name="VideoView_render_none">Render: None</string>
|
||||
<string name="VideoView_render_surface_view">Render: SurfaceView</string>
|
||||
<string name="VideoView_render_texture_view">Render: TextureView</string>
|
||||
|
||||
<!-- -->
|
||||
|
||||
<string name="pref_key_enable_background_play">pref.enable_background_play</string>
|
||||
|
||||
<string name="pref_key_player">pref.player</string>
|
||||
|
||||
<!-- -->
|
||||
|
||||
<string name="pref_key_using_media_codec">pref.using_media_codec</string>
|
||||
|
||||
<string name="pref_key_using_media_codec_auto_rotate">pref.using_media_codec_auto_rotate</string>
|
||||
|
||||
<string name="pref_key_media_codec_handle_resolution_change">pref.media_codec_handle_resolution_change</string>
|
||||
|
||||
<string name="pref_key_pixel_format">pref.pixel_format</string>
|
||||
|
||||
<!-- -->
|
||||
|
||||
<string name="pref_key_using_opensl_es">pref.using_opensl_es</string>
|
||||
|
||||
<!-- -->
|
||||
|
||||
<string name="pref_key_enable_no_view">pref.enable_no_view</string>
|
||||
|
||||
<string name="pref_key_enable_surface_view">pref.enable_surface_view</string>
|
||||
|
||||
<string name="pref_key_enable_texture_view">pref.enable_texture_view</string>
|
||||
|
||||
<string name="pref_key_enable_detached_surface_texture">pref.enable_detached_surface_texture</string>
|
||||
|
||||
<!-- -->
|
||||
<string name="pref_key_using_mediadatasource">pref.using_mediadatasource</string>
|
||||
|
||||
<!-- -->
|
||||
<string name="pref_key_last_directory"></string>
|
||||
</resources>
|
@ -0,0 +1,470 @@
|
||||
package com.example.myapplication.player;
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Color;
|
||||
import android.media.MediaScannerConnection;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.core.view.ViewConfigurationCompat;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.example.myapplication.BuildConfig;
|
||||
import com.example.myapplication.databinding.ActivityMainProBinding;
|
||||
import com.example.myapplication.player.utils.FileUtil;
|
||||
import com.example.myapplication.player.utils.SPUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
import tv.danmaku.ijk.media.player.IMediaPlayer;
|
||||
import tv.danmaku.ijk.media.player.IjkMediaPlayer;
|
||||
|
||||
public class ProVideoActivity extends AppCompatActivity {
|
||||
private static final String TAG = "ProVideoActivity";
|
||||
|
||||
public static final int REQUEST_WRITE_STORAGE = 111;
|
||||
|
||||
private String mVideoPath;
|
||||
private Uri mVideoUri;
|
||||
|
||||
private ActivityMainProBinding mBinding;
|
||||
private ProVideoView mVideoView; // 播放器View
|
||||
private View mProgress;
|
||||
|
||||
private GestureDetector detector;
|
||||
|
||||
private VideoControllerView mediaController;
|
||||
private MediaScannerConnection mScanner;
|
||||
|
||||
private Runnable mSpeedCalcTask;
|
||||
|
||||
private int mMode; // 画面模式
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
View decorView = getWindow().getDecorView();
|
||||
decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
|
||||
getWindow().setStatusBarColor(Color.TRANSPARENT);
|
||||
}
|
||||
|
||||
mBinding = ActivityMainProBinding.inflate(getLayoutInflater());
|
||||
setContentView(mBinding.getRoot());
|
||||
|
||||
// handle arguments
|
||||
mVideoPath = getIntent().getStringExtra("videoPath");
|
||||
|
||||
String mSnapPath = getIntent().getStringExtra("snapPath");
|
||||
if (TextUtils.isEmpty(mSnapPath)) {
|
||||
Glide.with(this).load(mSnapPath).into(mBinding.surfaceCover);
|
||||
ViewCompat.setTransitionName(mBinding.surfaceCover, "snapCover");
|
||||
}
|
||||
|
||||
Intent intent = getIntent();
|
||||
String intentAction = intent.getAction();
|
||||
if (!TextUtils.isEmpty(intentAction)) {
|
||||
if (intentAction.equals(Intent.ACTION_VIEW)) {
|
||||
mVideoPath = intent.getDataString();
|
||||
} else if (intentAction.equals(Intent.ACTION_SEND)) {
|
||||
mVideoUri = intent.getParcelableExtra(Intent.EXTRA_STREAM);
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||
String scheme = mVideoUri.getScheme();
|
||||
|
||||
if (TextUtils.isEmpty(scheme)) {
|
||||
Log.e(TAG, "Null unknown scheme\n");
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
if (scheme.equals(ContentResolver.SCHEME_ANDROID_RESOURCE)) {
|
||||
mVideoPath = mVideoUri.getPath();
|
||||
} else if (scheme.equals(ContentResolver.SCHEME_CONTENT)) {
|
||||
Log.e(TAG, "Can not resolve content below Android-ICS\n");
|
||||
finish();
|
||||
return;
|
||||
} else {
|
||||
Log.e(TAG, "Unknown scheme " + scheme + "\n");
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SPUtil.setDefaultParams(this);
|
||||
|
||||
if (BuildConfig.DEBUG) {
|
||||
IjkMediaPlayer.native_setLogLevel(IjkMediaPlayer.IJK_LOG_DEBUG);// init player
|
||||
}
|
||||
|
||||
mediaController = new VideoControllerView(this);
|
||||
mediaController.setMediaPlayer(mBinding.videoView);
|
||||
mVideoView = mBinding.videoView;
|
||||
mVideoView.setMediaController(mediaController);
|
||||
|
||||
mProgress = mBinding.progress;
|
||||
mVideoView.setOnInfoListener(new IMediaPlayer.OnInfoListener() {
|
||||
@Override
|
||||
public boolean onInfo(IMediaPlayer iMediaPlayer, int arg1, int arg2) {
|
||||
switch (arg1) {
|
||||
case IMediaPlayer.MEDIA_INFO_VIDEO_TRACK_LAGGING:
|
||||
Log.i(TAG, "MEDIA_INFO_VIDEO_TRACK_LAGGING");
|
||||
break;
|
||||
case IMediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START:
|
||||
Log.i(TAG, "MEDIA_INFO_VIDEO_RENDERING_START");
|
||||
mProgress.setVisibility(View.GONE);
|
||||
mBinding.surfaceCover.setVisibility(View.GONE);
|
||||
mBinding.playerContainer.setVisibility(View.GONE);
|
||||
mBinding.videoView.setVisibility(View.VISIBLE);
|
||||
mBinding.videoView2.setVisibility(View.VISIBLE);
|
||||
|
||||
// 快照
|
||||
File file = FileUtil.getSnapshotFile(mVideoPath);
|
||||
mVideoView.takePicture(file.getPath());
|
||||
break;
|
||||
case IMediaPlayer.MEDIA_INFO_BUFFERING_START:
|
||||
Log.i(TAG, "MEDIA_INFO_BUFFERING_START");
|
||||
break;
|
||||
case IMediaPlayer.MEDIA_INFO_BUFFERING_END:
|
||||
Log.i(TAG, "MEDIA_INFO_BUFFERING_END");
|
||||
break;
|
||||
case IMediaPlayer.MEDIA_INFO_NETWORK_BANDWIDTH:
|
||||
Log.i(TAG, "MEDIA_INFO_NETWORK_BANDWIDTH");
|
||||
break;
|
||||
case IMediaPlayer.MEDIA_INFO_BAD_INTERLEAVING:
|
||||
Log.i(TAG, "MEDIA_INFO_BAD_INTERLEAVING");
|
||||
break;
|
||||
case IMediaPlayer.MEDIA_INFO_NOT_SEEKABLE:
|
||||
Log.i(TAG, "MEDIA_INFO_NOT_SEEKABLE");
|
||||
break;
|
||||
case IMediaPlayer.MEDIA_INFO_METADATA_UPDATE:
|
||||
Log.i(TAG, "MEDIA_INFO_METADATA_UPDATE");
|
||||
break;
|
||||
case IMediaPlayer.MEDIA_INFO_UNSUPPORTED_SUBTITLE:
|
||||
Log.i(TAG, "MEDIA_INFO_UNSUPPORTED_SUBTITLE");
|
||||
break;
|
||||
case IMediaPlayer.MEDIA_INFO_SUBTITLE_TIMED_OUT:
|
||||
Log.i(TAG, "MEDIA_INFO_SUBTITLE_TIMED_OUT");
|
||||
break;
|
||||
case IMediaPlayer.MEDIA_INFO_VIDEO_ROTATION_CHANGED:
|
||||
Log.i(TAG, "MEDIA_INFO_VIDEO_ROTATION_CHANGED");
|
||||
break;
|
||||
case IMediaPlayer.MEDIA_INFO_AUDIO_RENDERING_START:
|
||||
Log.i(TAG, "MEDIA_INFO_AUDIO_RENDERING_START");
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
mVideoView.setOnErrorListener(new IMediaPlayer.OnErrorListener() {
|
||||
@Override
|
||||
public boolean onError(IMediaPlayer iMediaPlayer, int i, int i1) {
|
||||
Log.i(TAG, "播放错误");
|
||||
mBinding.videoView.setVisibility(View.GONE);
|
||||
mBinding.videoView2.setVisibility(View.GONE);
|
||||
mProgress.setVisibility(View.GONE);
|
||||
mBinding.playerContainer.setVisibility(View.VISIBLE);
|
||||
|
||||
mVideoView.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mVideoView.reStart();
|
||||
}
|
||||
}, 5000);
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
mVideoView.setOnCompletionListener(new IMediaPlayer.OnCompletionListener() {
|
||||
@Override
|
||||
public void onCompletion(IMediaPlayer iMediaPlayer) {
|
||||
Log.i(TAG, "播放完成");
|
||||
mProgress.setVisibility(View.GONE);
|
||||
|
||||
if (mVideoPath.toLowerCase().startsWith("rtsp") ||
|
||||
mVideoPath.toLowerCase().startsWith("rtmp") ||
|
||||
mVideoPath.toLowerCase().startsWith("http")) {
|
||||
mVideoView.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mVideoView.reStart();
|
||||
}
|
||||
}, 5000);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mVideoView.setOnPreparedListener(new IMediaPlayer.OnPreparedListener() {
|
||||
@Override
|
||||
public void onPrepared(IMediaPlayer iMediaPlayer) {
|
||||
Log.i(TAG, String.format("onPrepared"));
|
||||
}
|
||||
});
|
||||
|
||||
if (mVideoPath != null) {
|
||||
mVideoView.setVideoPath(mVideoPath);
|
||||
} else if (mVideoUri != null) {
|
||||
mVideoView.setVideoURI(mVideoUri);
|
||||
} else {
|
||||
Log.e(TAG, "Null Data Source\n");
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
mVideoView.start();
|
||||
|
||||
GestureDetector.SimpleOnGestureListener listener = new GestureDetector.SimpleOnGestureListener() {
|
||||
@Override
|
||||
public boolean onSingleTapConfirmed(MotionEvent e) {
|
||||
// if (!isLandscape() && mMode == 3) {
|
||||
// if (mFullScreenMode) {
|
||||
// mMode = mVideoView.toggleAspectRatio();
|
||||
// mBinding.playerContainer.setVisibility(View.VISIBLE);
|
||||
// mFullScreenMode = false;
|
||||
// } else {
|
||||
// mFullScreenMode = true;
|
||||
// mBinding.playerContainer.setVisibility(View.GONE);
|
||||
// }
|
||||
// } else {
|
||||
// mMode = mVideoView.toggleAspectRatio();
|
||||
// }
|
||||
|
||||
if (mVideoView.isInPlaybackState()) {
|
||||
mVideoView.toggleMediaControlsVisibility();
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onDoubleTap(MotionEvent e) {
|
||||
// setRequestedOrientation(isLandscape() ? ActivityInfo.SCREEN_ORIENTATION_PORTRAIT : ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
detector = new GestureDetector(this, listener);
|
||||
|
||||
mVideoView.setOnTouchListener(new View.OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
detector.onTouchEvent(event);
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
mSpeedCalcTask = new Runnable() {
|
||||
private long mReceivedBytes;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
long l = mVideoView.getReceivedBytes();
|
||||
long received = l - mReceivedBytes;
|
||||
mReceivedBytes = l;
|
||||
|
||||
mBinding.loadingSpeed.setText(String.format("%3.01fKB/s", received * 1.0f / 1024));
|
||||
|
||||
if (mProgress.getVisibility() == View.VISIBLE){
|
||||
mVideoView.postDelayed(this,1000);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
mVideoView.post(mSpeedCalcTask);
|
||||
|
||||
if (BuildConfig.DEBUG) {
|
||||
mBinding.videoView2.setVideoPath("rtmp://13088.liveplay.myqcloud.com/live/13088_65829b3d3e");
|
||||
mBinding.videoView2.setShowing(false);
|
||||
mBinding.videoView2.start();
|
||||
}
|
||||
}
|
||||
|
||||
// @Override
|
||||
// protected void onResume() {
|
||||
// super.onResume();
|
||||
//
|
||||
// if (mVideoView != null) {
|
||||
// mVideoView.postDelayed(new Runnable() {
|
||||
// @Override
|
||||
// public void run() {
|
||||
// mVideoView.reStart();
|
||||
// }
|
||||
// }, 5000);
|
||||
// }
|
||||
// }
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
|
||||
mVideoView.stopPlayback();
|
||||
|
||||
if (BuildConfig.DEBUG) {
|
||||
mBinding.videoView2.stopPlayback();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
if (mScanner != null) {
|
||||
mScanner.disconnect();
|
||||
mScanner = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
if (REQUEST_WRITE_STORAGE == requestCode){
|
||||
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
doTakePicture();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
LinearLayout container = mBinding.playerContainer;
|
||||
|
||||
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
setNavVisibility(false);
|
||||
// 横屏情况 播放窗口横着排开
|
||||
container.setOrientation(LinearLayout.HORIZONTAL);
|
||||
} else {
|
||||
// 竖屏,取消全屏状态
|
||||
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
setNavVisibility(true);
|
||||
// 竖屏情况 播放窗口竖着排开
|
||||
container.setOrientation(LinearLayout.VERTICAL);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isLandscape() {
|
||||
int orientation = getResources().getConfiguration().orientation;
|
||||
return orientation == ORIENTATION_LANDSCAPE;
|
||||
}
|
||||
|
||||
public void setNavVisibility(boolean visible) {
|
||||
if (!ViewConfigurationCompat.hasPermanentMenuKey(ViewConfiguration.get(this))) {
|
||||
int newVis = View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
|
||||
|
||||
if (!visible) {
|
||||
// } else {
|
||||
// newVis &= ~(View.SYSTEM_UI_FLAG_LOW_PROFILE |
|
||||
// View.SYSTEM_UI_FLAG_FULLSCREEN |
|
||||
// View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
|
||||
newVis |= View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE;
|
||||
}
|
||||
|
||||
// If we are now visible, schedule a timer for us to go invisible. Set the new desired visibility.
|
||||
getWindow().getDecorView().setSystemUiVisibility(newVis);
|
||||
}
|
||||
}
|
||||
|
||||
public void onChangeOrientation(View view) {
|
||||
setRequestedOrientation(isLandscape() ? ActivityInfo.SCREEN_ORIENTATION_PORTRAIT : ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
|
||||
}
|
||||
|
||||
public void onChangePlayMode(View view) {
|
||||
mMode = mVideoView.toggleAspectRatio();
|
||||
Log.i(TAG, "画面模式:" + mMode);
|
||||
}
|
||||
|
||||
/*
|
||||
* 截图
|
||||
* */
|
||||
public void onTakePicture(View view) {
|
||||
if (mVideoView.isInPlaybackState()) {
|
||||
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){
|
||||
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_WRITE_STORAGE);
|
||||
} else {
|
||||
doTakePicture();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void doTakePicture() {
|
||||
File file = new File(FileUtil.getPicturePath());
|
||||
file.mkdirs();
|
||||
|
||||
file = new File(file, "pic_" + new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + ".jpg");
|
||||
final String picture = mVideoView.takePicture(file.getPath());
|
||||
|
||||
if (!TextUtils.isEmpty(picture)) {
|
||||
Toast.makeText(ProVideoActivity.this,"图片已保存", Toast.LENGTH_SHORT).show();
|
||||
|
||||
if (mScanner == null) {
|
||||
MediaScannerConnection connection = new MediaScannerConnection(ProVideoActivity.this, new MediaScannerConnection.MediaScannerConnectionClient() {
|
||||
public void onMediaScannerConnected() {
|
||||
mScanner.scanFile(picture, "image/jpeg");
|
||||
}
|
||||
|
||||
public void onScanCompleted(String path1, Uri uri) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
connection.connect();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
mScanner = connection;
|
||||
} else {
|
||||
mScanner.scanFile(picture, "image/jpeg");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,226 @@
|
||||
package com.example.myapplication.player;
|
||||
|
||||
import static tv.danmaku.ijk.media.player.IjkMediaPlayer.native_active_days;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import androidx.core.app.ActivityCompat;
|
||||
|
||||
import com.example.myapplication.player.utils.FileUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
import tv.danmaku.ijk.media.player.IjkMediaPlayer;
|
||||
import tv.danmaku.ijk.media.widget.media.IjkVideoView;
|
||||
|
||||
/**
|
||||
* 播放器
|
||||
* <p>
|
||||
* Created by apple on 2017/2/11.
|
||||
*/
|
||||
public class ProVideoView extends IjkVideoView implements VideoControllerView.FullScreenAbleMediaPlayerControl {
|
||||
|
||||
private String mRecordPath;
|
||||
|
||||
public ProVideoView(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public ProVideoView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public ProVideoView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
public ProVideoView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
}
|
||||
|
||||
public static void setKey(String key) {
|
||||
Player_KEY = key;
|
||||
}
|
||||
|
||||
public static long getActiveDays(Context context, String key) {
|
||||
return native_active_days(context, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* ================ super override ================
|
||||
*/
|
||||
|
||||
public void startRecord(String path, int seconds) {
|
||||
if (mMediaPlayer == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
super.startRecord(path, seconds);
|
||||
mRecordPath = path;
|
||||
}
|
||||
|
||||
public void stopRecord() {
|
||||
if (mMediaPlayer == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
super.stopRecord();
|
||||
mRecordPath = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* ================ FullScreenAbleMediaPlayerControl ================
|
||||
*/
|
||||
|
||||
@Override
|
||||
public boolean isFullScreen() {
|
||||
if (getContext() instanceof ProVideoActivity) {
|
||||
ProVideoActivity pro = (ProVideoActivity) getContext();
|
||||
return pro.isLandscape();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toggleFullScreen() {
|
||||
if (getContext() instanceof ProVideoActivity) {
|
||||
ProVideoActivity pro = (ProVideoActivity) getContext();
|
||||
pro.onChangeOrientation(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean recordEnable() {
|
||||
Uri uri = mUri;
|
||||
|
||||
if (uri == null)
|
||||
return false;
|
||||
|
||||
if (uri.getScheme() == null)
|
||||
return false;
|
||||
|
||||
return !uri.getScheme().equals("file");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean speedCtrlEnable() {
|
||||
Uri uri = mUri;
|
||||
|
||||
if (uri == null)
|
||||
return false;
|
||||
|
||||
if (uri.getScheme() == null)
|
||||
return true;
|
||||
|
||||
return uri.getScheme().equals("file");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRecording() {
|
||||
if (mMediaPlayer == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !TextUtils.isEmpty(mRecordPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reStart() {
|
||||
super.reStart();
|
||||
if (mRecordPath != null) {
|
||||
toggleRecord();
|
||||
toggleRecord();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toggleRecord() {
|
||||
if (getContext() instanceof ProVideoActivity) {
|
||||
ProVideoActivity pro = (ProVideoActivity) getContext();
|
||||
|
||||
if (ActivityCompat.checkSelfPermission(pro, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
|
||||
ActivityCompat.requestPermissions(pro, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, ProVideoActivity.REQUEST_WRITE_STORAGE + 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isRecording()) {
|
||||
Uri uri = mUri;
|
||||
if (uri == null)
|
||||
return;
|
||||
|
||||
mRecordPath = "record_" + new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + ".mp4";
|
||||
File directory = new File(FileUtil.getMoviePath());
|
||||
|
||||
try {
|
||||
directory.mkdirs();
|
||||
startRecord(directory + "/" + mRecordPath, 30);
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
mRecordPath = null;
|
||||
}
|
||||
} else {
|
||||
stopRecord();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getSpeed() {
|
||||
if (mMediaPlayer == null) {
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
if (mMediaPlayer instanceof IjkMediaPlayer) {
|
||||
IjkMediaPlayer player = (IjkMediaPlayer) mMediaPlayer;
|
||||
return player.getSpeed();
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSpeed(float speed) {
|
||||
if (mMediaPlayer == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mMediaPlayer instanceof IjkMediaPlayer) {
|
||||
IjkMediaPlayer player = (IjkMediaPlayer) mMediaPlayer;
|
||||
player.setSpeed(speed);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void takePicture() {
|
||||
if (getContext() instanceof ProVideoActivity) {
|
||||
ProVideoActivity pro = (ProVideoActivity) getContext();
|
||||
pro.onTakePicture(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toggleMode() {
|
||||
if (getContext() instanceof ProVideoActivity) {
|
||||
ProVideoActivity pro = (ProVideoActivity) getContext();
|
||||
pro.onChangePlayMode(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCompleted() {
|
||||
if (mMediaPlayer instanceof IjkMediaPlayer) {
|
||||
IjkMediaPlayer player = (IjkMediaPlayer) mMediaPlayer;
|
||||
return player.isCompleted();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package com.example.myapplication.player.utils;
|
||||
|
||||
import android.os.Environment;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class FileUtil {
|
||||
|
||||
private static String path = Environment.getExternalStorageDirectory() +"/EasyPlayerRro";
|
||||
|
||||
public static String getPicturePath() {
|
||||
return path + "/picture";
|
||||
}
|
||||
|
||||
public static File getSnapshotFile(String url) {
|
||||
File file = new File(getPicturePath() + urlDir(url));
|
||||
file.mkdirs();
|
||||
|
||||
file = new File(file, "snapshot.jpg");
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
public static String getMoviePath() {
|
||||
return path + "/movie";
|
||||
}
|
||||
|
||||
private static String urlDir(String url) {
|
||||
url = url.replace("://", "");
|
||||
url = url.replace("/", "");
|
||||
url = url.replace(".", "");
|
||||
|
||||
if (url.length() > 64) {
|
||||
url.substring(0, 63);
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
package com.example.myapplication.player.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.preference.PreferenceManager;
|
||||
|
||||
public class SPUtil {
|
||||
|
||||
/* ============================ 使用MediaCodec解码 ============================ */
|
||||
private static final String KEY_SW_CODEC = "pref.using_media_codec";
|
||||
|
||||
public static boolean getMediaCodec(Context context) {
|
||||
return PreferenceManager.getDefaultSharedPreferences(context)
|
||||
.getBoolean(KEY_SW_CODEC, false);
|
||||
}
|
||||
|
||||
public static void setMediaCodec(Context context, boolean isChecked) {
|
||||
PreferenceManager.getDefaultSharedPreferences(context)
|
||||
.edit()
|
||||
.putBoolean(KEY_SW_CODEC, isChecked)
|
||||
.apply();
|
||||
}
|
||||
|
||||
/* ============================ KEY_UDP_MODE ============================ */
|
||||
private static final String KEY_UDP_MODE = "USE_UDP_MODE";
|
||||
|
||||
public static boolean getUDPMode(Context context) {
|
||||
return PreferenceManager.getDefaultSharedPreferences(context)
|
||||
.getBoolean(KEY_UDP_MODE, false);
|
||||
}
|
||||
|
||||
public static void setUDPMode(Context context, boolean isChecked) {
|
||||
PreferenceManager.getDefaultSharedPreferences(context)
|
||||
.edit()
|
||||
.putBoolean(KEY_UDP_MODE, isChecked)
|
||||
.apply();
|
||||
}
|
||||
|
||||
public static void setDefaultParams(Context context) {
|
||||
PreferenceManager.getDefaultSharedPreferences(context).edit()
|
||||
.putInt("timeout", 5)
|
||||
.putLong("analyzeduration", 21000000L)
|
||||
.apply();
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 324 B |
After Width: | Height: | Size: 302 B |
After Width: | Height: | Size: 940 B |
After Width: | Height: | Size: 827 B |
After Width: | Height: | Size: 675 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 962 B |
After Width: | Height: | Size: 850 B |
After Width: | Height: | Size: 943 B |
After Width: | Height: | Size: 753 B |
After Width: | Height: | Size: 263 B |
After Width: | Height: | Size: 245 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 924 B |
After Width: | Height: | Size: 714 B |
After Width: | Height: | Size: 409 B |
After Width: | Height: | Size: 344 B |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.3 KiB |
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:drawable="@drawable/loading"
|
||||
android:fromDegrees="0.0"
|
||||
android:pivotX="50.0%"
|
||||
android:pivotY="50.0%"
|
||||
android:toDegrees="360.0" />
|
After Width: | Height: | Size: 28 KiB |
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@drawable/new_fast_click" android:state_pressed="true"></item>
|
||||
<item android:drawable="@drawable/new_fast_white" android:state_selected="true"></item>
|
||||
<item android:drawable="@drawable/new_fast_white" android:state_enabled="true"></item>
|
||||
<item android:drawable="@drawable/new_fast_white"></item>
|
||||
</selector>
|
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@drawable/new_forward_click" android:state_pressed="true"></item>
|
||||
<item android:drawable="@drawable/new_forward_white" android:state_selected="true"></item>
|
||||
<item android:drawable="@drawable/new_forward_white" android:state_enabled="true"></item>
|
||||
<item android:drawable="@drawable/new_forward_white"></item>
|
||||
</selector>
|
After Width: | Height: | Size: 8.1 KiB |
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@drawable/new_moveback_click" android:state_pressed="true"></item>
|
||||
<item android:drawable="@drawable/new_moveback_white" android:state_selected="true"></item>
|
||||
<item android:drawable="@drawable/new_moveback_white" android:state_enabled="true"></item>
|
||||
<item android:drawable="@drawable/new_moveback_white"></item>
|
||||
</selector>
|
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@drawable/new_slow_click" android:state_pressed="true"></item>
|
||||
<item android:drawable="@drawable/new_slow_white" android:state_selected="true"></item>
|
||||
<item android:drawable="@drawable/new_slow_white" android:state_enabled="true"></item>
|
||||
<item android:drawable="@drawable/new_slow_white"></item>
|
||||
</selector>
|
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@drawable/new_snapshot_click" android:state_pressed="true"></item>
|
||||
<item android:drawable="@drawable/new_snapshot_white" android:state_selected="true"></item>
|
||||
<item android:drawable="@drawable/new_snapshot_white" android:state_enabled="true"></item>
|
||||
<item android:drawable="@drawable/new_snapshot_white"></item>
|
||||
</selector>
|
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@drawable/new_stretch_click" android:state_pressed="true"></item>
|
||||
<item android:drawable="@drawable/new_stretch_white" android:state_selected="true"></item>
|
||||
<item android:drawable="@drawable/new_stretch_white" android:state_enabled="true"></item>
|
||||
<item android:drawable="@drawable/new_stretch_white"></item>
|
||||
</selector>
|
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@drawable/new_videotape_click" android:state_pressed="true"></item>
|
||||
<item android:drawable="@drawable/new_videotape_white" android:state_selected="true"></item>
|
||||
<item android:drawable="@drawable/new_videotape_white" android:state_enabled="true"></item>
|
||||
<item android:drawable="@drawable/new_videotape_white"></item>
|
||||
</selector>
|
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval" >
|
||||
<size android:height="10dp" android:width="10dp"></size>
|
||||
<solid android:color="#ff0000"></solid>
|
||||
</shape>
|
@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<!--定义seekbar滑动条的底色-->
|
||||
<item android:id="@android:id/background">
|
||||
<shape>
|
||||
<corners android:radius="2dp" />
|
||||
<gradient
|
||||
android:angle="270"
|
||||
android:centerColor="#ffffff"
|
||||
android:centerY="0.75"
|
||||
android:endColor="#ffffff"
|
||||
android:startColor="#ffffff" />
|
||||
</shape>
|
||||
</item>
|
||||
<!--定义seekbar滑动条进度颜色-->
|
||||
<item android:id="@android:id/progress">
|
||||
<clip>
|
||||
<shape>
|
||||
<corners android:radius="2dp"/>
|
||||
<solid android:color="@color/colorTheme"/>
|
||||
</shape>
|
||||
</clip>
|
||||
</item>
|
||||
</layer-list>
|
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval" >
|
||||
<size android:height="10dp" android:width="10dp"></size>
|
||||
<solid android:color="#00000000"></solid>
|
||||
</shape>
|
@ -0,0 +1,92 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/render_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center"
|
||||
android:background="#000000">
|
||||
|
||||
<com.example.myapplication.player.ProVideoView
|
||||
android:id="@+id/video_view2"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:keepScreenOn="true"
|
||||
android:visibility="gone" />
|
||||
|
||||
<com.example.myapplication.player.ProVideoView
|
||||
android:id="@+id/video_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:keepScreenOn="true" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/surface_cover"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#000000"
|
||||
android:scaleType="centerCrop" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@android:id/progress"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="200dp"
|
||||
android:layout_gravity="center"
|
||||
android:background="@color/white"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ProgressBar
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="100dp"
|
||||
android:indeterminateBehavior="repeat"
|
||||
android:indeterminateDrawable="@drawable/anim" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/loading_speed"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_horizontal"
|
||||
android:text="加载中"
|
||||
android:textColor="@color/colorTheme" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/live_video_snap_thumb"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="50dp"
|
||||
android:layout_gravity="right|top"
|
||||
android:layout_margin="3dp"
|
||||
android:clickable="true"
|
||||
android:gravity="center"
|
||||
android:onClick="onTakePictureThumbClicked"
|
||||
android:padding="3dp"
|
||||
android:scaleType="centerCrop"
|
||||
android:visibility="invisible" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/player_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="200dp"
|
||||
android:layout_gravity="center"
|
||||
android:background="#fff"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/new_lost" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
</LinearLayout>
|
@ -0,0 +1,177 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/media_controller_ll"
|
||||
android:background="#6000"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="30dp"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_speed"
|
||||
style="@style/Widget.AppCompat.ActionButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
android:textColor="@color/white" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_fps"
|
||||
style="@style/Widget.AppCompat.ActionButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:visibility="gone"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/white" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_kbps"
|
||||
style="@style/Widget.AppCompat.ActionButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/white"
|
||||
android:visibility="gone" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="30dp"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/slow"
|
||||
android:layout_weight="1"
|
||||
style="@android:style/MediaButton.Next"
|
||||
android:src="@drawable/new_slow_btn" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/rewind"
|
||||
android:layout_weight="1"
|
||||
style="@android:style/MediaButton.Rew"
|
||||
android:src="@drawable/new_moveback_btn" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/pause"
|
||||
android:layout_weight="1"
|
||||
style="@android:style/MediaButton.Play"
|
||||
android:src="@drawable/new_stop_white" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/fast_forward"
|
||||
android:layout_weight="1"
|
||||
style="@android:style/MediaButton.Ffwd"
|
||||
android:src="@drawable/new_forward_btn"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/fast"
|
||||
android:layout_weight="1"
|
||||
style="@android:style/MediaButton.Ffwd"
|
||||
android:src="@drawable/new_fast_btn" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="30dp"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal"
|
||||
android:paddingTop="4dip">
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/action_take_picture"
|
||||
style="@style/Widget.AppCompat.ActionButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/new_snapshot_btn" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/action_record"
|
||||
style="@style/Widget.AppCompat.ActionButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/new_videotape_btn" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_record_time"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:visibility="gone"
|
||||
android:textColor="#ff0000"
|
||||
android:drawablePadding="5dp"
|
||||
android:drawableLeft="@drawable/red_dot"
|
||||
android:text="00:00" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/action_change_mode"
|
||||
style="@style/Widget.AppCompat.ActionButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/new_stretch_btn" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/fullscreen"
|
||||
style="@style/Widget.AppCompat.ActionButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/new_full" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="30dp"
|
||||
android:id="@+id/seek_bar_container"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/time_current"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:paddingLeft="4dip"
|
||||
android:paddingRight="4dip"
|
||||
android:paddingTop="4dip"
|
||||
android:textSize="14sp"
|
||||
android:textColor="@color/white"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/media_controller_progress"
|
||||
style="@style/CustomSeekbarStyle"
|
||||
android:progress="10"
|
||||
android:secondaryProgress="60"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/total_time"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:paddingLeft="4dip"
|
||||
android:textColor="@color/white"
|
||||
android:paddingRight="4dip"
|
||||
android:paddingTop="4dip"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
@ -0,0 +1,12 @@
|
||||
<resources>
|
||||
|
||||
<!-- Declare custom theme attributes that allow changing which styles are
|
||||
used for button bars depending on the API level.
|
||||
?android:attr/buttonBarStyle is new as of API 11 so this is
|
||||
necessary to support previous API levels. -->
|
||||
<declare-styleable name="ButtonBarContainerTheme">
|
||||
<attr name="metaButtonBarStyle" format="reference" />
|
||||
<attr name="metaButtonBarButtonStyle" format="reference" />
|
||||
</declare-styleable>
|
||||
|
||||
</resources>
|