Swift 中的 Fairplay 实现,AVAssetResourceDelegate

Posted

技术标签:

【中文标题】Swift 中的 Fairplay 实现,AVAssetResourceDelegate【英文标题】:Fairplay implementation in Swift, AVAssetResourceDelegate 【发布时间】:2018-01-19 12:11:43 【问题描述】:

我观看了 Apple FairPlay 介绍视频,我阅读了以下代码: https://gist.github.com/fousa/5709fb7c84e5b53dbdae508c9cb4fadc 而且我还浏览了苹果的 HLS 目录,最后一个问题是我只需要播放 DRM 视频而无需任何下载和所有这些东西,所以我从 GitHub 示例开始。 我有证书、FairPlay 中的视频和密钥服务器模块。 我的第一个也是主要的问题是当我给 AVURLAsset 提供视频 url 时 AVResourceDelegate 没有调用。我在堆栈中读到我需要将方案更改为其他内容,例如来自 https 的“DRM”和正确的 AVResourceDelegate 调用,但我没有 .m3u8 文件,因为视频链接错误! 你能不能请男孩/女孩帮帮我。

import Foundation
import AVKit
import NotificationCenter

public struct DRMVideoData
   var drmKey: String?
   var proxyFairPlay: String
   var fileFairPlay: String
   var idVideo: String


class VODDRMImplementation: NSObject, AVAssetResourceLoaderDelegate 

let domain = "DRMDelegate.ContentKeyQueue"
let contentKeyDelegateQueue = DispatchQueue(label: "DRMDelegate.ContentKeyQueue")

var drmData: DRMVideoData?

func startPlayerWithDRM(_ videoDRM: DRMVideoData,_ player: AVPlayer?,_ playerLayer: AVPlayerLayer?, c: @escaping (AVPlayer?, AVPlayerLayer?) -> Void) 

    var urlcomp = URLComponents(string: videoDRM.fileFairPlay)

    urlcomp?.scheme = "drm"

    if let url = try? urlcomp?.asURL()

        self.drmData = videoDRM

        let url = url

        let asset = AVURLAsset(url: url!)
        asset.resourceLoader.setDelegate(self, queue: self.contentKeyDelegateQueue)

        let playerItem = AVPlayerItem(asset: asset)
        let player = AVPlayer(playerItem: playerItem)
        let playerLayer = AVPlayerLayer(player: player)
        player.pause()

        c(player, playerLayer)
    else
        c(nil, nil)
    

    

func resourceLoader(_ resourceLoader: AVAssetResourceLoader, shouldWaitForLoadingOfRequestedResource loadingRequest: AVAssetResourceLoadingRequest) -> Bool 
    log.debug("DRM: started")

    // getting data for KSM server
    guard let urlToConvert = loadingRequest.request.url,
        let drmData = drmData else 
            log.debug("DRM: unable to read URL from loadingRequest")
            loadingRequest.finishLoading(with: NSError(domain: domain, code: -1, userInfo: nil))
            return false
    

    do

        log.debug("DRM: video link \(urlToVideo)")

        guard let certificateData = getCertificateFromServer() else 
                log.debug("DRM: false to get public certificate")
                loadingRequest.finishLoading(with: NSError(domain: domain, code: -3, userInfo: nil))
                return false
        

        let contentId = drmData.idVideo // content id
        guard let contentIdData = contentId.data(using: String.Encoding.utf8),
            let spcData = try? loadingRequest.streamingContentKeyRequestData(forApp: certificateData, contentIdentifier: contentIdData, options: nil),
            let dataRequest = loadingRequest.dataRequest else 
                loadingRequest.finishLoading(with: NSError(domain: domain, code: -3, userInfo: nil))
                log.debug("DRM: false to get SPC Data from video")
                return false
        

        let ksmServer = URL(string: drmData.proxyFairPlay)! // KSM link
        var request = URLRequest(url: ksmServer)
        request.httpMethod = "GET"
        request.httpBody = spcData
        let session = URLSession(configuration: .default)
        let task = session.dataTask(with: request)  data, response, error in
            guard let data = data else 
                log.debug("DRM: unable to fetch ckc key :/")
                loadingRequest.finishLoading(with: NSError(domain: self.domain, code: -4, userInfo: nil))
                return
            
            dataRequest.respond(with: data)
            loadingRequest.finishLoading()
        
        task.resume()

    catch
        loadingRequest.finishLoading(with: NSError(domain: domain, code: -3, userInfo: nil))
        log.debug("DRM: cannot generate url to video")
        return false
    


    return true




func takeURLFromId(_ videoLink: String) -> URL
    let urlString = videoLink
    let url = URLComponents(string: urlString)
    do
        let urlToReturn = try url?.asURL()
        guard let urlToReturn2 = urlToReturn else 
            let error = NSError(domain: domain, code: 0, userInfo: nil)
            throw error 
        return urlToReturn2
    catch
        if let url = NSURL(string: videoLink)
            return url as URL
        else
            return NSURL(string: videoLink)! as URL
        
    



func getCertificateFromServer() -> Data?
    let filePath = Bundle.main.path(forResource: "privatekey", ofType: "pem")

    guard let data = try? Data(contentsOf: URL(string: filePath!)!) else 
        return nil
    

    return data



【问题讨论】:

您好,您得到解决方案了吗,如果是,请分享答案 @pkesaj 你得到答案了吗? @RaviOjha 你解决了这个问题吗? @AmritTiwari 对不起。我没有得到解决方案 你是在设备还是模拟器上测试?不幸的是,您只会在设备上运行时获得 shouldWaitForLoadingOfRequestedResource 回调。此外,如果您正在寻找好的测试流,您可以在苹果开发者网站的“FairPlay Streaming Test Streams”下找到有效的 .m3u8 文件:developer.apple.com/streaming/fps 【参考方案1】:

你可以试试换行:

player.pause()player.play()

另外,我认为在您的班级中保留对播放器的引用应该会有所帮助,如下所示:

var player: AVPlayer?

【讨论】:

以上是关于Swift 中的 Fairplay 实现,AVAssetResourceDelegate的主要内容,如果未能解决你的问题,请参考以下文章

macOS 应用中的 Apple Fairplay 播放

Apple FairPlay DRM及其工作原理

Azure 媒体服务 Fairplay V3 DRM 内容密钥策略

iOS8 Today View Extension 小部件在应用商店中出现“FairPlay 解密失败”错误

Swift 中的 ObjC 协议实现

Swift:如何避免 CoreData 中的重复条目以使用 JSON 数组实现?