簡體   English   中英

如何使用AVAssetReader和AVAssetWriter控制視頻幀速率?

[英]How to control video frame rate with AVAssetReader and AVAssetWriter?

我們試圖了解如何控制/指定我們使用AVAssetReaderAVAssetWriter編碼的視頻的幀速率。 具體來說,我們正在使用AVAssetReaderAVAssetWriter來轉碼/編碼/壓縮我們從照片/視頻庫訪問過的視頻。 我們能夠控制比特率,寬高比變化等等,但無法弄清楚如何控制幀速率。 具體而言,我們希望能夠將長達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.

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