web-dev-qa-db-ja.com

Replaykit、startCaptureWithHandler()がcaptureHandlerでビデオタイプのCMSampleBufferRefを送信しない

画面とマイクの音声を録音する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];
                                    }
                                }
                            }

                        }
                    });

                }

また、このような状況が発生すると、システムスクリーンレコーダーも破損します。システムレコーダをクリックすると、次のエラーが表示されます。

mediaservice error

エラーには、「次の理由で画面の記録が停止しました:Mediaservicesエラーによる記録中の失敗」と表示されます。

2つの理由が必要です。

  1. iOSリプレイキットはベータ版であるため、使用後に問題が発生することがあります。
  2. 問題のあるロジックを実装しました。これにより、リプレイキットがクラッシュします。

問題がない場合。 1、それなら問題ありません。これが問題の場合はありません。 2それでは、どこが間違っているのかを知る必要がありますか?

ご意見・ご感想をお待ちしております。

7

そのため、デバイスを再起動しない限り、リプレイキットが完全にクラッシュし、システムレコーダーが毎回エラーを表示するシナリオに遭遇しました。

最初のシナリオ

記録を開始し、完了ハンドラーで停止したとき

[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

だからここに問題があります、私の実際のアプリは古く、それは維持され、タイムリーに更新されています。リプレイキットにエラーが発生した場合、元のアプリはビデオバッファーを受信できません。これを実現している構成があるかどうか、または何がわかりませんか?

しかし、新しいサンプルアプリは正常に動作しているようで、再生キットの後にエラーが発生します。次回startCaptureを呼び出すと、リプレイキットは正常になります。 奇妙な

アップデート

新しい問題を観察しました。権限アラートが表示されると、アプリはバックグラウンドに移行します。アプリがバックグラウンドに移行するたびにUIが変更され、記録が停止するようにコーディングしたためです。これはのエラーにつながりました

マルチタスクとコンテンツのサイズ変更によって記録が中断された

どの特定のUI変更がこの失敗を引き起こしているのかはまだわかりませんが、許可アラートが表示されてUIが変更された場合にのみ発生します誰かがこの問題の特定のケースに気付いた場合は、お知らせください。

1

画面に変更がない場合、ReplayKitはビデオでprocessSampleBuffer()を呼び出しません。たとえば、PowerPointプレゼンテーションでは、processSampleBuffer()は、新しいスライドが表示されたときにのみ呼び出されます。ビデオを含むprocessSampleBuffer()は、10秒または1分間呼び出されません。 Replaykitが新しいスライドでprocessSampleBuffer()を呼び出さない場合があります。この場合、ユーザーは1つのスライドを失っています。これは重要であり、ストッパーのバグを示しています。

一方、iOS 11.4では、オーディオ付きのprocessSampleBufferが500ミリ秒ごとに呼び出されます。

0
user1418067