fix: ensure progress is sent before onEnd callback (#3872)

* fix: add onProgress event before onEnd
This commit is contained in:
Olivier Bouillet 2024-06-03 12:13:52 +02:00 committed by GitHub
parent adedc052f0
commit 7133c96cac
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 38 additions and 30 deletions

View File

@ -57,9 +57,7 @@ class Source {
} }
/** return true if this and src are equals */ /** return true if this and src are equals */
fun isEquals(source: Source): Boolean { fun isEquals(source: Source): Boolean = this == source
return this == source
}
/** Metadata to display in notification */ /** Metadata to display in notification */
class Metadata { class Metadata {

View File

@ -258,17 +258,17 @@ public class ReactExoplayerView extends FrameLayout implements
private long lastBufferDuration = -1; private long lastBufferDuration = -1;
private long lastDuration = -1; private long lastDuration = -1;
private final Handler progressHandler = new Handler(Looper.getMainLooper()) { private void updateProgress() {
@Override
public void handleMessage(Message msg) {
if (msg.what == SHOW_PROGRESS) {
if (player != null) { if (player != null) {
if (playerControlView != null && isPlayingAd() && controls) { if (playerControlView != null && isPlayingAd() && controls) {
playerControlView.hide(); playerControlView.hide();
} }
long pos = player.getCurrentPosition();
long bufferedDuration = player.getBufferedPercentage() * player.getDuration() / 100; long bufferedDuration = player.getBufferedPercentage() * player.getDuration() / 100;
long duration = player.getDuration(); long duration = player.getDuration();
long pos = player.getCurrentPosition();
if (pos > duration) {
pos = duration;
}
if (lastPos != pos if (lastPos != pos
|| lastBufferDuration != bufferedDuration || lastBufferDuration != bufferedDuration
@ -278,11 +278,18 @@ public class ReactExoplayerView extends FrameLayout implements
lastDuration = duration; lastDuration = duration;
eventEmitter.progressChanged(pos, bufferedDuration, player.getDuration(), getPositionInFirstPeriodMsForCurrentWindow(pos)); eventEmitter.progressChanged(pos, bufferedDuration, player.getDuration(), getPositionInFirstPeriodMsForCurrentWindow(pos));
} }
}
}
private final Handler progressHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
if (msg.what == SHOW_PROGRESS) {
updateProgress();
msg = obtainMessage(SHOW_PROGRESS); msg = obtainMessage(SHOW_PROGRESS);
sendMessageDelayed(msg, Math.round(mProgressUpdateInterval)); sendMessageDelayed(msg, Math.round(mProgressUpdateInterval));
} }
} }
}
}; };
public double getPositionInFirstPeriodMsForCurrentWindow(long currentPosition) { public double getPositionInFirstPeriodMsForCurrentWindow(long currentPosition) {
@ -1337,6 +1344,7 @@ public class ReactExoplayerView extends FrameLayout implements
break; break;
case Player.STATE_ENDED: case Player.STATE_ENDED:
text += "ended"; text += "ended";
updateProgress();
eventEmitter.end(); eventEmitter.end();
onStopPlayback(); onStopPlayback();
setKeepScreenOn(false); setKeepScreenOn(false);
@ -1614,6 +1622,7 @@ public class ReactExoplayerView extends FrameLayout implements
// so we need to explicitly detect it. // so we need to explicitly detect it.
if (reason == Player.DISCONTINUITY_REASON_AUTO_TRANSITION if (reason == Player.DISCONTINUITY_REASON_AUTO_TRANSITION
&& player.getRepeatMode() == Player.REPEAT_MODE_ONE) { && player.getRepeatMode() == Player.REPEAT_MODE_ONE) {
updateProgress();
eventEmitter.end(); eventEmitter.end();
} }
} }

View File

@ -312,7 +312,7 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
// MARK: - Progress // MARK: - Progress
func sendProgressUpdate() { func sendProgressUpdate(didEnd: Bool = false) {
#if !USE_GOOGLE_IMA #if !USE_GOOGLE_IMA
// If we dont use Ads and onVideoProgress is not defined we dont need to run this code // If we dont use Ads and onVideoProgress is not defined we dont need to run this code
guard onVideoProgress != nil else { return } guard onVideoProgress != nil else { return }
@ -334,11 +334,11 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
} }
let currentPlaybackTime = _player?.currentItem?.currentDate() let currentPlaybackTime = _player?.currentItem?.currentDate()
let duration = CMTimeGetSeconds(playerDuration) let duration = CMTimeGetSeconds(playerDuration)
let currentTimeSecs = CMTimeGetSeconds(currentTime ?? .zero) var currentTimeSecs = CMTimeGetSeconds(currentTime ?? .zero)
NotificationCenter.default.post(name: NSNotification.Name("RCTVideo_progress"), object: nil, userInfo: [ if currentTimeSecs > duration || didEnd {
"progress": NSNumber(value: currentTimeSecs / duration), currentTimeSecs = duration
]) }
if currentTimeSecs >= 0 { if currentTimeSecs >= 0 {
#if USE_GOOGLE_IMA #if USE_GOOGLE_IMA
@ -348,10 +348,10 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
} }
#endif #endif
onVideoProgress?([ onVideoProgress?([
"currentTime": NSNumber(value: Float(currentTimeSecs)), "currentTime": currentTimeSecs,
"playableDuration": RCTVideoUtils.calculatePlayableDuration(_player, withSource: _source), "playableDuration": RCTVideoUtils.calculatePlayableDuration(_player, withSource: _source),
"atValue": NSNumber(value: currentTime?.value ?? .zero), "atValue": currentTime?.value ?? .zero,
"currentPlaybackTime": NSNumber(value: NSNumber(value: Double(currentPlaybackTime?.timeIntervalSince1970 ?? 0 * 1000)).int64Value), "currentPlaybackTime": NSNumber(value: Double(currentPlaybackTime?.timeIntervalSince1970 ?? 0 * 1000)).int64Value,
"target": reactTag, "target": reactTag,
"seekableDuration": RCTVideoUtils.calculateSeekableDuration(_player), "seekableDuration": RCTVideoUtils.calculateSeekableDuration(_player),
]) ])
@ -1570,6 +1570,7 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
@objc @objc
func handlePlayerItemDidReachEnd(notification: NSNotification!) { func handlePlayerItemDidReachEnd(notification: NSNotification!) {
sendProgressUpdate(didEnd: true)
onVideoEnd?(["target": reactTag as Any]) onVideoEnd?(["target": reactTag as Any])
#if USE_GOOGLE_IMA #if USE_GOOGLE_IMA
if notification.object as? AVPlayerItem == _player?.currentItem { if notification.object as? AVPlayerItem == _player?.currentItem {