This article details how to integrate CDN Mesh Delivery into your mobile app using Brightcove Player on iOS and tvOS
Not into tutorials? |
Brightcove Player
The Brightcove iOS and tvOS Player SDK is a mobile player that provide tools for building video-enabled apps for iOS and tvOS.
To learn more about the Brightcove Player SDKs, please refer to their documentation.
Project setup
CocoaPods
Note
Cocoapods 1.6.0 + is recommended
To integrate StreamrootSDK and Brightcove Player SDK into your Xcode project using CocoaPods, specify this in your Podfile
:
use_frameworks!
target ' <Your Target Name >'
pod 'StreamrootSDK', '~> 3.25.1'
pod 'Brightcove-Player-Core/dynamic',
end
Then, run the following command:
$ pod update
Finally, open the generated Workspace.
Integrate Streamroot SDK
1. Import the SDK
import StreamrootSDK
2. Set the streamrootKey
In the Project Navigator, right click on the main target "Info.plist", and "Open as" → "Source Code".
Add the following lines with the values of the right parameters:
<key>Streamroot</key>
<dict>
<key>Key</key>
<string>streamrootKey</string>
</dict>
Here, <streamrootKey>
refers to your Streamroot key that you will find in the Account section of the Streamroot Dashboard. If you don't have a Streamroot Key, you can register for a free trial account on our website.
3. Declare the Mesh Delivery Client as an Instance Variable
var dnaClient: DNAClient?
4. Build and start the Mesh Delivery Client
do {
dnaClient = try DNAClient.builder()
.dnaClientDelegate(self)
.latency(30)
.streamrootKey(<#streamrootKey#>)
.start(manifestUrl)
} catch let error {
print("\(error)")
}
Parameter name | Mandatory | Description |
---|---|---|
manifestUrl | Yes | The HLS .m3u8 master playlist URL needsed to be passed to the dnaClient |
latency | Yes | The delay between the playback time and the live edge. To get the best offload capacity, we recommend setting 30s. |
streamrootKey | Yes | The unique Streamroot key that we have assigned to you; make sure it is identical to the one provided in the Account section of your dashboard. |
Other DNA options can be called in the builder. Please refer to this article to learn more about them.
5. Play the stream
playbackService.findVideo(withVideoID: ConfigConstants.VideoID, parameters: nil) { [weak self] (video: BCOVVideo?, json: [AnyHashable:Any]?, error: Error?) in
guard let self = self else { return }
if let error = error {
print("ViewController Debug - Error retrieving video playlist: \(error.localizedDescription)")
return
}
guard let video = video else {
return
}
// Look for an HLS stream to setup streamroot DNA client
let hlsSource = video.sources
.compactMap { $0 as? BCOVSource }
.filter { $0.deliveryMethod == "application/x-mpegURL"}
.first
guard hlsSource != nil else {
self.playbackController?.setVideos([video] as NSFastEnumeration)
return
}
self.setupDnaWithSource(hlsSource!)
guard let localManifest = self.dnaClient?.manifestLocalURLPath else {
self.playbackController?.setVideos([video] as NSFastEnumeration)
return
}
let dnaVideo = BCOVVideo(hlsSourceURL: URL(string: localManifest)!)
self.playbackController?.setVideos([dnaVideo] as NSFastEnumeration)
}
localManifest
refers to the HLS .m3u8
master playlist URL that will be passed to the player.
In order to improve the peer traffic offload for Live streaming, we highly recommend setting different buffer levels for each player.
Here, at player instantiation, we will distribute the buffer target between 10 and the latency set above (30s recommended).
The maximum buffer target will then dynamically evolve for greater Mesh Delivery efficiency (Only from SDK 3.9.0).
6. Implement DNAClientDelegate
It is first needed to add the DNAClientDelegate
in an extension.
extension ViewController: DNAClientDelegate {
func playbackTime() -> Double {
if let player = self.currentPlayer {
return CMTimeGetSeconds(player.currentTime())
}
return 0
}
func loadedTimeRanges() -> [NSValue] {
guard let player = self.currentPlayer else {
return []
}
guard let playerItem = player.currentItem else {
return []
}
return playerItem.loadedTimeRanges.map { (value) -> NSValue in
NSValue(timeRange: TimeRange(range: value.timeRangeValue))
}
}
func updatePeakBitRate(_ bitRate: Double) {
currentPlayer?.currentItem?.preferredPeakBitRate = bitRate
}
func setBufferTarget(_ target: Double) {
self.currentPlayer?.currentItem?.preferredForwardBufferDuration = target
}
func bufferTarget() -> Double {
guard let item = self.currentPlayer?.currentItem else {
return 0
}
return item.preferredForwardBufferDuration
}
}
Method name | Mandatory | Description |
---|---|---|
playbackTime | Yes | Must return the current time of the player in seconds. |
loadedTimeRanges | Yes | Must return an array of NSValue. Each NSValue must contain a TimeRange object (in seconds). |
bitrate | Yes | Must be implemented to update the player with the new bitrate. |
bufferTarget | No | Should be implemented to update the player with dynamic buffer target. |
As our SDK will dynamically update buffer target to optimize peer offload, it is recommended to deactivate Brightcove buffer own optimization to prevent conflicts.
controller?.disableBufferOptimisation()func playbackController(_ controller: BCOVPlaybackController!, didAdvanceTo session: BCOVPlaybackSession!) {
print("ViewController Debug - Advanced to new session.")
currentPlayer = session.player
controller?.disableBufferOptimisation()
}
Stop the SDK
dnaClient?.stop()
Disable App Transport security
In the Project Navigator, right click on "Info.plist", and "Open as" → "Source Code".
Add the following lines with the corresponding parameters values.
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
Integrating with the Brightcove SSAI Plugin
If you are using the SSAI plugin provided by Brightcove, you will need to perform this steps instead.
Specify this in your Podfile
:
use_frameworks!
target ' <Your Target Name >'
pod 'StreamrootSDK', '~> 3.14.0'
pod 'Brightcove-Player-SSAI/dynamic',
end
Use this method to retrieve and play the stream (instead of Step 5)
playbackService.findVideo(withVideoID: Constants.VideoId, parameters: queryParameters) { [weak self] (video: BCOVVideo?, jsonResponse: [AnyHashable: Any]?, error: Error?) -> Void in
guard let self = self else { return }
if let error = error {
print("ViewController Debug - Error retrieving video playlist: \(error.localizedDescription)")
return
}
guard let video = video else {
return
}
// Look for an HLS stream to setup streamroot DNA client
let hlsSource = video.sources
.compactMap { $0 as? BCOVSource }
.filter { $0.deliveryMethod == "application/x-mpegURL.boltSSAI"}
.first
guard hlsSource != nil else {
self.playbackController?.setVideos([video] as NSFastEnumeration)
return
}
self.setupDnaWithSource(hlsSource!)
guard let localManifest = self.dnaClient?.manifestLocalURLPath else {
self.playbackController?.setVideos([video] as NSFastEnumeration)
return
}
let dnaVideo = BCOVVideo(url: URL(string: localManifest)!,
deliveryMethod: "application/x-mpegURL.boltSSAI")
self.playbackController?.setVideos([dnaVideo] as NSFastEnumeration)
}
A full sample application can be found here.