iOS更新录音时间自录音开始以来的正确方法

4
如何在使用AVFoundation捕获视频时更新时间标签:

如何在使用AVFoundation进行视频捕获时更新时间标签:

Mic -> AVCaptureDeviceInput -> AVCaptureSession -> AVCaptureAudioDataOutput

AVCaptureAudioDataOutput有委托和AVAssetWritter,可以将sampleBuffers写入输出文件。在这个解决方案中,我想更新录制时间。我可以在用户点击记录/暂停/恢复/停止按钮时记录时间,但是否有官方方法可以在开始时更新一些UILabel的00:00:00时间呢?我知道我可以使用NSTimer,但是当我使用AVAudioRecorder时,我有一个属性:

var currentTime: TimeInterval
// The time, in seconds, since the beginning of the recording.

以上属性也适用于暂停/继续按钮。

现在在这个新的管道中,我没有这个属性,我想知道是否我漏掉了什么,而且我无法在任何地方找到它。此外,我注意到AVCaptureSession有一个名为masterClock的CMClock属性,但我不确定如何使用它来计算时间,从录制开始算起。

我不理解这个时钟和像这样的方法的整个概念:

CMClockGetHostTimeClock()
CMSyncConvertTime

如何在AVCaptureAudioDataOutputSampleBufferDelegate方法中基于sampleBuffer获得从录制开始的秒数?

更新

我发现了一个叫做CMTimebase的类,我认为这正是我正在寻找的。目前我不知道如何使用它,但我在playground中进行实验后发现了一些东西。

import CoreMedia

let masterClock = CMClockGetHostTimeClock() // This masterclock should be from captureSession
var timebase: CMTimebase? = nil
CMTimebaseCreateWithMasterClock(kCFAllocatorDefault, masterClock, &timebase)

print(CMTimeGetSeconds(CMTimebaseGetTime(timebase!)))

CMTimebaseSetRate(timebase!, 1.0) // Start/Resume recording

sleep(1)
print(CMTimeGetSeconds(CMTimebaseGetTime(timebase!)))

sleep(1)
CMTimebaseSetRate(timebase!, 0.0) // Pause Recording
print(CMTimeGetSeconds(CMTimebaseGetTime(timebase!)))


sleep(1)
print(CMTimeGetSeconds(CMTimebaseGetTime(timebase!)))

这个对象非常有用。当我将速率设置为1.0时,它会与masterClock同步测量时间。当我将速率设置为0.0时,它停止测量时间。我还注意到我可以将Timer附加到这个对象上。不幸的是,关于Core Media的东西没有好的教程,只有一些苹果网站上的api文档。有人知道是否有关于CoreMedia Framework的WWDC相关主题的好教程/视频吗?


我以前没有使用过CMTimebase,但你应该能够使用它来实现你想要的功能。只需在开始录制时将其附加到AVCaptureSession时钟上,并在需要更新时间时读取CMTimebase即可。不过,为了使显示更新,仍然需要定时器或触发器每隔一段时间缓冲区。不幸的是,我不熟悉任何文档,快速搜索也没有显示出任何文档。祝好运! - Tim Bull
1个回答

5
当你处理AVCaptureAudioOutput时,你现在正在处理样本缓冲区。无论是视频还是音频,你需要两个关键信息:样本数据和记录该样本数据的时间。
这就是“时钟”https://developer.apple.com/reference/coremedia/cmclock 时钟代表时间信息的来源:通常是测量时间流逝的硬件设备。它非常精确,这也是重点。它是只读的,你只能从中获取引用,不能设置它。缓冲区可能会发生各种事情,比如排队、通过网络转发,但如果你有那个CMTime引用,它们可以按正确的顺序和正确的时间重构。
CMSync是一组函数的子集,用于处理时钟漂移(两个不同的时钟报告略微不同的值之间的差异)。你根本不需要担心这些。
实际上,要做你想做的事情非常简单。以下是一个可以在playground中复制粘贴的示例。
import AVFoundation

let masterClock : CMClock = CMClockGetHostTimeClock()
let startTime : CMTime = CMClockGetTime(masterClock)
sleep(1)
let endTime : CMTime = CMClockGetTime(masterClock)
let difference : CMTime = CMTimeSubtract(endTime, startTime)
let seconds = CMTimeGetSeconds(difference)

这应该会给你一个略大于1秒的秒数值。

当你在应用程序中点击录制按钮时,获取AVCaptureSession时钟并捕获startTime。

当你想要更新UI时,只需再次检查AVCaptureSession时钟,减去两个CMTime并转换为秒。

只需确保不要在每个样本缓冲区上更新UI,那将太频繁了,所以也许只需每14,000个样本左右(或适用于你正在使用的音频频率)这样做。还要确保将其卸载到单独的线程。

希望这有所帮助,并对时钟进行了一些解释。祝好运!


1
非常好的答案,非常有信息量。我会在游乐场里试着玩一下这个。我知道如果我只有60fps的显示频率,那么每秒尝试更新UILabel 44100次(与音频采样率频率相同)是没有意义的。我明天会解决这个问题。非常感谢! - Marcin Kapusta
很高兴这对你有用,一旦你测试过并且满意了,请随意接受并点赞这个答案。 - Tim Bull
Tim,你能看一下我对问题的更新并审查一下我的发现吗?也许你知道一些关于CoreMedia Framework的好教程/资料? - Marcin Kapusta
请问各位好心的朋友们,你们知道是否可以使用这种技术来将视频与捕获会话同步吗?比如说,我想录制一个视频,覆盖在另一个正在播放的视频上,并且我希望将结果同步,使它们在时间上完全一致。我能否使用这些主时钟来实现呢?我已经寻找了很久一种准确同步两个视频的方法!谢谢 - Luke Smith
如果我想说“记录从此刻开始”,我该如何获取绝对系统时间? - Petrus Theron
@PetrusTheron 我认为这是不可能的。它是用于计时帧的时间参考,而不是“这是在周五下午2点”的时钟意义上的时间。这种信息(视频录制时间)将在视频的元数据中。 - Tim Bull

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接