diff --git a/demo/src/main/java/hybridmediaplayer/demo/MainActivity.java b/demo/src/main/java/hybridmediaplayer/demo/MainActivity.java index be3bb7d..9bb9a26 100644 --- a/demo/src/main/java/hybridmediaplayer/demo/MainActivity.java +++ b/demo/src/main/java/hybridmediaplayer/demo/MainActivity.java @@ -152,7 +152,10 @@ private void createPlayer() { // sources.add(source1); sources.add(source1); sources.add(source2); -// sources.add(source3); + sources.add(source3); + sources.add(source1); + sources.add(source2); + sources.add(source3); // sources.add(source4); // sources.add(source5); mediaPlayer.setPlayerView(this, playerView); @@ -206,7 +209,7 @@ public void onClick(View view) { KLog.i(mediaPlayer.getDuration()); } } else if (view.getId() == R.id.fastForward) { - mediaPlayer.seekTo(mediaPlayer.getDuration() - 2000); + mediaPlayer.seekTo(mediaPlayer.getDuration() - 200); } else if (view.getId() == R.id.btSpeed) { // if (speed == 1) // speed = 2f; diff --git a/hybridmediaplayer/src/main/java/hybridmediaplayer/BasePlayer.java b/hybridmediaplayer/src/main/java/hybridmediaplayer/BasePlayer.java new file mode 100644 index 0000000..4a47332 --- /dev/null +++ b/hybridmediaplayer/src/main/java/hybridmediaplayer/BasePlayer.java @@ -0,0 +1,181 @@ +package hybridmediaplayer; + +import androidx.annotation.Nullable; + +import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.Player; +import com.google.android.exoplayer2.Timeline; +import com.google.android.exoplayer2.util.Util; + +/** Abstract base {@link Player} which implements common implementation independent methods. */ +public abstract class BasePlayer implements Player { + + protected final Timeline.Window window; + + public BasePlayer() { + window = new Timeline.Window(); + } + + @Override + public final void seekToDefaultPosition() { + seekToDefaultPosition(getCurrentWindowIndex()); + } + + @Override + public final void seekToDefaultPosition(int windowIndex) { + seekTo(windowIndex, /* positionMs= */ C.TIME_UNSET); + } + + @Override + public final void seekTo(long positionMs) { + seekTo(getCurrentWindowIndex(), positionMs); + } + + @Override + public final boolean hasPrevious() { + return getPreviousWindowIndex() != C.INDEX_UNSET; + } + + @Override + public final void previous() { + int previousWindowIndex = getPreviousWindowIndex(); + if (previousWindowIndex != C.INDEX_UNSET) { + seekToDefaultPosition(previousWindowIndex); + } + } + + @Override + public final boolean hasNext() { + return getNextWindowIndex() != C.INDEX_UNSET; + } + + @Override + public final void next() { + int nextWindowIndex = getNextWindowIndex(); + if (nextWindowIndex != C.INDEX_UNSET) { + seekToDefaultPosition(nextWindowIndex); + } + } + + @Override + public final void stop() { + stop(/* reset= */ false); + } + + @Override + public final int getNextWindowIndex() { + Timeline timeline = getCurrentTimeline(); + return timeline.isEmpty() + ? C.INDEX_UNSET + : timeline.getNextWindowIndex( + getCurrentWindowIndex(), getRepeatModeForNavigation(), getShuffleModeEnabled()); + } + + @Override + public final int getPreviousWindowIndex() { + Timeline timeline = getCurrentTimeline(); + return timeline.isEmpty() + ? C.INDEX_UNSET + : timeline.getPreviousWindowIndex( + getCurrentWindowIndex(), getRepeatModeForNavigation(), getShuffleModeEnabled()); + } + + @Override + @Nullable + public final Object getCurrentTag() { + int windowIndex = getCurrentWindowIndex(); + Timeline timeline = getCurrentTimeline(); + return windowIndex >= timeline.getWindowCount() + ? null + : timeline.getWindow(windowIndex, window, /* setTag= */ true).tag; + } + + @Override + public final int getBufferedPercentage() { + long position = getBufferedPosition(); + long duration = getDuration(); + return position == C.TIME_UNSET || duration == C.TIME_UNSET + ? 0 + : duration == 0 ? 100 : Util.constrainValue((int) ((position * 100) / duration), 0, 100); + } + + @Override + public final boolean isCurrentWindowDynamic() { + Timeline timeline = getCurrentTimeline(); + return !timeline.isEmpty() && timeline.getWindow(getCurrentWindowIndex(), window).isDynamic; + } + + @Override + public final boolean isCurrentWindowSeekable() { + Timeline timeline = getCurrentTimeline(); + return !timeline.isEmpty() && timeline.getWindow(getCurrentWindowIndex(), window).isSeekable; + } + + @Override + public final long getContentDuration() { + Timeline timeline = getCurrentTimeline(); + return timeline.isEmpty() + ? C.TIME_UNSET + : timeline.getWindow(getCurrentWindowIndex(), window).getDurationMs(); + } + + @RepeatMode + private int getRepeatModeForNavigation() { + @RepeatMode int repeatMode = getRepeatMode(); + return repeatMode == REPEAT_MODE_ONE ? REPEAT_MODE_OFF : repeatMode; + } + + /** Holds a listener reference. */ + protected static final class ListenerHolder { + + /** + * The listener on which {link #invoke} will execute {@link ListenerInvocation listener + * invocations}. + */ + public final Player.EventListener listener; + + private boolean released; + + public ListenerHolder(Player.EventListener listener) { + this.listener = listener; + } + + /** Prevents any further {@link ListenerInvocation} to be executed on {@link #listener}. */ + public void release() { + released = true; + } + + /** + * Executes the given {@link ListenerInvocation} on {@link #listener}. Does nothing if {@link + * #release} has been called on this instance. + */ + public void invoke(ListenerInvocation listenerInvocation) { + if (!released) { + listenerInvocation.invokeListener(listener); + } + } + + @Override + public boolean equals(@Nullable Object other) { + if (this == other) { + return true; + } + if (other == null || getClass() != other.getClass()) { + return false; + } + return listener.equals(((ListenerHolder) other).listener); + } + + @Override + public int hashCode() { + return listener.hashCode(); + } + } + + /** Parameterized invocation of a {@link Player.EventListener} method. */ + protected interface ListenerInvocation { + + /** Executes the invocation on the given {@link Player.EventListener}. */ + void invokeListener(Player.EventListener listener); + } +} \ No newline at end of file diff --git a/hybridmediaplayer/src/main/java/hybridmediaplayer/CastPlayer.java b/hybridmediaplayer/src/main/java/hybridmediaplayer/CastPlayer.java index a8b7a9a..3f6948c 100644 --- a/hybridmediaplayer/src/main/java/hybridmediaplayer/CastPlayer.java +++ b/hybridmediaplayer/src/main/java/hybridmediaplayer/CastPlayer.java @@ -48,7 +48,9 @@ import com.google.android.gms.cast.framework.media.RemoteMediaClient.MediaChannelResult; import com.google.android.gms.common.api.PendingResult; import com.google.android.gms.common.api.ResultCallback; +import com.socks.library.KLog; +import java.util.Arrays; import java.util.List; import java.util.concurrent.CopyOnWriteArraySet; @@ -487,6 +489,8 @@ public int getCurrentPeriodIndex() { @Override public int getCurrentWindowIndex() { + KLog.d(fetchCurrentWindowIndex(getMediaStatus())); + KLog.e(pendingSeekWindowIndex); return pendingSeekWindowIndex != C.INDEX_UNSET ? pendingSeekWindowIndex : currentWindowIndex; } @@ -581,6 +585,7 @@ public void updateInternalState() { } } int currentWindowIndex = fetchCurrentWindowIndex(getMediaStatus()); + KLog.d("cdcd "+currentWindowIndex); if (this.currentWindowIndex != currentWindowIndex && pendingSeekCount == 0) { this.currentWindowIndex = currentWindowIndex; for (EventListener listener : listeners) { @@ -745,9 +750,9 @@ private static int fetchRepeatMode(RemoteMediaClient remoteMediaClient) { * Retrieves the current item index from {@code mediaStatus} and maps it into a window index. If * there is no media session, returns 0. */ - private static int fetchCurrentWindowIndex(@Nullable MediaStatus mediaStatus) { + private int fetchCurrentWindowIndex(@Nullable MediaStatus mediaStatus) { Integer currentItemId = mediaStatus != null - ? mediaStatus.getIndexById(mediaStatus.getCurrentItemId()) : null; + ? Arrays.binarySearch(remoteMediaClient.getMediaQueue().getItemIds(),remoteMediaClient.getMediaStatus().getCurrentItemId() ) : null; return currentItemId != null ? currentItemId : -1; } diff --git a/hybridmediaplayer/src/main/java/hybridmediaplayer/ExoMediaPlayer.java b/hybridmediaplayer/src/main/java/hybridmediaplayer/ExoMediaPlayer.java index 9c93a4e..4bb5012 100644 --- a/hybridmediaplayer/src/main/java/hybridmediaplayer/ExoMediaPlayer.java +++ b/hybridmediaplayer/src/main/java/hybridmediaplayer/ExoMediaPlayer.java @@ -43,6 +43,7 @@ import com.google.android.gms.cast.framework.SessionManager; import com.google.android.gms.cast.framework.media.RemoteMediaClient; import com.google.android.gms.common.images.WebImage; +import com.socks.library.KLog; import java.util.ArrayList; import java.util.List; @@ -562,12 +563,17 @@ public void onPlayerStateChanged(boolean playWhenReady, int playbackState) { break; case Player.STATE_IDLE: + KLog.e(currentWindow); + KLog.w(getWindowCount()); if (isCasting && currentWindow == getWindowCount() - 1) { SessionManager sessionManager = CastContext.getSharedInstance(context).getSessionManager(); CastSession castSession = sessionManager.getCurrentCastSession(); if (castSession != null) { + KLog.e(); RemoteMediaClient remoteMediaClient = castSession.getRemoteMediaClient(); if (remoteMediaClient != null) { + KLog.e(remoteMediaClient.getIdleReason()); + if (MediaStatus.IDLE_REASON_FINISHED == remoteMediaClient.getIdleReason()) { if (onCompletionListener != null) { currentPlayer.setPlayWhenReady(false);