Merge branch 'implement-ima-sdk' into master
# Conflicts: # android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java # examples/basic/package.json # examples/basic/yarn.lock # ios/Video/RCTVideo.h # ios/Video/RCTVideo.m # ios/Video/RCTVideoManager.m # react-native-video.podspec
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
#import "RCTVideoPlayerViewControllerDelegate.h"
|
||||
#import <React/RCTComponent.h>
|
||||
#import <React/RCTBridgeModule.h>
|
||||
@import GoogleInteractiveMediaAds;
|
||||
|
||||
#if __has_include(<react-native-video/RCTVideoCache.h>)
|
||||
#import <react-native-video/RCTVideoCache.h>
|
||||
@@ -14,11 +15,11 @@
|
||||
|
||||
@class RCTEventDispatcher;
|
||||
#if __has_include(<react-native-video/RCTVideoCache.h>)
|
||||
@interface RCTVideo : UIView <RCTVideoPlayerViewControllerDelegate, DVAssetLoaderDelegatesDelegate, AVAssetResourceLoaderDelegate>
|
||||
@interface RCTVideo : UIView <RCTVideoPlayerViewControllerDelegate, DVAssetLoaderDelegatesDelegate, AVAssetResourceLoaderDelegate, IMAAdsLoaderDelegate, IMAAdsManagerDelegate>
|
||||
#elif TARGET_OS_TV
|
||||
@interface RCTVideo : UIView <RCTVideoPlayerViewControllerDelegate, AVAssetResourceLoaderDelegate>
|
||||
#else
|
||||
@interface RCTVideo : UIView <RCTVideoPlayerViewControllerDelegate, AVPictureInPictureControllerDelegate, AVAssetResourceLoaderDelegate>
|
||||
@interface RCTVideo : UIView <RCTVideoPlayerViewControllerDelegate, AVPictureInPictureControllerDelegate, AVAssetResourceLoaderDelegate, IMAAdsLoaderDelegate, IMAAdsManagerDelegate>
|
||||
#endif
|
||||
|
||||
@property (nonatomic, copy) RCTDirectEventBlock onVideoLoadStart;
|
||||
@@ -55,6 +56,12 @@ typedef NS_ENUM(NSInteger, RCTVideoError) {
|
||||
RCTVideoErrorNoFairplayDRM,
|
||||
RCTVideoErrorNoDRMData
|
||||
};
|
||||
/// Playhead used by the SDK to track content video progress and insert mid-rolls.
|
||||
@property(nonatomic, strong) IMAAVPlayerContentPlayhead *contentPlayhead;
|
||||
/// Entry point for the SDK. Used to make ad requests.
|
||||
@property(nonatomic, strong) IMAAdsLoader *adsLoader;
|
||||
/// Main point of interaction with the SDK. Created by the SDK as the result of an ad request.
|
||||
@property(nonatomic, strong) IMAAdsManager *adsManager;
|
||||
|
||||
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
|
@@ -45,6 +45,7 @@ static int const RCTVideoUnset = -1;
|
||||
BOOL _playbackRateObserverRegistered;
|
||||
BOOL _isExternalPlaybackActiveObserverRegistered;
|
||||
BOOL _videoLoadStarted;
|
||||
BOOL _isRequestAds;
|
||||
|
||||
bool _pendingSeek;
|
||||
float _pendingSeekTime;
|
||||
@@ -82,6 +83,7 @@ static int const RCTVideoUnset = -1;
|
||||
NSString * _fullscreenOrientation;
|
||||
BOOL _fullscreenPlayerPresented;
|
||||
NSString *_filterName;
|
||||
NSString * _adTagUrl;
|
||||
BOOL _filterEnabled;
|
||||
UIViewController * _presentingViewController;
|
||||
#if __has_include(<react-native-video/RCTVideoCache.h>)
|
||||
@@ -118,6 +120,7 @@ static int const RCTVideoUnset = -1;
|
||||
_allowsExternalPlayback = YES;
|
||||
_playWhenInactive = false;
|
||||
_pictureInPicture = false;
|
||||
_isRequestAds = false;
|
||||
_ignoreSilentSwitch = @"inherit"; // inherit, ignore, obey
|
||||
_mixWithOthers = @"inherit"; // inherit, mix, duck
|
||||
#if TARGET_OS_IOS
|
||||
@@ -152,14 +155,16 @@ static int const RCTVideoUnset = -1;
|
||||
|
||||
- (RCTVideoPlayerViewController*)createPlayerViewController:(AVPlayer*)player
|
||||
withPlayerItem:(AVPlayerItem*)playerItem {
|
||||
RCTVideoPlayerViewController* viewController = [[RCTVideoPlayerViewController alloc] init];
|
||||
viewController.showsPlaybackControls = YES;
|
||||
viewController.rctDelegate = self;
|
||||
viewController.preferredOrientation = _fullscreenOrientation;
|
||||
|
||||
viewController.view.frame = self.bounds;
|
||||
viewController.player = player;
|
||||
return viewController;
|
||||
RCTVideoPlayerViewController* viewController = [[RCTVideoPlayerViewController alloc] init];
|
||||
viewController.showsPlaybackControls = YES;
|
||||
viewController.rctDelegate = self;
|
||||
viewController.preferredOrientation = _fullscreenOrientation;
|
||||
self.contentPlayhead = [[IMAAVPlayerContentPlayhead alloc] initWithAVPlayer:player];
|
||||
[self setupAdsLoader];
|
||||
|
||||
viewController.view.frame = self.bounds;
|
||||
viewController.player = player;
|
||||
return viewController;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------
|
||||
@@ -280,8 +285,12 @@ static int const RCTVideoUnset = -1;
|
||||
const Float64 currentTimeSecs = CMTimeGetSeconds(currentTime);
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"RCTVideo_progress" object:nil userInfo:@{@"progress": [NSNumber numberWithDouble: currentTimeSecs / duration]}];
|
||||
|
||||
|
||||
if( currentTimeSecs >= 0 && self.onVideoProgress) {
|
||||
if(!_isRequestAds && currentTimeSecs >= 0.0001) {
|
||||
[self requestAds];
|
||||
_isRequestAds = true;
|
||||
}
|
||||
self.onVideoProgress(@{
|
||||
@"currentTime": [NSNumber numberWithFloat:CMTimeGetSeconds(currentTime)],
|
||||
@"playableDuration": [self calculatePlayableDuration],
|
||||
@@ -694,7 +703,6 @@ static int const RCTVideoUnset = -1;
|
||||
@"target": self.reactTag});
|
||||
}
|
||||
_videoLoadStarted = NO;
|
||||
|
||||
[self attachListeners];
|
||||
[self applyModifiers];
|
||||
} else if (_playerItem.status == AVPlayerItemStatusFailed && self.onVideoError) {
|
||||
@@ -757,6 +765,74 @@ static int const RCTVideoUnset = -1;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setupAdsLoader {
|
||||
// Re-use this IMAAdsLoader instance for the entire lifecycle of your app.
|
||||
self.adsLoader = [[IMAAdsLoader alloc] initWithSettings:nil];
|
||||
// NOTE: This line will cause a warning until the next step, "Get the Ads Manager".
|
||||
self.adsLoader.delegate = self;
|
||||
}
|
||||
|
||||
- (void)requestAds {
|
||||
// Create an ad display container for ad rendering.
|
||||
IMAAdDisplayContainer *adDisplayContainer =
|
||||
[[IMAAdDisplayContainer alloc] initWithAdContainer:self companionSlots:nil];
|
||||
// Create an ad request with our ad tag, display container, and optional user context.
|
||||
IMAAdsRequest *request = [[IMAAdsRequest alloc] initWithAdTagUrl:_adTagUrl
|
||||
adDisplayContainer:adDisplayContainer
|
||||
contentPlayhead:self.contentPlayhead
|
||||
userContext:nil];
|
||||
[self.adsLoader requestAdsWithRequest:request];
|
||||
}
|
||||
|
||||
#pragma mark AdsLoader Delegates
|
||||
|
||||
- (void)adsLoader:(IMAAdsLoader *)loader adsLoadedWithData:(IMAAdsLoadedData *)adsLoadedData {
|
||||
// Grab the instance of the IMAAdsManager and set ourselves as the delegate.
|
||||
self.adsManager = adsLoadedData.adsManager;
|
||||
|
||||
// NOTE: This line will cause a warning until the next step, "Display Ads".
|
||||
self.adsManager.delegate = self;
|
||||
|
||||
// Create ads rendering settings and tell the SDK to use the in-app browser.
|
||||
IMAAdsRenderingSettings *adsRenderingSettings = [[IMAAdsRenderingSettings alloc] init];
|
||||
adsRenderingSettings.webOpenerPresentingController = _playerViewController;
|
||||
|
||||
// Initialize the ads manager.
|
||||
[self.adsManager initializeWithAdsRenderingSettings:adsRenderingSettings];
|
||||
}
|
||||
|
||||
- (void)adsLoader:(IMAAdsLoader *)loader failedWithErrorData:(IMAAdLoadingErrorData *)adErrorData {
|
||||
// Something went wrong loading ads. Log the error and play the content.
|
||||
NSLog(@"Error loading ads: %@", adErrorData.adError.message);
|
||||
[_player play];
|
||||
}
|
||||
|
||||
#pragma mark AdsManager Delegates
|
||||
|
||||
- (void)adsManager:(IMAAdsManager *)adsManager didReceiveAdEvent:(IMAAdEvent *)event {
|
||||
if (event.type == kIMAAdEvent_LOADED) {
|
||||
// When the SDK notifies us that ads have been loaded, play them.
|
||||
[adsManager start];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)adsManager:(IMAAdsManager *)adsManager didReceiveAdError:(IMAAdError *)error {
|
||||
// Something went wrong with the ads manager after ads were loaded. Log the error and play the
|
||||
// content.
|
||||
NSLog(@"AdsManager error: %@", error.message);
|
||||
[_player play];
|
||||
}
|
||||
|
||||
- (void)adsManagerDidRequestContentPause:(IMAAdsManager *)adsManager {
|
||||
// The SDK is going to play ads, so pause the content.
|
||||
[_player pause];
|
||||
}
|
||||
|
||||
- (void)adsManagerDidRequestContentResume:(IMAAdsManager *)adsManager {
|
||||
// The SDK is done playing ads (at least for now), so resume the content.
|
||||
[_player play];
|
||||
}
|
||||
|
||||
- (void)attachListeners
|
||||
{
|
||||
// listen for end of file
|
||||
@@ -824,6 +900,9 @@ static int const RCTVideoUnset = -1;
|
||||
|
||||
- (void)playerItemDidReachEnd:(NSNotification *)notification
|
||||
{
|
||||
if (notification.object == _player.currentItem) {
|
||||
[self.adsLoader contentComplete];
|
||||
}
|
||||
if(self.onVideoEnd) {
|
||||
self.onVideoEnd(@{@"target": self.reactTag});
|
||||
}
|
||||
@@ -1535,6 +1614,10 @@ static int const RCTVideoUnset = -1;
|
||||
_filterEnabled = filterEnabled;
|
||||
}
|
||||
|
||||
- (void)setAdTagUrl:(NSString *)adTagUrl {
|
||||
_adTagUrl = adTagUrl;
|
||||
}
|
||||
|
||||
#pragma mark - React View Management
|
||||
|
||||
- (void)insertReactSubview:(UIView *)view atIndex:(NSInteger)atIndex
|
||||
|
@@ -20,6 +20,7 @@ RCT_EXPORT_MODULE();
|
||||
|
||||
RCT_EXPORT_VIEW_PROPERTY(src, NSDictionary);
|
||||
RCT_EXPORT_VIEW_PROPERTY(drm, NSDictionary);
|
||||
RCT_EXPORT_VIEW_PROPERTY(adTagUrl, NSString);
|
||||
RCT_EXPORT_VIEW_PROPERTY(maxBitRate, float);
|
||||
RCT_EXPORT_VIEW_PROPERTY(resizeMode, NSString);
|
||||
RCT_EXPORT_VIEW_PROPERTY(repeat, BOOL);
|
||||
|
Reference in New Issue
Block a user