Creating a live video playback app in Swift 3 using XCode

This guide focuses on the bare minimum required to play broadcasts. Make sure to familiarize yourself with the example apps in the SDK bundles for a more complete overview.

Choose your environment:

Xcode (Objective C) Xcode (Swift 3)

Xcode and Swift

  • Open Xcode.

  • Select File -> New -> Project...

  • Choose the Single View Application template.

  • Enter a suitable product name, your organization and identifier

  • Ensure Swift is the selected language. Or see the if you prefer Objective-C.

Add dependencies

Installing dependencies using CocoaPods

If your development environment is set up for it, you can use CocoaPods to install the Bambuser SDK:s.

  • Prepare the newly created project for CocoaPods use with pod init.

  • Add pod 'libbambuser-ios', '0.9.19' to your Podfile.

  • Run pod install to fetch the SDK and configure the project.

Add the objective-C bridging header

As the Bambuser player SDK is written in Objective-C, we will need to add a bridging header to our Swift project.

  • Create a new Header file and add it to the project - in this example we name it libbambuser-bridge.h

  • Add the following import to the new file:

#import "libbambuserplayer.h"
- Navigate through the project settings by selecting the project in the navigation view - Select your target and navigate to the Build Settings tab - Under the section Swift Compiler - General, edit the property named Objective-C Bridging Header and set it to the path where you created the header file. In our example it is `PlaybackDemo/libbambuser-bridge.h`

Installing dependencies manually

The Bambuser playback library has the following dependencies:

  • AudioToolbox, AVFoundation, CoreMedia, Foundation, MediaPlayer and UIKit.

Add the playback SDK

  • Log in to the Bambuser site and download the latest iOS SDK bundle from the Developer page.

  • Open up Finder and navigate to the download folder, then double click the SDK bundle to unzip it.

  • Drag the contents of the prebuilt/ directory from the SDK bundle into the Frameworks section of your Xcode project's file tree navigator.

  • Check Copy items if needed to ensure that your project folder gets its own copy of the files.
  • Select the newly added prebuilt group, press enter to rename it to something more descriptive.
  • The act of adding prebuilt into Frameworks should also have added libbambuser.a and libbambuserplayer.a to Linked Frameworks and Libraries under General. For the purpose of this guide, at least libbambuserplayer.a must be linked.

Add Objective C bridging header

  • Drag the Objective-C bridging header from the testplayer-swift project from the iOS SDK bundle to your project.

  • Under the build settings for your build target, add the bridging header.

Add a BambuserPlayer

Now that you have installed the dependencies it's time to start using the SDK.

	var bambuserPlayer: BambuserPlayer

Add the player view

In ViewController.swift in the viewDidLoad method, add the player view to the view hierarchy.

self.view.addSubview(bambuserPlayer)

In ViewController.swift add a viewWillLayoutSubviews method, then add some code to ensure the player view is assigned the desired dimensions.

override func viewWillLayoutSubviews() {
	let statusBarOffset = self.topLayoutGuide.length
	bambuserPlayer.frame = CGRect(x: 0, y: 0 + statusBarOffset, width: self.view.bounds.size.width, height: self.view.bounds.size.height - statusBarOffset)
}

Authentication

To be allowed to retrieve broadcasts, your app needs to identify itself to Bambuser. Head over to the Developer page on the Bambuser site again and get the Sandbox application ID, then assign it to BambuserPlayer in ViewController.swift directly after initialization.

Remember to replace the Sandbox id with a Production application ID before you release your app!

bambuserPlayer.applicationId = "GFZalqkR5iyZcIgaolQmA"

We will also need a signed resource URI for the broadcast we want to play. For the scope of this guide, we will use a pre-signed resource URI supplied in the example application distributed with the Bambuser iOS SDK bundle. To learn how to sign your own resource URIs, refer to the chapter on Resource URI.

bambuserPlayer.playVideo("https://cdn.bambuser.net/broadcasts/ec968ec1-2fd9-f8f3-4f0a-d8e19dccd739?da_signature_method=HMAC-SHA256&da_id=432cebc3-4fde-5cbb-e82f-88b013140ebe&da_timestamp=1456740399&da_static=1&da_ttl=0&da_signature=8e0f9b98397c53e58f9d06d362e1de3cb6b69494e5d0e441307dfc9f854a2479")

Add playback controls

Let's add some buttons to be able to control the playback.

In ViewController.swift, declare the following buttons:

class ViewController: UIViewController {
	var bambuserPlayer: BambuserPlayer
	var playButton: UIButton
	var pauseButton: UIButton
	var rewindButton: UIButton
	...

In ViewController.swift, in the viewDidLoad method, initialize the buttons and add them to the view hierarchy:

playButton.setTitle("Play", for: UIControlState.normal)
playButton.addTarget(bambuserPlayer, action: #selector(BambuserPlayer.playVideo as (BambuserPlayer) -> () -> Void), for: UIControlEvents.touchUpInside)
self.view.addSubview(playButton)
pauseButton.setTitle("Pause", for: UIControlState.normal)
pauseButton.addTarget(bambuserPlayer, action: #selector(BambuserPlayer.pauseVideo as (BambuserPlayer) -> () -> Void), for: UIControlEvents.touchUpInside)
self.view.addSubview(pauseButton)
rewindButton.setTitle("Rewind", for: UIControlState.normal)
rewindButton.addTarget(self, action: #selector(ViewController.rewind), for: UIControlEvents.touchUpInside)
self.view.addSubview(rewindButton)

In ViewController.swift, in the viewWillLayoutSubviews method, position the buttons to taste

playButton.frame = CGRect(x: 20, y: 20 + statusBarOffset, width: 100, height: 40)
pauseButton.frame = CGRect(x: 20, y: 80 + statusBarOffset, width: 100, height: 40)
rewindButton.frame = CGRect(x: 20, y: 140 + statusBarOffset, width: 100, height: 40)

Also add a new method to handle the rewind action

func rewind() {
	bambuserPlayer.seek(to: 0.0);
}

Subscribe to events

To know what's going on inside BambuserPlayer, we should hook up ourself as its delegate. In ViewController.swift, assign self to bambuserPlayer.delegate directly after initialization:

bambuserPlayer.delegate = self;

In ViewController.swift, we now need to declare that we support the BambuserPlayerDelegate protocol:

class ViewController: UIViewController, BambuserPlayerDelegate {

Finally, we add a few callback methods in ViewController.swift:

func playbackStarted() {
	playButton.isEnabled = false
	pauseButton.isEnabled = true
}

func playbackPaused() {
	playButton.isEnabled = true
	pauseButton.isEnabled = false
}

func playbackStopped() {
	playButton.isEnabled = true
	pauseButton.isEnabled = false
}

func videoLoadFail() {
	NSLog("Failed to load video for %@", bambuserPlayer.resourceUri);
}

Putting it all together

The above steps should lead to a main view source somewhat along these lines:

ViewController.swift

import UIKit

class ViewController: UIViewController, BambuserPlayerDelegate {
	var bambuserPlayer: BambuserPlayer
	var playButton: UIButton
	var pauseButton: UIButton
	var rewindButton: UIButton

	required init?(coder aDecoder: NSCoder) {
		bambuserPlayer = BambuserPlayer()
		playButton = UIButton(type: UIButtonType.system)
		pauseButton = UIButton(type: UIButtonType.system)
		rewindButton = UIButton(type: UIButtonType.system)
		super.init(coder: aDecoder)
	}

	override func viewDidLoad() {
		super.viewDidLoad()
		// Do any additional setup after loading the view, typically from a nib.
		bambuserPlayer.delegate = self
		bambuserPlayer.applicationId = "GFZalqkR5iyZcIgaolQmA"
		bambuserPlayer.playVideo("https://cdn.bambuser.net/broadcasts/ec968ec1-2fd9-f8f3-4f0a-d8e19dccd739?da_signature_method=HMAC-SHA256&da_id=432cebc3-4fde-5cbb-e82f-88b013140ebe&da_timestamp=1456740399&da_static=1&da_ttl=0&da_signature=8e0f9b98397c53e58f9d06d362e1de3cb6b69494e5d0e441307dfc9f854a2479")
		self.view.addSubview(bambuserPlayer)
		playButton.setTitle("Play", for: UIControlState.normal)
		playButton.addTarget(bambuserPlayer, action: #selector(BambuserPlayer.playVideo as (BambuserPlayer) -> () -> Void), for: UIControlEvents.touchUpInside)
		self.view.addSubview(playButton)
		pauseButton.setTitle("Pause", for: UIControlState.normal)
		pauseButton.addTarget(bambuserPlayer, action: #selector(BambuserPlayer.pauseVideo as (BambuserPlayer) -> () -> Void), for: UIControlEvents.touchUpInside)
		self.view.addSubview(pauseButton)
		rewindButton.setTitle("Rewind", for: UIControlState.normal)
		rewindButton.addTarget(self, action: #selector(ViewController.rewind), for: UIControlEvents.touchUpInside)
		self.view.addSubview(rewindButton)
	}

	func rewind() {
		bambuserPlayer.seek(to: 0.0);
	}

	override func viewWillLayoutSubviews() {
		let statusBarOffset = self.topLayoutGuide.length
		bambuserPlayer.frame = CGRect(x: 0, y: 0 + statusBarOffset, width: self.view.bounds.size.width, height: self.view.bounds.size.height - statusBarOffset)
		playButton.frame = CGRect(x: 20, y: 20 + statusBarOffset, width: 100, height: 40)
		pauseButton.frame = CGRect(x: 20, y: 80 + statusBarOffset, width: 100, height: 40)
		rewindButton.frame = CGRect(x: 20, y: 140 + statusBarOffset, width: 100, height: 40)
	}

	override func didReceiveMemoryWarning() {
		super.didReceiveMemoryWarning()
		// Dispose of any resources that can be recreated.
	}

	func playbackStarted() {
		playButton.isEnabled = false
		pauseButton.isEnabled = true
	}

	func playbackPaused() {
		playButton.isEnabled = true
		pauseButton.isEnabled = false
	}

	func playbackStopped() {
		playButton.isEnabled = true
		pauseButton.isEnabled = false
	}

	func videoLoadFail() {
		NSLog("Failed to load video for %@", bambuserPlayer.resourceUri);
	}
}

Testing in the simulator

To test in the simulator, we just select a suitable simulator device as a target and click run. If your applicationId and resourceUri were valid, you should see something like this.

Testing on a device

How to configure Xcode for ad-hoc development is out of scope for this guide. Assuming you have all the required privileges, you should be able to tether your iOS device to Xcode and click run.

If your applicationId and resourceUri were valid, you should see something like this.