Replaykit,startCaptureWithHandler() 不在 captureHandler 中发送 Video 类型的 CMSampleBufferRef
Replaykit, startCaptureWithHandler() not sending CMSampleBufferRef of Video type in 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];
}
}
}
}
});
}
此外,当这种情况发生时,系统屏幕录像机也会损坏。单击系统记录器时,出现此错误:
错误显示“屏幕录制已停止,原因是:由于媒体服务错误导致录制失败”。
肯定有两个原因:
- iOS Replay 套件处于测试阶段,这就是为什么它有时会在使用后出现问题。
- 我已经实现了任何有问题的逻辑,这会导致 replaykit 崩溃。
如果是问题号。 1、那就没问题了。
如果这是问题号。 2 然后我必须知道我可能哪里错了?
意见和帮助将不胜感激。
如果屏幕没有变化,ReplayKit 不会用视频调用 processSampleBuffer()。
例如,在 PowerPoint 演示文稿中,只有在显示新幻灯片时才会调用 processSampleBuffer()。 10 秒或 1 分钟内没有调用带有视频的 processSampleBuffer()。
有时 Replaykit 不会在新幻灯片上调用 processSampleBuffer()。没有这种情况,用户丢失了一张幻灯片。这很关键并显示了阻止错误。
另一方面,processSampleBuffer with Audio 在 iOS 11.4 每 500 毫秒调用一次。
在 videoOutputSettings
中使 AVVideoWidthKey
& AVVideoHeightKey
NSNumber
而不是 NSString
.
在 audioOutputSettings
中删除 AVEncoderBitDepthHintKey
& AVChannelLayoutKey.
添加 AVEncoderBitRateKey
和 NSNumber
64000
并将 AVFormatIDKey
值更改为 kAudioFormatMPEG4AAC
替换 kAudioFormatAppleLossless
.
在我的项目中,我遇到了类似的问题。据我所知,问题出在我的输出设置上。
您还可以尝试将 startCaptureWithHandler
成功块中的所有代码移动到同步块中。
dispatch_sync(dispatch_get_main_queue(), ^ {
// your block code
}
所以,我遇到过一些 Replay 套件完全崩溃并且系统记录器每次都显示错误的情况,除非您重新启动设备。
第一个场景
当您开始记录并在完成处理程序中停止时
[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");
}];
}];
第二个场景
开始录制时直接在capture handler中停止
__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) {}];
More Scenarios are yet to be discovered and I will keep updating the
answer
更新 1
It is true that the system screen recorded gives error when we stop recording right after the start, but it seem to work alright after we call startcapture again.
I have also encountered a scenario where I don't get video buffer in my
app only and the system screen recorder works fine, will update the
solution soon.
更新 2
So here is the issue, My actual app is old and it is being maintained and getting updated timely. When the replaykit becomes erroneous, My original app can't receive video buffers, I don't know if there is a configuration that is making this happen, or what?
But new sample app seem to work fine and after replay kit becomes erroneous. when I call startCapture next time, the replay kit becomes fine.
Weird
更新 3
我发现了新问题。当出现权限警报时,应用程序将进入后台。由于我编写了代码,每当应用程序进入后台时,都会发生一些 UI 更改并且录制将停止。
这导致了
的错误
Recording interrupted by multitasking and content resizing
我还不确定,是哪个特定的 UI 更改导致了此故障,但它只会在 出现权限警报并且 UI进行了更改。
如果有人注意到此问题的任何特殊情况,请告诉我们。
我遇到了完全相同的问题。我改变了很多东西并一次又一次地编写代码。我终于明白问题的原因是关于 main window.
如果您对主要 window 进行了任何更改(例如 window 级别 ),将它们恢复原状即可解决问题。
p.s:如果问主window和replay kit的关系,replay kit记录主window.
我已经实现了 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];
}
}
}
}
});
}
此外,当这种情况发生时,系统屏幕录像机也会损坏。单击系统记录器时,出现此错误:
错误显示“屏幕录制已停止,原因是:由于媒体服务错误导致录制失败”。
肯定有两个原因:
- iOS Replay 套件处于测试阶段,这就是为什么它有时会在使用后出现问题。
- 我已经实现了任何有问题的逻辑,这会导致 replaykit 崩溃。
如果是问题号。 1、那就没问题了。 如果这是问题号。 2 然后我必须知道我可能哪里错了?
意见和帮助将不胜感激。
如果屏幕没有变化,ReplayKit 不会用视频调用 processSampleBuffer()。 例如,在 PowerPoint 演示文稿中,只有在显示新幻灯片时才会调用 processSampleBuffer()。 10 秒或 1 分钟内没有调用带有视频的 processSampleBuffer()。 有时 Replaykit 不会在新幻灯片上调用 processSampleBuffer()。没有这种情况,用户丢失了一张幻灯片。这很关键并显示了阻止错误。
另一方面,processSampleBuffer with Audio 在 iOS 11.4 每 500 毫秒调用一次。
在 videoOutputSettings
中使 AVVideoWidthKey
& AVVideoHeightKey
NSNumber
而不是 NSString
.
在 audioOutputSettings
中删除 AVEncoderBitDepthHintKey
& AVChannelLayoutKey.
添加 AVEncoderBitRateKey
和 NSNumber
64000
并将 AVFormatIDKey
值更改为 kAudioFormatMPEG4AAC
替换 kAudioFormatAppleLossless
.
在我的项目中,我遇到了类似的问题。据我所知,问题出在我的输出设置上。
您还可以尝试将 startCaptureWithHandler
成功块中的所有代码移动到同步块中。
dispatch_sync(dispatch_get_main_queue(), ^ {
// your block code
}
所以,我遇到过一些 Replay 套件完全崩溃并且系统记录器每次都显示错误的情况,除非您重新启动设备。
第一个场景
当您开始记录并在完成处理程序中停止时
[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");
}];
}];
第二个场景
开始录制时直接在capture handler中停止
__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) {}];
More Scenarios are yet to be discovered and I will keep updating the answer
更新 1
It is true that the system screen recorded gives error when we stop recording right after the start, but it seem to work alright after we call startcapture again.
I have also encountered a scenario where I don't get video buffer in my app only and the system screen recorder works fine, will update the solution soon.
更新 2
So here is the issue, My actual app is old and it is being maintained and getting updated timely. When the replaykit becomes erroneous, My original app can't receive video buffers, I don't know if there is a configuration that is making this happen, or what?
But new sample app seem to work fine and after replay kit becomes erroneous. when I call startCapture next time, the replay kit becomes fine. Weird
更新 3
我发现了新问题。当出现权限警报时,应用程序将进入后台。由于我编写了代码,每当应用程序进入后台时,都会发生一些 UI 更改并且录制将停止。 这导致了
的错误Recording interrupted by multitasking and content resizing
我还不确定,是哪个特定的 UI 更改导致了此故障,但它只会在 出现权限警报并且 UI进行了更改。 如果有人注意到此问题的任何特殊情况,请告诉我们。
我遇到了完全相同的问题。我改变了很多东西并一次又一次地编写代码。我终于明白问题的原因是关于 main window.
如果您对主要 window 进行了任何更改(例如 window 级别 ),将它们恢复原状即可解决问题。
p.s:如果问主window和replay kit的关系,replay kit记录主window.