Merge branch 'master' into AndroidRangePlayback

This commit is contained in:
Olivier Bouillet 2023-04-05 22:49:22 +02:00 committed by GitHub
commit afcde3e335
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 65 additions and 32 deletions

13
API.md
View File

@ -74,6 +74,17 @@ Video with caching ([more info](docs/caching.md)):
end end
``` ```
#### Enable custom feature in podfile file
##### Google IMA
Google IMA is the google SDK to support Client Side Ads Integration (CSAI), see [google documentation](https://developers.google.com/interactive-media-ads/docs/sdks/ios/client-side) for more informations.
To enable google IMA usage define add following line in your podfile:
```podfile
$RNVideoUseGoogleIMA=true
```
</details> </details>
### tvOS installation ### tvOS installation
@ -935,7 +946,7 @@ Platforms: iOS, Android
Property | Description | Platforms Property | Description | Platforms
--- | --- | --- --- | --- | ---
fontSizeTrack | Adjust the font size of the subtitles. Default: font size of the device | Android fontSize | Adjust the font size of the subtitles. Default: font size of the device | Android
paddingTop | Adjust the top padding of the subtitles. Default: 0| Android paddingTop | Adjust the top padding of the subtitles. Default: 0| Android
paddingBottom | Adjust the bottom padding of the subtitles. Default: 0| Android paddingBottom | Adjust the bottom padding of the subtitles. Default: 0| Android
paddingLeft | Adjust the left padding of the subtitles. Default: 0| Android paddingLeft | Adjust the left padding of the subtitles. Default: 0| Android

View File

@ -2,6 +2,9 @@
### Version 6.0.0-alpha.6 ### Version 6.0.0-alpha.6
- Feature: Video range support [#3030](https://github.com/react-native-video/react-native-video/pull/3030) - Feature: Video range support [#3030](https://github.com/react-native-video/react-native-video/pull/3030)
- iOS: remove undocumented `currentTime` property [#3064](https://github.com/react-native-video/react-native-video/pull/3064)
- iOS: make sure that the audio in ads is muted when the player is muted. [#3068](https://github.com/react-native-video/react-native-video/pull/3077)
- iOS: make IMA build optionnal
### Version 6.0.0-alpha.5 ### Version 6.0.0-alpha.5

View File

@ -522,7 +522,6 @@ Video.propTypes = {
disableBuffering: PropTypes.bool, disableBuffering: PropTypes.bool,
controls: PropTypes.bool, controls: PropTypes.bool,
audioOnly: PropTypes.bool, audioOnly: PropTypes.bool,
currentTime: PropTypes.number,
fullscreenAutorotate: PropTypes.bool, fullscreenAutorotate: PropTypes.bool,
fullscreenOrientation: PropTypes.oneOf(['all', 'landscape', 'portrait']), fullscreenOrientation: PropTypes.oneOf(['all', 'landscape', 'portrait']),
progressUpdateInterval: PropTypes.number, progressUpdateInterval: PropTypes.number,
@ -559,17 +558,11 @@ Video.propTypes = {
onAudioFocusChanged: PropTypes.func, onAudioFocusChanged: PropTypes.func,
onAudioBecomingNoisy: PropTypes.func, onAudioBecomingNoisy: PropTypes.func,
onPictureInPictureStatusChanged: PropTypes.func, onPictureInPictureStatusChanged: PropTypes.func,
needsToRestoreUserInterfaceForPictureInPictureStop: PropTypes.func,
onExternalPlaybackChange: PropTypes.func, onExternalPlaybackChange: PropTypes.func,
adTagUrl: PropTypes.string, adTagUrl: PropTypes.string,
onReceiveAdEvent: PropTypes.func, onReceiveAdEvent: PropTypes.func,
/* Required by react-native */ /* Required by react-native */
scaleX: PropTypes.number,
scaleY: PropTypes.number,
translateX: PropTypes.number,
translateY: PropTypes.number,
rotation: PropTypes.number,
...ViewPropTypes, ...ViewPropTypes,
}; };

View File

@ -7,6 +7,8 @@ install! 'cocoapods', :deterministic_uuids => false
target 'videoplayer' do target 'videoplayer' do
config = use_native_modules! config = use_native_modules!
# $RNVideoUseGoogleIMA = true
# Flags change depending on the env values. # Flags change depending on the env values.
flags = get_default_flags() flags = get_default_flags()

View File

@ -44,7 +44,7 @@ static NSString *const kRNConcurrentRoot = @"concurrentRoot";
#endif #endif
NSDictionary *initProps = [self prepareInitialProps]; NSDictionary *initProps = [self prepareInitialProps];
UIView *rootView = RCTAppSetupDefaultRootView(bridge, @"videoplayer", initProps); UIView *rootView = RCTAppSetupDefaultRootView(bridge, @"VideoPlayer", initProps);
if (@available(iOS 13.0, *)) { if (@available(iOS 13.0, *)) {
rootView.backgroundColor = [UIColor systemBackgroundColor]; rootView.backgroundColor = [UIColor systemBackgroundColor];
@ -85,7 +85,7 @@ static NSString *const kRNConcurrentRoot = @"concurrentRoot";
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{ {
#if DEBUG #if DEBUG
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"]; return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"src/index"];
#else #else
return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#endif #endif

View File

@ -1,3 +1,4 @@
#if USE_GOOGLE_IMA
import Foundation import Foundation
import GoogleInteractiveMediaAds import GoogleInteractiveMediaAds
@ -76,6 +77,10 @@ class RCTIMAAdsManager: NSObject, IMAAdsLoaderDelegate, IMAAdsManagerDelegate {
// MARK: - IMAAdsManagerDelegate // MARK: - IMAAdsManagerDelegate
func adsManager(_ adsManager: IMAAdsManager, didReceive event: IMAAdEvent) { func adsManager(_ adsManager: IMAAdsManager, didReceive event: IMAAdEvent) {
// Mute ad if the main player is muted
if (_video.isMuted()) {
adsManager.volume = 0;
}
// Play each ad once it has been loaded // Play each ad once it has been loaded
if event.type == IMAAdEventType.LOADED { if event.type == IMAAdEventType.LOADED {
adsManager.start() adsManager.start()
@ -185,3 +190,4 @@ class RCTIMAAdsManager: NSObject, IMAAdsLoaderDelegate, IMAAdsManagerDelegate {
return result; return result;
} }
} }
#endif

View File

@ -1,7 +1,9 @@
import AVFoundation import AVFoundation
import AVKit import AVKit
import Foundation import Foundation
#if USE_GOOGLE_IMA
import GoogleInteractiveMediaAds import GoogleInteractiveMediaAds
#endif
import React import React
import Promises import Promises
@ -63,11 +65,13 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
/* IMA Ads */ /* IMA Ads */
private var _adTagUrl:String? private var _adTagUrl:String?
#if USE_GOOGLE_IMA
private var _imaAdsManager: RCTIMAAdsManager! private var _imaAdsManager: RCTIMAAdsManager!
private var _didRequestAds:Bool = false
private var _adPlaying:Bool = false
/* Playhead used by the SDK to track content video progress and insert mid-rolls. */ /* Playhead used by the SDK to track content video progress and insert mid-rolls. */
private var _contentPlayhead: IMAAVPlayerContentPlayhead? private var _contentPlayhead: IMAAVPlayerContentPlayhead?
#endif
private var _didRequestAds:Bool = false
private var _adPlaying:Bool = false
private var _resouceLoaderDelegate: RCTResourceLoaderDelegate? private var _resouceLoaderDelegate: RCTResourceLoaderDelegate?
private var _playerObserver: RCTPlayerObserver = RCTPlayerObserver() private var _playerObserver: RCTPlayerObserver = RCTPlayerObserver()
@ -107,8 +111,9 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
init(eventDispatcher:RCTEventDispatcher!) { init(eventDispatcher:RCTEventDispatcher!) {
super.init(frame: CGRect(x: 0, y: 0, width: 100, height: 100)) super.init(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
#if USE_GOOGLE_IMA
_imaAdsManager = RCTIMAAdsManager(video: self) _imaAdsManager = RCTIMAAdsManager(video: self)
#endif
_eventDispatcher = eventDispatcher _eventDispatcher = eventDispatcher
@ -147,8 +152,9 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
required init?(coder aDecoder: NSCoder) { required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder) super.init(coder: aDecoder)
#if USE_GOOGLE_IMA
_imaAdsManager = RCTIMAAdsManager(video: self) _imaAdsManager = RCTIMAAdsManager(video: self)
#endif
} }
deinit { deinit {
@ -220,10 +226,12 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
]) ])
if currentTimeSecs >= 0 { if currentTimeSecs >= 0 {
#if USE_GOOGLE_IMA
if !_didRequestAds && currentTimeSecs >= 0.0001 && _adTagUrl != nil { if !_didRequestAds && currentTimeSecs >= 0.0001 && _adTagUrl != nil {
_imaAdsManager.requestAds() _imaAdsManager.requestAds()
_didRequestAds = true _didRequestAds = true
} }
#endif
onVideoProgress?([ onVideoProgress?([
"currentTime": NSNumber(value: Float(currentTimeSecs)), "currentTime": NSNumber(value: Float(currentTimeSecs)),
"playableDuration": RCTVideoUtils.calculatePlayableDuration(_player, withSource: _source), "playableDuration": RCTVideoUtils.calculatePlayableDuration(_player, withSource: _source),
@ -311,13 +319,14 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
self.setAutomaticallyWaitsToMinimizeStalling(self._automaticallyWaitsToMinimizeStalling) self.setAutomaticallyWaitsToMinimizeStalling(self._automaticallyWaitsToMinimizeStalling)
} }
#if USE_GOOGLE_IMA
if self._adTagUrl != nil { if self._adTagUrl != nil {
// Set up your content playhead and contentComplete callback. // Set up your content playhead and contentComplete callback.
self._contentPlayhead = IMAAVPlayerContentPlayhead(avPlayer: self._player!) self._contentPlayhead = IMAAVPlayerContentPlayhead(avPlayer: self._player!)
self._imaAdsManager.setUpAdsLoader() self._imaAdsManager.setUpAdsLoader()
} }
#endif
//Perform on next run loop, otherwise onVideoLoadStart is nil //Perform on next run loop, otherwise onVideoLoadStart is nil
self.onVideoLoadStart?([ self.onVideoLoadStart?([
"src": [ "src": [
@ -428,7 +437,9 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
func setPaused(_ paused:Bool) { func setPaused(_ paused:Bool) {
if paused { if paused {
if _adPlaying { if _adPlaying {
#if USE_GOOGLE_IMA
_imaAdsManager.getAdsManager()?.pause() _imaAdsManager.getAdsManager()?.pause()
#endif
} else { } else {
_player?.pause() _player?.pause()
_player?.rate = 0.0 _player?.rate = 0.0
@ -437,7 +448,9 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
RCTPlayerOperations.configureAudio(ignoreSilentSwitch:_ignoreSilentSwitch, mixWithOthers:_mixWithOthers) RCTPlayerOperations.configureAudio(ignoreSilentSwitch:_ignoreSilentSwitch, mixWithOthers:_mixWithOthers)
if _adPlaying { if _adPlaying {
#if USE_GOOGLE_IMA
_imaAdsManager.getAdsManager()?.resume() _imaAdsManager.getAdsManager()?.resume()
#endif
} else { } else {
if #available(iOS 10.0, *), !_automaticallyWaitsToMinimizeStalling { if #available(iOS 10.0, *), !_automaticallyWaitsToMinimizeStalling {
_player?.playImmediately(atRate: _rate) _player?.playImmediately(atRate: _rate)
@ -452,15 +465,6 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
_paused = paused _paused = paused
} }
@objc
func setCurrentTime(_ currentTime:Float) {
let info:NSDictionary = [
"time": NSNumber(value: currentTime),
"tolerance": NSNumber(value: 100)
]
setSeek(info)
}
@objc @objc
func setSeek(_ info:NSDictionary!) { func setSeek(_ info:NSDictionary!) {
let seekTime:NSNumber! = info["time"] as! NSNumber let seekTime:NSNumber! = info["time"] as! NSNumber
@ -500,6 +504,11 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
applyModifiers() applyModifiers()
} }
@objc
func isMuted() -> Bool {
return _muted
}
@objc @objc
func setMuted(_ muted:Bool) { func setMuted(_ muted:Bool) {
_muted = muted _muted = muted
@ -840,11 +849,11 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
func setAdTagUrl(_ adTagUrl:String!) { func setAdTagUrl(_ adTagUrl:String!) {
_adTagUrl = adTagUrl _adTagUrl = adTagUrl
} }
#if USE_GOOGLE_IMA
func getContentPlayhead() -> IMAAVPlayerContentPlayhead? { func getContentPlayhead() -> IMAAVPlayerContentPlayhead? {
return _contentPlayhead return _contentPlayhead
} }
#endif
func setAdPlaying(_ adPlaying:Bool) { func setAdPlaying(_ adPlaying:Bool) {
_adPlaying = adPlaying _adPlaying = adPlaying
} }
@ -1012,7 +1021,10 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
} }
if _pendingSeek { if _pendingSeek {
setCurrentTime(_pendingSeekTime) setSeek([
"time": NSNumber(value: _pendingSeekTime),
"tolerance": NSNumber(value: 100)
])
_pendingSeek = false _pendingSeek = false
} }
@ -1122,11 +1134,11 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
@objc func handlePlayerItemDidReachEnd(notification:NSNotification!) { @objc func handlePlayerItemDidReachEnd(notification:NSNotification!) {
onVideoEnd?(["target": reactTag as Any]) onVideoEnd?(["target": reactTag as Any])
#if USE_GOOGLE_IMA
if notification.object as? AVPlayerItem == _player?.currentItem { if notification.object as? AVPlayerItem == _player?.currentItem {
_imaAdsManager.getAdsLoader()?.contentComplete() _imaAdsManager.getAdsLoader()?.contentComplete()
} }
#endif
if _repeat { if _repeat {
let item:AVPlayerItem! = notification.object as? AVPlayerItem let item:AVPlayerItem! = notification.object as? AVPlayerItem
item.seek(to: CMTime.zero, completionHandler: nil) item.seek(to: CMTime.zero, completionHandler: nil)

View File

@ -27,7 +27,6 @@ RCT_EXPORT_VIEW_PROPERTY(ignoreSilentSwitch, NSString);
RCT_EXPORT_VIEW_PROPERTY(mixWithOthers, NSString); RCT_EXPORT_VIEW_PROPERTY(mixWithOthers, NSString);
RCT_EXPORT_VIEW_PROPERTY(rate, float); RCT_EXPORT_VIEW_PROPERTY(rate, float);
RCT_EXPORT_VIEW_PROPERTY(seek, NSDictionary); RCT_EXPORT_VIEW_PROPERTY(seek, NSDictionary);
RCT_EXPORT_VIEW_PROPERTY(currentTime, float);
RCT_EXPORT_VIEW_PROPERTY(fullscreen, BOOL); RCT_EXPORT_VIEW_PROPERTY(fullscreen, BOOL);
RCT_EXPORT_VIEW_PROPERTY(fullscreenAutorotate, BOOL); RCT_EXPORT_VIEW_PROPERTY(fullscreenAutorotate, BOOL);
RCT_EXPORT_VIEW_PROPERTY(fullscreenOrientation, NSString); RCT_EXPORT_VIEW_PROPERTY(fullscreenOrientation, NSString);

View File

@ -19,8 +19,15 @@ Pod::Spec.new do |s|
ss.source_files = "ios/Video/**/*.{h,m,swift}" ss.source_files = "ios/Video/**/*.{h,m,swift}"
ss.dependency "PromisesSwift" ss.dependency "PromisesSwift"
if defined?($RNVideoUseGoogleIMA)
Pod::UI.puts "RNVideo: enable IMA SDK"
ss.ios.dependency 'GoogleAds-IMA-iOS-SDK', '~> 3.18.1' ss.ios.dependency 'GoogleAds-IMA-iOS-SDK', '~> 3.18.1'
ss.tvos.dependency 'GoogleAds-IMA-tvOS-SDK', '~> 4.2' ss.tvos.dependency 'GoogleAds-IMA-tvOS-SDK', '~> 4.2'
ss.pod_target_xcconfig = {
'OTHER_SWIFT_FLAGS' => '$(inherited) -D USE_GOOGLE_IMA'
}
end
end end
s.subspec "VideoCaching" do |ss| s.subspec "VideoCaching" do |ss|