SecTransform

时间:2015-07-31 18:16:55

标签: swift avfoundation

我正在读取音频文件中的数据并计算它的散列,就像ffmpeg MD5 muxer的样式一样,除了我做SHA2并使用AVFoundation和OS X {{ 1}} API。

它的作用是打开一个音频文件,如果它被压缩,将其转换为原生PCM格式,然后逐字节地散列交错的样本。

当我从文件中读取音频时,我通常会将样本读入SecTransformfor循环的缓冲区。

while

我遇到的问题是,我可以看到将数据加载到extension AVAudioFile { func sha2() throws -> NSData { let bufSize = AVAudioFrameCount(0x1000) let buffer = AVAudioPCMBuffer(PCMFormat: self.processingFormat, frameCapacity: bufSize) // initialize digest algo for(;;) { try readIntoBuffer(buffer) if buffer.frameLength > 0 { // read buffer into digest } else { break } } // finalize digest and return... return NSData() } } 的唯一方法是在SecTransform中一次性处理所有数据,或者作为{ {1}}。如何将我的缓冲区数据反馈到CFData

1 个答案:

答案 0 :(得分:0)

我想通了,用NSStream.getBoundStreamsWithBufferSize()创建了绑定的输入和输出流,然后用异步循环提供输出流

完整的实现是这样的:

func writeSamplesFromBuffer(buffer: AVAudioPCMBuffer, toStream : NSOutputStream) {
    assert(buffer.format.interleaved == true)
    var rawBuffer = UnsafePointer<UInt8>(buffer.int32ChannelData.memory)
    var toWrite = sizeof(Int32) * 
        Int(buffer.format.channelCount) * Int(buffer.frameLength)
    while toWrite > 0 {
        let written = toStream.write(rawBuffer, maxLength: toWrite)
        rawBuffer = rawBuffer.advancedBy(written)
        toWrite -= written
    }
}

func writeAudioDataFromURL(url : NSURL,
    usingFormat format: AVAudioCommonFormat,
    toStream: NSOutputStream) throws {

        let audioFile = try AVAudioFile(forReading: url,
            commonFormat: format,
            interleaved: true)

        let pcmBuffer = AVAudioPCMBuffer(PCMFormat:
            audioFile.processingFormat,
            frameCapacity: 0x1000)

        toStream.open()

        let writerQueue = dispatch_get_global_queue(QOS_CLASS_UTILITY, 0)
        dispatch_async(writerQueue) {
            while true {
                do {
                    try audioFile.readIntoBuffer(pcmBuffer)
                    if pcmBuffer.frameLength > 0 {
                        writeSamplesFromBuffer(pcmBuffer, toStream: toStream)
                    } else {
                        break
                    }
                } catch let error {
                    fatalError("Fatal error: \(error) while reading audio file \(audioFile) at URL \(url)")
                }
            }
            toStream.close()
        }
}

func sha256DigestForStream(stream : NSInputStream) throws -> NSData {
    let transform = SecTransformCreateGroupTransform().takeRetainedValue()
    let readXform = SecTransformCreateReadTransformWithReadStream(stream as CFReadStreamRef).takeRetainedValue()

    var error : Unmanaged<CFErrorRef>? = nil
    let digestXform = SecDigestTransformCreate(kSecDigestSHA2, 256, &error).takeRetainedValue()

    SecTransformConnectTransforms(readXform, kSecTransformOutputAttributeName,
        digestXform, kSecTransformInputAttributeName,
        transform, &error)
    if let e = error { throw e.takeUnretainedValue() }

    if let output = SecTransformExecute(transform, &error) as? NSData {
        return output
    } else {
        throw error!.takeRetainedValue()
    }
}

func sha256DigestForAudioFile(url : NSURL,
    convertedToSampleFormat sampleFormat: AVAudioCommonFormat) throws -> NSData {

        let streamBufSize = 0x1000
        var rs : NSInputStream? = nil
        var ws : NSOutputStream? = nil

        NSStream.getBoundStreamsWithBufferSize(streamBufSize,
            inputStream: &rs, outputStream: &ws)

        guard let readStream = rs, writeStream = ws else {
            fatalError("Failed to create file read streams")
        }

        try writeAudioDataFromURL(url,
            usingFormat: sampleFormat, toStream: writeStream)

        return try sha256DigestForStream(readStream)
}