如何使用AVAsset和Swift 3读取时间码轨道?

问题描述:

我想读取时间码轨道的时间值。有一个从苹果的 出色的文档(见Technical Note 2310) 但它用Objective C.如何使用AVAsset和Swift 3读取时间码轨道?

我翻译了核心逻辑斯威夫特3.它的工作原理完全一样 ObjC版本,这意味着从CMSampleBuffer时间码 轨道被读取并转换为CMBlockBuffer。 I 创建数据指针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 
}  

编辑:数据指针检索的目标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)] 
} 

的解决方案是不是Int8数据转换像UInt32(rawData.pointee)但访问UnsafeMutablePointer<Int8>指针的内存一个不同的类型(暂时)。这应该是这样的:

if let frames = rawData?.withMemoryRebound(to: UInt32.self, capacity: 1, { CFSwapInt32BigToHost($0.pointee) }) { 
    return frames 
} 

全功能应该是这样的:

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 
} 

我希望这是对别人谁想要读取的视频的时间码轨道的起始时间码是有用的。