[Tutorial] How to trim videos in iOS with ABVideoRangeSlider — Part 1 of 2

Apps Boulevard
6 min readDec 10, 2016

--

This tutorial is made with Swift 3 using the ABVideoRangeSlider library. We included a simple video player in this example.

This example is hosted in our Github repository!

Creating the project

Let’s create our new project using a Single View Application template:

and let’s call it videoeditor.

Preparing the User Interface

First, let’s create our video player. Go to your Main.Storyboard and add an UIView and set the constraints

In this UIView, we’ll play the video

And add two buttons: Play and Pause

Connect these buttons to your ViewController.swift:

@IBOutlet var btnPlay: UIButton!
@IBOutlet var btnPause: UIButton!

Same with the UIViewthat will contain our video

@IBOutlet var videoContainer: UIView!

Adding video to our project

Drag and drop a video in the project navigator, in this case I’m using a mp4 video. And make sure that Copy items if needed and your project’s target are checked.

Go to your ViewController.swift and add this line in ViewDidLoad method:

let path = Bundle.main.path(forResource: "test", ofType:"mp4")

Now, in order to play that video we need to import AVFoundation into our controller:

import AVFoundation

and add this two properties to our controller:

let avPlayer = AVPlayer()var avPlayerLayer: AVPlayerLayer!

Initialize the AVPlayer and AVPlayerLayer

After getting the path of our video, we need to create an AVPlayerItem using that path, and initialize the avPlayer:

let playerItem = AVPlayerItem(url: URL(fileURLWithPath: path))avPlayer.replaceCurrentItem(with: playerItem)

and set our avPlayerLayer:

avPlayerLayer = AVPlayerLayer(player: avPlayer)videoContainer.layer.insertSublayer(avPlayerLayer, at: 0)videoContainer.layer.masksToBounds = true

The last two lines, we add that layer to our videoContainer. In order to be sure our video player fit our container we have to add this line to the viewWillLayoutSubviews method:

override func viewDidLayoutSubviews() {   super.viewWillLayoutSubviews()   avPlayerLayer.frame = videoContainer.bounds}

Now add the corresponding actions to our buttons Play and Pause, and connect them through Interface Builder:

@IBAction func playTapped(_ sender: Any) {   avPlayer.play()   btnPlay.isEnabled = false   btnPause.isEnabled = true}@IBAction func pauseTapped(_ sender: Any) {   avPlayer.pause()   btnPlay.isEnabled = true   btnPause.isEnabled = false}

Our ViewController.swift should look like this:

Now we can stop an play our video, but we cannot seek to a specific time (yet!). So, lets add our ABVideoRangeSlider, which has a progress indicator integrated that will help us.

Adding ABVideoRangeSlider pod

Open Terminal and go to your project’s root folder, and create a Podfile using the following command:

pod init

Open Podfile and add:

target 'videoeditor' do
use_frameworks!
pod 'ABVideoRangeSlider'
end

And run pod install . After installing it, remember to close your current project and open the new .xcworkspace file.

Now that ABVideoRangelSlider is installed, let’s add it to our Main.storyboard:

Drag an UIView into your view, and set the constraints. I set the height value to 40, and changed the background color to a dark gray. Then change its class to ABVideoRangeSlider:

And connect this new view to our ViewController.swift:

@IBOutlet var rangeSlider: ABVideoRangeSlider!

Then inside viewDidLoad, set the video url for our rangeSlider:

rangeSlider.setVideoURL(videoURL: URL(fileURLWithPath: path))

Run the project and the preview of the video will appear in our range slider. But our progress indicator doesn’t move at all. Let’s add our code to make it work!

We need to add some new properties to our controller first:

var startTime = 0.0;
var endTime = 0.0;
var progressTime = 0.0;
var shouldUpdateProgressIndicator = true
var isSeeking = false

These values will store the start and end time (in seconds) of our video, a flag to tell the app if the progress indicator should be updated, and a flag telling us if the player is seeking.

In our viewDidLoad, set the endTime as the total duration of the video.

self.endTime = CMTimeGetSeconds((avPlayer.currentItem?.duration)!)

We have set up our rangeSlider, but we didn’t implemented the delegates yet. Add ABVideoRangeSliderDelegate and add this line after setting the video url:

rangeSlider.delegate = self

and add the delegate methods:

func didChangeValue(videoRangeSlider: ABVideoRangeSlider, startTime: Float64, endTime: Float64) {}func indicatorDidChangePosition(videoRangeSlider: ABVideoRangeSlider, position: Float64) {}

The first method, tracks the start and end indicators, returning the time in seconds of their current positions.

The second method, returns the time in seconds of the progress indicator. And its called when this indicator is dragged.

Add a time observer

Add another property to the controller

var timeObserver: AnyObject!

And initialize it inside viewDidLoad:

let timeInterval: CMTime = CMTimeMakeWithSeconds(0.01, 100)timeObserver = avPlayer.addPeriodicTimeObserver(forInterval: timeInterval,queue: DispatchQueue.main) { (elapsedTime: CMTime) -> Void inself.observeTime(elapsedTime: elapsedTime) } as AnyObject!

This observer will call observeTime every 0.01 seconds, and will update our progress indicator.

Now add this method below:

private func observeTime(elapsedTime: CMTime) {   let elapsedTime = CMTimeGetSeconds(elapsedTime)   if (avPlayer.currentTime().seconds > self.endTime){       avPlayer.pause()       btnPlay.isEnabled = true       btnPause.isEnabled = false   }   if self.shouldUpdateProgressIndicator{      rangeSlider.updateProgressIndicator(seconds: elapsedTime)   }}

Here, we check if the current time didn’t reach the time it should be paused. Now, the self.endTime is equal to the total duration of the video. And also, we tell the rangeSlider to update the position of the progress indicator

Run the app and see how the indicator moves when our video is playing. Finally!

Updating startTime and endTime

If we want to play only the range selected, we need to update startTime and endTime. Add these lines to our delegate we implemented before:

func didChangeValue(videoRangeSlider: ABVideoRangeSlider, startTime: Float64, endTime: Float64) {   self.endTime = endTime   if startTime != self.startTime{
self.startTime = startTime
let timescale = self.avPlayer.currentItem?.asset.duration.timescale
let time = CMTimeMakeWithSeconds(self.startTime, timescale!)
if !self.isSeeking{
self.isSeeking = true
avPlayer.seek(to: time, toleranceBefore: kCMTimeZero, toleranceAfter: kCMTimeZero){_ in
self.isSeeking = false
}
}
}
}

Here, we update our values every time the rangeSlider its moved, and seek to the startTime

Seek with the progress indicator

In order to move through the video, we have to track the position of the progress indicator every time it’s dragged. Add these lines to our delegate method:

func indicatorDidChangePosition(videoRangeSlider: ABVideoRangeSlider, position: Float64) {
self.shouldUpdateProgressIndicator = false
avPlayer.pause()
btnPlay.isEnabled = true
btnPause.isEnabled = false
if self.progressPosition != position {
self.progressPosition = position
let timescale = self.avPlayer.currentItem?.asset.duration.timescale
let time = CMTimeMakeWithSeconds(self.progressPosition, timescale!)
if !self.isSeeking{
self.isSeeking = true
avPlayer.seek(to: time, toleranceBefore: kCMTimeZero, toleranceAfter: kCMTimeZero){_ in
self.isSeeking = false } } }
}

And add this line inside the playTapped method of our button:

self.shouldUpdateProgressIndicator = true

So far, we are able to indicate where to start and end the video, and seek to a specific time with the progress indicator.

Our controller should look like this:

Run the project and test it!

The example project used here is in our Github, so feel free to download it and play with it.

In the part 2 of this tutorial, we’ll trim the video using the startTime and endTime , and export it as a new video!

--

--