aboutsummaryrefslogtreecommitdiffstats
path: root/Video Tuneup/SimpleEditor.m
diff options
context:
space:
mode:
Diffstat (limited to 'Video Tuneup/SimpleEditor.m')
-rw-r--r--Video Tuneup/SimpleEditor.m525
1 files changed, 525 insertions, 0 deletions
diff --git a/Video Tuneup/SimpleEditor.m b/Video Tuneup/SimpleEditor.m
new file mode 100644
index 0000000..f666d6c
--- /dev/null
+++ b/Video Tuneup/SimpleEditor.m
@@ -0,0 +1,525 @@
+
+/*
+ File: SimpleEditor.m
+ Abstract: Demonstrates construction of AVComposition, AVAudioMix, and AVVideoComposition.
+
+ Version: 1.1
+
+ Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
+ Inc. ("Apple") in consideration of your agreement to the following
+ terms, and your use, installation, modification or redistribution of
+ this Apple software constitutes acceptance of these terms. If you do
+ not agree with these terms, please do not use, install, modify or
+ redistribute this Apple software.
+
+ In consideration of your agreement to abide by the following terms, and
+ subject to these terms, Apple grants you a personal, non-exclusive
+ license, under Apple's copyrights in this original Apple software (the
+ "Apple Software"), to use, reproduce, modify and redistribute the Apple
+ Software, with or without modifications, in source and/or binary forms;
+ provided that if you redistribute the Apple Software in its entirety and
+ without modifications, you must retain this notice and the following
+ text and disclaimers in all such redistributions of the Apple Software.
+ Neither the name, trademarks, service marks or logos of Apple Inc. may
+ be used to endorse or promote products derived from the Apple Software
+ without specific prior written permission from Apple. Except as
+ expressly stated in this notice, no other rights or licenses, express or
+ implied, are granted by Apple herein, including but not limited to any
+ patent rights that may be infringed by your derivative works or by other
+ works in which the Apple Software may be incorporated.
+
+ The Apple Software is provided by Apple on an "AS IS" basis. APPLE
+ MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
+ THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
+ FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
+ OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
+
+ IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
+ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
+ MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
+ AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
+ STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+ Copyright (C) 2010 Apple Inc. All Rights Reserved.
+
+ */
+
+#import "SimpleEditor.h"
+
+#import <CoreMedia/CoreMedia.h>
+#import <AVFoundation/AVFoundation.h>
+
+@interface SimpleEditor ()
+@property (nonatomic, readwrite, retain) AVComposition *composition;
+@property (nonatomic, readwrite, retain) AVVideoComposition *videoComposition;
+@property (nonatomic, readwrite, retain) AVAudioMix *audioMix;
+@property (nonatomic, readwrite, retain) AVPlayerItem *playerItem;
+@property (nonatomic, readwrite, retain) AVSynchronizedLayer *synchronizedLayer;
+
+@end
+
+
+@implementation SimpleEditor
+
+- (id)init
+{
+ if (self = [super init]) {
+ _commentaryStartTime = CMTimeMake(2, 1); // Default start time for the commentary is two seconds.
+
+ _transitionDuration = CMTimeMake(1, 1); // Default transition duration is one second.
+
+ // just until we have the UI for this wired up
+ NSMutableArray *clipTimeRanges = [[NSMutableArray alloc] initWithCapacity:3];
+ CMTimeRange defaultTimeRange = CMTimeRangeMake(kCMTimeZero, CMTimeMake(5, 1));
+ NSValue *defaultTimeRangeValue = [NSValue valueWithCMTimeRange:defaultTimeRange];
+ [clipTimeRanges addObject:defaultTimeRangeValue];
+ [clipTimeRanges addObject:defaultTimeRangeValue];
+ [clipTimeRanges addObject:defaultTimeRangeValue];
+ _clipTimeRanges = clipTimeRanges;
+ }
+ return self;
+}
+
+// Configuration
+
+@synthesize clips = _clips, clipTimeRanges = _clipTimeRanges;
+@synthesize commentary = _commentary, commentaryStartTime = _commentaryStartTime;
+@synthesize transitionType = _transitionType, transitionDuration = _transitionDuration;
+@synthesize titleText = _titleText;
+
+// Composition objects.
+
+@synthesize composition = _composition;
+@synthesize videoComposition =_videoComposition;
+@synthesize audioMix = _audioMix;
+@synthesize playerItem = _playerItem;
+@synthesize synchronizedLayer = _synchronizedLayer;
+
+static CGImageRef createStarImage(CGFloat radius)
+{
+ int i, count = 5;
+#if TARGET_OS_IPHONE
+ CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
+#else // not TARGET_OS_IPHONE
+ CGColorSpaceRef colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
+#endif // not TARGET_OS_IPHONE
+ CGImageRef image = NULL;
+ size_t width = 2*radius;
+ size_t height = 2*radius;
+ size_t bytesperrow = width * 4;
+ CGContextRef context = CGBitmapContextCreate((void *)NULL, width, height, 8, bytesperrow, colorspace, kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedFirst);
+ CGContextClearRect(context, CGRectMake(0, 0, 2*radius, 2*radius));
+ CGContextSetLineWidth(context, radius / 15.0);
+
+ for( i = 0; i < 2 * count; i++ ) {
+ CGFloat angle = i * M_PI / count;
+ CGFloat pointradius = (i % 2) ? radius * 0.37 : radius * 0.95;
+ CGFloat x = radius + pointradius * cos(angle);
+ CGFloat y = radius + pointradius * sin(angle);
+ if (i == 0)
+ CGContextMoveToPoint(context, x, y);
+ else
+ CGContextAddLineToPoint(context, x, y);
+ }
+ CGContextClosePath(context);
+
+ CGContextSetRGBFillColor(context, 1.0, 1.0, 1.0, 1.0);
+ CGContextSetRGBStrokeColor(context, 0.0, 0.0, 0.0, 1.0);
+ CGContextDrawPath(context, kCGPathFillStroke);
+ CGColorSpaceRelease(colorspace);
+ image = CGBitmapContextCreateImage(context);
+ CGContextRelease(context);
+ return image;
+}
+
+- (void)buildSequenceComposition:(AVMutableComposition *)composition
+{
+ CMTime nextClipStartTime = kCMTimeZero;
+ NSInteger i;
+
+ // No transitions: place clips into one video track and one audio track in composition.
+ NSLog(@"Building sequence composition. Count is %i", [_clips count]);
+
+ AVMutableCompositionTrack *compositionVideoTrack = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
+ AVMutableCompositionTrack *compositionAudioTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
+
+ for (i = 0; i < [_clips count]; i++ ) {
+ AVURLAsset *asset = [_clips objectAtIndex:i];
+ NSValue *clipTimeRange = [_clipTimeRanges objectAtIndex:i];
+ CMTimeRange timeRangeInAsset;
+ if (clipTimeRange)
+ timeRangeInAsset = [clipTimeRange CMTimeRangeValue];
+ else
+ timeRangeInAsset = CMTimeRangeMake(kCMTimeZero, [asset duration]);
+
+ AVAssetTrack *clipVideoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
+ [compositionVideoTrack insertTimeRange:timeRangeInAsset ofTrack:clipVideoTrack atTime:nextClipStartTime error:nil];
+
+ NSLog(@"Composition audio?");
+ if ([[asset tracksWithMediaType:AVMediaTypeAudio] count] != 0) {
+ AVAssetTrack *clipAudioTrack = [[asset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0];
+ NSLog(@"Composition audio!");
+ [compositionAudioTrack insertTimeRange:timeRangeInAsset ofTrack:clipAudioTrack atTime:nextClipStartTime error:nil];
+ }
+
+ // Note: This is largely equivalent:
+ // [composition insertTimeRange:timeRangeInAsset ofAsset:asset atTime:nextClipStartTime error:NULL];
+ // except that if the video tracks dimensions do not match, additional video tracks will be added to the composition.
+
+ nextClipStartTime = CMTimeAdd(nextClipStartTime, timeRangeInAsset.duration);
+ }
+}
+
+- (void)buildTransitionComposition:(AVMutableComposition *)composition andVideoComposition:(AVMutableVideoComposition *)videoComposition
+{
+ CMTime nextClipStartTime = kCMTimeZero;
+ NSInteger i;
+
+ // Make transitionDuration no greater than half the shortest clip duration.
+ CMTime transitionDuration = self.transitionDuration;
+ for (i = 0; i < [_clips count]; i++ ) {
+ NSValue *clipTimeRange = [_clipTimeRanges objectAtIndex:i];
+ if (clipTimeRange) {
+ CMTime halfClipDuration = [clipTimeRange CMTimeRangeValue].duration;
+ halfClipDuration.timescale *= 2; // You can halve a rational by doubling its denominator.
+ transitionDuration = CMTimeMinimum(transitionDuration, halfClipDuration);
+ }
+ }
+
+ // Add two video tracks and two audio tracks.
+ AVMutableCompositionTrack *compositionVideoTracks[2];
+ AVMutableCompositionTrack *compositionAudioTracks[2];
+ compositionVideoTracks[0] = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
+ compositionVideoTracks[1] = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
+ compositionAudioTracks[0] = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
+ compositionAudioTracks[1] = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
+
+ CMTimeRange *passThroughTimeRanges = alloca(sizeof(CMTimeRange) * [_clips count]);
+ CMTimeRange *transitionTimeRanges = alloca(sizeof(CMTimeRange) * [_clips count]);
+
+ // Place clips into alternating video & audio tracks in composition, overlapped by transitionDuration.
+ for (i = 0; i < [_clips count]; i++ ) {
+ NSInteger alternatingIndex = i % 2; // alternating targets: 0, 1, 0, 1, ...
+ AVURLAsset *asset = [_clips objectAtIndex:i];
+ NSValue *clipTimeRange = [_clipTimeRanges objectAtIndex:i];
+ CMTimeRange timeRangeInAsset;
+ if (clipTimeRange)
+ timeRangeInAsset = [clipTimeRange CMTimeRangeValue];
+ else
+ timeRangeInAsset = CMTimeRangeMake(kCMTimeZero, [asset duration]);
+
+ AVAssetTrack *clipVideoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
+ [compositionVideoTracks[alternatingIndex] insertTimeRange:timeRangeInAsset ofTrack:clipVideoTrack atTime:nextClipStartTime error:nil];
+
+ AVAssetTrack *clipAudioTrack = [[asset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0];
+ [compositionAudioTracks[alternatingIndex] insertTimeRange:timeRangeInAsset ofTrack:clipAudioTrack atTime:nextClipStartTime error:nil];
+
+ // Remember the time range in which this clip should pass through.
+ // Every clip after the first begins with a transition.
+ // Every clip before the last ends with a transition.
+ // Exclude those transitions from the pass through time ranges.
+ passThroughTimeRanges[i] = CMTimeRangeMake(nextClipStartTime, timeRangeInAsset.duration);
+ if (i > 0) {
+ passThroughTimeRanges[i].start = CMTimeAdd(passThroughTimeRanges[i].start, transitionDuration);
+ passThroughTimeRanges[i].duration = CMTimeSubtract(passThroughTimeRanges[i].duration, transitionDuration);
+ }
+ if (i+1 < [_clips count]) {
+ passThroughTimeRanges[i].duration = CMTimeSubtract(passThroughTimeRanges[i].duration, transitionDuration);
+ }
+
+ // The end of this clip will overlap the start of the next by transitionDuration.
+ // (Note: this arithmetic falls apart if timeRangeInAsset.duration < 2 * transitionDuration.)
+ nextClipStartTime = CMTimeAdd(nextClipStartTime, timeRangeInAsset.duration);
+ nextClipStartTime = CMTimeSubtract(nextClipStartTime, transitionDuration);
+
+ // Remember the time range for the transition to the next item.
+ transitionTimeRanges[i] = CMTimeRangeMake(nextClipStartTime, transitionDuration);
+ }
+
+ // Set up the video composition if we are to perform crossfade or push transitions between clips.
+ NSMutableArray *instructions = [NSMutableArray array];
+
+ // Cycle between "pass through A", "transition from A to B", "pass through B", "transition from B to A".
+ for (i = 0; i < [_clips count]; i++ ) {
+ NSInteger alternatingIndex = i % 2; // alternating targets
+
+ // Pass through clip i.
+ AVMutableVideoCompositionInstruction *passThroughInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
+ passThroughInstruction.timeRange = passThroughTimeRanges[i];
+ AVMutableVideoCompositionLayerInstruction *passThroughLayer = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:compositionVideoTracks[alternatingIndex]];
+
+ passThroughInstruction.layerInstructions = [NSArray arrayWithObject:passThroughLayer];
+ [instructions addObject:passThroughInstruction];
+
+ if (i+1 < [_clips count]) {
+ // Add transition from clip i to clip i+1.
+
+ AVMutableVideoCompositionInstruction *transitionInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
+ transitionInstruction.timeRange = transitionTimeRanges[i];
+ AVMutableVideoCompositionLayerInstruction *fromLayer = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:compositionVideoTracks[alternatingIndex]];
+ AVMutableVideoCompositionLayerInstruction *toLayer = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:compositionVideoTracks[1-alternatingIndex]];
+
+ if (self.transitionType == SimpleEditorTransitionTypeCrossFade) {
+ // Fade out the fromLayer by setting a ramp from 1.0 to 0.0.
+ [fromLayer setOpacityRampFromStartOpacity:1.0 toEndOpacity:0.0 timeRange:transitionTimeRanges[i]];
+ }
+ else if (self.transitionType == SimpleEditorTransitionTypePush) {
+ // Set a transform ramp on fromLayer from identity to all the way left of the screen.
+ [fromLayer setTransformRampFromStartTransform:CGAffineTransformIdentity toEndTransform:CGAffineTransformMakeTranslation(-composition.naturalSize.width, 0.0) timeRange:transitionTimeRanges[i]];
+ // Set a transform ramp on toLayer from all the way right of the screen to identity.
+ [toLayer setTransformRampFromStartTransform:CGAffineTransformMakeTranslation(+composition.naturalSize.width, 0.0) toEndTransform:CGAffineTransformIdentity timeRange:transitionTimeRanges[i]];
+ }
+
+ transitionInstruction.layerInstructions = [NSArray arrayWithObjects:fromLayer, toLayer, nil];
+ [instructions addObject:transitionInstruction];
+ }
+ }
+
+ videoComposition.instructions = instructions;
+}
+
+- (void)addCommentaryTrackToComposition:(AVMutableComposition *)composition withAudioMix:(AVMutableAudioMix *)audioMix
+{
+ NSInteger i;
+ NSArray *tracksToDuck = [composition tracksWithMediaType:AVMediaTypeAudio]; // before we add the commentary
+
+ // Clip commentary duration to composition duration.
+ CMTimeRange commentaryTimeRange = CMTimeRangeMake(self.commentaryStartTime, self.commentary.duration);
+ if (CMTIME_COMPARE_INLINE(CMTimeRangeGetEnd(commentaryTimeRange), >, [composition duration]))
+ commentaryTimeRange.duration = CMTimeSubtract([composition duration], commentaryTimeRange.start);
+
+ // Add the commentary track.
+ AVMutableCompositionTrack *compositionCommentaryTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
+ [compositionCommentaryTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, commentaryTimeRange.duration) ofTrack:[[self.commentary tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0] atTime:commentaryTimeRange.start error:nil];
+
+
+ NSMutableArray *trackMixArray = [NSMutableArray array];
+ CMTime rampDuration = CMTimeMake(1, 2); // half-second ramps
+ for (i = 0; i < [tracksToDuck count]; i++) {
+ AVMutableAudioMixInputParameters *trackMix = [AVMutableAudioMixInputParameters audioMixInputParametersWithTrack:[tracksToDuck objectAtIndex:i]];
+ [trackMix setVolumeRampFromStartVolume:1.0 toEndVolume:0.2 timeRange:CMTimeRangeMake(CMTimeSubtract(commentaryTimeRange.start, rampDuration), rampDuration)];
+ [trackMix setVolumeRampFromStartVolume:0.2 toEndVolume:1.0 timeRange:CMTimeRangeMake(CMTimeRangeGetEnd(commentaryTimeRange), rampDuration)];
+ [trackMixArray addObject:trackMix];
+ }
+ audioMix.inputParameters = trackMixArray;
+}
+
+- (void)buildPassThroughVideoComposition:(AVMutableVideoComposition *)videoComposition forComposition:(AVMutableComposition *)composition
+{
+ // Make a "pass through video track" video composition.
+ AVMutableVideoCompositionInstruction *passThroughInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
+ passThroughInstruction.timeRange = CMTimeRangeMake(kCMTimeZero, [composition duration]);
+
+ AVAssetTrack *videoTrack = [[composition tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
+ AVMutableVideoCompositionLayerInstruction *passThroughLayer = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoTrack];
+
+ passThroughInstruction.layerInstructions = [NSArray arrayWithObject:passThroughLayer];
+ videoComposition.instructions = [NSArray arrayWithObject:passThroughInstruction];
+}
+
+- (CALayer *)buildAnimatedTitleLayerForSize:(CGSize)videoSize
+{
+ // Create a layer for the overall title animation.
+ CALayer *animatedTitleLayer = [CALayer layer];
+
+ // Create a layer for the text of the title.
+ CATextLayer *titleLayer = [CATextLayer layer];
+ titleLayer.string = self.titleText;
+ titleLayer.font = @"Helvetica";
+ titleLayer.fontSize = videoSize.height / 6;
+ //?? titleLayer.shadowOpacity = 0.5;
+ titleLayer.alignmentMode = kCAAlignmentCenter;
+ titleLayer.bounds = CGRectMake(0, 0, videoSize.width, videoSize.height / 6);
+
+ // Add it to the overall layer.
+ [animatedTitleLayer addSublayer:titleLayer];
+
+ // Create a layer that contains a ring of stars.
+ CALayer *ringOfStarsLayer = [CALayer layer];
+
+ NSInteger starCount = 9, s;
+ CGFloat starRadius = videoSize.height / 10;
+ CGFloat ringRadius = videoSize.height * 0.8 / 2;
+ CGImageRef starImage = createStarImage(starRadius);
+ for (s = 0; s < starCount; s++) {
+ CALayer *starLayer = [CALayer layer];
+ CGFloat angle = s * 2 * M_PI / starCount;
+ starLayer.bounds = CGRectMake(0, 0, 2 * starRadius, 2 * starRadius);
+ starLayer.position = CGPointMake(ringRadius * cos(angle), ringRadius * sin(angle));
+// starLayer.contents = (id)starImage;
+ starLayer.contents = (__bridge id)starImage;
+ [ringOfStarsLayer addSublayer:starLayer];
+ }
+ CGImageRelease(starImage);
+
+ // Rotate the ring of stars.
+ CABasicAnimation *rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
+ rotationAnimation.repeatCount = 1e100; // forever
+ rotationAnimation.fromValue = [NSNumber numberWithFloat:0.0];
+ rotationAnimation.toValue = [NSNumber numberWithFloat:2 * M_PI];
+ rotationAnimation.duration = 10.0; // repeat every 10 seconds
+ rotationAnimation.additive = YES;
+ rotationAnimation.removedOnCompletion = NO;
+ rotationAnimation.beginTime = 1e-100; // CoreAnimation automatically replaces zero beginTime with CACurrentMediaTime(). The constant AVCoreAnimationBeginTimeAtZero is also available.
+ [ringOfStarsLayer addAnimation:rotationAnimation forKey:nil];
+
+ // Add the ring of stars to the overall layer.
+ animatedTitleLayer.position = CGPointMake(videoSize.width / 2.0, videoSize.height / 2.0);
+ [animatedTitleLayer addSublayer:ringOfStarsLayer];
+
+ // Animate the opacity of the overall layer so that it fades out from 3 sec to 4 sec.
+ CABasicAnimation *fadeAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
+ fadeAnimation.fromValue = [NSNumber numberWithFloat:1.0];
+ fadeAnimation.toValue = [NSNumber numberWithFloat:0.0];
+ fadeAnimation.additive = NO;
+ fadeAnimation.removedOnCompletion = NO;
+ fadeAnimation.beginTime = 10.0;
+ fadeAnimation.duration = 2.0;
+ fadeAnimation.fillMode = kCAFillModeBoth;
+ [animatedTitleLayer addAnimation:fadeAnimation forKey:nil];
+
+ return animatedTitleLayer;
+}
+
+- (void)buildCompositionObjectsForPlayback:(BOOL)forPlayback
+{
+ NSLog(@"Building. Count is %i", [_clips count]);
+ CGSize videoSize = [[_clips objectAtIndex:0] naturalSize];
+ AVMutableComposition *composition = [AVMutableComposition composition];
+ AVMutableVideoComposition *videoComposition = nil;
+ AVMutableAudioMix *audioMix = nil;
+ CALayer *animatedTitleLayer = nil;
+
+ composition.naturalSize = videoSize;
+
+ if (self.transitionType == SimpleEditorTransitionTypeNone) {
+ // No transitions: place clips into one video track and one audio track in composition.
+
+ [self buildSequenceComposition:composition];
+ }
+ else {
+ // With transitions:
+ // Place clips into alternating video & audio tracks in composition, overlapped by transitionDuration.
+ // Set up the video composition to cycle between "pass through A", "transition from A to B",
+ // "pass through B", "transition from B to A".
+
+ videoComposition = [AVMutableVideoComposition videoComposition];
+ [self buildTransitionComposition:composition andVideoComposition:videoComposition];
+ }
+
+ // If one is provided, add a commentary track and duck all other audio during it.
+ if (self.commentary) {
+ // Add the commentary track and duck all other audio during it.
+
+ audioMix = [AVMutableAudioMix audioMix];
+ [self addCommentaryTrackToComposition:composition withAudioMix:audioMix];
+ }
+
+ // Set up Core Animation layers to contribute a title animation overlay if we have a title set.
+// if (self.titleText) {
+// animatedTitleLayer = [self buildAnimatedTitleLayerForSize:videoSize];
+//
+// if (! forPlayback) {
+// // For export: build a Core Animation tree that contains both the animated title and the video.
+// CALayer *parentLayer = [CALayer layer];
+// CALayer *videoLayer = [CALayer layer];
+// parentLayer.frame = CGRectMake(0, 0, videoSize.width, videoSize.height);
+// videoLayer.frame = CGRectMake(0, 0, videoSize.width, videoSize.height);
+// [parentLayer addSublayer:videoLayer];
+// [parentLayer addSublayer:animatedTitleLayer];
+//
+// if (! videoComposition) {
+// // No transition set -- make a "pass through video track" video composition so we can include the Core Animation tree as a post-processing stage.
+// videoComposition = [AVMutableVideoComposition videoComposition];
+//
+// [self buildPassThroughVideoComposition:videoComposition forComposition:composition];
+// }
+//
+// videoComposition.animationTool = [AVVideoCompositionCoreAnimationTool videoCompositionCoreAnimationToolWithPostProcessingAsVideoLayer:videoLayer inLayer:parentLayer];
+// }
+// }
+
+ if (videoComposition) {
+ // Every videoComposition needs these properties to be set:
+ videoComposition.frameDuration = CMTimeMake(1, 30); // 30 fps
+ videoComposition.renderSize = videoSize;
+ }
+
+ self.composition = composition;
+ self.videoComposition = videoComposition;
+ self.audioMix = audioMix;
+
+ self.synchronizedLayer = nil;
+
+ NSLog(@"Almost done building");
+
+ if (forPlayback) {
+#if TARGET_OS_EMBEDDED
+ // Render high-def movies at half scale for real-time playback (device-only).
+ if (videoSize.width > 640)
+ videoComposition.renderScale = 0.5;
+#endif // TARGET_OS_EMBEDDED
+
+ AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset:composition];
+ playerItem.videoComposition = videoComposition;
+ playerItem.audioMix = audioMix;
+ self.playerItem = playerItem;
+
+// if (animatedTitleLayer) {
+// // Build an AVSynchronizedLayer that contains the animated title.
+// self.synchronizedLayer = [AVSynchronizedLayer synchronizedLayerWithPlayerItem:self.playerItem];
+// self.synchronizedLayer.bounds = CGRectMake(0, 0, videoSize.width, videoSize.height);
+// [self.synchronizedLayer addSublayer:animatedTitleLayer];
+// }
+ }
+}
+
+- (void)getPlayerItem:(AVPlayerItem**)playerItemOut andSynchronizedLayer:(AVSynchronizedLayer**)synchronizedLayerOut
+{
+ if (playerItemOut) {
+ *playerItemOut = _playerItem;
+ }
+ if (synchronizedLayerOut) {
+ *synchronizedLayerOut = _synchronizedLayer;
+ }
+}
+
+- (AVAssetImageGenerator*)assetImageGenerator
+{
+ AVAssetImageGenerator *generator = [AVAssetImageGenerator assetImageGeneratorWithAsset:self.composition];
+ generator.videoComposition = self.videoComposition;
+ return generator;
+}
+
+- (AVAssetExportSession*)assetExportSessionWithPreset:(NSString*)presetName
+{
+ AVAssetExportSession *session = [[AVAssetExportSession alloc] initWithAsset:self.composition presetName:presetName];
+ session.videoComposition = self.videoComposition;
+ session.audioMix = self.audioMix;
+// return [session autorelease];
+ return session;
+}
+
+//- (void)dealloc
+//{
+// [_clips release];
+// [_clipTimeRanges release];
+//
+// [_commentary release];
+// [_titleText release];
+//
+//
+// [_composition release];
+// [_videoComposition release];
+// [_audioMix release];
+//
+// [_playerItem release];
+// [_synchronizedLayer release];
+//
+// [super dealloc];
+//}
+
+@end