-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adds a basic example for pod serving DAI in Objective-C.
PiperOrigin-RevId: 647085760
- Loading branch information
Showing
8 changed files
with
824 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
source 'https://github.com/CocoaPods/Specs.git' | ||
|
||
platform :tvos, '17' | ||
project 'VideoStitcherExample.xcodeproj' | ||
|
||
target "VideoStitcherExample" do | ||
pod 'GoogleAds-IMA-tvOS-SDK' | ||
end |
456 changes: 456 additions & 0 deletions
456
ObjectiveC/VideoStitcherExample/VideoStitcherExample.xcodeproj/project.pbxproj
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
// Copyright 2024 Google LLC. All rights reserved. | ||
// | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this | ||
// file except in compliance with the License. You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software distributed under | ||
// the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF | ||
// ANY KIND, either express or implied. See the License for the specific language governing | ||
// permissions and limitations under the License. | ||
|
||
#import <UIKit/UIKit.h> | ||
|
||
@interface AppDelegate : UIResponder <UIApplicationDelegate> | ||
@property(nonatomic) UIWindow *window; | ||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
// Copyright 2024 Google LLC. All rights reserved. | ||
// | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this | ||
// file except in compliance with the License. You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software distributed under | ||
// the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF | ||
// ANY KIND, either express or implied. See the License for the specific language governing | ||
// permissions and limitations under the License. | ||
|
||
#import "AppDelegate.h" | ||
|
||
#import "ViewController.h" | ||
|
||
@implementation AppDelegate | ||
|
||
- (BOOL)application:(UIApplication *)application | ||
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { | ||
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; | ||
self.window.rootViewController = [[ViewController alloc] init]; | ||
[self.window makeKeyAndVisible]; | ||
return YES; | ||
} | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||
<plist version="1.0"> | ||
<dict> | ||
<key>CFBundleDevelopmentRegion</key> | ||
<string>en</string> | ||
<key>CFBundleExecutable</key> | ||
<string>$(EXECUTABLE_NAME)</string> | ||
<key>CFBundleIdentifier</key> | ||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> | ||
<key>CFBundleInfoDictionaryVersion</key> | ||
<string>6.0</string> | ||
<key>CFBundleName</key> | ||
<string>$(PRODUCT_NAME)</string> | ||
<key>CFBundlePackageType</key> | ||
<string>APPL</string> | ||
<key>CFBundleShortVersionString</key> | ||
<string>1.0</string> | ||
<key>CFBundleVersion</key> | ||
<string>1</string> | ||
<key>LSRequiresIPhoneOS</key> | ||
<true/> | ||
<key>UIRequiredDeviceCapabilities</key> | ||
<array> | ||
<string>arm64</string> | ||
</array> | ||
<key>UIUserInterfaceStyle</key> | ||
<string>Automatic</string> | ||
<key>NSAppTransportSecurity</key> | ||
<dict> | ||
<key>NSAllowsArbitraryLoads</key> | ||
<true/> | ||
</dict> | ||
</dict> | ||
</plist> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
// Copyright 2024 Google LLC. All rights reserved. | ||
// | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this | ||
// file except in compliance with the License. You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software distributed under | ||
// the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF | ||
// ANY KIND, either express or implied. See the License for the specific language governing | ||
// permissions and limitations under the License. | ||
|
||
#import <UIKit/UIKit.h> | ||
|
||
@interface ViewController : UIViewController | ||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,241 @@ | ||
// Copyright 2024 Google LLC. All rights reserved. | ||
// | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this | ||
// file except in compliance with the License. You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software distributed under | ||
// the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF | ||
// ANY KIND, either express or implied. See the License for the specific language governing | ||
// permissions and limitations under the License. | ||
|
||
#import "ViewController.h" | ||
|
||
#import <AVKit/AVKit.h> | ||
|
||
@import GoogleInteractiveMediaAds; | ||
|
||
typedef enum {kLiveStream, kVODStream} streamType; | ||
|
||
/// VideoStitcher stream request type. Either kLiveStream or kVODStream. | ||
static streamType const kRequestType = kLiveStream; | ||
|
||
/// The live stream event ID associated with this stream in your Google Cloud project. | ||
static NSString *const kLiveStreamEventID = @""; | ||
/// The custom asset key associated with this stream in your Google Cloud project. | ||
static NSString *const kCustomAssetKey = @""; | ||
|
||
/// The VOD stream config ID associated with this stream in your Google Cloud project. | ||
static NSString *const kVODConfigID = @""; | ||
|
||
/// The network code of the Google Cloud account containing the Video Stitcher API project. | ||
static NSString *const kNetworkCode = @""; | ||
/// The project number associated with your Video Stitcher API project. | ||
static NSString *const kProjectNumber = @""; | ||
/// The Google Cloud region where your Video Stitcher API project is located. | ||
static NSString *const kLocation = @""; | ||
/// A recently generated OAuth Token for a Google Cloud service worker account with the Video | ||
/// Stitcher API enabled. | ||
static NSString *const kOAuthToken = @""; | ||
|
||
/// Fallback URL in case something goes wrong in loading the stream. If all goes well, this will not | ||
/// be used. | ||
static NSString *const kBackupStreamURLString = | ||
@"http://googleimadev-vh.akamaihd.net/i/big_buck_bunny/bbb-,480p,720p,1080p,.mov.csmil/" | ||
@"master.m3u8"; | ||
|
||
@interface ViewController () <IMAAdsLoaderDelegate, | ||
IMAStreamManagerDelegate, | ||
AVPlayerViewControllerDelegate> | ||
@property(nonatomic) IMAAdsLoader *adsLoader; | ||
@property(nonatomic) IMAAdDisplayContainer *adDisplayContainer; | ||
@property(nonatomic) UIView *adContainerView; | ||
@property(nonatomic) id<IMAVideoDisplay> videoDisplay; | ||
@property(nonatomic) IMAStreamManager *streamManager; | ||
@property(nonatomic) AVPlayerViewController *playerViewController; | ||
@property(nonatomic, getter=isAdBreakActive) BOOL adBreakActive; | ||
@end | ||
|
||
@implementation ViewController | ||
|
||
- (void)viewDidLoad { | ||
[super viewDidLoad]; | ||
self.view.backgroundColor = [UIColor blackColor]; | ||
[self setupAdsLoader]; | ||
[self setupPlayer]; | ||
[self setupAdContainer]; | ||
} | ||
|
||
- (void)viewDidAppear:(BOOL)animated { | ||
[super viewDidAppear:animated]; | ||
[self requestStream]; | ||
} | ||
|
||
- (void)setupAdsLoader { | ||
self.adsLoader = [[IMAAdsLoader alloc] init]; | ||
self.adsLoader.delegate = self; | ||
} | ||
|
||
- (void)setupPlayer { | ||
// Create a stream video player. | ||
AVPlayer *player = [[AVPlayer alloc] init]; | ||
self.playerViewController = [[AVPlayerViewController alloc] init]; | ||
self.playerViewController.player = player; | ||
|
||
// Attach video player to view hierarchy. | ||
[self addChildViewController:self.playerViewController]; | ||
[self.view addSubview:self.playerViewController.view]; | ||
self.playerViewController.view.frame = self.view.bounds; | ||
[self.playerViewController didMoveToParentViewController:self]; | ||
} | ||
|
||
- (void)setupAdContainer { | ||
// Attach the ad container to the view hierarchy on top of the player. | ||
self.adContainerView = [[UIView alloc] init]; | ||
[self.view addSubview:self.adContainerView]; | ||
self.adContainerView.frame = self.view.bounds; | ||
// Keep hidden initially, until an ad break. | ||
self.adContainerView.hidden = YES; | ||
} | ||
|
||
- (void)requestStream { | ||
self.videoDisplay = | ||
[[IMAAVPlayerVideoDisplay alloc] initWithAVPlayer:self.playerViewController.player]; | ||
self.adDisplayContainer = [[IMAAdDisplayContainer alloc] initWithAdContainer:self.adContainerView | ||
viewController:self]; | ||
// Create a stream request. | ||
IMAStreamRequest *streamRequest; | ||
if (kRequestType == kLiveStream) { | ||
streamRequest = | ||
[[IMAVideoStitcherLiveStreamRequest alloc] initWithLiveStreamEventID:kLiveStreamEventID | ||
region:kLocation | ||
projectNumber:kProjectNumber | ||
OAuthToken:kOAuthToken | ||
networkCode:kNetworkCode | ||
customAssetKey:kCustomAssetKey | ||
adDisplayContainer:self.adDisplayContainer | ||
videoDisplay:self.videoDisplay | ||
userContext:nil | ||
videoStitcherSessionOptions:nil]; | ||
} else { | ||
streamRequest = | ||
[[IMAVideoStitcherVODStreamRequest alloc] initWithVODConfigID:kVODConfigID | ||
region:kLocation | ||
projectNumber:kProjectNumber | ||
OAuthToken:kOAuthToken | ||
networkCode:kNetworkCode | ||
adDisplayContainer:self.adDisplayContainer | ||
videoDisplay:self.videoDisplay | ||
userContext:nil | ||
videoStitcherSessionOptions:nil]; | ||
} | ||
[self.adsLoader requestStreamWithRequest:streamRequest]; | ||
} | ||
|
||
- (void)playBackupStream { | ||
NSURL *backupStreamURL = [NSURL URLWithString:kBackupStreamURLString]; | ||
[self.videoDisplay loadStream:backupStreamURL withSubtitles:@[]]; | ||
[self.videoDisplay play]; | ||
[self startMediaSession]; | ||
} | ||
|
||
- (void)startMediaSession { | ||
[[AVAudioSession sharedInstance] setActive:YES error:nil]; | ||
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil]; | ||
} | ||
|
||
#pragma mark - UIFocusEnvironment | ||
|
||
- (NSArray<id<UIFocusEnvironment>> *)preferredFocusEnvironments { | ||
if (self.isAdBreakActive && self.adDisplayContainer.focusEnvironment) { | ||
// Send focus to the ad display container during an ad break. | ||
return @[ self.adDisplayContainer.focusEnvironment ]; | ||
} else { | ||
// Send focus to the content player otherwise. | ||
return @[ self.playerViewController ]; | ||
} | ||
} | ||
|
||
#pragma mark - IMAAdsLoaderDelegate | ||
|
||
- (void)adsLoader:(IMAAdsLoader *)loader adsLoadedWithData:(IMAAdsLoadedData *)adsLoadedData { | ||
// Initialize and listen to stream manager's events. | ||
self.streamManager = adsLoadedData.streamManager; | ||
self.streamManager.delegate = self; | ||
[self.streamManager initializeWithAdsRenderingSettings:nil]; | ||
NSLog(@"Stream created with: %@.", self.streamManager.streamId); | ||
} | ||
|
||
- (void)adsLoader:(IMAAdsLoader *)loader failedWithErrorData:(IMAAdLoadingErrorData *)adErrorData { | ||
// Fall back to playing the backup stream. | ||
NSLog(@"Error loading ads: %@", adErrorData.adError.message); | ||
[self playBackupStream]; | ||
} | ||
|
||
#pragma mark - IMAStreamManagerDelegate | ||
|
||
- (void)streamManager:(IMAStreamManager *)streamManager didReceiveAdEvent:(IMAAdEvent *)event { | ||
NSLog(@"StreamManager event (%@).", event.typeString); | ||
switch (event.type) { | ||
case kIMAAdEvent_STREAM_STARTED: { | ||
[self startMediaSession]; | ||
break; | ||
} | ||
case kIMAAdEvent_STARTED: { | ||
// Log extended data. | ||
NSString *extendedAdPodInfo = [[NSString alloc] | ||
initWithFormat:@"Showing ad %zd/%zd, bumper: %@, title: %@, description: %@, contentType:" | ||
@"%@, pod index: %zd, time offset: %lf, max duration: %lf.", | ||
event.ad.adPodInfo.adPosition, event.ad.adPodInfo.totalAds, | ||
event.ad.adPodInfo.isBumper ? @"YES" : @"NO", event.ad.adTitle, | ||
event.ad.adDescription, event.ad.contentType, event.ad.adPodInfo.podIndex, | ||
event.ad.adPodInfo.timeOffset, event.ad.adPodInfo.maxDuration]; | ||
|
||
NSLog(@"%@", extendedAdPodInfo); | ||
break; | ||
} | ||
case kIMAAdEvent_AD_BREAK_STARTED: { | ||
self.adContainerView.hidden = NO; | ||
// Trigger an update to send focus to the ad display container. | ||
self.adBreakActive = YES; | ||
[self setNeedsFocusUpdate]; | ||
break; | ||
} | ||
case kIMAAdEvent_AD_BREAK_ENDED: { | ||
self.adContainerView.hidden = YES; | ||
// Trigger an update to send focus to the content player. | ||
self.adBreakActive = NO; | ||
[self setNeedsFocusUpdate]; | ||
break; | ||
} | ||
case kIMAAdEvent_ICON_FALLBACK_IMAGE_CLOSED: { | ||
// Resume playback after the user has closed the dialog. | ||
[self.videoDisplay play]; | ||
break; | ||
} | ||
default: | ||
break; | ||
} | ||
} | ||
|
||
- (void)streamManager:(IMAStreamManager *)streamManager didReceiveAdError:(IMAAdError *)error { | ||
// Fall back to playing the backup stream. | ||
NSLog(@"StreamManager error: %@", error.message); | ||
[self playBackupStream]; | ||
} | ||
|
||
#pragma mark - AVPlayerViewControllerDelegate | ||
|
||
- (CMTime)playerViewController:(AVPlayerViewController *)playerViewController | ||
timeToSeekAfterUserNavigatedFromTime:(CMTime)oldTime | ||
toTime:(CMTime)targetTime { | ||
if (self.isAdBreakActive) { | ||
// Disable seeking during ad breaks. | ||
return oldTime; | ||
} | ||
return targetTime; | ||
} | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
// Copyright 2024 Google LLC. All rights reserved. | ||
// | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this | ||
// file except in compliance with the License. You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software distributed under | ||
// the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF | ||
// ANY KIND, either express or implied. See the License for the specific language governing | ||
// permissions and limitations under the License. | ||
|
||
#import <UIKit/UIKit.h> | ||
#import "AppDelegate.h" | ||
|
||
int main(int argc, char* argv[]) { | ||
@autoreleasepool { | ||
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); | ||
} | ||
} |