[英]Problem setting video frame rate using AVAssetWriter/AVAssetReader
[英]How to control video frame rate with AVAssetReader and AVAssetWriter?
我們試圖了解如何控制/指定我們使用AVAssetReader
和AVAssetWriter
編碼的視頻的幀速率。 具體來說,我們正在使用AVAssetReader
和AVAssetWriter
來轉碼/編碼/壓縮我們從照片/視頻庫訪問過的視頻。 我們能夠控制比特率,寬高比變化等等,但無法弄清楚如何控制幀速率。 具體而言,我們希望能夠將長達5分鍾的30 FPS視頻作為輸入,並以15 FPS發出5分鍾視頻。
我們處理樣本緩沖區的當前循環是:
[writer startWriting];
[writer startSessionAtSourceTime:kCMTimeZero];
[videoReader startReading];
[videoWriterInput requestMediaDataWhenReadyOnQueue:videoEncoderQueue usingBlock:
^{
while ([videoWriterInput isReadyForMoreMediaData]) {
CMSampleBufferRef sampleBuffer;
if ([videoReader status] == AVAssetReaderStatusReading
&& (sampleBuffer = [videoReaderTrackOutput copyNextSampleBuffer])) {
if (sampleBuffer) {
BOOL result = [videoWriterInput appendSampleBuffer:sampleBuffer];
CFRelease(sampleBuffer);
if (!result) {
[videoReader cancelReading];
break;
}
}
} else {
// deal with status other than AVAssetReaderStatusReading
[videoWriterInput markAsFinished];
// [...]
break;
}
}
}];
我們如何增加或改變這一點,以便我們可以控制創建的視頻的幀速率? 我們似乎無法在SO或其他任何明確解釋如何執行此操作的地方找到樣本。 我認為我們應該使用CMTime
以及除上面代碼示例中的其他方法之外的其他方法,但細節尚不清楚。
根據您合成幀的方式,您可能只需要設置movieTimeScale
。
或者,您需要使用CMTime
在將每個幀添加到CMTime
時設置每個幀的時間。
CMTime time = CMTimeMake(0, 30); // (time, time_scale)
這將以每秒30幀的幀速率創建第一幀的時間。 將第二個參數設置為所需的幀速率,不要更改它。 為添加到編寫器的每個幀增加第一個。
編輯:
有許多不同的方法可以處理傳入和傳出的數據。 因此,有多種選擇可以如何/需要指定時間。 通常,上述內容適用於使用AVAssetWriterInputPixelBufferAdaptor
(如果您正在編輯視頻幀)。
根據您更新的代碼,您正在進行更“簡單”的傳遞,您可能需要使用CMSampleBufferCreateCopyWithNewTiming
來生成從閱讀器接收的sampleBuffer
的副本。 奇怪的是,我認為,這使得時間更加復雜。 根據您嘗試使用編輯實現的內容,您可能需要創建可用於所有幀的新單個CMSampleTimingInfo
,或者使用CMSampleBufferGetSampleTimingInfoArray
從樣本緩沖區獲取現有時序信息,然后創建其編輯版本。 有點像:
CMItemCount count;
CMTime newTimeStamp = CMTimeMake(...);
CMSampleBufferGetSampleTimingInfoArray(sampleBuffer, 0, nil, &count);
CMSampleTimingInfo *timingInfo = malloc(sizeof(CMSampleTimingInfo) * count);
CMSampleBufferGetSampleTimingInfoArray(sampleBuffer, count, timingInfo, &count);
for (CMItemCount i = 0; i < count; i++)
{
timingInfo[i].decodeTimeStamp = kCMTimeInvalid;
timingInfo[i].presentationTimeStamp = newTimeStamp;
}
CMSampleBufferRef completedSampleBuffer;
CMSampleBufferCreateCopyWithNewTiming(kCFAllocatorDefault, sampleBuffer, count, timingInfo, &completedSampleBuffer);
free(timingInfo);
你如何選擇你的newTimeStamp
決定了你會得到什么結果。
在此之前,我使用dispatch_block_wait在delta時間執行塊以再次調用整個函數。 但是一旦我意識到它有一天會成為一個有缺陷的東西,我使用dispatch_source_t作為計時器來執行塊作為FPS的控制。
創建一個你想做的事情的塊:
var block = dispatch_block_create(...)
var queue = dispatch_queue_create(...)
var source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue)
dispatch_set_timer(source,STARTTIME,INTERVAL,0)
dispatch_source_set_event_handler(source,block)
dispatch_resume(source)
如果您正在尋找獲取緩沖區的真實案例參考,我已經在https://github.com/matthewlui/FSVideoView上創建了它。 *添加傳入的時間間隔是納秒秒數= 1 / 1,000,000,000秒。 將您的願望加速到下一幀。
更好的方法是相應地設置AVSampleBufferDisplayLayer的timebase屬性:
CMTimebaseRef timebase;
OSStatus timebaseResult;
timebaseResult = CMTimebaseCreateWithMasterClock(NULL, CMClockGetHostTimeClock(), &timebase);
if (timebaseResult != 0)
{
NSLog(@"ERROR: could not create timebase");
} else {
CMTimebaseSetTime(timebase, CMTimeMake(1, 3000));
CMTimebaseSetRate(timebase, 1.0f);
}
[(AVSampleBufferDisplayLayer *)self.layer setControlTimebase:timebase];
CFRelease(timebase);
應該明白為什么這是所有其他方式的首選方式。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.