diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/road/V2XRoadVideoWindow.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/road/V2XRoadVideoWindow.java
index da6d1b592b..268fa90b4b 100644
--- a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/road/V2XRoadVideoWindow.java
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/road/V2XRoadVideoWindow.java
@@ -53,13 +53,21 @@ public class V2XRoadVideoWindow extends RelativeLayout implements IV2XWindow, ID
private void initView(Context context) {
LayoutInflater.from(context).inflate(R.layout.window_road_video, this);
- mVideoView = findViewById(R.id.roadVideoView);
+ mVideoView = findViewById(R.id.roadVideoView);/*播放器*/
+ windowPalyImageView = findViewById(R.id.window_video_play);/*播放键*/
+ mThumbnailImageView = findViewById(R.id.thumbnail_image);/*第一帧图片*/
closeImage = findViewById(R.id.roadVideoClose);
- mThumbnailImageView = findViewById(R.id.thumbnail_image);
- windowPalyImageView = findViewById(R.id.window_video_play);
closeImage.setOnClickListener(v -> {
close();
});
+ mVideoView.setOnClickListener(v -> {
+ mThumbnailImageView.setVisibility(View.GONE);
+ if (mVideoView.isPlaying()) {
+ videoPause();
+ } else {
+ videoResume();
+ }
+ });
}
@Override
@@ -93,7 +101,6 @@ public class V2XRoadVideoWindow extends RelativeLayout implements IV2XWindow, ID
mVideoView.setVisibility(VISIBLE);
mVideoView.setVideoPath(path);
mVideoView.setOnPreparedListener(mediaPlayer -> {
- Logger.w(MODULE_NAME, "全屏准备。。。。。");
mThumbnailImageView.setVisibility(View.GONE);
windowPalyImageView.setVisibility(View.GONE);
});
@@ -101,9 +108,28 @@ public class V2XRoadVideoWindow extends RelativeLayout implements IV2XWindow, ID
}
/*
- * 视频播放结束
- * */
- private void videoPlayEnd(String path){
+ * 视频暂停播放
+ * */
+ private void videoPause() {
+ mVideoView.pause();
+ windowPalyImageView.setVisibility(View.VISIBLE);
+ windowPalyImageView.setOnClickListener(v -> {
+ videoResume();
+ });
+ }
+
+ /*
+ * 视频暂停后继续播放
+ * */
+ private void videoResume() {
+ mThumbnailImageView.setVisibility(View.INVISIBLE);
+ mVideoView.resume();
+ }
+
+ /*
+ * 视频播放结束
+ * */
+ private void videoPlayEnd(String path) {
Bitmap bitmap = BitmapHelper.getVideoThumbnail(path);
mThumbnailImageView.setVisibility(View.VISIBLE);
mThumbnailImageView.setImageBitmap(bitmap);
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/view/TextureVideoView.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/view/TextureVideoView.java
index b628bcd25f..25d6690a3c 100644
--- a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/view/TextureVideoView.java
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/view/TextureVideoView.java
@@ -1,5 +1,3 @@
-package com.mogo.module.v2x.view;
-
/*
* Copyright (C) 2006 The Android Open Source Project
*
@@ -16,129 +14,245 @@ package com.mogo.module.v2x.view;
* limitations under the License.
*/
-import android.annotation.SuppressLint;
+package com.mogo.module.v2x.view;
+
+import android.app.AlertDialog;
import android.content.Context;
-import android.graphics.Color;
+import android.content.DialogInterface;
+import android.content.res.Resources;
import android.graphics.SurfaceTexture;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
-import android.media.MediaPlayer.OnSeekCompleteListener;
+import android.media.MediaPlayer.OnInfoListener;
import android.net.Uri;
+import android.opengl.GLES20;
+import android.os.Build;
import android.util.AttributeSet;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
import android.view.Surface;
import android.view.TextureView;
+import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.MediaController;
import android.widget.MediaController.MediaPlayerControl;
-import com.mogo.module.v2x.R;
-import com.mogo.module.v2x.V2XConst;
-import com.mogo.utils.logger.Logger;
-
import java.io.IOException;
import java.util.Map;
-import static com.mogo.module.v2x.V2XConst.MODULE_NAME;
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.egl.EGLDisplay;
+import javax.microedition.khronos.egl.EGLSurface;
/**
- * Displays a video file. The VideoView class can load images from various
- * sources (such as resources or content providers), takes care of computing its
- * measurement from the video so that it can be used in any layout manager, and
- * provides various display options such as scaling and tinting.
+ * Displays a video file. The TextureVideoView class
+ * can load images from various sources (such as resources or content
+ * providers), takes care of computing its measurement from the video so that
+ * it can be used in any layout manager, and provides various display options
+ * such as scaling and tinting.
+ *
+ * Note: VideoView does not retain its full state when going into the
+ * background. In particular, it does not restore the current play state,
+ * play position or selected tracks. Applications should
+ * save and restore these on their own in
+ * {@link android.app.Activity#onSaveInstanceState} and
+ * {@link android.app.Activity#onRestoreInstanceState}.
+ * Also note that the audio session id (from {@link #getAudioSessionId}) may
+ * change from its previously returned value when the VideoView is restored.
+ *
+ * This code is based on the official Android sources for 7.1.1_r13 with the following differences:
+ *
+ * - extends {@link TextureView} instead of a {@link android.view.SurfaceView}
+ * allowing proper view animations
+ * - removes code that uses hidden APIs and thus is not available (e.g. subtitle support)
+ * - various small fixes and improvements
+ *
*/
-public class TextureVideoView extends TextureView implements MediaPlayerControl {
- private String TAG = "V2XModuleProvider";
+public class TextureVideoView extends TextureView
+ implements MediaPlayerControl {
+ private static final String TAG = "TextureVideoView";
+
+ // all possible internal states
+ 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;
+
// settable by the client
private Uri mUri;
private Map mHeaders;
- private int mDuration;
- // all possible internal states
- private int mCurrentState = V2XConst.STATE_IDLE;
- private int mTargetState = mCurrentState, mTagetStateBackup = mCurrentState;
- private int mPrepareState = V2XConst.STATE_PREPARED;
+ // mCurrentState is a TextureVideoView object's current state.
+ // mTargetState is the state that a method caller intends to reach.
+ // For instance, regardless the TextureVideoView object's current state,
+ // calling pause() intends to bring the object to a target state
+ // of STATE_PAUSED.
+ private int mCurrentState = STATE_IDLE;
+ private int mTargetState = STATE_IDLE;
// All the stuff we need for playing and showing a video
private Surface mSurface = null;
private MediaPlayer mMediaPlayer = null;
- private int mVideoWidth, mVideoHeight;
-
+ private int mAudioSession;
+ private int mVideoWidth;
+ private int mVideoHeight;
+ private MediaController mMediaController;
private OnCompletionListener mOnCompletionListener;
private MediaPlayer.OnPreparedListener mOnPreparedListener;
private int mCurrentBufferPercentage;
private OnErrorListener mOnErrorListener;
- private MediaPlayer.OnInfoListener mOnInfoListener;
- private int mSeekWhenPrepared;
- private boolean isFocusLoss = true;
- private boolean isSuspendFromActivity = false;
- private int progressWhileSuspend = 0;
- public static final int MEDIA_INFO_VIDEO_NOT_SUPPORTED = 860;
- public static final int MEDIA_INFO_AUDIO_NOT_SUPPORTED = 862;
+ private OnInfoListener mOnInfoListener;
+ private int mSeekWhenPrepared; // recording the seek position while preparing
+ private boolean mCanPause;
+ private boolean mCanSeekBack;
+ private boolean mCanSeekForward;
+ private boolean mShouldRequestAudioFocus = true;
public TextureVideoView(Context context) {
- super(context);
- initVideoView();
+ this(context, null);
}
public TextureVideoView(Context context, AttributeSet attrs) {
- super(context, attrs, 0);
- initVideoView();
+ this(context, attrs, 0);
}
public TextureVideoView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
- initVideoView();
+
+ mVideoWidth = 0;
+ mVideoHeight = 0;
+
+ setSurfaceTextureListener(mSurfaceTextureListener);
+
+ setFocusable(true);
+ setFocusableInTouchMode(true);
+ requestFocus();
+
+ mCurrentState = STATE_IDLE;
+ mTargetState = STATE_IDLE;
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ //Log.i("@@@@", "onMeasure(" + MeasureSpec.toString(widthMeasureSpec) + ", "
+ // + MeasureSpec.toString(heightMeasureSpec) + ")");
+
+ int width = getDefaultSize(mVideoWidth, widthMeasureSpec);
+ int height = getDefaultSize(mVideoHeight, heightMeasureSpec);
+ if (mVideoWidth > 0 && mVideoHeight > 0) {
+
+ int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
+ int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
+ int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
+ int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
+
+ if (widthSpecMode == MeasureSpec.EXACTLY && heightSpecMode == MeasureSpec.EXACTLY) {
+ // the size is fixed
+ width = widthSpecSize;
+ height = heightSpecSize;
+
+ // for compatibility, we adjust size based on aspect ratio
+ if ( mVideoWidth * height < width * mVideoHeight ) {
+ //Log.i("@@@", "image too wide, correcting");
+ width = height * mVideoWidth / mVideoHeight;
+ } else if ( mVideoWidth * height > width * mVideoHeight ) {
+ //Log.i("@@@", "image too tall, correcting");
+ height = width * mVideoHeight / mVideoWidth;
+ }
+ } else if (widthSpecMode == MeasureSpec.EXACTLY) {
+ // only the width is fixed, adjust the height to match aspect ratio if possible
+ width = widthSpecSize;
+ height = width * mVideoHeight / mVideoWidth;
+ if (heightSpecMode == MeasureSpec.AT_MOST && height > heightSpecSize) {
+ // couldn't match aspect ratio within the constraints
+ height = heightSpecSize;
+ }
+ } else if (heightSpecMode == MeasureSpec.EXACTLY) {
+ // only the height is fixed, adjust the width to match aspect ratio if possible
+ height = heightSpecSize;
+ width = height * mVideoWidth / mVideoHeight;
+ if (widthSpecMode == MeasureSpec.AT_MOST && width > widthSpecSize) {
+ // couldn't match aspect ratio within the constraints
+ width = widthSpecSize;
+ }
+ } else {
+ // neither the width nor the height are fixed, try to use actual video size
+ width = mVideoWidth;
+ height = mVideoHeight;
+ if (heightSpecMode == MeasureSpec.AT_MOST && height > heightSpecSize) {
+ // too tall, decrease both width and height
+ height = heightSpecSize;
+ width = height * mVideoWidth / mVideoHeight;
+ }
+ if (widthSpecMode == MeasureSpec.AT_MOST && width > widthSpecSize) {
+ // too wide, decrease both width and height
+ width = widthSpecSize;
+ height = width * mVideoHeight / mVideoWidth;
+ }
+ }
+ } else {
+ // no size yet, just adopt the given spec sizes
+ }
+ setMeasuredDimension(width, height);
}
- @SuppressLint("NewApi")
@Override
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(event);
event.setClassName(TextureVideoView.class.getName());
}
- @SuppressLint("NewApi")
@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
info.setClassName(TextureVideoView.class.getName());
}
- private void initVideoView() {
- mVideoWidth = 0;
- mVideoHeight = 0;
- setSurfaceTextureListener(mSurfaceTextureListener);
- setFocusable(true);
- setFocusableInTouchMode(true);
- requestFocus();
- mCurrentState = V2XConst.STATE_IDLE;
- mTargetState = V2XConst.STATE_IDLE;
- }
-
- public boolean requestAudioFocus() {
- return true;
+ public int resolveAdjustedSize(int desiredSize, int measureSpec) {
+ return getDefaultSize(desiredSize, measureSpec);
}
+ /**
+ * 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) {
- if (uri == null) {
- Logger.i(MODULE_NAME, "setVideoURI--- uri = null");
- } else {
- Logger.i(MODULE_NAME, "setVideoURI--- uri = " + uri.getPath());
- }
mUri = uri;
mHeaders = headers;
mSeekWhenPrepared = 0;
- isFocusLoss = !requestAudioFocus();
openVideo();
requestLayout();
invalidate();
@@ -149,232 +263,354 @@ public class TextureVideoView extends TextureView implements MediaPlayerControl
mMediaPlayer.stop();
mMediaPlayer.release();
mMediaPlayer = null;
- mTargetState = mCurrentState = V2XConst.STATE_IDLE;
+ mCurrentState = STATE_IDLE;
+ mTargetState = STATE_IDLE;
+ if (mShouldRequestAudioFocus) {
+ AudioManager am = (AudioManager) getContext().getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
+ am.abandonAudioFocus(null);
+ }
}
+ clearSurface();
}
- @SuppressLint("NewApi")
- private void openVideo() {
- Logger.i(MODULE_NAME, "openVideo");
- if (mUri == null) {
- Logger.i(MODULE_NAME, "mUri == null ");
- }
- if (mSurface == null) {
- Logger.i(MODULE_NAME, "mSurface == null ");
- }
- if (mUri == null || mSurface == null || isSuspendFromActivity) {
- Logger.i(MODULE_NAME, "isSuspendFromActivity = " + isSuspendFromActivity);
+ /**
+ * Clears the surface texture by attaching a GL context and clearing it.
+ * Code taken from Hugo Gresse's answer on stackoverflow.com.
+ */
+ private void clearSurface() {
+ if (mSurface == null || Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
return;
}
+
+ EGL10 egl = (EGL10) EGLContext.getEGL();
+ EGLDisplay display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
+ egl.eglInitialize(display, null);
+
+ int[] attribList = {
+ EGL10.EGL_RED_SIZE, 8,
+ EGL10.EGL_GREEN_SIZE, 8,
+ EGL10.EGL_BLUE_SIZE, 8,
+ EGL10.EGL_ALPHA_SIZE, 8,
+ EGL10.EGL_RENDERABLE_TYPE, EGL10.EGL_WINDOW_BIT,
+ EGL10.EGL_NONE, 0, // placeholder for recordable [@-3]
+ EGL10.EGL_NONE
+ };
+ EGLConfig[] configs = new EGLConfig[1];
+ int[] numConfigs = new int[1];
+ egl.eglChooseConfig(display, attribList, configs, configs.length, numConfigs);
+ EGLConfig config = configs[0];
+ EGLContext context = egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT, new int[]{
+ 12440, 2, EGL10.EGL_NONE
+ });
+ EGLSurface eglSurface = egl.eglCreateWindowSurface(display, config, mSurface, new int[]{
+ EGL10.EGL_NONE
+ });
+
+ egl.eglMakeCurrent(display, eglSurface, eglSurface, context);
+ GLES20.glClearColor(0, 0, 0, 1);
+ GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
+ egl.eglSwapBuffers(display, eglSurface);
+ egl.eglDestroySurface(display, eglSurface);
+ egl.eglMakeCurrent(display, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
+ egl.eglDestroyContext(display, context);
+ egl.eglTerminate(display);
+ }
+
+ private void openVideo() {
+ if (mUri == null || mSurface == 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);
+
+ if (mShouldRequestAudioFocus) {
+ AudioManager am = (AudioManager) getContext().getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
+ am.requestAudioFocus(null, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
+ }
+
try {
mMediaPlayer = new MediaPlayer();
+
+ if (mAudioSession != 0) {
+ mMediaPlayer.setAudioSessionId(mAudioSession);
+ } else {
+ mAudioSession = mMediaPlayer.getAudioSessionId();
+ }
mMediaPlayer.setOnPreparedListener(mPreparedListener);
mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener);
- mDuration = -1;
mMediaPlayer.setOnCompletionListener(mCompletionListener);
mMediaPlayer.setOnErrorListener(mErrorListener);
- mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener);
- mMediaPlayer.setOnSeekCompleteListener(mSeekCompleteListener);
mMediaPlayer.setOnInfoListener(mInfoListener);
+ mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener);
mCurrentBufferPercentage = 0;
- mMediaPlayer.setDataSource(getContext(), mUri, mHeaders);
- if (mSurface != null) {
- /*
- Canvas mCanvas = mSurface.lockCanvas(new Rect());
- mCanvas.drawColor(Color.BLACK);
- mSurface.unlockCanvasAndPost(mCanvas);
- invalidate();
- */
- mMediaPlayer.setSurface(mSurface);
- }
+ mMediaPlayer.setDataSource(getContext().getApplicationContext(), mUri, mHeaders);
+ mMediaPlayer.setSurface(mSurface);
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mMediaPlayer.setScreenOnWhilePlaying(true);
- mMediaPlayer.setVolume(0, 0);
- mMediaPlayer.prepare();
- mPrepareState = mCurrentState = V2XConst.STATE_PREPARING;
- } catch (IOException | SecurityException | IllegalStateException | IllegalArgumentException ex) {
- ex.printStackTrace();
- onError();
+ mMediaPlayer.prepareAsync();
+
+ // we don't set the target state here either, but preserve the
+ // target state that was there before.
+ mCurrentState = STATE_PREPARING;
+ attachMediaController();
+ } catch (IOException ex) {
+ Log.w(TAG, "Unable to open content: " + mUri, ex);
+ mCurrentState = STATE_ERROR;
+ mTargetState = STATE_ERROR;
+ mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0);
+ return;
+ } catch (IllegalArgumentException ex) {
+ Log.w(TAG, "Unable to open content: " + mUri, ex);
+ mCurrentState = STATE_ERROR;
+ mTargetState = STATE_ERROR;
+ mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0);
+ return;
}
}
- private void onError() {
- mTargetState = mCurrentState = V2XConst.STATE_ERROR;
- if (mErrorListener != null)
- mErrorListener.onError(mMediaPlayer,
- MediaPlayer.MEDIA_ERROR_UNKNOWN, 0);
+ public void setMediaController(MediaController controller) {
+ if (mMediaController != null) {
+ mMediaController.hide();
+ }
+ mMediaController = controller;
+ attachMediaController();
}
- MediaPlayer.OnVideoSizeChangedListener mSizeChangedListener = new MediaPlayer.OnVideoSizeChangedListener() {
- public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
- mVideoWidth = mp.getVideoWidth();
- mVideoHeight = mp.getVideoHeight();
- if (mVideoWidth != 0 && mVideoHeight != 0) {
- Logger.d(MODULE_NAME, "OnVideoSizeChangedListener mVideoWidth:" + " mVideoWidth:" + mVideoWidth + " mVideoHeight:" + mVideoHeight);
- }
+ 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());
}
- };
+ }
+
+ MediaPlayer.OnVideoSizeChangedListener mSizeChangedListener =
+ new MediaPlayer.OnVideoSizeChangedListener() {
+ public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
+ mVideoWidth = mp.getVideoWidth();
+ mVideoHeight = mp.getVideoHeight();
+ if (mVideoWidth != 0 && mVideoHeight != 0) {
+ /*
+ * 播放器大小等于获取的视频尺寸!!!!!!!!!!!!!!!!!
+ * */
+// getSurfaceTexture().setDefaultBufferSize(mVideoWidth, mVideoHeight);
+// requestLayout();
+ }
+ }
+ };
MediaPlayer.OnPreparedListener mPreparedListener = new MediaPlayer.OnPreparedListener() {
public void onPrepared(MediaPlayer mp) {
- Logger.i(MODULE_NAME, "MediaPlayer.OnPreparedListener");
- mPrepareState = mCurrentState = V2XConst.STATE_PREPARED;
- mVideoWidth = mp.getVideoWidth();
- mVideoHeight = mp.getVideoHeight();
+ mCurrentState = STATE_PREPARED;
+
+ mCanPause = mCanSeekBack = mCanSeekForward = true;
- int seekToPosition = mSeekWhenPrepared;
-// Logger.i(MODULE_NAME, "seekToPosition = " + seekToPosition);
- if (seekToPosition != 0) {
- seekTo(seekToPosition);
- }
- if (mTargetState == V2XConst.STATE_PLAYING) {
- start();
- }
if (mOnPreparedListener != null) {
mOnPreparedListener.onPrepared(mMediaPlayer);
}
- mp.setOnInfoListener((mp1, what, extra) -> {
- if (what == MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START)
- setBackgroundColor(Color.TRANSPARENT);
- return true;
- });
- }
- };
-
- private OnCompletionListener mCompletionListener = new OnCompletionListener() {
- public void onCompletion(MediaPlayer mp) {
- Logger.i(MODULE_NAME, "MediaPlayer.OnCompletionListener");
- mTargetState = mCurrentState = V2XConst.STATE_PLAYBACK_COMPLETED;
- if (mOnCompletionListener != null) {
- mOnCompletionListener.onCompletion(mMediaPlayer);
+ if (mMediaController != null) {
+ mMediaController.setEnabled(true);
}
- }
- };
+ mVideoWidth = mp.getVideoWidth();
+ mVideoHeight = mp.getVideoHeight();
- private OnSeekCompleteListener mSeekCompleteListener = mp -> {
- Logger.i(MODULE_NAME, "MediaPlayer.OnSeekCompleteListener");
- try {
- mCurrentState = mMediaPlayer.isPlaying() ? V2XConst.STATE_PLAYING : V2XConst.STATE_PAUSED;
- } catch (Exception e) {
- mCurrentState = V2XConst.STATE_PLAYING;
- e.printStackTrace();
- }
- };
-
- private OnErrorListener mErrorListener = new OnErrorListener() {
- public boolean onError(MediaPlayer mp, int framework_err, int impl_err) {
- Logger.i(MODULE_NAME, "MediaPlayer.onError");
- mTargetState = mPrepareState = mCurrentState = V2XConst.STATE_ERROR;
- if (mOnErrorListener != null) {
- mOnErrorListener.onError(mMediaPlayer, framework_err,
- impl_err);
+ int seekToPosition = mSeekWhenPrepared; // mSeekWhenPrepared may be changed after seekTo() call
+ if (seekToPosition != 0) {
+ seekTo(seekToPosition);
}
- release(false);
- return true;
- }
- };
-
- private MediaPlayer.OnInfoListener mInfoListener = new MediaPlayer.OnInfoListener() {
-
- @Override
- public boolean onInfo(MediaPlayer mp, int what, int extra) {
- Logger.i(MODULE_NAME, "MediaPlayer.OnInfoListener---what = " + what + ";extra = " + extra);
- int messageId = 0;
- if (what == MEDIA_INFO_VIDEO_NOT_SUPPORTED) {
- messageId = R.string.VideoView_info_text_video_not_supported;
- } else if (what == MEDIA_INFO_AUDIO_NOT_SUPPORTED) {
- messageId = R.string.file_not_support;
- }
- if (messageId != 0) {
- if (mOnInfoListener != null) {
- mOnInfoListener.onInfo(mp, what, extra);
+ if (mVideoWidth != 0 && mVideoHeight != 0) {
+ //Log.i("@@@@", "video size: " + mVideoWidth +"/"+ mVideoHeight);
+// getSurfaceTexture().setDefaultBufferSize(mVideoWidth, mVideoHeight);/*播放器大小等于获取的视频尺寸!!!!!!!!!!!!!!!!!*/
+ // We won't get a "surface changed" callback if the surface is already the right size, 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();
}
- return true;
}
- return false;
}
};
- private MediaPlayer.OnBufferingUpdateListener mBufferingUpdateListener = new MediaPlayer.OnBufferingUpdateListener() {
- public void onBufferingUpdate(MediaPlayer mp, int percent) {
- Logger.i(MODULE_NAME, "MediaPlayer.OnBufferingUpdateListener");
- mCurrentBufferPercentage = percent;
- }
- };
+ private OnCompletionListener mCompletionListener =
+ new OnCompletionListener() {
+ public void onCompletion(MediaPlayer mp) {
+ mCurrentState = STATE_PLAYBACK_COMPLETED;
+ mTargetState = STATE_PLAYBACK_COMPLETED;
+ if (mMediaController != null) {
+ mMediaController.hide();
+ }
+ if (mOnCompletionListener != null) {
+ mOnCompletionListener.onCompletion(mMediaPlayer);
+ }
+ }
+ };
+
+ private OnInfoListener mInfoListener =
+ new OnInfoListener() {
+ public boolean onInfo(MediaPlayer mp, int arg1, int arg2) {
+ if (mOnInfoListener != null) {
+ mOnInfoListener.onInfo(mp, arg1, arg2);
+ }
+ return true;
+ }
+ };
+
+ private OnErrorListener mErrorListener =
+ new OnErrorListener() {
+ public boolean onError(MediaPlayer mp, int framework_err, int impl_err) {
+ 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 = getContext().getResources();
+ int messageId;
+
+ if (framework_err == MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK) {
+ messageId = android.R.string.VideoView_error_text_invalid_progressive_playback;
+ } else {
+ messageId = android.R.string.VideoView_error_text_unknown;
+ }
+
+ new AlertDialog.Builder(getContext())
+ .setMessage(messageId)
+ .setPositiveButton(android.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 MediaPlayer.OnBufferingUpdateListener mBufferingUpdateListener =
+ new MediaPlayer.OnBufferingUpdateListener() {
+ public void onBufferingUpdate(MediaPlayer mp, int percent) {
+ mCurrentBufferPercentage = percent;
+ }
+ };
/**
- * Register a callback to be invoked when the media file is loaded and ready
- * to go.
+ * 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(MediaPlayer.OnPreparedListener l) {
+ public void setOnPreparedListener(MediaPlayer.OnPreparedListener l)
+ {
mOnPreparedListener = l;
}
/**
- * Register a callback to be invoked when the end of a media file has been
- * reached during playback.
+ * 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(OnCompletionListener l) {
+ public void setOnCompletionListener(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.
+ * 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, TextureVideoView will inform
+ * the user of any errors.
*
* @param l The callback that will be run
*/
- public void setOnErrorListener(OnErrorListener l) {
+ public void setOnErrorListener(OnErrorListener l)
+ {
mOnErrorListener = l;
}
- public void setOnInfoListener(MediaPlayer.OnInfoListener 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(OnInfoListener l) {
mOnInfoListener = l;
}
- SurfaceTextureListener mSurfaceTextureListener = new SurfaceTextureListener() {
+ SurfaceTextureListener mSurfaceTextureListener = new SurfaceTextureListener()
+ {
@Override
- public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
- Logger.i(MODULE_NAME, "onSurfaceTextureAvailable");
+ public void onSurfaceTextureSizeChanged(final SurfaceTexture surface, final int width, final int height) {
+ boolean isValidState = (mTargetState == STATE_PLAYING);
+ boolean hasValidSize = (width > 0 && height > 0);
+ if (mMediaPlayer != null && isValidState && hasValidSize) {
+ if (mSeekWhenPrepared != 0) {
+ seekTo(mSeekWhenPrepared);
+ }
+ start();
+ }
+ }
+
+ @Override
+ public void onSurfaceTextureAvailable(final SurfaceTexture surface, final int width, final int height) {
mSurface = new Surface(surface);
openVideo();
}
@Override
- public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
- Logger.d(MODULE_NAME, "onSurfaceTextureSizeChanged mVideoWidth:" + " mVideoWidth:" + mVideoWidth + " mVideoHeight:" + mVideoHeight);
- boolean isValidState = (mTargetState == V2XConst.STATE_PLAYING);
- boolean hasValidSize = (mVideoWidth == width && mVideoHeight == height);
- if (mMediaPlayer != null && isValidState && hasValidSize) {
- if (mSeekWhenPrepared != 0) {
- seekTo(mSeekWhenPrepared);
- }
- try {
- Thread.sleep(100);
- } catch (Exception e) {
- e.printStackTrace();
- }
- if (!isFocusLoss) start();
+ public boolean onSurfaceTextureDestroyed(final SurfaceTexture surface) {
+ // after we return from this we can't use the surface any more
+ if (mSurface != null) {
+ mSurface.release();
+ mSurface = null;
}
+ if (mMediaController != null) mMediaController.hide();
+ release(true);
+ return true;
}
-
@Override
- public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
- Logger.i(MODULE_NAME, "onSurfaceTextureDestroyed");
- mSurface = null;
- release(false);
- return false;
- }
-
- @Override
- public void onSurfaceTextureUpdated(SurfaceTexture surface) {
-
+ public void onSurfaceTextureUpdated(final SurfaceTexture surface) {
+ // do nothing
}
};
@@ -382,131 +618,125 @@ public class TextureVideoView extends TextureView implements MediaPlayerControl
* release the media player in any state
*/
private void release(boolean cleartargetstate) {
- Logger.i(MODULE_NAME, "release ---cleartargetstate=" + cleartargetstate);
- mCurrentState = V2XConst.STATE_IDLE;
-
- if (cleartargetstate) {
- mTargetState = V2XConst.STATE_IDLE;
-
- new Thread(new Runnable() {
- @Override
- public void run() {
- try {
- if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {
- try {
- mMediaPlayer.stop();
- } catch (IllegalStateException e) {
- try {
- this.wait(1000);
- mMediaPlayer.stop();
- } catch (InterruptedException e1) {
- e1.printStackTrace();
- }
- }
- }
-
- if (mMediaPlayer != null) {
- mMediaPlayer.release();// release会做reset
- }
- mMediaPlayer = null;
- } catch (IllegalStateException e) {
- e.printStackTrace();
- }
- }
- }).start();
- } else {
- if (mMediaPlayer != null) {
- try {
- mMediaPlayer.stop();
- mMediaPlayer.reset();
- mMediaPlayer.release();
- } catch (IllegalStateException e) {
- e.printStackTrace();
- }
- }
+ if (mMediaPlayer != null) {
+ mMediaPlayer.reset();
+ mMediaPlayer.release();
mMediaPlayer = null;
+ mCurrentState = STATE_IDLE;
+ if (cleartargetstate) {
+ mTargetState = STATE_IDLE;
+ }
+ if (mShouldRequestAudioFocus) {
+ AudioManager am = (AudioManager) getContext().getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
+ am.abandonAudioFocus(null);
+ }
}
}
- public boolean isPrepared() {
- return mPrepareState == V2XConst.STATE_PREPARED
- || isSuspendFromActivity;
+ @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() {
- Logger.i(MODULE_NAME, "TextureVideoView---start");
- if (isFocusLoss) {
- mTagetStateBackup = V2XConst.STATE_PLAYING;
- return;
- }
- //Logger.i(MODULE_NAME, "mCurrentState = " + mCurrentState);
if (isInPlaybackState()) {
- Logger.i(MODULE_NAME, "MediaPlayer.start");
mMediaPlayer.start();
- mCurrentState = V2XConst.STATE_PLAYING;
+ mCurrentState = STATE_PLAYING;
}
- mTargetState = V2XConst.STATE_PLAYING;
+ mTargetState = STATE_PLAYING;
}
+ @Override
public void pause() {
- Logger.i(MODULE_NAME, "TextureVideoView---pause");
- Logger.i(MODULE_NAME, "mCurrentState = " + mCurrentState);
if (isInPlaybackState()) {
if (mMediaPlayer.isPlaying()) {
- Logger.i(MODULE_NAME, " MediaPlayer.pause");
mMediaPlayer.pause();
- mCurrentState = V2XConst.STATE_PAUSED;
+ mCurrentState = STATE_PAUSED;
}
}
- mTagetStateBackup = mTargetState = V2XConst.STATE_PAUSED;
+ mTargetState = STATE_PAUSED;
}
public void suspend() {
- Logger.i(MODULE_NAME, "TextureVideoView---suspend");
- if (!isSuspendFromActivity) {
- isSuspendFromActivity = true;
- if (mMediaPlayer != null) {
- if (isInPlaybackState()) {
- progressWhileSuspend = mMediaPlayer.getCurrentPosition();
- }
- }
- release(false);
- }
+ release(false);
}
public void resume() {
- Logger.i(MODULE_NAME, "TextureVideoView---resume");
- isSuspendFromActivity = false;
openVideo();
- seekTo(progressWhileSuspend);
-
- if (mTagetStateBackup == V2XConst.STATE_IDLE)
- mTagetStateBackup = mTargetState;
- Logger.i(MODULE_NAME, "isFocusLoss = " + isFocusLoss);
- Logger.i(MODULE_NAME, "isInPlaybackState() = " + isInPlaybackState());
- if (isFocusLoss) {
- if (isInPlaybackState()) {
- if (mMediaPlayer.isPlaying()) {
- mMediaPlayer.pause();
- mCurrentState = V2XConst.STATE_PAUSED;
- }
- }
- }
}
- // cache duration as mDuration for faster access
+ @Override
public int getDuration() {
if (isInPlaybackState()) {
- if (mDuration > 0) {
- return mDuration;
- }
- mDuration = mMediaPlayer.getDuration();
- return mDuration;
+ return mMediaPlayer.getDuration();
}
- mDuration = -1;
- return mDuration;
+
+ return -1;
}
+ @Override
public int getCurrentPosition() {
if (isInPlaybackState()) {
return mMediaPlayer.getCurrentPosition();
@@ -514,25 +744,22 @@ public class TextureVideoView extends TextureView implements MediaPlayerControl
return 0;
}
+ @Override
public void seekTo(int msec) {
- Logger.i(MODULE_NAME, "TextureVideoView---seekTo---msec = " + msec);
- if (isInPlaybackState() && mCurrentState != V2XConst.STATE_SEEKING
- && msec > 0 && msec < getDuration()) {
- mCurrentState = V2XConst.STATE_SEEKING;
-
- Logger.i(MODULE_NAME, "MediaPlayer.seekTo(msec) = " + msec);
+ if (isInPlaybackState()) {
mMediaPlayer.seekTo(msec);
mSeekWhenPrepared = 0;
} else {
mSeekWhenPrepared = msec;
- progressWhileSuspend = msec;
}
}
+ @Override
public boolean isPlaying() {
return isInPlaybackState() && mMediaPlayer.isPlaying();
}
+ @Override
public int getBufferPercentage() {
if (mMediaPlayer != null) {
return mCurrentBufferPercentage;
@@ -541,50 +768,58 @@ public class TextureVideoView extends TextureView implements MediaPlayerControl
}
private boolean isInPlaybackState() {
- return (mMediaPlayer != null && mCurrentState != V2XConst.STATE_ERROR
- && mCurrentState != V2XConst.STATE_IDLE && mCurrentState != V2XConst.STATE_PREPARING);
+ return (mMediaPlayer != null &&
+ mCurrentState != STATE_ERROR &&
+ mCurrentState != STATE_IDLE &&
+ mCurrentState != STATE_PREPARING);
}
@Override
public boolean canPause() {
- return false;
+ return mCanPause;
}
@Override
public boolean canSeekBackward() {
- return false;
+ return mCanSeekBack;
}
@Override
public boolean canSeekForward() {
- return false;
+ return mCanSeekForward;
}
- public String getVideoPath() {
- if (mUri != null) {
- return mUri.getPath();
- }
- return null;
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int width = getDefaultSize(mVideoWidth, widthMeasureSpec);
- int height = getDefaultSize(mVideoHeight, heightMeasureSpec);
- setMeasuredDimension(width, height);
- }
-
- public boolean isPause() {
- return mTargetState == V2XConst.STATE_PAUSED;
- }
-
- @Override
public int getAudioSessionId() {
- return 0;
+ if (mAudioSession == 0) {
+ MediaPlayer foo = new MediaPlayer();
+ mAudioSession = foo.getAudioSessionId();
+ foo.release();
+ }
+ return mAudioSession;
}
- public int getCurrentState() {
- Logger.i(MODULE_NAME, "mCurrentState == " + mCurrentState);
- return mCurrentState;
+ /**
+ * Sets the request audio focus flag. If enabled, {@link TextureVideoView} will request
+ * audio focus when opening a video by calling {@link AudioManager}. This flag
+ * should be set before calling {@link TextureVideoView#setVideoPath(String)} or
+ * {@link TextureVideoView#setVideoURI(Uri)}. By default, {@link TextureVideoView} will
+ * request audio focus.
+ *
+ * @param shouldRequestAudioFocus If {@code true}, {@link TextureVideoView} will request
+ * audio focus before opening a video, else audio focus is not requested
+ */
+ public void setShouldRequestAudioFocus(boolean shouldRequestAudioFocus) {
+ mShouldRequestAudioFocus = shouldRequestAudioFocus;
}
+
+ /**
+ * Returns the current state of the audio focus request flag.
+ *
+ * @return {@code true}, if {@link TextureVideoView} will request
+ * audio focus before opening a video, else {@code false}
+ */
+ public boolean shouldRequestAudioFocus() {
+ return mShouldRequestAudioFocus;
+ }
+
}