desc:移植player,播放本地视频

main
xiaowusky
parent 767781e47f
commit c69ec99c44

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

@ -3,7 +3,6 @@ apply plugin: 'com.android.application'
android {
compileSdkVersion 31
lintOptions {
checkReleaseBuilds false
// Or, if you prefer, you can continue to check for errors in release builds,
@ -30,6 +29,10 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
viewBinding {
enabled = true
}
}
@ -51,7 +54,9 @@ dependencies {
}
implementation 'io.reactivex.rxjava2:rxjava:2.1.6'
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
implementation 'com.github.bumptech.glide:glide:4.12.0'
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
implementation 'androidx.lifecycle:lifecycle-reactivestreams:2.4.1'
implementation project(path: ':ijkplayer-java')
annotationProcessor 'androidx.lifecycle:lifecycle-compiler:2.0.0'
}

@ -50,5 +50,11 @@
android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
android:resource="@xml/device_filter" />
</activity>
<activity
android:name=".player.ProVideoActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:exported="false"
android:theme="@style/FullscreenTheme" />
</application>
</manifest>

@ -1,16 +1,22 @@
package com.example.myapplication;
import androidx.lifecycle.Observer;
import android.Manifest;
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.SurfaceTexture;
import android.media.projection.MediaProjectionManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.preference.PreferenceManager;
import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat;
import androidx.appcompat.app.AppCompatActivity;
import android.view.TextureView;
import android.view.View;
import android.widget.CheckBox;
@ -18,6 +24,8 @@ import android.widget.CompoundButton;
import android.widget.TextView;
import android.widget.Toast;
import com.example.myapplication.player.ProVideoActivity;
import org.easydarwin.push.MediaStream;
import io.reactivex.Single;
@ -49,6 +57,7 @@ public class MainActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0);
CheckBox hevc_enable = findViewById(R.id.enable_265);
hevc_enable.setChecked(PreferenceManager.getDefaultSharedPreferences(this).getBoolean("try_265_encode", false));
@ -105,10 +114,10 @@ public class MainActivity extends AppCompatActivity {
pushingStateText.append("\n");
if ("avc".equals(pushingState.videoCodec)) {
pushingStateText.append("视频编码方式:" + "H264硬编码");
}else if ("hevc".equals(pushingState.videoCodec)) {
pushingStateText.append("视频编码方式:" + "H265硬编码");
}else if ("x264".equals(pushingState.videoCodec)) {
pushingStateText.append("视频编码方式:" + "x264");
} else if ("hevc".equals(pushingState.videoCodec)) {
pushingStateText.append("视频编码方式:" + "H265硬编码");
} else if ("x264".equals(pushingState.videoCodec)) {
pushingStateText.append("视频编码方式:" + "x264");
}
}
@ -232,15 +241,18 @@ public class MainActivity extends AppCompatActivity {
}
public void onUVCCamera(View view) {
Intent intent = new Intent(this, UVCActivity.class);
// Intent intent = new Intent(this, UVCActivity.class);
Intent intent = new Intent(this, ProVideoActivity.class);
String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/mvtest.mp4";
intent.putExtra("videoPath", path);
startActivity(intent);
}
public void OnSaveRecord(View view) {
if(mediaStream.isRecording()){
if (mediaStream.isRecording()) {
mediaStream.stopRecord();
}else{
mediaStream.startRecord(this.getCacheDir().getAbsolutePath(), 10*1000);
} else {
mediaStream.startRecord(this.getCacheDir().getAbsolutePath(), 10 * 1000);
}
}
}

@ -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,970 @@
package com.example.myapplication.player;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.MediaController;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
import com.example.myapplication.R;
import java.lang.ref.WeakReference;
import java.util.Formatter;
import java.util.Locale;
import tv.danmaku.ijk.media.widget.media.IMediaController;
import tv.danmaku.ijk.media.widget.media.IjkVideoView;
/**
*
*
* Created by jiaozebo on 2017/6/11.
*/
public class VideoControllerView extends FrameLayout implements IMediaController {
private static final String TAG = "VideoControllerView";
private MediaController.MediaPlayerControl mPlayer;
private boolean mShowing;
private boolean mDragging;
private static final int sDefaultTimeout = 10000;
private static final int FADE_OUT = 1;
private static final int SHOW_PROGRESS = 2;
private boolean mUseFastForward;
private boolean mFromXml;
StringBuilder mFormatBuilder;
Formatter mFormatter;
private Context mContext;
private View mAnchor;
private View mRoot;
private View mediaControllerLL;
private SeekBar mProgress; // 播放进度滚动条
private TextView mCurrentTime; // 当前播放时间点
private TextView mEndTime; // 总时长
private ImageButton mPauseButton; // 暂停or开始
private ImageButton mFastButton; // 快进
private ImageButton mRewindButton; // 快退
private ImageButton mFullscreenButton; // 全屏
private ImageButton mRecordButton; // 录像
private ImageButton mFastPlay; // 播放速度加快
private ImageButton mSlowPlay; // 播放速度减慢
private TextView mTVSpeed;
private TextView mTVRecordDuration;
private TextView fps, kbps;
private View mPictureBtn, mChangeModeBtn;
private long mReceivedBytes;
private long mReceivedPackets;
private long lastBitsMillis;
private long recordBeginTime;
private Handler mHandler = new MessageHandler(this);
Runnable mRecordTickTask = new Runnable() {
@Override
public void run() {
long recordSecond = (System.currentTimeMillis() - recordBeginTime) / 1000;
if (recordSecond >= 300) { // 分段
}
recordSecond %= 3600;
mTVRecordDuration.setText(String.format("%02d:%02d", recordSecond / 60, recordSecond % 60));
mTVRecordDuration.setCompoundDrawablesWithIntrinsicBounds(recordSecond % 2 == 0 ? R.drawable.red_dot : R.drawable.transparent_dot, 0, 0, 0);
postDelayed(this, 1000);
}
};
// 每一秒更新fps/bps
Runnable fpsBpsTickTask = new Runnable() {
long firstTimeStamp = 0l;
@Override
public void run() {
if (firstTimeStamp == 0l)
firstTimeStamp = System.currentTimeMillis();
if (mPlayer != null && (mPlayer instanceof IjkVideoView)) {
IjkVideoView ijk = (IjkVideoView) mPlayer;
long l = ijk.getReceivedBytes();
long received = l - mReceivedBytes;
long packets = ijk.getVideoCachePackets();
long receivedPackets = packets - mReceivedPackets;
mReceivedBytes = l;
mReceivedPackets = packets;
if (ijk.isPlaying() && lastBitsMillis != 0) {
long l1 = SystemClock.uptimeMillis() - lastBitsMillis;
if (l1 >= 300) {
long time = System.currentTimeMillis() - firstTimeStamp;
if (time < 900) {
fps.setText("");
kbps.setText("");
} else {
receivedPackets = Math.min(receivedPackets, 30);
fps.setText(String.format("%dfps", receivedPackets));
kbps.setText(String.format("%3.01fKB/s", received * 1.0f * 1000 / l1 / 1024));
}
}
} else {
fps.setText("");
kbps.setText("");
}
lastBitsMillis = SystemClock.uptimeMillis();
}
postDelayed(this, 1000);
}
};
private Runnable mSeekingPending;
// 暂停/开始的点击事件
private OnClickListener mPauseListener = new OnClickListener() {
public void onClick(View v) {
doPauseResume();
show(sDefaultTimeout);
}
};
// 全屏的点击事件
private OnClickListener mFullscreenListener = new OnClickListener() {
public void onClick(View v) {
doToggleFullscreen();
show(sDefaultTimeout);
}
};
// 录像的点击事件
private OnClickListener mRecordingListener = new OnClickListener() {
public void onClick(View v) {
doToggleRecord();
show(sDefaultTimeout);
}
};
// 滚动条的点击事件
private OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() {
public void onStartTrackingTouch(SeekBar bar) {
show(3600000);
mDragging = true;
mHandler.removeMessages(SHOW_PROGRESS);
}
public void onProgressChanged(SeekBar bar, int progress, boolean fromuser) {
if (mPlayer == null) {
return;
}
if (!fromuser) {
return;
}
if (mSeekingPending != null) {
removeCallbacks(mSeekingPending);
mSeekingPending = null;
}
if (mPlayer.getDuration() <= 0)
return;
// long duration = mPlayer.getDuration();
// long newPosition = (duration * progress) / 1000L;
mPlayer.seekTo((int) progress);
if (mCurrentTime != null)
mCurrentTime.setText(stringForTime((int) progress));
}
public void onStopTrackingTouch(SeekBar bar) {
mDragging = false;
setProgress();
updatePausePlay();
show(sDefaultTimeout);
mHandler.sendEmptyMessage(SHOW_PROGRESS);
}
};
// 快退的点击事件
private OnClickListener mRewindListener = new OnClickListener() {
public void onClick(View v) {
if (mPlayer == null) {
return;
}
int pos = mPlayer.getCurrentPosition();
pos -= 5000; // милисекунд
mPlayer.seekTo(pos);
setProgress();
show(sDefaultTimeout);
}
};
// 快进的点击事件
private OnClickListener mFastListener = new OnClickListener() {
public void onClick(View v) {
if (mPlayer == null) {
return;
}
int pos = mPlayer.getCurrentPosition();
pos += 15000; // милисекунд
mPlayer.seekTo(pos);
setProgress();
show(sDefaultTimeout);
}
};
// 播放速度加快
private OnClickListener mFastPlayListener = new OnClickListener() {
@Override
public void onClick(View v) {
show(sDefaultTimeout);
if (mPlayer instanceof FullScreenAbleMediaPlayerControl) {
FullScreenAbleMediaPlayerControl player = (FullScreenAbleMediaPlayerControl) mPlayer;
float speed = player.getSpeed();
if (speed > 2.0) {
return;
}
if (speed >= 1.0f) {
mTVSpeed.setText(String.format("%d倍速", (int) (speed * 2)));
} else {
mTVSpeed.setText(String.format("%.02f倍速", speed * 2));
}
if (speed == 0.5) {
mTVSpeed.setVisibility(GONE);
} else {
mTVSpeed.setVisibility(VISIBLE);
}
player.setSpeed(speed * 2);
} else {
}
}
};
private OnClickListener mSlowPlayListener = new OnClickListener() {
@Override
public void onClick(View v) {
if (mPlayer instanceof FullScreenAbleMediaPlayerControl) {
FullScreenAbleMediaPlayerControl player = (FullScreenAbleMediaPlayerControl) mPlayer;
float speed = player.getSpeed();
if (speed < 0.5) {
return;
}
if (speed >= 2.0f) {
mTVSpeed.setText(String.format("%d倍速", (int) (speed * 0.5)));
} else {
mTVSpeed.setText(String.format("%.02f倍速", speed * 0.5));
}
if (speed == 2.0) {
mTVSpeed.setVisibility(GONE);
} else {
mTVSpeed.setVisibility(VISIBLE);
}
player.setSpeed(speed * 0.5f);
}
show(sDefaultTimeout);
}
};
private OnClickListener takePicListener = new OnClickListener() {
@Override
public void onClick(View v) {
show(sDefaultTimeout);
if (mPlayer instanceof FullScreenAbleMediaPlayerControl) {
FullScreenAbleMediaPlayerControl player = (FullScreenAbleMediaPlayerControl) mPlayer;
player.takePicture();
}
}
};
private OnClickListener modeListener = new OnClickListener() {
@Override
public void onClick(View v) {
show(sDefaultTimeout);
if (mPlayer instanceof FullScreenAbleMediaPlayerControl) {
FullScreenAbleMediaPlayerControl player = (FullScreenAbleMediaPlayerControl) mPlayer;
player.toggleMode();
}
}
};
/** ==================== constructor ==================== */
public VideoControllerView(Context context, AttributeSet attrs) {
super(context, attrs);
mRoot = null;
mContext = context;
mUseFastForward = true;
mFromXml = true;
Log.i(TAG, TAG);
}
public VideoControllerView(Context context, boolean useFastForward) {
super(context);
mContext = context;
mUseFastForward = useFastForward;
Log.i(TAG, TAG);
}
public VideoControllerView(Context context) {
this(context, true);
Log.i(TAG, TAG);
}
/** ==================== system Override ==================== */
/*
* activityoncreate
* */
@Override
public void onFinishInflate() {
super.onFinishInflate();
if (mRoot != null)
initControllerView(mRoot);
}
/*
* ()
* */
@Override
public boolean onTrackballEvent(MotionEvent ev) {
show(sDefaultTimeout);
return false;
}
/*
*
* */
@Override
public boolean onTouchEvent(MotionEvent event) {
show(sDefaultTimeout);
return true;
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (mPlayer == null) {
return true;
}
int keyCode = event.getKeyCode();
final boolean uniqueDown = event.getRepeatCount() == 0 && event.getAction() == KeyEvent.ACTION_DOWN;
if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK ||
keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE ||
keyCode == KeyEvent.KEYCODE_SPACE) {
if (uniqueDown) {
doPauseResume();
show(sDefaultTimeout);
if (mPauseButton != null) {
mPauseButton.requestFocus();
}
}
return true;
} else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) {
if (uniqueDown && !mPlayer.isPlaying()) {
mPlayer.start();
updatePausePlay();
show(sDefaultTimeout);
}
return true;
} else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP
|| keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) {
if (uniqueDown && mPlayer.isPlaying()) {
mPlayer.pause();
updatePausePlay();
show(sDefaultTimeout);
}
return true;
} else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN ||
keyCode == KeyEvent.KEYCODE_VOLUME_UP ||
keyCode == KeyEvent.KEYCODE_VOLUME_MUTE) {
return super.dispatchKeyEvent(event);
} else if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_MENU) {
if (uniqueDown) {
hide();
}
return true;
}
show(sDefaultTimeout);
return super.dispatchKeyEvent(event);
}
/** ==================== IMediaController ==================== */
@Override
public void hide() {
if (mAnchor == null) {
return;
}
try {
if (mAnchor instanceof ViewGroup) {
ViewGroup vg = (ViewGroup) mAnchor;
vg.removeView(this);
}
mHandler.removeMessages(SHOW_PROGRESS);
} catch (IllegalArgumentException ex) {
Log.w("MediaController", "already removed");
}
mShowing = false;
}
public boolean isShowing() {
return mShowing;
}
@Override
public void setAnchorView(View view) {
mAnchor = view;
LayoutParams frameParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
removeAllViews();
View v = makeControllerView();
addView(v, frameParams);
}
@Override
public void setEnabled(boolean enabled) {
if (mPauseButton != null) {
mPauseButton.setEnabled(enabled);
}
if (mFastButton != null) {
mFastButton.setEnabled(enabled);
}
if (mRewindButton != null) {
mRewindButton.setEnabled(enabled);
}
if (mProgress != null) {
mProgress.setEnabled(enabled);
}
disableUnsupportedButtons();
super.setEnabled(enabled);
}
@Override
public void setMediaPlayer(MediaController.MediaPlayerControl player) {
mPlayer = player;
updatePausePlay();
updateFullScreen();
updateRecord();
updateSpeedCtrl();
}
@Override
public void show() {
show(sDefaultTimeout);
}
@Override
public void show(int timeout) {
if (!mShowing && mAnchor != null) {
setProgress();
if (mPauseButton != null) {
mPauseButton.requestFocus();
}
disableUnsupportedButtons();
LayoutParams tlp = new LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT,
Gravity.BOTTOM);
if (mAnchor instanceof ViewGroup) {
ViewGroup vg = (ViewGroup) mAnchor;
vg.addView(this, tlp);
}
mShowing = true;
}
updatePausePlay();
updateFullScreen();
updateRecord();
updateSpeedCtrl();
mHandler.sendEmptyMessage(SHOW_PROGRESS);
Message msg = mHandler.obtainMessage(FADE_OUT);
if (timeout != 0) {
mHandler.removeMessages(FADE_OUT);
mHandler.sendMessageDelayed(msg, timeout);
}
if (mPlayer != null && mPlayer.isPlaying()) {
removeCallbacks(fpsBpsTickTask);
post(fpsBpsTickTask);
}
}
@Override
public void showOnce(View view) {
}
/** ==================== UI操作 ==================== */
/**
*
*/
protected View makeControllerView() {
LayoutInflater inflate = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mRoot = inflate.inflate(R.layout.media_controller, null);
initControllerView(mRoot);
return mRoot;
}
private void initControllerView(View v) {
mediaControllerLL = (LinearLayout) v.findViewById(R.id.media_controller_ll);
mPauseButton = (ImageButton) v.findViewById(R.id.pause);
if (mPauseButton != null) {
mPauseButton.requestFocus();
mPauseButton.setOnClickListener(mPauseListener);
}
mFullscreenButton = (ImageButton) v.findViewById(R.id.fullscreen);
if (mFullscreenButton != null) {
mFullscreenButton.requestFocus();
mFullscreenButton.setOnClickListener(mFullscreenListener);
}
mRecordButton = (ImageButton) v.findViewById(R.id.action_record);
if (mRecordButton != null) {
mRecordButton.requestFocus();
mRecordButton.setOnClickListener(mRecordingListener);
}
mFastButton = (ImageButton) v.findViewById(R.id.fast_forward);
if (mFastButton != null) {
mFastButton.setOnClickListener(mFastListener);
if (!mFromXml) {
mFastButton.setVisibility(mUseFastForward ? View.VISIBLE : View.GONE);
}
}
mRewindButton = (ImageButton) v.findViewById(R.id.rewind);
if (mRewindButton != null) {
mRewindButton.setOnClickListener(mRewindListener);
if (!mFromXml) {
mRewindButton.setVisibility(mUseFastForward ? View.VISIBLE : View.GONE);
}
}
mProgress = (SeekBar) v.findViewById(R.id.media_controller_progress);
if (mProgress != null) {
if (mProgress instanceof SeekBar) {
SeekBar seeker = (SeekBar) mProgress;
seeker.setOnSeekBarChangeListener(mSeekListener);
}
mProgress.setMax(1000);
}
fps = (TextView) v.findViewById(R.id.tv_fps);
kbps = (TextView) v.findViewById(R.id.tv_kbps);
mEndTime = (TextView) v.findViewById(R.id.total_time);
mCurrentTime = (TextView) v.findViewById(R.id.time_current);
mTVSpeed = (TextView) v.findViewById(R.id.tv_speed);
mFormatBuilder = new StringBuilder();
mFormatter = new Formatter(mFormatBuilder, Locale.getDefault());
mFastPlay = (ImageButton) v.findViewById(R.id.fast);
mFastPlay.setOnClickListener(mFastPlayListener);
mSlowPlay = (ImageButton) v.findViewById(R.id.slow);
mSlowPlay.setOnClickListener(mSlowPlayListener);
mPictureBtn = v.findViewById(R.id.action_take_picture);
mPictureBtn.setOnClickListener(takePicListener);
mChangeModeBtn = v.findViewById(R.id.action_change_mode);
mChangeModeBtn.setOnClickListener(modeListener);
mTVRecordDuration = (TextView) v.findViewById(R.id.tv_record_time);
mTVRecordDuration.setOnClickListener(mRecordingListener);
if (this.mPlayer instanceof FullScreenAbleMediaPlayerControl) {
FullScreenAbleMediaPlayerControl mPlayer = (FullScreenAbleMediaPlayerControl) this.mPlayer;
} else {
mFastPlay.setVisibility(GONE);
mSlowPlay.setVisibility(GONE);
}
if (mPlayer.isPlaying()) {
post(fpsBpsTickTask);
}
if (!mPlayer.canSeekBackward() || !mPlayer.canSeekForward()) {
v.findViewById(R.id.seek_bar_container).setVisibility(GONE);
} else {
v.findViewById(R.id.seek_bar_container).setVisibility(VISIBLE);
}
}
/**
* Отключить паузу или seek button, если поток не может быть приостановлена
* Это требует интерфейс управления MediaPlayerControlExt
*/
private void disableUnsupportedButtons() {
if (mPlayer == null) {
return;
}
try {
if (mPauseButton != null && !mPlayer.canPause()) {
mPauseButton.setEnabled(false);
}
if (mRewindButton != null && !mPlayer.canSeekBackward()) {
mRewindButton.setEnabled(false);
}
if (mFastButton != null && !mPlayer.canSeekForward()) {
mFastButton.setEnabled(false);
}
} catch (IncompatibleClassChangeError ex) {
//выводите в лог что хотите из ex
}
}
private void updateSpeedCtrl() {
if (mRoot == null || mRecordButton == null || this.mPlayer == null) {
return;
}
if (this.mPlayer instanceof FullScreenAbleMediaPlayerControl) {
FullScreenAbleMediaPlayerControl mPlayer = (FullScreenAbleMediaPlayerControl) this.mPlayer;
if (mPlayer.speedCtrlEnable()) {
mFastPlay.setVisibility(VISIBLE);
mSlowPlay.setVisibility(VISIBLE);
mTVSpeed.setVisibility(VISIBLE);
kbps.setVisibility(GONE);
fps.setVisibility(GONE);
} else {
mFastPlay.setVisibility(GONE);
mSlowPlay.setVisibility(GONE);
mTVSpeed.setVisibility(GONE);
kbps.setVisibility(VISIBLE);
fps.setVisibility(VISIBLE);
}
}
}
/*
* /
* */
private void doPauseResume() {
if (mPlayer == null) {
return;
}
removeCallbacks(fpsBpsTickTask);
if (mPlayer.isPlaying()) {
mPlayer.pause();
} else {
boolean isCompleted = false;
if (this.mPlayer instanceof FullScreenAbleMediaPlayerControl) {
FullScreenAbleMediaPlayerControl mPlayer = (FullScreenAbleMediaPlayerControl) this.mPlayer;
isCompleted = mPlayer.isCompleted();
}
mPlayer.start();
if (isCompleted) {
int duration = mPlayer.getDuration();
final int progress = mProgress.getProgress();
Log.d(TAG,String.valueOf(duration));
mSeekingPending = new Runnable() {
@Override
public void run() {
if (mPlayer != null) {
mPlayer.seekTo(progress);
}
}
};
postDelayed(mSeekingPending,500);
}
post(fpsBpsTickTask);
mReceivedBytes = 0;
mReceivedPackets = 0;
}
updatePausePlay();
}
public void updatePausePlay() {
if (mRoot == null || mPauseButton == null || mPlayer == null) {
return;
}
if (mPlayer.isPlaying()) {
if (this.mPlayer instanceof FullScreenAbleMediaPlayerControl) {
mPauseButton.setImageResource(R.drawable.new_stop_white);
} else {
mPauseButton.setImageResource(R.drawable.new_stop);
}
} else {
if (this.mPlayer instanceof FullScreenAbleMediaPlayerControl) {
mPauseButton.setImageResource(R.drawable.new_play_white);
} else {
mPauseButton.setImageResource(R.drawable.new_play);
}
}
}
private int setProgress() {
if (mPlayer == null || mDragging) {
return 0;
}
int position = (int) (mPlayer.getCurrentPosition());
// 非文件流的duration为0.
int duration = mPlayer.getDuration();
if (mProgress != null) {
if (duration > 0) {
int max = mProgress.getMax();
if (max != duration) {
mProgress.setMax(duration);
mProgress.setProgress(position);
} else {
if (position > mProgress.getProgress()){
mProgress.setProgress(position);
}
}
} else {
mProgress.setMax(0);
mProgress.setProgress(0);
}
int percent = mPlayer.getBufferPercentage();
mProgress.setSecondaryProgress(percent * 10);
}
if (mEndTime != null)
mEndTime.setText(stringForTime(duration));
if (mCurrentTime != null)
mCurrentTime.setText(stringForTime(position));
return position;
}
public void updateFullScreen() {
if (mRoot == null || mFullscreenButton == null || this.mPlayer == null) {
return;
}
if (this.mPlayer instanceof FullScreenAbleMediaPlayerControl) {
FullScreenAbleMediaPlayerControl mPlayer = (FullScreenAbleMediaPlayerControl) this.mPlayer;
if (mPlayer.isFullScreen()) {
mFullscreenButton.setImageResource(R.drawable.new_full);
} else {
mFullscreenButton.setImageResource(R.drawable.new_full_white);
}
}
}
private void updateRecord() {
if (mRoot == null || mRecordButton == null || this.mPlayer == null) {
return;
}
if (this.mPlayer instanceof FullScreenAbleMediaPlayerControl) {
FullScreenAbleMediaPlayerControl mPlayer = (FullScreenAbleMediaPlayerControl) this.mPlayer;
if (mPlayer.isRecording()) {
mRecordButton.setImageResource(R.drawable.new_videotape_click);
removeCallbacks(mRecordTickTask);
post(mRecordTickTask);
} else {
mRecordButton.setImageResource(R.drawable.new_videotape_btn);
}
if (mPlayer.recordEnable()) {
mRecordButton.setVisibility(VISIBLE);
} else {
mRecordButton.setVisibility(GONE);
}
}
}
private void doToggleFullscreen() {
if (mPlayer == null) {
return;
}
if (this.mPlayer instanceof FullScreenAbleMediaPlayerControl) {
FullScreenAbleMediaPlayerControl mPlayer = (FullScreenAbleMediaPlayerControl) this.mPlayer;
mPlayer.toggleFullScreen();
}
updateFullScreen();
}
private void doToggleRecord() {
if (mPlayer == null) {
return;
}
if (this.mPlayer instanceof FullScreenAbleMediaPlayerControl) {
FullScreenAbleMediaPlayerControl mPlayer = (FullScreenAbleMediaPlayerControl) this.mPlayer;
mPlayer.toggleRecord();
if (mPlayer.isRecording()) {
findViewById(R.id.tv_record_time).setVisibility(VISIBLE);
recordBeginTime = System.currentTimeMillis();
post(mRecordTickTask);
} else {
findViewById(R.id.tv_record_time).setVisibility(GONE);
removeCallbacks(mRecordTickTask);
}
}
}
private String stringForTime(int timeMs) {
int totalSeconds = timeMs / 1000;
int seconds = totalSeconds % 60;
int minutes = (totalSeconds / 60) % 60;
int hours = totalSeconds / 3600;
mFormatBuilder.setLength(0);
if (hours > 0) {
return mFormatter.format("%d:%02d:%02d", hours, minutes, seconds).toString();
} else {
return mFormatter.format("%02d:%02d", minutes, seconds).toString();
}
}
public interface FullScreenAbleMediaPlayerControl extends MediaController.MediaPlayerControl {
boolean isFullScreen();
void toggleFullScreen();
boolean recordEnable();
boolean speedCtrlEnable();
boolean isRecording();
void toggleRecord();
float getSpeed();
void setSpeed(float speed);
void takePicture();
void toggleMode();
boolean isCompleted();
}
private static class MessageHandler extends Handler {
private final WeakReference<VideoControllerView> mView;
MessageHandler(VideoControllerView view) {
mView = new WeakReference<VideoControllerView>(view);
}
@Override
public void handleMessage(Message msg) {
VideoControllerView view = mView.get();
if (view == null || view.mPlayer == null) {
return;
}
switch (msg.what) {
case FADE_OUT:
view.hide();
break;
case SHOW_PROGRESS:
if (!view.mDragging && view.mShowing && view.mPlayer.isPlaying()) {
msg = obtainMessage(SHOW_PROGRESS);
sendMessageDelayed(msg, 1000 - (view.setProgress() % 1000));
}
break;
}
}
}
}

@ -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();
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 324 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 302 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 940 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 827 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 675 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 962 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 850 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 943 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 753 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 924 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 714 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 409 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 344 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

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

Binary file not shown.

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>

Binary file not shown.

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>

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save