解密媒体文件并通过AVPlayer播放 [英] Decrypt Media Files in chunks and play via AVPlayer

查看:128
本文介绍了解密媒体文件并通过AVPlayer播放的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个 mp4视频文件,我正在对其进行加密保存并通过AVPlayer进行 decrypting 播放.使用CRYPTOSWIFT库进行加密/解密

I have a mp4 video file which i am encrypting to save and decrypting to play via AVPlayer. Using CRYPTOSWIFT Library for encrypting/decrypting

当我一次解密整个文件时,它工作正常,但是我的文件很大,占用100%的CPU使用率和大量内存.因此,我需要对加密文件进行解密.我尝试对文件进行解密,但由于AVPlayer无法识别解密的数据块,因此可能无法播放视频,这可能是因为在加密文件时没有按顺序存储数据.我已经尝试过 chacha20,AES,AES.CTR&AES.CBC 协议来加密和解密文件,但无济于事.

Its working fine when i am decrypting whole file at once but my file is quite big and taking 100% CPU usage and lot of memory. So, I need to decrypt encrypted file in chunks. I tried to decrypt file in chunks but its not playing video as AVPlayer is not recognizing decrypted chunk data maybe data is not stored sequentially while encrypting file. I have tried chacha20, AES, AES.CTR & AES.CBC protocols to encrypt and decrypt files but to no avail.

extension PlayerController: AVAssetResourceLoaderDelegate {

   func resourceLoader(resourceLoader: AVAssetResourceLoader, shouldWaitForLoadingOfRequestedResource loadingRequest: AVAssetResourceLoadingRequest) -> Bool {
      let request = loadingRequest.request
      guard let path = request.URL?.path where request.URL?.scheme == Constants.customVideoScheme else { return true }
      if let contentRequest = loadingRequest.contentInformationRequest {
         do {
            let fileAttributes = try NSFileManager.defaultManager().attributesOfItemAtPath(path)
            if let fileSizeNumber = fileAttributes[NSFileSize] {
               contentRequest.contentLength = fileSizeNumber.longLongValue
            }
         } catch { }

         if fileHandle == nil {
            fileHandle = NSFileHandle(forReadingAtPath: (request.URL?.path)!)!
         }
         contentRequest.contentType = "video/mp4"
         contentRequest.byteRangeAccessSupported = true
      }

      if let data = decryptData(loadingRequest, path: path), dataRequest = loadingRequest.dataRequest {
         dataRequest.respondWithData(data)
         loadingRequest.finishLoading()
         return true
      }
      return true
   }

   func decryptData(loadingRequest: AVAssetResourceLoadingRequest, path: String) -> NSData? {
      print("Current OFFSET: \(loadingRequest.dataRequest?.currentOffset)")
      print("requested OFFSET: \(loadingRequest.dataRequest?.requestedOffset)")
      print("Current Length: \(loadingRequest.dataRequest?.requestedLength)")
      if loadingRequest.contentInformationRequest != nil {
         var data = fileHandle!.readDataOfLength((loadingRequest.dataRequest?.requestedLength)!)
         fileHandle!.seekToFileOffset(0)
         data = decodeVideoData(data)!
         return data
      } else {
         fileHandle?.seekToFileOffset(UInt64((loadingRequest.dataRequest?.currentOffset)!))
         let data = fileHandle!.readDataOfLength((loadingRequest.dataRequest?.requestedLength)!)
// let data = fileHandle!.readDataOfLength(length!) ** When I use this its not playing video but play fine when try with requestedLength **
         return decodeVideoData(data)
      }
   }
}

解码代码以解码nsdata:

Decode code to decode nsdata :

 func decodeVideoData(data: NSData) -> NSData? {
      if let cha = ChaCha20(key: Constants.Encryption.SecretKey, iv: Constants.Encryption.IvKey) {
         let decrypted: NSData = try! data.decrypt(cha)
         return decrypted
      }
      return nil
   }

我需要有关此问题的帮助,请引导我以正确的方式实现此目标.

I need help regarding this issue, Kindly guide me to the right way to achieve this.

推荐答案

有关深入和更完整的CommonCrypto包装的信息,请查看

For in depth and a more complete CommonCrypto wrapper, check out my CommonCrypto wrapper. I've extracted bits and pieces for this answer.

首先,我们需要定义一些执行加密/解密的功能.我现在假设您使用带有PKCS#7填充的AES(256)CBC.总结以下代码段:我们有一个update函数,可以重复调用该函数以消耗这些块.还有一个最终功能将包裹所有剩余的东西(通常用于填充).

First of all, we need to define some functions that will do the encryption/decryption. I'm assuming, for now, you use AES(256) CBC with PKCS#7 padding. Summarising the snippet below: we have an update function, that can be called repeatedly to consume the chunks. There's also a final function that will wrap up any left overs (usually deals with padding).

import CommonCrypto
import Foundation

enum CryptoError: Error {
    case generic(CCCryptorStatus)
}

func getOutputLength(_ reference: CCCryptorRef?, inputLength: Int, final: Bool) -> Int {
    CCCryptorGetOutputLength(reference, inputLength, final)
}

func update(_ reference: CCCryptorRef?, data: Data) throws -> Data {
    var output = [UInt8](repeating: 0, count: getOutputLength(reference, inputLength: data.count, final: false))
    let status = data.withUnsafeBytes { dataPointer -> CCCryptorStatus in
        CCCryptorUpdate(reference, dataPointer.baseAddress, data.count, &output, output.count, nil)
    }
    guard status == kCCSuccess else {
        throw CryptoError.generic(status)
    }
    return Data(output)
}

func final(_ reference: CCCryptorRef?) throws -> Data {
    var output = [UInt8](repeating: 0, count: getOutputLength(reference, inputLength: 0, final: true))
    var moved = 0
    let status = CCCryptorFinal(reference, &output, output.count, &moved)
    guard status == kCCSuccess else {
        throw CryptoError.generic(status)
    }
    output.removeSubrange(moved...)
    return Data(output)
}

接下来,出于演示的目的,加密.

Next up, for the purpose of demonstration, the encryption.

let key = Data(repeating: 0x0a, count: kCCKeySizeAES256)
let iv = Data(repeating: 0, count: kCCBlockSizeAES128)
let bigFile = (0 ..< 0xffff).map { _ in
    return Data(repeating: UInt8.random(in: 0 ... UInt8.max), count: kCCBlockSizeAES128)
}.reduce(Data(), +)

var encryptor: CCCryptorRef?
CCCryptorCreate(CCOperation(kCCEncrypt), CCAlgorithm(kCCAlgorithmAES), CCOptions(kCCOptionPKCS7Padding), Array(key), key.count, Array(iv), &encryptor)

do {
    let ciphertext = try update(encryptor, data: bigFile) + final(encryptor)
    print(ciphertext)    // 1048576 bytes
} catch {
    print(error)
}

在我看来,这是一个很大的文件.现在,解密将以类似的方式进行.

That appears to me as quite a large file. Now decrypting, would be done in a similar fashion.

var decryptor: CCCryptorRef?
CCCryptorCreate(CCOperation(kCCDecrypt), CCAlgorithm(kCCAlgorithmAES), CCOptions(kCCOptionPKCS7Padding), Array(key), key.count, Array(iv), &decryptor)

do {
    var plaintext = Data()
    for i in 0 ..< 0xffff {
        plaintext += try update(decryptor, data: ciphertext[i * kCCBlockSizeAES128 ..< i * kCCBlockSizeAES128 + kCCBlockSizeAES128])
    }
    plaintext += try final(decryptor)
    
    print(plaintext == bigFile, plaintext)    // true 1048560 bytes
} catch {
    print(error)
}

可以为不同的模式更改加密器,完成后也应将其释放,而且我不太确定 update 函数上的任意输出将如何运行,但这应该足以让您了解如何使用 CommonCrypto .

The encryptor can be altered for different modes and should also be released once it's done, and I'm not too sure how arbitrary output on the update function will behave, but this should be enough to give you an idea of how it can be done using CommonCrypto.

这篇关于解密媒体文件并通过AVPlayer播放的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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