While working on a project I found the benefits of having a square video resolution (e.g. 600 x 600) very useful for things such as file size, upload speed and buffering. This is probably why video apps such as vine and instgram use similar resolutions. However for something seemly simple I could not find very much on the web at all for producing such effects in objective C, Xcode.. Apparently IOS 7 has a new function to crop video but I didn’t look into it as many people still use IOS 6.  Anyway so I thought I might kick start my tutorial section with a short tutorial on cropping video to a square with the project file being available for download at the end. 

Setting up

For this tutorial we will need several frameworks in our project, so make sure you have imported the following:

  • QuartzCore.framework
  • CoreMedia.framework
  • Foundation.framework
  • Coregraphics.frameworks
  • UIKit.frameworks

And finally the most important one,

  • AVFoundation.framework

cropping-video-square

I’m going to be pretty brief about setting up as there are many great tutorials on this, but overall the main beef of our project will be using AVFoundation.framework, a powerful media tool provided by apple. A great tutorial including many different features of the AVFoundation framework and be found over at raywenderlich.

Next Import the needed frameworks into your ViewController.h file:

     #import <UIKit/UIKit.h>
     #import <Foundation/Foundation.h>
     #import <AVFoundation/AVFoundation.h>
     #import <CoreMedia/CMTime.h>

Then proceed to set up the View Controller by declaring some properties:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
@class AVPlayerDemoPlaybackView;
@class AVPlayer;
@interface ViewController : UIViewController{

        IBOutlet AVPlayerDemoPlaybackView  *mPlaybackView;
        AVAssetExportSession *exporter;
}

@property (readwrite, retain) AVPlayer* mPlayer;
@property (nonatomic, retain) IBOutlet AVPlayerDemoPlaybackView *mPlaybackView;

- (void)exportDidFinish:(AVAssetExportSession*)session;

- (void)observeValueForKeyPath:(NSString*) path ofObject:(id)object change:(NSDictionary*)change context:(void*)context;
@end

 

The @class AVPlayerDemoPlaybackView lets us playback the cropped video and its class is provided in the source code below. Don’t forget to add it to your project.

Next in our ViewController.m file we have to import our header, declare a static void and synthesize our properties.

1
2
3
4
5
6
7
8
static void *AVPlayerDemoPlaybackViewControllerStatusObservationContext = &AVPlayerDemoPlaybackViewControllerStatusObservationContext;

@implementation ViewController
@synthesize mPlayer, mPlaybackView;

- (void)dealloc{
          
[super dealloc]; }

 

In our viewDidLoad method we want to call the method CropVideoSquare which will be the beef our project.

1
2
3
4
5
6
- (void)viewDidLoad {
         [super viewDidLoad];

        //Call the Crop Video Method
         [self CropVideoSquare];
}

 

Now to start off our cropping method we need to get our video (“OriginalVideo”) which is 1080 x 1920 and store it as an AVAsset. We then have to create an AVAssetTrack with it.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
    //load our movie Asset
    AVAsset *asset = [AVAsset assetWithURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"OriginalVideo" ofType:@"mov"]]];
    
    //create an avassetrack with our asset
    AVAssetTrack *clipVideoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
    
    //create a video composition and preset some settings
    AVMutableVideoComposition* videoComposition = [AVMutableVideoComposition videoComposition];
    videoComposition.frameDuration = CMTimeMake(1, 30);
    //here we are setting its render size to its height x height (Square)
    videoComposition.renderSize = CGSizeMake(clipVideoTrack.naturalSize.height, clipVideoTrack.naturalSize.height);

 

We then create an AVMutableVideoComposition and set its frameduration. We then set its renderSize, take note how we set it to clipVideoTrack.naturalSize.height x clipVideoTrack.naturalSize.height or (1080 x 1080).

We then make an AVMutableVideoCompositionInstruction, set its timeRange and then create a AVMutableVideoCompositionLayerInstruction with our VideoTrackAsset.

1
2
3
4
5
6
 //create a video instruction
AVMutableVideoCompositionInstruction *instruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];

instruction.timeRange = CMTimeRangeMake(kCMTimeZero, CMTimeMakeWithSeconds(60, 30));
    
AVMutableVideoCompositionLayerInstruction* transformer = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:clipVideoTrack];

 

Now heres where a lot of the magic happens, we create a CGAffineTransform and move the ‘Square view’ to start at the top of the video.

1
CGAffineTransform t1 = CGAffineTransformMakeTranslation(clipVideoTrack.naturalSize.height, 0);

 

If you want the square to be in the middle of the video use:

1
CGAffineTransform t1 = CGAffineTransformMakeTranslation(clipVideoTrack.naturalSize.height, -(clipVideoTrack.naturalSize.width - clipVideoTrack.naturalSize.height) /2 );

 

Add another CGaffineTransform to make sure the video is portrait then finish up by setting the Transform.

1
2
3
4
5
6
7
8
9
//Make sure the square is portrait
CGAffineTransform t2 = CGAffineTransformRotate(t1, M_PI_2);

CGAffineTransform finalTransform = t2;
[transformer setTransform:finalTransform atTime:kCMTimeZero];

//add the transformer layer instructions, then add to video composition
instruction.layerInstructions = [NSArray arrayWithObject:transformer];
videoComposition.instructions = [NSArray arrayWithObject: instruction];

 

With the main parts already done now we have to set up an export url and make sure delete whatever there.

1
2
3
4
5
//Create an Export Path to store the cropped video 
NSString * documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];

NSString *exportPath = [documentsPath stringByAppendingFormat:@"/CroppedVideo.mp4"];
NSURL *exportUrl = [NSURL fileURLWithPath:exportPath];  

 

Now initiate the export session and set a block to be call ‘exportDidFinish’ when the session is over.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
//Export
exporter = [[AVAssetExportSession alloc] initWithAsset:asset presetName:AVAssetExportPresetHighestQuality] ;
    exporter.videoComposition = videoComposition;
    exporter.outputURL = exportUrl;
    exporter.outputFileType = AVFileTypeQuickTimeMovie;

    [exporter exportAsynchronouslyWithCompletionHandler:^
     {
         dispatch_async(dispatch_get_main_queue(), ^{
             //Call when finished
             [self exportDidFinish:exporter];
         });
     }];

 

The last to methods are pretty straight forward they just play the cropped video back using an AVPlayer

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
- (void)exportDidFinish:(AVAssetExportSession*)session{
    //Play the New Cropped video
    NSURL *outputURL = session.outputURL;
    AVURLAsset* asset = [AVURLAsset URLAssetWithURL:outputURL options:nil];
    AVPlayerItem * newPlayerItem = [AVPlayerItem playerItemWithAsset:asset];

    self.mPlayer = [AVPlayer playerWithPlayerItem:newPlayerItem];

    [mPlayer addObserver:self forKeyPath:@"status" options:0 context:AVPlayerDemoPlaybackViewControllerStatusObservationContext];
}


- (void)observeValueForKeyPath:(NSString*) path ofObject:(id)object change:(NSDictionary*)change context:(void*)context
{
    if (mPlayer.status == AVPlayerStatusReadyToPlay) {
         [self.mPlaybackView setPlayer:self.mPlayer];
        [self.mPlayer play];
    }
}

 

Thats It!

Here is the source_code with all of the code above.
Hopefully this method will help save plenty of time and effort to get all the benefits out of a square video resolution.
Feel free to leave a question bellow and ill get back as soon as possible.