Reputation: 4989
What would be the best way to resize a user recorded video on the iphone?
I currently have the ability to get video data in two ways:
1) Get the file URL from UIImagePickerController
after the video has been recorded
2) Get the frames of the video as its being sent by a AVCaptureSession
For 1) I would need something which can read .mov h.264 files and spit out individual frames. Is there such a thing?
For 2) I thought about getting a UIImage for each frame, resizing the image and then recompiling a video with something like AVAssetWriter
. But this seems like a very intensive operation and I'm wondering if there's a better way to approach this problem.
Is the general idea of resizing videos to resize each individual frame and then recompile a video? Or is there a way to directly resize an entire video?
I just basically need the user to record a video, and then to resize that video to 320x320, no fancy editing.
Thanks for any help you can provide.
Edit: Maybe 'resize' is the wrong word. I really just need to crop the video so that it goes from 480x360 to 320x320. Also this cropping doesn't need to happen in real time, I can do it once the video has been recorded.
Upvotes: 7
Views: 14062
Reputation: 48065
You need to take video orientation into account. Here is a Swift 2 solution. It both crops and scales the video
extension AVAsset {
private var g_naturalSize: CGSize {
return tracksWithMediaType(AVMediaTypeVideo).first?.naturalSize ?? .zero
}
var g_correctSize: CGSize {
return g_isPortrait ? CGSize(width: g_naturalSize.height, height: g_naturalSize.width) : g_naturalSize
}
var g_isPortrait: Bool {
let portraits: [UIInterfaceOrientation] = [.Portrait, .PortraitUpsideDown]
return portraits.contains(g_orientation)
// Same as UIImageOrientation
var g_orientation: UIInterfaceOrientation {
guard let transform = tracksWithMediaType(AVMediaTypeVideo).first?.preferredTransform else {
return .Portrait
}
switch (transform.tx, transform.ty) {
case (0, 0):
return .LandscapeRight
case (g_naturalSize.width, g_naturalSize.height):
return .LandscapeLeft
case (0, g_naturalSize.width):
return .PortraitUpsideDown
default:
return .Portrait
}
}
}
Here is how to get the transform
func transform(avAsset: AVAsset, scaleFactor: CGFloat) -> CGAffineTransform {
let offset: CGPoint
let angle: Double
switch avAsset.g_orientation {
case .LandscapeLeft:
offset = CGPoint(x: avAsset.g_correctSize.width, y: avAsset.g_correctSize.height)
angle = M_PI
case .LandscapeRight:
offset = CGPoint.zero
angle = 0
case .PortraitUpsideDown:
offset = CGPoint(x: 0, y: avAsset.g_correctSize.height)
angle = -M_PI_2
default:
offset = CGPoint(x: avAsset.g_correctSize.width, y: 0)
angle = M_PI_2
}
let scale = CGAffineTransformMakeScale(scaleFactor, scaleFactor)
let translation = CGAffineTransformTranslate(scale, offset.x, offset.y)
let rotation = CGAffineTransformRotate(translation, CGFloat(angle))
return rotation
}
And how to use it in the layer instruction
let layer = AVMutableVideoCompositionLayerInstruction(assetTrack: track)
layer.setTransform(transform(avAsset, scaleFactor: 0.8), atTime: kCMTimeZero)
Reference
Upvotes: 1
Reputation: 47
The perfect way is to use AVAssetReader
to read the video file and use AVAssetWriter
to write this video file to a new video file again. you can set a outputSetting for writer input:
NSDictionary* settings = [NSDictionary dictionaryWithObjectsAndKeys:
AVVideoCodecH264, AVVideoCodecKey,
[NSNumber numberWithInt: 480], AVVideoWidthKey,
[NSNumber numberWithInt: 480], AVVideoHeightKey,
AVVideoScalingModeResizeAspectFill,AVVideoScalingModeKey,
nil];
NSLog(@"%@",[assetVideoTrack mediaType]);
assetWriterVideoInput = [AVAssetWriterInput assetWriterInputWithMediaType: [assetVideoTrack mediaType]
outputSettings:settings];
[assetWriter addInput:assetWriterVideoInput];
you search apple developer documentation to find guid line about AVAssetReader
Upvotes: 5
Reputation: 8287
Alternative 2 is not going to work in real-time.
For alternative 1, use AVAssetExportSession
initialized with one of the desired presets - for example AVAssetExportPreset640x480
.
Experiment with AVAssetExportPresetLowQuality
, AVAssetExportPresetMediumQuality
, AVAssetExportPresetHighestQuality
for additional resolutions.
Note that video files usually have 16:9 (or 4:3) aspect ratios, so 320x320 is not a valid option. There are no good reasons for resizing to other than the supported resolutions, unless you plan on distribute to other phones, in which case server-side transcoding is available.
Upvotes: 4
Reputation: 374
In case anyone runs across this I have found you can do this with AVFoundation if you us an AVMutableVideoComposition as well as an AVMutableComposition. You then set renderSize on the video composition and that will effect the output size of the video. You then use AVMutableVideoCompositionLayerInstruction to position the video where you want it. There is a good article here on getting started with all these classes and his example project has commented out code at the end for exporting it.
Upvotes: 6