How to Play, Record and Merge Videos in iOS and Swift
Learn the basics of working with videos on iOS with AV Foundation in this tutorial. You’ll play, record and even do some light video editing! By Owen L Brown.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
How to Play, Record and Merge Videos in iOS and Swift
30 mins
Saving Video
Back in RecordVideoViewController.swift, add the following method to the UIImagePickerControllerDelegate
extension:
func imagePickerController(
_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]
) {
dismiss(animated: true, completion: nil)
guard
let mediaType = info[UIImagePickerController.InfoKey.mediaType] as? String,
mediaType == (kUTTypeMovie as String),
// 1
let url = info[UIImagePickerController.InfoKey.mediaURL] as? URL,
// 2
UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(url.path)
else { return }
// 3
UISaveVideoAtPathToSavedPhotosAlbum(
url.path,
self,
#selector(video(_:didFinishSavingWithError:contextInfo:)),
nil)
}
Don’t worry about the error — you’ll take care of that shortly.
- As before, the delegate method gives you a URL pointing to the video.
- Verify that the app can save the file to the device’s photo album.
- If it can, save it.
UISaveVideoAtPathToSavedPhotosAlbum
is the function provided by the SDK to save videos to the device’s photo album. You pass it the path to the video you want to save as well as a target and action to call back, which will inform you of the status of the save operation.
Next, add the implementation of the callback to the main class definition:
@objc func video(
_ videoPath: String,
didFinishSavingWithError error: Error?,
contextInfo info: AnyObject
) {
let title = (error == nil) ? "Success" : "Error"
let message = (error == nil) ? "Video was saved" : "Video failed to save"
let alert = UIAlertController(
title: title,
message: message,
preferredStyle: .alert)
alert.addAction(UIAlertAction(
title: "OK",
style: UIAlertAction.Style.cancel,
handler: nil))
present(alert, animated: true, completion: nil)
}
The callback method simply displays an alert to the user, announcing whether the video file was saved or not, based on the error status.
Build and run. Record a video and select Use Video when you’re done recording. If you’re asked for permission to save to your video library, tap OK. When the Video was saved alert pops up, you just successfully saved your video to the photo library!
Now that you can play videos and record videos, it’s time to take the next step and try some light video editing.
Merging Videos
The final piece of functionality for the app is to do a little editing. Your user will select two videos and a song from the music library, and the app will combine the two videos and mix in the music.
The project already has a starter implementation in MergeVideoViewController.swift, with the code similar to the code you wrote to play a video. The big difference is when merging, the user must select two videos. That part is already set up, so the user can make two selections that will be stored in firstAsset
and secondAsset
.
The next step is to add the functionality to select the audio file.
Selecting the Audio File
UIImagePickerController
provides functionality to select only video and images from the media library. To select audio files from your music library, you must use MPMediaPickerController
. It works essentially the same as UIImagePickerController
, but instead of images and video, it accesses audio files in the media library.
Open MergeVideoViewController.swift and add the following code to loadAudio(_:):
let mediaPickerController = MPMediaPickerController(mediaTypes: .any)
mediaPickerController.delegate = self
mediaPickerController.prompt = "Select Audio"
present(mediaPickerController, animated: true, completion: nil)
The code above creates a new MPMediaPickerController
instance and displays it as a modal view controller.
Build and run. Now, tap Merge Video, then Load Audio to access the audio library on your device.
Of course, you’ll need some audio files on your device. Otherwise, the list will be empty. The songs will also have to be physically present on the device, so make sure you’re not trying to load a song from the cloud.
Select a song from the list and you’ll notice that nothing happens. That’s right! MPMediaPickerController
needs delegate methods!
To implement them, find the MPMediaPickerControllerDelegate
extension at the bottom of the file and add the following two methods to it:
func mediaPicker(
_ mediaPicker: MPMediaPickerController,
didPickMediaItems mediaItemCollection: MPMediaItemCollection
) {
// 1
dismiss(animated: true) {
// 2
let selectedSongs = mediaItemCollection.items
guard let song = selectedSongs.first else { return }
// 3
let title: String
let message: String
if let url = song.value(forProperty: MPMediaItemPropertyAssetURL) as? URL {
self.audioAsset = AVAsset(url: url)
title = "Asset Loaded"
message = "Audio Loaded"
} else {
self.audioAsset = nil
title = "Asset Not Available"
message = "Audio Not Loaded"
}
// 4
let alert = UIAlertController(
title: title,
message: message,
preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil))
self.present(alert, animated: true, completion: nil)
}
}
func mediaPickerDidCancel(_ mediaPicker: MPMediaPickerController) {
// 5
dismiss(animated: true, completion: nil)
}
The code above is similar to the delegate methods for UIImagePickerController
. Here’s what it does:
- Dismiss the picker just like you did before.
- Find the selected songs and, from that, the first one in the case that multiple are selected.
- Obtain the URL to the media asset that backs the song. Then make an
AVAsset
pointing to the song that was chosen. - Finally, for
mediaPicker(_:didPickMediaItems:)
, show an alert to indicate if the asset was successfully loaded or not. - In the case the media picker was canceled, simply dismiss the view controller.
Build and run, then go to the Merge Videos screen. Select an audio file and you’ll see the Audio Loaded message.
You now have all your assets loading correctly so it’s time to merge the various media files into one file. But before you get into that code, you need to do a bit of setup.
Merging Completion Handler
You will shortly write the code to merge your assets. This will need a completion handler that saves the final video to the photo album. You’ll add this first.
Add the following import statement at the top of the MergeVideoViewController.swift file:
import Photos
Then, add the method below to MergeVideoViewController
:
func exportDidFinish(_ session: AVAssetExportSession) {
// 1
activityMonitor.stopAnimating()
firstAsset = nil
secondAsset = nil
audioAsset = nil
// 2
guard
session.status == AVAssetExportSession.Status.completed,
let outputURL = session.outputURL
else { return }
// 3
let saveVideoToPhotos = {
// 4
let changes: () -> Void = {
PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: outputURL)
}
PHPhotoLibrary.shared().performChanges(changes) { saved, error in
DispatchQueue.main.async {
let success = saved && (error == nil)
let title = success ? "Success" : "Error"
let message = success ? "Video saved" : "Failed to save video"
let alert = UIAlertController(
title: title,
message: message,
preferredStyle: .alert)
alert.addAction(UIAlertAction(
title: "OK",
style: UIAlertAction.Style.cancel,
handler: nil))
self.present(alert, animated: true, completion: nil)
}
}
}
// 5
if PHPhotoLibrary.authorizationStatus() != .authorized {
PHPhotoLibrary.requestAuthorization { status in
if status == .authorized {
saveVideoToPhotos()
}
}
} else {
saveVideoToPhotos()
}
}
Here’s what that code does:
- There’s a spinner that will animate when the assets are being processed. This stops the spinner and then clears the assets ready to select new ones.
- Ensure that the processing is complete and there is a URL of the resulting video.
- Create a closure that…
- Tells the photo library to make a “create request” from the resulting video before showing an alert to indicate if this succeeds or fails.
- Check if there is permission to access the photo library. If there is not permission, then ask for it before running the closure that saves the video. Otherwise, simply run the closure immediately as permission is already granted.
Now, you’ll add some code to merge(_:)
. Because there’s a lot of code, you’ll complete this in steps.