Почему AVSampleBufferDisplayLayer перестает показывать CMSampleBuffers, взятые из делегата AVCaptureVideoDataOutput?

Я хочу отобразить некоторые CMSampleBuffer с помощью AVSampleBufferDisplayLayer, но он зависает после показа первого образца.

Я получаю буферы выборки из делегата AVCaptureVideoDataOutputSampleBuffer:

-(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection
{
    CFRetain(sampleBuffer);
    [self imageToBuffer:sampleBuffer];
    CFRelease(sampleBuffer);
}

поместить их в вектор

-(void) imageToBuffer: (CMSampleBufferRef )source{
//buffers is defined as: std::vector<CMSampleBufferRef> buffers;
        CMSampleBufferRef newRef;
        CMSampleBufferCreateCopy(kCFAllocatorDefault, source, &newRef);
        buffers.push_back(newRef);
}

Затем попробуйте показать их через AVSampleBufferDisplayLayer (в другом ViewController)

AVSampleBufferDisplayLayer * displayLayer = [[AVSampleBufferDisplayLayer alloc] init];

    displayLayer.bounds = self.view.bounds;
    displayLayer.position = CGPointMake(CGRectGetMidX(self.displayOnMe.bounds), CGRectGetMidY(self.displayOnMe.bounds));
    displayLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
    displayLayer.backgroundColor = [[UIColor greenColor] CGColor];

    [self.view.layer addSublayer:displayLayer];
    self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

    dispatch_queue_t queue = dispatch_queue_create("My queue", DISPATCH_QUEUE_SERIAL);
    [displayLayer setNeedsDisplay];
    [displayLayer requestMediaDataWhenReadyOnQueue:queue
                                        usingBlock:^{
                                            while ([displayLayer isReadyForMoreMediaData]) {

                                                if (samplesKey < buffers.size()) {
                                                    CMSampleBufferRef buf = buffers[samplesKey];
                                                    [displayLayer enqueueSampleBuffer:buffers[samplesKey]];
                                                    samplesKey++;

                                                }else
                                                {
                                                    [displayLayer stopRequestingMediaData];
                                                    break;
                                                }
                                            }

                                        }];

но он показывает первый образец, затем зависает и ничего не делает.

И мои настройки вывода видеоданных следующие:

//set up our output
self.videoDataOutput = [[AVCaptureVideoDataOutput alloc] init];
dispatch_queue_t queue = dispatch_queue_create("VideoQueue", DISPATCH_QUEUE_SERIAL);
[_videoDataOutput setSampleBufferDelegate:self queue:queue];
[_videoDataOutput setVideoSettings:[NSDictionary dictionaryWithObjectsAndKeys:
                                                [NSNumber numberWithInt:kCVPixelFormatType_32BGRA],(id)kCVPixelBufferPixelFormatTypeKey,
                                                nil]]; 

person didReceiveMemoryWarning    schedule 24.02.2015    source источник
comment
Как Вы это исправили?   -  person Pablo Martinez    schedule 10.08.2015
comment
удалите время из requestMediaDataWhenReadyOnQueue: вызов функции. Поскольку в этом нет необходимости, эта функция уже вызывается, когда isReadyForMoreMediaData становится истинным. Таким образом, в то время как сделать функцию неправильной. Если у вас есть дополнительные вопросы, уточните, в чем именно у вас возникла проблема, я постараюсь внести ясность.   -  person didReceiveMemoryWarning    schedule 11.08.2015


Ответы (1)


Я столкнулся с этой проблемой в том же контексте, пытаясь получить вывод из AVCaptureVideoDataOutput и отобразить его в слое AVSampleDisplay.

Если ваши кадры отображаются в порядке отображения, то исправить это очень просто, просто установите флаг немедленного отображения в CMSampleBufferRef.

Получите образец буфера, возвращенный делегатом, а затем...

CFArrayRef attachments = CMSampleBufferGetSampleAttachmentsArray(sampleBuffer, YES);
CFMutableDictionaryRef dict = (CFMutableDictionaryRef)CFArrayGetValueAtIndex(attachments, 0);

CFDictionarySetValue(dict, kCMSampleAttachmentKey_DisplayImmediately, kCFBooleanTrue);

Если ваши кадры выходят в порядке кодирования (не в порядке отображения), то метки времени в CMSampleBuffer должны быть смещены на ноль и перенастроены так, чтобы метка времени первых кадров была равна времени 0.

 double pts = CMTimeGetSeconds(CMSampleBufferGetPresentationTimeStamp(sampleBuffer));

 // ptsStart is equal to the first frames presentationTimeStamp so playback starts from time 0.
 CMTime presentationTimeStamp = CMTimeMake((pts-ptsStart)*1000000,1000000);

 CMSampleBufferSetOutputPresentationTimeStamp(sampleBuffer, presentationTimeStamp);

Обновление:

Я столкнулся с ситуацией, когда какое-то видео по-прежнему не воспроизводилось гладко, когда я использовал метод нулевого смещения, и я продолжил расследование. Кажется, правильный ответ — использовать PTS из первого кадра, который вы собираетесь воспроизвести.

Мой ответ здесь, но я также опубликую его здесь.

Установить скорость, с которой AVSampleBufferDisplayLayer отображает образцы буферов

Временная база должна быть установлена ​​на отметку времени презентации (pts) первого кадра, который вы собираетесь декодировать. Я индексировал точки первого кадра до 0, вычитая начальные точки из всех последующих точек и устанавливая временную базу на 0. По какой-то причине это не работало с определенным видео.

Вам нужно что-то вроде этого (вызывается перед вызовом декодирования):

CMTimebaseRef controlTimebase;
CMTimebaseCreateWithMasterClock( CFAllocatorGetDefault(), CMClockGetHostTimeClock(), &controlTimebase );

displayLayer.controlTimebase = controlTimebase;

// Set the timebase to the initial pts here
CMTimebaseSetTime(displayLayer.controlTimebase, CMTimeMake(ptsInitial, 1));
CMTimebaseSetRate(displayLayer.controlTimebase, 1.0);

Установите PTS для CMSampleBuffer...

CMSampleBufferSetOutputPresentationTimeStamp(sampleBuffer, presentationTimeStamp);

И, возможно, убедитесь, что отображение сразу не установлено....

CFDictionarySetValue(dict, kCMSampleAttachmentKey_DisplayImmediately, kCFBooleanFalse);

Это очень кратко описано в сессии 513 WWDC 2014.

person Kaleb    schedule 09.11.2015
comment
Откуда вы получаете presentationTimeStamp при установке PTS для CMSampleBuffer? - person Xavier L.; 16.02.2020