8

我已经实现了一个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 套件处于测试阶段,这就是为什么它在使用后有时会出现问题。
  2. 我已经实现了任何有问题的逻辑,这会导致 replaykit 崩溃。

如果是问题没有。1,那就没问题了。如果这是问题号。2 那么我必须知道我可能错在哪里?

意见和帮助将不胜感激。

4

4 回答 4

3

因此,我遇到了一些情况,其中重播套件完全崩溃并且系统记录器每次都显示错误,除非您重新启动设备。

第一种情况

当您开始录制并在完成处理程序中停止它时

[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");
    }];
}];

第二个场景

当您开始录制并直接在捕获处理程序中停止它时

__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

我观察到新问题。当权限警报出现时,应用程序进入后台。由于我编写了每当应用程序进入后台时,都会发生一些 UI 更改并停止录制。这导致了错误

录制因多任务处理和内容大小调整而中断

我还不确定哪个特定的 UI 更改导致了此失败,但它仅在出现权限警报并进行 UI 更改时出现。 如果有人注意到此问题的任何特殊情况,请告诉我们。

于 2018-09-13T05:31:59.857 回答
1

videoOutputSettingsmake AVVideoWidthKey&AVVideoHeightKey NSNumber而不是NSString.

audioOutputSettingsremove AVEncoderBitDepthHintKey& AVChannelLayoutKey.Add AVEncoderBitRateKeywithNSNumber 64000并将AVFormatIDKey值更改为kAudioFormatMPEG4AACreplace kAudioFormatAppleLossless

在我的项目中,我遇到了类似的问题。据我所知,问题出在我的输出设置上。

您还可以尝试在startCaptureWithHandler同步块内移动成功块中的所有代码。

dispatch_sync(dispatch_get_main_queue(), ^ {
    // your block code
}
于 2018-09-03T10:51:00.130 回答
1

如果屏幕没有变化,ReplayKit 不会用视频调用 processSampleBuffer()。例如,在 PowerPoint 演示文稿中,仅在显示新幻灯片时才调用 processSampleBuffer()。10 秒或 1 分钟内没有调用带有视频的 processSampleBuffer()。有时 Replaykit 不会在新幻灯片上调用 processSampleBuffer()。没有这种情况,用户缺少一张幻灯片。这是关键并显示停止错误。

另一方面,在 iOS 11.4 上,每 500 毫秒调用一次带有音频的 processSampleBuffer。

于 2018-07-23T23:38:38.507 回答
0

我有完全相同的问题。我改变了很多东西,一次又一次地编写代码。我终于明白问题的原因是关于主窗口

如果您更改有关主窗口的任何内容(例如windowLevel),将它们恢复回来将解决问题。

ps:如果问主窗口和replay kit的关系,replay kit记录的是主窗口。

于 2021-05-04T18:57:42.830 回答