(以下內容均翻譯自英文版文件,最新資訊及內容敬請切換至英文語系參考原文)
本指南將指導您完成使用 iOS 播放器 SDK 啟動 BlendVision One 串流播放的步驟。
此外,您還可以查看範例專案,以便更順利地開始工作:
https://github.com/BlendVision/iOS-Player-SDK/tree/main/Samples
開始之前
在實施步驟之前,請確保您已準備好 BlendVision One 的串流內容。您可以通過兩種方式準備您的內容:
步驟 1:取得播放令牌
- 按照 驗證步驟 取得有效的 API 令牌
- 取得預期要播放的串流內容的
resource_id
和resource_type
。
有兩種方法可以取得此資訊:
- 利用 API 檢索
- VOD: /bv/cms/v1/vods
- 直播: /bv/cms/v1/lives
- 從 BlendVision One Console 裡複製
- 利用 API 檢索
- 使用 API 取得
playback token
: /bv/cms/v1/tokens ,並將resource_id
和resource_type
設為主體參數- 您也可以在主體參數中加入
customer_id
,以使用自定義的使用者 ID 來追蹤使用者的播放行為
- 您也可以在主體參數中加入
let API_DOMAIN = "https://api.one.blendvision.com" struct PlaybackTokenResponse: Codable { let playbackToken: String } protocol ApiService { func getPlaybackToken(resourceId: String, resourceType: String, customerId: String?, completion: @escaping (Result<PlaybackTokenResponse, Error>) -> Void) } extension ApiService { func getPlaybackToken(resourceId: String, resourceType: String, customerId: String? = nil, completion: @escaping (Result<PlaybackTokenResponse, Error>) -> Void) { guard let url = URL(string: API_DOMAIN + "/bv/cms/v1/tokens") else { // Handle invalid URL error return } var parameters: [String: Any] if let customerId = customerId { parameters = [ "resource_id": resourceId, "resource_type": resourceType, "customer_id": customerId ] } else { parameters = [ "resource_id": resourceId, "resource_type": resourceType ] } var request = URLRequest(url: url) request.httpMethod = "POST" request.httpBody = try? JSONSerialization.data(withJSONObject: parameters, options: []) request.addValue("application/json", forHTTPHeaderField: "Content-Type") URLSession.shared.dataTask(with: request) { data, response, error in if let error = error { // Handle network error completion(.failure(error)) return } guard let data = data else { // Handle empty response data return } do { let response = try JSONDecoder().decode(PlaybackTokenResponse.self, from: data) completion(.success(response)) } catch { // Handle decoding error completion(.failure(error)) } }.resume() } }
步驟 2:開始播放工作階段
- 向 bv/playback/v1/sessions/<device-id>:start 發出 POST 請求,以開始播放工作階段
- 回應: drm_server_endpoint
- 從 bv/playback/v1/sessions/<device-id> 取得串流數據
- 回應: protocol, manifest url, resolutions, thumbnail_seeking_url
let API_DOMAIN = "https://api.one.blendvision.com" struct StartSessionResponse: Codable { let endPoint: String } struct GetStreamInfoResponse: Codable { let sources: [Source] } struct Source: Codable { let manifests: [Manifest] let thumbnailSeekingUrl: String } struct Manifest: Codable { let protocal: String let url: String let resolutions: [Resolution] } struct Resolution: Codable { let height: String let width: String } protocol ApiService { func startPlaybackSession(playbackToken: String, deviceId: String, completion: @escaping (Result<StartSessionResponse, Error>) -> Void) func getStreamInfo(playbackToken: String, deviceId: String, completion: @escaping (Result<GetStreamInfoResponse, Error>) -> Void) } extension ApiService { func startPlaybackSession(playbackToken: String, deviceId: String, completion: @escaping (Result<StartSessionResponse, Error>) -> Void) { guard let url = URL(string: "\(API_DOMAIN)/bv/playback/v1/sessions/\(deviceId):start") else { // Handle invalid URL error return } var request = URLRequest(url: url) request.httpMethod = "POST" request.addValue("application/json", forHTTPHeaderField: "Content-Type") request.addValue(playbackToken, forHTTPHeaderField: "Authorization") URLSession.shared.dataTask(with: request) { data, response, error in if let error = error { // Handle network error completion(.failure(error)) return } guard let data = data else { // Handle empty response data return } do { let response = try JSONDecoder().decode(StartSessionResponse.self, from: data) completion(.success(response)) } catch { // Handle decoding error completion(.failure(error)) } }.resume() } func getStreamInfo(playbackToken: String, deviceId: String, completion: @escaping (Result<GetStreamInfoResponse, Error>) -> Void) { guard let url = URL(string: "\(API_DOMAIN)/bv/playback/v1/sessions/\(deviceId)") else { // Handle invalid URL error return } var request = URLRequest(url: url) request.addValue("application/json", forHTTPHeaderField: "Content-Type") request.addValue(playbackToken, forHTTPHeaderField: "Authorization") URLSession.shared.dataTask(with: request) { data, response, error in if let error = error { // Handle network error completion(.failure(error)) return } guard let data = data else { // Handle empty response data return } do { let response = try JSONDecoder().decode(GetStreamInfoResponse.self, from: data) completion(.success(response)) } catch { // Handle decoding error completion(.failure(error)) } }.resume() } }
步驟 3:初始化播放器
- 從您的 BlendVision One console 取得有效的播放器許可證密鑰
- 為了播放串流內容,請在初始化配置中使用許可證密鑰和串流資訊建立播放器實例:
How to create your player view, please refer to the following url https://developer.apple.com/documentation/avfoundation/avplayerlayer // Create player configuration let playerConfig = UniPlayerConfig() playerConfig.key = "Your player license key" // Create player based on player config let player = UniPlayerFactory.create(player: playerConfig) // Create player view and pass the player instance to it playerView = UniPlayerView(player: player, frame: .zero) // Add player view into subview and layout your player view view.addSubview(playerView) view.bringSubviewToFront(playerView) // Handle HLS stream guard let hlsUrl = URL(string: "Your HLS URL string") else { debugPrint("The HLS URL not found") return } // Create source config let sourceConfig = UniSourceConfig(url: hlsUrl, type: .hls) sourceConfig.title = "your title" sourceConfig.description = "your description" // Load source config player.load(sourceConfig: sourceConfig)
步驟 4:管理播放工作階段的生命週期
- 為了保持播放工作階段的存活,每隔 10 秒定期調用一次心跳 API
func startHeartbeatTimer(deviceId: String, headers: [String: String], player: UniPlayer) { let heartbeatTimer = Timer.scheduledTimer(withTimeInterval: 10, repeats: true) { _ in guard let heartbeatURL = URL(string: "https://api.one.blendvision.com/bv/playback/v1/sessions/\(deviceId):heartbeat") else { // Handle invalid URL error return } var heartbeatRequest = URLRequest(url: heartbeatURL) heartbeatRequest.allHTTPHeaderFields = headers URLSession.shared.dataTask(with: heartbeatRequest) { data, response, error in if let error = error { // Handle network error return } guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else { // Handle player release player.destroy() return } }.resume() } // Start the timer heartbeatTimer.fire() } func endPlayback(deviceId: String, headers: [String: String]) { guard let endPlaybackURL = URL(string: "https://api.one.blendvision.com/bv/playback/v1/sessions/\(deviceId):end") else { // Handle invalid URL error return } var endPlaybackRequest = URLRequest(url: endPlaybackURL) endPlaybackRequest.allHTTPHeaderFields = headers URLSession.shared.dataTask(with: endPlaybackRequest) { data, response, error in if let error = error { // Handle network error return } // Handle successful end of playback }.resume() }