diff --git a/CHANGELOG.md b/CHANGELOG.md
index 185fb0c14b..ef6b869369 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,25 @@
+# [6.3.0](https://github.com/TheWidlarzGroup/react-native-video/compare/v6.2.0...v6.3.0) (2024-06-22)
+
+
+### Bug Fixes
+
+* **android:** allow chunk less preparation ([#3913](https://github.com/TheWidlarzGroup/react-native-video/issues/3913)) ([264b57a](https://github.com/TheWidlarzGroup/react-native-video/commit/264b57aa2ed01b52c09b2992d5ca61e33871315b))
+* **android:** avoid crash multiplayer with notification ([#3931](https://github.com/TheWidlarzGroup/react-native-video/issues/3931)) ([104ee70](https://github.com/TheWidlarzGroup/react-native-video/commit/104ee703bac0f7d5fb162d17c30f65f8071d07a6))
+* **android:** show controls in notification on older androids ([#3886](https://github.com/TheWidlarzGroup/react-native-video/issues/3886)) ([098a754](https://github.com/TheWidlarzGroup/react-native-video/commit/098a754110a2c58fc18062769f4dd83775d20de4))
+* **android:** use UI thread to pause when lost audio focus ([#3916](https://github.com/TheWidlarzGroup/react-native-video/issues/3916)) ([856b1dd](https://github.com/TheWidlarzGroup/react-native-video/commit/856b1dd58ba9bb1832397ee0fc86f778f737798f))
+* **ios:** crash on ads after leaving the app ([#3911](https://github.com/TheWidlarzGroup/react-native-video/issues/3911)) ([3d6bc94](https://github.com/TheWidlarzGroup/react-native-video/commit/3d6bc9409c2111760620a821466b0ea0f1970681))
+* **ios:** missing notification controls when enabled from start ([#3898](https://github.com/TheWidlarzGroup/react-native-video/issues/3898)) ([2d793db](https://github.com/TheWidlarzGroup/react-native-video/commit/2d793dbde16cc140d1e7b873c40ff4dec285e253))
+* **JS:** safety check on resolve uri ([#3915](https://github.com/TheWidlarzGroup/react-native-video/issues/3915)) ([84bb910](https://github.com/TheWidlarzGroup/react-native-video/commit/84bb910d10e3e3d70f9e92b57c17de829d73218a))
+* **typescript:** type checks on selectedTextTrack, selectedAudioTrack, selectedVideoTrack ([#3910](https://github.com/TheWidlarzGroup/react-native-video/issues/3910)) ([dc2a2ab](https://github.com/TheWidlarzGroup/react-native-video/commit/dc2a2ab863dc53fb2da1c2cea99e59e6d8387803))
+
+
+### Features
+
+* **android:** add `onControlsVisiblityChange` ([#3925](https://github.com/TheWidlarzGroup/react-native-video/issues/3925)) ([c2ce372](https://github.com/TheWidlarzGroup/react-native-video/commit/c2ce372bcf21e21b97878e7efe77780168d09a79))
+* **ios:** add live key to now playing dict to decorate when livestream playing ([#3922](https://github.com/TheWidlarzGroup/react-native-video/issues/3922)) ([91751ab](https://github.com/TheWidlarzGroup/react-native-video/commit/91751abc870109e0a2667dfffbd2baa2e4cf997b))
+
# [6.2.0](https://github.com/TheWidlarzGroup/react-native-video/compare/v6.1.2...v6.2.0) (2024-06-07)
diff --git a/android/src/main/java/com/brentvatne/common/react/VideoEventEmitter.java b/android/src/main/java/com/brentvatne/common/react/VideoEventEmitter.java
index 0f163b2525..be6166b46f 100644
--- a/android/src/main/java/com/brentvatne/common/react/VideoEventEmitter.java
+++ b/android/src/main/java/com/brentvatne/common/react/VideoEventEmitter.java
@@ -37,6 +37,7 @@ public VideoEventEmitter(ReactContext reactContext) {
private static final String EVENT_ERROR = "onVideoError";
private static final String EVENT_PROGRESS = "onVideoProgress";
private static final String EVENT_BANDWIDTH = "onVideoBandwidthUpdate";
+ private static final String EVENT_CONTROLS_VISIBILITY_CHANGE = "onControlsVisibilityChange";
private static final String EVENT_SEEK = "onVideoSeek";
private static final String EVENT_END = "onVideoEnd";
private static final String EVENT_FULLSCREEN_WILL_PRESENT = "onVideoFullscreenPlayerWillPresent";
@@ -89,6 +90,7 @@ public VideoEventEmitter(ReactContext reactContext) {
EVENT_TEXT_TRACK_DATA_CHANGED,
EVENT_VIDEO_TRACKS,
EVENT_BANDWIDTH,
+ EVENT_CONTROLS_VISIBILITY_CHANGE,
EVENT_ON_RECEIVE_AD_EVENT
};
@@ -120,6 +122,7 @@ public VideoEventEmitter(ReactContext reactContext) {
EVENT_TEXT_TRACK_DATA_CHANGED,
EVENT_VIDEO_TRACKS,
EVENT_BANDWIDTH,
+ EVENT_CONTROLS_VISIBILITY_CHANGE,
EVENT_ON_RECEIVE_AD_EVENT
})
@interface VideoEvents {
@@ -164,6 +167,8 @@ public VideoEventEmitter(ReactContext reactContext) {
private static final String EVENT_PROP_IS_PLAYING = "isPlaying";
+ private static final String EVENT_CONTROLS_VISIBLE = "isVisible";
+
public void setViewId(int viewId) {
this.viewId = viewId;
}
@@ -354,6 +359,12 @@ public void end() {
receiveEvent(EVENT_END, null);
}
+ public void controlsVisibilityChanged(boolean isVisible) {
+ WritableMap map = Arguments.createMap();
+ map.putBoolean(EVENT_CONTROLS_VISIBLE, isVisible);
+ receiveEvent(EVENT_CONTROLS_VISIBILITY_CHANGE, map);
+ }
+
public void fullscreenWillPresent() {
receiveEvent(EVENT_FULLSCREEN_WILL_PRESENT, null);
}
diff --git a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java
index 960821001b..ef6c88784f 100644
--- a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java
+++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java
@@ -420,6 +420,12 @@ private void togglePlayerControlVisibility() {
private void initializePlayerControl() {
if (playerControlView == null) {
playerControlView = new LegacyPlayerControlView(getContext());
+ playerControlView.addVisibilityListener(new LegacyPlayerControlView.VisibilityListener() {
+ @Override
+ public void onVisibilityChange(int visibility) {
+ eventEmitter.controlsVisibilityChanged(visibility == View.VISIBLE);
+ }
+ });
}
if (fullScreenPlayerView == null) {
diff --git a/android/src/main/java/com/brentvatne/exoplayer/VideoPlaybackService.kt b/android/src/main/java/com/brentvatne/exoplayer/VideoPlaybackService.kt
index ba2590e49f..1a0da5becb 100644
--- a/android/src/main/java/com/brentvatne/exoplayer/VideoPlaybackService.kt
+++ b/android/src/main/java/com/brentvatne/exoplayer/VideoPlaybackService.kt
@@ -69,8 +69,6 @@ class VideoPlaybackService : MediaSessionService() {
hidePlayerNotification(player)
val session = mediaSessionsList.remove(player)
session?.release()
- sourceActivity = null
-
if (mediaSessionsList.isEmpty()) {
cleanup()
stopSelf()
diff --git a/docs/pages/component/events.mdx b/docs/pages/component/events.mdx
index 713547571f..146c0d65d5 100644
--- a/docs/pages/component/events.mdx
+++ b/docs/pages/component/events.mdx
@@ -121,6 +121,26 @@ Example:
}
```
+### `onControlsVisibilityChange`
+
+
+
+Callback function that is called when the controls are hidden or shown. Not possible on iOS.
+
+Payload:
+
+| Property | Type | Description |
+| ----------- | ------- | ---------------------------------------------- |
+| isVisible | boolean | Boolean indicating whether controls are visible |
+
+Example:
+
+```javascript
+{
+ isVisible: true;
+}
+```
+
### `onEnd`
diff --git a/ios/Video/NowPlayingInfoCenterManager.swift b/ios/Video/NowPlayingInfoCenterManager.swift
index 674c249180..157502ed9a 100644
--- a/ios/Video/NowPlayingInfoCenterManager.swift
+++ b/ios/Video/NowPlayingInfoCenterManager.swift
@@ -226,6 +226,12 @@ class NowPlayingInfoCenterManager {
})
}
+ if CMTIME_IS_INDEFINITE(currentItem.asset.duration) {
+ nowPlayingInfo[MPNowPlayingInfoPropertyIsLiveStream] = true
+ } else {
+ nowPlayingInfo[MPNowPlayingInfoPropertyIsLiveStream] = false
+ }
+
nowPlayingInfo[MPMediaItemPropertyPlaybackDuration] = currentItem.duration.seconds
nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = currentItem.currentTime().seconds
nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = player.rate
@@ -245,6 +251,11 @@ class NowPlayingInfoCenterManager {
nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = currentItem.currentTime().seconds.rounded()
nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = player.rate
+ if CMTIME_IS_INDEFINITE(currentItem.asset.duration) {
+ nowPlayingInfo[MPNowPlayingInfoPropertyIsLiveStream] = true
+ } else {
+ nowPlayingInfo[MPNowPlayingInfoPropertyIsLiveStream] = false
+ }
MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo
}
}
diff --git a/package.json b/package.json
index 2d299d5843..a4419c949a 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "react-native-video",
- "version": "6.2.0",
+ "version": "6.3.0",
"description": "A element for react-native",
"main": "lib/index",
"source": "src/index",
diff --git a/src/Video.tsx b/src/Video.tsx
index 627c993082..fbad49c56b 100644
--- a/src/Video.tsx
+++ b/src/Video.tsx
@@ -22,6 +22,7 @@ import NativeVideoComponent, {
type OnAudioTracksData,
type OnBandwidthUpdateData,
type OnBufferData,
+ type OnControlsVisibilityChange,
type OnExternalPlaybackChangeData,
type OnGetLicenseData,
type OnLoadStartData,
@@ -91,6 +92,7 @@ const Video = forwardRef(
onEnd,
onBuffer,
onBandwidthUpdate,
+ onControlsVisibilityChange,
onExternalPlaybackChange,
onFullscreenPlayerWillPresent,
onFullscreenPlayerDidPresent,
@@ -482,6 +484,13 @@ const Video = forwardRef(
[onAspectRatio],
);
+ const _onControlsVisibilityChange = useCallback(
+ (e: NativeSyntheticEvent) => {
+ onControlsVisibilityChange?.(e.nativeEvent);
+ },
+ [onControlsVisibilityChange],
+ );
+
const useExternalGetLicense = drm?.getLicense instanceof Function;
const onGetLicense = useCallback(
@@ -639,6 +648,9 @@ const Video = forwardRef(
? (_onReceiveAdEvent as (e: NativeSyntheticEvent