Replaykit, startCaptureWithHandler() не отправляет CMSampleBufferRef типа Video в captureHandler

Я реализовал RPScreenRecorder, который записывает экран, а также микрофон аудио. После завершения нескольких записей я прекращаю запись и объединяю аудиозаписи с видео с помощью AVMutableComposition а затем объединить все видео в единое видео.

Для записи экрана и получения видео и аудио файлов я использую

- (void)startCaptureWithHandler:(nullable void(^)(CMSampleBufferRef sampleBuffer, RPSampleBufferType bufferType, NSError * _Nullable error))captureHandler completionHandler:

Для остановки записи. Я называю эту функцию:

- (void)stopCaptureWithHandler:(void (^)(NSError *error))handler;

И это довольно просто.

В большинстве случаев это прекрасно работает, я получаю видео и аудио CMSampleBuffers. Но иногда бывает так, что startCaptureWithHandlerотправляет только аудио буферы, но не видео буферы. И как только я столкнусь с этой проблемой, она не будет работать, пока я не перезапущу свое устройство и не переустановлю приложение. Это делает мое приложение настолько ненадежным для пользователя. Я думаю, что это проблема с набором для воспроизведения, но я не смог найти проблем с другими разработчиками. дайте мне знать, если кто-нибудь из вас сталкивался с этой проблемой и нашел решение.

Я проверял несколько раз, но не видел проблем в конфигурации. Но здесь это все равно.

NSError *videoWriterError;
videoWriter = [[AVAssetWriter alloc] initWithURL:fileString fileType:AVFileTypeQuickTimeMovie
                                           error:&videoWriterError];


NSError *audioWriterError;
audioWriter = [[AVAssetWriter alloc] initWithURL:audioFileString fileType:AVFileTypeAppleM4A
                                           error:&audioWriterError];

CGFloat width =UIScreen.mainScreen.bounds.size.width;
NSString *widthString = [NSString stringWithFormat:@"%f", width];
CGFloat height =UIScreen.mainScreen.boNSString *heightString = [NSString stringWithFormat:@"%f", height];unds.size.height;

NSDictionary  * videoOutputSettings= @{AVVideoCodecKey : AVVideoCodecTypeH264,
                                       AVVideoWidthKey: widthString,
                                       AVVideoHeightKey : heightString};
videoInput  = [[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeVideo outputSettings:videoOutputSettings];

videoInput.expectsMediaDataInRealTime = true;

AudioChannelLayout acl;
bzero( &acl, sizeof(acl));
acl.mChannelLayoutTag = kAudioChannelLayoutTag_Mono;
NSDictionary * audioOutputSettings = [ NSDictionary dictionaryWithObjectsAndKeys:
                                      [ NSNumber numberWithInt: kAudioFormatAppleLossless ], AVFormatIDKey,
                                      [ NSNumber numberWithInt: 16 ], AVEncoderBitDepthHintKey,
                                      [ NSNumber numberWithFloat: 44100.0 ], AVSampleRateKey,
                                      [ NSNumber numberWithInt: 1 ], AVNumberOfChannelsKey,
                                      [ NSData dataWithBytes: &acl length: sizeof( acl ) ], AVChannelLayoutKey,
                                      nil ];

audioInput = [[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeAudio outputSettings:audioOutputSettings];

[audioInput setExpectsMediaDataInRealTime:YES];

[videoWriter addInput:videoInput];
    [audioWriter addInput:audioInput];

    [[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker error:nil];

[RPScreenRecorder.sharedRecorder startCaptureWithHandler:^(CMSampleBufferRef  _Nonnull sampleBuffer, RPSampleBufferType bufferType, NSError * _Nullable myError) {

Block

}

Функция startCaptureWithHandler также имеет довольно простую функциональность:

[RPScreenRecorder.sharedRecorder startCaptureWithHandler:^(CMSampleBufferRef  _Nonnull sampleBuffer, RPSampleBufferType bufferType, NSError * _Nullable myError) {

                    dispatch_sync(dispatch_get_main_queue(), ^{


                        if(CMSampleBufferDataIsReady(sampleBuffer))
                        {

                            if (self->videoWriter.status == AVAssetWriterStatusUnknown)
                            {
                                    self->writingStarted = true;
                                    [self->videoWriter startWriting];
                                    [self->videoWriter startSessionAtSourceTime:CMSampleBufferGetPresentationTimeStamp(sampleBuffer)];

                                    [self->audioWriter startWriting];
                                    [self->audioWriter startSessionAtSourceTime:CMSampleBufferGetPresentationTimeStamp(sampleBuffer)];
                            }
                            if (self->videoWriter.status == AVAssetWriterStatusFailed) {
                                return;
                            }

                            if (bufferType == RPSampleBufferTypeVideo)
                            {

                                if (self->videoInput.isReadyForMoreMediaData)
                                {
                                        [self->videoInput appendSampleBuffer:sampleBuffer];
                                }
                            }
                            else if (bufferType == RPSampleBufferTypeAudioMic)
                            {
                                //                                printf("\n+++ bufferAudio received %d \n",arc4random_uniform(100));
                                if (writingStarted){
                                    if (self->audioInput.isReadyForMoreMediaData)
                                    {
                                            [self->audioInput appendSampleBuffer:sampleBuffer];
                                    }
                                }
                            }

                        }
                    });

                }

Кроме того, когда возникает такая ситуация, системный рекордер экрана также повреждается. При нажатии на системный рекордер появляется эта ошибка:

ошибка медиасервиса

В сообщении об ошибке говорится: "Запись экрана остановлена ​​из-за: сбоя во время записи из-за ошибки Mediaservices".

Там должно быть две причины:

  1. iOS Replay Kit находится в бета-версии, поэтому он иногда вызывает проблемы.
  2. Я реализовал любую проблемную логику, которая приводит к сбою replay kit.

Если это проблема нет. 1, то без проблем. Если это проблема нет. 2 тогда я должен знать, где я могу ошибаться?

Мнения и помощь будут оценены.

4 ответа

Итак, я столкнулся с некоторыми сценариями, когда Replay kit полностью вылетает и системный рекордер выдает ошибку каждый раз, пока вы не перезапустите устройство.

1-й сценарий

Когда вы начинаете запись и останавливаете ее в обработчике завершения

[RPScreenRecorder.sharedRecorder startCaptureWithHandler:^(CMSampleBufferRef  _Nonnull sampleBuffer, RPSampleBufferType bufferType, NSError * _Nullable error) {
    printf("recording");
} completionHandler:^(NSError * _Nullable error) {
    [RPScreenRecorder.sharedRecorder stopCaptureWithHandler:^(NSError * _Nullable error) {
        printf("Ended");
    }];
}];

2-й сценарий

Когда вы начинаете запись и останавливаете ее прямо в обработчике захвата

__block BOOL stopDone = NO;
[RPScreenRecorder.sharedRecorder startCaptureWithHandler:^(CMSampleBufferRef  _Nonnull sampleBuffer, RPSampleBufferType bufferType, NSError * _Nullable error) {
    if (!stopDone){
        [RPScreenRecorder.sharedRecorder stopCaptureWithHandler:^(NSError * _Nullable error) {
            printf("Ended");
        }];
        stopDone = YES;
    }
    printf("recording");
} completionHandler:^(NSError * _Nullable error) {}];

Другие сценарии еще предстоит выяснить, и я буду обновлять ответ

Обновление 1

Это правда, что записанный системный экран выдает ошибку, когда мы прекращаем запись сразу после запуска, но, похоже, он работает нормально после того, как мы снова вызываем startcapture.

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

Обновление 2

Итак, вот в чем проблема: мое настоящее приложение устарело, оно поддерживается и своевременно обновляется. Когда replaykit становится ошибочным, Мое оригинальное приложение не может получать видеобуферы, я не знаю, есть ли конфигурация, которая делает это, или что?

Но новое приложение-образец, кажется, работает нормально и после повторного набора становится ошибочным. когда я в следующий раз вызываю startCapture, набор для воспроизведения становится нормальным. странный

Обновление 3

Я заметил новую проблему. Когда появляется предупреждение о разрешении, приложение переходит в фоновый режим. Поскольку я кодировал это, когда приложение переходит в фоновый режим, происходят некоторые изменения в пользовательском интерфейсе, и запись будет остановлена. Это привело к ошибке

Запись прервана многозадачностью и изменением размера контента

Я еще не уверен, какое именно изменение пользовательского интерфейса создает этот сбой, но это происходит только тогда, когда появляется предупреждение о разрешении и вносятся изменения в пользовательский интерфейс. Если кто-то заметил какой-либо конкретный случай по этой проблеме, пожалуйста, сообщите нам.

В videoOutputSettings делать AVVideoWidthKey & AVVideoHeightKeyNSNumber вместо NSString,

В audioOutputSettings Удалить AVEncoderBitDepthHintKey & AVChannelLayoutKey. добавлять AVEncoderBitRateKey с NSNumber64000 и изменить AVFormatIDKey значение для kAudioFormatMPEG4AAC замена kAudioFormatAppleLossless,

В моем проекте я столкнулся с подобной проблемой. Насколько я помню, проблема заключалась в моих настройках вывода.

Вы также можете попробовать переместить весь свой код в startCaptureWithHandler блок успеха внутри синхронного блока.

dispatch_sync(dispatch_get_main_queue(), ^ {
    // your block code
}

Если экран не имеет изменений, ReplayKit не вызывает processSampleBuffer() с видео. Например, в презентации PowerPoint processSampleBuffer() вызывается только при показе нового слайда. Никакая функция processSampleBuffer() с видео не вызывается в течение 10 секунд или 1 минуты. Иногда Replaykit не вызывает processSampleBuffer() на новом слайде. Нет в этом случае, пользователю не хватает одного слайда. Это критично и покажет ошибку пробки.

С другой стороны, processSampleBuffer with Audio вызывается каждые 500 мс на iOS 11.4.

У меня была точно такая же проблема. Я многое изменил и снова и снова пишу код. Я наконец понял, что проблема связана с главным окном.

Если вы измените что-либо в главном окне (например, windowLevel), возврат их обратно решит проблему.

ps: Если вы спросите взаимосвязь между главным окном и комплектом воспроизведения, комплект воспроизведения запишет главное окно.

Другие вопросы по тегам