AVPlayer停止在线模式下播放AES加密的离线HLS视频 [英] AVPlayer Stops Playing AES encrypted offline HLS Video in online mode

查看:123
本文介绍了AVPlayer停止在线模式下播放AES加密的离线HLS视频的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我写了一个代码来下载HLS视频并以离线模式播放。
此代码对编码视频有效。现在,我有一个经过AES加密的视频,并且我们为其具有自定义的加密密钥。下载AES加密的HLS视频后,我使用下面给出的代码提供视频解密的密钥。

I have written a code to download HLS video and play it in offline mode. This code works fine for encoded video. Now I have a video which is AES encrypted and we are having custom encryption key for it. After downloading AES encrypted HLS video I am using below given code to supply key for decryption of video.

- (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest {

NSString *scheme = loadingRequest.request.URL.scheme;

if ([scheme isEqualToString:@"ckey"]) {

    NSString *request = loadingRequest.request.URL.host;
    NSData *data = [[NSUserDefaults standardUserDefaults] objectForKey:request];

    if (data) {
        [loadingRequest.dataRequest respondWithData:data];
        [loadingRequest finishLoading];
    } else {
        // Data loading fail
    }
}
return NO; }       

我正在拦截对密钥的请求,并传递存储在UserDefaults中的密钥以进行解密。

I am intercepting a request for a key and passing key stored in UserDefaults for decryption.

当我的设备的wifi或数据连接关闭时,带有自定义密钥的AES加密HLS视频可以很好地播放。

如果在启用设备的wifi或数据连接后开始播放此视频,或者
在播放视频时启用设备的wifi或数据连接;视频立即停止播放,没有任何错误,并且再也不会播放。

我已经检查了playerItem的accessLog和errorLog,但没有发现任何帮助。

I have checked accessLog and errorLog of playerItem but haven't found anything helpful.

要在下载HLS内容后提供自定义URL密钥,我将通过替换

To provide a custom URL key after downloading of HLS content I am updating a content of .m3u8 file by replacing

URI = ...

URI="..."

带有


URI = ckey:// ...

URI="ckey://..."

是为AES加密视频提供密钥的正确方法?

Is this a correct way to provide key for AES encrypted video?

,可能是此行为的原因以及如何解决此问题?

and what could be the reason of this behaviour and how to solve this issue?

请先感谢。

推荐答案

最后,我设法解决了这个问题。下载的HLS视频的大致包结构如下所示:

Finally I managed to solve this issue. Rough package structure of downloaded HLS video is like given below:

HLS.movpkg 
 |_ 0-12345
    |_ 123.m3u8
    |_ StreamInfoBoot.xml
    |_ StreamInfoRoot.xml
    |_ <>.frag
 |_ boot.xml




  1. boot.xml包含HLS的网络URL(基于https:)

  2. StreamBootInfo.xml包含HLS URL(基于https :)与本地下载的.frag文件之间的映射。

在离线模式下,HLS视频可以完美播放。但是启用网络连接后,它是指https:URL而不是本地.frag文件。

In offline mode HLS video was playing perfectly. But when network connection was enabled it was referring to https: URL instead of local .frag files.

我用自定义方案(fakehttps :)替换了这些文件中的https:方案。限制AVPlayer联机获取资源。

I replaced https: scheme in these files with custom scheme (fakehttps:) to restrict AVPlayer going online for resources.

这件事解决了我的问题,但我不知道其背后的确切原因以及AVPlayer如何播放HLS。

This thing solved my issue but I don't know the exact reason behind it and how HLS is played by AVPlayer.

我提到了> ,并且有了一些想法,因此尝试了一些尝试。

I referred this and got some idea so tried something .

我正在进一步更新此答案,以说明如何在离线模式下播放加密的视频。



  1. 获取视频解密所需的密钥。

  1. Get the key required for video decryption.

将该键保存在某个位置。

Save that key some where.

您可以将该键另存为 NSData 或<$ UserDefault 中的c $ c> Data 对象我正在使用视频文件名作为密钥,将密钥数据保存在UserDefaults中。

You can save that key as NSData or Data object in UserDefault I am using video file name as key to save key data in UserDefaults.


  1. 使用 FileManager API遍历<$ c $中的所有文件c> .movpkg 。

获取每个 .m3u8 的内容文件,然后将 URI = some key url 替换为URI = ckey:// keyusedToSaveKeyDataInUserDefaults

Get the content of each .m3u8 file and replace URI="some key url" with URI="ckey://keyusedToSaveKeyDataInUserDefaults"

您可以参考下面给出的此过程代码。

You can refer code given below for this process.



  if let url = asset.asset?.url, let data = data {

            let keyFileName = "\(asset.contentCode!).key"
            UserDefaults.standard.set(data, forKey: keyFileName)

            do {

                // ***** Create key file *****
                let keyFilePath = "ckey://\(keyFileName)"

                let subDirectories = try fileManager.contentsOfDirectory(at: url,
                                                                                 includingPropertiesForKeys: nil, options: .skipsSubdirectoryDescendants)

                for url in subDirectories {

                    var isDirectory: ObjCBool = false

                    if fileManager.fileExists(atPath: url.path, isDirectory: &isDirectory) {

                        if isDirectory.boolValue {

                            let path = url.path as NSString

                            let folderName = path.lastPathComponent
                            let playlistFilePath = path.appendingPathComponent("\(folderName).m3u8")

                            if fileManager.fileExists(atPath: playlistFilePath) {

                                var fileContent = try String.init(contentsOf: URL.init(fileURLWithPath: playlistFilePath))

                                let stringArray = self.matches(for: "URI=\"(.+?)\"", in: fileContent)

                                for pattern in stringArray {
                                    fileContent = fileContent.replacingOccurrences(of: pattern, with: "URI=\"\(keyFilePath)\"")
                                }

                                try fileContent.write(toFile: playlistFilePath, atomically: true, encoding: .utf8)
                            }

                            let streamInfoXML = path.appendingPathComponent("StreamInfoBoot.xml")

                            if fileManager.fileExists(atPath: streamInfoXML) {

                                var fileContent = try String.init(contentsOf: URL.init(fileURLWithPath: streamInfoXML))
                                fileContent = fileContent.replacingOccurrences(of: "https:", with: "fakehttps:")
                                try fileContent.write(toFile: streamInfoXML, atomically: true, encoding: .utf8)
                            }
                        } else {

                            if url.lastPathComponent == "boot.xml" {

                                let bootXML = url.path

                                if fileManager.fileExists(atPath: bootXML) {

                                    var fileContent = try String.init(contentsOf: URL.init(fileURLWithPath: bootXML))
                                    fileContent = fileContent.replacingOccurrences(of: "https:", with: "fakehttps:")
                                    try fileContent.write(toFile: bootXML, atomically: true, encoding: .utf8)
                                }
                            }
                        }
                    }
                }

                userInfo[Asset.Keys.state] = Asset.State.downloaded.rawValue

                // Update download status to db
                let user = RoboUser.sharedObject()
                let sqlDBManager = RoboSQLiteDatabaseManager.init(databaseManagerForCourseCode: user?.lastSelectedCourse)
                sqlDBManager?.updateContentDownloadStatus(downloaded, forContentCode: asset.contentCode!)

                self.notifyServerAboutContentDownload(asset: asset)

                NotificationCenter.default.post(name: AssetDownloadStateChangedNotification, object: nil, userInfo: userInfo)
            } catch  {
            }
        }

func matches(for regex: String, in text: String) -> [String] {

    do {
        let regex = try NSRegularExpression(pattern: regex)
        let nsString = text as NSString
        let results = regex.matches(in: text, range: NSRange(location: 0, length: nsString.length))
        return results.map { nsString.substring(with: $0.range)}
    } catch let error {
        print("invalid regex: \(error.localizedDescription)")
        return []
    }
}

这将更新您的下载包结构,以便在离线模式下播放加密的视频。

现在最后要做的就是在AVAssetResourceLoader类的给定方法下实现如下

- (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest {

    NSString *scheme = loadingRequest.request.URL.scheme;

    if ([scheme isEqualToString:@"ckey"]) {

        NSString *request = loadingRequest.request.URL.host;
        NSData *data = [[NSUserDefaults standardUserDefaults] objectForKey:request];

        if (data) {
            loadingRequest.contentInformationRequest.contentType = AVStreamingKeyDeliveryPersistentContentKeyType;
            loadingRequest.contentInformationRequest.byteRangeAccessSupported = YES;
            loadingRequest.contentInformationRequest.contentLength = data.length;
            [loadingRequest.dataRequest respondWithData:data];
            [loadingRequest finishLoading];
        } else {
            // Data loading fail
        }
    }

    return YES;
}

此方法将在播放视频时提供视频解密的密钥。

This method will provide key to video while playing to decrypt it.

这篇关于AVPlayer停止在线模式下播放AES加密的离线HLS视频的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆