diff --git a/ijkplayer/src/main/AndroidManifest.xml b/ijkplayer/src/main/AndroidManifest.xml
index 6d57040bc..fee45f451 100644
--- a/ijkplayer/src/main/AndroidManifest.xml
+++ b/ijkplayer/src/main/AndroidManifest.xml
@@ -1,2 +1,2 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/ijkplayer/src/main/java/com/lodz/android/mmsplayer/ijk/bean/MediaInfo.java b/ijkplayer/src/main/java/com/lodz/android/mmsplayer/ijk/bean/MediaInfo.java
new file mode 100644
index 000000000..ed38f5e3f
--- /dev/null
+++ b/ijkplayer/src/main/java/com/lodz/android/mmsplayer/ijk/bean/MediaInfo.java
@@ -0,0 +1,10 @@
+package com.lodz.android.mmsplayer.ijk.bean;
+
+public class MediaInfo {
+
+ public String playerName = "";
+ public String resolution = "";
+ public String length = "";
+ public TrackVideoInfo trackVideoInfoBean;
+ public TrackAudioInfo trackAudioInfoBean;
+}
diff --git a/ijkplayer/src/main/java/com/lodz/android/mmsplayer/ijk/bean/TrackAudioInfo.java b/ijkplayer/src/main/java/com/lodz/android/mmsplayer/ijk/bean/TrackAudioInfo.java
new file mode 100644
index 000000000..e7591f0d5
--- /dev/null
+++ b/ijkplayer/src/main/java/com/lodz/android/mmsplayer/ijk/bean/TrackAudioInfo.java
@@ -0,0 +1,13 @@
+package com.lodz.android.mmsplayer.ijk.bean;
+
+public class TrackAudioInfo {
+
+ public String type = "";
+ public String language = "";
+ public String codec = "";
+ public String profileLevel = "";
+ public String sampleRate = "";
+ public String channels = "";
+ public String bitRate = "";
+ public boolean isSelected;
+}
\ No newline at end of file
diff --git a/ijkplayer/src/main/java/com/lodz/android/mmsplayer/ijk/bean/TrackVideoInfo.java b/ijkplayer/src/main/java/com/lodz/android/mmsplayer/ijk/bean/TrackVideoInfo.java
new file mode 100644
index 000000000..00d13a9d4
--- /dev/null
+++ b/ijkplayer/src/main/java/com/lodz/android/mmsplayer/ijk/bean/TrackVideoInfo.java
@@ -0,0 +1,14 @@
+package com.lodz.android.mmsplayer.ijk.bean;
+
+public class TrackVideoInfo {
+
+ public String type = "";
+ public String language = "";
+ public String codec = "";
+ public String profileLevel = "";
+ public String pixelFormat = "";
+ public String resolution = "";
+ public String frameRate = "";
+ public String bitRate = "";
+ public boolean isSelected;
+}
diff --git a/ijkplayer/src/main/java/com/lodz/android/mmsplayer/ijk/media/IRenderView.java b/ijkplayer/src/main/java/com/lodz/android/mmsplayer/ijk/media/IRenderView.java
new file mode 100644
index 000000000..187d2b095
--- /dev/null
+++ b/ijkplayer/src/main/java/com/lodz/android/mmsplayer/ijk/media/IRenderView.java
@@ -0,0 +1,73 @@
+package com.lodz.android.mmsplayer.ijk.media;
+
+import android.graphics.SurfaceTexture;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import tv.danmaku.ijk.media.player.IMediaPlayer;
+
+public interface IRenderView {
+
+ int AR_ASPECT_FIT_PARENT = 0;
+ int AR_ASPECT_FILL_PARENT = 1;
+ 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;
+
+ 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(@NonNull IRenderCallback callback);
+
+ void removeRenderCallback(@NonNull IRenderCallback callback);
+
+ interface ISurfaceHolder {
+
+ void bindToMediaPlayer(IMediaPlayer mp);
+
+ @NonNull
+ IRenderView getRenderView();
+
+ @Nullable
+ SurfaceHolder getSurfaceHolder();
+
+ @Nullable
+ Surface openSurface();
+
+ @Nullable
+ SurfaceTexture getSurfaceTexture();
+ }
+
+ interface IRenderCallback {
+ /**
+ * @param holder
+ * @param width could be 0
+ * @param height could be 0
+ */
+ void onSurfaceCreated(@NonNull ISurfaceHolder holder, int width, int height);
+
+ /**
+ * @param holder
+ * @param format could be 0
+ * @param width
+ * @param height
+ */
+ void onSurfaceChanged(@NonNull ISurfaceHolder holder, int format, int width, int height);
+
+ void onSurfaceDestroyed(@NonNull ISurfaceHolder holder);
+ }
+}
\ No newline at end of file
diff --git a/ijkplayer/src/main/java/com/lodz/android/mmsplayer/ijk/media/IjkVideoView.java b/ijkplayer/src/main/java/com/lodz/android/mmsplayer/ijk/media/IjkVideoView.java
new file mode 100644
index 000000000..8a666555f
--- /dev/null
+++ b/ijkplayer/src/main/java/com/lodz/android/mmsplayer/ijk/media/IjkVideoView.java
@@ -0,0 +1,1102 @@
+package com.lodz.android.mmsplayer.ijk.media;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.res.Resources;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.net.Uri;
+import android.os.Build;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.MediaController;
+
+import androidx.annotation.NonNull;
+
+import com.lodz.android.mmsplayer.R;
+import com.lodz.android.mmsplayer.ijk.bean.MediaInfo;
+import com.lodz.android.mmsplayer.ijk.bean.TrackAudioInfo;
+import com.lodz.android.mmsplayer.ijk.bean.TrackVideoInfo;
+import com.lodz.android.mmsplayer.ijk.setting.IjkPlayerSetting;
+import com.lodz.android.mmsplayer.ijk.utils.MediaInfoUtils;
+
+import java.util.Locale;
+import java.util.Map;
+
+import tv.danmaku.ijk.media.player.IMediaPlayer;
+import tv.danmaku.ijk.media.player.IjkMediaPlayer;
+import tv.danmaku.ijk.media.player.misc.IMediaFormat;
+import tv.danmaku.ijk.media.player.misc.ITrackInfo;
+import tv.danmaku.ijk.media.player.misc.IjkMediaFormat;
+
+public class IjkVideoView extends FrameLayout implements MediaController.MediaPlayerControl {
+
+ private static final String TAG_LISTENER = "listener";
+ private static final String TAG = "IjkVideoView";
+
+ private Uri mUri;
+ private Map mHeaders;
+
+ private static final int STATE_ERROR = -1;
+ private static final int STATE_IDLE = 0;
+ private static final int STATE_PREPARING = 1;
+ private static final int STATE_PREPARED = 2;
+ private static final int STATE_PLAYING = 3;
+ private static final int STATE_PAUSED = 4;
+ private static final int STATE_PLAYBACK_COMPLETED = 5;
+
+ public static final int RENDER_NONE = 0;
+ public static final int RENDER_SURFACE_VIEW = 1;
+ public static final int RENDER_TEXTURE_VIEW = 2;
+
+ private int mCurrentState = STATE_IDLE;
+ private int mTargetState = STATE_IDLE;
+
+ private IRenderView.ISurfaceHolder mSurfaceHolder = null;
+ private IjkMediaPlayer mMediaPlayer = null;
+
+ private int mVideoWidth;
+ private int mVideoHeight;
+ private int mSurfaceWidth;
+ private int mSurfaceHeight;
+ private int mVideoRotationDegree;
+ private IMediaPlayer.OnCompletionListener mOnCompletionListener;
+ private IMediaPlayer.OnPreparedListener mOnPreparedListener;
+ private int mCurrentBufferPercentage;
+ private IMediaPlayer.OnErrorListener mOnErrorListener;
+ private IMediaPlayer.OnInfoListener mOnInfoListener;
+ private long mSeekWhenPrepared;
+ private boolean mCanPause = true;
+ private boolean mCanSeekBack = true;
+ private boolean mCanSeekForward = true;
+
+ private Context mAppContext;
+ private IRenderView mRenderView;
+ private int mVideoSarNum;
+ private int mVideoSarDen;
+
+ private long mPrepareStartTime = 0;
+ private long mPrepareEndTime = 0;
+
+ private long mSeekStartTime = 0;
+ private long mSeekEndTime = 0;
+
+ private IjkPlayerSetting mSetting;
+
+ public IjkVideoView(Context context) {
+ super(context);
+ initVideoView(context);
+ }
+
+ public IjkVideoView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ initVideoView(context);
+ }
+
+ public IjkVideoView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ initVideoView(context);
+ }
+
+ public IjkVideoView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ initVideoView(context);
+ }
+
+ private void initVideoView(Context context) {
+ mAppContext = context.getApplicationContext();
+ }
+
+ public void init(IjkPlayerSetting setting) {
+ mSetting = setting == null ? IjkPlayerSetting.getDefault() : setting;
+ mVideoWidth = 0;
+ mVideoHeight = 0;
+ setFocusable(true);
+ setFocusableInTouchMode(true);
+ requestFocus();
+ mCurrentState = STATE_IDLE;
+ mTargetState = STATE_IDLE;
+ }
+
+ public void setRenderView(IRenderView renderView) {
+ if (mRenderView != null) {
+ if (mMediaPlayer != null) mMediaPlayer.setDisplay(null);
+ View renderUIView = mRenderView.getView();
+ mRenderView.removeRenderCallback(mSHCallback);
+ mRenderView = null;
+ removeView(renderUIView);
+ }
+ if (renderView == null) return;
+ mRenderView = renderView;
+ mRenderView.setAspectRatio(mSetting.aspectRatioType);
+ if (mVideoWidth > 0 && mVideoHeight > 0) {
+ mRenderView.setVideoSize(mVideoWidth, mVideoHeight);
+ }
+ if (mVideoSarNum > 0 && mVideoSarDen > 0) {
+ mRenderView.setVideoSampleAspectRatio(mVideoSarNum, mVideoSarDen);
+ }
+ View renderUIView = mRenderView.getView();
+ FrameLayout.LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, Gravity.CENTER);
+ renderUIView.setLayoutParams(lp);
+ addView(renderUIView);
+ mRenderView.addRenderCallback(mSHCallback);
+ mRenderView.setVideoRotation(mVideoRotationDegree);
+ }
+
+ public void setRender(int render) {
+ switch (render) {
+ case RENDER_NONE:
+ setRenderView(null);
+ break;
+ case RENDER_TEXTURE_VIEW: {
+ TextureRenderView renderView = new TextureRenderView(getContext());
+ if (mMediaPlayer != null) {
+ renderView.getSurfaceHolder().bindToMediaPlayer(mMediaPlayer);
+ renderView.setVideoSize(mMediaPlayer.getVideoWidth(), mMediaPlayer.getVideoHeight());
+ renderView.setVideoSampleAspectRatio(mMediaPlayer.getVideoSarNum(), mMediaPlayer.getVideoSarDen());
+ renderView.setAspectRatio(mCurrentAspectRatio);
+ }
+ setRenderView(renderView);
+ break;
+ }
+ case RENDER_SURFACE_VIEW: {
+ SurfaceRenderView renderView = new SurfaceRenderView(getContext());
+ setRenderView(renderView);
+ break;
+ }
+ default:
+ Log.e(TAG, String.format(Locale.getDefault(), "invalid render %d\n", render));
+ break;
+ }
+ }
+
+ /**
+ * Sets video path.
+ *
+ * @param path the path of the video.
+ */
+ public void setVideoPath(String path) {
+ setVideoURI(Uri.parse(path));
+ }
+
+ /**
+ * Sets video URI.
+ *
+ * @param uri the URI of the video.
+ */
+ public void setVideoURI(Uri uri) {
+ setVideoURI(uri, null);
+ }
+
+ /**
+ * Sets video URI using specific headers.
+ *
+ * @param uri the URI of the video.
+ * @param headers the headers for the URI request.
+ * Note that the cross domain redirection is allowed by default, but that can be
+ * changed with key/value pairs through the headers parameter with
+ * "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value
+ * to disallow or allow cross domain redirection.
+ */
+ public void setVideoURI(Uri uri, Map headers) {
+ mUri = uri;
+ mHeaders = headers;
+ mSeekWhenPrepared = 0;
+ openVideo();
+ requestLayout();
+ invalidate();
+ }
+
+ // REMOVED: addSubtitleSource
+ // REMOVED: mPendingSubtitleTracks
+
+ public void stopPlayback() {
+ if (mMediaPlayer != null) {
+ mMediaPlayer.stop();
+ mMediaPlayer.release();
+ mMediaPlayer = null;
+ if (mHudViewHolder != null) {
+ mHudViewHolder.setMediaPlayer(null);
+ }
+ mCurrentState = STATE_IDLE;
+ mTargetState = STATE_IDLE;
+ AudioManager am = (AudioManager) mAppContext.getSystemService(Context.AUDIO_SERVICE);
+ if (am != null) {
+ am.abandonAudioFocus(null);
+ }
+ }
+ }
+
+ @TargetApi(Build.VERSION_CODES.M)
+ private void openVideo() {
+ if (mUri == null || mSurfaceHolder == null) {
+ // not ready for playback just yet, will try again later
+ return;
+ }
+ // we shouldn't clear the target state, because somebody might have
+ // called start() previously
+ release(false);
+
+ AudioManager am = (AudioManager) mAppContext.getSystemService(Context.AUDIO_SERVICE);
+ if (am != null) {
+ am.requestAudioFocus(null, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
+ }
+
+ try {
+ mMediaPlayer = createPlayer(mSetting.playerType);
+
+ // TODO: create SubtitleController in MediaPlayer, but we need
+ // a context for the subtitle renderers
+ final Context context = getContext();
+ // REMOVED: SubtitleController
+
+ // REMOVED: mAudioSession
+ mMediaPlayer.setOnPreparedListener(mPreparedListener);
+ mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener);
+ mMediaPlayer.setOnCompletionListener(mCompletionListener);
+ mMediaPlayer.setOnErrorListener(mErrorListener);
+ mMediaPlayer.setOnInfoListener(mInfoListener);
+ mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener);
+ mMediaPlayer.setOnSeekCompleteListener(mSeekCompleteListener);
+ mCurrentBufferPercentage = 0;
+ mMediaPlayer.setDataSource(mAppContext, mUri, mHeaders);
+ bindSurfaceHolder(mMediaPlayer, mSurfaceHolder);
+ mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
+ mMediaPlayer.setScreenOnWhilePlaying(true);
+ mPrepareStartTime = System.currentTimeMillis();
+ mMediaPlayer.prepareAsync();
+ if (mHudViewHolder != null) {
+ mHudViewHolder.setMediaPlayer(mMediaPlayer);
+ }
+
+ // REMOVED: mPendingSubtitleTracks
+
+ // we don't set the target state here either, but preserve the
+ // target state that was there before.
+ mCurrentState = STATE_PREPARING;
+ attachMediaController();
+ onMediaPlayerCreated(mMediaPlayer);
+ } catch (Exception ex) {
+ Log.w(TAG, "Unable to open content: " + mUri, ex);
+ mCurrentState = STATE_ERROR;
+ mTargetState = STATE_ERROR;
+ mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0);
+ } finally {
+ // REMOVED: mPendingSubtitleTracks.clear();
+ }
+ }
+
+ public void onMediaPlayerCreated(IMediaPlayer mMediaPlayer) {
+ }
+
+ public void setMediaController(IMediaController controller) {
+ if (mMediaController != null) {
+ mMediaController.hide();
+ }
+ mMediaController = controller;
+ attachMediaController();
+ }
+
+ private void attachMediaController() {
+ if (mMediaPlayer != null && mMediaController != null) {
+ mMediaController.setMediaPlayer(this);
+ View anchorView = this.getParent() instanceof View ?
+ (View) this.getParent() : this;
+ mMediaController.setAnchorView(anchorView);
+ mMediaController.setEnabled(isInPlaybackState());
+ }
+ }
+
+ IMediaPlayer.OnVideoSizeChangedListener mSizeChangedListener = new IMediaPlayer.OnVideoSizeChangedListener() {
+ public void onVideoSizeChanged(IMediaPlayer mp, int width, int height, int sarNum, int sarDen) {
+ Log.i(TAG_LISTENER, "mSizeChangedListener");
+ mVideoWidth = mp.getVideoWidth();
+ mVideoHeight = mp.getVideoHeight();
+ mVideoSarNum = mp.getVideoSarNum();
+ mVideoSarDen = mp.getVideoSarDen();
+ if (mVideoWidth != 0 && mVideoHeight != 0) {
+ if (mRenderView != null) {
+ mRenderView.setVideoSize(mVideoWidth, mVideoHeight);
+ mRenderView.setVideoSampleAspectRatio(mVideoSarNum, mVideoSarDen);
+ }
+ // REMOVED: getHolder().setFixedSize(mVideoWidth, mVideoHeight);
+ requestLayout();
+ }
+ }
+ };
+
+ IMediaPlayer.OnPreparedListener mPreparedListener = new IMediaPlayer.OnPreparedListener() {
+ public void onPrepared(IMediaPlayer mp) {
+ Log.i(TAG_LISTENER, "mPreparedListener");
+ mPrepareEndTime = System.currentTimeMillis();
+ if (mHudViewHolder != null) {
+ mHudViewHolder.updateLoadCost(mPrepareEndTime - mPrepareStartTime);
+ }
+ mCurrentState = STATE_PREPARED;
+
+ // Get the capabilities of the player for this stream
+ // REMOVED: Metadata
+
+ if (mOnPreparedListener != null) {
+ mOnPreparedListener.onPrepared(mMediaPlayer);
+ }
+ if (mMediaController != null) {
+ mMediaController.setEnabled(true);
+ }
+ mVideoWidth = mp.getVideoWidth();
+ mVideoHeight = mp.getVideoHeight();
+
+ long seekToPosition = mSeekWhenPrepared; // mSeekWhenPrepared may be changed after seekTo() call
+ if (seekToPosition != 0) {
+ seekTo(seekToPosition);
+ }
+ if (mVideoWidth != 0 && mVideoHeight != 0) {
+ //Log.i("@@@@", "video size: " + mVideoWidth +"/"+ mVideoHeight);
+ // REMOVED: getHolder().setFixedSize(mVideoWidth, mVideoHeight);
+ if (mRenderView != null) {
+ mRenderView.setVideoSize(mVideoWidth, mVideoHeight);
+ mRenderView.setVideoSampleAspectRatio(mVideoSarNum, mVideoSarDen);
+ if (!mRenderView.shouldWaitForResize() || mSurfaceWidth == mVideoWidth && mSurfaceHeight == mVideoHeight) {
+ // We didn't actually change the size (it was already at the size
+ // we need), so we won't get a "surface changed" callback, so
+ // start the video here instead of in the callback.
+ if (mTargetState == STATE_PLAYING) {
+ start();
+ if (mMediaController != null) {
+ mMediaController.show();
+ }
+ } else if (!isPlaying() &&
+ (seekToPosition != 0 || getCurrentPosition() > 0)) {
+ if (mMediaController != null) {
+ // Show the media controls when we're paused into a video and make 'em stick.
+ mMediaController.show(0);
+ }
+ }
+ }
+ }
+ } else {
+ // We don't know the video size yet, but should start anyway.
+ // The video size might be reported to us later.
+ if (mTargetState == STATE_PLAYING) {
+ start();
+ }
+ }
+ }
+ };
+
+ private IMediaPlayer.OnCompletionListener mCompletionListener = new IMediaPlayer.OnCompletionListener() {
+ public void onCompletion(IMediaPlayer mp) {
+ Log.i(TAG_LISTENER, "mCompletionListener");
+ mCurrentState = STATE_PLAYBACK_COMPLETED;
+ mTargetState = STATE_PLAYBACK_COMPLETED;
+ if (mMediaController != null) {
+ mMediaController.hide();
+ }
+ if (mOnCompletionListener != null) {
+ mOnCompletionListener.onCompletion(mMediaPlayer);
+ }
+ }
+ };
+
+ private IMediaPlayer.OnInfoListener mInfoListener = new IMediaPlayer.OnInfoListener() {
+ public boolean onInfo(IMediaPlayer mp, int arg1, int arg2) {
+ if (mOnInfoListener != null) {
+ mOnInfoListener.onInfo(mp, arg1, arg2);
+ }
+ switch (arg1) {
+ case IMediaPlayer.MEDIA_INFO_VIDEO_TRACK_LAGGING:
+ Log.d(TAG_LISTENER, "mInfoListener ---> MEDIA_INFO_VIDEO_TRACK_LAGGING:");
+ break;
+ case IMediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START:
+ Log.d(TAG_LISTENER, "mInfoListener ---> MEDIA_INFO_VIDEO_RENDERING_START:");
+ break;
+ case IMediaPlayer.MEDIA_INFO_BUFFERING_START:
+ Log.d(TAG_LISTENER, "mInfoListener ---> MEDIA_INFO_BUFFERING_START:");
+ break;
+ case IMediaPlayer.MEDIA_INFO_BUFFERING_END:
+ Log.d(TAG_LISTENER, "mInfoListener ---> MEDIA_INFO_BUFFERING_END:");
+ break;
+ case IMediaPlayer.MEDIA_INFO_NETWORK_BANDWIDTH:
+ Log.d(TAG_LISTENER, "mInfoListener ---> MEDIA_INFO_NETWORK_BANDWIDTH: " + arg2);
+ break;
+ case IMediaPlayer.MEDIA_INFO_BAD_INTERLEAVING:
+ Log.d(TAG_LISTENER, "mInfoListener ---> MEDIA_INFO_BAD_INTERLEAVING:");
+ break;
+ case IMediaPlayer.MEDIA_INFO_NOT_SEEKABLE:
+ Log.d(TAG_LISTENER, "mInfoListener ---> MEDIA_INFO_NOT_SEEKABLE:");
+ break;
+ case IMediaPlayer.MEDIA_INFO_METADATA_UPDATE:
+ Log.d(TAG_LISTENER, "mInfoListener ---> MEDIA_INFO_METADATA_UPDATE:");
+ break;
+ case IMediaPlayer.MEDIA_INFO_UNSUPPORTED_SUBTITLE:
+ Log.d(TAG_LISTENER, "mInfoListener ---> MEDIA_INFO_UNSUPPORTED_SUBTITLE:");
+ break;
+ case IMediaPlayer.MEDIA_INFO_SUBTITLE_TIMED_OUT:
+ Log.d(TAG_LISTENER, "mInfoListener ---> MEDIA_INFO_SUBTITLE_TIMED_OUT:");
+ break;
+ case IMediaPlayer.MEDIA_INFO_VIDEO_ROTATION_CHANGED:
+ mVideoRotationDegree = arg2;
+ Log.d(TAG_LISTENER, "mInfoListener ---> MEDIA_INFO_VIDEO_ROTATION_CHANGED: " + arg2);
+ if (mRenderView != null)
+ mRenderView.setVideoRotation(arg2);
+ break;
+ case IMediaPlayer.MEDIA_INFO_AUDIO_RENDERING_START:
+ Log.d(TAG_LISTENER, "mInfoListener ---> MEDIA_INFO_AUDIO_RENDERING_START:");
+ break;
+ }
+ return true;
+ }
+ };
+
+ private IMediaPlayer.OnErrorListener mErrorListener = new IMediaPlayer.OnErrorListener() {
+ public boolean onError(IMediaPlayer mp, int framework_err, int impl_err) {
+ Log.i(TAG_LISTENER, "mErrorListener");
+ Log.d(TAG, "Error: " + framework_err + "," + impl_err);
+ mCurrentState = STATE_ERROR;
+ mTargetState = STATE_ERROR;
+ if (mMediaController != null) {
+ mMediaController.hide();
+ }
+
+ /* If an error handler has been supplied, use it and finish. */
+ if (mOnErrorListener != null) {
+ if (mOnErrorListener.onError(mMediaPlayer, framework_err, impl_err)) {
+ return true;
+ }
+ }
+
+ /* Otherwise, pop up an error dialog so the user knows that
+ * something bad has happened. Only try and pop up the dialog
+ * if we're attached to a window. When we're going away and no
+ * longer have a window, don't bother showing the user an error.
+ */
+ if (getWindowToken() != null) {
+ Resources r = mAppContext.getResources();
+ int messageId;
+
+ if (framework_err == MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK) {
+ messageId = R.string.mmsplayer_videoview_error_text_invalid_progressive_playback;
+ } else {
+ messageId = R.string.mmsplayer_videoview_error_text_unknown;
+ }
+
+// new AlertDialog.Builder(getContext())
+// .setMessage(messageId)
+// .setPositiveButton(R.string.VideoView_error_button,
+// new DialogInterface.OnClickListener() {
+// public void onClick(DialogInterface dialog, int whichButton) {
+// /* If we get here, there is no onError listener, so
+// * at least inform them that the video is over.
+// */
+// if (mOnCompletionListener != null) {
+// mOnCompletionListener.onCompletion(mMediaPlayer);
+// }
+// }
+// })
+// .setCancelable(false)
+// .show();
+ }
+ return true;
+ }
+ };
+
+ private IMediaPlayer.OnBufferingUpdateListener mBufferingUpdateListener = new IMediaPlayer.OnBufferingUpdateListener() {
+ @Override
+ public void onBufferingUpdate(IMediaPlayer iMediaPlayer, int percent) {
+// Log.i(TAG_LISTENER, "mBufferingUpdateListener");
+ mCurrentBufferPercentage = percent;
+ }
+ };
+
+ private IMediaPlayer.OnSeekCompleteListener mSeekCompleteListener = new IMediaPlayer.OnSeekCompleteListener() {
+
+ @Override
+ public void onSeekComplete(IMediaPlayer mp) {
+ Log.i(TAG_LISTENER, "mSeekCompleteListener");
+ mSeekEndTime = System.currentTimeMillis();
+ if (mHudViewHolder != null) {
+ mHudViewHolder.updateSeekCost(mSeekEndTime - mSeekStartTime);
+ }
+ }
+ };
+
+ /**
+ * Register a callback to be invoked when the media file
+ * is loaded and ready to go.
+ *
+ * @param l The callback that will be run
+ */
+ public void setOnPreparedListener(IMediaPlayer.OnPreparedListener l) {
+ mOnPreparedListener = l;
+ }
+
+ /**
+ * Register a callback to be invoked when the end of a media file
+ * has been reached during playback.
+ *
+ * @param l The callback that will be run
+ */
+ public void setOnCompletionListener(IMediaPlayer.OnCompletionListener l) {
+ mOnCompletionListener = l;
+ }
+
+ /**
+ * Register a callback to be invoked when an error occurs
+ * during playback or setup. If no listener is specified,
+ * or if the listener returned false, VideoView will inform
+ * the user of any errors.
+ *
+ * @param l The callback that will be run
+ */
+ public void setOnErrorListener(IMediaPlayer.OnErrorListener l) {
+ mOnErrorListener = l;
+ }
+
+ /**
+ * Register a callback to be invoked when an informational event
+ * occurs during playback or setup.
+ *
+ * @param l The callback that will be run
+ */
+ public void setOnInfoListener(IMediaPlayer.OnInfoListener l) {
+ mOnInfoListener = l;
+ }
+
+ // REMOVED: mSHCallback
+ private void bindSurfaceHolder(IMediaPlayer mp, IRenderView.ISurfaceHolder holder) {
+ if (mp == null)
+ return;
+
+ if (holder == null) {
+ mp.setDisplay(null);
+ return;
+ }
+
+ holder.bindToMediaPlayer(mp);
+ }
+
+ IRenderView.IRenderCallback mSHCallback = new IRenderView.IRenderCallback() {
+ @Override
+ public void onSurfaceChanged(@NonNull IRenderView.ISurfaceHolder holder, int format, int w, int h) {
+ if (holder.getRenderView() != mRenderView) {
+ Log.e(TAG, "onSurfaceChanged: unmatched render callback\n");
+ return;
+ }
+
+ mSurfaceWidth = w;
+ mSurfaceHeight = h;
+ boolean isValidState = (mTargetState == STATE_PLAYING);
+ boolean hasValidSize = !mRenderView.shouldWaitForResize() || (mVideoWidth == w && mVideoHeight == h);
+ if (mMediaPlayer != null && isValidState && hasValidSize) {
+ if (mSeekWhenPrepared != 0) {
+ seekTo(mSeekWhenPrepared);
+ }
+ start();
+ }
+ }
+
+ @Override
+ public void onSurfaceCreated(@NonNull IRenderView.ISurfaceHolder holder, int width, int height) {
+ if (holder.getRenderView() != mRenderView) {
+ Log.e(TAG, "onSurfaceCreated: unmatched render callback\n");
+ return;
+ }
+
+ mSurfaceHolder = holder;
+ if (mMediaPlayer != null)
+ bindSurfaceHolder(mMediaPlayer, holder);
+ else
+ openVideo();
+ }
+
+ @Override
+ public void onSurfaceDestroyed(@NonNull IRenderView.ISurfaceHolder holder) {
+ if (holder.getRenderView() != mRenderView) {
+ Log.e(TAG, "onSurfaceDestroyed: unmatched render callback\n");
+ return;
+ }
+
+ // after we return from this we can't use the surface any more
+ mSurfaceHolder = null;
+ // REMOVED: if (mMediaController != null) mMediaController.hide();
+ // REMOVED: release(true);
+ releaseWithoutStop();
+ }
+ };
+
+ public void releaseWithoutStop() {
+ if (mMediaPlayer != null)
+ mMediaPlayer.setDisplay(null);
+ }
+
+ /*
+ * release the media player in any state
+ */
+ public void release(boolean cleartargetstate) {
+ if (mMediaPlayer != null) {
+ mMediaPlayer.reset();
+ mMediaPlayer.release();
+ mMediaPlayer = null;
+ // REMOVED: mPendingSubtitleTracks.clear();
+ mCurrentState = STATE_IDLE;
+ if (cleartargetstate) {
+ mTargetState = STATE_IDLE;
+ }
+ AudioManager am = (AudioManager) mAppContext.getSystemService(Context.AUDIO_SERVICE);
+ if (am != null) {
+ am.abandonAudioFocus(null);
+ }
+ }
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ if (isInPlaybackState() && mMediaController != null) {
+ toggleMediaControlsVisiblity();
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onTrackballEvent(MotionEvent ev) {
+ if (isInPlaybackState() && mMediaController != null) {
+ toggleMediaControlsVisiblity();
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ boolean isKeyCodeSupported = keyCode != KeyEvent.KEYCODE_BACK &&
+ keyCode != KeyEvent.KEYCODE_VOLUME_UP &&
+ keyCode != KeyEvent.KEYCODE_VOLUME_DOWN &&
+ keyCode != KeyEvent.KEYCODE_VOLUME_MUTE &&
+ keyCode != KeyEvent.KEYCODE_MENU &&
+ keyCode != KeyEvent.KEYCODE_CALL &&
+ keyCode != KeyEvent.KEYCODE_ENDCALL;
+ if (isInPlaybackState() && isKeyCodeSupported && mMediaController != null) {
+ if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK ||
+ keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) {
+ if (mMediaPlayer.isPlaying()) {
+ pause();
+ mMediaController.show();
+ } else {
+ start();
+ mMediaController.hide();
+ }
+ return true;
+ } else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) {
+ if (!mMediaPlayer.isPlaying()) {
+ start();
+ mMediaController.hide();
+ }
+ return true;
+ } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP
+ || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) {
+ if (mMediaPlayer.isPlaying()) {
+ pause();
+ mMediaController.show();
+ }
+ return true;
+ } else {
+ toggleMediaControlsVisiblity();
+ }
+ }
+
+ return super.onKeyDown(keyCode, event);
+ }
+
+ private void toggleMediaControlsVisiblity() {
+ if (mMediaController.isShowing()) {
+ mMediaController.hide();
+ } else {
+ mMediaController.show();
+ }
+ }
+
+ @Override
+ public void start() {
+ if (isInPlaybackState()) {
+ mMediaPlayer.start();
+ mCurrentState = STATE_PLAYING;
+ }
+ mTargetState = STATE_PLAYING;
+ }
+
+ @Override
+ public void pause() {
+ if (isInPlaybackState()) {
+ if (mMediaPlayer.isPlaying()) {
+ mMediaPlayer.pause();
+ mCurrentState = STATE_PAUSED;
+ }
+ }
+ mTargetState = STATE_PAUSED;
+ }
+
+ public void suspend() {
+ release(false);
+ }
+
+ public void resume() {
+ openVideo();
+ start();
+ }
+
+ @Override
+ public int getDuration() {
+ if (isInPlaybackState()) {
+ return (int) mMediaPlayer.getDuration();
+ }
+
+ return -1;
+ }
+
+ public long getVideoDuration() {
+ if (isInPlaybackState()) {
+ return mMediaPlayer.getDuration();
+ }
+ return -1;
+ }
+
+ @Override
+ public int getCurrentPosition() {
+ if (isInPlaybackState()) {
+ return (int) mMediaPlayer.getCurrentPosition();
+ }
+ return 0;
+ }
+
+ public long getCurrentPlayPosition() {
+ if (isInPlaybackState()) {
+ return mMediaPlayer.getCurrentPosition();
+ }
+ return 0;
+ }
+
+ public long getBreakPosition() {
+ long position = 0;
+ try {
+ position = mMediaPlayer.getCurrentPosition();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return position;
+ }
+
+ @Override
+ public void seekTo(int msec) {
+ if (isInPlaybackState()) {
+ mSeekStartTime = System.currentTimeMillis();
+ mMediaPlayer.seekTo(msec);
+ mSeekWhenPrepared = 0;
+ } else {
+ mSeekWhenPrepared = msec;
+ }
+ }
+
+ public void seekTo(long position) {
+ if (isInPlaybackState()) {
+ mSeekStartTime = System.currentTimeMillis();
+ mMediaPlayer.seekTo(position);
+ mSeekWhenPrepared = 0;
+ } else {
+ mSeekWhenPrepared = position;
+ }
+ }
+
+ @Override
+ public boolean isPlaying() {
+ return isInPlaybackState() && mMediaPlayer.isPlaying();
+ }
+
+ public boolean isPause() {
+ return isInPlaybackState() && mCurrentState == STATE_PAUSED;
+ }
+
+ public boolean isCompleted() {
+ return isInPlaybackState() && mCurrentState == STATE_PLAYBACK_COMPLETED;
+ }
+
+ public boolean isAlreadySetPath() {
+ return mUri != null;
+ }
+
+ @Override
+ public int getBufferPercentage() {
+ if (mMediaPlayer != null) {
+ return mCurrentBufferPercentage;
+ }
+ return 0;
+ }
+
+ private boolean isInPlaybackState() {
+ return (mMediaPlayer != null &&
+ mCurrentState != STATE_ERROR &&
+ mCurrentState != STATE_IDLE &&
+ mCurrentState != STATE_PREPARING);
+ }
+
+ @Override
+ public boolean canPause() {
+ return mCanPause;
+ }
+
+ @Override
+ public boolean canSeekBackward() {
+ return mCanSeekBack;
+ }
+
+ @Override
+ public boolean canSeekForward() {
+ return mCanSeekForward;
+ }
+
+ @Override
+ public int getAudioSessionId() {
+ return 0;
+ }
+
+ private static final int[] s_allAspectRatio = {
+ IRenderView.AR_ASPECT_FIT_PARENT,
+ IRenderView.AR_ASPECT_FILL_PARENT,
+ IRenderView.AR_ASPECT_WRAP_CONTENT,
+ IRenderView.AR_MATCH_PARENT,
+ IRenderView.AR_16_9_FIT_PARENT,
+ IRenderView.AR_4_3_FIT_PARENT};
+
+ private int mCurrentAspectRatioIndex = 0;
+ private int mCurrentAspectRatio = s_allAspectRatio[0];
+
+ public int toggleAspectRatio() {
+ mCurrentAspectRatioIndex++;
+ mCurrentAspectRatioIndex %= s_allAspectRatio.length;
+ mCurrentAspectRatio = s_allAspectRatio[mCurrentAspectRatioIndex];
+ if (mRenderView != null) mRenderView.setAspectRatio(mCurrentAspectRatio);
+ return mCurrentAspectRatio;
+ }
+
+ @NonNull
+ public String getRenderText(Context context) {
+ String text;
+ switch (mSetting.renderViewType) {
+ case IjkPlayerSetting.RenderViewType.NO_VIEW:
+ text = context.getString(R.string.mmsplayer_videoview_render_none);
+ break;
+ case IjkPlayerSetting.RenderViewType.SURFACE_VIEW:
+ text = context.getString(R.string.mmsplayer_videoview_render_surface_view);
+ break;
+ case IjkPlayerSetting.RenderViewType.TEXTURE_VIEW:
+ text = context.getString(R.string.mmsplayer_videoview_render_texture_view);
+ break;
+ default:
+ text = context.getString(R.string.mmsplayer_n_a);
+ break;
+ }
+ return text;
+ }
+
+ //-------------------------
+ // Extend: Player
+ //-------------------------
+
+ @NonNull
+ public String getPlayerText(Context context) {
+ String text;
+ switch (mSetting.playerType) {
+ case IjkPlayerSetting.PlayerType.PALY_ANDROID_MEDIA:
+ text = context.getString(R.string.mmsplayer_videoview_player_androidmediaplayer);
+ break;
+ case IjkPlayerSetting.PlayerType.PLAY_IJK:
+ text = context.getString(R.string.mmsplayer_videoview_player_ijkmediaplayer);
+ break;
+ default:
+ text = context.getString(R.string.mmsplayer_n_a);
+ break;
+ }
+ return text;
+ }
+
+ public IjkMediaPlayer createPlayer(@IjkPlayerSetting.PlayerType int playerType) {
+ IjkMediaPlayer ijkMediaPlayer = new IjkMediaPlayer();
+ IjkMediaPlayer.native_setLogLevel(IjkMediaPlayer.IJK_LOG_DEBUG);
+ if (mSetting.isUsingMediaCodec) {
+ ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 1);
+ if (mSetting.isUsingMediaCodecAutoRotate) {
+ ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 1);
+ } else {
+ ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 0);
+ }
+ if (mSetting.isMediaCodecHandleResolutionChange) {
+ ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-handle-resolution-change", 1);
+ } else {
+ ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-handle-resolution-change", 0);
+ }
+ } else {
+ ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 0);
+ }
+
+ if (mSetting.isUsingOpenSLES) {
+ ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "opensles", 1);
+ } else {
+ ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "opensles", 0);
+ }
+
+ String pixelFormat = getPixelFormat(mSetting.pixelFormatType);
+ if (TextUtils.isEmpty(pixelFormat)) {
+ ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "overlay-format", IjkMediaPlayer.SDL_FCC_RV32);
+ } else {
+ ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "overlay-format", pixelFormat);
+ }
+ ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "framedrop", 1);
+ ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", 0);
+ ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "http-detect-range-support", 0);
+ ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_loop_filter", 48);
+ return ijkMediaPlayer;
+ }
+
+ private String getPixelFormat(int pixelFormatType) {
+ switch (pixelFormatType) {
+ case IjkPlayerSetting.PixelFormatType.PIXEL_RGB_565:
+ return "fcc-rv16";
+ case IjkPlayerSetting.PixelFormatType.PIXEL_RGB_888:
+ return "fcc-rv24";
+ case IjkPlayerSetting.PixelFormatType.PIXEL_RGBX_8888:
+ return "fcc-rv32";
+ case IjkPlayerSetting.PixelFormatType.PIXEL_YV12:
+ return "fcc-yv12";
+ case IjkPlayerSetting.PixelFormatType.PIXEL_OPENGL_ES2:
+ return "fcc-_es2";
+ case IjkPlayerSetting.PixelFormatType.PIXEL_AUTO:
+ default:
+ return "";
+ }
+ }
+
+ /** ----------------------- 轨道处理 -------------------------------- */
+ /**
+ * 获取视频信息
+ */
+ public MediaInfo getMediaInfo() {
+ if (mMediaPlayer == null) {
+ return null;
+ }
+
+ IMediaPlayer player = mMediaPlayer;
+
+ MediaInfo mediaInfoBean = new MediaInfo();
+ mediaInfoBean.playerName = MediaPlayerCompat.getName(player);
+ mediaInfoBean.resolution = MediaInfoUtils.buildResolution(mVideoWidth, mVideoHeight, mVideoSarNum, mVideoSarDen);
+ mediaInfoBean.length = MediaInfoUtils.buildTimeMilli(player.getDuration());
+
+ ITrackInfo trackInfos[] = player.getTrackInfo();
+ if (trackInfos == null || trackInfos.length == 0) {// 没有轨道信息直接返回
+ return mediaInfoBean;
+ }
+
+ TrackVideoInfo videoInfoBean = new TrackVideoInfo();
+ TrackAudioInfo audioInfoBean = new TrackAudioInfo();
+ int index = -1;
+ for (ITrackInfo trackInfo : trackInfos) {
+ index++;
+ int trackType = trackInfo.getTrackType();//获取轨道类型
+ IMediaFormat mediaFormat = trackInfo.getFormat();
+ if (mediaFormat instanceof IjkMediaFormat) {
+ switch (trackType) {
+ case ITrackInfo.MEDIA_TRACK_TYPE_VIDEO:
+ videoInfoBean = getTrackVideoInfoBean(index, player, mediaFormat, trackInfo);
+ break;
+ case ITrackInfo.MEDIA_TRACK_TYPE_AUDIO:
+ audioInfoBean = getTrackAudioInfoBean(index, player, mediaFormat, trackInfo);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ // 获取当前选中的视频和音频轨道index
+ mediaInfoBean.trackVideoInfoBean = videoInfoBean;
+ mediaInfoBean.trackAudioInfoBean = audioInfoBean;
+ return mediaInfoBean;
+ }
+
+ /**
+ * 获取视频轨信息
+ *
+ * @param index 索引
+ * @param player 播放器
+ * @param mediaFormat 视频格式
+ * @param trackInfo 轨道信息
+ */
+ private TrackVideoInfo getTrackVideoInfoBean(int index, IMediaPlayer player, IMediaFormat mediaFormat, ITrackInfo trackInfo) {
+ TrackVideoInfo videoInfoBean = new TrackVideoInfo();
+ int selectedVideoTrack = MediaPlayerCompat.getSelectedTrack(player, ITrackInfo.MEDIA_TRACK_TYPE_VIDEO);
+ if (index == selectedVideoTrack) {
+ videoInfoBean.isSelected = true;
+ }
+ videoInfoBean.type = MediaInfoUtils.buildTrackType(getContext(), ITrackInfo.MEDIA_TRACK_TYPE_VIDEO);
+ videoInfoBean.language = MediaInfoUtils.buildLanguage(trackInfo.getLanguage());
+ videoInfoBean.codec = mediaFormat.getString(IjkMediaFormat.KEY_IJK_CODEC_LONG_NAME_UI);
+ videoInfoBean.profileLevel = mediaFormat.getString(IjkMediaFormat.KEY_IJK_CODEC_PROFILE_LEVEL_UI);
+ videoInfoBean.pixelFormat = mediaFormat.getString(IjkMediaFormat.KEY_IJK_CODEC_PIXEL_FORMAT_UI);
+ videoInfoBean.resolution = mediaFormat.getString(IjkMediaFormat.KEY_IJK_RESOLUTION_UI);
+ videoInfoBean.frameRate = mediaFormat.getString(IjkMediaFormat.KEY_IJK_FRAME_RATE_UI);
+ videoInfoBean.bitRate = mediaFormat.getString(IjkMediaFormat.KEY_IJK_BIT_RATE_UI);
+ return videoInfoBean;
+ }
+
+ /**
+ * 获取音频轨信息
+ *
+ * @param index 索引
+ * @param player 播放器
+ * @param mediaFormat 音频格式
+ * @param trackInfo 轨道信息
+ */
+ private TrackAudioInfo getTrackAudioInfoBean(int index, IMediaPlayer player, IMediaFormat mediaFormat, ITrackInfo trackInfo) {
+ TrackAudioInfo audioInfoBean = new TrackAudioInfo();
+ int selectedAudioTrack = MediaPlayerCompat.getSelectedTrack(player, ITrackInfo.MEDIA_TRACK_TYPE_AUDIO);
+ if (index == selectedAudioTrack) {
+ audioInfoBean.isSelected = true;
+ }
+ audioInfoBean.type = MediaInfoUtils.buildTrackType(getContext(), ITrackInfo.MEDIA_TRACK_TYPE_AUDIO);
+ audioInfoBean.language = MediaInfoUtils.buildLanguage(trackInfo.getLanguage());
+ audioInfoBean.codec = mediaFormat.getString(IjkMediaFormat.KEY_IJK_CODEC_LONG_NAME_UI);
+ audioInfoBean.profileLevel = mediaFormat.getString(IjkMediaFormat.KEY_IJK_CODEC_PROFILE_LEVEL_UI);
+ audioInfoBean.sampleRate = mediaFormat.getString(IjkMediaFormat.KEY_IJK_SAMPLE_RATE_UI);
+ audioInfoBean.channels = mediaFormat.getString(IjkMediaFormat.KEY_IJK_CHANNEL_UI);
+ audioInfoBean.bitRate = mediaFormat.getString(IjkMediaFormat.KEY_IJK_BIT_RATE_UI);
+ return audioInfoBean;
+ }
+
+ public ITrackInfo[] getTrackInfo() {
+ if (mMediaPlayer == null)
+ return null;
+
+ return mMediaPlayer.getTrackInfo();
+ }
+
+ public void selectTrack(int stream) {
+ MediaPlayerCompat.selectTrack(mMediaPlayer, stream);
+ }
+
+ public void deselectTrack(int stream) {
+ MediaPlayerCompat.deselectTrack(mMediaPlayer, stream);
+ }
+
+ public int getSelectedTrack(int trackType) {
+ return MediaPlayerCompat.getSelectedTrack(mMediaPlayer, trackType);
+ }
+
+ public void setSpeed(float speed) {
+ if (mMediaPlayer != null) {
+ mMediaPlayer.setSpeed(speed);
+ }
+ }
+}
\ No newline at end of file
diff --git a/ijkplayer/src/main/java/com/lodz/android/mmsplayer/ijk/media/MeasureHelper.java b/ijkplayer/src/main/java/com/lodz/android/mmsplayer/ijk/media/MeasureHelper.java
new file mode 100644
index 000000000..f72ae6cc3
--- /dev/null
+++ b/ijkplayer/src/main/java/com/lodz/android/mmsplayer/ijk/media/MeasureHelper.java
@@ -0,0 +1,162 @@
+package com.lodz.android.mmsplayer.ijk.media;
+
+import android.view.View;
+
+import java.lang.ref.WeakReference;
+
+public final class MeasureHelper {
+
+ private final WeakReference 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);
+ }
+
+ public View getView() {
+ 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;
+ }
+
+ public void doMeasure(int widthMeasureSpec, int 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) {
+ width = widthSpecSize;
+ height = (int) (width / displayAspectRatio);
+ } else {
+ height = heightSpecSize;
+ width = (int) (height * displayAspectRatio);
+ }
+ break;
+ case IRenderView.AR_ASPECT_FILL_PARENT:
+ if (shouldBeWider) {
+ height = heightSpecSize;
+ width = (int) (height * displayAspectRatio);
+ } else {
+ width = widthSpecSize;
+ height = (int) (width / displayAspectRatio);
+ }
+ break;
+ case IRenderView.AR_ASPECT_WRAP_CONTENT:
+ default:
+ if (shouldBeWider) {
+ width = Math.min(mVideoWidth, widthSpecSize);
+ height = (int) (width / displayAspectRatio);
+ } else {
+ height = Math.min(mVideoHeight, heightSpecSize);
+ width = (int) (height * displayAspectRatio);
+ }
+ break;
+ }
+ } else if (widthSpecMode == View.MeasureSpec.EXACTLY && heightSpecMode == View.MeasureSpec.EXACTLY) {
+ width = widthSpecSize;
+ height = heightSpecSize;
+ if (mVideoWidth * height < width * mVideoHeight) {
+ width = height * mVideoWidth / mVideoHeight;
+ } else if (mVideoWidth * height > width * mVideoHeight) {
+ height = width * mVideoHeight / mVideoWidth;
+ }
+ } else if (widthSpecMode == View.MeasureSpec.EXACTLY) {
+ width = widthSpecSize;
+ height = width * mVideoHeight / mVideoWidth;
+ if (heightSpecMode == View.MeasureSpec.AT_MOST && height > heightSpecSize) {
+ height = heightSpecSize;
+ }
+ } else if (heightSpecMode == View.MeasureSpec.EXACTLY) {
+ height = heightSpecSize;
+ width = height * mVideoWidth / mVideoHeight;
+ if (widthSpecMode == View.MeasureSpec.AT_MOST && width > widthSpecSize) {
+ width = widthSpecSize;
+ }
+ } else {
+ width = mVideoWidth;
+ height = mVideoHeight;
+ if (heightSpecMode == View.MeasureSpec.AT_MOST && height > heightSpecSize) {
+ height = heightSpecSize;
+ width = height * mVideoWidth / mVideoHeight;
+ }
+ if (widthSpecMode == View.MeasureSpec.AT_MOST && width > widthSpecSize) {
+ width = widthSpecSize;
+ height = width * mVideoHeight / mVideoWidth;
+ }
+ }
+ }
+ mMeasuredWidth = width;
+ mMeasuredHeight = height;
+ }
+
+ public int getMeasuredWidth() {
+ return mMeasuredWidth;
+ }
+
+ public int getMeasuredHeight() {
+ return mMeasuredHeight;
+ }
+
+ public void setAspectRatio(int aspectRatio) {
+ mCurrentAspectRatio = aspectRatio;
+ }
+}
\ No newline at end of file
diff --git a/ijkplayer/src/main/java/com/lodz/android/mmsplayer/ijk/media/MediaPlayerCompat.java b/ijkplayer/src/main/java/com/lodz/android/mmsplayer/ijk/media/MediaPlayerCompat.java
new file mode 100644
index 000000000..e5dab397f
--- /dev/null
+++ b/ijkplayer/src/main/java/com/lodz/android/mmsplayer/ijk/media/MediaPlayerCompat.java
@@ -0,0 +1,56 @@
+package com.lodz.android.mmsplayer.ijk.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);
+ }
+}
diff --git a/ijkplayer/src/main/java/com/lodz/android/mmsplayer/ijk/media/SurfaceRenderView.java b/ijkplayer/src/main/java/com/lodz/android/mmsplayer/ijk/media/SurfaceRenderView.java
new file mode 100644
index 000000000..0e747152f
--- /dev/null
+++ b/ijkplayer/src/main/java/com/lodz/android/mmsplayer/ijk/media/SurfaceRenderView.java
@@ -0,0 +1,240 @@
+package com.lodz.android.mmsplayer.ijk.media;
+
+import android.content.Context;
+import android.graphics.SurfaceTexture;
+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 androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+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;
+
+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);
+ }
+
+ 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);
+ getHolder().setType(SurfaceHolder.SURFACE_TYPE_NORMAL);
+ }
+
+ @Override
+ public View getView() {
+ return this;
+ }
+
+ @Override
+ public boolean shouldWaitForResize() {
+ return true;
+ }
+
+ @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());
+ }
+
+ private static final class InternalSurfaceHolder implements IRenderView.ISurfaceHolder {
+
+ private final SurfaceRenderView mSurfaceView;
+ private final SurfaceHolder mSurfaceHolder;
+
+ private InternalSurfaceHolder(@NonNull SurfaceRenderView surfaceView, @Nullable SurfaceHolder surfaceHolder) {
+ mSurfaceView = surfaceView;
+ mSurfaceHolder = surfaceHolder;
+ }
+
+ public void bindToMediaPlayer(IMediaPlayer mp) {
+ if (mp != null) {
+ if (mp instanceof ISurfaceTextureHolder) {
+ ISurfaceTextureHolder textureHolder = (ISurfaceTextureHolder) mp;
+ textureHolder.setSurfaceTexture(null);
+ }
+ mp.setDisplay(mSurfaceHolder);
+ }
+ }
+
+ @NonNull
+ @Override
+ public IRenderView getRenderView() {
+ return mSurfaceView;
+ }
+
+ @Nullable
+ @Override
+ public SurfaceHolder getSurfaceHolder() {
+ return mSurfaceHolder;
+ }
+
+ @Nullable
+ @Override
+ public SurfaceTexture getSurfaceTexture() {
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public Surface openSurface() {
+ if (mSurfaceHolder == null) return null;
+ return mSurfaceHolder.getSurface();
+ }
+ }
+
+ @Override
+ public void addRenderCallback(@NonNull IRenderCallback callback) {
+ mSurfaceCallback.addRenderCallback(callback);
+ }
+
+ @Override
+ public void removeRenderCallback(@NonNull 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 final WeakReference mWeakSurfaceView;
+ private final Map mRenderCallbackMap = new ConcurrentHashMap<>();
+
+ private SurfaceCallback(@NonNull SurfaceRenderView surfaceView) {
+ mWeakSurfaceView = new WeakReference<>(surfaceView);
+ }
+
+ private void addRenderCallback(@NonNull IRenderCallback callback) {
+ mRenderCallbackMap.put(callback, callback);
+ ISurfaceHolder surfaceHolder = null;
+ if (mSurfaceHolder != 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);
+ }
+ }
+
+ private void removeRenderCallback(@NonNull 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;
+ ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakSurfaceView.get(), mSurfaceHolder);
+ for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) {
+ renderCallback.onSurfaceChanged(surfaceHolder, format, width, height);
+ }
+ }
+ }
+
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(SurfaceRenderView.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(SurfaceRenderView.class.getName());
+ }
+}
\ No newline at end of file
diff --git a/ijkplayer/src/main/java/com/lodz/android/mmsplayer/ijk/media/TextureRenderView.java b/ijkplayer/src/main/java/com/lodz/android/mmsplayer/ijk/media/TextureRenderView.java
new file mode 100644
index 000000000..40423e270
--- /dev/null
+++ b/ijkplayer/src/main/java/com/lodz/android/mmsplayer/ijk/media/TextureRenderView.java
@@ -0,0 +1,292 @@
+package com.lodz.android.mmsplayer.ijk.media;
+
+import android.content.Context;
+import android.graphics.SurfaceTexture;
+import android.util.AttributeSet;
+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 androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+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;
+
+public class TextureRenderView extends TextureView implements IRenderView {
+
+ 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);
+ }
+
+ 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();
+ }
+
+ @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());
+ }
+
+ public IRenderView.ISurfaceHolder getSurfaceHolder() {
+ return new InternalSurfaceHolder(this, mSurfaceCallback.mSurfaceTexture);
+ }
+
+ private static final class InternalSurfaceHolder implements IRenderView.ISurfaceHolder {
+
+ private final TextureRenderView mTextureView;
+ private final SurfaceTexture mSurfaceTexture;
+
+ private InternalSurfaceHolder(@NonNull TextureRenderView textureView, @Nullable SurfaceTexture surfaceTexture) {
+ mTextureView = textureView;
+ mSurfaceTexture = surfaceTexture;
+ }
+
+ public void bindToMediaPlayer(IMediaPlayer mp) {
+ if (mp == null) return;
+ if (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());
+ }
+ }
+
+ @NonNull
+ @Override
+ public IRenderView getRenderView() {
+ return mTextureView;
+ }
+
+ @Nullable
+ @Override
+ public SurfaceHolder getSurfaceHolder() {
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public SurfaceTexture getSurfaceTexture() {
+ return mSurfaceTexture;
+ }
+
+ @Nullable
+ @Override
+ public Surface openSurface() {
+ if (mSurfaceTexture == null) return null;
+ return new Surface(mSurfaceTexture);
+ }
+ }
+
+ @Override
+ public void addRenderCallback(@NonNull IRenderCallback callback) {
+ mSurfaceCallback.addRenderCallback(callback);
+ }
+
+ @Override
+ public void removeRenderCallback(@NonNull 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 final WeakReference mWeakRenderView;
+ private final Map mRenderCallbackMap = new ConcurrentHashMap<>();
+
+ private SurfaceCallback(@NonNull TextureRenderView renderView) {
+ mWeakRenderView = new WeakReference<>(renderView);
+ }
+
+ private void setOwnSurfaceTexture(boolean ownSurfaceTexture) {
+ mOwnSurfaceTexture = ownSurfaceTexture;
+ }
+
+ private void addRenderCallback(@NonNull IRenderCallback callback) {
+ mRenderCallbackMap.put(callback, callback);
+ ISurfaceHolder surfaceHolder = null;
+ if (mSurfaceTexture != null) {
+ surfaceHolder = new InternalSurfaceHolder(mWeakRenderView.get(), mSurfaceTexture);
+ callback.onSurfaceCreated(surfaceHolder, mWidth, mHeight);
+ }
+ if (mIsFormatChanged) {
+ if (surfaceHolder == null) surfaceHolder = new InternalSurfaceHolder(mWeakRenderView.get(), mSurfaceTexture);
+ callback.onSurfaceChanged(surfaceHolder, 0, mWidth, mHeight);
+ }
+ }
+
+ private void removeRenderCallback(@NonNull 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);
+ 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);
+ 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);
+ for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) renderCallback.onSurfaceDestroyed(surfaceHolder);
+ return mOwnSurfaceTexture;
+ }
+
+ @Override
+ public void onSurfaceTextureUpdated(SurfaceTexture surface) {
+ }
+
+ @Override
+ public void releaseSurfaceTexture(SurfaceTexture surfaceTexture) {
+ if (surfaceTexture == null) return;
+ if (mDidDetachFromWindow) {
+ if (surfaceTexture != mSurfaceTexture) {
+ surfaceTexture.release();
+ } else if (!mOwnSurfaceTexture) {
+ surfaceTexture.release();
+ }
+ } else if (mWillDetachFromWindow) {
+ if (surfaceTexture != mSurfaceTexture) {
+ surfaceTexture.release();
+ } else if (!mOwnSurfaceTexture) {
+ setOwnSurfaceTexture(true);
+ }
+ } else {
+ if (surfaceTexture != mSurfaceTexture) {
+ surfaceTexture.release();
+ } else if (!mOwnSurfaceTexture) {
+ setOwnSurfaceTexture(true);
+ }
+ }
+ }
+
+ private void willDetachFromWindow() {
+ mWillDetachFromWindow = true;
+ }
+
+ private void didDetachFromWindow() {
+ mDidDetachFromWindow = true;
+ }
+ }
+
+ @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());
+ }
+}
\ No newline at end of file
diff --git a/ijkplayer/src/main/java/com/lodz/android/mmsplayer/ijk/setting/IjkPlayerSetting.java b/ijkplayer/src/main/java/com/lodz/android/mmsplayer/ijk/setting/IjkPlayerSetting.java
new file mode 100644
index 000000000..4b91075d9
--- /dev/null
+++ b/ijkplayer/src/main/java/com/lodz/android/mmsplayer/ijk/setting/IjkPlayerSetting.java
@@ -0,0 +1,128 @@
+package com.lodz.android.mmsplayer.ijk.setting;
+
+
+import androidx.annotation.IntDef;
+import com.lodz.android.mmsplayer.ijk.media.IRenderView;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+public class IjkPlayerSetting {
+
+ public static IjkPlayerSetting getDefault(){
+ IjkPlayerSetting setting = new IjkPlayerSetting();
+ setting.playerType = PlayerType.PLAY_IJK;
+ setting.isBackgroundPlay = false;
+ setting.isUsingMediaCodec = true;
+ setting.isUsingMediaCodecAutoRotate = true;
+ setting.isMediaCodecHandleResolutionChange = true;
+ setting.isUsingOpenSLES = true;
+ setting.pixelFormatType = PixelFormatType.PIXEL_AUTO;
+ setting.isUsingMediaDataSource = true;
+ setting.renderViewType = RenderViewType.TEXTURE_VIEW;
+ setting.isEnableDetachedSurfaceTexture = true;
+ setting.aspectRatioType = IRenderView.AR_ASPECT_FIT_PARENT;
+ return setting;
+ }
+
+ //--------------------------- 播放器设置 -----------------------------------
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({PlayerType.PALY_ANDROID_MEDIA, PlayerType.PLAY_IJK})
+ public @interface PlayerType {
+ /** android原生播放器 */
+ int PALY_ANDROID_MEDIA = 1;
+ /** Ijk播放器 */
+ int PLAY_IJK = 2;
+ }
+
+ /** 播放器类型 */
+ @PlayerType
+ public int playerType = PlayerType.PLAY_IJK;
+
+//--------------------------- 后台播放设置 -----------------------------------
+ /** 是否后台播放(4.0+) */
+ public boolean isBackgroundPlay = false;
+
+//--------------------------- 硬解码设置 -----------------------------------
+ /** 是否使用硬解码 */
+ public boolean isUsingMediaCodec = true;
+ /** 是否使用硬解码下自动旋转 */
+ public boolean isUsingMediaCodecAutoRotate = true;
+ /** 是否使用硬解码下处理分辨率更改 */
+ public boolean isMediaCodecHandleResolutionChange = true;
+
+//--------------------------- 使用OpenGLES -----------------------------------
+ /** 是否使用OpenGLES */
+ public boolean isUsingOpenSLES = true;
+
+ //--------------------------- 设置像素格式 -----------------------------------
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({PixelFormatType.PIXEL_AUTO, PixelFormatType.PIXEL_RGB_565, PixelFormatType.PIXEL_RGB_888, PixelFormatType.PIXEL_RGBX_8888,
+ PixelFormatType.PIXEL_YV12, PixelFormatType.PIXEL_OPENGL_ES2})
+ public @interface PixelFormatType {
+ /** 自动选择 */
+ int PIXEL_AUTO = 0;
+ /** RGB 565 */
+ int PIXEL_RGB_565 = 1;
+ /** RGB 888 */
+ int PIXEL_RGB_888 = 2;
+ /** RGBX 8888 */
+ int PIXEL_RGBX_8888 = 3;
+ /** YV12 */
+ int PIXEL_YV12 = 4;
+ /** OpenGL ES2 */
+ int PIXEL_OPENGL_ES2 = 5;
+ }
+
+ /** 像素格式 */
+ @PixelFormatType
+ public int pixelFormatType = PixelFormatType.PIXEL_AUTO;
+
+//--------------------------- 使用数据源 -----------------------------------
+ /** 是否使用数据源(需要6.0+且播放本地文件) */
+ public boolean isUsingMediaDataSource = true;
+
+ //--------------------------- 渲染的view -----------------------------------
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({RenderViewType.NO_VIEW, RenderViewType.SURFACE_VIEW, RenderViewType.TEXTURE_VIEW})
+ public @interface RenderViewType {
+ /** 不使用渲染的view */
+ int NO_VIEW = 0;
+ /** 用surfaceview渲染 */
+ int SURFACE_VIEW = 1;
+ /** 用textureview渲染 */
+ int TEXTURE_VIEW = 2;
+ }
+
+ /** 渲染的view的类型 */
+ @RenderViewType
+ public int renderViewType = RenderViewType.TEXTURE_VIEW;
+
+ /** 是否使用SurfaceTexture处理视频图像 */
+ public boolean isEnableDetachedSurfaceTexture = true;
+
+ //--------------------------- 播放器长宽比类型 -----------------------------------
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({IRenderView.AR_ASPECT_FIT_PARENT, IRenderView.AR_ASPECT_FILL_PARENT, IRenderView.AR_ASPECT_WRAP_CONTENT,
+ IRenderView.AR_16_9_FIT_PARENT, IRenderView.AR_4_3_FIT_PARENT})
+ public @interface AspectRatioType {}
+ @AspectRatioType
+ public int aspectRatioType = IRenderView.AR_ASPECT_FIT_PARENT;
+
+ @Override
+ public String toString() {
+ return "IjkPlayerSetting{" +
+ "\n playerType=" + playerType +
+ ", \n isBackgroundPlay=" + isBackgroundPlay +
+ ", \n isUsingMediaCodec=" + isUsingMediaCodec +
+ ", \n isUsingMediaCodecAutoRotate=" + isUsingMediaCodecAutoRotate +
+ ", \n isMediaCodecHandleResolutionChange=" + isMediaCodecHandleResolutionChange +
+ ", \n isUsingOpenSLES=" + isUsingOpenSLES +
+ ", \n pixelFormatType=" + pixelFormatType +
+ ", \n isUsingMediaDataSource=" + isUsingMediaDataSource +
+ ", \n renderViewType=" + renderViewType +
+ ", \n isEnableDetachedSurfaceTexture=" + isEnableDetachedSurfaceTexture +
+ ", \n aspectRatioType=" + aspectRatioType +
+ '}';
+ }
+}
\ No newline at end of file
diff --git a/ijkplayer/src/main/java/com/lodz/android/mmsplayer/ijk/utils/MediaInfoUtils.java b/ijkplayer/src/main/java/com/lodz/android/mmsplayer/ijk/utils/MediaInfoUtils.java
new file mode 100644
index 000000000..df0a6e13e
--- /dev/null
+++ b/ijkplayer/src/main/java/com/lodz/android/mmsplayer/ijk/utils/MediaInfoUtils.java
@@ -0,0 +1,71 @@
+package com.lodz.android.mmsplayer.ijk.utils;
+
+import android.content.Context;
+import android.text.TextUtils;
+
+import com.lodz.android.mmsplayer.R;
+
+import java.util.Locale;
+
+import tv.danmaku.ijk.media.player.misc.ITrackInfo;
+
+/**
+ * 视频信息帮助类
+ * Created by zhouL on 2016/12/1.
+ */
+public class MediaInfoUtils {
+
+ public static String buildResolution(int width, int height, int sarNum, int sarDen) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(width);
+ sb.append(" x ");
+ sb.append(height);
+ if (sarNum > 1 || sarDen > 1) {
+ sb.append("[");
+ sb.append(sarNum);
+ sb.append(":");
+ sb.append(sarDen);
+ sb.append("]");
+ }
+ return sb.toString();
+ }
+
+ public static String buildTimeMilli(long duration) {
+ long total_seconds = duration / 1000;
+ long hours = total_seconds / 3600;
+ long minutes = (total_seconds % 3600) / 60;
+ long seconds = total_seconds % 60;
+ if (duration <= 0) {
+ return "00:00";
+ }
+ if (hours >= 100) {
+ return String.format(Locale.US, "%d:%02d:%02d", hours, minutes, seconds);
+ } else if (hours > 0) {
+ return String.format(Locale.US, "%02d:%02d:%02d", hours, minutes, seconds);
+ } else {
+ return String.format(Locale.US, "%02d:%02d", minutes, seconds);
+ }
+ }
+
+ public static String buildTrackType(Context context, int type) {
+ switch (type) {
+ case ITrackInfo.MEDIA_TRACK_TYPE_VIDEO:
+ return context.getString(R.string.mmsplayer_tracktype_video);
+ case ITrackInfo.MEDIA_TRACK_TYPE_AUDIO:
+ return context.getString(R.string.mmsplayer_tracktype_audio);
+ case ITrackInfo.MEDIA_TRACK_TYPE_SUBTITLE:
+ return context.getString(R.string.mmsplayer_tracktype_subtitle);
+ case ITrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT:
+ return context.getString(R.string.mmsplayer_tracktype_timedtext);
+ case ITrackInfo.MEDIA_TRACK_TYPE_METADATA:
+ return context.getString(R.string.mmsplayer_tracktype_metadata);
+ case ITrackInfo.MEDIA_TRACK_TYPE_UNKNOWN:
+ default:
+ return context.getString(R.string.mmsplayer_tracktype_unknown);
+ }
+ }
+
+ public static String buildLanguage(String language) {
+ return TextUtils.isEmpty(language)? "N/A" : language;
+ }
+}
\ No newline at end of file
diff --git a/ijkplayer/src/main/java/tv/danmaku/ijk/media/player/IjkMediaPlayer.java b/ijkplayer/src/main/java/tv/danmaku/ijk/media/player/IjkMediaPlayer.java
index beb73f76e..86ea2be50 100644
--- a/ijkplayer/src/main/java/tv/danmaku/ijk/media/player/IjkMediaPlayer.java
+++ b/ijkplayer/src/main/java/tv/danmaku/ijk/media/player/IjkMediaPlayer.java
@@ -188,7 +188,6 @@ public final class IjkMediaPlayer extends AbstractMediaPlayer {
libLoader = sLocalLibLoader;
libLoader.loadLibrary("ijkffmpeg");
- libLoader.loadLibrary("ijksdl");
libLoader.loadLibrary("ijkplayer");
mIsLibLoaded = true;
}