This article details how to integrate CDN Mesh Delivery into your mobile app using Apple's AVPlayer on iOS.
To make the integration easier, we recommend using our dedicated DNA Plugin for AVPlayer.
Not into tutorials? |
Since DNA 2.15, due to the update of WebRTC, we no longer support iOS under version 10.
If you still require it, we recommend to stick to our SDK 3.12.x
Project setup
CocoaPods
Cocoapods 1.6.0 + is recommended
To integrate StreamrootSDK into your Xcode project using CocoaPods, specify this in your Podfile
:
use_frameworks!
target ' <Your Target Name >'
pod 'StreamrootSDK', '~> 3.23.0'
end
Then, run the following command:
$ pod update
Finally, open the generated Workspace.
Carthage
Add the dependencies to the Cartfile:
binary "https://sdk.streamroot.io/ios/StreamrootSDK.json" ~> 3.23.0
github "daltoniam/Starscream" ~> 3.1.1
github "getsentry/sentry-cocoa" ~> 4.4.0
github "apple/swift-protobuf" ~> 1.7.0
Link the frameworks to your project:
Manually
1. Install dependencies
Current dependencies versions can be found here.
2. Downloads Streamroot SDK
StreamrootSDK.framework: https://sdk.streamroot.io/ios/StreamrootSDK/3.22.0/StreamrootSDK.framework.zip
Copy script: http://sdk.streamroot.io/ios/copy_streamroot_sdk
3. Add StreamrootSDK.framework to your project
4. Link Binary With Libraries
Drag and drop StreamrootSDK.framework
into your Xcode project. Make sure StreamrootSDK.framework
has been added to your target's build phases under Link Binary With Libraries
.
5. Copy the framework
There is no need for the script if using ARM version.
Create a new build phase (New Run Script Phase
) to call the copy_streamroot_sdk
script.
Add the folder containing the StreamrootSDK.framework
in the Input Files
field. It defaults to $(SRCROOT)/
if none input file is set.
Notes: Make sure to have all the dependencies installed in your project (ref above);
For Objective-C projects, set Always Embed Swift Standard Libraries
to yes
in your target's build settings.
Integrate Streamroot SDK
1. Import the SDK
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
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)")
}
NSError *error;
self.dnaClient = [[[[DNAClient.builder dnaClientDelegate: self] latency: 30] streamrootKey:@"your-streamroot"] start:manifestUrl error: &error];
if (error || !self.dnaClient) {
NSLog(@"error: %@", error);
}
Parameter name | Mandatory | Description |
---|---|---|
manifestUrl | Yes | The HLS .m3u8 master playlist URL needed 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 Mesh Delivery options can be called in the builder. Please refer to this article to learn more about them.
5. Play the stream
guard let localPath = self.dnaClient?.manifestLocalURLPath,
let localUrl = URL(string: localPath) else {
print("Could not generate localPath, please check your network")
return
}
let playerItem = AVPlayerItem(asset: AVURLAsset(url: localUrl))
if let bufferTarget = dnaClient?.bufferTarget {
if #available(iOS 10.0, tvOS 10.2, *) {
playerItem.preferredForwardBufferDuration = bufferTarget
}
}
player = AVPlayer(playerItem: playerItem)
player?.play()
NSURL *url = [[NSURL alloc] initWithString: self.dnaClient.manifestLocalURLPath];
AVPlayerItem *playerItem =[[AVPlayerItem alloc] initWithURL: url];
if (@available(iOS 10.0, tvOS 10.2, *)) {
playerItem.preferredForwardBufferDuration = self.dnaClient.bufferTarget;
}
self.player = [[AVPlayer alloc] initWithPlayerItem: playerItem];
[self.player play];
[self.view setNeedsLayout];
[self.view layoutIfNeeded];
[self.dnaClient displayStatsOnView: self.contentOverlayView];
localUrl
refers to the HLS .m3u8
master playlist URL that will be passed to the player.
In order to improve the DNA 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 DNA delivery efficiency (Only from SDK 3.9.0).
6. Implement DNAClientDelegate
It is first needed to add the DNAClientDelegate
in an extension.
extension CurrentPlayerViewController: DNAClientDelegate {
func playbackTime() -> Double {
if let player = self.player {
return CMTimeGetSeconds(player.currentTime())
}
return 0
}
func loadedTimeRanges() -> [NSValue] {
guard let player = self.player else {
return []
}
let timeRanges = player.currentItem!.loadedTimeRanges
return timeRanges.map { (value) -> NSValue in
NSValue(timeRange: TimeRange(range: value.timeRangeValue))
}
}
func updatePeakBitRate(_ bitRate: Double) {
self.player?.currentItem?.preferredPeakBitRate = bitRate
}
public func bufferTarget() -> Double {
if #available(iOS 10.0, tvOS 10.0, *) {
return self.player?.currentItem?.preferredForwardBufferDuration ?? 0
}
return 0.0
}
public func setBufferTarget(_ target: Double) {
if #available(iOS 10.0, tvOS 10.0, *) {
self.player?.currentItem?.preferredForwardBufferDuration = target
}
}
}
- (double)playbackTime {
return CMTimeGetSeconds([self.player currentTime]);
}
- (NSArray<NSValue *> *)loadedTimeRanges {
if (self.player == nil) {
return [NSArray array];
}
NSMutableArray *timeRanges = [NSMutableArray array];
for (NSValue *value in [[self.player currentItem] loadedTimeRanges]) {
TimeRange *timeRange = [[TimeRange alloc] initWithRange:[value CMTimeRangeValue]];
[timeRanges addObject:[[NSValue alloc] initWithTimeRange:timeRange]];
}
return timeRanges;
}
- (void)updatePeakBitRate:(double)bitRate {
self.player.currentItem.preferredPeakBitRate = bitRate;
}
- (double) bufferTarget {
return self.player.currentItem.preferredForwardBufferDuration;
}
- (void)setBufferTarget:(double)target {
self.player.currentItem.preferredForwardBufferDuration = target;
}
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. |
7. Integrate the QoS module (optional)
This QoS module will allow you to link AVPlayer events to our SDK in order to get accurate metrics regarding the client session.
If this QoS module isn't implemented, a default module directly embedded in the SDK will be used instead.
let qosModule: QosModule = DNAQos.module(type: .plugin)
// call the method given the corresponding event
@objc public protocol QosModule {
@objc func playerStateDidChange(_ state: PlaybackState)
@objc func trackSwitchOccurred()
@objc func updateDroppedFrameCount(_ count: Int)
@objc func playbackErrorOccurred()
}
@objc public enum PlaybackState: Int {
case idle
case playing
case paused
case buffering
case seeking
case ended
}
It is then required to call the QosModule in the builder.
do {
dnaClient = try DNAClient.builder().dnaClientDelegate(self)
.latency(30)
.streamrootKey(<#streamrootKey#>)
.qosModule(qosModule)
.start(manifestUrl)
} catch let error {
print("\(error)")
}
Stop the SDK
override func viewDidDisappear(_ animated: Bool) {
dnaClient?.stop()
}
[self.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>