会議のスクリーンキャスト:スクリーンキャストデータストリーム取得スキーム

31325 ワード

目次

  • -異なるシステムバージョンの進化
  • 1. RPScreenRecorder
  • 2. Extensionはリアルタイムストリームデータのリターン
  • を実現する
  • Extension呼び出しにどのように触れますか?
  • -注意事項
  • 1. Extension 50 Mメモリ制限
  • 2. Extension論理処理の時間制限
  • 3. 同期スレッドキュー実行符号化
  • 4. RPSystemBroadcastPickerView
  • -開発中の経験
  • 1. Extension
  • のデバッグ方法
  • 2. 奇妙な現象をどう処理するか
  • 3.SampleHandlerは異なるインタフェースで、返されるストリームの状況が異なります.

  • -異なるシステムバージョンの進化


    ここではiosを例に挙げるだけで、後で他のプラットフォームでの実現方法を別々に書く時間があります.アップルはこの方面でずっと消極的で緩やかな進化状態にあり、いつも開発者に少し巧みな婦人がやりにくい感じを与えている.与えられた実装スキームであっても,種々の不安定性のため,実際の呼び出し中に安定して実行できない.後で一つ一つ説明します.
    iosにおける比較的正規の実装方式はExtensionで実現され,ios 9から限られた実装が提供される.ios 9以前にもプロトコルを解読することによって実現されていましたが、本稿ではExtension拡張方式で実現していますが、拡張をどのように追加するかは一つ一つ説明しません.

    1. RPScreenRecorder


    RPScreenRecorderはアップルが最初に提案したスクリーン録画案で、直接コードの一部をアップロードします.
    
    /*
     *   , , , 。
     */
     #pragma mark- start record
    -(IBAction)onStart:(id)sender{
        if([[RPScreenRecorder sharedRecorder] isAvailable] == YES){
            [[RPScreenRecorder sharedRecorder] startRecordingWithMicrophoneEnabled:YES handler:^(NSError * _Nullable error) {
                
            }];
        }
    }
    
    #pragma mark- stop record
    - (IBAction)onStop:(id)sender{
        if([[RPScreenRecorder sharedRecorder] isAvailable] == YES){
            [[RPScreenRecorder sharedRecorder]stopRecordingWithHandler:^(RPPreviewViewController * _Nullable previewViewController, NSError * _Nullable error) {
                [self presentViewController:previewViewController animated:YES completion:^{
                    
                }];
            }];
        }
    }
    
    

    上記の方法では、クリックが完了すると、システムが提供するプレビューインタフェースがモードで開きます.インタフェースでは共有を保存するなどの方法を選択できます.この方式はストリーミングのリアルタイムリターンをサポートしていないことを示しており、生放送を行うのは明らかに適切ではなく、主に後期に完全なファイルを生成して再アップロードする方法に用いられる.
    そこでios 11では、アップルがこれに基づいて一定のリアルタイムストリームを返す能力を提供している場合がありますが、実際にはこの場合、この方法で実現する必要はありません.この場合、呼び出し方法を提供します.
    
    //  
        if (@available(iOS 11.0, *)) {
            [[RPScreenRecorder sharedRecorder] startCaptureWithHandler:^(CMSampleBufferRef  _Nonnull sampleBuffer, RPSampleBufferType bufferType, NSError * _Nullable error) {
                NSLog(@"--- [ ]  %@", sampleBuffer);
            } completionHandler:^(NSError * _Nullable error) {
                NSLog(@"Recording started error %@",error);
            }];
        }
    
    
    //  
        if (@available(iOS 11.0, *)) {
            [[RPScreenRecorder sharedRecorder] stopCaptureWithHandler:^(NSError * _Nullable error) {
                
            }];
        }
        
    

    この場合、リアルタイムストリームデータの戻り問題は解決されたが、アプリケーション外のコンテンツは常に録画できない.実はこの需要を実現するには、すでに別の実現方式がある.

    2.Extensionはリアルタイムストリームデータの返却を実現する


    Extensionの追加プロセスは上図ではありません.target->add->Broadcast Upload Extension->Next->ポイント選択include UI Extensionオプション->追加完了
    このとき、システムには2つのフォルダが追加され、1つは録画前のuiディスプレイを実現するために使用される(ios 12以降、新しい起動方式に基づいて、もうコールバックされなくなった).通常、このインタフェースでいくつかの設定や過剰なインタフェースとして示すことができます.
    
    @implementation BroadcastSetupViewController
    
    // Call this method when the user has finished interacting with the view controller and a broadcast stream can start
    - (void)userDidFinishSetup {
        NSURL *broadcastURL = [NSURL URLWithString:@"http://apple.com/broadcast/streamID"];
        NSDictionary *setupInfo = @{ @"broadcastName" : @"example" };
        [self.extensionContext completeRequestWithBroadcastURL:broadcastURL setupInfo:setupInfo];
    }
    
    - (void)userDidCancelSetup {
        [self.extensionContext cancelRequestWithError:[NSError errorWithDomain:@"YourAppDomain" code:-1 userInfo:nil]];
    }
    
    @end
    
    

    注記から分かるように、**userDidFinishSetup**を呼び出し、**userDidCancelSetup**を呼び出してスクリーンを録画する前に終了する.
    上記のプロセスが完了すると、システムは録画画面に入り、リアルタイムストリームのリターンフェーズに戻ります.コールバック・コードは次のとおりです.
    
    #import "SampleHandler.h"
    
    @implementation SampleHandler
    
    - (void)broadcastStartedWithSetupInfo:(NSDictionary<NSString *,NSObject *> *)setupInfo {
        // User has requested to start the broadcast. Setup info from the UI extension can be supplied but optional. 
    }
    
    - (void)broadcastPaused {
        // User has requested to pause the broadcast. Samples will stop being delivered.
    }
    
    - (void)broadcastResumed {
        // User has requested to resume the broadcast. Samples delivery will resume.
    }
    
    - (void)broadcastFinished {
        // User has requested to finish the broadcast.
    }
    
    - (void)processSampleBuffer:(CMSampleBufferRef)sampleBuffer withType:(RPSampleBufferType)sampleBufferType {
        
        switch (sampleBufferType) {
            case RPSampleBufferTypeVideo:
                // Handle video sample buffer
                break;
            case RPSampleBufferTypeAudioApp:
                // Handle audio sample buffer for app audio
                break;
            case RPSampleBufferTypeAudioMic:
                // Handle audio sample buffer for mic audio
                break;
                
            default:
                break;
        }
    }
    
    @end
    

    Extension呼び出しにどのように触れますか?


    ios 10では、ユーザが自発的にExtensionを選択する第1の方法が提供される.
    // , 
    [RPBroadcastActivityViewController loadBroadcastActivityViewControllerWithHandler:^(RPBroadcastActivityViewController * _Nullable broadcastActivityViewController, NSError * _Nullable error) {
            [self presentViewController:broadcastActivityViewController animated:YES completion:nil];
        }];
    

    ios 11の場合、システムはさらに直接的なトリガを提供します.
    [RPBroadcastActivityViewController loadBroadcastActivityViewControllerWithPreferredExtension:@"com.ReplayKitDemo.TestSetupUI" handler:^(RPBroadcastActivityViewController * _Nullable controller, NSError * _Nullable error) {
            controller.delegate = self;
            [self presentViewController:controller animated:YES completion:nil];
        }];
    

    コードの「com.ReplayKitDemo.TestSetup UI」は、Setup UIのBoundIDに対応します.その後、アップルは上の2つの方法が直接的ではないと思っていましたが、ios 12で提案された3つ目の方法がありました.
    if (@available(iOS 12.0, *)) {
            RPSystemBroadcastPickerView *pickView = [[RPSystemBroadcastPickerView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
            pickView.preferredExtension = @"com.awesome.SRUtil.SRExtUpload";
            [self.view addSubview:pickView];
        }
    

    RPSystemBroadcastPickerViewは、直接呼び出し機能を提供するシステムが提供するコントロールです.このように呼び出されると、Setup UIを呼び出して表示するのではなく、SampleHandlerが存在する拡張プロジェクトを直接呼び出す.「com.awesome.SRUtil.SRExtUpload」は、SampleHandlerプロジェクトのBoundIDです.しかし、ios 13が発売されるまでは、この呼び出し方式はまだ安定していない.

    -注意事項


    1.Extension 50 Mメモリ制限


    プロジェクトの実践では、この50 Mのメモリ制限に対して、プッシュフロースキームで多くの方法を試みました.Tcpからマルチエンドへの同時プッシュ、UDPマルチキャスト送信まで、ほとんどのプロセスがメモリ制限をめぐって展開されます.
    TPLineのハードな指標要求のため,プッシュフローはサーバを経由して中継することができず,PTPの直接送達を実現する.従って、早期にTcpで同時マルチエンドプッシュを実現する過程で、最初の案を修正し、コールバックデータの蓄積速度がTcpマルチエンドプッシュストリームより速いという問題を解決するために、送信データキューの慣用的な実現方式を断固として放棄した.これは後のブログで一つ一つ説明します.
    後でUDP+マルチキャスト方式で送信する場合,マルチエンド同時プッシュフローの問題は確かに解決されるが,UDPに合わせて多様なエラー制御を実現する案が必要である.これは後のブログで一つ一つ説明します.
    プッシュストリームがサーバを経由して中継できないことに制限され、ローカルエリアネットワーク内の会議スクリーンシステムに使用され、UDP+マルチキャスト方式を採用することが最良の実践方式であることが実証された.

    2.Extension論理処理の時間制限


    拡張debugを行う過程で、最初は次のブレークポイントに慣れてデータのデバッグを行います.しかし、すぐに拡張がすぐに終了し、終了したことに気づきます.
    システムは拡張サポートを提供すると同時に、システムのスムーズさを維持するために最大の稼働時間を制限しています.符号化を含むすべての論理は、この最大実行時間制限の下で実行される.しかし,実際の応用では,符号化動作を行う時間に十分であることが示されている.
    だから論理設計を行う時、“processsSampleBuffer”の方法でコールバックした後の処理は多くのスレッドの処理をしなければなりません.

    3.同期スレッドキュー実行符号化

    //
    //  SampleHandler.m
    //  TPUpload
    //
    //  Created by adam on 2019/5/22.
    //  Copyright © 2019 lcs. All rights reserved.
    //
    
    - (void)processSampleBuffer:(CMSampleBufferRef)sampleBuffer withType:(RPSampleBufferType)sampleBufferType {
        switch (sampleBufferType) {
            case RPSampleBufferTypeVideo:
                [[H264Encoder shareInstance] startH264EncodeWithSampleBuffer:sampleBuffer andReturnData:^(NSData *data) {
                    [[UDPServerMain shareInstance] pushVideo:data];
                }];
                break;
            case RPSampleBufferTypeAudioApp:
                break;
            case RPSampleBufferTypeAudioMic:
                break;
            default:
                break;
        }
    }
    
    //
    //  H264Encoder.m
    //  Awesome
    //
    //  Created by adam on 2019/1/8.
    //  Copyright © 2019  common. All rights reserved.
    //
    
    
    #pragma mark- startH264EncodeWithSampleBuffer
    -(void)startH264EncodeWithSampleBuffer:(CMSampleBufferRef)sampleBuffer andReturnData:(ReturnDataBlock)block{
        self.returnDataBlock = block;
        dispatch_sync(m_EncodeQueue, ^{
            [self encode:sampleBuffer];
        });
    }
    
    
    

    上記のコードを例にとると、H 264 Encoderはハードコーディングを行い、「startH 264 EncodeWithSampleBuffer」メソッドでは、コーディング操作を行う際にdispatch_async非同期方式で呼び出されます.

    4. RPSystemBroadcastPickerView

  • がiOS 12に不安定に発展した後、アップルはついにRPSystemBroadcastPickerViewを開発とユーザーに開放し、ついにアプリケーション内でシステムを起動する録画機能を実現することができたが、実際には、呼び出されたpickerViewが、preferredExtensionを指定した場合、対応するExtensionに表示されない場合があることが分かった.
  • 外観はRPSystemBroadcastPickerViewの外観をカスタマイズすることができず、外観をカスタマイズすることができず、システムのスタイルをデフォルトで表示するしかなく、インタフェースが高度にカスタマイズされている場合、調和のとれた共存を実現することは難しい.そこで一般的には の方式を用いて,RPSystemBroadcastPickerViewをページに追加してから非表示にする.RPSystemBroadcastPickerViewは他のボタンによってトリガされ、参照コードは以下の通りである:
  • for (UIView *item in self.pickView.subviews) {
            if ([item isKindOfClass:UIButton.class] == YES) {
                actionButton = (UIButton*)item;
            }
        }
    dispatch_async(dispatch_get_main_queue(), ^{
                [self->actionButton sendActionsForControlEvents:UIControlEventTouchDown];
     });
    

    -開発の経験


    1.Extensionのデバッグ方法


    簡単に言えば、Extensionをデバッグする過程で、まず実行主プロジェクトをインストールし、SampleHandlerプロジェクトtargetを選択してスケジューリング実行し、開いているアプリケーションリストであなたのプロジェクトを選択します.
    コードが変更された後も、上記の手順を繰り返して、最新のコードが実行されていることを確認したほうがいいです.

    2.奇妙な現象をどう扱うか


    多くの奇妙な現象はいくつかの主要な要素に由来しています.
  • 長期電源を切らないこれはおそらく多くの開発者の習慣だろう
  • 長期にわたってデバッグを行うには、携帯電話の再起動、パソコンの再起動などの方法で
  • を行うことができます.
  • システムが不安定なのはxcodeの問題かもしれないしapiの問題かもしれないし、これらの状況は面倒だ.

  • 3.SampleHandlerは、異なるインタフェースで、返されるストリームの状況が異なります。

  • ReplayKitは、SampleHandlerprocessSampleBufferメソッドを、リスニング画面に変化があるかどうかによってコールバックする.テストでは、携帯電話によってアプリケーションによってインタフェースに表示されるprocessSampleBufferのコールバック状況が異なることが分かった.
  • はipad上で動作し、上の赤いtitleはずっとグラデーションの変化があるため、通常、コールバック方法ではずっとフローコールバックが来ています.
  • しかし、携帯電話側で動作すると、デスクトップ以外(アプリケーションを開く)の場合、アプリケーションに現在のインタフェースがないか、操作や変化がないか、ユーザーがタッチ操作をしていないか、ストリームのコールバックが停止します.だから、多くの人はスクリーンデータが少ないと思って、多くの人は直接フレームごとに画像に変えて再生端に伝えて、実はこれは1つの誤区です.IBPフレームを有効に利用することで、ネットワークトラフィックや画面のスムーズさを効果的に低減できます.
  • であるため、プッシュ・ストリーム・サービス・エンドでは、最後のIBPフレーム・グループとsps,ppsデータをキャッシュして、新しいアクセス・エンド・アクセスがある場合、IBPフレームのプッシュ・ストリームのためにホワイト・スクリーンが発生しないという気まずい状況を解決することが望ましい.これも秒開を実現する方法の一つである.