|
@@ -0,0 +1,664 @@
|
|
|
+package com.sjkj.appthreefloor_tsgz.utils;
|
|
|
+
|
|
|
+import android.content.Context;
|
|
|
+import android.media.MediaPlayer;
|
|
|
+import android.os.CountDownTimer;
|
|
|
+import android.util.AttributeSet;
|
|
|
+import android.view.View;
|
|
|
+import android.widget.RelativeLayout;
|
|
|
+
|
|
|
+import androidx.annotation.NonNull;
|
|
|
+import androidx.annotation.Nullable;
|
|
|
+
|
|
|
+import com.google.android.exoplayer2.DeviceInfo;
|
|
|
+import com.google.android.exoplayer2.ExoPlayer;
|
|
|
+import com.google.android.exoplayer2.MediaItem;
|
|
|
+import com.google.android.exoplayer2.MediaMetadata;
|
|
|
+import com.google.android.exoplayer2.PlaybackException;
|
|
|
+import com.google.android.exoplayer2.PlaybackParameters;
|
|
|
+import com.google.android.exoplayer2.Player;
|
|
|
+import com.google.android.exoplayer2.Timeline;
|
|
|
+import com.google.android.exoplayer2.Tracks;
|
|
|
+import com.google.android.exoplayer2.audio.AudioAttributes;
|
|
|
+import com.google.android.exoplayer2.metadata.Metadata;
|
|
|
+import com.google.android.exoplayer2.text.CueGroup;
|
|
|
+import com.google.android.exoplayer2.trackselection.TrackSelectionParameters;
|
|
|
+import com.google.android.exoplayer2.ui.PlayerView;
|
|
|
+import com.google.android.exoplayer2.video.VideoSize;
|
|
|
+import com.king.zxing.util.LogUtils;
|
|
|
+
|
|
|
+import java.util.Map;
|
|
|
+
|
|
|
+/**
|
|
|
+ * createTime :2024/6/21 9:14
|
|
|
+ * createBy :lkl
|
|
|
+ */
|
|
|
+public class MyVideoView extends RelativeLayout {
|
|
|
+ /**
|
|
|
+ * 准备时长,超过这个时间就重试加载
|
|
|
+ */
|
|
|
+ private static final long INIT_TIME_OUT = 1000 * 15;
|
|
|
+ /**
|
|
|
+ * 进度回调间隔
|
|
|
+ */
|
|
|
+ private final static long TimeInterval = 1000;
|
|
|
+ //计时器
|
|
|
+ private CountDownTimer timer;
|
|
|
+ //准备就绪后开始播放(默认false)
|
|
|
+ private boolean autoPlay = false;
|
|
|
+ //视频时长
|
|
|
+ private long duration;
|
|
|
+ //回调
|
|
|
+ private OnVideoCallBack onVideoCallBack;
|
|
|
+ //
|
|
|
+ private int videoWidth, videoHeight;
|
|
|
+ //是否准备就绪了
|
|
|
+ private boolean isReady = false;
|
|
|
+ //暂停时的位置、需要恢复的位置
|
|
|
+ private long currentPosition = -1;
|
|
|
+ //出错时,重试的次数
|
|
|
+ private int retryCount = 0;
|
|
|
+ //视频连接
|
|
|
+ private String videoPath;
|
|
|
+ //头部信息
|
|
|
+ private Map<String, String> header;
|
|
|
+ private final ExoPlayer player;
|
|
|
+ private final Context context;
|
|
|
+
|
|
|
+ public MyVideoView(Context context) {
|
|
|
+ this(context, null, 0, 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ public MyVideoView(Context context, AttributeSet attrs) {
|
|
|
+ this(context, attrs, 0, 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ public MyVideoView(Context context, AttributeSet attrs, int defStyleAttr) {
|
|
|
+ this(context, attrs, defStyleAttr, 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ public MyVideoView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
|
|
+ super(context, attrs, defStyleAttr, defStyleRes);
|
|
|
+ this.context = context;
|
|
|
+ this.player = new ExoPlayer.Builder(context).build();
|
|
|
+ PlayerView videoView = new PlayerView(context);
|
|
|
+ videoView.setPlayer(player);
|
|
|
+ videoView.setUseController(false);
|
|
|
+ videoView.setClickable(false);
|
|
|
+ videoView.setFocusableInTouchMode(false);
|
|
|
+ RelativeLayout.LayoutParams lp = new LayoutParams(-1, -1);
|
|
|
+ lp.addRule(CENTER_IN_PARENT);
|
|
|
+ videoView.setLayoutParams(lp);
|
|
|
+ addView(videoView);
|
|
|
+ initListener();
|
|
|
+ }
|
|
|
+
|
|
|
+ private void initListener() {
|
|
|
+ player.addListener(new Player.Listener() {
|
|
|
+ @Override
|
|
|
+ public void onEvents(@NonNull Player player, @NonNull Player.Events events) {
|
|
|
+ Player.Listener.super.onEvents(player, events);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onTimelineChanged(@NonNull Timeline timeline, int reason) {
|
|
|
+ Player.Listener.super.onTimelineChanged(timeline, reason);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onMediaItemTransition(@Nullable MediaItem mediaItem, int reason) {
|
|
|
+ Player.Listener.super.onMediaItemTransition(mediaItem, reason);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onTracksChanged(@NonNull Tracks tracks) {
|
|
|
+ Player.Listener.super.onTracksChanged(tracks);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onMediaMetadataChanged(@NonNull MediaMetadata mediaMetadata) {
|
|
|
+ Player.Listener.super.onMediaMetadataChanged(mediaMetadata);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onPlaylistMetadataChanged(@NonNull MediaMetadata mediaMetadata) {
|
|
|
+ Player.Listener.super.onPlaylistMetadataChanged(mediaMetadata);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onIsLoadingChanged(boolean isLoading) {
|
|
|
+ Player.Listener.super.onIsLoadingChanged(isLoading);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onAvailableCommandsChanged(@NonNull Player.Commands availableCommands) {
|
|
|
+ Player.Listener.super.onAvailableCommandsChanged(availableCommands);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onTrackSelectionParametersChanged(@NonNull TrackSelectionParameters parameters) {
|
|
|
+ Player.Listener.super.onTrackSelectionParametersChanged(parameters);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onPlaybackStateChanged(int playbackState) {
|
|
|
+ Player.Listener.super.onPlaybackStateChanged(playbackState);
|
|
|
+ switch (playbackState) {
|
|
|
+ case Player.STATE_READY:
|
|
|
+ onPreparedCallBack();
|
|
|
+ if (onVideoCallBack != null) {
|
|
|
+ onVideoCallBack.onBufferEnd();
|
|
|
+ }
|
|
|
+ if (timer != null) {
|
|
|
+ timer.cancel();
|
|
|
+ }
|
|
|
+ timeOut.cancel();
|
|
|
+ break;
|
|
|
+ case Player.STATE_BUFFERING:
|
|
|
+ if (onVideoCallBack != null) {
|
|
|
+ onVideoCallBack.onBufferStart();
|
|
|
+ }
|
|
|
+ if (timer != null) {
|
|
|
+ timer.cancel();
|
|
|
+ }
|
|
|
+ timeOut.cancel();
|
|
|
+ timeOut.start();
|
|
|
+ break;
|
|
|
+ case Player.STATE_ENDED:
|
|
|
+ if (onVideoCallBack != null) {
|
|
|
+ onVideoCallBack.onComplete();
|
|
|
+ }
|
|
|
+ if (timer != null) {
|
|
|
+ timer.cancel();
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case Player.STATE_IDLE:
|
|
|
+ //玩家是空闲的,这意味着它只拥有有限的资源。播放器在播放媒体之前必须做好准备。
|
|
|
+ LogUtils.i("初始状态");
|
|
|
+ if (timer != null) {
|
|
|
+ timer.cancel();
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ LogUtils.i("播放器状态:" + playbackState);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onPlayWhenReadyChanged(boolean playWhenReady, int reason) {
|
|
|
+ Player.Listener.super.onPlayWhenReadyChanged(playWhenReady, reason);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onPlaybackSuppressionReasonChanged(int playbackSuppressionReason) {
|
|
|
+ Player.Listener.super.onPlaybackSuppressionReasonChanged(playbackSuppressionReason);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onIsPlayingChanged(boolean isPlaying) {
|
|
|
+ Player.Listener.super.onIsPlayingChanged(isPlaying);
|
|
|
+ if (onVideoCallBack != null) {
|
|
|
+ if (isPlaying) {
|
|
|
+ onVideoCallBack.onStart();
|
|
|
+ if (timer != null) {
|
|
|
+ timer.start();
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ onVideoCallBack.onPause();
|
|
|
+ if (timer != null) {
|
|
|
+ timer.cancel();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onRepeatModeChanged(int repeatMode) {
|
|
|
+ Player.Listener.super.onRepeatModeChanged(repeatMode);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {
|
|
|
+ Player.Listener.super.onShuffleModeEnabledChanged(shuffleModeEnabled);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onPlayerError(@NonNull PlaybackException error) {
|
|
|
+ Player.Listener.super.onPlayerError(error);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onPlayerErrorChanged(@Nullable PlaybackException error) {
|
|
|
+ Player.Listener.super.onPlayerErrorChanged(error);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onPositionDiscontinuity(@NonNull Player.PositionInfo oldPosition, @NonNull Player.PositionInfo newPosition, int reason) {
|
|
|
+ Player.Listener.super.onPositionDiscontinuity(oldPosition, newPosition, reason);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onPlaybackParametersChanged(@NonNull PlaybackParameters playbackParameters) {
|
|
|
+ Player.Listener.super.onPlaybackParametersChanged(playbackParameters);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onSeekBackIncrementChanged(long seekBackIncrementMs) {
|
|
|
+ Player.Listener.super.onSeekBackIncrementChanged(seekBackIncrementMs);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onSeekForwardIncrementChanged(long seekForwardIncrementMs) {
|
|
|
+ Player.Listener.super.onSeekForwardIncrementChanged(seekForwardIncrementMs);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onMaxSeekToPreviousPositionChanged(long maxSeekToPreviousPositionMs) {
|
|
|
+ Player.Listener.super.onMaxSeekToPreviousPositionChanged(maxSeekToPreviousPositionMs);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onAudioSessionIdChanged(int audioSessionId) {
|
|
|
+ Player.Listener.super.onAudioSessionIdChanged(audioSessionId);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onAudioAttributesChanged(@NonNull AudioAttributes audioAttributes) {
|
|
|
+ Player.Listener.super.onAudioAttributesChanged(audioAttributes);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onVolumeChanged(float volume) {
|
|
|
+ Player.Listener.super.onVolumeChanged(volume);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onSkipSilenceEnabledChanged(boolean skipSilenceEnabled) {
|
|
|
+ Player.Listener.super.onSkipSilenceEnabledChanged(skipSilenceEnabled);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onDeviceInfoChanged(@NonNull DeviceInfo deviceInfo) {
|
|
|
+ Player.Listener.super.onDeviceInfoChanged(deviceInfo);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onDeviceVolumeChanged(int volume, boolean muted) {
|
|
|
+ Player.Listener.super.onDeviceVolumeChanged(volume, muted);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onVideoSizeChanged(@NonNull VideoSize videoSize) {
|
|
|
+ Player.Listener.super.onVideoSizeChanged(videoSize);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onSurfaceSizeChanged(int width, int height) {
|
|
|
+ Player.Listener.super.onSurfaceSizeChanged(width, height);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onRenderedFirstFrame() {
|
|
|
+ Player.Listener.super.onRenderedFirstFrame();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onCues(@NonNull CueGroup cueGroup) {
|
|
|
+ Player.Listener.super.onCues(cueGroup);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onMetadata(@NonNull Metadata metadata) {
|
|
|
+ Player.Listener.super.onMetadata(metadata);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ private void onPreparedCallBack() {
|
|
|
+ if (isReady) {
|
|
|
+ LogUtils.i("无需重复调用准备就绪回调");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ LogUtils.i("准备就绪!!!");
|
|
|
+ timeOut.cancel();
|
|
|
+ isReady = true;
|
|
|
+ VideoSize size = player.getVideoSize();
|
|
|
+ videoWidth = size.width;
|
|
|
+ videoHeight = size.height;
|
|
|
+ duration = player.getDuration();
|
|
|
+ if (timer != null) {
|
|
|
+ timer.cancel();
|
|
|
+ }
|
|
|
+ timer = new CountDownTimer(duration, TimeInterval) {
|
|
|
+ @Override
|
|
|
+ public void onTick(long left) {
|
|
|
+ if (onVideoCallBack != null) {
|
|
|
+ currentPosition = player.getCurrentPosition();
|
|
|
+ onVideoCallBack.onProgress(currentPosition, duration);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onFinish() {
|
|
|
+
|
|
|
+ }
|
|
|
+ };
|
|
|
+ if (onVideoCallBack != null) {
|
|
|
+ onVideoCallBack.onPrepared(duration);
|
|
|
+ }
|
|
|
+ if (currentPosition != -1) {
|
|
|
+ //恢复播放
|
|
|
+ LogUtils.i("恢复播放,恢复进度:" + currentPosition);
|
|
|
+ seekTo(currentPosition);
|
|
|
+ start();
|
|
|
+ } else {
|
|
|
+ if (autoPlay) {
|
|
|
+ start();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public void timePause() {
|
|
|
+ if (timer != null) {
|
|
|
+ timer.cancel();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public void timeContinue() {
|
|
|
+ if (timer != null) {
|
|
|
+ timer.cancel();
|
|
|
+ timer.start();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public boolean isReady() {
|
|
|
+ return isReady;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void setAutoPlay(boolean autoPlay) {
|
|
|
+ this.autoPlay = autoPlay;
|
|
|
+ }
|
|
|
+
|
|
|
+ public int getVideoWidth() {
|
|
|
+ return videoWidth;
|
|
|
+ }
|
|
|
+
|
|
|
+ public int getVideoHeight() {
|
|
|
+ return videoHeight;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void setOnVideoCallBack(OnVideoCallBack onVideoCallBack) {
|
|
|
+ this.onVideoCallBack = onVideoCallBack;
|
|
|
+ }
|
|
|
+
|
|
|
+ public interface OnVideoCallBack {
|
|
|
+ void onStartPrepare();
|
|
|
+
|
|
|
+ void onPrepared(long duration);
|
|
|
+
|
|
|
+ void onStart();
|
|
|
+
|
|
|
+ void onPause();
|
|
|
+
|
|
|
+ void onBufferStart();
|
|
|
+
|
|
|
+ void onBufferEnd();
|
|
|
+
|
|
|
+ void onProgress(long progress, long duration);
|
|
|
+
|
|
|
+ void onComplete();
|
|
|
+
|
|
|
+ void onError(String error);
|
|
|
+ }
|
|
|
+
|
|
|
+ public void retry() {
|
|
|
+ reset();
|
|
|
+ setVideoPath(videoPath, header);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 播放出错后,尝试重新播放
|
|
|
+ */
|
|
|
+ private void retryPlay(int what, String defError) {
|
|
|
+ if (timer != null) {
|
|
|
+ timer.cancel();
|
|
|
+ }
|
|
|
+ if (retryCount >= 2) {
|
|
|
+ if (onVideoCallBack != null) {
|
|
|
+ onVideoCallBack.onError(defError + what);
|
|
|
+ onVideoCallBack.onPause();
|
|
|
+ }
|
|
|
+ retryCount = 0;
|
|
|
+ reset();
|
|
|
+ LogUtils.e("确实播放出错了,编号:" + what);
|
|
|
+ } else {
|
|
|
+ retryCount++;
|
|
|
+ LogUtils.e("准备重试,第" + retryCount + "次(" + what + ")");
|
|
|
+ reset();
|
|
|
+ postDelayed(retryRunnable, 3000);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ //超时计时器
|
|
|
+ private final CountDownTimer timeOut = new CountDownTimer(INIT_TIME_OUT, 1000) {
|
|
|
+ @Override
|
|
|
+ public void onTick(long left) {
|
|
|
+// LogUtils.i("超时倒计时:" + (left / 1000));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onFinish() {
|
|
|
+ onErrorListener.onError(null, MediaPlayer.MEDIA_ERROR_TIMED_OUT, 0);
|
|
|
+ }
|
|
|
+ };
|
|
|
+ //重试倒计时
|
|
|
+ private final Runnable retryRunnable = () -> setVideoPath(videoPath, header);
|
|
|
+ //错误回调
|
|
|
+ private final MediaPlayer.OnErrorListener onErrorListener = (mp, what, extra) -> {
|
|
|
+ int p = mp != null ? mp.getCurrentPosition() : 0;
|
|
|
+ if (p > 0) {
|
|
|
+ currentPosition = p;
|
|
|
+ }
|
|
|
+ switch (what) {
|
|
|
+ case MediaPlayer.MEDIA_ERROR_IO:
|
|
|
+ retryPlay(what, "无法加载视频");
|
|
|
+ break;
|
|
|
+ case MediaPlayer.MEDIA_ERROR_TIMED_OUT:
|
|
|
+ retryPlay(what, "加载超时");
|
|
|
+ break;
|
|
|
+ case MediaPlayer.MEDIA_ERROR_SERVER_DIED:
|
|
|
+ retryPlay(what, "拒绝访问");
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ retryPlay(what, "播放出错,请重试");
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ };
|
|
|
+
|
|
|
+ public void setVideoPath(String videoPath, Map<String, String> headers) {
|
|
|
+ try {
|
|
|
+ if (videoPath == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (!videoPath.equals(this.videoPath)) {
|
|
|
+ currentPosition = -1;
|
|
|
+ }
|
|
|
+ this.videoPath = videoPath;
|
|
|
+ this.header = headers;
|
|
|
+ reset();
|
|
|
+ if (this.header == null) {
|
|
|
+ player.setMediaItem(MediaItem.fromUri(videoPath));
|
|
|
+ } else {
|
|
|
+ MediaItem mediaItem = new MediaItem.Builder().setDrmLicenseRequestHeaders(headers).setUri(videoPath).build();
|
|
|
+ player.setMediaItem(mediaItem);
|
|
|
+ }
|
|
|
+ player.prepare();
|
|
|
+// videoView.setVideoURI(Uri.parse(videoPath), headers);
|
|
|
+// videoView.prepareAsync();
|
|
|
+ if (isReady) {
|
|
|
+ //曾经准备就绪过,只是因为出错了,要重新准备
|
|
|
+ if (onVideoCallBack != null) {
|
|
|
+ onVideoCallBack.onBufferStart();
|
|
|
+ }
|
|
|
+ LogUtils.i("曾经准备就绪过,只是因为出错了,要重新准备,播放位置为:" + currentPosition);
|
|
|
+ } else {
|
|
|
+// lastPausePosition = -1;
|
|
|
+ if (onVideoCallBack != null) {
|
|
|
+ onVideoCallBack.onStartPrepare();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ isReady = false;
|
|
|
+ timeOut.cancel();
|
|
|
+ timeOut.start();
|
|
|
+ LogUtils.i("开始准备:" + videoPath);
|
|
|
+ } catch (Exception e) {
|
|
|
+ LogUtils.e(e);
|
|
|
+ if (onVideoCallBack != null) {
|
|
|
+ onVideoCallBack.onError(e.toString());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ public void start() {
|
|
|
+ if (player == null) {
|
|
|
+ LogUtils.e("mediaPlayer不能为空");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (!isReady) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ switch (player.getPlaybackState()) {
|
|
|
+ case Player.STATE_READY:
|
|
|
+ case Player.STATE_BUFFERING:
|
|
|
+ player.play();
|
|
|
+ break;
|
|
|
+ case Player.STATE_ENDED:
|
|
|
+ case Player.STATE_IDLE:
|
|
|
+ isReady = false;
|
|
|
+ currentPosition = -1;
|
|
|
+ setVideoPath(videoPath, header);
|
|
|
+ player.play();
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ public void pause() {
|
|
|
+ if (player == null) {
|
|
|
+ LogUtils.e("mediaPlayer不能为空");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (!isReady) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ player.pause();
|
|
|
+ currentPosition = player.getCurrentPosition();
|
|
|
+ }
|
|
|
+
|
|
|
+ public void resume() {
|
|
|
+// mediaPlayer.resume();
|
|
|
+ if (currentPosition > 1000) {
|
|
|
+ seekTo(currentPosition);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void reset() {
|
|
|
+ if (player == null) {
|
|
|
+ LogUtils.e("mediaPlayer不能为空");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ player.stop();
|
|
|
+ } catch (Exception e) {
|
|
|
+ LogUtils.e(e);
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ public void onDestroy() {
|
|
|
+ this.reset();
|
|
|
+ removeCallbacks(retryRunnable);
|
|
|
+ //结束并释放资源
|
|
|
+ reset();
|
|
|
+ if (timer != null) {
|
|
|
+ timer.cancel();
|
|
|
+ timer = null;
|
|
|
+ }
|
|
|
+// holder.removeCallback(holderCallback);
|
|
|
+ timeOut.cancel();
|
|
|
+ }
|
|
|
+
|
|
|
+ public void seekTo(long msec) {
|
|
|
+ this.seekTo((int) msec);
|
|
|
+ }
|
|
|
+
|
|
|
+ public void seekTo(int msec) {
|
|
|
+ if (!isReady) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (player == null) {
|
|
|
+ LogUtils.e("mediaPlayer不能为空");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ player.seekTo(msec);
|
|
|
+ }
|
|
|
+
|
|
|
+ public boolean isPlaying() {
|
|
|
+ if (player == null) {
|
|
|
+ LogUtils.e("mediaPlayer不能为空");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ return player.isPlaying();
|
|
|
+ }
|
|
|
+
|
|
|
+ public static abstract class MySingleOrDoubleClickListener implements View.OnClickListener {
|
|
|
+ /**
|
|
|
+ * 双击有效时长(毫秒),建议200~500毫秒内
|
|
|
+ */
|
|
|
+ private static final long ClickInterval = 210L;
|
|
|
+ //上次点击的时间
|
|
|
+ private long tLastClick = 0;
|
|
|
+ private View v;
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onClick(View v) {
|
|
|
+ this.v = v;
|
|
|
+ long t = System.currentTimeMillis();
|
|
|
+ if (tLastClick == 0) {
|
|
|
+ timer.start();
|
|
|
+ } else {
|
|
|
+ if (t - tLastClick <= ClickInterval) {
|
|
|
+ onDouble(v);
|
|
|
+ timer.cancel();
|
|
|
+ } else {
|
|
|
+ timer.start();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ tLastClick = t;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 单击倒计时
|
|
|
+ */
|
|
|
+ private final CountDownTimer timer = new CountDownTimer(ClickInterval, ClickInterval) {
|
|
|
+ @Override
|
|
|
+ public void onTick(long millisUntilFinished) {
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onFinish() {
|
|
|
+ onSingle(v);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ public abstract void onSingle(View v);
|
|
|
+
|
|
|
+ public abstract void onDouble(View v);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|