Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add isSeeking to onPlaybackStateChanged #3899

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -69,7 +69,7 @@ class VideoEventEmitter {
lateinit var onVideoError: (errorString: String, exception: Exception, errorCode: String) -> Unit
lateinit var onVideoProgress: (currentPosition: Long, bufferedDuration: Long, seekableDuration: Long, currentPlaybackTime: Double) -> Unit
lateinit var onVideoBandwidthUpdate: (bitRateEstimate: Long, height: Int, width: Int, trackId: String) -> Unit
lateinit var onVideoPlaybackStateChanged: (isPlaying: Boolean) -> Unit
lateinit var onVideoPlaybackStateChanged: (isPlaying: Boolean, isSeeking: Boolean) -> Unit
lateinit var onVideoSeek: (currentPosition: Long, seekTime: Long) -> Unit
lateinit var onVideoEnd: () -> Unit
lateinit var onVideoFullscreenPlayerWillPresent: () -> Unit
@@ -158,9 +158,10 @@ class VideoEventEmitter {
putString("trackId", trackId)
}
}
onVideoPlaybackStateChanged = { isPlaying ->
onVideoPlaybackStateChanged = { isPlaying, isSeeking ->
event.dispatch(EventTypes.EVENT_PLAYBACK_STATE_CHANGED) {
putBoolean("isPlaying", isPlaying)
putBoolean("isSeeking", isSeeking)
}
}
onVideoSeek = { currentPosition, seekTime ->
Original file line number Diff line number Diff line change
@@ -222,6 +222,13 @@ public class ReactExoplayerView extends FrameLayout implements
private boolean useCache = false;
private ControlsConfig controlsConfig = new ControlsConfig();

/*
* When user is seeking first called is on onPositionDiscontinuity -> DISCONTINUITY_REASON_SEEK
* Then we set if to false when playback is back in onIsPlayingChanged -> true
*/
private boolean isSeeking = false;
private long seekPosition = -1;

// Props from React
private Source source = new Source();
private boolean repeat;
@@ -1620,14 +1627,20 @@ private void onBuffering(boolean buffering) {
return;
}

if (isPaused && isSeeking && !buffering) {
eventEmitter.onVideoSeek.invoke(player.getCurrentPosition(), seekPosition);
isSeeking = false;
}

isBuffering = buffering;
eventEmitter.onVideoBuffer.invoke(buffering);
}

@Override
public void onPositionDiscontinuity(@NonNull Player.PositionInfo oldPosition, @NonNull Player.PositionInfo newPosition, @Player.DiscontinuityReason int reason) {
if (reason == Player.DISCONTINUITY_REASON_SEEK) {
eventEmitter.onVideoSeek.invoke(player.getCurrentPosition(), newPosition.positionMs % 1000); // time are in seconds /°\
isSeeking = true;
seekPosition = newPosition.positionMs;
if (isUsingContentResolution) {
// We need to update the selected track to make sure that it still matches user selection if track list has changed in this period
setSelectedTrack(C.TRACK_TYPE_VIDEO, videoTrackType, videoTrackValue);
@@ -1678,7 +1691,15 @@ public void onVolumeChanged(float volume) {

@Override
public void onIsPlayingChanged(boolean isPlaying) {
eventEmitter.onVideoPlaybackStateChanged.invoke(isPlaying);
if (isPlaying && isSeeking) {
eventEmitter.onVideoSeek.invoke(player.getCurrentPosition(), seekPosition);
}

eventEmitter.onVideoPlaybackStateChanged.invoke(isPlaying, isSeeking);

if (isPlaying) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this test should be before eventEmitter.playbackStateChanged ?
becuase you are changing isSeeking, but not sending it to app ?! 🤔

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes that's correct the flow is:

  1. when seek "is detected" onPositionDiscontinuity is called and in it we are setting isSeeking to true
  2. after seeking is done (and we want to triger onSeek on JS side) two thing can happen:
    A) If player was not paused when user seek -> onIsPlayingChanged will be called
    B) If player was paused when user seek -> we will catch that loading is finished in onBuffering

We are sending event with isSeeking=true bcs we want to inform that onPlaybackStateChanged was called bcs of seeking, and then we will set in back to false

This is needed to check if we should show spiner when user is seeking (custom controls) - otherwise we don't know if onPlaybackStateChanged was called bcs of "pause/resume" or "seeking"

So I believe this is correct

isSeeking = false;
}
}

@Override
8 changes: 5 additions & 3 deletions docs/pages/component/events.mdx
Original file line number Diff line number Diff line change
@@ -296,15 +296,17 @@ Callback function that is called when the playback state changes.

Payload:

| Property | Type | Description |
| --------- | ----------- | ------------------------------------------------- |
| isPlaying | boolean | Boolean indicating if the media is playing or not |
| Property | Type | Description |
| --------- | ----------- | -------------------------------------------------- |
| isPlaying | boolean | Boolean indicating if the media is playing or not |
| isSeeking | boolean | Boolean indicating if the player is seeking or not |

Example:

```javascript
{
isPlaying: true,
isSeeking: false
}
```

6 changes: 4 additions & 2 deletions ios/Video/RCTVideo.swift
Original file line number Diff line number Diff line change
@@ -758,8 +758,10 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
let seekTime: NSNumber! = info["time"] as! NSNumber
let seekTolerance: NSNumber! = info["tolerance"] as! NSNumber
let item: AVPlayerItem? = _player?.currentItem

_pendingSeek = true

guard item != nil, let player = _player, let item, item.status == AVPlayerItem.Status.readyToPlay else {
_pendingSeek = true
_pendingSeekTime = seekTime.floatValue
return
}
@@ -1487,7 +1489,7 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH

guard _isPlaying != isPlaying else { return }
_isPlaying = isPlaying
onVideoPlaybackStateChanged?(["isPlaying": isPlaying, "target": reactTag as Any])
onVideoPlaybackStateChanged?(["isPlaying": isPlaying, "isSeeking": self._pendingSeek == true, "target": reactTag as Any])
}

func handlePlaybackRateChange(player: AVPlayer, change: NSKeyValueObservedChange<Float>) {
1 change: 1 addition & 0 deletions src/specs/VideoNativeComponent.ts
Original file line number Diff line number Diff line change
@@ -187,6 +187,7 @@ export type OnSeekData = Readonly<{

export type OnPlaybackStateChangedData = Readonly<{
isPlaying: boolean;
isSeeking: boolean;
}>;

export type OnTimedMetadataData = Readonly<{
Loading