Skip to main content
Version: iOS SDK v1.0.11

Create a Custom Recorder

This guide shows you how to create a custom recorder.

Get started by adding VideoKit into the Podfile of your Xcode project.

Setup

Refer to our Getting Started Guide on how to install VideoKit into your project.

Import VideoKitRecorder and AVFoundation in your ViewController

import VideoKitRecorder
import AVFoundation

Add Controls

Add a record button, the camrea preview and a recording progress.

For the button, our Recorder package ships with a convenient VKRecordButton.

The recording progress can be displayed using our progress view VKRecordingProgressView.

internal var recordButton: VKRecordButton = {
let button = VKRecordButton(frame: CGRect(origin: .zero, size: CGSize(width: 90, height: 90)))
return button
}()

internal var previewView: UIView = {
let view = UIView(frame: UIScreen.main.bounds)
view.backgroundColor = .black
return view
}()

var recordingProgressView: VKRecordingProgressView = {
let view = VKRecordingProgressView(progressViewStyle: .default)
return view
}()

The default max recording time for the progress indicator is 60 seconds. You can change that by adjusting recordingProgressView.maxTime. Make sure that you also stop your recording after 60 seconds!

Delegates and Recorder Instance

Add a reference to the shared VKRecorder instance and a delegate variable.

internal var vkRecorder: VKRecorder = VKRecorder.shared
internal var delegate: VKRecorderDelegate?

Initialize Recorder

Define a function to make sure video and audio persmissions are granted and to start the camera.

func startCamera() {
if vkRecorder.canCaptureVideo {
if VKRecorder.authorizationStatus(forMediaType: AVMediaType.video) == .authorized &&
VKRecorder.authorizationStatus(forMediaType: AVMediaType.audio) == .authorized {

do {
if vkRecorder.session == nil {
try vkRecorder.start()
}
} catch let error {
print("Failed to start camera \(error)")
}

} else {
VKRecorder.requestAuthorization(forMediaType: .video) { (mediaType, status) in
if VKRecorder.authorizationStatus(forMediaType: AVMediaType.video) == .authorized && VKRecorder.authorizationStatus(forMediaType: AVMediaType.audio) == .authorized {
self.startCamera()
}
}

VKRecorder.requestAuthorization(forMediaType: AVMediaType.audio) { (mediaType, status) in
if VKRecorder.authorizationStatus(forMediaType: AVMediaType.video) == .authorized && VKRecorder.authorizationStatus(forMediaType: AVMediaType.audio) == .authorized {
self.startCamera()
}
}
}
}
}

Now that we have a preview view, a recording button and the recorders preview and recording instance defined, we can setup the recorder and controls in our viewDidLoad.

override func viewDidLoad() {
super.viewDidLoad()

view.addSubview(previewView)
vkRecorder.delegate = self
vkRecorder.videoDelegate = self
vkRecorder.videoConfiguration.autoMergeClipsAfterRecording = true

view.addSubview(recordButton)
let safeAreaBottom: CGFloat = view.safeAreaInsets.bottom
recordButton.center = CGPoint(x: view.bounds.midX, y: view.bounds.size.height - safeAreaBottom - (recordButton.frame.size.height * 0.5) - 50.0)
recordButton.addTarget(self, action: #selector(toggleRecording), for: .touchUpInside)

view.addSubview(recordingProgressView)
recordingProgressView.center = CGPoint(x: view.bounds.midX, y: view.bounds.size.height - safeAreaBottom - (recordButton.frame.size.height * 0.5) - 125)

previewView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
vkRecorder.previewLayer.frame = previewView.bounds
previewView.layer.addSublayer(vkRecorder.previewLayer)
}

Update Progress View

To update the progress view to reflect the current recording time, implement the vkRecorder didAppendVideoSampleBuffer inSession delegate in your view controller.

extension ViewController: VKRecorderVideoDelegate {
func vkRecorder(_ vkRecorder: VKRecorder, didAppendVideoSampleBuffer sampleBuffer: CMSampleBuffer, inSession session: VKRecorderSession) {
let progress = Float(session.totalDuration.seconds / recordingProgressView.maxTime)

recordingProgressView.setProgress(min(max(progress, 0), 1), animated: true)
}
}

Access Clips after Recording

Since we are using autoMergeClipsAfterRecording we are able to get access to our final video through our delegate method vkRecorderDidFinishAutoMerge.

extension ViewController: VKRecorderDelegate {
func vkRecorderDidFinishAutoMerge(_ url: URL, _ session: VKRecorderSession) {
print("\(session.clips.count) video clip(s) have/has been merged to \(url.absoluteString)")
}
}

Full Example

Here is the full source code of the final View Controller:

ViewController.swift
//
// ViewController.swift
// VideoKitCustomRecorder
//
// Created by Dennis Stücken on 11/11/20.
//
import UIKit
import VideoKitRecorder
import AVFoundation

class ViewController: UIViewController {
internal var videoURL: URL?
internal var vkRecorder: VKRecorder = VKRecorder.shared

internal var recordButton: VKRecordButton = {
let button = VKRecordButton(frame: CGRect(origin: .zero, size: CGSize(width: 90, height: 90)))
return button
}()

internal var previewView: UIView = {
let view = UIView(frame: UIScreen.main.bounds)
view.backgroundColor = .black
return view
}()

var recordingProgressView: VKRecordingProgressView = {
let view = VKRecordingProgressView(progressViewStyle: .default)

// Set recoding time to 60 seconds max
view.maxTime = 60

return view
}()

internal var isRecording: Bool = false {
didSet {
if isRecording {
recordButton.startRecordingAnimation()
recordButton.showStopIndicator()
} else {
recordButton.stopRecordingAnimation()
recordButton.hideStopIndicator()
}
}
}

func startCamera() {
if vkRecorder.canCaptureVideo {
if VKRecorder.authorizationStatus(forMediaType: AVMediaType.video) == .authorized &&
VKRecorder.authorizationStatus(forMediaType: AVMediaType.audio) == .authorized {

do {
// Only start session if session is not already started
if vkRecorder.session == nil {
try vkRecorder.start()
}
} catch let error {
print("Failed to start camera \(error)")
}

} else {
// Ask for video permissions
VKRecorder.requestAuthorization(forMediaType: .video) { (mediaType, status) in
if VKRecorder.authorizationStatus(forMediaType: AVMediaType.video) == .authorized && VKRecorder.authorizationStatus(forMediaType: AVMediaType.audio) == .authorized {
self.startCamera()
}
}

// Ask for audio permissions
VKRecorder.requestAuthorization(forMediaType: AVMediaType.audio) { (mediaType, status) in
if VKRecorder.authorizationStatus(forMediaType: AVMediaType.video) == .authorized && VKRecorder.authorizationStatus(forMediaType: AVMediaType.audio) == .authorized {
self.startCamera()
}
}
}
}
}

override func viewDidLoad() {
super.viewDidLoad()

view.addSubview(previewView)
vkRecorder.delegate = self
vkRecorder.videoDelegate = self
vkRecorder.videoConfiguration.autoMergeClipsAfterRecording = true

view.addSubview(recordButton)
let safeAreaBottom: CGFloat = view.safeAreaInsets.bottom
recordButton.center = CGPoint(x: view.bounds.midX, y: view.bounds.size.height - safeAreaBottom - (recordButton.frame.size.height * 0.5) - 50.0)
recordButton.addTarget(self, action: #selector(toggleRecording), for: .touchUpInside)

view.addSubview(recordingProgressView)
recordingProgressView.center = CGPoint(x: view.bounds.midX, y: view.bounds.size.height - safeAreaBottom - (recordButton.frame.size.height * 0.5) - 125)

previewView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
vkRecorder.previewLayer.frame = previewView.bounds
previewView.layer.addSublayer(vkRecorder.previewLayer)
}

override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
startCamera()
}

override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
vkRecorder.stop()
}

override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
vkRecorder.previewLayer.frame = previewView.bounds
}

fileprivate func startRecording() {
if !isRecording {
isRecording = true

// Reset zoom factor
vkRecorder.videoZoomFactor = 0

// Start recording
vkRecorder.record()
}
}

@objc func toggleRecording() {
if isRecording {
vkRecorder.pause()
isRecording = false
} else {
startRecording()
}
}

deinit {
vkRecorder.stop()
vkRecorder.session?.removeAllClips()
}
}

extension ViewController: VKRecorderVideoDelegate {

func vkRecorder(_ vkRecorder: VKRecorder, didAppendVideoSampleBuffer sampleBuffer: CMSampleBuffer, inSession session: VKRecorderSession) {
let progress = Float(session.totalDuration.seconds / recordingProgressView.maxTime)

recordingProgressView.setProgress(min(max(progress, 0), 1), animated: true)
}

}

extension ViewController: VKRecorderDelegate {
func vkRecorderDidFinishAutoMerge(_ url: URL, _ session: VKRecorderSession) {
print("\(session.clips.count) video clip(s) have/has been merged to \(url.absoluteString)")
}
}

Source Code

If you prefer to take a look at the working project, here is the full project source code:

Download Source

Github