From 5250bbc99d55736b5e8519573e0a9dda54170228 Mon Sep 17 00:00:00 2001 From: FongMi Date: Thu, 17 Nov 2022 18:43:55 +0800 Subject: [PATCH] Support ijkplayer - part 5 --- ijkplayer/src/main/AndroidManifest.xml | 2 +- .../android/mmsplayer/ijk/bean/MediaInfo.java | 10 + .../mmsplayer/ijk/bean/TrackAudioInfo.java | 13 + .../mmsplayer/ijk/bean/TrackVideoInfo.java | 14 + .../mmsplayer/ijk/media/IRenderView.java | 73 ++ .../mmsplayer/ijk/media/IjkVideoView.java | 1102 +++++++++++++++++ .../mmsplayer/ijk/media/MeasureHelper.java | 162 +++ .../ijk/media/MediaPlayerCompat.java | 56 + .../ijk/media/SurfaceRenderView.java | 240 ++++ .../ijk/media/TextureRenderView.java | 292 +++++ .../ijk/setting/IjkPlayerSetting.java | 128 ++ .../mmsplayer/ijk/utils/MediaInfoUtils.java | 71 ++ .../ijk/media/player/IjkMediaPlayer.java | 1 - 13 files changed, 2162 insertions(+), 2 deletions(-) create mode 100644 ijkplayer/src/main/java/com/lodz/android/mmsplayer/ijk/bean/MediaInfo.java create mode 100644 ijkplayer/src/main/java/com/lodz/android/mmsplayer/ijk/bean/TrackAudioInfo.java create mode 100644 ijkplayer/src/main/java/com/lodz/android/mmsplayer/ijk/bean/TrackVideoInfo.java create mode 100644 ijkplayer/src/main/java/com/lodz/android/mmsplayer/ijk/media/IRenderView.java create mode 100644 ijkplayer/src/main/java/com/lodz/android/mmsplayer/ijk/media/IjkVideoView.java create mode 100644 ijkplayer/src/main/java/com/lodz/android/mmsplayer/ijk/media/MeasureHelper.java create mode 100644 ijkplayer/src/main/java/com/lodz/android/mmsplayer/ijk/media/MediaPlayerCompat.java create mode 100644 ijkplayer/src/main/java/com/lodz/android/mmsplayer/ijk/media/SurfaceRenderView.java create mode 100644 ijkplayer/src/main/java/com/lodz/android/mmsplayer/ijk/media/TextureRenderView.java create mode 100644 ijkplayer/src/main/java/com/lodz/android/mmsplayer/ijk/setting/IjkPlayerSetting.java create mode 100644 ijkplayer/src/main/java/com/lodz/android/mmsplayer/ijk/utils/MediaInfoUtils.java 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; }