14

The goal is to capture full screen video on a device with Swift. In the code below, video capture appears to happen at full screen (while recording the camera preview uses the full screen), but the rendering of the video happens at a different resolution. For a 5S specifically, it appears like capture happens at 320x568 but rendering occurs at 320x480.

How can you capture and render full screen video?

Code for video capture:

private func initPBJVision() {
    // Store PBJVision in var for convenience
    let vision = PBJVision.sharedInstance()

    // Configure PBJVision
    vision.delegate = self
    vision.cameraMode = PBJCameraMode.Video
    vision.cameraOrientation = PBJCameraOrientation.Portrait
    vision.focusMode = PBJFocusMode.ContinuousAutoFocus
    vision.outputFormat = PBJOutputFormat.Preset
    vision.cameraDevice = PBJCameraDevice.Back

    // Let taps start/pause recording
    let tapHandler = UITapGestureRecognizer(target: self, action: "doTap:")
    view.addGestureRecognizer(tapHandler)

    // Log status
    print("Configured PBJVision")
}


private func startCameraPreview() {
    // Store PBJVision in var for convenience
    let vision = PBJVision.sharedInstance()

    // Connect PBJVision camera preview to <videoView>
    // -- Get preview width
    let deviceWidth = CGRectGetWidth(view.frame)
    let deviceHeight = CGRectGetHeight(view.frame)

    // -- Configure PBJVision's preview layer
    let previewLayer = vision.previewLayer
    previewLayer.frame = CGRectMake(0, 0, deviceWidth, deviceHeight)
    previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
    ...
}

Video rendering code:

func exportVideo(fileUrl: NSURL) {
    // Create main composition object
    let videoAsset = AVURLAsset(URL: fileUrl, options: nil)
    let mainComposition = AVMutableComposition()
    let compositionVideoTrack = mainComposition.addMutableTrackWithMediaType(AVMediaTypeVideo, preferredTrackID: CMPersistentTrackID(kCMPersistentTrackID_Invalid))
    let compositionAudioTrack = mainComposition.addMutableTrackWithMediaType(AVMediaTypeAudio, preferredTrackID: CMPersistentTrackID(kCMPersistentTrackID_Invalid))

    // -- Extract and apply video & audio tracks to composition
    let sourceVideoTrack = videoAsset.tracksWithMediaType(AVMediaTypeVideo)[0]
    let sourceAudioTrack = videoAsset.tracksWithMediaType(AVMediaTypeAudio)[0]
    do {
        try compositionVideoTrack.insertTimeRange(CMTimeRangeMake(kCMTimeZero, videoAsset.duration), ofTrack: sourceVideoTrack, atTime: kCMTimeZero)
    } catch {
        print("Error with insertTimeRange. Video error: \(error).")
    }
    do {
        try compositionAudioTrack.insertTimeRange(CMTimeRangeMake(kCMTimeZero, videoAsset.duration), ofTrack: sourceAudioTrack, atTime: kCMTimeZero)
    } catch {
        print("Error with insertTimeRange. Audio error: \(error).")
    }

    // Add text to video
    // -- Create video composition object
    let renderSize = compositionVideoTrack.naturalSize
    let videoComposition = AVMutableVideoComposition()
    videoComposition.renderSize = renderSize
    videoComposition.frameDuration = CMTimeMake(Int64(1), Int32(videoFrameRate))

    // -- Add instruction to  video composition object
    let instruction = AVMutableVideoCompositionInstruction()
    instruction.timeRange = CMTimeRangeMake(kCMTimeZero, videoAsset.duration)
    let videoLayerInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: compositionVideoTrack)
    instruction.layerInstructions = [videoLayerInstruction]
    videoComposition.instructions = [instruction]

    // -- Define video frame
    let videoFrame = CGRectMake(0, 0, renderSize.width, renderSize.height)
    print("Video Frame: \(videoFrame)")  // <-- Prints frame of 320x480 so render size already wrong here 
    ...
Crashalot
  • 33,605
  • 61
  • 269
  • 439
  • did you look at `naturalSize` value? what is it? And what are you doing next? You are using `AVAssetExportSession`? – rkyr Jan 10 '16 at 17:52
  • @rkyr yes, we `renderSize` is equal to `naturalSize`. At the point of the print out the render size is already wrong (320x480), so that's why no more code was included. Any suggestions? – Crashalot Jan 10 '16 at 20:49

2 Answers2

11

If I get you right, it seems that you have misunderstood the fact that device screen width is'n equal to camera preview (and capture) size.

The videoGravity property of your previewLayer indicates how to stretch/fit your preview inside your layer. It doesn't affect capture output.

Actual frame size of output depends on sessionPreset property of your current AVCaptureSession. And as I can understand by reading GitHub repository of PBJVision lib, its singleton has setter for this (called captureSessionPreset). You can change it inside your initPBJVision method.

There you can find possible values of session presets.

Crashalot
  • 33,605
  • 61
  • 269
  • 439
rkyr
  • 3,131
  • 2
  • 23
  • 38
  • Thanks @rkyr but from the class docs, it seems like `sessionPreset` affects the bitrate but not the actual dimensions? How do you change the actual dimensions of the capture and get it to match the preview? – Crashalot Jan 10 '16 at 21:29
  • @Crashalot, it change the image quality by changing its dimension. API didn't allow you to set the capturing frame. [Here](http://stackoverflow.com/a/14322378/5247504) you can find how someone deal with it. – rkyr Jan 11 '16 at 06:11
  • Hi @rkyr what's the best way to reach you? Would like to ask a quick question. – Crashalot Jan 19 '16 at 23:40
  • Hi @Crashalot. Please write me to roma.kyrylenko@gmail.com and we'll reach agreement. – rkyr Jan 20 '16 at 08:11
  • Have you found a solution for this @Crashalot? I am trying to fix it for some time now as well. Thanks! – Dani Pralea Jan 30 '16 at 23:54
  • @DanutPralea yes, it's as said. The input size is dictated by the sessionPreset setting for AVCaptureSession while the output size for the video is dictated by the renderSize property of the video composition. If the two are not the same, you must apply transforms to make the input size match the output size. – Crashalot Jan 31 '16 at 05:03
  • Thank you for your reply. @Crashalot can you give a code snippet of how you did it? Because I'm doing the exact same things you did in your code snippets – Dani Pralea Jan 31 '16 at 10:07
  • @DanutPralea hi, I'm in the same boat as you were, were you able to figure this out? I came across a comment that said captureSession.sessionPreset = .hd1920x1080 for full screen video. I haven't been able to try it yet – Lance Samaria Jul 22 '20 at 09:37
  • @Crashalot How do you apply the transforms? Can you send link to a similar answer? – Lance Samaria Jul 22 '20 at 09:43
4

Try SDAVAssetExportSession

You can specify the AVVideoWidthKey and AVVideoHeightKey You can also specify the profile, AVVideoProfileLevelKey ie AVVideoProfileLevelH264HighAutoLevel for supporting something 4k, a friend (Michael Lowin) tracked that property down to help us improve some export qualities.

Lamour
  • 3,002
  • 2
  • 16
  • 28
Mark Essel
  • 4,606
  • 2
  • 29
  • 47