在CMTime和AVFoundation中单帧移动的方法

8
我正在尝试使用AVFoundation播放视频。我使用以下代码来实现按一次按钮推进播放一帧的功能。
它的效果是间歇性的,有些时候它会推进一帧,但大多数情况下,我需要按3或4次按钮才能推进一帧。
这让我认为这是某种精度问题,但我无法弄清楚是什么问题。每次运行时,新的CMTime似乎都按相同的数量推进。
我的另一个理论是,这可能是由于currentTime没有设置为我帧速率下的精确帧边界(由于浏览视频而导致)。但我不知道如何“捕捉”到我帧速率下最近的帧。
AVAssetTrack *videoTrack = ...;
Float64 frameRate = [videoTrack nominalFrameRate];

CMTime currentTime = [self.playerItem currentTime];
CMTime oneFrame = CMTimeMakeWithSeconds(1.0 / frameRate, currentTime.timescale);
CMTime added = CMTimeAdd(currentTime, oneFrame);

[self.player seekToTime:added toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero];

感谢您的帮助!

从技术上讲,您的代码应该在假设视频帧以1/30秒均匀间隔,并且每秒有30帧的情况下工作。即使当前时间不是在边界上,也会在推进播放器时通过下一帧的PTS并显示它。现在,如果由于某种原因,两帧之间的硬件采样差大于1/30秒,则您的代码将在此处失败。我认为更好的方法是使用类似AVAssetReader的东西来请求下一帧。然后获取该帧的PTS并推进播放头。 - Steve McFarlin
嗨Steve,感谢您的回复。我不确定您所说的PTS是什么意思,能否详细说明一下?此外,我正在开发的应用程序是为电影行业设计的,它处理的大多数剪辑的帧速率都不是30,而是23.976或29.97等其他帧速率。(这尤其复杂,因为CMTime只使用有理数,所以这些浮点数的帧速率很麻烦。)因此,我需要独立于帧速率。(顺便说一句,我的代码在上述三种帧速率中间断地失败。) - Wil Gieseler
演示时间戳。这是一个棘手的问题,我对AVFoundation编辑方面不够熟悉,无法给出确切的答案。看起来AVAssetTrack有一些其他的函数可以映射时间。不确定这是否有帮助。我认为像我建议的使用AVAssetReader对你来说行不通,因为据我所知,你不能向后倒退。 - Steve McFarlin
2个回答

6

这个可能不太明显(因为奇怪的是它只在AVPlayerItem中而不是AVPlayer中可用),但如果AVPlayerItem返回YES,表明canStepForward/canStepBackward,那么你可以调用stepByCount:(NSInteger)stepCount:按一定数量的帧向前或向后移动。


这让我得到了我所需的紧密推动功能。尝试使用非常小的时间增量不仅精度远低,而且在计算上也更加昂贵。 - Brian Hodge

5
你的问题的答案在第二行代码中,在那里你需要将"nominal"从nominalFrameRate中删除。
你的代码假定视频的帧速率是恒定的。这是不正确的。
一个视频可以有恒定的fps,但不一定非要如此。例如,如果你用手机在不同的光照条件下拍摄电影,则帧曝光时间可能会改变,帧速率也会相应变化。
要“捕捉”到帧边界,你需要使用AVAssetReader逐个遍历每个帧。每个帧都带有显示时间戳(其“t”),但如果你正在遍历,则这并不重要,除非你正在更新位置标记。

你确定吗?通常情况下,帧速率不取决于曝光时间,除非曝光时间超过每帧的时间。我不知道 iPhone 是否也是如此,但这是一个经验法则。根据我的经验,视频几乎总是以固定的帧速率进行编码。 - angularsen
1
您可以通过检查iPhone制作的电影文件的时间戳来自行确认。 - Rhythmic Fistman
你是完全正确的,我应该先上谷歌搜索一下。我太快就默认那只是曝光时间和帧率的又一个误解了。 - angularsen

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