Skip to content

Commit

Permalink
feature: support DRM playback (#52)
Browse files Browse the repository at this point in the history
* Expose DRM APIs (#36)

* Support content key session operations

Co-authored-by: Emily Dixon <edixon@mux.com>
Co-authored-by: AJ Lauer Barinov <abarinov@mux.com>
  • Loading branch information
andrewjl-mux and daytime-em authored Jul 11, 2024
1 parent ffc6b34 commit 83bf223
Show file tree
Hide file tree
Showing 24 changed files with 2,420 additions and 201 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
19DD16AF2BEC010400F4DF4F /* SinglePlayerExampleController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19DD16AE2BEC010400F4DF4F /* SinglePlayerExampleController.swift */; };
19DD16B12BEC028C00F4DF4F /* SmartCacheExampleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19DD16B02BEC028C00F4DF4F /* SmartCacheExampleViewController.swift */; };
19DD16B32BEC048300F4DF4F /* SinglePlayerLayerExampleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19DD16B22BEC048300F4DF4F /* SinglePlayerLayerExampleViewController.swift */; };
19F6A1CE2C2F23EC00EE408A /* DRMExampleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19F6A1CD2C2F23EC00EE408A /* DRMExampleViewController.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -56,6 +57,7 @@
19DD16AE2BEC010400F4DF4F /* SinglePlayerExampleController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SinglePlayerExampleController.swift; sourceTree = "<group>"; };
19DD16B02BEC028C00F4DF4F /* SmartCacheExampleViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SmartCacheExampleViewController.swift; sourceTree = "<group>"; };
19DD16B22BEC048300F4DF4F /* SinglePlayerLayerExampleViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SinglePlayerLayerExampleViewController.swift; sourceTree = "<group>"; };
19F6A1CD2C2F23EC00EE408A /* DRMExampleViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DRMExampleViewController.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -112,6 +114,7 @@
19DD16AE2BEC010400F4DF4F /* SinglePlayerExampleController.swift */,
19DD16B02BEC028C00F4DF4F /* SmartCacheExampleViewController.swift */,
19DD16B22BEC048300F4DF4F /* SinglePlayerLayerExampleViewController.swift */,
19F6A1CD2C2F23EC00EE408A /* DRMExampleViewController.swift */,
193228C02ACF6AC700966FE1 /* Main.storyboard */,
193228C32ACF6AC800966FE1 /* Assets.xcassets */,
193228C52ACF6AC800966FE1 /* LaunchScreen.storyboard */,
Expand Down Expand Up @@ -275,6 +278,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
19F6A1CE2C2F23EC00EE408A /* DRMExampleViewController.swift in Sources */,
19DD16B32BEC048300F4DF4F /* SinglePlayerLayerExampleViewController.swift in Sources */,
193228BB2ACF6AC700966FE1 /* AppDelegate.swift in Sources */,
19DD16B12BEC028C00F4DF4F /* SmartCacheExampleViewController.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,19 @@
// MuxPlayerSwiftExample
//

import AVFoundation
import UIKit

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
let audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.setCategory(AVAudioSession.Category.playback)
} catch {
print("Setting category to AVAudioSessionCategoryPlayback failed.")
}

return true
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,33 @@
<segue destination="JrU-9c-zyG" kind="show" id="q3v-VV-Yv9"/>
</connections>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="OjC-bp-Jb4" detailTextLabel="bKF-tz-9d2" rowHeight="88" style="IBUITableViewCellStyleSubtitle" id="odP-sm-cIe">
<rect key="frame" x="0.0" y="314" width="393" height="88"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="odP-sm-cIe" id="dOo-zJ-LIx">
<rect key="frame" x="0.0" y="0.0" width="393" height="88"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="DRM Example" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="OjC-bp-Jb4" userLabel="DRM Example">
<rect key="frame" x="20.000000000000007" y="21.333333333333329" width="122.66666666666667" height="24"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" type="system" pointSize="20"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Using AVPlayerViewController" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="bKF-tz-9d2" userLabel="Using AVPlayerViewController">
<rect key="frame" x="20" y="49" width="169.33333333333334" height="14.333333333333334"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" type="system" pointSize="0.0"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</tableViewCellContentView>
<connections>
<segue destination="buK-6n-LKx" kind="show" id="eWu-XH-VJ4"/>
</connections>
</tableViewCell>
</cells>
</tableViewSection>
</sections>
Expand Down Expand Up @@ -163,6 +190,22 @@
</objects>
<point key="canvasLocation" x="-161" y="271"/>
</scene>
<!--Example View Controller-->
<scene sceneID="U4Y-IB-cxt">
<objects>
<viewController id="buK-6n-LKx" customClass="DRMExampleViewController" customModule="MuxPlayerSwiftExample" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="FcW-4i-Ijl">
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<viewLayoutGuide key="safeArea" id="kho-JC-OWZ"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
</view>
<navigationItem key="navigationItem" id="ZYa-D0-7Yh"/>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="vny-A5-ZSO" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="434" y="573"/>
</scene>
<!--Navigation Controller-->
<scene sceneID="2gk-Ck-Rao">
<objects>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
//
// DRMExampleViewController.swift
// MuxPlayerSwiftExample
//

import AVKit
import UIKit

import MuxPlayerSwift

class DRMExampleViewController: UIViewController {

// MARK: Player View Controller

lazy var playerViewController = AVPlayerViewController(
playbackID: playbackID,
playbackOptions: PlaybackOptions(
playbackToken: playbackToken,
drmToken: drmToken,
customDomain: customDomain
)
)

// MARK: Mux Data Monitoring Parameters

var playerName: String = "MuxPlayerSwift-DRMExample"

var environmentKey: String? {
ProcessInfo.processInfo.environmentKey
}

// MARK: Mux Video Playback Parameters

var playbackID: String {
ProcessInfo.processInfo.playbackID ?? "qxb01i6T202018GFS02vp9RIe01icTcDCjVzQpmaB00CUisJ4"
}

// TODO: Display error alert if ProcessInfo returns nil
var playbackToken: String {
ProcessInfo.processInfo.playbackToken ?? ""
}

// TODO: Display error alert if ProcessInfo returns nil
var drmToken: String {
ProcessInfo.processInfo.drmToken ?? ""
}

// TODO: Display error alert if ProcessInfo returns nil
var customDomain: String? {
ProcessInfo.processInfo.customDomain ?? nil
}

// MARK: Status Bar Appearance

override var childForStatusBarStyle: UIViewController? {
playerViewController
}

// MARK: View Controller Lifecycle

override func viewDidLoad() {
super.viewDidLoad()

view.backgroundColor = .black

playerViewController.delegate = self
playerViewController.allowsPictureInPicturePlayback = true
playerViewController.canStartPictureInPictureAutomaticallyFromInline = true

displayPlayerViewController()
}

// MARK: Player Lifecycle

func displayPlayerViewController() {
playerViewController.willMove(toParent: self)
addChild(playerViewController)
view.addSubview(playerViewController.view)
playerViewController.didMove(toParent: self)
playerViewController
.view
.translatesAutoresizingMaskIntoConstraints = false
view.addConstraints([
playerViewController.view.leadingAnchor.constraint(
equalTo: view.leadingAnchor
),
playerViewController.view.trailingAnchor.constraint(
equalTo: view.trailingAnchor
),
playerViewController.view.layoutMarginsGuide.topAnchor.constraint(
equalTo: view.topAnchor
),
playerViewController.view.layoutMarginsGuide.bottomAnchor
.constraint(equalTo: view.bottomAnchor),
])
}

func hidePlayerViewController() {
playerViewController.willMove(toParent: nil)
playerViewController.view.removeFromSuperview()
playerViewController.removeFromParent()
}

}

extension DRMExampleViewController: AVPlayerViewControllerDelegate{
func playerViewController(
_ playerViewController: AVPlayerViewController,
restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: @escaping (Bool) -> Void
) {
completionHandler(true)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,9 @@
</array>
</dict>
</dict>
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
</array>
</dict>
</plist>
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@

import Foundation

// Configure these as environment variables in
// application scheme
extension ProcessInfo {

// MARK: Mux Data Environment Key
var environmentKey: String? {
guard let value = environment["ENV_KEY"],
!value.isEmpty else {
Expand All @@ -15,6 +19,8 @@ extension ProcessInfo {
return value
}

// MARK: Mux Video Playback Constants

var playbackID: String? {
guard let value = environment["PLAYBACK_ID"],
!value.isEmpty else {
Expand All @@ -23,4 +29,71 @@ extension ProcessInfo {

return value
}

var playbackToken: String? {
guard let value = environment["PLAYBACK_TOKEN"],
!value.isEmpty else {
return nil
}

return value
}

var drmToken: String? {
guard let value = environment["DRM_TOKEN"],
!value.isEmpty else {
return nil
}

return value
}

var secondaryPlaybackID: String? {
guard let value = environment["SECONDARY_PLAYBACK_ID"],
!value.isEmpty else {
return nil
}

return value
}

var secondaryPlaybackToken: String? {
guard let value = environment["SECONDARY_PLAYBACK_TOKEN"],
!value.isEmpty else {
return nil
}

return value
}

var secondaryDRMToken: String? {
guard let value = environment["SECONDARY_DRM_TOKEN"],
!value.isEmpty else {
return nil
}

return value
}

// MARK: Mux Video Custom Playback Domain


/// Once Mux has provisioned your Custom Domain, set the
/// `CUSTOM_DOMAIN` environment variable and Mux Player
/// Swift will pass that on to `AVPlayer` to use when
/// streaming video.
///
/// Mux Player Swift automatically prepends the `stream`
/// subdomain for you. This means that if you set
/// `media.example.com` as `CUSTOM_DOMAIN` then `AVPlayer`
/// will use `stream.media.example.com` when requesting
/// media. [See here for more details](https://www.mux.com/blog/introducing-custom-domains).
var customDomain: String? {
guard let value = environment["CUSTOM_DOMAIN"],
!value.isEmpty else {
return nil
}

return value
}
}
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ A collection of helpful utilities for using AVKit and AVFoundation to stream vid

We'd love to hear your feedback, shoot us a note at avplayer@mux.com with any feature requests, API feedback, or to tell us about what you'd like to build.

#### Mux Video DRM Beta

This SDK supports Mux Video's DRM feature, which is currently in closed beta. If you are interested in using our DRM features, please sign up on our [beta page](https://www.mux.com/beta/drm)

## Installation

### Installing in Xcode using Swift Package Manager
Expand Down
Loading

0 comments on commit 83bf223

Please sign in to comment.