├── .gitignore ├── Podfile ├── Podfile.lock ├── README.md ├── VideoTransitionsSample.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── work.xcuserdatad │ │ └── WorkspaceSettings.xcsettings └── xcuserdata │ └── work.xcuserdatad │ └── xcschemes │ ├── VideoTransitionsSample.xcscheme │ └── xcschememanagement.plist ├── VideoTransitionsSample.xcworkspace └── contents.xcworkspacedata ├── VideoTransitionsSample ├── Base.lproj │ └── LaunchScreen.xib ├── Business layer │ ├── AutoReplayPlayer │ │ ├── VTSAutoReplayPlayer.h │ │ └── VTSAutoReplayPlayer.m │ ├── BusinessFacade │ │ ├── VTSBusinessFacade.h │ │ └── VTSBusinessFacade.m │ ├── FileManager │ │ └── FRSFileManager.swift │ ├── GPUImageWrapper │ │ └── FRSGPUImageWrapper.swift │ └── VideoEditor │ │ ├── APLVideoEditor..m │ │ ├── APLVideoEditor.h │ │ ├── Custom Compositors │ │ ├── APLCustomVideoCompositionInstruction.h │ │ ├── APLCustomVideoCompositionInstruction.m │ │ ├── APLCustomVideoCompositor.h │ │ └── APLCustomVideoCompositor.m │ │ └── OpenGL renderer │ │ ├── APLOpenGLRenderer.h │ │ ├── APLOpenGLRenderer.m │ │ └── Transition renderers │ │ ├── Fold │ │ ├── VTSFoldRenderer.h │ │ └── VTSFoldRenderer.m │ │ ├── Pinwheel │ │ ├── VTSPinwheelRenderer.h │ │ └── VTSPinwheelRenderer.m │ │ ├── SimpleFlip │ │ ├── VTSSimpleFlipRenderer.h │ │ └── VTSSimpleFlipRenderer.m │ │ ├── StarWipe │ │ ├── VTSStarWipeRenderer.h │ │ └── VTSStarWipeRenderer.m │ │ └── Wind │ │ ├── VTSWindRenderer.h │ │ └── VTSWindRenderer.m ├── Helpers │ ├── DurationStringFromTimeInterval │ │ ├── DurationStringFromTimeInterval.h │ │ └── DurationStringFromTimeIntervalString.m │ └── UIImagePickerController+DelegateBlocks │ │ ├── UIImagePickerController+DelegateBlocks.h │ │ └── UIImagePickerController+DelegateBlocks.m ├── Images.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Close.imageset │ │ ├── Cancel-100 (1)-1.png │ │ ├── Cancel-100 (1).png │ │ ├── Cancel-50 (1).png │ │ └── Contents.json │ ├── New Folder │ │ ├── Pause.imageset │ │ │ ├── Contents.json │ │ │ ├── Pause Filled-100-1.png │ │ │ ├── Pause Filled-100.png │ │ │ └── Pause Filled-50.png │ │ └── Play.imageset │ │ │ ├── Contents.json │ │ │ ├── Play Filled-100-1.png │ │ │ ├── Play Filled-100.png │ │ │ └── Play Filled-50.png │ ├── Preview.imageset │ │ ├── Contents.json │ │ ├── Movie-100 (1)-1.png │ │ ├── Movie-100 (1).png │ │ └── Movie-50 (1).png │ ├── Save.imageset │ │ ├── Contents.json │ │ ├── Download-100 (1)-1.png │ │ ├── Download-100 (1).png │ │ └── Download-50 (1).png │ └── TransitionBackgroundImage.imageset │ │ ├── Contents.json │ │ ├── fonstola.ru-55269-1.jpg │ │ ├── fonstola.ru-55269-2.jpg │ │ └── fonstola.ru-55269.jpg ├── Info.plist ├── PrefixHeader.pch ├── Resources │ ├── Storyboards │ │ └── VTSStoryboard.storyboard │ └── Videos │ │ ├── preview_video_1.mp4 │ │ └── preview_video_2.mp4 ├── UI layer │ ├── Custom elements │ │ ├── Buttons │ │ │ ├── TransitionButton │ │ │ │ ├── VTSTransitionButton.h │ │ │ │ └── VTSTransitionButton.m │ │ │ └── VideoButton │ │ │ │ ├── VTSVideoButton.h │ │ │ │ └── VTSVideoButton.m │ │ ├── IgnoreTouchesView │ │ │ ├── VTSIgnoreTouchesView.h │ │ │ └── VTSIgnoreTouchesView.m │ │ ├── PagedFlowView │ │ │ ├── PagedFlowView.h │ │ │ └── PagedFlowView.m │ │ └── PlayerView │ │ │ ├── VTSPlayerView.h │ │ │ └── VTSPlayerView.m │ └── ViewControllers │ │ ├── BaseViewController │ │ ├── VTSBaseViewController+ShwAlertOK.h │ │ ├── VTSBaseViewController+ShwAlertOK.m │ │ ├── VTSBaseViewController.h │ │ └── VTSBaseViewController.m │ │ ├── MainViewController │ │ ├── VTSMainViewController.h │ │ └── VTSMainViewController.m │ │ ├── PlayerViewController │ │ ├── VTSPlayerViewController.h │ │ └── VTSPlayerViewController.m │ │ └── TransitionsViewController │ │ ├── VTSTransitionsViewController.h │ │ └── VTSTransitionsViewController.m ├── VTSAppDelegate.h ├── VTSAppDelegate.m ├── VideoTransitionsSample-Bridging-Header.h └── main.m └── VideoTransitionsSampleTests ├── Info.plist └── VideoTransitionsSampleTests.m /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | *.xccheckout 14 | *.moved-aside 15 | DerivedData 16 | *.hmap 17 | *.ipa 18 | *.xcuserstate 19 | 20 | # CocoaPods 21 | # 22 | # We recommend against adding the Pods directory to your .gitignore. However 23 | # you should judge for yourself, the pros and cons are mentioned at: 24 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 25 | # 26 | Pods/ -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | source 'https://github.com/CocoaPods/Specs.git' 2 | platform :ios, '8.0' 3 | pod 'CocoaLumberjack' 4 | pod 'MBProgressHUD' 5 | pod 'GPUImage' 6 | pod 'UIAlertController+Blocks' -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - CocoaLumberjack (2.0.1): 3 | - CocoaLumberjack/Default 4 | - CocoaLumberjack/Extensions 5 | - CocoaLumberjack/Core (2.0.1) 6 | - CocoaLumberjack/Default (2.0.1): 7 | - CocoaLumberjack/Core 8 | - CocoaLumberjack/Extensions (2.0.1): 9 | - CocoaLumberjack/Default 10 | - GPUImage (0.1.7) 11 | - MBProgressHUD (0.9.1) 12 | - UIAlertController+Blocks (0.9) 13 | 14 | DEPENDENCIES: 15 | - CocoaLumberjack 16 | - GPUImage 17 | - MBProgressHUD 18 | - UIAlertController+Blocks 19 | 20 | SPEC CHECKSUMS: 21 | CocoaLumberjack: 019d1361244274a6138c788c6cb80baabc13fb8f 22 | GPUImage: 733a5f0fab92df9de1c37ba9df520a833ccb406d 23 | MBProgressHUD: c47f2c166c126cf2ce36498d80f33e754d4e93ad 24 | UIAlertController+Blocks: b48c673ff60726da468b32ab21bbc7683512d45b 25 | 26 | COCOAPODS: 0.34.4 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #iQueSoft iOS Demos 2 | 3 | Video transition demo 4 | 5 | #The MIT License (MIT) 6 | 7 | Copyright (c) 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in 17 | all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /VideoTransitionsSample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /VideoTransitionsSample.xcodeproj/project.xcworkspace/xcuserdata/work.xcuserdatad/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HasAskedToTakeAutomaticSnapshotBeforeSignificantChanges 6 | 7 | SnapshotAutomaticallyBeforeSignificantChanges 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /VideoTransitionsSample.xcodeproj/xcuserdata/work.xcuserdatad/xcschemes/VideoTransitionsSample.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 65 | 66 | 67 | 68 | 78 | 80 | 86 | 87 | 88 | 89 | 90 | 91 | 97 | 99 | 105 | 106 | 107 | 108 | 110 | 111 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /VideoTransitionsSample.xcodeproj/xcuserdata/work.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | VideoTransitionsSample.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 96268F9E1AD693F5000A77BD 16 | 17 | primary 18 | 19 | 20 | 96268FB71AD693F5000A77BD 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /VideoTransitionsSample.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /VideoTransitionsSample/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /VideoTransitionsSample/Business layer/AutoReplayPlayer/VTSAutoReplayPlayer.h: -------------------------------------------------------------------------------- 1 | // 2 | // VTSAutoReplayPlayer.h 3 | // VideoTransitionsSample 4 | // 5 | // Created by Ruslan Shevtsov on 4/15/15. 6 | // Copyright (c) 2015 iQueSoft. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface VTSAutoReplayPlayer : AVPlayer 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /VideoTransitionsSample/Business layer/AutoReplayPlayer/VTSAutoReplayPlayer.m: -------------------------------------------------------------------------------- 1 | // 2 | // VTSAutoReplayPlayer.m 3 | // VideoTransitionsSample 4 | // 5 | // Created by Ruslan Shevtsov on 4/15/15. 6 | // Copyright (c) 2015 iQueSoft. All rights reserved. 7 | // 8 | 9 | #import "VTSAutoReplayPlayer.h" 10 | 11 | @implementation VTSAutoReplayPlayer 12 | 13 | - (instancetype)init { 14 | self = [super init]; 15 | if (!self) { 16 | return nil; 17 | } 18 | 19 | self.volume = 0.0f; 20 | self.actionAtItemEnd = AVPlayerActionAtItemEndNone; 21 | 22 | [[NSNotificationCenter defaultCenter] addObserver:self 23 | selector:@selector(playerItemDidReachEnd:) 24 | name:AVPlayerItemDidPlayToEndTimeNotification 25 | object:nil]; 26 | 27 | return self; 28 | } 29 | 30 | - (void)dealloc { 31 | [[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:nil]; 32 | } 33 | 34 | #pragma mark - 35 | #pragma mark Notification 36 | 37 | - (void)playerItemDidReachEnd:(NSNotification *)notification { 38 | if (notification.object != self.currentItem) { 39 | return; 40 | } 41 | [self pause]; 42 | [self.currentItem seekToTime:kCMTimeZero]; 43 | [self play]; 44 | } 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /VideoTransitionsSample/Business layer/BusinessFacade/VTSBusinessFacade.h: -------------------------------------------------------------------------------- 1 | // 2 | // VTSBusinessFacade.h 3 | // VideoTransitionsSample 4 | // 5 | // Created by Ruslan Shevtsov on 4/13/15. 6 | // Copyright (c) 2015 iQueSoft. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | // Video processor 12 | #import "APLVideoEditor.h" 13 | 14 | @class AVPlayerItem; 15 | @class AVURLAsset; 16 | @class AVPlayer; 17 | 18 | typedef void (^VTSExportMoviCompletionBlock)(BOOL isDone, NSError *anError); 19 | 20 | @interface VTSBusinessFacade : NSObject 21 | 22 | @property (nonatomic, strong) NSURL *firstVideoURL; 23 | @property (nonatomic, strong) NSURL *secondVideoURL; 24 | 25 | @property (nonatomic, assign) VTSTransitionType transitionType; 26 | 27 | @property (nonatomic, strong) AVPlayer *firstVideoPreviewPlayer; 28 | @property (nonatomic, strong) AVPlayer *secondVideoPreviewPlayer; 29 | @property (nonatomic, strong) AVPlayer *transitionPreviewPlayer; 30 | 31 | #pragma mark - 32 | #pragma mark Singleton 33 | 34 | + (instancetype)sharedBusinessFacade; 35 | 36 | #pragma mark - 37 | #pragma mark @publick 38 | 39 | - (void)exportToMovieWithCompletion:(VTSExportMoviCompletionBlock)aCompletionBlock; 40 | 41 | - (AVPlayer *)playerWithCurrentVideo; 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /VideoTransitionsSample/Business layer/BusinessFacade/VTSBusinessFacade.m: -------------------------------------------------------------------------------- 1 | // 2 | // VTSBusinessFacade.m 3 | // VideoTransitionsSample 4 | // 5 | // Created by Ruslan Shevtsov on 4/13/15. 6 | // Copyright (c) 2015 iQueSoft. All rights reserved. 7 | // 8 | 9 | #import "VTSBusinessFacade.h" 10 | 11 | // Frameworks 12 | #import 13 | #import 14 | 15 | // Player 16 | #import "VTSAutoReplayPlayer.h" 17 | 18 | @interface VTSBusinessFacade () 19 | 20 | @property (copy) VTSExportMoviCompletionBlock exportMoviCompletionBlock; 21 | 22 | // Preview transition 23 | @property (nonatomic, strong) APLVideoEditor *previewTransitionEditor; 24 | 25 | @end 26 | 27 | @implementation VTSBusinessFacade 28 | 29 | #pragma mark - 30 | #pragma mark Singleton 31 | 32 | + (instancetype)sharedBusinessFacade { 33 | static VTSBusinessFacade *_sharedBusinessFacade = nil; 34 | static dispatch_once_t onceToken; 35 | dispatch_once(&onceToken, ^{ 36 | _sharedBusinessFacade = [VTSBusinessFacade new]; 37 | }); 38 | 39 | return _sharedBusinessFacade; 40 | } 41 | 42 | - (instancetype)init { 43 | self = [super init]; 44 | if (!self) { 45 | return nil; 46 | } 47 | 48 | self.firstVideoPreviewPlayer = [VTSAutoReplayPlayer new]; 49 | self.secondVideoPreviewPlayer = [VTSAutoReplayPlayer new]; 50 | self.transitionPreviewPlayer = [VTSAutoReplayPlayer new]; 51 | 52 | self.previewTransitionEditor = [self buildVideoEditorWithURLs:@[[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"preview_video_1" ofType:@"mp4"]], 53 | [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"preview_video_2" ofType:@"mp4"]]]]; 54 | 55 | return self; 56 | } 57 | 58 | #pragma mark - 59 | #pragma mark Setters 60 | 61 | - (void)setFirstVideoURL:(NSURL *)firstVideoURL { 62 | _firstVideoURL = firstVideoURL; 63 | 64 | [self.firstVideoPreviewPlayer pause]; 65 | [self.firstVideoPreviewPlayer replaceCurrentItemWithPlayerItem:[AVPlayerItem playerItemWithURL:firstVideoURL]]; 66 | [self.firstVideoPreviewPlayer play]; 67 | } 68 | 69 | - (void)setSecondVideoURL:(NSURL *)secondVideoURL { 70 | _secondVideoURL = secondVideoURL; 71 | 72 | [self.secondVideoPreviewPlayer pause]; 73 | [self.secondVideoPreviewPlayer replaceCurrentItemWithPlayerItem:[AVPlayerItem playerItemWithURL:secondVideoURL]]; 74 | [self.secondVideoPreviewPlayer play]; 75 | 76 | } 77 | 78 | - (void)setTransitionType:(VTSTransitionType)transitionType { 79 | _transitionType = transitionType; 80 | 81 | self.previewTransitionEditor.transitionType = _transitionType; 82 | 83 | [self.previewTransitionEditor buildCompositionObjectsForPreviewTransition:YES]; 84 | 85 | AVPlayerItem *playerItem = [self.previewTransitionEditor playerItem]; 86 | 87 | [self.transitionPreviewPlayer pause]; 88 | [self.transitionPreviewPlayer replaceCurrentItemWithPlayerItem:playerItem]; 89 | [self.transitionPreviewPlayer play]; 90 | } 91 | 92 | #pragma mark - 93 | #pragma mark @public 94 | 95 | - (void)exportToMovieWithCompletion:(VTSExportMoviCompletionBlock)aCompletionBlock { 96 | 97 | if (self.firstVideoURL == nil || self.secondVideoURL == nil) { 98 | aCompletionBlock(NO, nil); 99 | return; 100 | } 101 | 102 | self.exportMoviCompletionBlock = aCompletionBlock; 103 | 104 | APLVideoEditor *editor = [self buildVideoEditorWithURLs:@[self.firstVideoURL, self.secondVideoURL]]; 105 | 106 | __weak __typeof(self)weakSelf = self; 107 | 108 | editor.transitionType = self.transitionType; 109 | [editor buildCompositionObjects]; 110 | 111 | AVAssetExportSession *session = [editor assetExportSessionWithPreset:AVAssetExportPreset1280x720]; 112 | 113 | session.outputURL = [[FRSFileManager new] urlForVideo]; 114 | session.outputFileType = AVFileTypeQuickTimeMovie; 115 | 116 | [session exportAsynchronouslyWithCompletionHandler:^{ 117 | dispatch_async(dispatch_get_main_queue(), ^{ 118 | [weakSelf exportCompleted:session]; 119 | }); 120 | }]; 121 | } 122 | 123 | - (AVPlayer *)playerWithCurrentVideo { 124 | if (self.firstVideoURL == nil || self.secondVideoURL == nil) { 125 | return nil; 126 | } 127 | 128 | APLVideoEditor *editor = [self buildVideoEditorWithURLs:@[self.firstVideoURL, self.secondVideoURL]]; 129 | 130 | editor.transitionType = self.transitionType; 131 | [editor buildCompositionObjects]; 132 | 133 | AVPlayerItem *playerItem = [editor playerItem]; 134 | 135 | AVPlayer *player = [[AVPlayer alloc] initWithPlayerItem:playerItem]; 136 | 137 | return player; 138 | } 139 | 140 | #pragma mark - 141 | #pragma mark @private 142 | 143 | - (void)exportCompleted:(AVAssetExportSession *)session { 144 | NSURL *outputURL = session.outputURL; 145 | 146 | if ( session.status != AVAssetExportSessionStatusCompleted ) { 147 | DDLogDebug(@"exportSession error:%@", session.error); 148 | [self reportError:session.error]; 149 | } 150 | 151 | if ( session.status != AVAssetExportSessionStatusCompleted ) { 152 | self.exportMoviCompletionBlock(NO, nil); 153 | return; 154 | } 155 | 156 | ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init]; 157 | [library writeVideoAtPathToSavedPhotosAlbum:outputURL completionBlock:^(NSURL *assetURL, NSError *error) { 158 | if (error == NULL) { 159 | self.exportMoviCompletionBlock(YES, nil); 160 | } else { 161 | DDLogDebug(@"writeVideoToAssestsLibrary failed: %@", error); 162 | self.exportMoviCompletionBlock(NO, error); 163 | [self reportError:error]; 164 | } 165 | }]; 166 | } 167 | 168 | - (void)reportError:(NSError *)error { 169 | dispatch_async(dispatch_get_main_queue(), ^{ 170 | if (error) { 171 | UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:[error localizedDescription] 172 | message:[error localizedRecoverySuggestion] 173 | delegate:nil 174 | cancelButtonTitle:NSLocalizedString(@"OK", nil) 175 | otherButtonTitles:nil]; 176 | 177 | [alertView show]; 178 | } 179 | }); 180 | } 181 | 182 | #pragma mark - 183 | #pragma mark Video editor builder 184 | 185 | - (APLVideoEditor *)buildVideoEditorWithURLs:(NSArray *)anURLs { 186 | 187 | APLVideoEditor *videoEditor = [[APLVideoEditor alloc] init]; 188 | 189 | AVURLAsset *firstPreviewAsset = [AVURLAsset assetWithURL:anURLs[0]]; 190 | AVURLAsset *secondPreviewAsset = [AVURLAsset assetWithURL:anURLs[1]]; 191 | 192 | NSMutableArray *clips = [NSMutableArray array]; 193 | 194 | [clips addObject:firstPreviewAsset]; 195 | [clips addObject:secondPreviewAsset]; 196 | 197 | videoEditor.clips = clips; 198 | 199 | NSMutableArray *timeRanges = [NSMutableArray array]; 200 | 201 | AVAssetTrack *videoTrack1 = [[firstPreviewAsset tracksWithMediaType:AVMediaTypeVideo] lastObject]; 202 | [timeRanges addObject:[NSValue valueWithCMTimeRange:videoTrack1.timeRange]]; 203 | AVAssetTrack *videoTrack2 = [[secondPreviewAsset tracksWithMediaType:AVMediaTypeVideo] lastObject]; 204 | [timeRanges addObject:[NSValue valueWithCMTimeRange:videoTrack2.timeRange]]; 205 | 206 | videoEditor.clipTimeRanges = timeRanges; 207 | 208 | return videoEditor; 209 | } 210 | 211 | @end 212 | -------------------------------------------------------------------------------- /VideoTransitionsSample/Business layer/FileManager/FRSFileManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FRSFileManager.swift 3 | // FiltersRecordSample 4 | // 5 | // Created by Ruslan Shevtsov on 4/7/15. 6 | // Copyright (c) 2015 Work. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class FRSFileManager: NSObject { 12 | 13 | func urlForVideo() -> NSURL? { 14 | 15 | if (self.checkVideosDirectory() == false) { 16 | return nil 17 | } 18 | 19 | var urlForVideo:NSURL? = nil 20 | let timestamp:Double = NSDate().timeIntervalSince1970 * 1000 21 | let path:String = NSTemporaryDirectory() + "Videos/video-" + String(format:"%f", timestamp) + ".MOV" 22 | urlForVideo = NSURL(fileURLWithPath: path) 23 | return urlForVideo 24 | 25 | } 26 | 27 | func clearVideosDirectory() -> Bool { 28 | 29 | let path:String = NSTemporaryDirectory() + "/Videos/" 30 | let isDirectory:Bool = NSFileManager.defaultManager().fileExistsAtPath(path) 31 | 32 | if (isDirectory == false) { 33 | return true; 34 | } 35 | 36 | do { 37 | try NSFileManager.defaultManager().removeItemAtPath(path) 38 | return true 39 | } catch _ { 40 | return false 41 | } 42 | 43 | } 44 | 45 | func removeFile(fileURL:NSURL?) -> NSError? { 46 | 47 | if (fileURL == nil) { 48 | return nil 49 | } 50 | 51 | var error: NSError? 52 | 53 | if (NSFileManager.defaultManager().fileExistsAtPath(fileURL!.path!)) { 54 | do { 55 | try NSFileManager.defaultManager().removeItemAtPath(fileURL!.path!) 56 | } catch let error1 as NSError { 57 | error = error1 58 | } 59 | } 60 | 61 | return error 62 | } 63 | 64 | func checkFileExists(fileURL:NSURL?) -> Bool { 65 | if (fileURL == nil) { 66 | return false 67 | } 68 | return NSFileManager.defaultManager().fileExistsAtPath(fileURL!.path!) 69 | } 70 | 71 | // MARK: private 72 | 73 | private func checkVideosDirectory() -> Bool { 74 | 75 | var path:String = NSTemporaryDirectory() + "/Videos/" 76 | let isDirectory:Bool = NSFileManager.defaultManager().fileExistsAtPath(path) 77 | 78 | if (isDirectory == true) { 79 | return true; 80 | } 81 | 82 | path = NSTemporaryDirectory() + "/Videos" 83 | let url: NSURL = NSURL(fileURLWithPath: path) 84 | do { 85 | try NSFileManager.defaultManager().createDirectoryAtURL(url, withIntermediateDirectories: true, attributes: nil) 86 | return true 87 | } catch _ { 88 | return false 89 | } 90 | } 91 | } -------------------------------------------------------------------------------- /VideoTransitionsSample/Business layer/GPUImageWrapper/FRSGPUImageWrapper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FRSGPUImageWrapper.swift 3 | // FiltersRecordSample 4 | // 5 | // Created by Ruslan Shevtsov on 4/8/15. 6 | // Copyright (c) 2015 Work. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | // Frameworks 12 | import AVFoundation 13 | 14 | enum FRSFilter: Int { 15 | case Empty 16 | case Brightness 17 | case Sepia 18 | case Grayscale 19 | case Inverted 20 | case Pixellated 21 | case PolkaDot 22 | case Blur 23 | case FiltersCount 24 | } 25 | 26 | protocol FRSGPUImageWrapperDelegate:class { 27 | func recordingProgress(progress:Double) 28 | func recordIsFinished() 29 | } 30 | 31 | class FRSGPUImageWrapper: NSObject { 32 | 33 | var previewView:GPUImageView! 34 | var videoCamera:GPUImageVideoCamera? 35 | var filtersGroup:GPUImageFilterGroup! 36 | var movieWriter:GPUImageMovieWriter! 37 | var sessionPreset:String! 38 | 39 | weak var delegate:FRSGPUImageWrapperDelegate? 40 | 41 | var currentVideoURL:NSURL? 42 | var filterType:FRSFilter = .Empty { 43 | didSet { 44 | self.updateFilter(self.filterForType(filterType)) 45 | } 46 | } 47 | var recordintTimer: NSTimer? 48 | var isRecorded:Bool = false 49 | 50 | // MARK: Setup camera 51 | 52 | init(previewView:GPUImageView, orientation:UIInterfaceOrientation) { 53 | super.init() 54 | self.previewView = previewView 55 | self.sessionPreset = AVCaptureSessionPreset1280x720 56 | self.videoCamera = GPUImageVideoCamera(sessionPreset: self.sessionPreset, cameraPosition: .Back) 57 | self.videoCamera!.outputImageOrientation = orientation 58 | self.videoCamera!.startCameraCapture() 59 | } 60 | 61 | init(previewView:GPUImageView, orientation:UIInterfaceOrientation, sessionPreset:String) { 62 | super.init() 63 | self.previewView = previewView 64 | self.sessionPreset = sessionPreset 65 | self.videoCamera = GPUImageVideoCamera(sessionPreset: self.sessionPreset, cameraPosition: .Back) 66 | self.videoCamera!.outputImageOrientation = orientation 67 | self.videoCamera!.startCameraCapture() 68 | } 69 | 70 | // MARK: Blur 71 | 72 | func startBlur() { 73 | if (self.videoCamera == nil) { 74 | self.videoCamera = GPUImageVideoCamera(sessionPreset: self.sessionPreset, cameraPosition: .Back) 75 | self.videoCamera!.startCameraCapture() 76 | } 77 | self.filterType = .Blur 78 | } 79 | 80 | func stopBlur() { 81 | self.videoCamera!.stopCameraCapture() 82 | self.videoCamera = nil 83 | } 84 | 85 | // MARK: Update filter 86 | 87 | func filterForType(filterType:FRSFilter) -> GPUImageOutput? { 88 | switch (filterType) { 89 | case .Brightness: 90 | let filter:GPUImageBrightnessFilter = GPUImageBrightnessFilter() 91 | filter.brightness = 0.5 92 | return filter 93 | 94 | case .Sepia: 95 | return GPUImageSepiaFilter() 96 | 97 | case .Grayscale: 98 | return GPUImageGrayscaleFilter() 99 | 100 | case .Inverted: 101 | return GPUImageColorInvertFilter() 102 | 103 | case .Pixellated: 104 | let filter:GPUImagePixellateFilter = GPUImagePixellateFilter() 105 | filter.fractionalWidthOfAPixel = 0.01 106 | return filter 107 | 108 | case .PolkaDot: 109 | return GPUImagePolkaDotFilter() 110 | 111 | case .Blur: 112 | return GPUImageiOSBlurFilter() 113 | 114 | default: 115 | break 116 | } 117 | 118 | return nil 119 | } 120 | 121 | private func updateFilter(filter:GPUImageOutput?) { 122 | 123 | self.videoCamera!.removeAllTargets() 124 | 125 | if (self.filtersGroup != nil) { 126 | self.filtersGroup.removeAllTargets() 127 | self.filtersGroup = nil 128 | } 129 | 130 | if (filter == nil) { 131 | self.videoCamera!.addTarget(self.previewView) 132 | return 133 | } 134 | 135 | self.filtersGroup = GPUImageFilterGroup() 136 | 137 | self.filtersGroup.addFilter(filter!) 138 | self.filtersGroup.initialFilters = [filter!] 139 | self.filtersGroup.terminalFilter = filter! 140 | 141 | self.filtersGroup.addTarget(self.previewView) 142 | 143 | self.videoCamera!.addTarget(self.filtersGroup) 144 | } 145 | 146 | // MARK: Record process 147 | 148 | func startRecord() { 149 | 150 | self.isRecorded = true 151 | 152 | let url: NSURL? = FRSFileManager().urlForVideo() 153 | 154 | FRSFileManager().removeFile(self.currentVideoURL) 155 | self.currentVideoURL = url 156 | 157 | self.movieWriter = GPUImageMovieWriter(movieURL: url, size: CGSizeMake(1200, 720)) 158 | self.videoCamera!.audioEncodingTarget = self.movieWriter 159 | 160 | if (self.filtersGroup != nil) { 161 | self.filtersGroup.addTarget(self.movieWriter) 162 | } else { 163 | self.videoCamera!.addTarget(self.movieWriter) 164 | } 165 | 166 | self.movieWriter.startRecording() 167 | 168 | self.startRecordTimer() 169 | } 170 | 171 | func stopRecord() { 172 | self.movieWriter.finishRecordingWithCompletionHandler({ [weak self] () -> Void in 173 | 174 | dispatch_async(dispatch_get_main_queue()) { 175 | 176 | self?.stopRecordTimer() 177 | 178 | if (self?.filtersGroup != nil) { 179 | self?.filtersGroup.removeTarget(self?.movieWriter) 180 | } else { 181 | self?.videoCamera!.removeTarget(self?.movieWriter) 182 | } 183 | self?.videoCamera!.audioEncodingTarget = nil 184 | self?.movieWriter = nil 185 | 186 | self?.isRecorded = false 187 | 188 | self?.delegate?.recordIsFinished() 189 | } 190 | }) 191 | } 192 | 193 | // MARK: Timer 194 | 195 | func startRecordTimer() { 196 | self.recordintTimer = NSTimer(timeInterval: 0.1, target: self, selector: "timerDidFire:", userInfo: nil, repeats: true) 197 | NSRunLoop.currentRunLoop().addTimer(self.recordintTimer!, forMode: NSRunLoopCommonModes) 198 | } 199 | 200 | func stopRecordTimer() { 201 | self.recordintTimer?.invalidate() 202 | self.recordintTimer = nil 203 | } 204 | 205 | func timerDidFire(timer:NSTimer!) { 206 | let progress:Double = CMTimeGetSeconds(self.movieWriter.duration) 207 | self.delegate?.recordingProgress(progress) 208 | } 209 | 210 | } 211 | -------------------------------------------------------------------------------- /VideoTransitionsSample/Business layer/VideoEditor/APLVideoEditor..m: -------------------------------------------------------------------------------- 1 | /* 2 | File: APLVideoEditor.m 3 | Abstract: Simple editor sets up an AVMutableComposition using supplied clips and time ranges. It also sets up an AVVideoComposition to perform custom compositor rendering. 4 | Version: 1.2 5 | 6 | Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple 7 | Inc. ("Apple") in consideration of your agreement to the following 8 | terms, and your use, installation, modification or redistribution of 9 | this Apple software constitutes acceptance of these terms. If you do 10 | not agree with these terms, please do not use, install, modify or 11 | redistribute this Apple software. 12 | 13 | In consideration of your agreement to abide by the following terms, and 14 | subject to these terms, Apple grants you a personal, non-exclusive 15 | license, under Apple's copyrights in this original Apple software (the 16 | "Apple Software"), to use, reproduce, modify and redistribute the Apple 17 | Software, with or without modifications, in source and/or binary forms; 18 | provided that if you redistribute the Apple Software in its entirety and 19 | without modifications, you must retain this notice and the following 20 | text and disclaimers in all such redistributions of the Apple Software. 21 | Neither the name, trademarks, service marks or logos of Apple Inc. may 22 | be used to endorse or promote products derived from the Apple Software 23 | without specific prior written permission from Apple. Except as 24 | expressly stated in this notice, no other rights or licenses, express or 25 | implied, are granted by Apple herein, including but not limited to any 26 | patent rights that may be infringed by your derivative works or by other 27 | works in which the Apple Software may be incorporated. 28 | 29 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE 30 | MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION 31 | THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS 32 | FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND 33 | OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. 34 | 35 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL 36 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 37 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 38 | INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, 39 | MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED 40 | AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), 41 | STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE 42 | POSSIBILITY OF SUCH DAMAGE. 43 | 44 | Copyright (C) 2014 Apple Inc. All Rights Reserved. 45 | 46 | */ 47 | 48 | #import "APLVideoEditor.h" 49 | #import "APLCustomVideoCompositor.h" 50 | #import "APLCustomVideoCompositionInstruction.h" 51 | #import 52 | #import 53 | 54 | 55 | @interface APLVideoEditor () 56 | 57 | @property (nonatomic, strong) AVMutableComposition *composition; 58 | @property (nonatomic, strong) AVMutableVideoComposition *videoComposition; 59 | 60 | @end 61 | 62 | 63 | 64 | @implementation APLVideoEditor 65 | 66 | 67 | - (void)buildTransitionComposition:(AVMutableComposition *)composition andVideoComposition:(AVMutableVideoComposition *)videoComposition previewTransition:(BOOL)previewTransition 68 | { 69 | CMTime nextClipStartTime = kCMTimeZero; 70 | NSInteger i; 71 | NSUInteger clipsCount = [_clips count]; 72 | 73 | // Make transitionDuration no greater than half the shortest clip duration. 74 | CMTime transitionDuration = self.transitionDuration; 75 | for (i = 0; i < clipsCount; i++ ) { 76 | NSValue *clipTimeRange = [_clipTimeRanges objectAtIndex:i]; 77 | if (clipTimeRange) { 78 | CMTime halfClipDuration = [clipTimeRange CMTimeRangeValue].duration; 79 | if (previewTransition == NO) { 80 | halfClipDuration.timescale *= 2; // You can halve a rational by doubling its denominator. 81 | } 82 | transitionDuration = CMTimeMinimum(transitionDuration, halfClipDuration); 83 | } 84 | } 85 | 86 | // Add two video tracks and two audio tracks. 87 | AVMutableCompositionTrack *compositionVideoTracks[2]; 88 | AVMutableCompositionTrack *compositionAudioTracks[2]; 89 | compositionVideoTracks[0] = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid]; 90 | compositionVideoTracks[1] = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid]; 91 | compositionAudioTracks[0] = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid]; 92 | compositionAudioTracks[1] = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid]; 93 | 94 | CMTimeRange *passThroughTimeRanges = alloca(sizeof(CMTimeRange) * clipsCount); 95 | CMTimeRange *transitionTimeRanges = alloca(sizeof(CMTimeRange) * clipsCount); 96 | 97 | // Place clips into alternating video & audio tracks in composition, overlapped by transitionDuration. 98 | for (i = 0; i < clipsCount; i++ ) { 99 | NSInteger alternatingIndex = i % 2; // alternating targets: 0, 1, 0, 1, ... 100 | AVURLAsset *asset = [_clips objectAtIndex:i]; 101 | NSValue *clipTimeRange = [_clipTimeRanges objectAtIndex:i]; 102 | CMTimeRange timeRangeInAsset; 103 | if (clipTimeRange) 104 | timeRangeInAsset = [clipTimeRange CMTimeRangeValue]; 105 | else 106 | timeRangeInAsset = CMTimeRangeMake(kCMTimeZero, [asset duration]); 107 | 108 | AVAssetTrack *clipVideoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0]; 109 | [compositionVideoTracks[alternatingIndex] insertTimeRange:timeRangeInAsset ofTrack:clipVideoTrack atTime:nextClipStartTime error:nil]; 110 | 111 | if ([[asset tracksWithMediaType:AVMediaTypeAudio] count]) { 112 | AVAssetTrack *clipAudioTrack = [[asset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0]; 113 | [compositionAudioTracks[alternatingIndex] insertTimeRange:timeRangeInAsset ofTrack:clipAudioTrack atTime:nextClipStartTime error:nil]; 114 | } 115 | 116 | // Remember the time range in which this clip should pass through. 117 | // First clip ends with a transition. 118 | // Second clip begins with a transition. 119 | // Exclude that transition from the pass through time ranges. 120 | passThroughTimeRanges[i] = CMTimeRangeMake(nextClipStartTime, timeRangeInAsset.duration); 121 | if (i > 0) { 122 | passThroughTimeRanges[i].start = CMTimeAdd(passThroughTimeRanges[i].start, transitionDuration); 123 | passThroughTimeRanges[i].duration = CMTimeSubtract(passThroughTimeRanges[i].duration, transitionDuration); 124 | } 125 | if (i+1 < clipsCount) { 126 | passThroughTimeRanges[i].duration = CMTimeSubtract(passThroughTimeRanges[i].duration, transitionDuration); 127 | } 128 | 129 | // The end of this clip will overlap the start of the next by transitionDuration. 130 | // (Note: this arithmetic falls apart if timeRangeInAsset.duration < 2 * transitionDuration.) 131 | nextClipStartTime = CMTimeAdd(nextClipStartTime, timeRangeInAsset.duration); 132 | nextClipStartTime = CMTimeSubtract(nextClipStartTime, transitionDuration); 133 | 134 | // Remember the time range for the transition to the next item. 135 | if (i+1 < clipsCount) { 136 | transitionTimeRanges[i] = CMTimeRangeMake(nextClipStartTime, transitionDuration); 137 | } 138 | } 139 | 140 | // Set up the video composition to perform cross dissolve or diagonal wipe transitions between clips. 141 | NSMutableArray *instructions = [NSMutableArray array]; 142 | 143 | // Cycle between "pass through A", "transition from A to B", "pass through B" 144 | for (i = 0; i < clipsCount; i++ ) { 145 | NSInteger alternatingIndex = i % 2; // alternating targets 146 | 147 | if (videoComposition.customVideoCompositorClass) { 148 | APLCustomVideoCompositionInstruction *videoInstruction = [[APLCustomVideoCompositionInstruction alloc] initPassThroughTrackID:compositionVideoTracks[alternatingIndex].trackID forTimeRange:passThroughTimeRanges[i]]; 149 | [instructions addObject:videoInstruction]; 150 | } 151 | else { 152 | // Pass through clip i. 153 | AVMutableVideoCompositionInstruction *passThroughInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction]; 154 | passThroughInstruction.timeRange = passThroughTimeRanges[i]; 155 | AVMutableVideoCompositionLayerInstruction *passThroughLayer = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:compositionVideoTracks[alternatingIndex]]; 156 | 157 | passThroughInstruction.layerInstructions = [NSArray arrayWithObject:passThroughLayer]; 158 | [instructions addObject:passThroughInstruction]; 159 | } 160 | 161 | if (i+1 < clipsCount) { 162 | // Add transition from clip i to clip i+1. 163 | 164 | if (videoComposition.customVideoCompositorClass) { 165 | APLCustomVideoCompositionInstruction *videoInstruction = [[APLCustomVideoCompositionInstruction alloc] initTransitionWithSourceTrackIDs:@[[NSNumber numberWithInt:compositionVideoTracks[0].trackID], [NSNumber numberWithInt:compositionVideoTracks[1].trackID]] forTimeRange:transitionTimeRanges[i]]; 166 | if (alternatingIndex == 0) { 167 | // First track -> Foreground track while compositing 168 | videoInstruction.foregroundTrackID = compositionVideoTracks[alternatingIndex].trackID; 169 | // Second track -> Background track while compositing 170 | videoInstruction.backgroundTrackID = compositionVideoTracks[1-alternatingIndex].trackID; 171 | } 172 | 173 | [instructions addObject:videoInstruction]; 174 | } 175 | else { 176 | AVMutableVideoCompositionInstruction *transitionInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction]; 177 | transitionInstruction.timeRange = transitionTimeRanges[i]; 178 | AVMutableVideoCompositionLayerInstruction *fromLayer = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:compositionVideoTracks[alternatingIndex]]; 179 | AVMutableVideoCompositionLayerInstruction *toLayer = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:compositionVideoTracks[1-alternatingIndex]]; 180 | 181 | transitionInstruction.layerInstructions = [NSArray arrayWithObjects:fromLayer, toLayer, nil]; 182 | [instructions addObject:transitionInstruction]; 183 | } 184 | } 185 | } 186 | 187 | videoComposition.instructions = instructions; 188 | } 189 | 190 | - (void)buildCompositionObjectsForPreviewTransition:(BOOL)previewTransition { 191 | if ( (_clips == nil) || [_clips count] == 0 ) { 192 | self.composition = nil; 193 | self.videoComposition = nil; 194 | return; 195 | } 196 | 197 | CGSize videoSize = [[_clips objectAtIndex:0] naturalSize]; 198 | AVMutableComposition *composition = [AVMutableComposition composition]; 199 | AVMutableVideoComposition *videoComposition = nil; 200 | 201 | composition.naturalSize = videoSize; 202 | 203 | // With transitions: 204 | // Place clips into alternating video & audio tracks in composition, overlapped by transitionDuration. 205 | // Set up the video composition to cycle between "pass through A", "transition from A to B", 206 | // "pass through B". 207 | 208 | videoComposition = [AVMutableVideoComposition videoComposition]; 209 | 210 | // TODO: set customVideoCompositorClass 211 | switch (self.transitionType) { 212 | case VTSTransitionDefault: 213 | videoComposition.customVideoCompositorClass = [APLCustomVideoCompositor class]; 214 | break; 215 | case VTSTransitionPinwheel: 216 | videoComposition.customVideoCompositorClass = [VTSPinwheelVideoCompositor class]; 217 | break; 218 | case VTSTransitionSimpleFlip: 219 | videoComposition.customVideoCompositorClass = [VTSSimpleFlipVideoCompositor class]; 220 | break; 221 | case VTSTransitionWind: 222 | videoComposition.customVideoCompositorClass = [VTSWindVideoCompositor class]; 223 | break; 224 | case VTSTransitionFold: 225 | videoComposition.customVideoCompositorClass = [VTSFoldVideoCompositor class]; 226 | break; 227 | case VTSTransitionStarWipe: 228 | videoComposition.customVideoCompositorClass = [VTSStarWipeVideoCompositor class]; 229 | break; 230 | case VTSTransitionsCount: break; 231 | } 232 | 233 | [self buildTransitionComposition:composition andVideoComposition:videoComposition previewTransition:previewTransition]; 234 | 235 | if (videoComposition) { 236 | // Every videoComposition needs these properties to be set: 237 | videoComposition.frameDuration = CMTimeMake(1, 30); // 30 fps 238 | videoComposition.renderSize = videoSize; 239 | } 240 | 241 | self.composition = composition; 242 | self.videoComposition = videoComposition; 243 | } 244 | 245 | - (void)buildCompositionObjects 246 | { 247 | [self buildCompositionObjectsForPreviewTransition:NO]; 248 | } 249 | 250 | - (AVAssetExportSession*)assetExportSessionWithPreset:(NSString*)presetName 251 | { 252 | AVAssetExportSession *session = [[AVAssetExportSession alloc] initWithAsset:self.composition presetName:presetName]; 253 | session.videoComposition = self.videoComposition; 254 | return session; 255 | } 256 | 257 | - (AVPlayerItem *)playerItem 258 | { 259 | AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset:self.composition]; 260 | playerItem.videoComposition = self.videoComposition; 261 | 262 | return playerItem; 263 | } 264 | 265 | @end 266 | -------------------------------------------------------------------------------- /VideoTransitionsSample/Business layer/VideoEditor/APLVideoEditor.h: -------------------------------------------------------------------------------- 1 | /* 2 | File: APLVideoEditor.h 3 | Abstract: Simple editor sets up an AVMutableComposition using supplied clips and time ranges. It also sets up an AVVideoComposition to perform custom compositor rendering. 4 | Version: 1.2 5 | 6 | Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple 7 | Inc. ("Apple") in consideration of your agreement to the following 8 | terms, and your use, installation, modification or redistribution of 9 | this Apple software constitutes acceptance of these terms. If you do 10 | not agree with these terms, please do not use, install, modify or 11 | redistribute this Apple software. 12 | 13 | In consideration of your agreement to abide by the following terms, and 14 | subject to these terms, Apple grants you a personal, non-exclusive 15 | license, under Apple's copyrights in this original Apple software (the 16 | "Apple Software"), to use, reproduce, modify and redistribute the Apple 17 | Software, with or without modifications, in source and/or binary forms; 18 | provided that if you redistribute the Apple Software in its entirety and 19 | without modifications, you must retain this notice and the following 20 | text and disclaimers in all such redistributions of the Apple Software. 21 | Neither the name, trademarks, service marks or logos of Apple Inc. may 22 | be used to endorse or promote products derived from the Apple Software 23 | without specific prior written permission from Apple. Except as 24 | expressly stated in this notice, no other rights or licenses, express or 25 | implied, are granted by Apple herein, including but not limited to any 26 | patent rights that may be infringed by your derivative works or by other 27 | works in which the Apple Software may be incorporated. 28 | 29 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE 30 | MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION 31 | THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS 32 | FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND 33 | OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. 34 | 35 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL 36 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 37 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 38 | INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, 39 | MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED 40 | AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), 41 | STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE 42 | POSSIBILITY OF SUCH DAMAGE. 43 | 44 | Copyright (C) 2014 Apple Inc. All Rights Reserved. 45 | 46 | */ 47 | 48 | #import 49 | 50 | #import 51 | 52 | typedef NS_ENUM(NSUInteger, VTSTransitionType) { 53 | VTSTransitionDefault, 54 | VTSTransitionPinwheel, 55 | VTSTransitionSimpleFlip, 56 | VTSTransitionWind, 57 | VTSTransitionFold, 58 | VTSTransitionStarWipe, 59 | VTSTransitionsCount 60 | }; 61 | 62 | @class AVPlayerItem, AVAssetExportSession; 63 | 64 | @interface APLVideoEditor : NSObject 65 | 66 | // Set these properties before building the composition objects. 67 | @property (nonatomic, copy) NSArray *clips; // array of AVURLAssets 68 | @property (nonatomic, copy) NSArray *clipTimeRanges; // array of CMTimeRanges stored in NSValues. 69 | 70 | @property (nonatomic) VTSTransitionType transitionType; 71 | @property (nonatomic) CMTime transitionDuration; 72 | 73 | // Builds the composition and videoComposition 74 | - (void)buildCompositionObjects; 75 | 76 | - (void)buildCompositionObjectsForPreviewTransition:(BOOL)previewTransition; 77 | 78 | - (AVAssetExportSession*)assetExportSessionWithPreset:(NSString*)presetName; 79 | 80 | - (AVPlayerItem *)playerItem; 81 | 82 | @end 83 | -------------------------------------------------------------------------------- /VideoTransitionsSample/Business layer/VideoEditor/Custom Compositors/APLCustomVideoCompositionInstruction.h: -------------------------------------------------------------------------------- 1 | /* 2 | File: APLCustomVideoCompositionInstruction.h 3 | Abstract: Custom video composition instruction class implementing AVVideoCompositionInstruction protocol. 4 | Version: 1.2 5 | 6 | Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple 7 | Inc. ("Apple") in consideration of your agreement to the following 8 | terms, and your use, installation, modification or redistribution of 9 | this Apple software constitutes acceptance of these terms. If you do 10 | not agree with these terms, please do not use, install, modify or 11 | redistribute this Apple software. 12 | 13 | In consideration of your agreement to abide by the following terms, and 14 | subject to these terms, Apple grants you a personal, non-exclusive 15 | license, under Apple's copyrights in this original Apple software (the 16 | "Apple Software"), to use, reproduce, modify and redistribute the Apple 17 | Software, with or without modifications, in source and/or binary forms; 18 | provided that if you redistribute the Apple Software in its entirety and 19 | without modifications, you must retain this notice and the following 20 | text and disclaimers in all such redistributions of the Apple Software. 21 | Neither the name, trademarks, service marks or logos of Apple Inc. may 22 | be used to endorse or promote products derived from the Apple Software 23 | without specific prior written permission from Apple. Except as 24 | expressly stated in this notice, no other rights or licenses, express or 25 | implied, are granted by Apple herein, including but not limited to any 26 | patent rights that may be infringed by your derivative works or by other 27 | works in which the Apple Software may be incorporated. 28 | 29 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE 30 | MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION 31 | THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS 32 | FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND 33 | OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. 34 | 35 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL 36 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 37 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 38 | INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, 39 | MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED 40 | AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), 41 | STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE 42 | POSSIBILITY OF SUCH DAMAGE. 43 | 44 | Copyright (C) 2014 Apple Inc. All Rights Reserved. 45 | 46 | */ 47 | 48 | #import 49 | #import 50 | 51 | @interface APLCustomVideoCompositionInstruction : NSObject 52 | 53 | @property CMPersistentTrackID foregroundTrackID; 54 | @property CMPersistentTrackID backgroundTrackID; 55 | 56 | - (id)initPassThroughTrackID:(CMPersistentTrackID)passthroughTrackID forTimeRange:(CMTimeRange)timeRange; 57 | - (id)initTransitionWithSourceTrackIDs:(NSArray*)sourceTrackIDs forTimeRange:(CMTimeRange)timeRange; 58 | 59 | @end 60 | -------------------------------------------------------------------------------- /VideoTransitionsSample/Business layer/VideoEditor/Custom Compositors/APLCustomVideoCompositionInstruction.m: -------------------------------------------------------------------------------- 1 | /* 2 | File: APLCustomVideoCompositionInstruction.m 3 | Abstract: Custom video composition instruction class implementing AVVideoCompositionInstruction protocol. 4 | Version: 1.2 5 | 6 | Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple 7 | Inc. ("Apple") in consideration of your agreement to the following 8 | terms, and your use, installation, modification or redistribution of 9 | this Apple software constitutes acceptance of these terms. If you do 10 | not agree with these terms, please do not use, install, modify or 11 | redistribute this Apple software. 12 | 13 | In consideration of your agreement to abide by the following terms, and 14 | subject to these terms, Apple grants you a personal, non-exclusive 15 | license, under Apple's copyrights in this original Apple software (the 16 | "Apple Software"), to use, reproduce, modify and redistribute the Apple 17 | Software, with or without modifications, in source and/or binary forms; 18 | provided that if you redistribute the Apple Software in its entirety and 19 | without modifications, you must retain this notice and the following 20 | text and disclaimers in all such redistributions of the Apple Software. 21 | Neither the name, trademarks, service marks or logos of Apple Inc. may 22 | be used to endorse or promote products derived from the Apple Software 23 | without specific prior written permission from Apple. Except as 24 | expressly stated in this notice, no other rights or licenses, express or 25 | implied, are granted by Apple herein, including but not limited to any 26 | patent rights that may be infringed by your derivative works or by other 27 | works in which the Apple Software may be incorporated. 28 | 29 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE 30 | MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION 31 | THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS 32 | FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND 33 | OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. 34 | 35 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL 36 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 37 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 38 | INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, 39 | MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED 40 | AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), 41 | STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE 42 | POSSIBILITY OF SUCH DAMAGE. 43 | 44 | Copyright (C) 2014 Apple Inc. All Rights Reserved. 45 | 46 | */ 47 | 48 | #import "APLCustomVideoCompositionInstruction.h" 49 | 50 | @implementation APLCustomVideoCompositionInstruction 51 | 52 | @synthesize timeRange = _timeRange; 53 | @synthesize enablePostProcessing = _enablePostProcessing; 54 | @synthesize containsTweening = _containsTweening; 55 | @synthesize requiredSourceTrackIDs = _requiredSourceTrackIDs; 56 | @synthesize passthroughTrackID = _passthroughTrackID; 57 | 58 | - (id)initPassThroughTrackID:(CMPersistentTrackID)passthroughTrackID forTimeRange:(CMTimeRange)timeRange 59 | { 60 | self = [super init]; 61 | if (self) { 62 | _passthroughTrackID = passthroughTrackID; 63 | _requiredSourceTrackIDs = nil; 64 | _timeRange = timeRange; 65 | _containsTweening = FALSE; 66 | _enablePostProcessing = FALSE; 67 | } 68 | 69 | return self; 70 | } 71 | 72 | - (id)initTransitionWithSourceTrackIDs:(NSArray *)sourceTrackIDs forTimeRange:(CMTimeRange)timeRange 73 | { 74 | self = [super init]; 75 | if (self) { 76 | _requiredSourceTrackIDs = sourceTrackIDs; 77 | _passthroughTrackID = kCMPersistentTrackID_Invalid; 78 | _timeRange = timeRange; 79 | _containsTweening = TRUE; 80 | _enablePostProcessing = FALSE; 81 | } 82 | 83 | return self; 84 | } 85 | 86 | @end 87 | -------------------------------------------------------------------------------- /VideoTransitionsSample/Business layer/VideoEditor/Custom Compositors/APLCustomVideoCompositor.h: -------------------------------------------------------------------------------- 1 | /* 2 | File: APLCustomVideoCompositor.h 3 | Abstract: Custom video compositor class implementing the AVVideoCompositing protocol. 4 | Version: 1.2 5 | 6 | Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple 7 | Inc. ("Apple") in consideration of your agreement to the following 8 | terms, and your use, installation, modification or redistribution of 9 | this Apple software constitutes acceptance of these terms. If you do 10 | not agree with these terms, please do not use, install, modify or 11 | redistribute this Apple software. 12 | 13 | In consideration of your agreement to abide by the following terms, and 14 | subject to these terms, Apple grants you a personal, non-exclusive 15 | license, under Apple's copyrights in this original Apple software (the 16 | "Apple Software"), to use, reproduce, modify and redistribute the Apple 17 | Software, with or without modifications, in source and/or binary forms; 18 | provided that if you redistribute the Apple Software in its entirety and 19 | without modifications, you must retain this notice and the following 20 | text and disclaimers in all such redistributions of the Apple Software. 21 | Neither the name, trademarks, service marks or logos of Apple Inc. may 22 | be used to endorse or promote products derived from the Apple Software 23 | without specific prior written permission from Apple. Except as 24 | expressly stated in this notice, no other rights or licenses, express or 25 | implied, are granted by Apple herein, including but not limited to any 26 | patent rights that may be infringed by your derivative works or by other 27 | works in which the Apple Software may be incorporated. 28 | 29 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE 30 | MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION 31 | THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS 32 | FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND 33 | OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. 34 | 35 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL 36 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 37 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 38 | INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, 39 | MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED 40 | AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), 41 | STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE 42 | POSSIBILITY OF SUCH DAMAGE. 43 | 44 | Copyright (C) 2014 Apple Inc. All Rights Reserved. 45 | 46 | */ 47 | 48 | #import 49 | #import 50 | 51 | @interface APLCustomVideoCompositor : NSObject 52 | 53 | @end 54 | 55 | @interface VTSPinwheelVideoCompositor : APLCustomVideoCompositor 56 | 57 | @end 58 | 59 | @interface VTSSimpleFlipVideoCompositor : APLCustomVideoCompositor 60 | 61 | @end 62 | 63 | @interface VTSWindVideoCompositor : APLCustomVideoCompositor 64 | 65 | @end 66 | 67 | @interface VTSFoldVideoCompositor : APLCustomVideoCompositor 68 | 69 | @end 70 | 71 | @interface VTSStarWipeVideoCompositor : APLCustomVideoCompositor 72 | 73 | @end 74 | -------------------------------------------------------------------------------- /VideoTransitionsSample/Business layer/VideoEditor/Custom Compositors/APLCustomVideoCompositor.m: -------------------------------------------------------------------------------- 1 | /* 2 | File: APLCustomVideoCompositor.m 3 | Abstract: Custom video compositor class implementing the AVVideoCompositing protocol. 4 | Version: 1.2 5 | 6 | Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple 7 | Inc. ("Apple") in consideration of your agreement to the following 8 | terms, and your use, installation, modification or redistribution of 9 | this Apple software constitutes acceptance of these terms. If you do 10 | not agree with these terms, please do not use, install, modify or 11 | redistribute this Apple software. 12 | 13 | In consideration of your agreement to abide by the following terms, and 14 | subject to these terms, Apple grants you a personal, non-exclusive 15 | license, under Apple's copyrights in this original Apple software (the 16 | "Apple Software"), to use, reproduce, modify and redistribute the Apple 17 | Software, with or without modifications, in source and/or binary forms; 18 | provided that if you redistribute the Apple Software in its entirety and 19 | without modifications, you must retain this notice and the following 20 | text and disclaimers in all such redistributions of the Apple Software. 21 | Neither the name, trademarks, service marks or logos of Apple Inc. may 22 | be used to endorse or promote products derived from the Apple Software 23 | without specific prior written permission from Apple. Except as 24 | expressly stated in this notice, no other rights or licenses, express or 25 | implied, are granted by Apple herein, including but not limited to any 26 | patent rights that may be infringed by your derivative works or by other 27 | works in which the Apple Software may be incorporated. 28 | 29 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE 30 | MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION 31 | THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS 32 | FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND 33 | OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. 34 | 35 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL 36 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 37 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 38 | INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, 39 | MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED 40 | AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), 41 | STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE 42 | POSSIBILITY OF SUCH DAMAGE. 43 | 44 | Copyright (C) 2014 Apple Inc. All Rights Reserved. 45 | 46 | */ 47 | 48 | #import "APLCustomVideoCompositor.h" 49 | #import "APLCustomVideoCompositionInstruction.h" 50 | #import "APLOpenGLRenderer.h" 51 | 52 | #import 53 | 54 | // Renderers 55 | #import "VTSPinwheelRenderer.h" 56 | #import "VTSSimpleFlipRenderer.h" 57 | #import "VTSWindRenderer.h" 58 | #import "VTSFoldRenderer.h" 59 | #import "VTSStarWipeRenderer.h" 60 | 61 | @interface APLCustomVideoCompositor() 62 | { 63 | BOOL _shouldCancelAllRequests; 64 | BOOL _renderContextDidChange; 65 | dispatch_queue_t _renderingQueue; 66 | dispatch_queue_t _renderContextQueue; 67 | AVVideoCompositionRenderContext* _renderContext; 68 | CVPixelBufferRef _previousBuffer; 69 | } 70 | 71 | @property (nonatomic, strong) APLOpenGLRenderer *oglRenderer; 72 | 73 | @end 74 | 75 | // ============ VTSPinwheelVideoCompositor ============ 76 | 77 | @implementation VTSPinwheelVideoCompositor 78 | 79 | - (id)init 80 | { 81 | self = [super init]; 82 | 83 | if (self) { 84 | self.oglRenderer = [[VTSPinwheelRenderer alloc] init]; 85 | } 86 | 87 | return self; 88 | } 89 | 90 | @end 91 | 92 | // ============ VTSSimpleFlipVideoCompositor ============ 93 | 94 | @implementation VTSSimpleFlipVideoCompositor 95 | 96 | - (id)init 97 | { 98 | self = [super init]; 99 | 100 | if (self) { 101 | self.oglRenderer = [[VTSSimpleFlipRenderer alloc] init]; 102 | } 103 | 104 | return self; 105 | } 106 | 107 | @end 108 | 109 | // ============ VTSWindVideoCompositor ============ 110 | 111 | @implementation VTSWindVideoCompositor 112 | 113 | - (id)init 114 | { 115 | self = [super init]; 116 | 117 | if (self) { 118 | self.oglRenderer = [[VTSWindRenderer alloc] init]; 119 | } 120 | 121 | return self; 122 | } 123 | 124 | @end 125 | 126 | // ============ VTSFoldVideoCompositor ============ 127 | 128 | @implementation VTSFoldVideoCompositor 129 | 130 | - (id)init 131 | { 132 | self = [super init]; 133 | 134 | if (self) { 135 | self.oglRenderer = [[VTSFoldRenderer alloc] init]; 136 | } 137 | 138 | return self; 139 | } 140 | 141 | @end 142 | 143 | // ============ VTSStarWipeVideoCompositor ============ 144 | 145 | @implementation VTSStarWipeVideoCompositor 146 | 147 | - (id)init 148 | { 149 | self = [super init]; 150 | 151 | if (self) { 152 | self.oglRenderer = [[VTSStarWipeRenderer alloc] init]; 153 | } 154 | 155 | return self; 156 | } 157 | 158 | @end 159 | 160 | @implementation APLCustomVideoCompositor 161 | 162 | #pragma mark - AVVideoCompositing protocol 163 | 164 | - (id)init 165 | { 166 | self = [super init]; 167 | if (self) 168 | { 169 | 170 | self.oglRenderer = [[APLOpenGLRenderer alloc] init]; 171 | _renderingQueue = dispatch_queue_create("com.apple.aplcustomvideocompositor.renderingqueue", DISPATCH_QUEUE_SERIAL); 172 | _renderContextQueue = dispatch_queue_create("com.apple.aplcustomvideocompositor.rendercontextqueue", DISPATCH_QUEUE_SERIAL); 173 | _previousBuffer = nil; 174 | _renderContextDidChange = NO; 175 | } 176 | return self; 177 | } 178 | 179 | - (NSDictionary *)sourcePixelBufferAttributes 180 | { 181 | return @{ (NSString *)kCVPixelBufferPixelFormatTypeKey : [NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA], 182 | (NSString*)kCVPixelBufferOpenGLESCompatibilityKey : [NSNumber numberWithBool:YES]}; 183 | } 184 | 185 | - (NSDictionary *)requiredPixelBufferAttributesForRenderContext 186 | { 187 | return @{ (NSString *)kCVPixelBufferPixelFormatTypeKey : [NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA], 188 | (NSString*)kCVPixelBufferOpenGLESCompatibilityKey : [NSNumber numberWithBool:YES]}; 189 | } 190 | 191 | - (void)renderContextChanged:(AVVideoCompositionRenderContext *)newRenderContext 192 | { 193 | dispatch_sync(_renderContextQueue, ^() { 194 | _renderContext = newRenderContext; 195 | _renderContextDidChange = YES; 196 | }); 197 | } 198 | 199 | - (void)startVideoCompositionRequest:(AVAsynchronousVideoCompositionRequest *)request 200 | { 201 | @autoreleasepool { 202 | dispatch_async(_renderingQueue,^() { 203 | 204 | // Check if all pending requests have been cancelled 205 | if (_shouldCancelAllRequests) { 206 | [request finishCancelledRequest]; 207 | } else { 208 | NSError *err = nil; 209 | // Get the next rendererd pixel buffer 210 | CVPixelBufferRef resultPixels = [self newRenderedPixelBufferForRequest:request error:&err]; 211 | 212 | if (resultPixels) { 213 | // The resulting pixelbuffer from OpenGL renderer is passed along to the request 214 | [request finishWithComposedVideoFrame:resultPixels]; 215 | CFRelease(resultPixels); 216 | } else { 217 | [request finishWithError:err]; 218 | } 219 | } 220 | }); 221 | } 222 | } 223 | 224 | - (void)cancelAllPendingVideoCompositionRequests 225 | { 226 | // pending requests will call finishCancelledRequest, those already rendering will call finishWithComposedVideoFrame 227 | _shouldCancelAllRequests = YES; 228 | 229 | dispatch_barrier_async(_renderingQueue, ^() { 230 | // start accepting requests again 231 | _shouldCancelAllRequests = NO; 232 | }); 233 | } 234 | 235 | #pragma mark - Utilities 236 | 237 | static Float64 factorForTimeInRange(CMTime time, CMTimeRange range) /* 0.0 -> 1.0 */ 238 | { 239 | CMTime elapsed = CMTimeSubtract(time, range.start); 240 | return CMTimeGetSeconds(elapsed) / CMTimeGetSeconds(range.duration); 241 | } 242 | 243 | - (CVPixelBufferRef)newRenderedPixelBufferForRequest:(AVAsynchronousVideoCompositionRequest *)request error:(NSError **)errOut 244 | { 245 | CVPixelBufferRef dstPixels = nil; 246 | 247 | // tweenFactor indicates how far within that timeRange are we rendering this frame. This is normalized to vary between 0.0 and 1.0. 248 | // 0.0 indicates the time at first frame in that videoComposition timeRange 249 | // 1.0 indicates the time at last frame in that videoComposition timeRange 250 | float tweenFactor = factorForTimeInRange(request.compositionTime, request.videoCompositionInstruction.timeRange); 251 | 252 | APLCustomVideoCompositionInstruction *currentInstruction = request.videoCompositionInstruction; 253 | 254 | // Source pixel buffers are used as inputs while rendering the transition 255 | CVPixelBufferRef foregroundSourceBuffer = [request sourceFrameByTrackID:currentInstruction.foregroundTrackID]; 256 | CVPixelBufferRef backgroundSourceBuffer = [request sourceFrameByTrackID:currentInstruction.backgroundTrackID]; 257 | 258 | // Destination pixel buffer into which we render the output 259 | dstPixels = [_renderContext newPixelBuffer]; 260 | 261 | // Recompute normalized render transform everytime the render context changes 262 | if (_renderContextDidChange) { 263 | // The renderTransform returned by the renderContext is in X: [0, w] and Y: [0, h] coordinate system 264 | // But since in this sample we render using OpenGLES which has its coordinate system between [-1, 1] we compute a normalized transform 265 | CGSize renderSize = _renderContext.size; 266 | CGSize destinationSize = CGSizeMake(CVPixelBufferGetWidth(dstPixels), CVPixelBufferGetHeight(dstPixels)); 267 | CGAffineTransform renderContextTransform = {renderSize.width/2, 0, 0, renderSize.height/2, renderSize.width/2, renderSize.height/2}; 268 | CGAffineTransform destinationTransform = {2/destinationSize.width, 0, 0, 2/destinationSize.height, -1, -1}; 269 | CGAffineTransform normalizedRenderTransform = CGAffineTransformConcat(CGAffineTransformConcat(renderContextTransform, _renderContext.renderTransform), destinationTransform); 270 | _oglRenderer.renderTransform = normalizedRenderTransform; 271 | 272 | _renderContextDidChange = NO; 273 | } 274 | 275 | [_oglRenderer renderPixelBuffer:dstPixels usingForegroundSourceBuffer:foregroundSourceBuffer andBackgroundSourceBuffer:backgroundSourceBuffer forTweenFactor:tweenFactor]; 276 | 277 | return dstPixels; 278 | } 279 | 280 | @end 281 | -------------------------------------------------------------------------------- /VideoTransitionsSample/Business layer/VideoEditor/OpenGL renderer/APLOpenGLRenderer.h: -------------------------------------------------------------------------------- 1 | /* 2 | File: APLOpenGLRenderer.h 3 | Abstract: OpenGL base class renderer sets up an EAGLContext for rendering, it also loads, compiles and links the vertex and fragment shaders for both the Y and UV planes. 4 | Version: 1.2 5 | 6 | Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple 7 | Inc. ("Apple") in consideration of your agreement to the following 8 | terms, and your use, installation, modification or redistribution of 9 | this Apple software constitutes acceptance of these terms. If you do 10 | not agree with these terms, please do not use, install, modify or 11 | redistribute this Apple software. 12 | 13 | In consideration of your agreement to abide by the following terms, and 14 | subject to these terms, Apple grants you a personal, non-exclusive 15 | license, under Apple's copyrights in this original Apple software (the 16 | "Apple Software"), to use, reproduce, modify and redistribute the Apple 17 | Software, with or without modifications, in source and/or binary forms; 18 | provided that if you redistribute the Apple Software in its entirety and 19 | without modifications, you must retain this notice and the following 20 | text and disclaimers in all such redistributions of the Apple Software. 21 | Neither the name, trademarks, service marks or logos of Apple Inc. may 22 | be used to endorse or promote products derived from the Apple Software 23 | without specific prior written permission from Apple. Except as 24 | expressly stated in this notice, no other rights or licenses, express or 25 | implied, are granted by Apple herein, including but not limited to any 26 | patent rights that may be infringed by your derivative works or by other 27 | works in which the Apple Software may be incorporated. 28 | 29 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE 30 | MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION 31 | THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS 32 | FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND 33 | OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. 34 | 35 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL 36 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 37 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 38 | INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, 39 | MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED 40 | AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), 41 | STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE 42 | POSSIBILITY OF SUCH DAMAGE. 43 | 44 | Copyright (C) 2014 Apple Inc. All Rights Reserved. 45 | 46 | */ 47 | 48 | #import 49 | #import 50 | #import 51 | #import 52 | 53 | #define STRINGIZE(x) #x 54 | #define STRINGIZE2(x) STRINGIZE(x) 55 | #define SHADER_STRING(text) @ STRINGIZE2(text) 56 | 57 | enum 58 | { 59 | UNIFORM_RGB_FROM, 60 | UNIFORM_RGB_TO, 61 | UNIFORM_RENDER_TRANSFORM_RGB, 62 | UNIFORM_RENDER_PROGRESS_RGB, 63 | NUM_UNIFORMS 64 | }; 65 | GLint uniforms[NUM_UNIFORMS]; 66 | 67 | enum 68 | { 69 | ATTRIB_VERTEX_RGB, 70 | ATTRIB_TEXCOORD_RGB, 71 | NUM_ATTRIBUTES 72 | }; 73 | 74 | @interface APLOpenGLRenderer : NSObject 75 | 76 | @property GLuint programRGB; 77 | @property CGAffineTransform renderTransform; 78 | @property CVOpenGLESTextureCacheRef videoTextureCache; 79 | @property EAGLContext *currentContext; 80 | @property GLuint offscreenBufferHandle; 81 | 82 | - (CVOpenGLESTextureRef)rgbTextureForPixelBuffer:(CVPixelBufferRef)pixelBuffer; 83 | - (void)renderPixelBuffer:(CVPixelBufferRef)destinationPixelBuffer usingForegroundSourceBuffer:(CVPixelBufferRef)foregroundPixelBuffer andBackgroundSourceBuffer:(CVPixelBufferRef)backgroundPixelBuffer forTweenFactor:(float)tween; 84 | 85 | #pragma mark - 86 | #pragma mark Base methods for extension 87 | 88 | - (NSString *)fragmentShaderString; 89 | 90 | @end 91 | -------------------------------------------------------------------------------- /VideoTransitionsSample/Business layer/VideoEditor/OpenGL renderer/APLOpenGLRenderer.m: -------------------------------------------------------------------------------- 1 | /* 2 | File: APLOpenGLRenderer.m 3 | Abstract: OpenGL base class renderer sets up an EAGLContext for rendering, it also loads, compiles and links the vertex and fragment shaders for both the Y and UV planes. 4 | Version: 1.2 5 | 6 | Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple 7 | Inc. ("Apple") in consideration of your agreement to the following 8 | terms, and your use, installation, modification or redistribution of 9 | this Apple software constitutes acceptance of these terms. If you do 10 | not agree with these terms, please do not use, install, modify or 11 | redistribute this Apple software. 12 | 13 | In consideration of your agreement to abide by the following terms, and 14 | subject to these terms, Apple grants you a personal, non-exclusive 15 | license, under Apple's copyrights in this original Apple software (the 16 | "Apple Software"), to use, reproduce, modify and redistribute the Apple 17 | Software, with or without modifications, in source and/or binary forms; 18 | provided that if you redistribute the Apple Software in its entirety and 19 | without modifications, you must retain this notice and the following 20 | text and disclaimers in all such redistributions of the Apple Software. 21 | Neither the name, trademarks, service marks or logos of Apple Inc. may 22 | be used to endorse or promote products derived from the Apple Software 23 | without specific prior written permission from Apple. Except as 24 | expressly stated in this notice, no other rights or licenses, express or 25 | implied, are granted by Apple herein, including but not limited to any 26 | patent rights that may be infringed by your derivative works or by other 27 | works in which the Apple Software may be incorporated. 28 | 29 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE 30 | MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION 31 | THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS 32 | FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND 33 | OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. 34 | 35 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL 36 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 37 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 38 | INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, 39 | MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED 40 | AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), 41 | STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE 42 | POSSIBILITY OF SUCH DAMAGE. 43 | 44 | Copyright (C) 2014 Apple Inc. All Rights Reserved. 45 | 46 | */ 47 | 48 | #import "APLOpenGLRenderer.h" 49 | 50 | NSString *const kPassThroughVertexShader = SHADER_STRING 51 | ( 52 | attribute vec4 position; 53 | attribute vec2 texCoord; 54 | uniform mat4 renderTransform; 55 | varying vec2 texCoordVarying; 56 | void main() 57 | { 58 | gl_Position = position * renderTransform; 59 | texCoordVarying = texCoord; 60 | } 61 | 62 | ); 63 | 64 | NSString *const kPassThroughFragmentShaderRGB = SHADER_STRING 65 | ( 66 | 67 | varying highp vec2 texCoordVarying; 68 | uniform highp float progress; 69 | uniform sampler2D from; 70 | uniform sampler2D to; 71 | 72 | void main() 73 | { 74 | highp vec2 p = texCoordVarying; 75 | gl_FragColor = mix(texture2D(from, p), texture2D(to, p), progress); 76 | } 77 | 78 | ); 79 | 80 | @interface APLOpenGLRenderer () 81 | 82 | - (void)setupOffscreenRenderContext; 83 | 84 | - (BOOL)loadShaders; 85 | - (BOOL)compileShader:(GLuint *)shader type:(GLenum)type source:(NSString *)source; 86 | - (BOOL)linkProgram:(GLuint)prog; 87 | - (BOOL)validateProgram:(GLuint)prog; 88 | 89 | @end 90 | 91 | @implementation APLOpenGLRenderer 92 | 93 | - (id)init 94 | { 95 | self = [super init]; 96 | if(self) { 97 | _currentContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; 98 | [EAGLContext setCurrentContext:_currentContext]; 99 | 100 | [self setupOffscreenRenderContext]; 101 | [self loadShaders]; 102 | 103 | [EAGLContext setCurrentContext:nil]; 104 | } 105 | 106 | return self; 107 | } 108 | 109 | - (void)dealloc 110 | { 111 | if (_videoTextureCache) { 112 | CFRelease(_videoTextureCache); 113 | } 114 | if (_offscreenBufferHandle) { 115 | glDeleteFramebuffers(1, &_offscreenBufferHandle); 116 | _offscreenBufferHandle = 0; 117 | } 118 | } 119 | 120 | - (void)renderPixelBuffer:(CVPixelBufferRef)destinationPixelBuffer usingForegroundSourceBuffer:(CVPixelBufferRef)foregroundPixelBuffer andBackgroundSourceBuffer:(CVPixelBufferRef)backgroundPixelBuffer forTweenFactor:(float)tween 121 | { 122 | [EAGLContext setCurrentContext:self.currentContext]; 123 | 124 | if (foregroundPixelBuffer != NULL || backgroundPixelBuffer != NULL) { 125 | 126 | CVOpenGLESTextureRef foregroundRGBTexture = [self rgbTextureForPixelBuffer:foregroundPixelBuffer]; 127 | 128 | CVOpenGLESTextureRef backgroundRGBTexture = [self rgbTextureForPixelBuffer:backgroundPixelBuffer]; 129 | 130 | CVOpenGLESTextureRef destRGBTexture = [self rgbTextureForPixelBuffer:destinationPixelBuffer]; 131 | 132 | glUseProgram(self.programRGB); 133 | 134 | // Set the render transform 135 | GLfloat preferredRenderTransform [] = { 136 | self.renderTransform.a, self.renderTransform.b, self.renderTransform.tx, 0.0, 137 | self.renderTransform.c, self.renderTransform.d, self.renderTransform.ty, 0.0, 138 | 0.0, 0.0, 1.0, 0.0, 139 | 0.0, 0.0, 0.0, 1.0, 140 | }; 141 | 142 | glUniformMatrix4fv(uniforms[UNIFORM_RENDER_TRANSFORM_RGB], 1, GL_FALSE, preferredRenderTransform); 143 | 144 | glBindFramebuffer(GL_FRAMEBUFFER, self.offscreenBufferHandle); 145 | glViewport(0, 0, (int)CVPixelBufferGetWidth(destinationPixelBuffer), (int)CVPixelBufferGetHeight(destinationPixelBuffer)); 146 | 147 | // Y planes of foreground and background frame are used to render the Y plane of the destination frame 148 | glActiveTexture(GL_TEXTURE0); 149 | glBindTexture(CVOpenGLESTextureGetTarget(foregroundRGBTexture), CVOpenGLESTextureGetName(foregroundRGBTexture)); 150 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 151 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 152 | glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 153 | glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 154 | 155 | glActiveTexture(GL_TEXTURE1); 156 | glBindTexture(CVOpenGLESTextureGetTarget(backgroundRGBTexture), CVOpenGLESTextureGetName(backgroundRGBTexture)); 157 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 158 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 159 | glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 160 | glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 161 | 162 | // Attach the destination texture as a color attachment to the off screen frame buffer 163 | glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, CVOpenGLESTextureGetTarget(destRGBTexture), CVOpenGLESTextureGetName(destRGBTexture), 0); 164 | 165 | GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); 166 | 167 | if (status != GL_FRAMEBUFFER_COMPLETE) { 168 | DDLogDebug(@"Failed to make complete framebuffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER)); 169 | goto bail; 170 | } 171 | 172 | glClearColor(0.0f, 0.0f, 0.0f, 1.0f); 173 | glClear(GL_COLOR_BUFFER_BIT); 174 | 175 | GLfloat quadVertexData1 [] = { 176 | -1.0, 1.0, 177 | 1.0, 1.0, 178 | -1.0, -1.0, 179 | 1.0, -1.0, 180 | }; 181 | 182 | // texture data varies from 0 -> 1, whereas vertex data varies from -1 -> 1 183 | GLfloat quadTextureData1 [] = { 184 | 0.5 + quadVertexData1[0]/2, 0.5 + quadVertexData1[1]/2, 185 | 0.5 + quadVertexData1[2]/2, 0.5 + quadVertexData1[3]/2, 186 | 0.5 + quadVertexData1[4]/2, 0.5 + quadVertexData1[5]/2, 187 | 0.5 + quadVertexData1[6]/2, 0.5 + quadVertexData1[7]/2, 188 | }; 189 | 190 | glUniform1i(uniforms[UNIFORM_RGB_FROM], 0); 191 | glUniform1i(uniforms[UNIFORM_RGB_TO], 1); 192 | glUniform1f(uniforms[UNIFORM_RENDER_PROGRESS_RGB], tween); 193 | 194 | glVertexAttribPointer(ATTRIB_VERTEX_RGB, 2, GL_FLOAT, 0, 0, quadVertexData1); 195 | glEnableVertexAttribArray(ATTRIB_VERTEX_RGB); 196 | 197 | glVertexAttribPointer(ATTRIB_TEXCOORD_RGB, 2, GL_FLOAT, 0, 0, quadTextureData1); 198 | glEnableVertexAttribArray(ATTRIB_TEXCOORD_RGB); 199 | 200 | // Blend function to draw the foreground frame 201 | glEnable(GL_BLEND); 202 | glBlendFunc(GL_ONE, GL_ZERO); 203 | 204 | // Draw the foreground frame 205 | glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 206 | 207 | glFlush(); 208 | 209 | bail: 210 | CFRelease(foregroundRGBTexture); 211 | CFRelease(backgroundRGBTexture); 212 | CFRelease(destRGBTexture); 213 | 214 | // Periodic texture cache flush every frame 215 | CVOpenGLESTextureCacheFlush(self.videoTextureCache, 0); 216 | 217 | [EAGLContext setCurrentContext:nil]; 218 | } 219 | 220 | } 221 | 222 | - (void)setupOffscreenRenderContext 223 | { 224 | //-- Create CVOpenGLESTextureCacheRef for optimal CVPixelBufferRef to GLES texture conversion. 225 | if (_videoTextureCache) { 226 | CFRelease(_videoTextureCache); 227 | _videoTextureCache = NULL; 228 | } 229 | CVReturn err = CVOpenGLESTextureCacheCreate(kCFAllocatorDefault, NULL, _currentContext, NULL, &_videoTextureCache); 230 | if (err != noErr) { 231 | DDLogDebug(@"Error at CVOpenGLESTextureCacheCreate %d", err); 232 | } 233 | 234 | glDisable(GL_DEPTH_TEST); 235 | 236 | glGenFramebuffers(1, &_offscreenBufferHandle); 237 | glBindFramebuffer(GL_FRAMEBUFFER, _offscreenBufferHandle); 238 | } 239 | 240 | - (CVOpenGLESTextureRef)rgbTextureForPixelBuffer:(CVPixelBufferRef)pixelBuffer 241 | { 242 | 243 | CVOpenGLESTextureRef rgbTexture = NULL; 244 | CVReturn err; 245 | 246 | if (!_videoTextureCache) { 247 | DDLogDebug(@"No video texture cache"); 248 | goto bail; 249 | } 250 | 251 | // Periodic texture cache flush every frame 252 | CVOpenGLESTextureCacheFlush(_videoTextureCache, 0); 253 | 254 | // CVOpenGLTextureCacheCreateTextureFromImage will create GL texture optimally from CVPixelBufferRef. 255 | // RGB 256 | err = CVOpenGLESTextureCacheCreateTextureFromImage(kCFAllocatorDefault, 257 | _videoTextureCache, 258 | pixelBuffer, 259 | NULL, 260 | GL_TEXTURE_2D, 261 | GL_RGBA, 262 | (int)CVPixelBufferGetWidth(pixelBuffer), 263 | (int)CVPixelBufferGetHeight(pixelBuffer), 264 | GL_BGRA, 265 | GL_UNSIGNED_BYTE, 266 | 0, 267 | &rgbTexture); 268 | 269 | if (!rgbTexture || err) { 270 | DDLogDebug(@"Error at creating rgb texture using CVOpenGLESTextureCacheCreateTextureFromImage %d", err); 271 | } 272 | 273 | bail: 274 | return rgbTexture; 275 | } 276 | 277 | #pragma mark - 278 | #pragma mark Base methods for extension 279 | 280 | - (NSString *)fragmentShaderString { 281 | return kPassThroughFragmentShaderRGB; 282 | } 283 | 284 | #pragma mark - OpenGL ES 2 shader compilation 285 | 286 | - (BOOL)loadShaders 287 | { 288 | GLuint vertShader, fragShaderRGB; 289 | NSString *vertShaderSource, *fragShaderRGBSource; 290 | 291 | // Create the shader program. 292 | _programRGB = glCreateProgram(); 293 | 294 | // Create and compile the vertex shader. 295 | vertShaderSource = kPassThroughVertexShader; 296 | if (![self compileShader:&vertShader type:GL_VERTEX_SHADER source:vertShaderSource]) { 297 | DDLogDebug(@"Failed to compile vertex shader"); 298 | return NO; 299 | } 300 | 301 | // Create and compile RGB fragment shader. 302 | fragShaderRGBSource = [self fragmentShaderString]; 303 | if (![self compileShader:&fragShaderRGB type:GL_FRAGMENT_SHADER source:fragShaderRGBSource]) { 304 | DDLogDebug(@"Failed to compile RGB fragment shader"); 305 | return NO; 306 | } 307 | 308 | // Attach vertex shader to programY. 309 | glAttachShader(_programRGB, vertShader); 310 | 311 | // Attach fragment shader to programY. 312 | glAttachShader(_programRGB, fragShaderRGB); 313 | 314 | // Bind attribute locations. This needs to be done prior to linking. 315 | 316 | glBindAttribLocation(_programRGB, ATTRIB_VERTEX_RGB, "position"); 317 | glBindAttribLocation(_programRGB, ATTRIB_TEXCOORD_RGB, "texCoord"); 318 | 319 | // Link the program. 320 | if (![self linkProgram:_programRGB]) { 321 | DDLogDebug(@"Failed to link program: %d", _programRGB); 322 | 323 | if (vertShader) { 324 | glDeleteShader(vertShader); 325 | vertShader = 0; 326 | } 327 | if (_programRGB) { 328 | glDeleteProgram(_programRGB); 329 | _programRGB = 0; 330 | } 331 | 332 | return NO; 333 | } 334 | 335 | // Get uniform locations. 336 | uniforms[UNIFORM_RGB_FROM] = glGetUniformLocation(_programRGB, "from"); 337 | uniforms[UNIFORM_RGB_TO] = glGetUniformLocation(_programRGB, "to"); 338 | uniforms[UNIFORM_RENDER_TRANSFORM_RGB] = glGetUniformLocation(_programRGB, "renderTransform"); 339 | uniforms[UNIFORM_RENDER_PROGRESS_RGB] = glGetUniformLocation(_programRGB, "progress"); 340 | 341 | 342 | // Release vertex and fragment shaders. 343 | if (vertShader) { 344 | glDetachShader(_programRGB, vertShader); 345 | glDeleteShader(vertShader); 346 | } 347 | if (fragShaderRGB) { 348 | glDetachShader(_programRGB, fragShaderRGB); 349 | glDeleteShader(fragShaderRGB); 350 | } 351 | 352 | return YES; 353 | } 354 | 355 | - (BOOL)compileShader:(GLuint *)shader type:(GLenum)type source:(NSString *)sourceString 356 | { 357 | if (sourceString == nil) { 358 | DDLogDebug(@"Failed to load vertex shader: Empty source string"); 359 | return NO; 360 | } 361 | 362 | GLint status; 363 | const GLchar *source; 364 | source = (GLchar *)[sourceString UTF8String]; 365 | 366 | *shader = glCreateShader(type); 367 | glShaderSource(*shader, 1, &source, NULL); 368 | glCompileShader(*shader); 369 | 370 | #if defined(DEBUG) 371 | GLint logLength; 372 | glGetShaderiv(*shader, GL_INFO_LOG_LENGTH, &logLength); 373 | if (logLength > 0) { 374 | GLchar *log = (GLchar *)malloc(logLength); 375 | glGetShaderInfoLog(*shader, logLength, &logLength, log); 376 | DDLogDebug(@"Shader compile log:\n%s", log); 377 | free(log); 378 | } 379 | #endif 380 | 381 | glGetShaderiv(*shader, GL_COMPILE_STATUS, &status); 382 | if (status == 0) { 383 | glDeleteShader(*shader); 384 | return NO; 385 | } 386 | 387 | return YES; 388 | } 389 | 390 | - (BOOL)linkProgram:(GLuint)prog 391 | { 392 | GLint status; 393 | glLinkProgram(prog); 394 | 395 | #if defined(DEBUG) 396 | GLint logLength; 397 | glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &logLength); 398 | if (logLength > 0) { 399 | GLchar *log = (GLchar *)malloc(logLength); 400 | glGetProgramInfoLog(prog, logLength, &logLength, log); 401 | DDLogDebug(@"Program link log:\n%s", log); 402 | free(log); 403 | } 404 | #endif 405 | 406 | glGetProgramiv(prog, GL_LINK_STATUS, &status); 407 | if (status == 0) { 408 | return NO; 409 | } 410 | 411 | return YES; 412 | } 413 | 414 | #if defined(DEBUG) 415 | 416 | - (BOOL)validateProgram:(GLuint)prog 417 | { 418 | GLint logLength, status; 419 | 420 | glValidateProgram(prog); 421 | glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &logLength); 422 | if (logLength > 0) { 423 | GLchar *log = (GLchar *)malloc(logLength); 424 | glGetProgramInfoLog(prog, logLength, &logLength, log); 425 | DDLogDebug(@"Program validate log:\n%s", log); 426 | free(log); 427 | } 428 | 429 | glGetProgramiv(prog, GL_VALIDATE_STATUS, &status); 430 | if (status == 0) { 431 | return NO; 432 | } 433 | 434 | return YES; 435 | } 436 | 437 | #endif 438 | 439 | @end 440 | -------------------------------------------------------------------------------- /VideoTransitionsSample/Business layer/VideoEditor/OpenGL renderer/Transition renderers/Fold/VTSFoldRenderer.h: -------------------------------------------------------------------------------- 1 | // 2 | // VTSFoldRenderer.h 3 | // VideoTransitionsSample 4 | // 5 | // Created by Ruslan Shevtsov on 4/14/15. 6 | // Copyright (c) 2015 iQueSoft. All rights reserved. 7 | // 8 | 9 | #import "APLOpenGLRenderer.h" 10 | 11 | @interface VTSFoldRenderer : APLOpenGLRenderer 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /VideoTransitionsSample/Business layer/VideoEditor/OpenGL renderer/Transition renderers/Fold/VTSFoldRenderer.m: -------------------------------------------------------------------------------- 1 | // 2 | // VTSFoldRenderer.m 3 | // VideoTransitionsSample 4 | // 5 | // Created by Ruslan Shevtsov on 4/14/15. 6 | // Copyright (c) 2015 iQueSoft. All rights reserved. 7 | // 8 | 9 | #import "VTSFoldRenderer.h" 10 | 11 | NSString *const kPassThroughFoldFragmentShader = SHADER_STRING 12 | ( 13 | varying highp vec2 texCoordVarying; 14 | uniform highp float progress; 15 | uniform sampler2D from; 16 | uniform sampler2D to; 17 | 18 | void main() 19 | { 20 | highp vec2 p = texCoordVarying; 21 | highp vec4 a = texture2D(from, (p - vec2(progress, 0.0)) / vec2(1.0-progress, 1.0)); 22 | highp vec4 b = texture2D(to, p / vec2(progress, 1.0)); 23 | gl_FragColor = mix(a, b, step(p.x, progress)); 24 | } 25 | 26 | ); 27 | 28 | @interface VTSFoldRenderer () 29 | 30 | @end 31 | 32 | @implementation VTSFoldRenderer 33 | 34 | - (NSString *)fragmentShaderString { 35 | return kPassThroughFoldFragmentShader; 36 | } 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /VideoTransitionsSample/Business layer/VideoEditor/OpenGL renderer/Transition renderers/Pinwheel/VTSPinwheelRenderer.h: -------------------------------------------------------------------------------- 1 | // 2 | // VTSPinwheelRenderer.h 3 | // VideoTransitionsSample 4 | // 5 | // Created by Ruslan Shevtsov on 4/14/15. 6 | // Copyright (c) 2015 iQueSoft. All rights reserved. 7 | // 8 | 9 | #import "APLOpenGLRenderer.h" 10 | 11 | @interface VTSPinwheelRenderer : APLOpenGLRenderer 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /VideoTransitionsSample/Business layer/VideoEditor/OpenGL renderer/Transition renderers/Pinwheel/VTSPinwheelRenderer.m: -------------------------------------------------------------------------------- 1 | // 2 | // VTSPinwheelRenderer.m 3 | // VideoTransitionsSample 4 | // 5 | // Created by Ruslan Shevtsov on 4/14/15. 6 | // Copyright (c) 2015 iQueSoft. All rights reserved. 7 | // 8 | 9 | #import "VTSPinwheelRenderer.h" 10 | 11 | NSString *const kPassThroughPinwheelFragmentShader = SHADER_STRING 12 | ( 13 | varying highp vec2 texCoordVarying; 14 | uniform highp float progress; 15 | uniform sampler2D from; 16 | uniform sampler2D to; 17 | 18 | void main() 19 | { 20 | highp vec2 p = texCoordVarying; 21 | highp float circPos = atan(p.y - 0.5, p.x - 0.5) + progress; 22 | highp float modPos = mod(circPos, 3.1415 / 4.); 23 | highp float signed = sign(progress - modPos); 24 | highp float smoothed = smoothstep(0., 1., signed); 25 | 26 | if (smoothed > 0.5){ 27 | gl_FragColor = texture2D(to, p); 28 | } else { 29 | gl_FragColor = texture2D(from, p); 30 | } 31 | 32 | } 33 | 34 | ); 35 | 36 | @interface VTSPinwheelRenderer () 37 | 38 | @end 39 | 40 | @implementation VTSPinwheelRenderer 41 | 42 | - (NSString *)fragmentShaderString { 43 | return kPassThroughPinwheelFragmentShader; 44 | } 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /VideoTransitionsSample/Business layer/VideoEditor/OpenGL renderer/Transition renderers/SimpleFlip/VTSSimpleFlipRenderer.h: -------------------------------------------------------------------------------- 1 | // 2 | // VTSSimpleFlipRenderer.h 3 | // VideoTransitionsSample 4 | // 5 | // Created by Ruslan Shevtsov on 4/14/15. 6 | // Copyright (c) 2015 iQueSoft. All rights reserved. 7 | // 8 | 9 | #import "APLOpenGLRenderer.h" 10 | 11 | @interface VTSSimpleFlipRenderer : APLOpenGLRenderer 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /VideoTransitionsSample/Business layer/VideoEditor/OpenGL renderer/Transition renderers/SimpleFlip/VTSSimpleFlipRenderer.m: -------------------------------------------------------------------------------- 1 | // 2 | // VTSSimpleFlipRenderer.m 3 | // VideoTransitionsSample 4 | // 5 | // Created by Ruslan Shevtsov on 4/14/15. 6 | // Copyright (c) 2015 iQueSoft. All rights reserved. 7 | // 8 | 9 | #import "VTSSimpleFlipRenderer.h" 10 | 11 | NSString *const kPassThroughSimpleFlipFragmentShader = SHADER_STRING 12 | ( 13 | varying highp vec2 texCoordVarying; 14 | uniform highp float progress; 15 | uniform sampler2D from; 16 | uniform sampler2D to; 17 | 18 | void main() 19 | { 20 | highp vec2 p = texCoordVarying; 21 | highp vec2 q = p; 22 | p.x = (p.x - 0.5)/abs(progress - 0.5)*0.5 + 0.5; 23 | highp vec4 a = texture2D(from, p); 24 | highp vec4 b = texture2D(to, p); 25 | gl_FragColor = vec4(mix(a, b, step(0.5, progress)).rgb * step(abs(q.x - 0.5), abs(progress - 0.5)), 1.0); 26 | 27 | } 28 | 29 | ); 30 | 31 | @interface VTSSimpleFlipRenderer () 32 | 33 | @end 34 | 35 | @implementation VTSSimpleFlipRenderer 36 | 37 | - (NSString *)fragmentShaderString { 38 | return kPassThroughSimpleFlipFragmentShader; 39 | } 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /VideoTransitionsSample/Business layer/VideoEditor/OpenGL renderer/Transition renderers/StarWipe/VTSStarWipeRenderer.h: -------------------------------------------------------------------------------- 1 | // 2 | // VTSStarWipeRenderer.h 3 | // VideoTransitionsSample 4 | // 5 | // Created by Ruslan Shevtsov on 4/14/15. 6 | // Copyright (c) 2015 iQueSoft. All rights reserved. 7 | // 8 | 9 | #import "APLOpenGLRenderer.h" 10 | 11 | @interface VTSStarWipeRenderer : APLOpenGLRenderer 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /VideoTransitionsSample/Business layer/VideoEditor/OpenGL renderer/Transition renderers/StarWipe/VTSStarWipeRenderer.m: -------------------------------------------------------------------------------- 1 | // 2 | // VTSStarWipeRenderer.m 3 | // VideoTransitionsSample 4 | // 5 | // Created by Ruslan Shevtsov on 4/14/15. 6 | // Copyright (c) 2015 iQueSoft. All rights reserved. 7 | // 8 | 9 | #import "VTSStarWipeRenderer.h" 10 | 11 | NSString *const kPassThroughStarWipeFragmentShader = SHADER_STRING 12 | ( 13 | precision highp float; 14 | 15 | varying vec2 texCoordVarying; 16 | uniform float progress; 17 | uniform sampler2D from; 18 | uniform sampler2D to; 19 | 20 | vec2 circlePoint( float ang ) 21 | { 22 | ang += 6.28318 * 0.15; 23 | return vec2( cos(ang), sin(ang) ); 24 | } 25 | 26 | float cross2d( vec2 a, vec2 b ) 27 | { 28 | return ( a.x * b.y - a.y * b.x ); 29 | } 30 | 31 | // quickly knocked together with some math from http://www.pixeleuphoria.com/node/30 32 | float star( vec2 p, float size ) 33 | { 34 | if( size <= 0.0 ) 35 | { 36 | return 0.0; 37 | } 38 | p /= size; 39 | 40 | vec2 p0 = circlePoint( 0.0 ); 41 | vec2 p1 = circlePoint( 6.28318 * 1.0 / 5.0 ); 42 | vec2 p2 = circlePoint( 6.28318 * 2.0 / 5.0 ); 43 | vec2 p3 = circlePoint( 6.28318 * 3.0 / 5.0 ); 44 | vec2 p4 = circlePoint( 6.28318 * 4.0 / 5.0 ); 45 | 46 | // are we on this side of the line 47 | float s0 = ( cross2d( p1 - p0, p - p0 ) ); 48 | float s1 = ( cross2d( p2 - p1, p - p1 ) ); 49 | float s2 = ( cross2d( p3 - p2, p - p2 ) ); 50 | float s3 = ( cross2d( p4 - p3, p - p3 ) ); 51 | float s4 = ( cross2d( p0 - p4, p - p4 ) ); 52 | 53 | // some trial and error math to get the star shape. I'm sure there's some elegance I'm missing. 54 | float s5 = min( min( min( s0, s1 ), min( s2, s3 ) ), s4 ); 55 | float s = max( 1.0 - sign( s0 * s1 * s2 * s3 * s4 ) + sign(s5), 0.0 ); 56 | s = sign( 2.6 - length(p) ) * s; 57 | 58 | return max( s, 0.0 ); 59 | } 60 | 61 | void main() 62 | { 63 | vec2 p = texCoordVarying; 64 | vec2 o = p * 2.0 - 1.0; 65 | 66 | float t = progress * 1.4; 67 | 68 | float c1 = star( o, t ); 69 | float c2 = star( o, t - 0.1 ); 70 | 71 | float border = max( c1 - c2, 0.0 ); 72 | 73 | gl_FragColor = mix(texture2D(from, p), texture2D(to, p), c1) + vec4( border, border, border, 0.0 ); 74 | } 75 | 76 | ); 77 | 78 | @interface VTSStarWipeRenderer () 79 | 80 | @end 81 | 82 | @implementation VTSStarWipeRenderer 83 | 84 | - (NSString *)fragmentShaderString { 85 | return kPassThroughStarWipeFragmentShader; 86 | } 87 | 88 | @end 89 | -------------------------------------------------------------------------------- /VideoTransitionsSample/Business layer/VideoEditor/OpenGL renderer/Transition renderers/Wind/VTSWindRenderer.h: -------------------------------------------------------------------------------- 1 | // 2 | // VTSWindRenderer.h 3 | // VideoTransitionsSample 4 | // 5 | // Created by Ruslan Shevtsov on 4/14/15. 6 | // Copyright (c) 2015 iQueSoft. All rights reserved. 7 | // 8 | 9 | #import "APLOpenGLRenderer.h" 10 | 11 | @interface VTSWindRenderer : APLOpenGLRenderer 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /VideoTransitionsSample/Business layer/VideoEditor/OpenGL renderer/Transition renderers/Wind/VTSWindRenderer.m: -------------------------------------------------------------------------------- 1 | // 2 | // VTSWindRenderer.m 3 | // VideoTransitionsSample 4 | // 5 | // Created by Ruslan Shevtsov on 4/14/15. 6 | // Copyright (c) 2015 iQueSoft. All rights reserved. 7 | // 8 | 9 | #import "VTSWindRenderer.h" 10 | 11 | NSString *const kPassThroughWindFragmentShader = SHADER_STRING 12 | ( 13 | varying highp vec2 texCoordVarying; 14 | uniform highp float progress; 15 | uniform sampler2D from; 16 | uniform sampler2D to; 17 | 18 | highp float rand (highp vec2 co) { 19 | return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); 20 | } 21 | 22 | void main() 23 | { 24 | highp float size = 0.2; 25 | 26 | highp vec2 p = texCoordVarying; 27 | highp float r = rand(vec2(0, p.y)); 28 | highp float m = smoothstep(0.0, -size, p.x*(1.0-size) + size*r - (progress * (1.0 + size))); 29 | gl_FragColor = mix(texture2D(from, p), texture2D(to, p), m); 30 | 31 | } 32 | 33 | ); 34 | 35 | @interface VTSWindRenderer () 36 | 37 | @end 38 | 39 | @implementation VTSWindRenderer 40 | 41 | - (NSString *)fragmentShaderString { 42 | return kPassThroughWindFragmentShader; 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /VideoTransitionsSample/Helpers/DurationStringFromTimeInterval/DurationStringFromTimeInterval.h: -------------------------------------------------------------------------------- 1 | // 2 | // DurationStringFromTimeInterval.h 3 | // VideoRecordSample 4 | // 5 | // Created by Ruslan Shevtsov on 4/2/15. 6 | // Copyright (c) 2015 iQueSoft rights reserved. 7 | // 8 | 9 | #import 10 | 11 | extern NSString * const kTimeZeroString; 12 | 13 | NSString* DurationFromTimeInterval(double duration); -------------------------------------------------------------------------------- /VideoTransitionsSample/Helpers/DurationStringFromTimeInterval/DurationStringFromTimeIntervalString.m: -------------------------------------------------------------------------------- 1 | // 2 | // DurationStringFromTimeInterval.m 3 | // VideoRecordSample 4 | // 5 | // Created by Ruslan Shevtsov on 4/2/15. 6 | // Copyright (c) 2015 iQueSoft rights reserved. 7 | // 8 | 9 | #import "DurationStringFromTimeInterval.h" 10 | 11 | NSString * const kTimeZeroString = @"0:00"; 12 | 13 | NSString* DurationFromTimeInterval(double duration) { 14 | if (duration == 0.0) { 15 | return kTimeZeroString; 16 | } 17 | NSString *timeIntervalString = nil; 18 | 19 | int minutes = (int)(duration / 60); 20 | int seconds = (int)(((int)duration) % 60); 21 | 22 | NSString *secondsStrong = nil; 23 | 24 | if (seconds >= 10) { 25 | secondsStrong = [NSString stringWithFormat:@"%d", seconds]; 26 | } else { 27 | secondsStrong = [NSString stringWithFormat:@"0%d", seconds]; 28 | } 29 | 30 | timeIntervalString = [NSString stringWithFormat:@"%d:%@", minutes, secondsStrong]; 31 | 32 | return timeIntervalString; 33 | } 34 | -------------------------------------------------------------------------------- /VideoTransitionsSample/Helpers/UIImagePickerController+DelegateBlocks/UIImagePickerController+DelegateBlocks.h: -------------------------------------------------------------------------------- 1 | // 2 | // protocolblocks: UIKit/UIImagePickerController+DelegateBlocks.h 3 | // 4 | // Copyright (C) 2011 by Constantin Rack, VIGOS AG. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | #import 26 | 27 | typedef void (^UIImagePickerControllerDidFinishPickingMediaWithInfoBlock)(UIImagePickerController* picker, NSDictionary* info); 28 | typedef void (^UIImagePickerControllerDidCancelBlock)(UIImagePickerController* picker); 29 | typedef void (^UIImagePickerControllerDidShowViewControllerBlock)(UINavigationController* navigationController, UIViewController* viewController, BOOL animated); 30 | typedef void (^UIImagePickerControllerWillShowViewControllerBlock)(UINavigationController* navigationController, UIViewController* viewController, BOOL animated); 31 | 32 | @interface UIImagePickerController (DelegateBlocks) 33 | 34 | -(id)useBlocksForDelegate; 35 | -(void)onDidFinishPickingMediaWithInfo:(UIImagePickerControllerDidFinishPickingMediaWithInfoBlock)block; 36 | -(void)onDidCancel:(UIImagePickerControllerDidCancelBlock)block; 37 | -(void)onDidShowViewController:(UIImagePickerControllerDidShowViewControllerBlock)block; 38 | -(void)onWillShowViewController:(UIImagePickerControllerWillShowViewControllerBlock)block; 39 | 40 | @end 41 | 42 | @interface UIImagePickerControllerDelegateBlocks : NSObject { 43 | UIImagePickerControllerDidFinishPickingMediaWithInfoBlock _didFinishPickingMediaWithInfoBlock; 44 | UIImagePickerControllerDidCancelBlock _didCancelBlock; 45 | UIImagePickerControllerDidShowViewControllerBlock _didShowViewControllerBlock; 46 | UIImagePickerControllerWillShowViewControllerBlock _willShowViewControllerBlock; 47 | } 48 | 49 | @property(nonatomic, copy) UIImagePickerControllerDidFinishPickingMediaWithInfoBlock didFinishPickingMediaWithInfoBlock; 50 | @property(nonatomic, copy) UIImagePickerControllerDidCancelBlock didCancelBlock; 51 | @property(nonatomic, copy) UIImagePickerControllerDidShowViewControllerBlock didShowViewControllerBlock; 52 | @property(nonatomic, copy) UIImagePickerControllerWillShowViewControllerBlock willShowViewControllerBlock; 53 | 54 | @end 55 | 56 | -------------------------------------------------------------------------------- /VideoTransitionsSample/Helpers/UIImagePickerController+DelegateBlocks/UIImagePickerController+DelegateBlocks.m: -------------------------------------------------------------------------------- 1 | // 2 | // protocolblocks: UIKit/UIImagePickerController+DelegateBlocks.m 3 | // 4 | // Copyright (C) 2011 by Constantin Rack, VIGOS AG. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | #import "UIImagePickerController+DelegateBlocks.h" 26 | #import 27 | 28 | static NSString* UIImagePickerControllerDelegateBlocksKey = @"UIImagePickerControllerDelegateBlocksKey"; 29 | 30 | @implementation UIImagePickerController (DelegateBlocks) 31 | 32 | -(id)useBlocksForDelegate { 33 | UIImagePickerControllerDelegateBlocks* delegate = [[UIImagePickerControllerDelegateBlocks alloc] init]; 34 | objc_setAssociatedObject (self, &UIImagePickerControllerDelegateBlocksKey, delegate, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 35 | self.delegate = delegate; 36 | return self; 37 | } 38 | 39 | -(void)onDidFinishPickingMediaWithInfo:(UIImagePickerControllerDidFinishPickingMediaWithInfoBlock)block { 40 | [((UIImagePickerControllerDelegateBlocks*)self.delegate) setDidFinishPickingMediaWithInfoBlock:block]; 41 | } 42 | 43 | -(void)onDidCancel:(UIImagePickerControllerDidCancelBlock)block { 44 | [((UIImagePickerControllerDelegateBlocks*)self.delegate) setDidCancelBlock:block]; 45 | } 46 | 47 | -(void)onDidShowViewController:(UIImagePickerControllerDidShowViewControllerBlock)block { 48 | [((UIImagePickerControllerDelegateBlocks*)self.delegate) setDidShowViewControllerBlock:block]; 49 | } 50 | 51 | -(void)onWillShowViewController:(UIImagePickerControllerWillShowViewControllerBlock)block { 52 | [((UIImagePickerControllerDelegateBlocks*)self.delegate) setWillShowViewControllerBlock:block]; 53 | } 54 | 55 | @end 56 | 57 | @implementation UIImagePickerControllerDelegateBlocks 58 | 59 | @synthesize didFinishPickingMediaWithInfoBlock = _didFinishPickingMediaWithInfoBlock; 60 | @synthesize didCancelBlock = _didCancelBlock; 61 | @synthesize didShowViewControllerBlock = _didShowViewControllerBlock; 62 | @synthesize willShowViewControllerBlock = _willShowViewControllerBlock; 63 | 64 | -(BOOL)respondsToSelector:(SEL)aSelector { 65 | if ( sel_isEqual(aSelector, @selector(imagePickerController:didFinishPickingMediaWithInfo:)) ) { 66 | return !!self.didFinishPickingMediaWithInfoBlock; 67 | } else if ( sel_isEqual(aSelector, @selector(imagePickerControllerDidCancel:)) ) { 68 | return !!self.didCancelBlock; 69 | } else if ( sel_isEqual(aSelector, @selector(navigationController:didShowViewController:animated:)) ) { 70 | return !!self.didShowViewControllerBlock; 71 | } else if ( sel_isEqual(aSelector, @selector(navigationController:willShowViewController:animated:)) ) { 72 | return !!self.willShowViewControllerBlock; 73 | } 74 | return [super respondsToSelector:aSelector]; 75 | } 76 | 77 | -(void)imagePickerController:(UIImagePickerController*)picker didFinishPickingMediaWithInfo:(NSDictionary*)info { 78 | UIImagePickerControllerDidFinishPickingMediaWithInfoBlock block = [self.didFinishPickingMediaWithInfoBlock copy]; 79 | block(picker, info); 80 | } 81 | 82 | -(void)imagePickerControllerDidCancel:(UIImagePickerController*)picker { 83 | UIImagePickerControllerDidCancelBlock block = [self.didCancelBlock copy]; 84 | block(picker); 85 | } 86 | 87 | -(void)navigationController:(UINavigationController*)navigationController didShowViewController:(UIViewController*)viewController animated:(BOOL)animated { 88 | UIImagePickerControllerDidShowViewControllerBlock block = [self.didShowViewControllerBlock copy]; 89 | block(navigationController, viewController, animated); 90 | } 91 | 92 | -(void)navigationController:(UINavigationController*)navigationController willShowViewController:(UIViewController*)viewController animated:(BOOL)animated { 93 | UIImagePickerControllerWillShowViewControllerBlock block = [self.willShowViewControllerBlock copy]; 94 | block(navigationController, viewController, animated); 95 | } 96 | 97 | @end 98 | 99 | -------------------------------------------------------------------------------- /VideoTransitionsSample/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /VideoTransitionsSample/Images.xcassets/Close.imageset/Cancel-100 (1)-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iQueSoft/iOSDemo_VideoTransitionsSample/37384abfb7eea680a0fe82f92c8b25b49860e3cd/VideoTransitionsSample/Images.xcassets/Close.imageset/Cancel-100 (1)-1.png -------------------------------------------------------------------------------- /VideoTransitionsSample/Images.xcassets/Close.imageset/Cancel-100 (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iQueSoft/iOSDemo_VideoTransitionsSample/37384abfb7eea680a0fe82f92c8b25b49860e3cd/VideoTransitionsSample/Images.xcassets/Close.imageset/Cancel-100 (1).png -------------------------------------------------------------------------------- /VideoTransitionsSample/Images.xcassets/Close.imageset/Cancel-50 (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iQueSoft/iOSDemo_VideoTransitionsSample/37384abfb7eea680a0fe82f92c8b25b49860e3cd/VideoTransitionsSample/Images.xcassets/Close.imageset/Cancel-50 (1).png -------------------------------------------------------------------------------- /VideoTransitionsSample/Images.xcassets/Close.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "Cancel-50 (1).png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "Cancel-100 (1).png" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x", 16 | "filename" : "Cancel-100 (1)-1.png" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /VideoTransitionsSample/Images.xcassets/New Folder/Pause.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "Pause Filled-50.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "Pause Filled-100.png" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x", 16 | "filename" : "Pause Filled-100-1.png" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /VideoTransitionsSample/Images.xcassets/New Folder/Pause.imageset/Pause Filled-100-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iQueSoft/iOSDemo_VideoTransitionsSample/37384abfb7eea680a0fe82f92c8b25b49860e3cd/VideoTransitionsSample/Images.xcassets/New Folder/Pause.imageset/Pause Filled-100-1.png -------------------------------------------------------------------------------- /VideoTransitionsSample/Images.xcassets/New Folder/Pause.imageset/Pause Filled-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iQueSoft/iOSDemo_VideoTransitionsSample/37384abfb7eea680a0fe82f92c8b25b49860e3cd/VideoTransitionsSample/Images.xcassets/New Folder/Pause.imageset/Pause Filled-100.png -------------------------------------------------------------------------------- /VideoTransitionsSample/Images.xcassets/New Folder/Pause.imageset/Pause Filled-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iQueSoft/iOSDemo_VideoTransitionsSample/37384abfb7eea680a0fe82f92c8b25b49860e3cd/VideoTransitionsSample/Images.xcassets/New Folder/Pause.imageset/Pause Filled-50.png -------------------------------------------------------------------------------- /VideoTransitionsSample/Images.xcassets/New Folder/Play.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "Play Filled-50.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "Play Filled-100.png" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x", 16 | "filename" : "Play Filled-100-1.png" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /VideoTransitionsSample/Images.xcassets/New Folder/Play.imageset/Play Filled-100-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iQueSoft/iOSDemo_VideoTransitionsSample/37384abfb7eea680a0fe82f92c8b25b49860e3cd/VideoTransitionsSample/Images.xcassets/New Folder/Play.imageset/Play Filled-100-1.png -------------------------------------------------------------------------------- /VideoTransitionsSample/Images.xcassets/New Folder/Play.imageset/Play Filled-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iQueSoft/iOSDemo_VideoTransitionsSample/37384abfb7eea680a0fe82f92c8b25b49860e3cd/VideoTransitionsSample/Images.xcassets/New Folder/Play.imageset/Play Filled-100.png -------------------------------------------------------------------------------- /VideoTransitionsSample/Images.xcassets/New Folder/Play.imageset/Play Filled-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iQueSoft/iOSDemo_VideoTransitionsSample/37384abfb7eea680a0fe82f92c8b25b49860e3cd/VideoTransitionsSample/Images.xcassets/New Folder/Play.imageset/Play Filled-50.png -------------------------------------------------------------------------------- /VideoTransitionsSample/Images.xcassets/Preview.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "Movie-50 (1).png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "Movie-100 (1).png" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x", 16 | "filename" : "Movie-100 (1)-1.png" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /VideoTransitionsSample/Images.xcassets/Preview.imageset/Movie-100 (1)-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iQueSoft/iOSDemo_VideoTransitionsSample/37384abfb7eea680a0fe82f92c8b25b49860e3cd/VideoTransitionsSample/Images.xcassets/Preview.imageset/Movie-100 (1)-1.png -------------------------------------------------------------------------------- /VideoTransitionsSample/Images.xcassets/Preview.imageset/Movie-100 (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iQueSoft/iOSDemo_VideoTransitionsSample/37384abfb7eea680a0fe82f92c8b25b49860e3cd/VideoTransitionsSample/Images.xcassets/Preview.imageset/Movie-100 (1).png -------------------------------------------------------------------------------- /VideoTransitionsSample/Images.xcassets/Preview.imageset/Movie-50 (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iQueSoft/iOSDemo_VideoTransitionsSample/37384abfb7eea680a0fe82f92c8b25b49860e3cd/VideoTransitionsSample/Images.xcassets/Preview.imageset/Movie-50 (1).png -------------------------------------------------------------------------------- /VideoTransitionsSample/Images.xcassets/Save.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "Download-50 (1).png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "Download-100 (1).png" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x", 16 | "filename" : "Download-100 (1)-1.png" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /VideoTransitionsSample/Images.xcassets/Save.imageset/Download-100 (1)-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iQueSoft/iOSDemo_VideoTransitionsSample/37384abfb7eea680a0fe82f92c8b25b49860e3cd/VideoTransitionsSample/Images.xcassets/Save.imageset/Download-100 (1)-1.png -------------------------------------------------------------------------------- /VideoTransitionsSample/Images.xcassets/Save.imageset/Download-100 (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iQueSoft/iOSDemo_VideoTransitionsSample/37384abfb7eea680a0fe82f92c8b25b49860e3cd/VideoTransitionsSample/Images.xcassets/Save.imageset/Download-100 (1).png -------------------------------------------------------------------------------- /VideoTransitionsSample/Images.xcassets/Save.imageset/Download-50 (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iQueSoft/iOSDemo_VideoTransitionsSample/37384abfb7eea680a0fe82f92c8b25b49860e3cd/VideoTransitionsSample/Images.xcassets/Save.imageset/Download-50 (1).png -------------------------------------------------------------------------------- /VideoTransitionsSample/Images.xcassets/TransitionBackgroundImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "fonstola.ru-55269.jpg" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "fonstola.ru-55269-1.jpg" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x", 16 | "filename" : "fonstola.ru-55269-2.jpg" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /VideoTransitionsSample/Images.xcassets/TransitionBackgroundImage.imageset/fonstola.ru-55269-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iQueSoft/iOSDemo_VideoTransitionsSample/37384abfb7eea680a0fe82f92c8b25b49860e3cd/VideoTransitionsSample/Images.xcassets/TransitionBackgroundImage.imageset/fonstola.ru-55269-1.jpg -------------------------------------------------------------------------------- /VideoTransitionsSample/Images.xcassets/TransitionBackgroundImage.imageset/fonstola.ru-55269-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iQueSoft/iOSDemo_VideoTransitionsSample/37384abfb7eea680a0fe82f92c8b25b49860e3cd/VideoTransitionsSample/Images.xcassets/TransitionBackgroundImage.imageset/fonstola.ru-55269-2.jpg -------------------------------------------------------------------------------- /VideoTransitionsSample/Images.xcassets/TransitionBackgroundImage.imageset/fonstola.ru-55269.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iQueSoft/iOSDemo_VideoTransitionsSample/37384abfb7eea680a0fe82f92c8b25b49860e3cd/VideoTransitionsSample/Images.xcassets/TransitionBackgroundImage.imageset/fonstola.ru-55269.jpg -------------------------------------------------------------------------------- /VideoTransitionsSample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | VTSStoryboard 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationLandscapeLeft 36 | UIInterfaceOrientationLandscapeRight 37 | UIInterfaceOrientationPortrait 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /VideoTransitionsSample/PrefixHeader.pch: -------------------------------------------------------------------------------- 1 | // 2 | // PrefixHeader.pch 3 | // VideoTransitionsSample 4 | // 5 | // Created by Ruslan Shevtsov on 4/10/15. 6 | // Copyright (c) 2015 iQueSoft. All rights reserved. 7 | // 8 | 9 | #import "DDLog.h" 10 | 11 | #ifdef DEBUG 12 | static const NSUInteger ddLogLevel = LOG_LEVEL_ALL; 13 | #else 14 | static const NSUInteger ddLogLevel = LOG_LEVEL_OFF; 15 | #endif 16 | 17 | // Use Swift 18 | 19 | #import "VideoTransitionsSample-Swift.h" -------------------------------------------------------------------------------- /VideoTransitionsSample/Resources/Storyboards/VTSStoryboard.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 27 | 28 | 29 | 30 | 44 | 58 | 71 | 84 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 172 | 185 | 195 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | -------------------------------------------------------------------------------- /VideoTransitionsSample/Resources/Videos/preview_video_1.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iQueSoft/iOSDemo_VideoTransitionsSample/37384abfb7eea680a0fe82f92c8b25b49860e3cd/VideoTransitionsSample/Resources/Videos/preview_video_1.mp4 -------------------------------------------------------------------------------- /VideoTransitionsSample/Resources/Videos/preview_video_2.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iQueSoft/iOSDemo_VideoTransitionsSample/37384abfb7eea680a0fe82f92c8b25b49860e3cd/VideoTransitionsSample/Resources/Videos/preview_video_2.mp4 -------------------------------------------------------------------------------- /VideoTransitionsSample/UI layer/Custom elements/Buttons/TransitionButton/VTSTransitionButton.h: -------------------------------------------------------------------------------- 1 | // 2 | // VTSTransitionButton.h 3 | // VideoTransitionsSample 4 | // 5 | // Created by Ruslan Shevtsov on 4/14/15. 6 | // Copyright (c) 2015 iQueSoft. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class VTSPlayerView; 12 | 13 | @interface VTSTransitionButton : UIButton 14 | 15 | @property (nonatomic, strong) VTSPlayerView *playerView; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /VideoTransitionsSample/UI layer/Custom elements/Buttons/TransitionButton/VTSTransitionButton.m: -------------------------------------------------------------------------------- 1 | // 2 | // VTSTransitionButton.m 3 | // VideoTransitionsSample 4 | // 5 | // Created by Ruslan Shevtsov on 4/14/15. 6 | // Copyright (c) 2015 iQueSoft. All rights reserved. 7 | // 8 | 9 | #import "VTSTransitionButton.h" 10 | 11 | // Frameworks 12 | #import 13 | 14 | // UI 15 | #import "VTSPlayerView.h" 16 | 17 | @interface VTSTransitionButton () 18 | 19 | @property (nonatomic, strong) UIImageView *backgroundImageView; 20 | 21 | @end 22 | 23 | @implementation VTSTransitionButton 24 | 25 | - (instancetype)init { 26 | self = [super init]; 27 | if (!self) { 28 | return nil; 29 | } 30 | 31 | [self initialization]; 32 | 33 | return self; 34 | } 35 | 36 | - (void)awakeFromNib { 37 | [super awakeFromNib]; 38 | 39 | [self initialization]; 40 | } 41 | 42 | - (void)initialization { 43 | 44 | self.backgroundColor = [UIColor clearColor]; 45 | 46 | self.layer.borderWidth = 2.0f; 47 | self.layer.borderColor = [UIColor colorWithRed:233.0f/255.0f green:56.0f/255.0f blue:59.0f/255.0f alpha:1.0f].CGColor; 48 | 49 | self.backgroundImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"TransitionBackgroundImage"]]; 50 | [self addSubview:self.backgroundImageView]; 51 | 52 | self.playerView = [VTSPlayerView new]; 53 | self.playerView.backgroundColor = [UIColor clearColor]; 54 | [self addSubview:self.playerView]; 55 | 56 | [self setupDecorSubviewsSize]; 57 | } 58 | 59 | - (void)setupDecorSubviewsSize { 60 | CGRect bounds = self.bounds; 61 | 62 | CGRect backgroundViewFrame = CGRectMake(4.0f, 63 | 4.0f, 64 | bounds.size.width - 8.0f, 65 | bounds.size.height - 8.0f); 66 | 67 | self.backgroundImageView.frame = backgroundViewFrame; 68 | self.playerView.frame = backgroundViewFrame; 69 | } 70 | 71 | #pragma mark - 72 | #pragma mark Setters 73 | 74 | - (void)setFrame:(CGRect)frame { 75 | [super setFrame:frame]; 76 | 77 | [self setupDecorSubviewsSize]; 78 | } 79 | 80 | - (void)setBounds:(CGRect)bounds { 81 | [super setBounds:bounds]; 82 | 83 | [self setupDecorSubviewsSize]; 84 | } 85 | 86 | @end 87 | -------------------------------------------------------------------------------- /VideoTransitionsSample/UI layer/Custom elements/Buttons/VideoButton/VTSVideoButton.h: -------------------------------------------------------------------------------- 1 | // 2 | // VTSVideoButton.h 3 | // VideoTransitionsSample 4 | // 5 | // Created by Ruslan Shevtsov on 4/9/15. 6 | // Copyright (c) 2015 iQueSoft. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class VTSPlayerView; 12 | 13 | @interface VTSVideoButton : UIButton 14 | 15 | @property (nonatomic, strong) VTSPlayerView *playerView; 16 | 17 | #pragma mark - 18 | #pragma mark Update UI for preview 19 | 20 | - (void)startPreview; 21 | 22 | - (void)stopPreview; 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /VideoTransitionsSample/UI layer/Custom elements/Buttons/VideoButton/VTSVideoButton.m: -------------------------------------------------------------------------------- 1 | // 2 | // VTSVideoButton.m 3 | // VideoTransitionsSample 4 | // 5 | // Created by Ruslan Shevtsov on 4/9/15. 6 | // Copyright (c) 2015 iQueSoft. All rights reserved. 7 | // 8 | 9 | #import "VTSVideoButton.h" 10 | 11 | // UI 12 | #import "VTSIgnoreTouchesView.h" 13 | #import "VTSPlayerView.h" 14 | 15 | // ======= VTSDecorView ======= 16 | 17 | @interface VTSDecorView : VTSIgnoreTouchesView 18 | 19 | @property (nonatomic, assign) CGFloat lineWidth; 20 | @property (nonatomic, assign) CGFloat lineProportion; 21 | @property (nonatomic, assign) CGFloat lineLenght; 22 | 23 | @property (nonatomic, assign) BOOL showCross; 24 | 25 | @end 26 | 27 | @implementation VTSDecorView 28 | 29 | - (instancetype)init { 30 | self = [super init]; 31 | if (!self) { 32 | return nil; 33 | } 34 | 35 | self.backgroundColor = [UIColor clearColor]; 36 | 37 | return self; 38 | } 39 | 40 | - (void)drawRect:(CGRect)rect { 41 | [super drawRect:rect]; 42 | 43 | CGContextRef context = UIGraphicsGetCurrentContext(); 44 | CGContextSetStrokeColorWithColor(context, [UIColor colorWithRed:233.0f/255.0f green:56.0f/255.0f blue:59.0f/255.0f alpha:1.0f].CGColor); 45 | 46 | CGContextSetLineWidth(context, self.lineWidth); 47 | 48 | CGSize selfSize = self.frame.size; 49 | 50 | // 1 51 | CGContextMoveToPoint(context, 0.0f, self.lineWidth / 2.0f); 52 | 53 | CGContextAddLineToPoint(context, self.lineLenght, self.lineWidth / 2.0f); 54 | 55 | // 2 56 | CGContextMoveToPoint(context, self.lineWidth / 2.0f, 0.0f); 57 | 58 | CGContextAddLineToPoint(context, self.lineWidth / 2.0f, self.lineLenght); 59 | 60 | // 3 61 | CGContextMoveToPoint(context, self.lineWidth / 2.0f, selfSize.height); 62 | 63 | CGContextAddLineToPoint(context, self.lineWidth / 2.0f, selfSize.height - self.lineLenght); 64 | 65 | // 4 66 | CGContextMoveToPoint(context, self.lineWidth / 2.0f, selfSize.height - self.lineWidth / 2.0f); 67 | 68 | CGContextAddLineToPoint(context, self.lineLenght, selfSize.height - self.lineWidth / 2.0f); 69 | 70 | // 5 71 | CGContextMoveToPoint(context, selfSize.width + self.lineWidth / 2.0f, self.lineWidth / 2.0f); 72 | 73 | CGContextAddLineToPoint(context, selfSize.width - self.lineLenght, self.lineWidth / 2.0f); 74 | 75 | // 6 76 | CGContextMoveToPoint(context, selfSize.width - self.lineWidth / 2.0f, self.lineWidth / 2.0f); 77 | 78 | CGContextAddLineToPoint(context, selfSize.width - self.lineWidth / 2.0f, self.lineLenght); 79 | 80 | // 7 81 | CGContextMoveToPoint(context, selfSize.width - self.lineWidth / 2.0f, selfSize.height); 82 | 83 | CGContextAddLineToPoint(context, selfSize.width - self.lineWidth / 2.0f, selfSize.height - self.lineLenght); 84 | 85 | // 8 86 | CGContextMoveToPoint(context, selfSize.width - self.lineWidth / 2.0f, selfSize.height - self.lineWidth / 2.0f); 87 | 88 | CGContextAddLineToPoint(context, selfSize.width - self.lineLenght, selfSize.height - self.lineWidth / 2.0f); 89 | 90 | // cross 91 | if (self.showCross) { 92 | 93 | CGContextMoveToPoint(context, selfSize.width / 2.0f, selfSize.height / 2.0f - self.lineLenght / 2.0f); 94 | 95 | CGContextAddLineToPoint(context, selfSize.width / 2.0f, selfSize.height / 2.0f + self.lineLenght / 2.0f); 96 | 97 | CGContextMoveToPoint(context, selfSize.width / 2.0f - self.lineLenght / 2.0f, selfSize.height / 2.0f); 98 | 99 | CGContextAddLineToPoint(context, selfSize.width / 2.0f + self.lineLenght / 2.0f, selfSize.height / 2.0f); 100 | } 101 | 102 | CGContextStrokePath(context); 103 | } 104 | 105 | #pragma mark - 106 | #pragma mark Setters 107 | 108 | - (void)setShowCross:(BOOL)showCross { 109 | _showCross = showCross; 110 | [self setNeedsDisplay]; 111 | } 112 | 113 | 114 | #pragma mark - 115 | #pragma mark Getters 116 | 117 | - (CGFloat)lineLenght { 118 | return (self.frame.size.width < self.frame.size.height) ? self.frame.size.width / self.lineProportion : self.frame.size.height / self.lineProportion; 119 | } 120 | 121 | @end 122 | 123 | // ======= VTSVideoButton ======= 124 | 125 | @interface VTSVideoButton () 126 | 127 | @property (nonatomic, strong) VTSDecorView *decorView; 128 | 129 | @end 130 | 131 | @implementation VTSVideoButton 132 | 133 | #pragma mark - 134 | #pragma mark Update UI for preview 135 | 136 | - (void)startPreview { 137 | self.playerView.alpha = 1.0f; 138 | self.decorView.showCross = NO; 139 | [self.decorView setNeedsDisplay]; 140 | } 141 | 142 | - (void)stopPreview { 143 | self.playerView.alpha = 3.0f; 144 | self.decorView.showCross = YES; 145 | [self.decorView setNeedsDisplay]; 146 | } 147 | 148 | - (void)awakeFromNib { 149 | [super awakeFromNib]; 150 | 151 | self.backgroundColor = [UIColor clearColor]; 152 | 153 | self.playerView = [VTSPlayerView new]; 154 | self.playerView.backgroundColor = [UIColor blackColor]; 155 | self.playerView.alpha = 0.3f; 156 | [self addSubview:self.playerView]; 157 | 158 | self.decorView = [VTSDecorView new]; 159 | 160 | self.decorView.lineWidth = 8; 161 | self.decorView.lineProportion = 3.5f; 162 | self.decorView.showCross = YES; 163 | 164 | [self addSubview:self.decorView]; 165 | 166 | [self setupDecorSubviewsSize]; 167 | } 168 | 169 | - (void)setupDecorSubviewsSize { 170 | CGRect bounds = self.bounds; 171 | self.playerView.frame = CGRectMake(self.decorView.lineWidth / 2.0f, 172 | self.decorView.lineWidth / 2.0f, 173 | bounds.size.width - self.decorView.lineWidth, 174 | bounds.size.height - self.decorView.lineWidth); 175 | 176 | self.decorView.frame = bounds; 177 | 178 | [self.decorView setNeedsDisplay]; 179 | } 180 | 181 | @end 182 | -------------------------------------------------------------------------------- /VideoTransitionsSample/UI layer/Custom elements/IgnoreTouchesView/VTSIgnoreTouchesView.h: -------------------------------------------------------------------------------- 1 | // 2 | // VTSIgnoreTouchesView.h 3 | // VideoTransitionsSample 4 | // 5 | // Created by Ruslan Shevtsov on 4/15/15. 6 | // Copyright (c) 2015 iQueSoft. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface VTSIgnoreTouchesView : UIView 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /VideoTransitionsSample/UI layer/Custom elements/IgnoreTouchesView/VTSIgnoreTouchesView.m: -------------------------------------------------------------------------------- 1 | // 2 | // VTSIgnoreTouchesView.m 3 | // VideoTransitionsSample 4 | // 5 | // Created by Ruslan Shevtsov on 4/15/15. 6 | // Copyright (c) 2015 iQueSoft. All rights reserved. 7 | // 8 | 9 | #import "VTSIgnoreTouchesView.h" 10 | 11 | @interface VTSIgnoreTouchesView () 12 | 13 | @end 14 | 15 | @implementation VTSIgnoreTouchesView 16 | 17 | - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { 18 | return self.superview; 19 | } 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /VideoTransitionsSample/UI layer/Custom elements/PagedFlowView/PagedFlowView.h: -------------------------------------------------------------------------------- 1 | // 2 | // PagedFlowView.h 3 | // taobao4iphone 4 | // 5 | // Created by Lu Kejin on 3/2/12. 6 | // Copyright (c) 2012 geeklu.com. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @protocol PagedFlowViewDataSource; 12 | @protocol PagedFlowViewDelegate; 13 | 14 | /****************************** 15 | 名词解释: 16 | Page是指某一页 17 | Cell某一页对应的View 18 | Index是某一页的页码,从0开始 19 | _visibleRange 是指当前可见Page的页码范围 20 | _visibleCells 是指当前可见Page对应的Views 21 | 22 | 说明: 23 | 页面滚动的方向分为横向和纵向 24 | 25 | Version 0.1: 26 | 本控件的目标是可以想使用UITableView那样使用翻页的View,且在显示当前页的同时,还可以看到与其相邻的页的部分内容 27 | 可以实现诸如Mobie Safari预览所有打开页面或者App Store中截图查看的效果 28 | 目前假设每一页的宽度都是一样的,每一页的View的类型也是一样的,如果后期需要修改,支持不同页的View类型不同,则需要对页面重用机制进行修改,需要为其页面的View创建一个基类View,添加reuseIdentifier属性 29 | ******************************/ 30 | 31 | typedef enum{ 32 | PagedFlowViewOrientationHorizontal = 0, 33 | PagedFlowViewOrientationVertical 34 | }PagedFlowViewOrientation; 35 | 36 | @interface PagedFlowView : UIView{ 37 | 38 | PagedFlowViewOrientation orientation;//默认为横向 39 | 40 | UIScrollView *_scrollView; 41 | BOOL _needsReload; 42 | CGSize _pageSize; //一页的尺寸 43 | NSInteger _pageCount; //总页数 44 | NSInteger _currentPageIndex; 45 | 46 | NSMutableArray *_cells; 47 | NSRange _visibleRange; 48 | NSMutableArray *_reusableCells;//如果以后需要支持reuseIdentifier,这边就得使用字典类型了 49 | 50 | UIPageControl *pageControl; //可以是自己自定义的PageControl 51 | 52 | //如果希望非当前页的大小或者透明度发生变化可以设置这两个值 53 | CGFloat _minimumPageAlpha; 54 | CGFloat _minimumPageScale; 55 | 56 | 57 | id __weak _dataSource; 58 | id __weak _delegate; 59 | } 60 | 61 | @property(nonatomic,weak) id dataSource; 62 | @property(nonatomic,weak) id delegate; 63 | @property(nonatomic,strong) UIPageControl *pageControl; 64 | @property (nonatomic, assign) CGFloat minimumPageAlpha; 65 | @property (nonatomic, assign) CGFloat minimumPageScale; 66 | @property (nonatomic, assign) PagedFlowViewOrientation orientation; 67 | @property (nonatomic, assign, readonly) NSInteger currentPageIndex; 68 | 69 | - (void)reloadData; 70 | 71 | //获取可重复使用的Cell 72 | - (UIView *)dequeueReusableCell; 73 | 74 | - (void)scrollToPage:(NSUInteger)pageNumber; 75 | 76 | @end 77 | 78 | 79 | @protocol PagedFlowViewDelegate 80 | 81 | - (CGSize)sizeForPageInFlowView:(PagedFlowView *)flowView; 82 | 83 | @optional 84 | 85 | - (void)flowView:(PagedFlowView *)flowView didScrollToPageAtIndex:(NSInteger)index; 86 | 87 | - (void)flowView:(PagedFlowView *)flowView didTapPageAtIndex:(NSInteger)index; 88 | 89 | @end 90 | 91 | 92 | @protocol PagedFlowViewDataSource 93 | 94 | //返回显示View的个数 95 | - (NSInteger)numberOfPagesInFlowView:(PagedFlowView *)flowView; 96 | 97 | //返回给某列使用的View 98 | - (UIView *)flowView:(PagedFlowView *)flowView cellForPageAtIndex:(NSInteger)index; 99 | 100 | @end -------------------------------------------------------------------------------- /VideoTransitionsSample/UI layer/Custom elements/PagedFlowView/PagedFlowView.m: -------------------------------------------------------------------------------- 1 | // 2 | // PagedFlowView.m 3 | // taobao4iphone 4 | // 5 | // Created by Lu Kejin on 3/2/12. 6 | // Copyright (c) 2012 geeklu.com. All rights reserved. 7 | // 8 | 9 | #import "PagedFlowView.h" 10 | #import 11 | 12 | @interface PagedFlowView () 13 | @property (nonatomic, assign, readwrite) NSInteger currentPageIndex; 14 | @end 15 | 16 | @implementation PagedFlowView 17 | @synthesize dataSource = _dataSource; 18 | @synthesize delegate = _delegate; 19 | @synthesize pageControl; 20 | @synthesize minimumPageAlpha = _minimumPageAlpha; 21 | @synthesize minimumPageScale = _minimumPageScale; 22 | @synthesize orientation; 23 | @synthesize currentPageIndex = _currentPageIndex; 24 | 25 | //////////////////////////////////////////////////////////////////////////////////////////////////// 26 | #pragma mark - 27 | #pragma mark Private Methods 28 | -(void)handleTapGesture:(UIGestureRecognizer*)gestureRecognizer{ 29 | NSInteger tappedIndex = 0; 30 | CGPoint locationInScrollView = [gestureRecognizer locationInView:_scrollView]; 31 | if (CGRectContainsPoint(_scrollView.bounds, locationInScrollView)) { 32 | tappedIndex = _currentPageIndex; 33 | if ([self.delegate respondsToSelector:@selector(flowView:didTapPageAtIndex:)]) { 34 | [self.delegate flowView:self didTapPageAtIndex:tappedIndex]; 35 | } 36 | } 37 | } 38 | 39 | - (void)initialize{ 40 | self.clipsToBounds = YES; 41 | 42 | UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)]; 43 | [self addGestureRecognizer:tapRecognizer]; 44 | 45 | _needsReload = YES; 46 | _pageSize = self.bounds.size; 47 | _pageCount = 0; 48 | _currentPageIndex = 0; 49 | 50 | _minimumPageAlpha = 1.0; 51 | _minimumPageScale = 1.0; 52 | 53 | _visibleRange = NSMakeRange(0, 0); 54 | 55 | _reusableCells = [[NSMutableArray alloc] initWithCapacity:0]; 56 | _cells = [[NSMutableArray alloc] initWithCapacity:0]; 57 | 58 | _scrollView = [[UIScrollView alloc] initWithFrame:self.bounds]; 59 | _scrollView.delegate = self; 60 | _scrollView.pagingEnabled = YES; 61 | _scrollView.clipsToBounds = NO; 62 | _scrollView.showsHorizontalScrollIndicator = NO; 63 | _scrollView.showsVerticalScrollIndicator = NO; 64 | 65 | /*由于UIScrollView在滚动之后会调用自己的layoutSubviews以及父View的layoutSubviews 66 | 这里为了避免scrollview滚动带来自己layoutSubviews的调用,所以给scrollView加了一层父View 67 | */ 68 | UIView *superViewOfScrollView = [[UIView alloc] initWithFrame:self.bounds]; 69 | [superViewOfScrollView setAutoresizingMask:UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight]; 70 | [superViewOfScrollView setBackgroundColor:[UIColor clearColor]]; 71 | [superViewOfScrollView addSubview:_scrollView]; 72 | [self addSubview:superViewOfScrollView]; 73 | 74 | } 75 | 76 | 77 | - (void)dealloc{ 78 | _scrollView.delegate = nil; 79 | } 80 | 81 | - (void)queueReusableCell:(UIView *)cell{ 82 | [_reusableCells addObject:cell]; 83 | } 84 | 85 | - (void)removeCellAtIndex:(NSInteger)index{ 86 | UIView *cell = [_cells objectAtIndex:index]; 87 | if ((NSObject *)cell == [NSNull null]) { 88 | return; 89 | } 90 | 91 | [self queueReusableCell:cell]; 92 | 93 | if (cell.superview) { 94 | cell.layer.transform = CATransform3DIdentity; 95 | [cell removeFromSuperview]; 96 | } 97 | 98 | [_cells replaceObjectAtIndex:index withObject:[NSNull null]]; 99 | } 100 | 101 | - (void)refreshVisibleCellAppearance{ 102 | 103 | if (_minimumPageAlpha == 1.0 && _minimumPageScale == 1.0) { 104 | return;//无需更新 105 | } 106 | switch (orientation) { 107 | case PagedFlowViewOrientationHorizontal:{ 108 | CGFloat offset = _scrollView.contentOffset.x; 109 | 110 | for (NSUInteger i = _visibleRange.location; i < _visibleRange.location + _visibleRange.length; i++) { 111 | UIView *cell = [_cells objectAtIndex:i]; 112 | CGFloat origin = cell.frame.origin.x; 113 | CGFloat delta = fabs(origin - offset); 114 | 115 | [UIView beginAnimations:@"CellAnimation" context:nil]; 116 | if (delta < _pageSize.width) { 117 | cell.alpha = 1 - (delta / _pageSize.width) * (1 - _minimumPageAlpha); 118 | 119 | CGFloat pageScale = 1 - (delta / _pageSize.width) * (1 - _minimumPageScale); 120 | cell.layer.transform = CATransform3DMakeScale(pageScale, pageScale, 1); 121 | } else { 122 | cell.alpha = _minimumPageAlpha; 123 | cell.layer.transform = CATransform3DMakeScale(_minimumPageScale, _minimumPageScale, 1); 124 | } 125 | [UIView commitAnimations]; 126 | } 127 | break; 128 | } 129 | case PagedFlowViewOrientationVertical:{ 130 | CGFloat offset = _scrollView.contentOffset.y; 131 | 132 | for (NSUInteger i = _visibleRange.location; i < _visibleRange.location + _visibleRange.length; i++) { 133 | UIView *cell = [_cells objectAtIndex:i]; 134 | CGFloat origin = cell.frame.origin.y; 135 | CGFloat delta = fabs(origin - offset); 136 | 137 | [UIView beginAnimations:@"CellAnimation" context:nil]; 138 | if (delta < _pageSize.height) { 139 | cell.alpha = 1 - (delta / _pageSize.height) * (1 - _minimumPageAlpha); 140 | 141 | CGFloat pageScale = 1 - (delta / _pageSize.height) * (1 - _minimumPageScale); 142 | cell.layer.transform = CATransform3DMakeScale(pageScale, pageScale, 1); 143 | } else { 144 | cell.alpha = _minimumPageAlpha; 145 | cell.layer.transform = CATransform3DMakeScale(_minimumPageScale, _minimumPageScale, 1); 146 | } 147 | [UIView commitAnimations]; 148 | } 149 | } 150 | default: 151 | break; 152 | } 153 | 154 | } 155 | 156 | - (void)setPageAtIndex:(NSInteger)pageIndex{ 157 | NSParameterAssert(pageIndex >= 0 && pageIndex < [_cells count]); 158 | 159 | UIView *cell = [_cells objectAtIndex:pageIndex]; 160 | 161 | if ((NSObject *)cell == [NSNull null]) { 162 | cell = [_dataSource flowView:self cellForPageAtIndex:pageIndex]; 163 | NSAssert(cell!=nil, @"datasource must not return nil"); 164 | [_cells replaceObjectAtIndex:pageIndex withObject:cell]; 165 | 166 | 167 | switch (orientation) { 168 | case PagedFlowViewOrientationHorizontal: 169 | cell.frame = CGRectMake(_pageSize.width * pageIndex, 0, _pageSize.width, _pageSize.height); 170 | break; 171 | case PagedFlowViewOrientationVertical: 172 | cell.frame = CGRectMake(0, _pageSize.height * pageIndex, _pageSize.width, _pageSize.height); 173 | break; 174 | default: 175 | break; 176 | } 177 | 178 | if (!cell.superview) { 179 | [_scrollView addSubview:cell]; 180 | } 181 | } 182 | } 183 | 184 | 185 | - (void)setPagesAtContentOffset:(CGPoint)offset{ 186 | if ([_cells count] == 0) 187 | return; 188 | //计算_visibleRange 189 | CGPoint startPoint = CGPointMake(offset.x - _scrollView.frame.origin.x, offset.y - _scrollView.frame.origin.y); 190 | CGPoint endPoint = CGPointMake(MAX(0, startPoint.x) + self.bounds.size.width, MAX(0, startPoint.y) + self.bounds.size.height); 191 | 192 | 193 | switch (orientation) { 194 | case PagedFlowViewOrientationHorizontal:{ 195 | NSInteger startIndex = 0; 196 | for (int i =0; i < [_cells count]; i++) { 197 | if (_pageSize.width * (i +1) > startPoint.x) { 198 | startIndex = i; 199 | break; 200 | } 201 | } 202 | 203 | NSInteger endIndex = startIndex; 204 | for (NSUInteger i = startIndex; i < [_cells count]; i++) { 205 | //如果都不超过则取最后一个 206 | if ((_pageSize.width * (i + 1) < endPoint.x && _pageSize.width * (i + 2) >= endPoint.x) || i+ 2 == [_cells count]) { 207 | endIndex = i + 1;//i+2 是以个数,所以其index需要减去1 208 | break; 209 | } 210 | } 211 | 212 | //可见页分别向前向后扩展一个,提高效率 213 | startIndex = MAX(startIndex - 1, 0); 214 | endIndex = MIN(endIndex + 1, [_cells count] - 1); 215 | 216 | if (_visibleRange.location == startIndex && _visibleRange.length == (endIndex - startIndex + 1)) { 217 | return; 218 | } 219 | 220 | _visibleRange.location = startIndex; 221 | _visibleRange.length = endIndex - startIndex + 1; 222 | 223 | for (NSUInteger i = startIndex; i <= endIndex; i++) { 224 | [self setPageAtIndex:i]; 225 | } 226 | 227 | for (int i = 0; i < startIndex; i ++) { 228 | [self removeCellAtIndex:i]; 229 | } 230 | 231 | for (NSUInteger i = endIndex + 1; i < [_cells count]; i ++) { 232 | [self removeCellAtIndex:i]; 233 | } 234 | break; 235 | } 236 | case PagedFlowViewOrientationVertical:{ 237 | NSInteger startIndex = 0; 238 | for (int i =0; i < [_cells count]; i++) { 239 | if (_pageSize.height * (i +1) > startPoint.y) { 240 | startIndex = i; 241 | break; 242 | } 243 | } 244 | 245 | NSInteger endIndex = startIndex; 246 | for (NSUInteger i = startIndex; i < [_cells count]; i++) { 247 | //如果都不超过则取最后一个 248 | if ((_pageSize.height * (i + 1) < endPoint.y && _pageSize.height * (i + 2) >= endPoint.y) || i+ 2 == [_cells count]) { 249 | endIndex = i + 1;//i+2 是以个数,所以其index需要减去1 250 | break; 251 | } 252 | } 253 | 254 | //可见页分别向前向后扩展一个,提高效率 255 | startIndex = MAX(startIndex - 1, 0); 256 | endIndex = MIN(endIndex + 1, [_cells count] - 1); 257 | 258 | if (_visibleRange.location == startIndex && _visibleRange.length == (endIndex - startIndex + 1)) { 259 | return; 260 | } 261 | 262 | _visibleRange.location = startIndex; 263 | _visibleRange.length = endIndex - startIndex + 1; 264 | 265 | for (NSUInteger i = startIndex; i <= endIndex; i++) { 266 | [self setPageAtIndex:i]; 267 | } 268 | 269 | for (int i = 0; i < startIndex; i ++) { 270 | [self removeCellAtIndex:i]; 271 | } 272 | 273 | for (NSUInteger i = endIndex + 1; i < [_cells count]; i ++) { 274 | [self removeCellAtIndex:i]; 275 | } 276 | break; 277 | } 278 | default: 279 | break; 280 | } 281 | 282 | 283 | 284 | } 285 | 286 | 287 | 288 | 289 | //////////////////////////////////////////////////////////////////////////////////////////////////// 290 | #pragma mark - 291 | #pragma mark Override Methods 292 | 293 | - (id)initWithFrame:(CGRect)frame 294 | { 295 | self = [super initWithFrame:frame]; 296 | if (self) 297 | { 298 | [self initialize]; 299 | } 300 | return self; 301 | } 302 | 303 | - (id)initWithCoder:(NSCoder *)aDecoder 304 | { 305 | self = [super initWithCoder:aDecoder]; 306 | if (self) 307 | { 308 | [self initialize]; 309 | } 310 | return self; 311 | } 312 | 313 | - (void)layoutSubviews{ 314 | [super layoutSubviews]; 315 | 316 | if (_needsReload) { 317 | //如果需要重新加载数据,则需要清空相关数据全部重新加载 318 | 319 | 320 | //重置pageCount 321 | if (_dataSource && [_dataSource respondsToSelector:@selector(numberOfPagesInFlowView:)]) { 322 | _pageCount = [_dataSource numberOfPagesInFlowView:self]; 323 | 324 | if (pageControl && [pageControl respondsToSelector:@selector(setNumberOfPages:)]) { 325 | [pageControl setNumberOfPages:_pageCount]; 326 | } 327 | } 328 | 329 | //重置pageWidth 330 | if (_delegate && [_delegate respondsToSelector:@selector(sizeForPageInFlowView:)]) { 331 | _pageSize = [_delegate sizeForPageInFlowView:self]; 332 | } 333 | 334 | [_reusableCells removeAllObjects]; 335 | _visibleRange = NSMakeRange(0, 0); 336 | 337 | //从supperView上移除cell 338 | for (NSInteger i=0; i<[_cells count]; i++) { 339 | [self removeCellAtIndex:i]; 340 | } 341 | 342 | //填充cells数组 343 | [_cells removeAllObjects]; 344 | for (NSInteger index=0; index<_pageCount; index++) 345 | { 346 | [_cells addObject:[NSNull null]]; 347 | } 348 | 349 | // 重置_scrollView的contentSize 350 | switch (orientation) { 351 | case PagedFlowViewOrientationHorizontal://横向 352 | _scrollView.frame = CGRectMake(0, 0, _pageSize.width, _pageSize.height); 353 | _scrollView.contentSize = CGSizeMake(_pageSize.width * _pageCount,_pageSize.height); 354 | CGPoint theCenter = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds)); 355 | _scrollView.center = theCenter; 356 | break; 357 | case PagedFlowViewOrientationVertical:{ 358 | _scrollView.frame = CGRectMake(0, 0, _pageSize.width, _pageSize.height); 359 | _scrollView.contentSize = CGSizeMake(_pageSize.width ,_pageSize.height * _pageCount); 360 | CGPoint theCenter = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds)); 361 | _scrollView.center = theCenter; 362 | break; 363 | } 364 | default: 365 | break; 366 | } 367 | } 368 | 369 | 370 | [self setPagesAtContentOffset:_scrollView.contentOffset];//根据当前scrollView的offset设置cell 371 | 372 | [self refreshVisibleCellAppearance];//更新各个可见Cell的显示外貌 373 | 374 | } 375 | 376 | //////////////////////////////////////////////////////////////////////////////////////////////////// 377 | #pragma mark - 378 | #pragma mark PagedFlowView API 379 | 380 | - (void)reloadData 381 | { 382 | _needsReload = YES; 383 | 384 | [self setNeedsLayout]; 385 | } 386 | 387 | 388 | - (UIView *)dequeueReusableCell{ 389 | UIView *cell = [_reusableCells lastObject]; 390 | if (cell) 391 | { 392 | [_reusableCells removeLastObject]; 393 | } 394 | 395 | return cell; 396 | } 397 | 398 | - (void)scrollToPage:(NSUInteger)pageNumber { 399 | if (pageNumber < _pageCount) { 400 | switch (orientation) { 401 | case PagedFlowViewOrientationHorizontal: 402 | [_scrollView setContentOffset:CGPointMake(_pageSize.width * pageNumber, 0) animated:YES]; 403 | break; 404 | case PagedFlowViewOrientationVertical: 405 | [_scrollView setContentOffset:CGPointMake(0, _pageSize.height * pageNumber) animated:YES]; 406 | break; 407 | } 408 | [self setPagesAtContentOffset:_scrollView.contentOffset]; 409 | [self refreshVisibleCellAppearance]; 410 | } 411 | } 412 | 413 | //////////////////////////////////////////////////////////////////////////////////////////////////// 414 | #pragma mark - 415 | #pragma mark hitTest 416 | 417 | - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { 418 | if ([self pointInside:point withEvent:event]) { 419 | CGPoint newPoint = CGPointZero; 420 | newPoint.x = point.x - _scrollView.frame.origin.x + _scrollView.contentOffset.x; 421 | newPoint.y = point.y - _scrollView.frame.origin.y + _scrollView.contentOffset.y; 422 | if ([_scrollView pointInside:newPoint withEvent:event]) { 423 | return [_scrollView hitTest:newPoint withEvent:event]; 424 | } 425 | 426 | return _scrollView; 427 | } 428 | 429 | return nil; 430 | } 431 | 432 | 433 | //////////////////////////////////////////////////////////////////////////////////////////////////// 434 | #pragma mark - 435 | #pragma mark UIScrollView Delegate 436 | 437 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView{ 438 | [self setPagesAtContentOffset:scrollView.contentOffset]; 439 | [self refreshVisibleCellAppearance]; 440 | } 441 | 442 | 443 | - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{ 444 | //如果有PageControl,计算出当前页码,并对pageControl进行更新 445 | 446 | NSInteger pageIndex; 447 | 448 | switch (orientation) { 449 | case PagedFlowViewOrientationHorizontal: 450 | pageIndex = floor(_scrollView.contentOffset.x / _pageSize.width); 451 | break; 452 | case PagedFlowViewOrientationVertical: 453 | pageIndex = floor(_scrollView.contentOffset.y / _pageSize.height); 454 | break; 455 | default: 456 | break; 457 | } 458 | 459 | if (pageControl && [pageControl respondsToSelector:@selector(setCurrentPage:)]) { 460 | [pageControl setCurrentPage:pageIndex]; 461 | } 462 | 463 | if ([_delegate respondsToSelector:@selector(flowView:didScrollToPageAtIndex:)] && _currentPageIndex != pageIndex) { 464 | [_delegate flowView:self didScrollToPageAtIndex:pageIndex]; 465 | } 466 | 467 | _currentPageIndex = pageIndex; 468 | } 469 | 470 | @end 471 | -------------------------------------------------------------------------------- /VideoTransitionsSample/UI layer/Custom elements/PlayerView/VTSPlayerView.h: -------------------------------------------------------------------------------- 1 | // 2 | // VTSPlayerView.h 3 | // VideoTransitionsSample 4 | // 5 | // Created by Ruslan Shevtsov on 4/15/15. 6 | // Copyright (c) 2015 iQueSoft. All rights reserved. 7 | // 8 | 9 | #import "VTSIgnoreTouchesView.h" 10 | 11 | @class AVPlayer; 12 | 13 | @interface VTSPlayerView : VTSIgnoreTouchesView 14 | 15 | @property (nonatomic, strong) AVPlayer *player; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /VideoTransitionsSample/UI layer/Custom elements/PlayerView/VTSPlayerView.m: -------------------------------------------------------------------------------- 1 | // 2 | // VTSPlayerView.m 3 | // VideoTransitionsSample 4 | // 5 | // Created by Ruslan Shevtsov on 4/15/15. 6 | // Copyright (c) 2015 iQueSoft. All rights reserved. 7 | // 8 | 9 | #import "VTSPlayerView.h" 10 | 11 | // Frameworks 12 | #import 13 | 14 | @interface VTSPlayerView () 15 | 16 | @end 17 | 18 | @implementation VTSPlayerView 19 | 20 | + (Class)layerClass { 21 | return [AVPlayerLayer class]; 22 | } 23 | 24 | - (AVPlayer *)player { 25 | return [(AVPlayerLayer *)[self layer] player]; 26 | } 27 | 28 | - (void)setPlayer:(AVPlayer *)player { 29 | ((AVPlayerLayer *)self.layer).videoGravity = AVLayerVideoGravityResizeAspectFill; 30 | [(AVPlayerLayer *)[self layer] setPlayer:player]; 31 | } 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /VideoTransitionsSample/UI layer/ViewControllers/BaseViewController/VTSBaseViewController+ShwAlertOK.h: -------------------------------------------------------------------------------- 1 | // 2 | // VTSBaseViewController+ShwAlertOK.h 3 | // VideoTransitionsSample 4 | // 5 | // Created by Ruslan Shevtsov on 4/13/15. 6 | // Copyright (c) 2015 iQueSoft. All rights reserved. 7 | // 8 | 9 | #import "VTSBaseViewController.h" 10 | 11 | @interface VTSBaseViewController (ShwAlertOK) 12 | 13 | - (void)showAlertOKWithText:(NSString *)aTextString; 14 | 15 | - (void)showAlertOKWithTitle:(NSString *)aTitleString message:(NSString *)aMessageString; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /VideoTransitionsSample/UI layer/ViewControllers/BaseViewController/VTSBaseViewController+ShwAlertOK.m: -------------------------------------------------------------------------------- 1 | // 2 | // VTSBaseViewController+ShwAlertOK.m 3 | // VideoTransitionsSample 4 | // 5 | // Created by Ruslan Shevtsov on 4/13/15. 6 | // Copyright (c) 2015 iQueSoft. All rights reserved. 7 | // 8 | 9 | #import "VTSBaseViewController+ShwAlertOK.h" 10 | 11 | // Strings 12 | #define kOKString NSLocalizedString(@"OK", nil) 13 | 14 | @implementation VTSBaseViewController (ShwAlertOK) 15 | 16 | - (void)showAlertOKWithText:(NSString *)aTextString { 17 | [self showAlertOKWithTitle:aTextString message:nil]; 18 | } 19 | 20 | - (void)showAlertOKWithTitle:(NSString *)aTitleString message:(NSString *)aMessageString { 21 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:aTitleString message:aMessageString preferredStyle:UIAlertControllerStyleAlert]; 22 | UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:kOKString style:UIAlertActionStyleCancel handler:nil]; 23 | [alert addAction:cancelAction]; 24 | [self presentViewController:alert animated:YES completion:nil]; 25 | } 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /VideoTransitionsSample/UI layer/ViewControllers/BaseViewController/VTSBaseViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // VTSBaseViewController.h 3 | // VideoTransitionsSample 4 | // 5 | // Created by Ruslan Shevtsov on 4/9/15. 6 | // Copyright (c) 2015 iQueSoft. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface VTSBaseViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /VideoTransitionsSample/UI layer/ViewControllers/BaseViewController/VTSBaseViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // VTSBaseViewController.m 3 | // VideoTransitionsSample 4 | // 5 | // Created by Ruslan Shevtsov on 4/9/15. 6 | // Copyright (c) 2015 iQueSoft. All rights reserved. 7 | // 8 | 9 | #import "VTSBaseViewController.h" 10 | 11 | // Frameworks 12 | #import 13 | #import "GPUImage.h" 14 | 15 | @interface VTSBaseViewController () 16 | 17 | @property (nonatomic, strong) FRSGPUImageWrapper *gpuImageWrapper; 18 | 19 | @end 20 | 21 | @implementation VTSBaseViewController 22 | 23 | #pragma mark - 24 | #pragma mark Life cycle 25 | 26 | - (void)viewDidLoad { 27 | [super viewDidLoad]; 28 | 29 | if ([self.view isKindOfClass:[GPUImageView class]]) { 30 | GPUImageView *previewView = (GPUImageView *)self.view; 31 | previewView.fillMode = kGPUImageFillModePreserveAspectRatioAndFill; 32 | 33 | self.gpuImageWrapper = [[FRSGPUImageWrapper alloc] initWithPreviewView:previewView 34 | orientation:[UIApplication sharedApplication].statusBarOrientation 35 | sessionPreset:AVCaptureSessionPreset352x288]; 36 | } 37 | 38 | } 39 | 40 | - (void)viewWillAppear:(BOOL)animated { 41 | [super viewWillAppear:animated]; 42 | 43 | } 44 | 45 | - (void)viewDidAppear:(BOOL)animated { 46 | [super viewDidAppear:animated]; 47 | 48 | [self.gpuImageWrapper startBlur]; 49 | } 50 | 51 | - (void)viewWillDisappear:(BOOL)animated { 52 | [super viewWillDisappear:animated]; 53 | 54 | [self.gpuImageWrapper stopBlur]; 55 | } 56 | 57 | - (void)viewDidDisappear:(BOOL)animated { 58 | [super viewDidDisappear:animated]; 59 | 60 | 61 | } 62 | 63 | #pragma mark - 64 | #pragma mark UIViewControllerRotation 65 | 66 | - (BOOL)shouldAutorotate { 67 | UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation; 68 | self.gpuImageWrapper.videoCamera.outputImageOrientation = orientation; 69 | return YES; 70 | } 71 | 72 | @end 73 | -------------------------------------------------------------------------------- /VideoTransitionsSample/UI layer/ViewControllers/MainViewController/VTSMainViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // VTSMainViewController.h 3 | // VideoTransitionsSample 4 | // 5 | // Created by Ruslan Shevtsov on 4/10/15. 6 | // Copyright (c) 2015 iQueSoft. All rights reserved. 7 | // 8 | 9 | #import "VTSBaseViewController.h" 10 | 11 | @interface VTSMainViewController : VTSBaseViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /VideoTransitionsSample/UI layer/ViewControllers/MainViewController/VTSMainViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // VTSMainViewController.m 3 | // VideoTransitionsSample 4 | // 5 | // Created by Ruslan Shevtsov on 4/10/15. 6 | // Copyright (c) 2015 iQueSoft. All rights reserved. 7 | // 8 | 9 | #import "VTSMainViewController.h" 10 | 11 | // Frameworks 12 | #import 13 | #import 14 | 15 | // Business facade 16 | #import "VTSBusinessFacade.h" 17 | 18 | // UI 19 | #import "VTSVideoButton.h" 20 | #import "UIAlertController+Blocks.h" 21 | #import "UIImagePickerController+DelegateBlocks.h" 22 | #import "MBProgressHUD.h" 23 | #import "VTSBaseViewController+ShwAlertOK.h" 24 | #import "VTSTransitionButton.h" 25 | #import "VTSTransitionsViewController.h" 26 | #import "VTSPlayerView.h" 27 | #import "VTSPlayerViewController.h" 28 | 29 | // Strings 30 | #define kErrorString NSLocalizedString(@"Error!", nil) 31 | #define kProcessingVideoString NSLocalizedString(@"Video processing...", nil) 32 | #define kSaveCompleteString NSLocalizedString(@"Save complete!", nil) 33 | #define kRecordVideoFirstString NSLocalizedString(@"You should record or choose a video first.", nil) 34 | 35 | @interface VTSMainViewController () 36 | 37 | @property (weak, nonatomic) IBOutlet VTSVideoButton *firstVideoButton; 38 | @property (weak, nonatomic) IBOutlet VTSVideoButton *secondVideoButton; 39 | @property (weak, nonatomic) IBOutlet VTSTransitionButton *transitionButton; 40 | 41 | @property (weak, nonatomic) IBOutlet UIView *transitionsView; 42 | @property (weak, nonatomic) IBOutlet UIView *interfaceContainerView; 43 | 44 | @end 45 | 46 | @implementation VTSMainViewController 47 | 48 | #pragma mark - 49 | #pragma mark Life cycle 50 | 51 | - (void)viewDidLoad { 52 | [super viewDidLoad]; 53 | 54 | [[VTSBusinessFacade sharedBusinessFacade] setTransitionType:VTSTransitionDefault]; 55 | 56 | self.firstVideoButton.playerView.player = [VTSBusinessFacade sharedBusinessFacade].firstVideoPreviewPlayer; 57 | self.secondVideoButton.playerView.player = [VTSBusinessFacade sharedBusinessFacade].secondVideoPreviewPlayer; 58 | self.transitionButton.playerView.player = [VTSBusinessFacade sharedBusinessFacade].transitionPreviewPlayer; 59 | 60 | [[NSNotificationCenter defaultCenter] addObserver:self 61 | selector:@selector(hidePlayerViewController:) 62 | name:kHidePlayerViewNotificationString 63 | object:nil]; 64 | } 65 | 66 | - (void)viewWillAppear:(BOOL)animated { 67 | [super viewWillAppear:animated]; 68 | 69 | } 70 | 71 | - (void)viewDidAppear:(BOOL)animated { 72 | [super viewDidAppear:animated]; 73 | 74 | [[VTSBusinessFacade sharedBusinessFacade].firstVideoPreviewPlayer play]; 75 | [[VTSBusinessFacade sharedBusinessFacade].secondVideoPreviewPlayer play]; 76 | [[VTSBusinessFacade sharedBusinessFacade].transitionPreviewPlayer play]; 77 | } 78 | 79 | - (void)viewWillDisappear:(BOOL)animated { 80 | [super viewWillDisappear:animated]; 81 | 82 | [[VTSBusinessFacade sharedBusinessFacade].firstVideoPreviewPlayer pause]; 83 | [[VTSBusinessFacade sharedBusinessFacade].secondVideoPreviewPlayer pause]; 84 | [[VTSBusinessFacade sharedBusinessFacade].transitionPreviewPlayer pause]; 85 | } 86 | 87 | - (void)viewDidDisappear:(BOOL)animated { 88 | [super viewDidDisappear:animated]; 89 | 90 | 91 | } 92 | 93 | - (void)dealloc { 94 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 95 | } 96 | 97 | #pragma mark - 98 | #pragma mark Orientation 99 | 100 | - (UIInterfaceOrientationMask)supportedInterfaceOrientations { 101 | return UIInterfaceOrientationMaskLandscape; 102 | } 103 | 104 | #pragma mark - 105 | #pragma mark IBActions 106 | 107 | - (IBAction)chooseFirstVideoAction:(VTSVideoButton *)sender { 108 | [self chooseVideoFromVideoButton:sender]; 109 | } 110 | 111 | - (IBAction)chooseSecondVideoButton:(VTSVideoButton *)sender { 112 | [self chooseVideoFromVideoButton:sender]; 113 | } 114 | 115 | - (IBAction)saveVideoAction:(id)sender { 116 | 117 | MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES]; 118 | hud.labelText = kProcessingVideoString; 119 | 120 | [[VTSBusinessFacade sharedBusinessFacade] exportToMovieWithCompletion:^(BOOL isDone, NSError *anError) { 121 | [MBProgressHUD hideHUDForView:self.view animated:YES]; 122 | if (isDone == NO) { 123 | [self showAlertOKWithTitle:kErrorString message:kRecordVideoFirstString]; 124 | } else { 125 | [self showAlertOKWithText:kSaveCompleteString]; 126 | } 127 | }]; 128 | } 129 | 130 | - (IBAction)showTransitionsViewAction:(id)sender { 131 | [self showTransitionsListView]; 132 | } 133 | 134 | #pragma mark - 135 | #pragma mark Choose video 136 | 137 | - (void)chooseVideoFromVideoButton:(VTSVideoButton *)videoButton { 138 | 139 | static NSString * const kGalleryTitleString = @"Gallery"; 140 | static NSString * const kCameraTitleString = @"Camera"; 141 | 142 | __weak __typeof(self)weakSelf = self; 143 | 144 | UIAlertControllerCompletionBlock tapBlock = ^(UIAlertController *controller, 145 | UIAlertAction *action, 146 | NSInteger buttonIndex) { 147 | if ([action.title isEqualToString:kGalleryTitleString]) { 148 | dispatch_async(dispatch_get_main_queue(), ^{ 149 | [weakSelf showImagePickerWithSourceType:UIImagePickerControllerSourceTypePhotoLibrary fromVideoButton:videoButton]; 150 | }); 151 | } else if ([action.title isEqualToString:kCameraTitleString]) { 152 | dispatch_async(dispatch_get_main_queue(), ^{ 153 | [weakSelf showImagePickerWithSourceType:UIImagePickerControllerSourceTypeCamera fromVideoButton:videoButton]; 154 | }); 155 | } 156 | }; 157 | 158 | [UIAlertController showActionSheetInViewController:self 159 | withTitle:@"Choose video" 160 | message:nil 161 | cancelButtonTitle:@"Cancel" 162 | destructiveButtonTitle:nil 163 | otherButtonTitles:@[kGalleryTitleString, kCameraTitleString] 164 | popoverPresentationControllerBlock:nil 165 | tapBlock:tapBlock]; 166 | } 167 | 168 | - (void)showImagePickerWithSourceType:(UIImagePickerControllerSourceType)sourceType 169 | fromVideoButton:(VTSVideoButton *)videoButton { 170 | UIImagePickerController *picker = [[UIImagePickerController alloc] init]; 171 | picker.videoQuality = UIImagePickerControllerQualityTypeIFrame1280x720; 172 | picker.allowsEditing = YES; 173 | picker.sourceType = sourceType; 174 | picker.mediaTypes = @[(NSString *)kUTTypeMovie]; 175 | 176 | [picker useBlocksForDelegate]; 177 | 178 | __weak VTSVideoButton *weakVideoButton = videoButton; 179 | __weak __typeof(self)weakSelf = self; 180 | 181 | [picker onDidFinishPickingMediaWithInfo:^(UIImagePickerController *picker, NSDictionary *info) { 182 | DDLogDebug(@"info %@", info); 183 | 184 | if (weakVideoButton == weakSelf.firstVideoButton) { 185 | [VTSBusinessFacade sharedBusinessFacade].firstVideoURL = info[UIImagePickerControllerMediaURL]; 186 | [weakSelf.firstVideoButton startPreview]; 187 | } else if (weakVideoButton == weakSelf.secondVideoButton) { 188 | [VTSBusinessFacade sharedBusinessFacade].secondVideoURL = info[UIImagePickerControllerMediaURL]; 189 | [weakSelf.secondVideoButton startPreview]; 190 | } 191 | 192 | [picker dismissViewControllerAnimated:YES completion:nil]; 193 | }]; 194 | 195 | [self presentViewController:picker animated:YES completion:NULL]; 196 | } 197 | 198 | #pragma mark - 199 | #pragma mark Transitions list 200 | 201 | - (void)showTransitionsListView { 202 | 203 | [[NSNotificationCenter defaultCenter] addObserver:self 204 | selector:@selector(hideTransitionsListView:) 205 | name:kHideTransitionsListViewNotificationString 206 | object:nil]; 207 | 208 | self.interfaceContainerView.hidden = YES; 209 | self.transitionsView.hidden = NO; 210 | } 211 | 212 | - (void)hideTransitionsListView { 213 | 214 | [[NSNotificationCenter defaultCenter] removeObserver:self name:kHideTransitionsListViewNotificationString object:nil]; 215 | 216 | self.transitionButton.playerView.player = [VTSBusinessFacade sharedBusinessFacade].transitionPreviewPlayer; 217 | 218 | self.transitionsView.hidden = YES; 219 | self.interfaceContainerView.hidden = NO; 220 | } 221 | 222 | #pragma mark - 223 | #pragma mark Notifications 224 | 225 | - (void)hideTransitionsListView:(NSNotification *)aNotification { 226 | [self hideTransitionsListView]; 227 | } 228 | 229 | - (void)hidePlayerViewController:(NSNotification *)aNotification { 230 | VTSPlayerViewController *playerViewController = aNotification.object; 231 | [playerViewController dismissViewControllerAnimated:YES completion:nil]; 232 | } 233 | 234 | @end 235 | -------------------------------------------------------------------------------- /VideoTransitionsSample/UI layer/ViewControllers/PlayerViewController/VTSPlayerViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // VTSPlayerViewController.h 3 | // VideoTransitionsSample 4 | // 5 | // Created by Ruslan Shevtsov on 4/15/15. 6 | // Copyright (c) 2015 iQueSoft. All rights reserved. 7 | // 8 | 9 | #import "VTSBaseViewController.h" 10 | 11 | extern NSString * const kHidePlayerViewNotificationString; 12 | 13 | @interface VTSPlayerViewController : VTSBaseViewController 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /VideoTransitionsSample/UI layer/ViewControllers/PlayerViewController/VTSPlayerViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // VTSPlayerViewController.m 3 | // VideoTransitionsSample 4 | // 5 | // Created by Ruslan Shevtsov on 4/15/15. 6 | // Copyright (c) 2015 iQueSoft. All rights reserved. 7 | // 8 | 9 | #import "VTSPlayerViewController.h" 10 | 11 | // Frameworks 12 | #import 13 | 14 | // Business facade 15 | #import "VTSBusinessFacade.h" 16 | 17 | // UI 18 | #import "VTSPlayerView.h" 19 | 20 | // Helpers 21 | #import "DurationStringFromTimeInterval.h" 22 | 23 | NSString * const kHidePlayerViewNotificationString = @"kHidePlayerViewNotificationString"; 24 | 25 | @interface VTSPlayerViewController () 26 | 27 | @property (weak, nonatomic) IBOutlet UIButton *playButton; 28 | @property (weak, nonatomic) IBOutlet UIButton *pauseButton; 29 | @property (weak, nonatomic) IBOutlet VTSPlayerView *playerView; 30 | @property (weak, nonatomic) IBOutlet UISlider *progressSlider; 31 | @property (weak, nonatomic) IBOutlet UILabel *currentTimeLabel; 32 | @property (weak, nonatomic) IBOutlet UILabel *lastTimeLabel; 33 | 34 | @property (nonatomic, strong) NSTimer *progressTimer; 35 | 36 | @end 37 | 38 | @implementation VTSPlayerViewController 39 | 40 | #pragma mark - 41 | #pragma mark Life cycle 42 | 43 | - (void)viewDidLoad { 44 | [super viewDidLoad]; 45 | 46 | self.progressTimer = [NSTimer timerWithTimeInterval:0.1 47 | target:self 48 | selector:@selector(timerDidFire:) 49 | userInfo:nil 50 | repeats:YES]; 51 | 52 | [[NSRunLoop mainRunLoop] addTimer:self.progressTimer 53 | forMode:NSDefaultRunLoopMode]; 54 | 55 | self.progressSlider.value = 0.0f; 56 | 57 | AVPlayer *player = [[VTSBusinessFacade sharedBusinessFacade] playerWithCurrentVideo]; 58 | 59 | if (player == nil) { 60 | return; 61 | } 62 | 63 | self.playerView.player = player; 64 | 65 | CGFloat duration = CMTimeGetSeconds(player.currentItem.duration); 66 | self.progressSlider.maximumValue = duration; 67 | self.lastTimeLabel.text = DurationFromTimeInterval(duration); 68 | 69 | [[NSNotificationCenter defaultCenter] addObserver:self 70 | selector:@selector(playerItemDidReachEnd:) 71 | name:AVPlayerItemDidPlayToEndTimeNotification 72 | object:nil]; 73 | } 74 | 75 | - (void)viewWillAppear:(BOOL)animated { 76 | [super viewWillAppear:animated]; 77 | 78 | 79 | } 80 | 81 | - (void)viewDidAppear:(BOOL)animated { 82 | [super viewDidAppear:animated]; 83 | 84 | AVPlayer *player = self.playerView.player; 85 | if (player) { 86 | self.playButton.hidden = YES; 87 | self.pauseButton.hidden = NO; 88 | [player play]; 89 | } 90 | } 91 | 92 | - (void)viewWillDisappear:(BOOL)animated { 93 | [super viewWillDisappear:animated]; 94 | 95 | [[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:nil]; 96 | 97 | [self.playerView.player pause]; 98 | self.playerView.player = nil; 99 | 100 | if (self.progressTimer != nil) { 101 | if ([self.progressTimer isValid]) { 102 | [self.progressTimer invalidate]; 103 | } 104 | self.progressTimer = nil; 105 | } 106 | } 107 | 108 | - (void)viewDidDisappear:(BOOL)animated { 109 | [super viewDidDisappear:animated]; 110 | 111 | 112 | } 113 | 114 | #pragma mark - 115 | #pragma mark IBActions 116 | 117 | - (IBAction)closeAction:(id)sender { 118 | [[NSNotificationCenter defaultCenter] postNotificationName:kHidePlayerViewNotificationString object:self userInfo:nil]; 119 | } 120 | 121 | - (IBAction)playAction:(id)sender { 122 | AVPlayer *player = self.playerView.player; 123 | if (player) { 124 | self.playButton.hidden = YES; 125 | self.pauseButton.hidden = NO; 126 | [player play]; 127 | } 128 | } 129 | 130 | - (IBAction)pauseAction:(id)sender { 131 | AVPlayer *player = self.playerView.player; 132 | if (player) { 133 | self.pauseButton.hidden = YES; 134 | self.playButton.hidden = NO; 135 | [player pause]; 136 | } 137 | } 138 | 139 | #pragma mark - 140 | #pragma mark Update progress UI 141 | 142 | - (void)timerDidFire:(NSTimer *)timer { 143 | [self updateProgresUI]; 144 | } 145 | 146 | - (void)updateProgresUI { 147 | 148 | AVPlayerItem *playerItem = self.playerView.player.currentItem; 149 | 150 | CGFloat currentTime = CMTimeGetSeconds(playerItem.currentTime); 151 | 152 | self.progressSlider.value = currentTime; 153 | self.currentTimeLabel.text = DurationFromTimeInterval(currentTime); 154 | } 155 | 156 | #pragma mark - 157 | #pragma mark Notifications 158 | 159 | - (void)playerItemDidReachEnd:(NSNotification *)notification { 160 | if (notification.object == self.playerView.player.currentItem) { 161 | [self.playerView.player seekToTime:kCMTimeZero]; 162 | self.pauseButton.hidden = YES; 163 | self.playButton.hidden = NO; 164 | } 165 | } 166 | 167 | @end 168 | -------------------------------------------------------------------------------- /VideoTransitionsSample/UI layer/ViewControllers/TransitionsViewController/VTSTransitionsViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // VTSTransitionsViewController.h 3 | // VideoTransitionsSample 4 | // 5 | // Created by Ruslan Shevtsov on 4/14/15. 6 | // Copyright (c) 2015 iQueSoft. All rights reserved. 7 | // 8 | 9 | #import "VTSBaseViewController.h" 10 | 11 | extern NSString * const kHideTransitionsListViewNotificationString; 12 | 13 | @interface VTSTransitionsViewController : VTSBaseViewController 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /VideoTransitionsSample/UI layer/ViewControllers/TransitionsViewController/VTSTransitionsViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // VTSTransitionsViewController.m 3 | // VideoTransitionsSample 4 | // 5 | // Created by Ruslan Shevtsov on 4/14/15. 6 | // Copyright (c) 2015 iQueSoft. All rights reserved. 7 | // 8 | 9 | #import "VTSTransitionsViewController.h" 10 | 11 | // Frameworks 12 | #import 13 | #import 14 | 15 | // Business facade 16 | #import "VTSBusinessFacade.h" 17 | 18 | // UI 19 | #import "PagedFlowView.h" 20 | #import "VTSTransitionButton.h" 21 | #import "VTSPlayerView.h" 22 | 23 | NSString * const kHideTransitionsListViewNotificationString = @"kHideFiltersListViewNotificationString"; 24 | 25 | @interface VTSTransitionsViewController () 26 | 27 | @property (weak, nonatomic) IBOutlet PagedFlowView *transitionsPagedFlowView; 28 | 29 | @property (nonatomic, strong) NSMutableDictionary *cells; 30 | 31 | @property (nonatomic, weak) VTSTransitionButton *currentPagedFlowViewCell; 32 | 33 | @property (nonatomic, assign) VTSTransitionType tempTransitionType; 34 | 35 | @end 36 | 37 | @implementation VTSTransitionsViewController 38 | 39 | #pragma mark - 40 | #pragma mark Life cycle 41 | 42 | - (void)viewDidLoad { 43 | [super viewDidLoad]; 44 | 45 | self.transitionsPagedFlowView.delegate = self; 46 | self.transitionsPagedFlowView.dataSource = self; 47 | self.transitionsPagedFlowView.minimumPageAlpha = 0.3; 48 | self.transitionsPagedFlowView.minimumPageScale = 0.7; 49 | 50 | for (NSInteger index = 0; index < VTSTransitionsCount; index++) { 51 | VTSTransitionButton *pagedFlowViewCell = [[VTSTransitionButton alloc] init]; 52 | [pagedFlowViewCell addTarget:self action:@selector(chooseTransition:) forControlEvents:UIControlEventTouchUpInside]; 53 | pagedFlowViewCell.layer.borderColor = [UIColor whiteColor].CGColor; 54 | [self.cells setObject:pagedFlowViewCell forKey:@(index)]; 55 | } 56 | } 57 | 58 | - (void)viewWillAppear:(BOOL)animated { 59 | [super viewWillAppear:animated]; 60 | 61 | 62 | } 63 | 64 | - (void)viewDidAppear:(BOOL)animated { 65 | [super viewDidAppear:animated]; 66 | 67 | VTSBusinessFacade *businessFacade = [VTSBusinessFacade sharedBusinessFacade]; 68 | 69 | self.tempTransitionType = businessFacade.transitionType; 70 | 71 | self.currentPagedFlowViewCell = self.cells[@(self.tempTransitionType)]; 72 | self.currentPagedFlowViewCell.layer.borderColor = [UIColor colorWithRed:233.0f/255.0f green:56.0f/255.0f blue:59.0f/255.0f alpha:1.0f].CGColor; 73 | 74 | self.currentPagedFlowViewCell = self.cells[@(self.tempTransitionType)]; 75 | self.currentPagedFlowViewCell.playerView.player = businessFacade.transitionPreviewPlayer; 76 | [self.transitionsPagedFlowView scrollToPage:self.tempTransitionType]; 77 | } 78 | 79 | - (void)viewWillDisappear:(BOOL)animated { 80 | [super viewWillDisappear:animated]; 81 | 82 | 83 | } 84 | 85 | - (void)viewDidDisappear:(BOOL)animated { 86 | [super viewDidDisappear:animated]; 87 | 88 | } 89 | 90 | #pragma mark - 91 | #pragma mark Lazy load 92 | 93 | - (NSMutableDictionary *)cells { 94 | if (_cells == nil) { 95 | _cells = [NSMutableDictionary dictionary]; 96 | } 97 | return _cells; 98 | } 99 | 100 | #pragma mark - 101 | #pragma mark IBActions 102 | 103 | - (IBAction)closeAction:(id)sender { 104 | [[VTSBusinessFacade sharedBusinessFacade] setTransitionType:self.tempTransitionType]; 105 | [[NSNotificationCenter defaultCenter] postNotificationName:kHideTransitionsListViewNotificationString object:self userInfo:nil]; 106 | } 107 | 108 | - (void)chooseTransition:(id)sender { 109 | 110 | VTSTransitionButton *prevButton = [self.cells objectForKey:@(self.tempTransitionType)]; 111 | 112 | prevButton.layer.borderColor = [UIColor whiteColor].CGColor; 113 | 114 | self.tempTransitionType = [(NSNumber *)[[self.cells allKeysForObject:sender] lastObject] integerValue]; 115 | ((VTSTransitionButton *)sender).layer.borderColor = [UIColor colorWithRed:233.0f/255.0f green:56.0f/255.0f blue:59.0f/255.0f alpha:1.0f].CGColor; 116 | } 117 | 118 | #pragma mark - 119 | #pragma mark PagedFlowViewDelegate 120 | 121 | - (CGSize)sizeForPageInFlowView:(PagedFlowView *)flowView { 122 | return CGSizeMake(160.0f, 90.0f); 123 | } 124 | 125 | - (void)flowView:(PagedFlowView *)flowView didScrollToPageAtIndex:(NSInteger)index { 126 | [[VTSBusinessFacade sharedBusinessFacade] setTransitionType:(VTSTransitionType)index]; 127 | 128 | self.currentPagedFlowViewCell.playerView.player = nil; 129 | self.currentPagedFlowViewCell = self.cells[@(index)]; 130 | self.currentPagedFlowViewCell.playerView.player = [VTSBusinessFacade sharedBusinessFacade].transitionPreviewPlayer; 131 | } 132 | 133 | #pragma mark - 134 | #pragma mark PagedFlowViewDataSource 135 | 136 | - (NSInteger)numberOfPagesInFlowView:(PagedFlowView *)flowView { 137 | return VTSTransitionsCount; 138 | } 139 | 140 | - (UIView *)flowView:(PagedFlowView *)flowView cellForPageAtIndex:(NSInteger)index { 141 | VTSTransitionButton *pagedFlowViewCell = self.cells[@(index)]; 142 | return pagedFlowViewCell; 143 | } 144 | 145 | @end 146 | -------------------------------------------------------------------------------- /VideoTransitionsSample/VTSAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // VTSAppDelegate.h 3 | // VideoTransitionsSample 4 | // 5 | // Created by Ruslan Shevtsov on 4/9/15. 6 | // Copyright (c) 2015 iQueSoft. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface VTSAppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /VideoTransitionsSample/VTSAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // VTSAppDelegate.m 3 | // VideoTransitionsSample 4 | // 5 | // Created by Ruslan Shevtsov on 4/9/15. 6 | // Copyright (c) 2015 iQueSoft. All rights reserved. 7 | // 8 | 9 | #import "VTSAppDelegate.h" 10 | 11 | // Loggers 12 | #import "DDTTYLogger.h" 13 | 14 | @interface VTSAppDelegate () 15 | 16 | @end 17 | 18 | @implementation VTSAppDelegate 19 | 20 | 21 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 22 | // Override point for customization after application launch. 23 | 24 | setenv("XcodeColors", "YES", 0); 25 | 26 | // Configure CocoaLumberjack 27 | [DDLog addLogger:[DDTTYLogger sharedInstance] withLevel:LOG_LEVEL_DEBUG]; 28 | 29 | // Enable Colors 30 | [[DDTTYLogger sharedInstance] setColorsEnabled:YES]; 31 | 32 | DDLogDebug(@"Start log"); 33 | 34 | return YES; 35 | } 36 | 37 | - (void)applicationWillResignActive:(UIApplication *)application { 38 | // 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. 39 | // 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. 40 | } 41 | 42 | - (void)applicationDidEnterBackground:(UIApplication *)application { 43 | // 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. 44 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 45 | } 46 | 47 | - (void)applicationWillEnterForeground:(UIApplication *)application { 48 | // 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. 49 | } 50 | 51 | - (void)applicationDidBecomeActive:(UIApplication *)application { 52 | // 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. 53 | } 54 | 55 | - (void)applicationWillTerminate:(UIApplication *)application { 56 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 57 | } 58 | 59 | @end 60 | -------------------------------------------------------------------------------- /VideoTransitionsSample/VideoTransitionsSample-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | #import "GPUImage.h" -------------------------------------------------------------------------------- /VideoTransitionsSample/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // VideoTransitionsSample 4 | // 5 | // Created by Ruslan Shevtsov on 4/9/15. 6 | // Copyright (c) 2015 iQueSoft. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "VTSAppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([VTSAppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /VideoTransitionsSampleTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /VideoTransitionsSampleTests/VideoTransitionsSampleTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // VideoTransitionsSampleTests.m 3 | // VideoTransitionsSampleTests 4 | // 5 | // Created by Ruslan Shevtsov on 4/9/15. 6 | // Copyright (c) 2015 iQueSoft. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface VideoTransitionsSampleTests : XCTestCase 13 | 14 | @end 15 | 16 | @implementation VideoTransitionsSampleTests 17 | 18 | - (void)setUp { 19 | [super setUp]; 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | } 22 | 23 | - (void)tearDown { 24 | // Put teardown code here. This method is called after the invocation of each test method in the class. 25 | [super tearDown]; 26 | } 27 | 28 | - (void)testExample { 29 | // This is an example of a functional test case. 30 | XCTAssert(YES, @"Pass"); 31 | } 32 | 33 | - (void)testPerformanceExample { 34 | // This is an example of a performance test case. 35 | [self measureBlock:^{ 36 | // Put the code you want to measure the time of here. 37 | }]; 38 | } 39 | 40 | @end 41 | --------------------------------------------------------------------------------