Tho*_*abo 4 avfoundation timecodes avassetreader avasset swift3
我想读取时间码轨道的时间值。Apple 提供了一份出色的文档(请参阅技术说明 2310),但它是用 Objective C 编写的。
我已将核心逻辑转换为 Swift 3。它与 ObjC 版本完全相同,这意味着CMSampleBuffer读取时间码轨道中的 a 并将其转换为CMBlockBuffer. 当我创建数据指针CMBlockBufferGetDataPointer(在timecodeFrame()func 中)时它失败了
,这意味着原始数据总是给我 0 帧。所以它归结为一个问题,我如何正确处理原始数据?
import Foundation
import AVFoundation
import CoreMedia
let movie = URL(fileURLWithPath: "videoWithTimecodeTrack.mov")
let asset = AVAsset(url: movie)
asset.loadValuesAsynchronously(forKeys: ["tracks"]) {
var error: NSError?
guard asset.statusOfValue(forKey: "tracks", error: &error) == AVKeyValueStatus.loaded
else { if let error = error { return print(error) } }
readStartTimecode(asset: asset)
}
func readStartTimecode(ofAsset asset: AVAsset) {
let timecodeTracks = asset.tracks(withMediaType: AVMediaTypeTimecode)
guard let timecodeTrack = timecodeTracks.first,
let assetReader = try? AVAssetReader(asset: asset) else { return }
let readerOutput = AVAssetReaderTrackOutput(track: timecodeTrack, outputSettings: nil)
assetReader.add(readerOutput)
guard assetReader.startReading() else { return }
while let sampleBuffer = readerOutput.copyNextSampleBuffer() {
if let frame = timecodeFrame(sampleBuffer: sampleBuffer) {
print("timecodeFrame: \(frame)")
}
}
}
func timecodeFrame(sampleBuffer: CMSampleBuffer) -> UInt32? {
guard let blockBuffer = CMSampleBufferGetDataBuffer(sampleBuffer),
let formatDescription = CMSampleBufferGetFormatDescription(sampleBuffer)
else { return nil }
var rawData: UnsafeMutablePointer<Int8>? = nil
var length: Int = 0
var totalLength: Int = 0
let status = CMBlockBufferGetDataPointer(blockBuffer, 0, &length, &totalLength, &rawData)
guard status == kCMBlockBufferNoErr,
let frameRead = rawData?.pointee
else { return nil }
let type = CMFormatDescriptionGetMediaSubType(formatDescription)
if type == kCMTimeCodeFormatType_TimeCode32 {
let frame = UInt32(frameRead)
let bigFrame = CFSwapInt32BigToHost(frame)
print("kCMTimeCodeFormatType_TimeCode32: \(bigFrame)")
}
if type == kCMTimeCodeFormatType_TimeCode64 {
print("kCMTimeCodeFormatType_TimeCode64")
// todo
}
return nil
}
Run Code Online (Sandbox Code Playgroud)
编辑:数据指针检索的目标 C 版本如下所示:
size_t length = 0;
size_t totalLength = 0;
char *rawData = NULL;
CMBlockBufferGetDataPointer(blockBuffer, 0, &length, &totalLength, &rawData);
if (status == kCMBlockBufferNoErr) {
int32_t *frameNumberRead = (int32_t *)rawData;
(int)Endian32_Swap(*frameNumberRead)]
}
Run Code Online (Sandbox Code Playgroud)
解决方案是不转换Int8数据,UInt32(rawData.pointee)而是将UnsafeMutablePointer<Int8>指针的内存作为不同类型(临时)访问。这看起来像这样:
if let frames = rawData?.withMemoryRebound(to: UInt32.self, capacity: 1, { CFSwapInt32BigToHost($0.pointee) }) {
return frames
}
Run Code Online (Sandbox Code Playgroud)
完整的函数如下所示:
func timecodeFrame(sampleBuffer: CMSampleBuffer) -> UInt32? {
guard let blockBuffer = CMSampleBufferGetDataBuffer(sampleBuffer),
let formatDescription = CMSampleBufferGetFormatDescription(sampleBuffer)
else { return nil }
var rawData: UnsafeMutablePointer<Int8>? = nil
var length: Int = 0
var totalLength: Int = 0
let status = CMBlockBufferGetDataPointer(blockBuffer, 0, &length, &totalLength, &rawData)
guard status == kCMBlockBufferNoErr else { return nil }
let type = CMFormatDescriptionGetMediaSubType(formatDescription)
if type == kCMTimeCodeFormatType_TimeCode32 {
if let frames = rawData?.withMemoryRebound(to: UInt32.self, capacity: 1, { CFSwapInt32BigToHost($0.pointee) }) {
return frames
}
}
if type == kCMTimeCodeFormatType_TimeCode64 {
if let frames = rawData?.withMemoryRebound(to: UInt64.self, capacity: 1, { CFSwapInt64BigToHost($0.pointee) }) {
return UInt32(frames)
}
}
return nil
}
Run Code Online (Sandbox Code Playgroud)
我希望这对想要阅读视频时间码轨道的开始时间码的其他人有用。
| 归档时间: |
|
| 查看次数: |
816 次 |
| 最近记录: |