nebs
nebs

Reputation: 4989

iOS: Resizing/Cropping recorded video

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

Answers (4)

onmyway133
onmyway133

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

Jason JX
Jason JX

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

ThomasRS
ThomasRS

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

pixelrevision
pixelrevision

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

Related Questions