ios - 使用 AVAssetWriter/AVAssetReader 设置视频帧率的问题

标签 ios swift macos video video-encoding

情况:
我正在尝试使用一些参数导出视频,例如视频比特率、音频比特率、帧速率、更改视频分辨率等。请注意,我让用户以分数设置视频帧速率;喜欢用户可以设置视频帧速率,比如 23.98。
我用 AVAssetWriter AVAssetReader 对于这个操作。我用 AVAssetWriterInputPixelBufferAdaptor用于写入样本缓冲区。
除了 之外,其他一切都很好。视频帧率 .
我试过的:

  • 设置 AVAssetWriter.movi​​eTimeScale 根据建议 here .这确实会改变视频帧速率,但也会使视频变慢。 ( gist here )
  • 设置 AVVideoExpectedSourceFrameRateKey .这没有帮助。 ( gist here )
  • 设置 AVAssetWriterInput.mediaTimeScale .同样,它会更改视频帧速率,但会像 AVAssetWriter.movi​​eTimeScale 一样使视频变慢。该视频在某些时候显示不同的帧,有时它会粘住并再次恢复。 ( gist here )
  • 使用 AVAssetReaderVideoCompositionOutput 和设置 AVMutableVideoComposition.frameDuration ;就像 SDAVAssetExportSession做。具有讽刺意味的是,使用 SDAVAssetExportSession 代码,视频正在以我想要的正确帧速率导出,但它在我的代码中不起作用。 gist here

  • 我不确定为什么它不适用于我的代码。这种方法的问题是它总是从 AVAssetReaderVideoCompositionOutput.copyNextSampleBuffer() 返回 nil。
  • 使用 手动更改帧的时间戳CMSampleTimingInfo ,如建议 here类似:
  • 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
    启动一个变量 n 等于 -rate 并每次添加 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);
        }
    }
    

    关于ios - 使用 AVAssetWriter/AVAssetReader 设置视频帧率的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56613335/

    相关文章:

    ios - 如何将两个或多个按钮添加到annotationView : MKAnnotationView?

    ios - NSData 存储在某处

    ios - NSString drawInRect 设置上下文

    ios - 检查在 UITableView 的每个部分中是否至少选择了一个单元格

    python - 从python的特定程序打开文件

    ios - Game Center 应该是可选的吗?

    ios - PHImageManager 的 requestImage 在滚动数千张图像时锁定 UI

    ios - 展开选项时崩溃,即使它不是零

    macos - 如何升级kubectl客户端版本

    macos - 使用 echo 修改 Hosts 文件