首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何通过单独提供密钥文件来播放m3u8加密播放列表?

如何通过单独提供密钥文件来播放m3u8加密播放列表?
EN

Stack Overflow用户
提问于 2012-12-21 15:32:53
回答 2查看 10.4K关注 0票数 4

我有一个m3u8播放列表文件(让我们称它为prime),它指向另一个播放列表文件,该文件又包含带有密钥文件URL的turn。我现在可以使用MPMoviePlayer播放prime m3u8文件。数据段使用AES-128位加密进行encrypted,密钥文件位于最终的m3u8文件中。有没有办法提供最终的m3u8文件,并告诉应用程序使用本地密钥文件来解密视频,这样我就不必公开发布密钥文件。

这与this SO question有些关系

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2013-01-02 16:46:11

可以--您可以在将最终的m3u8文件传递给播放器之前对其进行修改。例如,将关键行更改为引用http://localhost/key。然后,您可能希望运行本地http服务器,如cocoahttpserver,以将密钥传递给视频播放器。

票数 2
EN

Stack Overflow用户

发布于 2017-04-17 15:03:07

我已经实现了类似的东西。我们所做的是:

JWT令牌在运行时加密实时流片段的每个片段,该令牌具有密钥值对和时间戳的组合,因为validation.

  • Our

  • 知道如何解密此密钥。当解密的数据有效时,服务器用.ts文件进行响应,因此回放变得安全。

下面是完整的工作代码,其中有提到的步骤:

代码语言:javascript
复制
//Step 1,2:- Initialise player, change the scheme from http to fakehttp and set delete of resource loader. These both steps will trigger the resource loader delegate function so that we can manually handle the loading of segments. 

func setupPlayer(stream: String) {

operationQ.cancelAllOperations()
let blckOperation = BlockOperation {


    let currentTStamp = Int(Date().timeIntervalSince1970 + 86400)//
    let timeStamp = String(currentTStamp)
    self.token = JWT.encode(["Expiry": timeStamp],
                            algorithm: .hs256("qwerty".data(using: .utf8)!))

    self.asset = AVURLAsset(url: URL(string: "fake\(stream)")!, options: nil)
    let loader = self.asset?.resourceLoader
    loader?.setDelegate(self, queue: DispatchQueue.main)
    self.asset!.loadValuesAsynchronously(forKeys: ["playable"], completionHandler: {


        var error: NSError? = nil
        let keyStatus = self.asset!.statusOfValue(forKey: "playable", error: &error)
        if keyStatus == AVKeyValueStatus.failed {
            print("asset status failed reason \(error)")
            return
        }
        if !self.asset!.isPlayable {
            //FIXME: Handle if asset is not playable
            return
        }

        self.playerItem = AVPlayerItem(asset: self.asset!)
        self.player = AVPlayer(playerItem: self.playerItem!)
        self.playerView.playerLayer.player = self.player
        self.playerLayer?.backgroundColor = UIColor.black.cgColor
        self.playerLayer?.videoGravity = AVLayerVideoGravityResizeAspect

        NotificationCenter.default.addObserver(self, selector: #selector(self.playerItemDidReachEnd(notification:)), name: Notification.Name.AVPlayerItemDidPlayToEndTime, object: self.playerItem!)
        self.addObserver(self, forKeyPath: "player.currentItem.duration", options: [.new, .initial], context: &playerViewControllerKVOContext)
        self.addObserver(self, forKeyPath: "player.rate", options: [.new, .old], context: &playerViewControllerKVOContext)
        self.addObserver(self, forKeyPath: "player.currentItem.status", options: [.new, .initial], context: &playerViewControllerKVOContext)
        self.addObserver(self, forKeyPath: "player.currentItem.loadedTimeRanges", options: [.new], context: &playerViewControllerKVOContext)
        self.addObserver(self, forKeyPath: "player.currentItem.playbackLikelyToKeepUp", options: [.new], context: &playerViewControllerKVOContext)
        self.addObserver(self, forKeyPath: "player.currentItem.playbackBufferEmpty", options: [.new], context: &playerViewControllerKVOContext)
    })
}


operationQ.addOperation(blckOperation)
}

//Step 2, 3:- implement resource loader delegate functions and replace the fakehttp with http so that we can pass this m3u8 stream to the parser to get the current m3u8 in string format.

func resourceLoader(_ resourceLoader: AVAssetResourceLoader, shouldWaitForLoadingOfRequestedResource loadingRequest: AVAssetResourceLoadingRequest) -> Bool {

var url = loadingRequest.request.url?.absoluteString

let contentRequest = loadingRequest.contentInformationRequest
let dataRequest = loadingRequest.dataRequest
//Check if the it is a content request or data request, we have to check for data request and do the m3u8 file manipulation

if (contentRequest != nil) {

    contentRequest?.isByteRangeAccessSupported = true
}
if (dataRequest != nil) {

    //this is data request so processing the url. change the scheme to http

    url = url?.replacingOccurrences(of: "fakehttp", with: "http")

    if (url?.contains(".m3u8"))!
    {

        // do the parsing on background thread to avoid lags
// step 4: 
        self.parsingHandler(url: url!, loadingRequest: loadingRequest, completion: { (success) in

            return true
        })
    }
    else if (url?.contains(".ts"))! {

        let redirect = self.generateRedirectURL(sourceURL: url!)

        if (redirect != nil) {
            //Step 9 and 10:-
            loadingRequest.redirect = redirect!
            let response = HTTPURLResponse(url: URL(string: url!)!, statusCode: 302, httpVersion: nil, headerFields: nil)
            loadingRequest.response = response
            loadingRequest.finishLoading()
        }
        return true
    }
    return true
}
return true
}

func parsingHandler(url: String, loadingRequest: AVAssetResourceLoadingRequest, completion:((Bool)->Void)?) -> Void {

DispatchQueue.global(qos: .background).async {

    var string = ""

    var originalURIStrings = [String]()
    var updatedURIStrings = [String]()

    do {

        let model = try M3U8PlaylistModel(url: url)
        if model.masterPlaylist == nil {
            //Step 5:- 
            string = model.mainMediaPl.originalText
            let array = string.components(separatedBy: CharacterSet.newlines)
            if array.count > 0 {

                for line in array {
                    //Step 6:- 
                    if line.contains("EXT-X-KEY:") {

                        //at this point we have the ext-x-key tag line. now tokenize it with , and then
                        let furtherComponents = line.components(separatedBy: ",")

                        for component in furtherComponents {

                            if component.contains("URI") {
                                // Step 7:- 
                                //save orignal URI string to replaced later
                                originalURIStrings.append(component)

                                //now we have the URI
                                //get the string in double quotes

                                var finalString = component.replacingOccurrences(of: "URI=\"", with: "").replacingOccurrences(of: "\"", with: "")

                                finalString = "\"" + finalString + "&token=" + self.token! + "\""
                                finalString = "URI=" + finalString
                                updatedURIStrings.append(finalString)
                            }
                        }
                    }

                }
            }

            if originalURIStrings.count == updatedURIStrings.count {
                //Step 8:- 
                for uriElement in originalURIStrings {

                    string = string.replacingOccurrences(of: uriElement, with: updatedURIStrings[originalURIStrings.index(of: uriElement)!])
                }

                //print("String After replacing URIs \n")
                //print(string)
            }
        }

        else {

            string = model.masterPlaylist.originalText
        }
    }
    catch let error {

        print("Exception encountered")
    }

    loadingRequest.dataRequest?.respond(with: string.data(using: String.Encoding.utf8)!)
    loadingRequest.finishLoading()

    if completion != nil {
        completion!(true)
    }
}
}

func generateRedirectURL(sourceURL: String)-> URLRequest? {

    let redirect = URLRequest(url: URL(string: sourceURL)!)
    return redirect
}

  1. 实现用于自定义流处理的资产资源加载器委托。
  2. 伪造实况流的方案,以便资源加载器委托被调用(对于普通的http/https,它不会被调用,播放器尝试使用Http方案处理伪方案的流。
  3. 将流传递到M3U8解析器,以获取纯文本格式的m3u8文件,并使用纯字符串从EXT-X-KEY行中查找EXT-X-KEY标记得到单独制作的"URI“方法string.
  4. Append JWT令牌,使用m3u8.
  5. Replace中的当前URI方法,当前m3u8字符串中的所有URI实例都附加了新的标记URI string.
  6. Convert此字符串到NSData格式
  7. 再次将其馈送到播放器。

希望这能有所帮助!

票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/13986047

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档