diff --git a/app/src/main/java/com/fongmi/android/tv/player/Players.java b/app/src/main/java/com/fongmi/android/tv/player/Players.java index c68db660a..5b511a231 100644 --- a/app/src/main/java/com/fongmi/android/tv/player/Players.java +++ b/app/src/main/java/com/fongmi/android/tv/player/Players.java @@ -137,6 +137,10 @@ public class Players implements Player.Listener, IMediaPlayer.OnInfoListener, IM return isExo() ? exoPlayer.getDuration() : ijkPlayer.getDuration(); } + public long getBuffered() { + return isExo() ? exoPlayer.getBufferedPosition() : ijkPlayer.getBufferedPosition(); + } + public void seekTo(int time) { if (time == 0) return; if (isExo()) exoPlayer.seekTo(getPosition() + time); diff --git a/app/src/main/java/com/fongmi/android/tv/ui/custom/CustomSeekView.java b/app/src/main/java/com/fongmi/android/tv/ui/custom/CustomSeekView.java index c808233b0..6394e9c3b 100644 --- a/app/src/main/java/com/fongmi/android/tv/ui/custom/CustomSeekView.java +++ b/app/src/main/java/com/fongmi/android/tv/ui/custom/CustomSeekView.java @@ -13,16 +13,23 @@ import com.fongmi.android.tv.R; import com.fongmi.android.tv.player.Players; import com.google.android.exoplayer2.ui.DefaultTimeBar; import com.google.android.exoplayer2.ui.TimeBar; +import com.google.android.exoplayer2.util.Util; public class CustomSeekView extends FrameLayout implements TimeBar.OnScrubListener { - private Players listener; + private static final int MAX_UPDATE_INTERVAL_MS = 1000; + private static final int MIN_UPDATE_INTERVAL_MS = 200; + private TextView positionView; private TextView durationView; private DefaultTimeBar timeBar; + private Runnable runnable; + private Players listener; + + private long currentDuration; + private long currentPosition; private boolean scrubbing; - private boolean postProgress; public CustomSeekView(Context context) { this(context, null); @@ -35,11 +42,20 @@ public class CustomSeekView extends FrameLayout implements TimeBar.OnScrubListen public CustomSeekView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); LayoutInflater.from(context).inflate(R.layout.view_control_seek, this); + initView(); + initEvent(); + startProgress(); + } + + private void initView() { positionView = findViewById(R.id.position); durationView = findViewById(R.id.duration); timeBar = findViewById(R.id.timeBar); + runnable = this::updateProgress; + } + + private void initEvent() { timeBar.addListener(this); - startProgress(); } public void setListener(Players listener) { @@ -48,38 +64,51 @@ public class CustomSeekView extends FrameLayout implements TimeBar.OnScrubListen private void seekToTimeBarPosition(long positionMs) { listener.seekTo(positionMs); + updateProgress(); } public void startProgress() { stopProgress(); - postProgress = true; post(runnable); } public void stopProgress() { - postProgress = false; removeCallbacks(runnable); } - private final Runnable runnable = new Runnable() { - @Override - public void run() { - if (listener.isPlaying()) { - setProgress(); - } - if (postProgress) { - postDelayed(this, getDelay()); - } + private void updateProgress() { + long duration = listener.getDuration(); + long position = listener.getPosition(); + long buffered = listener.getBuffered(); + boolean positionChanged = position != currentPosition; + boolean durationChanged = duration != currentDuration; + currentDuration = duration; + currentPosition = position; + if (durationView != null && durationChanged) { + durationView.setText(listener.stringToTime(duration)); + } + if (timeBar != null && durationChanged) { + timeBar.setDuration(duration); + } + if (positionView != null && !scrubbing && positionChanged) { + positionView.setText(listener.stringToTime(position)); + } + if (timeBar != null) { + timeBar.setPosition(position); + timeBar.setBufferedPosition(buffered); + } + removeCallbacks(runnable); + if (listener.isPlaying()) { + long mediaTimeDelayMs = timeBar != null ? timeBar.getPreferredUpdateDelay() : MAX_UPDATE_INTERVAL_MS; + long mediaTimeUntilNextFullSecondMs = 1000 - position % 1000; + mediaTimeDelayMs = Math.min(mediaTimeDelayMs, mediaTimeUntilNextFullSecondMs); + float playbackSpeed = listener.getSpeed(); + long delayMs = playbackSpeed > 0 ? (long) (mediaTimeDelayMs / playbackSpeed) : MAX_UPDATE_INTERVAL_MS; + delayMs = Util.constrainValue(delayMs, MIN_UPDATE_INTERVAL_MS, MAX_UPDATE_INTERVAL_MS); + postDelayed(runnable, delayMs); + } else { + postDelayed(runnable, MAX_UPDATE_INTERVAL_MS); } - }; - - private long getDelay() { - return (long) ((1000 - listener.getPosition() % 1000) / listener.getSpeed()); - } - - private void setProgress() { - positionView.setText(listener.stringToTime(listener.getPosition())); - durationView.setText(listener.stringToTime(listener.getDuration())); } @Override diff --git a/ijkplayer/src/main/java/tv/danmaku/ijk/media/player/AbstractMediaPlayer.java b/ijkplayer/src/main/java/tv/danmaku/ijk/media/player/AbstractMediaPlayer.java index 0425b65ec..99c0da659 100644 --- a/ijkplayer/src/main/java/tv/danmaku/ijk/media/player/AbstractMediaPlayer.java +++ b/ijkplayer/src/main/java/tv/danmaku/ijk/media/player/AbstractMediaPlayer.java @@ -85,6 +85,11 @@ public abstract class AbstractMediaPlayer implements IMediaPlayer { mOnCompletionListener.onCompletion(this); } + protected final void notifyOnBufferingUpdate(long position) { + if (mOnBufferingUpdateListener != null) + mOnBufferingUpdateListener.onBufferingUpdate(this, position); + } + protected final void notifyOnBufferingUpdate(int percent) { if (mOnBufferingUpdateListener != null) mOnBufferingUpdateListener.onBufferingUpdate(this, percent); diff --git a/ijkplayer/src/main/java/tv/danmaku/ijk/media/player/IMediaPlayer.java b/ijkplayer/src/main/java/tv/danmaku/ijk/media/player/IMediaPlayer.java index da668efe3..5557ded6c 100644 --- a/ijkplayer/src/main/java/tv/danmaku/ijk/media/player/IMediaPlayer.java +++ b/ijkplayer/src/main/java/tv/danmaku/ijk/media/player/IMediaPlayer.java @@ -155,6 +155,9 @@ public interface IMediaPlayer { } interface OnBufferingUpdateListener { + + void onBufferingUpdate(IMediaPlayer mp, long position); + void onBufferingUpdate(IMediaPlayer mp, int percent); } 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 6c30bec46..2d07e5765 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 @@ -23,8 +23,8 @@ import android.annotation.TargetApi; import android.content.ContentResolver; import android.content.Context; import android.content.res.AssetFileDescriptor; -import android.graphics.SurfaceTexture; import android.graphics.Rect; +import android.graphics.SurfaceTexture; import android.media.MediaCodecInfo; import android.media.MediaCodecList; import android.media.RingtoneManager; @@ -997,7 +997,8 @@ public final class IjkMediaPlayer extends AbstractMediaPlayer { } // DebugLog.efmt(TAG, "Buffer (%d%%) %d/%d", percent, bufferPosition, duration); - player.notifyOnBufferingUpdate((int)percent); + player.notifyOnBufferingUpdate(bufferPosition); + player.notifyOnBufferingUpdate((int) percent); return; case MEDIA_SEEK_COMPLETE: diff --git a/ijkplayer/src/main/java/tv/danmaku/ijk/media/player/MediaPlayerProxy.java b/ijkplayer/src/main/java/tv/danmaku/ijk/media/player/MediaPlayerProxy.java index fe15c80b8..5b2e8d409 100644 --- a/ijkplayer/src/main/java/tv/danmaku/ijk/media/player/MediaPlayerProxy.java +++ b/ijkplayer/src/main/java/tv/danmaku/ijk/media/player/MediaPlayerProxy.java @@ -212,6 +212,11 @@ public class MediaPlayerProxy implements IMediaPlayer { if (listener != null) { final OnBufferingUpdateListener finalListener = listener; mBackEndMediaPlayer.setOnBufferingUpdateListener(new OnBufferingUpdateListener() { + @Override + public void onBufferingUpdate(IMediaPlayer mp, long position) { + finalListener.onBufferingUpdate(MediaPlayerProxy.this, position); + } + @Override public void onBufferingUpdate(IMediaPlayer mp, int percent) { finalListener.onBufferingUpdate(MediaPlayerProxy.this, percent); diff --git a/ijkplayer/src/main/java/tv/danmaku/ijk/media/player/ui/IjkVideoView.java b/ijkplayer/src/main/java/tv/danmaku/ijk/media/player/ui/IjkVideoView.java index 929dad1ad..e2e0c9860 100644 --- a/ijkplayer/src/main/java/tv/danmaku/ijk/media/player/ui/IjkVideoView.java +++ b/ijkplayer/src/main/java/tv/danmaku/ijk/media/player/ui/IjkVideoView.java @@ -52,6 +52,9 @@ public class IjkVideoView extends FrameLayout implements MediaController.MediaPl private int mCurrentState = STATE_IDLE; private int mTargetState = STATE_IDLE; + private int mCurrentBufferPercentage; + private long mCurrentBufferPosition; + // All the stuff we need for playing and showing a video private IRenderView.ISurfaceHolder mSurfaceHolder = null; private IjkMediaPlayer mMediaPlayer = null; @@ -62,7 +65,6 @@ public class IjkVideoView extends FrameLayout implements MediaController.MediaPl private int mVideoRotationDegree; private IMediaPlayer.OnCompletionListener mOnCompletionListener; private IMediaPlayer.OnPreparedListener mOnPreparedListener; - private int mCurrentBufferPercentage; private IMediaPlayer.OnErrorListener mOnErrorListener; private IMediaPlayer.OnInfoListener mOnInfoListener; @@ -183,6 +185,7 @@ public class IjkVideoView extends FrameLayout implements MediaController.MediaPl try { createPlayer(); setRender(mCurrentRender); + mCurrentBufferPosition = 0; mCurrentBufferPercentage = 0; mMediaPlayer.setDataSource(mAppContext, mUri, mHeaders); bindSurfaceHolder(mMediaPlayer, mSurfaceHolder); @@ -312,6 +315,12 @@ public class IjkVideoView extends FrameLayout implements MediaController.MediaPl }; private final IMediaPlayer.OnBufferingUpdateListener mBufferingUpdateListener = new IMediaPlayer.OnBufferingUpdateListener() { + @Override + public void onBufferingUpdate(IMediaPlayer mp, long position) { + mCurrentBufferPosition = position; + } + + @Override public void onBufferingUpdate(IMediaPlayer mp, int percent) { mCurrentBufferPercentage = percent; } @@ -454,6 +463,11 @@ public class IjkVideoView extends FrameLayout implements MediaController.MediaPl return isInPlaybackState() && mMediaPlayer.isPlaying(); } + public long getBufferedPosition() { + if (mMediaPlayer != null) return mCurrentBufferPosition; + return 0; + } + @Override public int getBufferPercentage() { if (mMediaPlayer != null) return mCurrentBufferPercentage;