ios - 使用 AVAssetWriter/AVAssetReader 设置视频帧率的问题
问题描述
情况:
我正在尝试使用一些参数导出视频,例如视频比特率、音频比特率、帧速率、更改视频分辨率等。请注意,我让用户以分数设置视频帧速率;喜欢用户可以设置视频帧速率,比如 23.98。
我使用AVAssetWriter和AVAssetReader进行此操作。我使用AVAssetWriterInputPixelBufferAdaptor来编写样本缓冲区。
其他一切都很好,除了视频帧率。
我试过的:
- 设置AVVideoExpectedSourceFrameRateKey。这没有帮助。(要点在这里)
- 设置AVAssetWriterInput.mediaTimeScale。同样,它会更改视频帧速率,但会像AVAssetWriter.movieTimeScale一样使视频变慢。该视频在某些时候显示不同的帧,有时它会粘住并再次恢复。(要点在这里)
- 使用AVAssetReaderVideoCompositionOutput并设置AVMutableVideoComposition.frameDuration;就像SDAVAssetExportSession一样。具有讽刺意味的是,使用 SDAVAssetExportSession 代码,视频正在以我想要的正确帧速率导出,但它在我的代码中不起作用。要点在这里
我不确定为什么它不适用于我的代码。这种方法的问题是它总是从AVAssetReaderVideoCompositionOutput.copyNextSampleBuffer()返回 nil 。
- 使用CMSampleTimingInfo手动更改帧的时间戳,如此处所建议的类似:
var sampleTimingInfo = CMSampleTimingInfo()
var sampleBufferToWrite: CMSampleBuffer?
CMSampleBufferGetSampleTimingInfo(vBuffer, at: 0, timingInfoOut: &sampleTimingInfo)
sampleTimingInfo.duration = CMTimeMake(value: 100, timescale: Int32(videoConfig.videoFrameRate * 100))
sampleTimingInfo.presentationTimeStamp = CMTimeAdd(previousPresentationTimeStamp, sampleTimingInfo.duration)
previousPresentationTimeStamp = sampleTimingInfo.presentationTimeStamp
let status = CMSampleBufferCreateCopyWithNewTiming(allocator: kCFAllocatorDefault, sampleBuffer: vBuffer,sampleTimingEntryCount: 1, sampleTimingArray: &sampleTimingInfo, sampleBufferOut: &sampleBufferToWrite)
使用这种方法,我确实可以正确设置帧速率,但它会增加视频持续时间(如该问题答案的评论中所述)。我认为在某些时候我可能不得不丢弃一些帧(如果目标帧速率较低;在大多数情况下我需要降低帧速率)。
如果我知道如果我想要 30fps,并且我当前的帧速率是 60fps,那么很容易丢弃每一秒帧并相应地设置 SampleBuffer 时间。
如果我采用这种方法(即设置 23.98 fps),我如何决定丢弃哪一帧,如果目标帧速率更高,复制哪一帧?提醒:帧速率可以是分数。
解决方案
这是一个选择框架的想法。假设源视频的fps是F,目标fps是TF。率 = TF/F
启动一个等于-rate的变量n,每次都加上rate,当n的整数部分改变时,选择帧。
e.g. rate = 0.3
n: -0.3 0 0.3 0.6 0.9 1.2 1.5 1.8 2.1
^ ^ ^
frame index: 0 1 2 3 4 5 6 7
select 0 4 7
float rate = 0.39999f; // TF/F
float n = -rate; // to make sure first frame will be selected
for (int i = 0; i < 100; ++i, n += rate) { // i stands for frame index, take a video with 100 frames as an example
int m = floor(n);
int tmp = n+rate;
// if rate > 1.0 repeat i
// if rate < 1.0 some of the frames will be dropped
for (int j = 0; m+j < tmp; ++j) {
// Use this frame
printf("%d ", i);
}
}
推荐阅读
- javascript - 将 p5js 添加到 Ember 应用程序
- ios - IPAD ONLY:尽管值加起来为 12,但引导网格元素换行到下一行
- vba - Visual Basic 中的运行时错误 3134
- c - 试图在 c 中创建一个函数,该函数接受用户输入并返回一个 char 字符串
- symfony4 - 在 Symfony 4 配置文件中引用嵌套参数
- linux - 如何找到所有唯一的文件名(消除重复和路径)
- oracle - 表 ESPUMA.ORDER_DETAILS 正在变异,触发器/函数可能看不到它
- javascript - 挂载和卸载的过渡
- c# - 返回列表中具有角色 id 的所有用户
- java - 尝试在空对象引用上调用虚拟方法“void android.widget.ImageView.setImageResource(int)”