我正在读取音频文件中的数据并计算它的散列,就像ffmpeg MD5 muxer的样式一样,除了我做SHA2并使用AVFoundation和OS X {{ 1}} API。
它的作用是打开一个音频文件,如果它被压缩,将其转换为原生PCM格式,然后逐字节地散列交错的样本。
当我从文件中读取音频时,我通常会将样本读入SecTransform
或for
循环的缓冲区。
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
?
答案 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)
}