簡體   English   中英

使用 AVAssetWriter/AVAssetReader 設置視頻幀率時出現問題

[英]Problem setting video frame rate using AVAssetWriter/AVAssetReader

情況:

我正在嘗試使用一些參數導出視頻,例如視頻比特率、音頻比特率、幀率、更改視頻分辨率等。請注意,我讓用戶以分數形式設置視頻幀率; 比如用戶可以設置視頻幀率,比如 23.98。

我使用AVAssetWriterAVAssetReader進行此操作。 我使用AVAssetWriterInputPixelBufferAdaptor來編寫示例緩沖區。

除了視頻幀率,其他一切都很好。

我試過的:

  1. 按照此處的建議設置AVAssetWriter.movieTimeScale 這確實會改變視頻幀速率,但也會使視頻變慢。 要點在這里

  1. 設置AVVideoExpectedSourceFrameRateKey 這沒有幫助。 要點在這里

  1. 設置AVAssetWriterInput.mediaTimeScale 同樣,它會改變視頻幀速率,但會像AVAssetWriter.movieTimeScale那樣使視頻變慢。 該視頻在某些時候顯示不同的幀,有時它會卡住並再次恢復。 要點在這里

  1. 使用AVAssetReaderVideoCompositionOutput並設置AVMutableVideoComposition.frameDuration 就像SDAVAssetExportSession一樣。 具有諷刺意味的是,使用 SDAVAssetExportSession 代碼,視頻正以我想要的正確幀速率導出,但它在我的代碼中不起作用。 要點在這里

我不確定為什么它不適用於我的代碼。 這種方法的問題是它總是從AVAssetReaderVideoCompositionOutput.copyNextSampleBuffer()返回 nil。


  1. 按照此處的建議,使用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 時間。

如果我使用這種方法 go(即設置 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);
    }
}
    NSMutableDictionary *writerInputParams = [[NSMutableDictionary alloc] init];
[writerInputParams setObject:AVVideoCodecTypeH264 forKey:AVVideoCodecKey];
[writerInputParams setObject:[NSNumber numberWithInt:width] forKey:AVVideoWidthKey];
[writerInputParams setObject:[NSNumber numberWithInt:height] forKey:AVVideoHeightKey];
[writerInputParams setObject:AVVideoScalingModeResizeAspectFill forKey:AVVideoScalingModeKey];
NSMutableDictionary * compressionProperties = [[NSMutableDictionary alloc] init];
[compressionProperties setObject:[NSNumber numberWithInt: 20] forKey:AVVideoExpectedSourceFrameRateKey];
[compressionProperties setObject:[NSNumber numberWithInt: 20] forKey:AVVideoAverageNonDroppableFrameRateKey];
[compressionProperties setObject:[NSNumber numberWithInt: 0.0] forKey:AVVideoMaxKeyFrameIntervalDurationKey];
[compressionProperties setObject:[NSNumber numberWithInt: 1] forKey:AVVideoMaxKeyFrameIntervalKey];
[compressionProperties setObject:[NSNumber numberWithBool:YES] forKey:AVVideoAllowFrameReorderingKey];
[compressionProperties setObject:AVVideoProfileLevelH264BaselineAutoLevel forKey:AVVideoProfileLevelKey];
[writerInputParams setObject:compressionProperties forKey:AVVideoCompressionPropertiesKey];

self.assetWriterInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:writerInputParams];
self.assetWriterInput.expectsMediaDataInRealTime = YES;

已經驗證過SCNView每秒刷新60幀,但是使用AVAssetWriter只想保存每秒20幀,怎么辦?

上面的 AVVideoExpectedSourceFrameRateKey 和 AVVideoAverageNonDroppableFrameRateKey 都不會影響 fps,配置 fps 將不起作用,.. // 設置它以確保制作功能電影。 即使錄音中途中斷。 在這種情況下,應該只損失最后一秒。 self,videoWriter;movieFragmentInterval = CMTimeMakeWithSeconds(1.0.1000); self.videoWriter.shouldOptimizeForNetworkUse = YES; self.videoWriter.movieTimeScale = 20; 以上配置也不會影響fps。

self.assetWriterInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:writerInputParams];
self.assetWriterInput.expectsMediaDataInRealTime = YES;
/// this config will change video frame presenttime to fit fps, but it will be change video duration.

// self.assetWriterInput.mediaTimeScale = 20; self.assetWriterInput.mediaTimeScale 會影響fps,但是會導致視頻時長被拉長3倍,因為BOOL isSUc = [self.writerAdaptor appendPixelBuffer:cvBuffer withPresentationTime:presentationTime]; 填充幀的時間會重新修改,所以配置了self.assetWriterInput.mediaTimeScale值,與預期嚴重不符,視頻時長不應拉長。

所以如果你想控制AVAssetWriter最后保存的視頻的fps,你必須通過控制,並且必須保證每秒調用20次。

CMTime presentationTime = CMTimeMake(_writeCount * (1.0/20.0) * 1000, 1000);
BOOL isSUc = [self.writerAdaptor appendPixelBuffer:cvBuffer withPresentationTime:presentationTime];
_writeCount += 1;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM