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>
|