programing

AVAssetWriter/AVAssetReader를 사용하여 비디오 프레임률을 설정하는 중 문제 발생

i4 2023. 9. 3. 12:10
반응형

AVAssetWriter/AVAssetReader를 사용하여 비디오 프레임률을 설정하는 중 문제 발생

상황:

비디오 비트 전송률, 오디오 비트 전송률, 프레임 전송률, 비디오 해상도 변경 등의 매개 변수를 사용하여 비디오를 내보내려고 합니다.사용자가 비디오 프레임률을 분수 단위로 설정할 수 있습니다. 사용자가 비디오 프레임률을 23.98로 설정할 수 있는 것처럼 말입니다.

이 작업에는 AVAssetWriterAVAssetReader를 사용합니다.저는 샘플 버퍼를 쓰기 위해 AVAssetWriterInputPixelBufferAdaptor를 사용합니다.

비디오 프레임률을 제외한 다른 모든 것은 정상적으로 작동합니다.

내가 시도한 것:

  1. 여기서 제안하는 대로 AVAssetWriter.movieTimeScale을 설정합니다.그러면 비디오 프레임률이 변경되지만 비디오가 느려집니다.(여기에 표시)

  1. AVVideoExpectedSourceFrameRateKey를 설정하고 있습니다.그건 도움이 안 돼요

  1. AVAssetWriterInput.mediaTimeScale을 설정하는 중입니다.다시 비디오 프레임률을 변경하지만 AVAssetWriter.movieTimeScale처럼 비디오가 느려집니다.비디오는 어느 시점에서 서로 다른 프레임을 보여주며, 때때로 고정되었다가 다시 시작됩니다.(여기에 표시)

  1. AVAssetReaderVideoComposition 사용SDAVAsetExportSession과 마찬가지로 AVMutableVideoComposition.frameDuration을 출력하고 설정합니다.SDAVAssetExportSession 코드를 사용하면 아이러니하게도 비디오가 원하는 프레임 속도로 내보내지지만 내 코드에서는 작동하지 않습니다.여기의 요지

왜 내 코드에서 작동하지 않는지 잘 모르겠습니다.이 접근 방식의 문제는 AVAssetReaderVideoComposition에서 항상 0을 반환한다는 것입니다.Output.copyNextSampleBuffer()입니다.


  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라면, 매 초 프레임을 폐기하고 그에 따라 샘플 버퍼 시간을 설정하는 것이 간단합니다.

이 접근 방식(예: 23.98fps 설정)을 사용할 경우 폐기할 프레임을 어떻게 결정하고 목표 프레임 속도가 더 높은 경우 복제할 프레임을 어떻게 결정합니까?주의: 프레임률은 분수 단위일 수 있습니다.


프레임을 선택하는 방법이 있습니다.소스 비디오의 fps가 F이고 대상 fps가 TF라고 가정합니다. 비율 = TF/F

-rate와 동일한 변수 n을 시작하고 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 또는 위의 AVVideoAverageNonDropableFrameRateKey는 fps에 영향을 주지 않으며 구성 fps가 작동하지 않습니다!!!녹화가 중간에 끊기는 경우에도 기능하는 동영상이 생성되도록 설정합니다.이 경우 마지막 1초만 손실되어야 합니다.self.videoWriter.movieFragment간격 = CMTimeMakeWithSeconds(1.0, 1000), self.videoWriter.최적화해야 합니다.NetworkUse = 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에 영향을 미치지만, BOOL은 SUC = [self]이기 때문에 비디오 지속 시간이 3배 연장됩니다.writerAdaptor appendPixelBuffer:cvBuffer with PresentationTime: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;

언급URL : https://stackoverflow.com/questions/56613335/problem-setting-video-frame-rate-using-avassetwriter-avassetreader

반응형