From 2fa1f9edf3bf211af40e617216ec0ae947b4519e Mon Sep 17 00:00:00 2001 From: ajayhcode <117063355+ajayhcode@users.noreply.github.com> Date: Fri, 6 Jan 2023 15:28:42 +0530 Subject: [PATCH 001/249] Update API.md --- API.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/API.md b/API.md index f298abf5..adef8a10 100644 --- a/API.md +++ b/API.md @@ -1122,7 +1122,7 @@ Payload: Property | Type | Description --- | --- | --- -currentPosition | number | Time in seconds where the media will start +currentTime | number | Time in seconds where the media will start duration | number | Length of the media in seconds naturalSize | object | Properties:
* width - Width in pixels that the video was encoded at
* height - Height in pixels that the video was encoded at
* orientation - "portrait" or "landscape" audioTracks | array | An array of audio track info objects with the following properties:
* index - Index number
* title - Description of the track
* language - 2 letter [ISO 639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) or 3 letter [ISO639-2](https://en.wikipedia.org/wiki/List_of_ISO_639-2_codes) language code
* type - Mime type of track From 0b7f91ccc527decbda2b962739424ce178a26ad9 Mon Sep 17 00:00:00 2001 From: Gez Quinn Date: Mon, 16 Jan 2023 17:26:25 +0000 Subject: [PATCH 002/249] Update API.md fix typo --- API.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/API.md b/API.md index f298abf5..30d69880 100644 --- a/API.md +++ b/API.md @@ -344,7 +344,7 @@ var styles = StyleSheet.create({ | [onVideoTracks](#onVideoTracks) | Android | ### Methods -| Name |Plateforms Support | +| Name |Platforms Support | |--|--| |[dismissFullscreenPlayer](#dismissfullscreenplayer)|Android, iOS| |[presentFullscreenPlayer](#presentfullscreenplayer)|Android, iOS| @@ -354,7 +354,7 @@ var styles = StyleSheet.create({ ### Static methods -| Name |Plateforms Support | +| Name |Platforms Support | |--|--| |[getWidevineLevel](#getWidevineLevel)|Android| |[isCodecSupported](#isCodecSupported)|Android| From 07d0c9ebfc6f0ecb53fb1026e929c3da4d7901f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samet=20=C5=9Eahin?= Date: Wed, 18 Jan 2023 13:41:32 +0300 Subject: [PATCH 003/249] fix --- .../java/com/brentvatne/exoplayer/ReactExoplayerView.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index d7ab0761..684dbca5 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -638,7 +638,7 @@ private void initializePlayerCore(ReactExoplayerView self) { .build(); player.addListener(self); exoPlayerView.setPlayer(player); - adsLoader.setPlayer(player); + adsLoader?.setPlayer(player); audioBecomingNoisyReceiver.setListener(self); bandwidthMeter.addEventListener(new Handler(), self); setPlayWhenReady(!isPaused); @@ -863,14 +863,14 @@ private MediaSource buildTextSource(String title, Uri uri, String mimeType, Stri private void releasePlayer() { if (player != null) { - adsLoader.setPlayer(null); + adsLoader?.setPlayer(null); updateResumePosition(); player.release(); player.removeListener(this); trackSelector = null; player = null; } - adsLoader.release(); + adsLoader?.release(); adsLoader = null; progressHandler.removeMessages(SHOW_PROGRESS); themedReactContext.removeLifecycleEventListener(this); From 9d7201100967f7eb8186825e951e2191031945ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samet=20=C5=9Eahin?= Date: Wed, 18 Jan 2023 14:53:34 +0300 Subject: [PATCH 004/249] fix --- .../com/brentvatne/exoplayer/ReactExoplayerView.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 684dbca5..0c0fbc17 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -638,7 +638,9 @@ private void initializePlayerCore(ReactExoplayerView self) { .build(); player.addListener(self); exoPlayerView.setPlayer(player); - adsLoader?.setPlayer(player); + if (adsLoader != null) { + adsLoader.setPlayer(player); + } audioBecomingNoisyReceiver.setListener(self); bandwidthMeter.addEventListener(new Handler(), self); setPlayWhenReady(!isPaused); @@ -863,14 +865,18 @@ private MediaSource buildTextSource(String title, Uri uri, String mimeType, Stri private void releasePlayer() { if (player != null) { - adsLoader?.setPlayer(null); + if (adsLoader != null) { + adsLoader.setPlayer(null); + } updateResumePosition(); player.release(); player.removeListener(this); trackSelector = null; player = null; } - adsLoader?.release(); + if (adsLoader != null) { + adsLoader.release(); + } adsLoader = null; progressHandler.removeMessages(SHOW_PROGRESS); themedReactContext.removeLifecycleEventListener(this); From 36af01cc7b901e14e68e6bd648b5fa5c5254b3bd Mon Sep 17 00:00:00 2001 From: PierluigiCod <62481658+PierluigiCod@users.noreply.github.com> Date: Mon, 23 Jan 2023 16:49:46 +0100 Subject: [PATCH 005/249] fix issue #3009 --- ios/Video/RCTVideo.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ios/Video/RCTVideo.swift b/ios/Video/RCTVideo.swift index 8f3b0e78..baca4d1a 100644 --- a/ios/Video/RCTVideo.swift +++ b/ios/Video/RCTVideo.swift @@ -243,6 +243,7 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH } self.removePlayerLayer() self._playerObserver.player = nil + self._resouceLoaderDelegate = nil self._playerObserver.playerItem = nil // perform on next run loop, otherwise other passed react-props may not be set @@ -750,7 +751,6 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH } func removePlayerLayer() { - _resouceLoaderDelegate = nil _playerLayer?.removeFromSuperlayer() _playerLayer = nil _playerObserver.playerLayer = nil @@ -882,6 +882,7 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH override func removeFromSuperview() { _player?.pause() _player = nil + _resouceLoaderDelegate = nil _playerObserver.clearPlayer() self.removePlayerLayer() From 2032a36969a022c57232baa8f3b9746eff103c28 Mon Sep 17 00:00:00 2001 From: olivier Date: Sat, 28 Jan 2023 14:40:45 +0100 Subject: [PATCH 006/249] fix(ios): ensure controls are not displayed when disabled by user --- ios/Video/RCTVideo.swift | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/ios/Video/RCTVideo.swift b/ios/Video/RCTVideo.swift index baca4d1a..e6bf61f5 100644 --- a/ios/Video/RCTVideo.swift +++ b/ios/Video/RCTVideo.swift @@ -614,7 +614,7 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH func setFullscreen(_ fullscreen:Bool) { if fullscreen && !_fullscreenPlayerPresented && _player != nil { // Ensure player view controller is not null - if _playerViewController == nil { + if _playerViewController == nil && _controls { self.usePlayerViewController() } @@ -638,7 +638,7 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH if let playerViewController = _playerViewController { viewController.present(playerViewController, animated:true, completion:{ - self._playerViewController?.showsPlaybackControls = true + self._playerViewController?.showsPlaybackControls = self._controls self._fullscreenPlayerPresented = fullscreen self._playerViewController?.autorotate = self._fullscreenAutorotate @@ -694,7 +694,7 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH func createPlayerViewController(player:AVPlayer, withPlayerItem playerItem:AVPlayerItem) -> RCTVideoPlayerViewController { let viewController = RCTVideoPlayerViewController() - viewController.showsPlaybackControls = true + viewController.showsPlaybackControls = self._controls viewController.rctDelegate = self viewController.preferredOrientation = _fullscreenOrientation @@ -836,12 +836,6 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH // MARK: - React View Management func insertReactSubview(view:UIView!, atIndex:Int) { - // We are early in the game and somebody wants to set a subview. - // That can only be in the context of playerViewController. - if !_controls && (_playerLayer == nil) && (_playerViewController == nil) { - setControls(true) - } - if _controls { view.frame = self.bounds _playerViewController?.contentOverlayView?.insertSubview(view, at:atIndex) From 0f6b5953fb0003e4dfe0693d7a88ba1ac9aa3848 Mon Sep 17 00:00:00 2001 From: olivier Date: Sat, 28 Jan 2023 14:44:59 +0100 Subject: [PATCH 007/249] chore: add readme line --- CHANGELOG.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 48703525..71b54f1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,12 +2,13 @@ ### Version 6.0.0-alpha.5 - - Android: add new events on tracks changed to be notified of audio/text/video Tracks update during playback [2806](https://github.com/react-native-video/react-native-video/pull/2806) - - iOS: app crashes on call to presentFullScreenPlayer [#2808](https://github.com/react-native-video/react-native-video/pull/2971) - - Android: Fix publicated progress handler causing duplicated progress event [#2972](https://github.com/react-native-video/react-native-video/pull/2972) - - Android: Fix audio/Subtitle tracks selection [#2979](https://github.com/react-native-video/react-native-video/pull/2979) - - Feature: Add VAST support for AVOD [#2923](https://github.com/react-native-video/react-native-video/pull/2923) - - Sample: Upgrade react-native version of basic sample [#2960](https://github.com/react-native-video/react-native-video/pull/2960) +- iOS: ensure controls are not displayed when disabled by user [#3017](https://github.com/react-native-video/react-native-video/pull/3017) +- iOS: app crashes on call to presentFullScreenPlayer [#2808](https://github.com/react-native-video/react-native-video/pull/2971) +- Android: Fix publicated progress handler causing duplicated progress event [#2972](https://github.com/react-native-video/react-native-video/pull/2972) +- Android: Fix audio/Subtitle tracks selection [#2979](https://github.com/react-native-video/react-native-video/pull/2979) +- Android: add new events on tracks changed to be notified of audio/text/video Tracks update during playback [2806](https://github.com/react-native-video/react-native-video/pull/2806) +- Feature: Add VAST support for AVOD [#2923](https://github.com/react-native-video/react-native-video/pull/2923) +- Sample: Upgrade react-native version of basic sample [#2960](https://github.com/react-native-video/react-native-video/pull/2960) ### Version 6.0.0-alpha.4 From d825d0e6bc7db24fd88d3821641083220e181d49 Mon Sep 17 00:00:00 2001 From: olivier Date: Sat, 28 Jan 2023 14:29:00 +0100 Subject: [PATCH 008/249] chore: review logs & add RNV prefix for easier filtering --- ios/Video/Features/RCTResourceLoaderDelegate.swift | 2 +- ios/Video/RCTVideo.swift | 2 +- ios/Video/RCTVideoSwiftLog/RCTVideoSwiftLog.swift | 14 ++++++++------ ios/VideoCaching/RCTVideoCache.m | 6 +++--- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/ios/Video/Features/RCTResourceLoaderDelegate.swift b/ios/Video/Features/RCTResourceLoaderDelegate.swift index e1f11528..52228517 100644 --- a/ios/Video/Features/RCTResourceLoaderDelegate.swift +++ b/ios/Video/Features/RCTResourceLoaderDelegate.swift @@ -44,7 +44,7 @@ class RCTResourceLoaderDelegate: NSObject, AVAssetResourceLoaderDelegate, URLSes } func resourceLoader(_ resourceLoader:AVAssetResourceLoader, didCancel loadingRequest:AVAssetResourceLoadingRequest) { - NSLog("didCancelLoadingRequest") + RCTLog("didCancelLoadingRequest") } func setLicenseResult(_ license:String!) { diff --git a/ios/Video/RCTVideo.swift b/ios/Video/RCTVideo.swift index baca4d1a..17a899b7 100644 --- a/ios/Video/RCTVideo.swift +++ b/ios/Video/RCTVideo.swift @@ -1080,7 +1080,7 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH let newRect = change.newValue if !oldRect!.equalTo(newRect!) { if newRect!.equalTo(UIScreen.main.bounds) { - NSLog("in fullscreen") + RCTLog("in fullscreen") self.reactViewController().view.frame = UIScreen.main.bounds self.reactViewController().view.setNeedsLayout() diff --git a/ios/Video/RCTVideoSwiftLog/RCTVideoSwiftLog.swift b/ios/Video/RCTVideoSwiftLog/RCTVideoSwiftLog.swift index 99ca8e8a..0e0e1697 100644 --- a/ios/Video/RCTVideoSwiftLog/RCTVideoSwiftLog.swift +++ b/ios/Video/RCTVideoSwiftLog/RCTVideoSwiftLog.swift @@ -25,29 +25,31 @@ * way into one or the other eventually. Feel free to reuse it as desired. */ +let logHeader: String = "RNV:" + func RCTLogError(_ message: String, _ file: String=#file, _ line: UInt=#line) { - RCTVideoSwiftLog.error(message, file: file, line: line) + RCTVideoSwiftLog.error(logHeader + message, file: file, line: line) } func RCTLogWarn(_ message: String, _ file: String=#file, _ line: UInt=#line) { - RCTVideoSwiftLog.warn(message, file: file, line: line) + RCTVideoSwiftLog.warn(logHeader + message, file: file, line: line) } func RCTLogInfo(_ message: String, _ file: String=#file, _ line: UInt=#line) { - RCTVideoSwiftLog.info(message, file: file, line: line) + RCTVideoSwiftLog.info(logHeader + message, file: file, line: line) } func RCTLog(_ message: String, _ file: String=#file, _ line: UInt=#line) { - RCTVideoSwiftLog.log(message, file: file, line: line) + RCTVideoSwiftLog.log(logHeader + message, file: file, line: line) } func RCTLogTrace(_ message: String, _ file: String=#file, _ line: UInt=#line) { - RCTVideoSwiftLog.trace(message, file: file, line: line) + RCTVideoSwiftLog.trace(logHeader + message, file: file, line: line) } func DebugLog(_ message: String) { #if DEBUG - print(message) + print(logHeader + message) #endif } diff --git a/ios/VideoCaching/RCTVideoCache.m b/ios/VideoCaching/RCTVideoCache.m index 1a2b83a5..05ad65d4 100644 --- a/ios/VideoCaching/RCTVideoCache.m +++ b/ios/VideoCaching/RCTVideoCache.m @@ -30,7 +30,7 @@ - (id)init { options.useDirectorySeparation = NO; #ifdef DEBUG options.debugOutput = ^(NSString *string) { - NSLog(@"Video Cache: %@", string); + RCTLog(@"Video Cache: %@", string); }; #endif [self createTemporaryPath]; @@ -48,7 +48,7 @@ - (void) createTemporaryPath { error:&error]; #ifdef DEBUG if (!success || error) { - NSLog(@"Error while! %@", error); + RCTLog(@"Error while! %@", error); } #endif } @@ -64,7 +64,7 @@ - (void)storeItem:(NSData *)data forUri:(NSString *)uri withCallback:(void(^)(BO [self.videoCache storeData:data forKey:key locked:NO withCallback:^(SPTPersistentCacheResponse * _Nonnull response) { if (response.error) { #ifdef DEBUG - NSLog(@"An error occured while saving the video into the cache: %@", [response.error localizedDescription]); + RCTLog(@"An error occured while saving the video into the cache: %@", [response.error localizedDescription]); #endif handler(NO); return; From 0e0ff18b279f7f0646129adab182435794d8cd4b Mon Sep 17 00:00:00 2001 From: olivier Date: Sat, 28 Jan 2023 14:54:01 +0100 Subject: [PATCH 009/249] fix(ios): ensure audio and subtitle tracks are well reported --- ios/Video/Features/RCTVideoUtils.swift | 11 +++++++++-- ios/Video/RCTVideo.swift | 6 ++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/ios/Video/Features/RCTVideoUtils.swift b/ios/Video/Features/RCTVideoUtils.swift index 50df8e3f..3953c9d0 100644 --- a/ios/Video/Features/RCTVideoUtils.swift +++ b/ios/Video/Features/RCTVideoUtils.swift @@ -103,10 +103,14 @@ enum RCTVideoUtils { title = value as! String } let language:String! = currentOption?.extendedLanguageTag ?? "" + + let selectedOption: AVMediaSelectionOption? = player.currentItem?.currentMediaSelection.selectedMediaOption(in: group!) + let audioTrack = [ "index": NSNumber(value: i), "title": title, - "language": language + "language": language ?? "", + "selected": currentOption?.displayName == selectedOption?.displayName ] as [String : Any] audioTracks.add(audioTrack) } @@ -129,10 +133,13 @@ enum RCTVideoUtils { title = value as! String } let language:String! = currentOption?.extendedLanguageTag ?? "" + let selectedOpt = player.currentItem?.currentMediaSelection + let selectedOption: AVMediaSelectionOption? = player.currentItem?.currentMediaSelection.selectedMediaOption(in: group!) let textTrack = TextTrack([ "index": NSNumber(value: i), "title": title, - "language": language + "language": language, + "selected": currentOption?.displayName == selectedOption?.displayName ]) textTracks.append(textTrack) } diff --git a/ios/Video/RCTVideo.swift b/ios/Video/RCTVideo.swift index baca4d1a..ce9c99ac 100644 --- a/ios/Video/RCTVideo.swift +++ b/ios/Video/RCTVideo.swift @@ -1007,6 +1007,8 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH } if _videoLoadStarted { + let audioTracks = RCTVideoUtils.getAudioTrackInfo(_player) + let textTracks = RCTVideoUtils.getTextTrackInfo(_player).map(\.json) onVideoLoad?(["duration": NSNumber(value: duration), "currentTime": NSNumber(value: Float(CMTimeGetSeconds(_playerItem.currentTime()))), "canPlayReverse": NSNumber(value: _playerItem.canPlayReverse), @@ -1020,8 +1022,8 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH "height": width != nil ? NSNumber(value: height!) : "undefinded", "orientation": orientation ], - "audioTracks": RCTVideoUtils.getAudioTrackInfo(_player), - "textTracks": _textTracks ?? RCTVideoUtils.getTextTrackInfo(_player), + "audioTracks": audioTracks, + "textTracks": textTracks, "target": reactTag as Any]) } _videoLoadStarted = false From 7da44c238f2d385d57b2d87dd31150bb93011fd4 Mon Sep 17 00:00:00 2001 From: Yavor Ivanov Date: Mon, 6 Feb 2023 21:43:14 +0200 Subject: [PATCH 010/249] iOS playback range --- Video.js | 2 ++ ios/Video/DataStructures/VideoSource.swift | 6 ++++++ ios/Video/RCTVideo.swift | 10 ++++++++++ 3 files changed, 18 insertions(+) diff --git a/Video.js b/Video.js index ac6bc085..87aa103a 100644 --- a/Video.js +++ b/Video.js @@ -342,6 +342,8 @@ export default class Video extends Component { mainVer: source.mainVer || 0, patchVer: source.patchVer || 0, requestHeaders: source.headers ? this.stringsOnlyObject(source.headers) : {}, + startTime: source.startTime, + endTime: source.endTime }, onVideoLoadStart: this._onLoadStart, onVideoPlaybackStateChanged: this._onPlaybackStateChanged, diff --git a/ios/Video/DataStructures/VideoSource.swift b/ios/Video/DataStructures/VideoSource.swift index 20ab7158..7cb7c38b 100644 --- a/ios/Video/DataStructures/VideoSource.swift +++ b/ios/Video/DataStructures/VideoSource.swift @@ -6,6 +6,8 @@ struct VideoSource { let isAsset: Bool let shouldCache: Bool let requestHeaders: Dictionary? + let startTime: Int64? + let endTime: Int64? let json: NSDictionary? @@ -18,6 +20,8 @@ struct VideoSource { self.isAsset = false self.shouldCache = false self.requestHeaders = nil + self.startTime = nil + self.endTime = nil return } self.json = json @@ -27,5 +31,7 @@ struct VideoSource { self.isAsset = json["isAsset"] as? Bool ?? false self.shouldCache = json["shouldCache"] as? Bool ?? false self.requestHeaders = json["requestHeaders"] as? Dictionary + self.startTime = json["startTime"] as? Int64 + self.endTime = json["endTime"] as? Int64 } } diff --git a/ios/Video/RCTVideo.swift b/ios/Video/RCTVideo.swift index baca4d1a..89974948 100644 --- a/ios/Video/RCTVideo.swift +++ b/ios/Video/RCTVideo.swift @@ -292,6 +292,7 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH self._playerItem = playerItem self._playerObserver.playerItem = self._playerItem self.setPreferredForwardBufferDuration(self._preferredForwardBufferDuration) + self.setPlaybackRange(playerItem, withVideoStart: self._source?.startTime, withVideoEnd: self._source?.endTime) self.setFilter(self._filterName) if let maxBitRate = self._maxBitRate { self._playerItem?.preferredPeakBitRate = Double(maxBitRate) @@ -533,6 +534,15 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH // Fallback on earlier versions } } + + func setPlaybackRange(_ item:AVPlayerItem!, withVideoStart videoStart:Int64?, withVideoEnd videoEnd:Int64?) { + if (videoStart != nil) { + item.reversePlaybackEndTime = CMTimeMake(value: videoStart!, timescale: 1000) + } + if (videoEnd != nil) { + item.forwardPlaybackEndTime = CMTimeMake(value: videoEnd!, timescale: 1000) + } + } func applyModifiers() { From 0f6057bea5708246d578e13cc992f19ddb53d8b0 Mon Sep 17 00:00:00 2001 From: Yavor Ivanov Date: Mon, 6 Feb 2023 23:31:14 +0200 Subject: [PATCH 011/249] Seeking to start time --- ios/Video/RCTVideo.swift | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ios/Video/RCTVideo.swift b/ios/Video/RCTVideo.swift index 89974948..7427d51e 100644 --- a/ios/Video/RCTVideo.swift +++ b/ios/Video/RCTVideo.swift @@ -537,7 +537,10 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH func setPlaybackRange(_ item:AVPlayerItem!, withVideoStart videoStart:Int64?, withVideoEnd videoEnd:Int64?) { if (videoStart != nil) { - item.reversePlaybackEndTime = CMTimeMake(value: videoStart!, timescale: 1000) + let start = CMTimeMake(value: videoStart!, timescale: 1000) + item.reversePlaybackEndTime = start + _pendingSeekTime = Float(CMTimeGetSeconds(start)) + _pendingSeek = true } if (videoEnd != nil) { item.forwardPlaybackEndTime = CMTimeMake(value: videoEnd!, timescale: 1000) From fe5fc543b948a519b143f4fd82c0a371a2b331cb Mon Sep 17 00:00:00 2001 From: Yavor Ivanov Date: Tue, 7 Feb 2023 22:50:54 +0200 Subject: [PATCH 012/249] Corrected currentTime & playableDuration when using start & end time --- ios/Video/Features/RCTVideoUtils.swift | 10 +++++++++- ios/Video/RCTVideo.swift | 7 +++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/ios/Video/Features/RCTVideoUtils.swift b/ios/Video/Features/RCTVideoUtils.swift index 50df8e3f..5ff5928c 100644 --- a/ios/Video/Features/RCTVideoUtils.swift +++ b/ios/Video/Features/RCTVideoUtils.swift @@ -12,13 +12,17 @@ enum RCTVideoUtils { * * \returns The playable duration of the current player item in seconds. */ - static func calculatePlayableDuration(_ player:AVPlayer?) -> NSNumber { + static func calculatePlayableDuration(_ player:AVPlayer?, withSource source:VideoSource?) -> NSNumber { guard let player = player, let video:AVPlayerItem = player.currentItem, video.status == AVPlayerItem.Status.readyToPlay else { return 0 } + if (source?.startTime != nil && source?.endTime != nil) { + return NSNumber(value: (Float64(source?.endTime ?? 0) - Float64(source?.startTime ?? 0)) / 1000) + } + var effectiveTimeRange:CMTimeRange? for (_, value) in video.loadedTimeRanges.enumerated() { let timeRange:CMTimeRange = value.timeRangeValue @@ -31,6 +35,10 @@ enum RCTVideoUtils { if let effectiveTimeRange = effectiveTimeRange { let playableDuration:Float64 = CMTimeGetSeconds(CMTimeRangeGetEnd(effectiveTimeRange)) if playableDuration > 0 { + if (source?.startTime != nil) { + return NSNumber(value: (playableDuration - Float64(source?.startTime ?? 0) / 1000)) + } + return playableDuration as NSNumber } } diff --git a/ios/Video/RCTVideo.swift b/ios/Video/RCTVideo.swift index 7427d51e..03811242 100644 --- a/ios/Video/RCTVideo.swift +++ b/ios/Video/RCTVideo.swift @@ -207,7 +207,10 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH return } - let currentTime = _player?.currentTime() + var currentTime = _player?.currentTime() + if (currentTime != nil && _source?.startTime != nil) { + currentTime = CMTimeSubtract(currentTime!, CMTimeMake(value: _source?.startTime ?? 0, timescale: 1000)) + } let currentPlaybackTime = _player?.currentItem?.currentDate() let duration = CMTimeGetSeconds(playerDuration) let currentTimeSecs = CMTimeGetSeconds(currentTime ?? .zero) @@ -223,7 +226,7 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH } onVideoProgress?([ "currentTime": NSNumber(value: Float(currentTimeSecs)), - "playableDuration": RCTVideoUtils.calculatePlayableDuration(_player), + "playableDuration": RCTVideoUtils.calculatePlayableDuration(_player, withSource: _source), "atValue": NSNumber(value: currentTime?.value ?? .zero), "currentPlaybackTime": NSNumber(value: NSNumber(value: floor(currentPlaybackTime?.timeIntervalSince1970 ?? 0 * 1000)).int64Value), "target": reactTag, From 57b4a767759e665ed2e24c3fbbdf073da9c83b65 Mon Sep 17 00:00:00 2001 From: olivier Date: Tue, 7 Feb 2023 23:14:50 +0100 Subject: [PATCH 013/249] fix: add stub for IMA and option to enable it on demand --- android/build.gradle | 29 ++++++++- .../ads/interactivemedia/v3/api/AdEvent.java | 11 ++++ .../exoplayer2/ext/ima/ImaAdsLoader.java | 65 +++++++++++++++++++ examples/basic/android/build.gradle | 2 + 4 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 android/src/main/java/com/google/ads/interactivemedia/v3/api/AdEvent.java create mode 100644 android/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoader.java diff --git a/android/build.gradle b/android/build.gradle index fdd257bf..a2ade2e6 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -4,6 +4,18 @@ def safeExtGet(prop, fallback) { rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback } +def useExoplayerIMA = safeExtGet("RNVUseExoplayerIMA", false) + +println "useExoplayerIMA:" + useExoplayerIMA + +// This string is used to define build path. +// As react native build output directory is react-native path of the module. +// We need to force a new path on each configuration change. +// If you add a new build parameter, please add the new value in this string +def configStringPath = ( + 'useExoplayerIMA' + useExoplayerIMA \ +).md5() + android { compileSdkVersion safeExtGet('compileSdkVersion', 31) buildToolsVersion safeExtGet('buildToolsVersion', '30.0.2') @@ -24,6 +36,19 @@ android { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } + + buildDir 'buildOutput_' + configStringPath + + sourceSets { + main { + java { + if (useExoplayerIMA) { + exclude 'com/google/ads/interactivemedia/v3/api' + exclude 'com/android/exoplayer2/ext/ima' + } + } + } + } } repositories { @@ -45,7 +70,9 @@ dependencies { implementation('com.google.android.exoplayer:extension-okhttp:2.18.1') { exclude group: 'com.squareup.okhttp3', module: 'okhttp' } - implementation 'com.google.android.exoplayer:extension-ima:2.18.1' + if (useExoplayerIMA) { + implementation 'com.google.android.exoplayer:extension-ima:2.18.1' + } implementation "com.squareup.okhttp3:okhttp:" + '$OKHTTP_VERSION' } diff --git a/android/src/main/java/com/google/ads/interactivemedia/v3/api/AdEvent.java b/android/src/main/java/com/google/ads/interactivemedia/v3/api/AdEvent.java new file mode 100644 index 00000000..1913df29 --- /dev/null +++ b/android/src/main/java/com/google/ads/interactivemedia/v3/api/AdEvent.java @@ -0,0 +1,11 @@ +package com.google.ads.interactivemedia.v3.api; + +import androidx.annotation.InspectableProperty; + +public abstract class AdEvent { + public abstract InspectableProperty getType(); + + public interface AdEventListener { + public void onAdEvent(AdEvent adEvent); + } +} diff --git a/android/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoader.java b/android/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoader.java new file mode 100644 index 00000000..d23e7dd6 --- /dev/null +++ b/android/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoader.java @@ -0,0 +1,65 @@ +package com.google.android.exoplayer2.ext.ima; + +import androidx.annotation.Nullable; + +import com.facebook.react.uimanager.ThemedReactContext; +import com.google.android.exoplayer2.ExoPlayer; +import com.google.android.exoplayer2.Player; +import com.google.android.exoplayer2.source.ads.AdsLoader; +import com.google.android.exoplayer2.source.ads.AdsMediaSource; +import com.google.android.exoplayer2.ui.AdViewProvider; +import com.google.android.exoplayer2.upstream.DataSpec; + +import java.io.IOException; + +public class ImaAdsLoader implements AdsLoader { + public void setPlayer(ExoPlayer player) { + } + + @Override + public void setPlayer(@Nullable Player player) { + + } + + public void release() { + } + + @Override + public void setSupportedContentTypes(int... ints) { + + } + + @Override + public void start(AdsMediaSource adsMediaSource, DataSpec dataSpec, Object o, AdViewProvider adViewProvider, EventListener eventListener) { + + } + + @Override + public void stop(AdsMediaSource adsMediaSource, EventListener eventListener) { + + } + + @Override + public void handlePrepareComplete(AdsMediaSource adsMediaSource, int i, int i1) { + + } + + @Override + public void handlePrepareError(AdsMediaSource adsMediaSource, int i, int i1, IOException e) { + + } + + public static class Builder { + public Builder(ThemedReactContext themedReactContext) { + + } + + public Builder setAdEventListener(Object reactExoplayerView) { + return this; + } + + public ImaAdsLoader build() { + return null; + } + } +} diff --git a/examples/basic/android/build.gradle b/examples/basic/android/build.gradle index 8569fee3..533ca601 100644 --- a/examples/basic/android/build.gradle +++ b/examples/basic/android/build.gradle @@ -14,6 +14,8 @@ buildscript { // Otherwise we default to the side-by-side NDK version from AGP. ndkVersion = "21.4.7075529" } + + RNVUseExoplayerIMA = true } repositories { google() From a3b4b94f71f7bb6c2a4512688473a644b9abd7fc Mon Sep 17 00:00:00 2001 From: olivier Date: Tue, 7 Feb 2023 23:15:05 +0100 Subject: [PATCH 014/249] chore: fix build of andrpoid sample --- examples/basic/metro.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/basic/metro.config.js b/examples/basic/metro.config.js index bede29ff..fa377047 100644 --- a/examples/basic/metro.config.js +++ b/examples/basic/metro.config.js @@ -5,7 +5,7 @@ * @format */ const path = require('path'); -const blacklist = require('metro-config/src/defaults/blacklist'); +const blacklist = require('metro-config/src/defaults/exclusionList'); module.exports = { resolver: { From 6ca0ab3834c947ddfc0c958cee6c29f4fed603be Mon Sep 17 00:00:00 2001 From: Radin Gospodinov Date: Thu, 9 Feb 2023 09:38:05 +0200 Subject: [PATCH 015/249] Android range playback. --- .../exoplayer/ReactExoplayerView.java | 50 +++++++++++++++---- .../exoplayer/ReactExoplayerViewManager.java | 9 +++- 2 files changed, 48 insertions(+), 11 deletions(-) diff --git a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 0c0fbc17..0acc0d00 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -93,6 +93,7 @@ import com.google.android.exoplayer2.ext.ima.ImaAdsLoader; import com.google.android.exoplayer2.source.ads.AdsMediaSource; import com.google.android.exoplayer2.source.DefaultMediaSourceFactory; +import com.google.android.exoplayer2.source.ClippingMediaSource; import com.google.common.collect.ImmutableList; import java.net.CookieHandler; @@ -181,6 +182,10 @@ class ReactExoplayerView extends FrameLayout implements // Props from React private int backBufferDurationMs = DefaultLoadControl.DEFAULT_BACK_BUFFER_DURATION_MS; private Uri srcUri; + + private long startTimeMs = -1; + + private long endTimeMs = -1; private String extension; private boolean repeat; private String audioTrackType; @@ -669,7 +674,7 @@ private DrmSessionManager initializePlayerDrm(ReactExoplayerView self) { private void initializePlayerSource(ReactExoplayerView self, DrmSessionManager drmSessionManager) { ArrayList mediaSourceList = buildTextSources(); - MediaSource videoSource = buildMediaSource(self.srcUri, self.extension, drmSessionManager); + MediaSource videoSource = buildMediaSource(self.srcUri, self.extension, drmSessionManager, startTimeMs, endTimeMs); MediaSource mediaSourceWithAds = null; if (adTagUrl != null) { MediaSource.Factory mediaSourceFactory = new DefaultMediaSourceFactory(mediaDataSourceFactory) @@ -764,7 +769,13 @@ private DrmSessionManager buildDrmSessionManager(UUID uuid, String licenseUrl, S } } - private MediaSource buildMediaSource(Uri uri, String overrideExtension, DrmSessionManager drmSessionManager) { + private MediaSource buildMediaSource( + Uri uri, + String overrideExtension, + DrmSessionManager drmSessionManager, + long startTimeMs, + long endTimeMs) { + if (uri == null) { throw new IllegalStateException("Invalid video uri"); } @@ -781,7 +792,7 @@ private MediaSource buildMediaSource(Uri uri, String overrideExtension, DrmSessi } MediaItem mediaItem = mediaItemBuilder.build(); - + MediaSource mediaSource = null; DrmSessionManagerProvider drmProvider = null; if (drmSessionManager != null) { drmProvider = new DrmSessionManagerProvider() { @@ -795,39 +806,50 @@ public DrmSessionManager get(MediaItem mediaItem) { } switch (type) { case CONTENT_TYPE_SS: - return new SsMediaSource.Factory( + mediaSource = SsMediaSource.Factory( new DefaultSsChunkSource.Factory(mediaDataSourceFactory), buildDataSourceFactory(false) ).setDrmSessionManagerProvider(drmProvider) .setLoadErrorHandlingPolicy( config.buildLoadErrorHandlingPolicy(minLoadRetryCount) ).createMediaSource(mediaItem); + break; case CONTENT_TYPE_DASH: - return new DashMediaSource.Factory( + mediaSource = DashMediaSource.Factory( new DefaultDashChunkSource.Factory(mediaDataSourceFactory), buildDataSourceFactory(false) ).setDrmSessionManagerProvider(drmProvider) .setLoadErrorHandlingPolicy( config.buildLoadErrorHandlingPolicy(minLoadRetryCount) ).createMediaSource(mediaItem); + break; case CONTENT_TYPE_HLS: - return new HlsMediaSource.Factory( + mediaSource = HlsMediaSource.Factory( mediaDataSourceFactory ).setDrmSessionManagerProvider(drmProvider) .setLoadErrorHandlingPolicy( config.buildLoadErrorHandlingPolicy(minLoadRetryCount) ).createMediaSource(mediaItem); + break; case CONTENT_TYPE_OTHER: - return new ProgressiveMediaSource.Factory( + mediaSource = ProgressiveMediaSource.Factory( mediaDataSourceFactory ).setDrmSessionManagerProvider(drmProvider) .setLoadErrorHandlingPolicy( config.buildLoadErrorHandlingPolicy(minLoadRetryCount) ).createMediaSource(mediaItem); + break; default: { throw new IllegalStateException("Unsupported type: " + type); } } + + if(startTimeMs >= 0 && endTimeMs >= 0) + { + return new ClippingMediaSource(mediaSource, startTimeMs * 1000, endTimeMs * 1000); + } + + return mediaSource; } private ArrayList buildTextSources() { @@ -1465,11 +1487,19 @@ public void onMetadata(Metadata metadata) { // ReactExoplayerViewManager public api - public void setSrc(final Uri uri, final String extension, Map headers) { + public void setSrc( + final Uri uri, + final long startTimeMs, + final long endTimeMs, + final String extension, + Map headers) { + if (uri != null) { - boolean isSourceEqual = uri.equals(srcUri); + boolean isSourceEqual = uri.equals(srcUri) && startTimeMs == this.startTimeMs && endTimeMs == this.endTimeMs; hasDrmFailed = false; this.srcUri = uri; + this.startTimeMs = startTimeMs; + this.endTimeMs = endTimeMs; this.extension = extension; this.requestHeaders = headers; this.mediaDataSourceFactory = @@ -1487,6 +1517,8 @@ public void clearSrc() { player.stop(); player.clearMediaItems(); this.srcUri = null; + this.startTimeMs = -1; + this.endTimeMs = -1; this.extension = null; this.requestHeaders = null; this.mediaDataSourceFactory = null; diff --git a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java index 8da025e7..6f2b1296 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java +++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java @@ -28,9 +28,12 @@ public class ReactExoplayerViewManager extends ViewGroupManager { private static final String REACT_CLASS = "RCTVideo"; - private static final String PROP_SRC = "src"; private static final String PROP_SRC_URI = "uri"; + + private static final String PROP_SRC_START_TIME = "startTime"; + + private static final String PROP_SRC_END_TIME = "endTime"; private static final String PROP_AD_TAG_URL = "adTagUrl"; private static final String PROP_SRC_TYPE = "type"; private static final String PROP_DRM = "drm"; @@ -152,6 +155,8 @@ public void setDRM(final ReactExoplayerView videoView, @Nullable ReadableMap drm public void setSrc(final ReactExoplayerView videoView, @Nullable ReadableMap src) { Context context = videoView.getContext().getApplicationContext(); String uriString = src.hasKey(PROP_SRC_URI) ? src.getString(PROP_SRC_URI) : null; + int startTimeMs = src.hasKey(PROP_SRC_START_TIME) ? src.getInt(PROP_SRC_START_TIME) : -1; + int endTimeMs = src.hasKey(PROP_SRC_END_TIME) ? src.getInt(PROP_SRC_END_TIME) : -1; String extension = src.hasKey(PROP_SRC_TYPE) ? src.getString(PROP_SRC_TYPE) : null; Map headers = src.hasKey(PROP_SRC_HEADERS) ? toStringMap(src.getMap(PROP_SRC_HEADERS)) : null; @@ -164,7 +169,7 @@ public void setSrc(final ReactExoplayerView videoView, @Nullable ReadableMap src Uri srcUri = Uri.parse(uriString); if (srcUri != null) { - videoView.setSrc(srcUri, extension, headers); + videoView.setSrc(srcUri, startTimeMs, endTimeMs, extension, headers); } } else { int identifier = context.getResources().getIdentifier( From a56b4d148c2f06e7dc8a3f7c047a43dca8da57f5 Mon Sep 17 00:00:00 2001 From: Yavor Ivanov Date: Thu, 9 Feb 2023 13:53:06 +0200 Subject: [PATCH 016/249] Fixed android build error --- .../java/com/brentvatne/exoplayer/ReactExoplayerView.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 0acc0d00..00983c4e 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -806,7 +806,7 @@ public DrmSessionManager get(MediaItem mediaItem) { } switch (type) { case CONTENT_TYPE_SS: - mediaSource = SsMediaSource.Factory( + mediaSource = new SsMediaSource.Factory( new DefaultSsChunkSource.Factory(mediaDataSourceFactory), buildDataSourceFactory(false) ).setDrmSessionManagerProvider(drmProvider) @@ -815,7 +815,7 @@ public DrmSessionManager get(MediaItem mediaItem) { ).createMediaSource(mediaItem); break; case CONTENT_TYPE_DASH: - mediaSource = DashMediaSource.Factory( + mediaSource = new DashMediaSource.Factory( new DefaultDashChunkSource.Factory(mediaDataSourceFactory), buildDataSourceFactory(false) ).setDrmSessionManagerProvider(drmProvider) @@ -824,7 +824,7 @@ public DrmSessionManager get(MediaItem mediaItem) { ).createMediaSource(mediaItem); break; case CONTENT_TYPE_HLS: - mediaSource = HlsMediaSource.Factory( + mediaSource = new HlsMediaSource.Factory( mediaDataSourceFactory ).setDrmSessionManagerProvider(drmProvider) .setLoadErrorHandlingPolicy( @@ -832,7 +832,7 @@ public DrmSessionManager get(MediaItem mediaItem) { ).createMediaSource(mediaItem); break; case CONTENT_TYPE_OTHER: - mediaSource = ProgressiveMediaSource.Factory( + mediaSource = new ProgressiveMediaSource.Factory( mediaDataSourceFactory ).setDrmSessionManagerProvider(drmProvider) .setLoadErrorHandlingPolicy( From 3c2009b2c72a836b0814823d53335f35534d857a Mon Sep 17 00:00:00 2001 From: olivier Date: Thu, 9 Feb 2023 21:52:54 +0100 Subject: [PATCH 017/249] doc(android): document IMA ads insertion activation --- API.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/API.md b/API.md index 8b6878d1..a769aeba 100644 --- a/API.md +++ b/API.md @@ -164,6 +164,21 @@ protected List getPackages() { ); } ``` + +#### Enable custom feature in gradle file + +##### Enable client side ads insertion +To enable client side ads insertion CSAI with google IMA SDK, you need to enable it in your gradle file. + +```gradle +buildscript { + ext { + ... + RNVUseExoplayerIMA = true + ... + } +} +``` ### Windows installation @@ -370,6 +385,9 @@ Example: adTagUrl="https://pubads.g.doubleclick.net/gampad/ads?iu=/21775744923/external/vmap_ad_samples&sz=640x480&cust_params=sample_ar%3Dpremidpostoptimizedpodbumper&ciu_szs=300x250&gdfp_req=1&ad_rule=1&output=vmap&unviewed_position_start=1&env=vp&impl=s&cmsid=496&vid=short_onecue&correlator=" ``` +Note: On android, you need enable IMA SDK in gradle file, see: [enableclient side ads insertion](#enable-client-side-ads-insertion) + + Platforms: Android, iOS #### allowsExternalPlayback From 7aeaa2a3d1d2f4492de8530d94a447ae699a3e8e Mon Sep 17 00:00:00 2001 From: olivier Date: Thu, 9 Feb 2023 23:20:10 +0100 Subject: [PATCH 018/249] chore(android): fix path to disable CSAI --- android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/build.gradle b/android/build.gradle index a2ade2e6..0c3969c9 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -44,7 +44,7 @@ android { java { if (useExoplayerIMA) { exclude 'com/google/ads/interactivemedia/v3/api' - exclude 'com/android/exoplayer2/ext/ima' + exclude 'com/google/android/exoplayer2/ext/ima' } } } From 437a043427bc502a2ecde379a47536ba64e530b1 Mon Sep 17 00:00:00 2001 From: olivier Date: Sun, 12 Feb 2023 21:21:32 +0100 Subject: [PATCH 019/249] 6.0.0-alpha.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bde0ebf5..aa671cd9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-video", - "version": "6.0.0-alpha.4", + "version": "6.0.0-alpha.5", "description": "A