From 1045ae9ec5d2f63a356e7e4f8f8d2faa50cad77f Mon Sep 17 00:00:00 2001
From: Goncalo Mendes <106695857+goncalocostamendes@users.noreply.github.com>
Date: Thu, 17 Aug 2023 23:31:23 +0100
Subject: [PATCH] [VIDCS-922] Add Video transformers sample (#289)
* Add Video transformers sample
* Add Video Transformers smaple to travis build
* Add a complete README with examples
* Include the installation steps
* Remove commented code showing how to remove transformers from publisher stream
* Add Changelog file with know issues
* Button to toggle Video transformers
---------
Co-authored-by: pvenum <78760156+pvenum@users.noreply.github.com>
---
Video-Transformers/CHANGELOG.md | 32 ++
Video-Transformers/Podfile | 8 +
Video-Transformers/README.md | 139 +++++++
.../project.pbxproj | 379 ++++++++++++++++++
.../contents.xcworkspacedata | 7 +
.../xcshareddata/IDEWorkspaceChecks.plist | 8 +
.../xcschemes/Basic-Video-Chat.xcscheme | 87 ++++
.../contents.xcworkspacedata | 10 +
.../xcshareddata/IDEWorkspaceChecks.plist | 8 +
.../Video-Transformers/AppDelegate.h | 14 +
.../Video-Transformers/AppDelegate.m | 45 +++
.../Base.lproj/Main_iPad.storyboard | 26 ++
.../Base.lproj/Main_iPhone.storyboard | 35 ++
.../AppIcon.appiconset/Contents.json | 98 +++++
.../Images.xcassets/Contents.json | 6 +
.../Video-Transformers/Info.plist | 57 +++
.../Video-Transformers/ViewController.h | 12 +
.../Video-Transformers/ViewController.m | 356 ++++++++++++++++
.../Video-Transformers/Vonage_Logo.png | Bin 0 -> 38656 bytes
.../en.lproj/InfoPlist.strings | 2 +
Video-Transformers/Video-Transformers/main.m | 18 +
travis_build.sh | 4 +
22 files changed, 1351 insertions(+)
create mode 100644 Video-Transformers/CHANGELOG.md
create mode 100644 Video-Transformers/Podfile
create mode 100644 Video-Transformers/README.md
create mode 100644 Video-Transformers/Video-Transformers.xcodeproj/project.pbxproj
create mode 100644 Video-Transformers/Video-Transformers.xcodeproj/project.xcworkspace/contents.xcworkspacedata
create mode 100644 Video-Transformers/Video-Transformers.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
create mode 100644 Video-Transformers/Video-Transformers.xcodeproj/xcshareddata/xcschemes/Basic-Video-Chat.xcscheme
create mode 100644 Video-Transformers/Video-Transformers.xcworkspace/contents.xcworkspacedata
create mode 100644 Video-Transformers/Video-Transformers.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
create mode 100644 Video-Transformers/Video-Transformers/AppDelegate.h
create mode 100644 Video-Transformers/Video-Transformers/AppDelegate.m
create mode 100644 Video-Transformers/Video-Transformers/Base.lproj/Main_iPad.storyboard
create mode 100644 Video-Transformers/Video-Transformers/Base.lproj/Main_iPhone.storyboard
create mode 100644 Video-Transformers/Video-Transformers/Images.xcassets/AppIcon.appiconset/Contents.json
create mode 100644 Video-Transformers/Video-Transformers/Images.xcassets/Contents.json
create mode 100644 Video-Transformers/Video-Transformers/Info.plist
create mode 100644 Video-Transformers/Video-Transformers/ViewController.h
create mode 100644 Video-Transformers/Video-Transformers/ViewController.m
create mode 100644 Video-Transformers/Video-Transformers/Vonage_Logo.png
create mode 100644 Video-Transformers/Video-Transformers/en.lproj/InfoPlist.strings
create mode 100644 Video-Transformers/Video-Transformers/main.m
diff --git a/Video-Transformers/CHANGELOG.md b/Video-Transformers/CHANGELOG.md
new file mode 100644
index 00000000..f68d4648
--- /dev/null
+++ b/Video-Transformers/CHANGELOG.md
@@ -0,0 +1,32 @@
+# Video Transformers Changelog
+
+All notable changes to this project will be documented in this file.
+
+## 2.25.2
+
+### Added
+
+- Support pre-built transformers in the Vonage Media Processor library or create your own custom video transformer to apply to published video.
+
+### Known issues
+
+- When using Vonage's Background Blur, the ML model is not being automatically added to the main bundle. It is required to be added manually:
+ - Select the target for which you want to add the file. Go to the "Build Phases" tab.
+ - Expand the "Copy Bundle Resources" section.
+ - Click the "+" button to add a new file. A file picker dialog will appear. Choose ./Pods/VonageClientSDKVideo/OpenTok.xcframework/ios-arm64/OpenTok.framework/selfie_segmentation.tflite
+
+### Fixed
+
+- NA
+
+### Enhancements
+
+- NA
+
+### Changed
+
+- NA
+
+### Deprecated
+
+- NA
diff --git a/Video-Transformers/Podfile b/Video-Transformers/Podfile
new file mode 100644
index 00000000..01c0065b
--- /dev/null
+++ b/Video-Transformers/Podfile
@@ -0,0 +1,8 @@
+require_relative '../OpenTokSDKVersion'
+
+platform :ios, '13'
+use_frameworks!
+
+target 'Video-Transformers' do
+ pod 'VonageClientSDKVideo', '2.25.2'
+end
diff --git a/Video-Transformers/README.md b/Video-Transformers/README.md
new file mode 100644
index 00000000..fad6fe1f
--- /dev/null
+++ b/Video-Transformers/README.md
@@ -0,0 +1,139 @@
+Video Transformers
+======================
+
+The Video Transformers app is a very simple application created on top of Basic Video Chat meant to get a new developer
+started using Media Processor APIs on OpenTok iOS SDK. For a full description, see the [Video Transformers tutorial at the
+OpenTok developer center](https://tokbox.com/developer/guides/vonage-media-processor/ios).
+
+You can use pre-built transformers in the Vonage Media Processor library or create your own custom video transformer to apply to published video.
+
+You can use the OTPublisherKit.videoTransformers properties to apply video transformers to a stream.
+
+For video, you can apply the background blur video transformer included in the Vonage Media Library.
+You can use the OTPublisherKit.audioTransformers
and
+OTPublisherKit.videoTransformers
+properties to apply audio and video transformers to a stream.
+
+
+ Important: The audio and video transformer API is a beta feature.
+
+
+For video, you can apply the background blur video transformer included in the Vonage Media Library.
+
+You can also create your own custom audio and video transformers.
+
+## Applying a video transformer from the Vonage Media Library
+
+Use the [OTVideoTransformer initWithName:properties:]
+method to create a video transformer that uses a named transformer from the Vonage Media Library.
+
+Currently, only one transformer is supported: background blur. Set the `name` parameter to `"BackgroundBlur"`.
+Set the `properties` parameter to a JSON string defining properties for the transformer.
+For the background blur transformer, this JSON includes one property -- `radius` -- which can be set
+to `"High"`, `"Low"`, or `"None"`.
+
+```objectivec
+NSMutableArray * myVideoTransformers = [[NSMutableArray alloc] init];
+OTVideoTransformer *backgroundBlur = [[OTVideoTransformer alloc] initWithName:@"BackgroundBlur"
+ properties:@"{\"radius\":\"High\"}"];
+[myVideoTransformers addObject:backgroundBlur];
+_publisher.videoTransformers = [[NSArray alloc] initWithArray:myVideoTransformers];
+```
+
+## Creating a custom video transformer
+
+Create a class that implements the OTCustomVideoTransformer
+protocol. Implement the `[OTCustomVideoTransformer transform:]` method, applying a transformation to the `OTVideoFrame` object passed into the method. The `[OTCustomVideoTransformer transform:]` method is triggered for each video frame:
+
+```objectivec
+@interface CustomTransformer : NSObject
+@end
+@implementation CustomTransformer
+- (void)transform:(nonnull OTVideoFrame *)videoFrame {
+ // Your custom transformation
+}
+@end
+```
+
+In this sample, to display one of the infinite transformations that can be applied to video frames, a logo is being added to the bottom right corner of the video.
+
+```objectivec
+@interface CustomTransformer : NSObject
+@end
+@implementation CustomTransformer
+- (void)transform:(nonnull OTVideoFrame *)videoFrame {
+
+ UIImage* image = [UIImage imageNamed:@"Vonage_Logo.png"];
+
+ uint32_t videoWidth = videoFrame.format.imageWidth;
+ uint32_t videoHeight = videoFrame.format.imageHeight;
+
+ // Calculate the desired size of the image
+ CGFloat desiredWidth = videoWidth / 8; // Adjust this value as needed
+ CGFloat desiredHeight = image.size.height * (desiredWidth / image.size.width);
+
+ // Resize the image to the desired size
+ UIImage *resizedImage = [self resizeImage:image toSize:CGSizeMake(desiredWidth, desiredHeight)];
+
+ // Get pointer to the Y plane
+ uint8_t* yPlane = [videoFrame getPlaneBinaryData:0];
+
+ // Create a CGContext from the Y plane
+ CGContextRef context = CGBitmapContextCreate(yPlane, videoWidth, videoHeight, 8, videoWidth, CGColorSpaceCreateDeviceGray(), kCGImageAlphaNone);
+
+ // Location of the image (in this case right bottom corner)
+ CGFloat x = videoWidth * 4/5;
+ CGFloat y = videoHeight * 1/5;
+
+ // Draw the resized image on top of the Y plane
+ CGRect rect = CGRectMake(x, y, desiredWidth, desiredHeight);
+ CGContextDrawImage(context, rect, resizedImage.CGImage);
+
+ CGContextRelease(context);
+}
+@end
+```
+
+Then set the `OTPublisherKit.videoTransformers` property to an array that includes the object that implements the
+OTCustomVideoTransformer interface:
+
+```objectivec
+CustomTransformer* logoTransformer;
+logoTransformer = [customTransformer alloc];
+OTVideoTransformer *myCustomTransformer = [[OTVideoTransformer alloc] initWithName:@"logo" transformer:logoTransformer];
+
+NSMutableArray * myVideoTransformers = [[NSMutableArray alloc] init];
+[myVideoTransformers addObject:myCustomTransformer];
+
+_publisher.videoTransformers = [[NSArray alloc] initWithArray:myVideoTransformers];
+```
+
+You can combine the Vonage Media library transformer (see the previous section) with custom transformers or apply
+multiple custom transformers by adding multiple PublisherKit.VideoTransformer objects to the ArrayList used
+for the `OTPublisherKit.videoTransformers` property.
+
+## Clearing video transformers for a publisher
+
+To clear video transformers for a publisher, set the `OTPublisherKit.videoTransformers` property to an empty array.
+
+```objectivec
+_publisher.videoTransformers = [[NSArray alloc] init];
+```
+
+Adding the OpenTok library
+==========================
+In this example the OpenTok iOS SDK was not included as a dependency,
+you can do it through Swift Package Manager or Cocoapods.
+
+
+Swift Package Manager
+---------------------
+To add a package dependency to your Xcode project, you should select
+*File* > *Swift Packages* > *Add Package Dependency* and enter the repository URL:
+`https://github.com/opentok/vonage-client-sdk-video.git`.
+
+
+Cocoapods
+---------
+To use CocoaPods to add the OpenTok library and its dependencies into this sample app
+simply open Terminal, navigate to the root directory of the project and run: `pod install`.
diff --git a/Video-Transformers/Video-Transformers.xcodeproj/project.pbxproj b/Video-Transformers/Video-Transformers.xcodeproj/project.pbxproj
new file mode 100644
index 00000000..b9ce6743
--- /dev/null
+++ b/Video-Transformers/Video-Transformers.xcodeproj/project.pbxproj
@@ -0,0 +1,379 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ A05376311EB173CC00645696 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = A05376251EB173CC00645696 /* AppDelegate.m */; };
+ A05376321EB173CC00645696 /* Main_iPad.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A05376261EB173CC00645696 /* Main_iPad.storyboard */; };
+ A05376331EB173CC00645696 /* Main_iPhone.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A05376281EB173CC00645696 /* Main_iPhone.storyboard */; };
+ A05376341EB173CC00645696 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = A053762A1EB173CC00645696 /* InfoPlist.strings */; };
+ A05376351EB173CC00645696 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A053762C1EB173CC00645696 /* Images.xcassets */; };
+ A05376361EB173CC00645696 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = A053762D1EB173CC00645696 /* main.m */; };
+ A05376371EB173CC00645696 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A053762F1EB173CC00645696 /* ViewController.m */; };
+ AD08FCCB2A2F8A4300F4F7A1 /* Vonage_Logo.png in Resources */ = {isa = PBXBuildFile; fileRef = AD08FCCA2A2F8A4300F4F7A1 /* Vonage_Logo.png */; };
+ AD534BB82A335ED8007D996A /* selfie_segmentation.tflite in Resources */ = {isa = PBXBuildFile; fileRef = AD534BB72A335ED8007D996A /* selfie_segmentation.tflite */; };
+ D442614C185F92C000E23699 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = D442614B185F92BF00E23699 /* README.md */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+ A05376221EB173CC00645696 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+ A05376241EB173CC00645696 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
+ A05376251EB173CC00645696 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; };
+ A05376271EB173CC00645696 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main_iPad.storyboard; sourceTree = ""; };
+ A05376291EB173CC00645696 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main_iPhone.storyboard; sourceTree = ""; };
+ A053762B1EB173CC00645696 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; };
+ A053762C1EB173CC00645696 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; };
+ A053762D1EB173CC00645696 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
+ A053762E1EB173CC00645696 /* ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; };
+ A053762F1EB173CC00645696 /* ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; };
+ AD08FCCA2A2F8A4300F4F7A1 /* Vonage_Logo.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Vonage_Logo.png; sourceTree = ""; };
+ AD534BB72A335ED8007D996A /* selfie_segmentation.tflite */ = {isa = PBXFileReference; lastKnownFileType = file; name = selfie_segmentation.tflite; path = "Pods/VonageClientSDKVideo/OpenTok.xcframework/ios-arm64/OpenTok.framework/selfie_segmentation.tflite"; sourceTree = ""; };
+ D44260F3185F81D800E23699 /* Video-Transformers.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Video-Transformers.app"; sourceTree = BUILT_PRODUCTS_DIR; };
+ D44260F6185F81D800E23699 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
+ D44260FA185F81D800E23699 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
+ D442614B185F92BF00E23699 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README.md; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ D44260F0185F81D800E23699 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 82EBC85A29B2215464A49FD9 /* Pods */ = {
+ isa = PBXGroup;
+ children = (
+ );
+ path = Pods;
+ sourceTree = "";
+ };
+ A05376211EB173CC00645696 /* Video-Transformers */ = {
+ isa = PBXGroup;
+ children = (
+ A05376221EB173CC00645696 /* Info.plist */,
+ AD08FCCA2A2F8A4300F4F7A1 /* Vonage_Logo.png */,
+ A05376241EB173CC00645696 /* AppDelegate.h */,
+ A05376251EB173CC00645696 /* AppDelegate.m */,
+ A05376261EB173CC00645696 /* Main_iPad.storyboard */,
+ A05376281EB173CC00645696 /* Main_iPhone.storyboard */,
+ A053762A1EB173CC00645696 /* InfoPlist.strings */,
+ A053762C1EB173CC00645696 /* Images.xcassets */,
+ A053762D1EB173CC00645696 /* main.m */,
+ A053762E1EB173CC00645696 /* ViewController.h */,
+ A053762F1EB173CC00645696 /* ViewController.m */,
+ );
+ path = "Video-Transformers";
+ sourceTree = "";
+ };
+ D44260EA185F81D800E23699 = {
+ isa = PBXGroup;
+ children = (
+ AD534BB72A335ED8007D996A /* selfie_segmentation.tflite */,
+ D442614B185F92BF00E23699 /* README.md */,
+ A05376211EB173CC00645696 /* Video-Transformers */,
+ D44260F5185F81D800E23699 /* Frameworks */,
+ D44260F4185F81D800E23699 /* Products */,
+ 82EBC85A29B2215464A49FD9 /* Pods */,
+ );
+ sourceTree = "";
+ };
+ D44260F4185F81D800E23699 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ D44260F3185F81D800E23699 /* Video-Transformers.app */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ D44260F5185F81D800E23699 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ D44260F6185F81D800E23699 /* Foundation.framework */,
+ D44260FA185F81D800E23699 /* UIKit.framework */,
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ D44260F2185F81D800E23699 /* Video-Transformers */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = D4426128185F81D800E23699 /* Build configuration list for PBXNativeTarget "Video-Transformers" */;
+ buildPhases = (
+ D44260EF185F81D800E23699 /* Sources */,
+ D44260F0185F81D800E23699 /* Frameworks */,
+ D44260F1185F81D800E23699 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = "Video-Transformers";
+ productName = "Hello-World";
+ productReference = D44260F3185F81D800E23699 /* Video-Transformers.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ D44260EB185F81D800E23699 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 1200;
+ ORGANIZATIONNAME = "TokBox, Inc.";
+ TargetAttributes = {
+ D44260F2185F81D800E23699 = {
+ DevelopmentTeam = 2A784Y2X3Q;
+ ProvisioningStyle = Automatic;
+ };
+ };
+ };
+ buildConfigurationList = D44260EE185F81D800E23699 /* Build configuration list for PBXProject "Video-Transformers" */;
+ compatibilityVersion = "Xcode 3.2";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = D44260EA185F81D800E23699;
+ productRefGroup = D44260F4185F81D800E23699 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ D44260F2185F81D800E23699 /* Video-Transformers */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ D44260F1185F81D800E23699 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ AD534BB82A335ED8007D996A /* selfie_segmentation.tflite in Resources */,
+ AD08FCCB2A2F8A4300F4F7A1 /* Vonage_Logo.png in Resources */,
+ A05376331EB173CC00645696 /* Main_iPhone.storyboard in Resources */,
+ A05376321EB173CC00645696 /* Main_iPad.storyboard in Resources */,
+ A05376351EB173CC00645696 /* Images.xcassets in Resources */,
+ D442614C185F92C000E23699 /* README.md in Resources */,
+ A05376341EB173CC00645696 /* InfoPlist.strings in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ D44260EF185F81D800E23699 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ A05376371EB173CC00645696 /* ViewController.m in Sources */,
+ A05376361EB173CC00645696 /* main.m in Sources */,
+ A05376311EB173CC00645696 /* AppDelegate.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+ A05376261EB173CC00645696 /* Main_iPad.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ A05376271EB173CC00645696 /* Base */,
+ );
+ name = Main_iPad.storyboard;
+ sourceTree = "";
+ };
+ A05376281EB173CC00645696 /* Main_iPhone.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ A05376291EB173CC00645696 /* Base */,
+ );
+ name = Main_iPhone.storyboard;
+ sourceTree = "";
+ };
+ A053762A1EB173CC00645696 /* InfoPlist.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ A053762B1EB173CC00645696 /* en */,
+ );
+ name = InfoPlist.strings;
+ sourceTree = "";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ D4426127185F81D800E23699 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = YES;
+ ENABLE_BITCODE = NO;
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.4;
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ D442612A185F81D800E23699 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CODE_SIGN_IDENTITY = "Apple Development";
+ CODE_SIGN_STYLE = Automatic;
+ DEVELOPMENT_TEAM = 2A784Y2X3Q;
+ ENABLE_BITCODE = NO;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "${SRCROOT}",
+ );
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = "";
+ INFOPLIST_FILE = "$(SRCROOT)/Video-Transformers/Info.plist";
+ IPHONEOS_DEPLOYMENT_TARGET = 12.4;
+ PRODUCT_BUNDLE_IDENTIFIER = "com.tokbox.Video-Transformers543e";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ WRAPPER_EXTENSION = app;
+ };
+ name = Release;
+ };
+ F8DE15D11D95710C00EFFA79 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = YES;
+ ENABLE_BITCODE = NO;
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.4;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Debug;
+ };
+ F8DE15D21D95710C00EFFA79 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CODE_SIGN_IDENTITY = "Apple Development";
+ CODE_SIGN_STYLE = Automatic;
+ DEVELOPMENT_TEAM = 2A784Y2X3Q;
+ ENABLE_BITCODE = NO;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "${SRCROOT}",
+ );
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = "";
+ INFOPLIST_FILE = "$(SRCROOT)/Video-Transformers/Info.plist";
+ IPHONEOS_DEPLOYMENT_TARGET = 12.4;
+ PRODUCT_BUNDLE_IDENTIFIER = "com.tokbox.Video-Transformers543e";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ WRAPPER_EXTENSION = app;
+ };
+ name = Debug;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ D44260EE185F81D800E23699 /* Build configuration list for PBXProject "Video-Transformers" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ D4426127185F81D800E23699 /* Release */,
+ F8DE15D11D95710C00EFFA79 /* Debug */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ D4426128185F81D800E23699 /* Build configuration list for PBXNativeTarget "Video-Transformers" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ D442612A185F81D800E23699 /* Release */,
+ F8DE15D21D95710C00EFFA79 /* Debug */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = D44260EB185F81D800E23699 /* Project object */;
+}
diff --git a/Video-Transformers/Video-Transformers.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Video-Transformers/Video-Transformers.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 00000000..df5530a0
--- /dev/null
+++ b/Video-Transformers/Video-Transformers.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/Video-Transformers/Video-Transformers.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Video-Transformers/Video-Transformers.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 00000000..18d98100
--- /dev/null
+++ b/Video-Transformers/Video-Transformers.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/Video-Transformers/Video-Transformers.xcodeproj/xcshareddata/xcschemes/Basic-Video-Chat.xcscheme b/Video-Transformers/Video-Transformers.xcodeproj/xcshareddata/xcschemes/Basic-Video-Chat.xcscheme
new file mode 100644
index 00000000..d84a4a39
--- /dev/null
+++ b/Video-Transformers/Video-Transformers.xcodeproj/xcshareddata/xcschemes/Basic-Video-Chat.xcscheme
@@ -0,0 +1,87 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Video-Transformers/Video-Transformers.xcworkspace/contents.xcworkspacedata b/Video-Transformers/Video-Transformers.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 00000000..926fe098
--- /dev/null
+++ b/Video-Transformers/Video-Transformers.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
diff --git a/Video-Transformers/Video-Transformers.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Video-Transformers/Video-Transformers.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 00000000..18d98100
--- /dev/null
+++ b/Video-Transformers/Video-Transformers.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/Video-Transformers/Video-Transformers/AppDelegate.h b/Video-Transformers/Video-Transformers/AppDelegate.h
new file mode 100644
index 00000000..c6e9853f
--- /dev/null
+++ b/Video-Transformers/Video-Transformers/AppDelegate.h
@@ -0,0 +1,14 @@
+//
+// AppDelegate.h
+// Hello-World
+//
+// Copyright (c) 2013 TokBox, Inc. All rights reserved.
+//
+
+#import
+
+@interface AppDelegate : UIResponder
+
+@property (strong, nonatomic) UIWindow *window;
+
+@end
diff --git a/Video-Transformers/Video-Transformers/AppDelegate.m b/Video-Transformers/Video-Transformers/AppDelegate.m
new file mode 100644
index 00000000..aafb971a
--- /dev/null
+++ b/Video-Transformers/Video-Transformers/AppDelegate.m
@@ -0,0 +1,45 @@
+//
+// AppDelegate.m
+// Hello-World
+//
+// Copyright (c) 2013 TokBox, Inc. All rights reserved.
+//
+
+#import "AppDelegate.h"
+
+@implementation AppDelegate
+
+- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
+{
+ // Override point for customization after application launch.
+ return YES;
+}
+
+- (void)applicationWillResignActive:(UIApplication *)application
+{
+ // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
+ // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
+}
+
+- (void)applicationDidEnterBackground:(UIApplication *)application
+{
+ // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
+ // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
+}
+
+- (void)applicationWillEnterForeground:(UIApplication *)application
+{
+ // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
+}
+
+- (void)applicationDidBecomeActive:(UIApplication *)application
+{
+ // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
+}
+
+- (void)applicationWillTerminate:(UIApplication *)application
+{
+ // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
+}
+
+@end
diff --git a/Video-Transformers/Video-Transformers/Base.lproj/Main_iPad.storyboard b/Video-Transformers/Video-Transformers/Base.lproj/Main_iPad.storyboard
new file mode 100644
index 00000000..a185e8a5
--- /dev/null
+++ b/Video-Transformers/Video-Transformers/Base.lproj/Main_iPad.storyboard
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Video-Transformers/Video-Transformers/Base.lproj/Main_iPhone.storyboard b/Video-Transformers/Video-Transformers/Base.lproj/Main_iPhone.storyboard
new file mode 100644
index 00000000..114c10e8
--- /dev/null
+++ b/Video-Transformers/Video-Transformers/Base.lproj/Main_iPhone.storyboard
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Video-Transformers/Video-Transformers/Images.xcassets/AppIcon.appiconset/Contents.json b/Video-Transformers/Video-Transformers/Images.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 00000000..1569f8dd
--- /dev/null
+++ b/Video-Transformers/Video-Transformers/Images.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,98 @@
+{
+ "images" : [
+ {
+ "idiom" : "iphone",
+ "size" : "20x20",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "20x20",
+ "scale" : "3x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "29x29",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "29x29",
+ "scale" : "3x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "40x40",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "40x40",
+ "scale" : "3x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "60x60",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "60x60",
+ "scale" : "3x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "20x20",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "20x20",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "29x29",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "29x29",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "40x40",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "40x40",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "76x76",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "76x76",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "83.5x83.5",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "ios-marketing",
+ "scale" : "1x",
+ "size" : "1024x1024"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/Video-Transformers/Video-Transformers/Images.xcassets/Contents.json b/Video-Transformers/Video-Transformers/Images.xcassets/Contents.json
new file mode 100644
index 00000000..73c00596
--- /dev/null
+++ b/Video-Transformers/Video-Transformers/Images.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Video-Transformers/Video-Transformers/Info.plist b/Video-Transformers/Video-Transformers/Info.plist
new file mode 100644
index 00000000..0db1a725
--- /dev/null
+++ b/Video-Transformers/Video-Transformers/Info.plist
@@ -0,0 +1,57 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleDisplayName
+ ${PRODUCT_NAME}
+ CFBundleExecutable
+ ${EXECUTABLE_NAME}
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ ${PRODUCT_NAME}
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1.0
+ LSRequiresIPhoneOS
+
+ NSCameraUsageDescription
+ Opentok needs access to the camera to stream your video
+ NSMicrophoneUsageDescription
+ Opentok needs access to the microphone to stream your audio
+ UIBackgroundModes
+
+ audio
+
+ UIMainStoryboardFile
+ Main_iPhone
+ UIMainStoryboardFile~ipad
+ Main_iPad
+ UIRequiredDeviceCapabilities
+
+ armv7
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+
+
diff --git a/Video-Transformers/Video-Transformers/ViewController.h b/Video-Transformers/Video-Transformers/ViewController.h
new file mode 100644
index 00000000..0d97d45f
--- /dev/null
+++ b/Video-Transformers/Video-Transformers/ViewController.h
@@ -0,0 +1,12 @@
+//
+// ViewController.h
+// Hello-World
+//
+// Copyright (c) 2013 TokBox, Inc. All rights reserved.
+//
+
+#import
+
+@interface ViewController : UIViewController
+
+@end
diff --git a/Video-Transformers/Video-Transformers/ViewController.m b/Video-Transformers/Video-Transformers/ViewController.m
new file mode 100644
index 00000000..38d42eb9
--- /dev/null
+++ b/Video-Transformers/Video-Transformers/ViewController.m
@@ -0,0 +1,356 @@
+//
+// ViewController.m
+// Hello-World
+//
+// Copyright (c) 2013 TokBox, Inc. All rights reserved.
+//
+
+#import "ViewController.h"
+#import
+
+// *** Fill the following variables using your own Project info ***
+// *** https://dashboard.tokbox.com/projects ***
+// Replace with your OpenTok API key
+static NSString* const kApiKey = @"";
+// Replace with your generated session ID
+static NSString* const kSessionId = @"";
+// Replace with your generated token
+static NSString* const kToken = @"";
+
+@interface ViewController ()
+@property (nonatomic) OTSession *session;
+@property (nonatomic) OTPublisher *publisher;
+@property (nonatomic) OTSubscriber *subscriber;
+@end
+
+static double widgetHeight = 240;
+static double widgetWidth = 320;
+
+@interface customTransformer : NSObject
+
+- (UIImage *)resizeImage:(UIImage *)image toSize:(CGSize)size;
+
+@end
+
+
+@implementation customTransformer
+
+- (UIImage *)resizeImage:(UIImage *)image toSize:(CGSize)size {
+ UIGraphicsBeginImageContextWithOptions(size, NO, 0.0);
+ [image drawInRect:CGRectMake(0, 0, size.width, size.height)];
+ UIImage *resizedImage = UIGraphicsGetImageFromCurrentImageContext();
+ UIGraphicsEndImageContext();
+ return resizedImage;
+}
+
+- (void)transform:(nonnull OTVideoFrame *)videoFrame {
+
+ UIImage* image = [UIImage imageNamed:@"Vonage_Logo.png"];
+
+ uint32_t videoWidth = videoFrame.format.imageWidth;
+ uint32_t videoHeight = videoFrame.format.imageHeight;
+
+ // Calculate the desired size of the image
+ CGFloat desiredWidth = videoWidth / 8; // Adjust this value as needed
+ CGFloat desiredHeight = image.size.height * (desiredWidth / image.size.width);
+
+ // Resize the image to the desired size
+ UIImage *resizedImage = [self resizeImage:image toSize:CGSizeMake(desiredWidth, desiredHeight)];
+
+ // Get pointer to the Y plane
+ uint8_t* yPlane = [videoFrame getPlaneBinaryData:0];
+
+ // Create a CGContext from the Y plane
+ CGContextRef context = CGBitmapContextCreate(yPlane, videoWidth, videoHeight, 8, videoWidth, CGColorSpaceCreateDeviceGray(), kCGImageAlphaNone);
+
+ // Location of the image (in this case right bottom corner)
+ CGFloat x = videoWidth * 4/5;
+ CGFloat y = videoHeight * 1/5;
+
+ // Draw the resized image on top of the Y plane
+ CGRect rect = CGRectMake(x, y, desiredWidth, desiredHeight);
+ CGContextDrawImage(context, rect, resizedImage.CGImage);
+
+ CGContextRelease(context);
+}
+
+@end
+
+customTransformer* logoTransformer;
+
+@implementation ViewController
+
+UIButton *buttonVideoTransformerToggle;
+
+#pragma mark - View lifecycle
+
+- (void)viewDidLoad
+{
+ [super viewDidLoad];
+
+ // Step 1: As the view comes into the foreground, initialize a new instance
+ // of OTSession and begin the connection process.
+ _session = [[OTSession alloc] initWithApiKey:kApiKey
+ sessionId:kSessionId
+ delegate:self];
+ [self doConnect];
+}
+
+- (BOOL)prefersStatusBarHidden
+{
+ return YES;
+}
+
+- (BOOL)shouldAutorotate {
+ return UIUserInterfaceIdiomPhone != [[UIDevice currentDevice] userInterfaceIdiom];
+}
+#pragma mark - OpenTok methods
+
+/**
+ * Asynchronously begins the session connect process. Some time later, we will
+ * expect a delegate method to call us back with the results of this action.
+ */
+- (void)doConnect
+{
+ OTError *error = nil;
+
+ [_session connectWithToken:kToken error:&error];
+ if (error)
+ {
+ [self showAlert:[error localizedDescription]];
+ }
+}
+
+/**
+ * Sets up an instance of OTPublisher to use with this session. OTPubilsher
+ * binds to the device camera and microphone, and will provide A/V streams
+ * to the OpenTok session.
+ */
+- (void)doPublish
+{
+ OTPublisherSettings *settings = [[OTPublisherSettings alloc] init];
+ settings.name = [UIDevice currentDevice].name;
+ _publisher = [[OTPublisher alloc] initWithDelegate:self settings:settings];
+
+ OTError *error = nil;
+ [_session publish:_publisher error:&error];
+ if (error)
+ {
+ [self showAlert:[error localizedDescription]];
+ }
+
+ [self.view addSubview:_publisher.view];
+ [_publisher.view setFrame:CGRectMake(0, 0, widgetWidth, widgetHeight)];
+
+ // Configure toogle button
+ buttonVideoTransformerToggle = [UIButton buttonWithType:UIButtonTypeCustom];
+ buttonVideoTransformerToggle.frame = CGRectMake(widgetWidth - 65, 15, 50, 25);
+ buttonVideoTransformerToggle.layer.cornerRadius = 5.0;
+ [self.view addSubview:buttonVideoTransformerToggle];
+ [self.view bringSubviewToFront:buttonVideoTransformerToggle];
+ [buttonVideoTransformerToggle setTitle:@"set" forState:UIControlStateNormal];
+ buttonVideoTransformerToggle.titleLabel.font = [UIFont systemFontOfSize:12];
+ [buttonVideoTransformerToggle setTitleColor:[UIColor grayColor] forState:UIControlStateNormal];
+ buttonVideoTransformerToggle.backgroundColor = [UIColor whiteColor];
+ buttonVideoTransformerToggle.layer.borderWidth = 1.0; // Adjust the width as desired
+ buttonVideoTransformerToggle.layer.borderColor = [UIColor grayColor].CGColor;
+ [buttonVideoTransformerToggle addTarget:self action:@selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];
+
+}
+
+/**
+ * Cleans up the publisher and its view. At this point, the publisher should not
+ * be attached to the session any more.
+ */
+- (void)cleanupPublisher {
+ [_publisher.view removeFromSuperview];
+ _publisher = nil;
+ // this is a good place to notify the end-user that publishing has stopped.
+}
+
+/**
+ * Instantiates a subscriber for the given stream and asynchronously begins the
+ * process to begin receiving A/V content for this stream. Unlike doPublish,
+ * this method does not add the subscriber to the view hierarchy. Instead, we
+ * add the subscriber only after it has connected and begins receiving data.
+ */
+- (void)doSubscribe:(OTStream*)stream
+{
+ _subscriber = [[OTSubscriber alloc] initWithStream:stream delegate:self];
+
+ OTError *error = nil;
+ [_session subscribe:_subscriber error:&error];
+ if (error)
+ {
+ [self showAlert:[error localizedDescription]];
+ }
+}
+
+/**
+ * Cleans the subscriber from the view hierarchy, if any.
+ * NB: You do *not* have to call unsubscribe in your controller in response to
+ * a streamDestroyed event. Any subscribers (or the publisher) for a stream will
+ * be automatically removed from the session during cleanup of the stream.
+ */
+- (void)cleanupSubscriber
+{
+ [_subscriber.view removeFromSuperview];
+ _subscriber = nil;
+}
+
+# pragma mark - OTSession delegate callbacks
+
+- (void)sessionDidConnect:(OTSession*)session
+{
+ NSLog(@"sessionDidConnect (%@)", session.sessionId);
+
+ // Step 2: We have successfully connected, now instantiate a publisher and
+ // begin pushing A/V streams into OpenTok.
+ [self doPublish];
+}
+
+- (void)sessionDidDisconnect:(OTSession*)session
+{
+ NSString* alertMessage =
+ [NSString stringWithFormat:@"Session disconnected: (%@)",
+ session.sessionId];
+ NSLog(@"sessionDidDisconnect (%@)", alertMessage);
+}
+
+
+- (void)session:(OTSession*)mySession
+ streamCreated:(OTStream *)stream
+{
+ NSLog(@"session streamCreated (%@)", stream.streamId);
+
+ if (nil == _subscriber)
+ {
+ [self doSubscribe:stream];
+ }
+}
+
+- (void)session:(OTSession*)session
+streamDestroyed:(OTStream *)stream
+{
+ NSLog(@"session streamDestroyed (%@)", stream.streamId);
+
+ if ([_subscriber.stream.streamId isEqualToString:stream.streamId])
+ {
+ [self cleanupSubscriber];
+ }
+}
+
+- (void) session:(OTSession *)session
+connectionCreated:(OTConnection *)connection
+{
+ NSLog(@"session connectionCreated (%@)", connection.connectionId);
+}
+
+- (void) session:(OTSession *)session
+connectionDestroyed:(OTConnection *)connection
+{
+ NSLog(@"session connectionDestroyed (%@)", connection.connectionId);
+ if ([_subscriber.stream.connection.connectionId
+ isEqualToString:connection.connectionId])
+ {
+ [self cleanupSubscriber];
+ }
+}
+
+- (void) session:(OTSession*)session
+didFailWithError:(OTError*)error
+{
+ NSLog(@"didFailWithError: (%@)", error);
+}
+
+# pragma mark - OTSubscriber delegate callbacks
+
+- (void)subscriberDidConnectToStream:(OTSubscriberKit*)subscriber
+{
+ NSLog(@"subscriberDidConnectToStream (%@)",
+ subscriber.stream.connection.connectionId);
+ assert(_subscriber == subscriber);
+ [_subscriber.view setFrame:CGRectMake(0, widgetHeight, widgetWidth,
+ widgetHeight)];
+ [self.view addSubview:_subscriber.view];
+}
+
+- (void)subscriber:(OTSubscriberKit*)subscriber
+ didFailWithError:(OTError*)error
+{
+ NSLog(@"subscriber %@ didFailWithError %@",
+ subscriber.stream.streamId,
+ error);
+}
+
+# pragma mark - OTPublisher delegate callbacks
+
+- (void)publisher:(OTPublisherKit *)publisher
+ streamCreated:(OTStream *)stream
+{
+ NSLog(@"Publishing");
+}
+
+- (void)publisher:(OTPublisherKit*)publisher
+ streamDestroyed:(OTStream *)stream
+{
+ if ([_subscriber.stream.streamId isEqualToString:stream.streamId])
+ {
+ [self cleanupSubscriber];
+ }
+
+ [self cleanupPublisher];
+}
+
+- (void)publisher:(OTPublisherKit*)publisher
+ didFailWithError:(OTError*) error
+{
+ NSLog(@"publisher didFailWithError %@", error);
+ [self cleanupPublisher];
+}
+
+- (void)showAlert:(NSString *)string
+{
+ // show alertview on main UI
+ dispatch_async(dispatch_get_main_queue(), ^{
+ UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:@"OTError"
+ message:string
+ preferredStyle:UIAlertControllerStyleAlert];
+ [self presentViewController:alertVC animated:YES completion:nil];
+ });
+}
+
+bool isSet = false;
+
+- (void)buttonTapped:(UIButton *)sender {
+
+ if(!isSet) {
+ // Create background blur Vonage transformer
+ OTVideoTransformer *BackgroundBlur = [[OTVideoTransformer alloc] initWithName:@"BackgroundBlur" properties:@"{\"radius\":\"High\"}"];
+
+ // Create custom transformer
+ logoTransformer = [customTransformer alloc];
+ OTVideoTransformer *myCustomTransformer = [[OTVideoTransformer alloc] initWithName:@"logo" transformer:logoTransformer];
+
+ NSMutableArray * myVideoTransformers = [[NSMutableArray alloc] init];
+
+ [myVideoTransformers addObject:BackgroundBlur];
+ [myVideoTransformers addObject:myCustomTransformer];
+
+ // Set video transformers to publisher video stream
+ _publisher.videoTransformers = [[NSMutableArray alloc] initWithArray:myVideoTransformers];
+
+ [buttonVideoTransformerToggle setTitle:@"reset" forState:UIControlStateNormal];
+ isSet = true;
+ } else {
+ // Clear all transformers from video stream
+ _publisher.videoTransformers = [[NSArray alloc] init];
+
+ [buttonVideoTransformerToggle setTitle:@"set" forState:UIControlStateNormal];
+ isSet = false;
+ }
+
+}
+
+
+@end
diff --git a/Video-Transformers/Video-Transformers/Vonage_Logo.png b/Video-Transformers/Video-Transformers/Vonage_Logo.png
new file mode 100644
index 0000000000000000000000000000000000000000..2087713a8047a289115488e37061d0d6e2b85896
GIT binary patch
literal 38656
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