1

メソッドで sampleBuffers の処理を​​選択的に (そして確実に) オフにする必要がありますcaptureOutput:didOutputSampleBuffer:fromConnection:。ご存じのように、メイン スレッドではなく GCD キューから呼び出されます...しかし、UIButton (もちろんメイン スレッド上) からユーザー入力を取得し、BOOL フラグを設定してすべての処理を停止するようにカメラ オブジェクトに指示しています。 .

ただし、処理が停止したと思われる後に、1 つの余分なフレームが亀裂をすり抜けてしまうことがあります。ボタンが押された後に何も処理されないことを確実にする方法はありますか? 現在、私は簡単なテストを行っています:

// in ViewController:
- (IBAction)tappedStop:(id)sender {
    NSLog("stop processing!");
    _camera.capturing = NO;
}

// in my camera obj:
- (void)captureOutput:(AVCaptureOutput *)captureOutput did... {
    if (!capturing) {
        return;
    }
    NSLog(@"processing!");
}

私は@synchronized、静的BOOL、およびセマフォを使用してみましたが、役に立ちません...時々、その余分なフレームがまだ忍び込んでいます.誰かがアイデアを持っていますか? おそらく、私が望むことを行うGCDメソッドがいくつかありますが、どうすればよいかわかりません。

デバッグ コンソールで結果が (場合によっては) どのように表示されるかを次に示します (読みやすくするために短縮されています)。

2012-09-29 23:29:01.869 __33-_block_invoke_0 [Line 322] processing!
2012-09-29 23:29:01.910 __33-_block_invoke_0 [Line 322] processing!
2012-09-29 23:29:01.953 __33-_block_invoke_0 [Line 322] processing!
2012-09-29 23:29:01.994 __33-_block_invoke_0 [Line 322] processing!
2012-09-29 23:29:02.047 __33-_block_invoke_0 [Line 322] processing!
2012-09-29 23:29:02.078 __33-_block_invoke_0 [Line 322] processing!
2012-09-29 23:29:02.121 __33-_block_invoke_0 [Line 322] processing!
2012-09-29 23:29:02.166 -[ViewController tappedButton:] [Line 913] stop processing!
2012-09-29 23:29:02.161 __33-_block_invoke_0 [Line 322] processing!
...

しかし、通常 (5 回のうち約 4 回) 私のコンソールは次のようになります。

2012-09-29 23:29:01.869 __33-_block_invoke_0 [Line 322] processing!
2012-09-29 23:29:01.910 __33-_block_invoke_0 [Line 322] processing!
2012-09-29 23:29:01.953 __33-_block_invoke_0 [Line 322] processing!
2012-09-29 23:29:01.994 __33-_block_invoke_0 [Line 322] processing!
2012-09-29 23:29:02.047 __33-_block_invoke_0 [Line 322] processing!
2012-09-29 23:29:02.078 __33-_block_invoke_0 [Line 322] processing!
2012-09-29 23:29:02.121 __33-_block_invoke_0 [Line 322] processing!
2012-09-29 23:29:02.166 -[ViewController tappedButton:] [Line 913] stop processing!

captureOutput:didOutput...また、呼び出し元のキューは制御できないフレームワークのスーパークラスにあるため、アクセスできないことにも言及する必要があります。

4

3 に答える 3

1

なぜこれをもっと早く考えなかったのかはわかりませんが、captureOutput:didOutput... メソッド全体をメインスレッドへの非同期呼び出しでラップすると、理想的ではないように思えますが、sampleBuffer 処理をオフにすると次のように機能します。期待される。理にかなっていると思います。私のタッチイベントは常にメインスレッドから発生し、バッファ出力キューで使用されたスレッドのハンドルを持っていないため、 isRecording を確実にチェックする唯一の方法 (私が見ることができる) は、メインスレッドから行うことです。スレッドも。

- (void)captureOutput:(AVCaptureOutput *)captureOutput did... {
    dispatch_async(dispatch_get_main_queue(), ^{
        if (!isRecording) {
            return;
        }

        NSLog(@"processing!");

        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
            // do actual processing
        });
    });
}

他に何か提案があれば、ぜひ聞いてみたいです。ありがとう!

于 2012-09-30T07:33:25.240 に答える
0

カメラクラスにローカルなシリアルディスパッチキューを使用して、一種のミューテックスとして機能させることができます。基本的な考え方は、同期する必要のあるすべての作業がそのキューにディスパッチされることです。つまり、キャプチャ変数の設定(および場合によっては取得)と、コールバックで行われる作業です。これには、すべての同期作業をカメラ自体に移動する必要がありました。コードが順番に実行されたかどうかを正確に検出できるように、同期作業の一部としてログを保持しています。60 fpsのビデオキャプチャをシミュレートするために、1秒間に60回実行すると正常に動作するようです。

Camera.h:

@interface Camera : NSObject

@property (nonatomic,getter = isCapturing) BOOL capturing;

@end

Camera.m

#define CALLBACK_INTERVAL (1.0/60.0)

@implementation Camera {
    dispatch_queue_t _sync_queue;
    BOOL _capturing;
}

- (id)init
{
    if (self = [super init])
    {
        _capturing = YES;
        _sync_queue = dispatch_queue_create("com.mycompany.whatever", NULL);
        [self performSelector:@selector(triggerCallback) withObject:nil afterDelay:CALLBACK_INTERVAL];
    }

    return self;
}


- (void)setCapturing:(BOOL)capturing
{
    dispatch_async(_sync_queue, ^{
        _capturing = capturing;
        if (!_capturing)
            NSLog(@"STOP");
    });
}

- (void)repeatingCallback
{
    dispatch_async(_sync_queue, ^{
        if (!_capturing)
            return;
        NSLog(@"WORKING");
    });
}

- (void)triggerCallback
{
    [NSObject cancelPreviousPerformRequestsWithTarget:self];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [self repeatingCallback];
    });
    [self performSelector:@selector(triggerCallback) withObject:nil afterDelay:CALLBACK_INTERVAL];
}

@end

そして、View Controllerでは:

- (IBAction)stopCapturing:(id)sender
{
    self.camera.capturing = NO;
}

これがお役に立てば幸いです。ご不明な点がありましたらお知らせください。考慮すべきことの1つは、コールバックの頻度と比較して処理作業にかかる時間です。キューに大量の作業が蓄積されるまでにかなり長い時間がかかる場合は、_capturingタップ後に変更されるまでに時間がかかることがあります。これは受け入れられないように聞こえますが、その時点でキューにない処理は停止する必要があります。タップされました。

于 2012-10-02T00:47:25.297 に答える
0

私はこれをするだろう

// in ViewController:
- (IBAction)tappedStop:(id)sender {
    NSLog("stop processing!");
    dispatch_async(capture_dispatch_queue, ^{_camera.capturing = NO;});
}

これにより、ブロックがシリアル ディスパッチ キューに挿入されます。それ以上の画像処理ブロックが処理される前に実行されます。現時点では、_camera.capturing 変数で競合状態が発生しています。これは、変数のセットをキャプチャ コールバックに関してシリアルに実行することにより、競合状態を解決します。

編集:

おそらく、captureOutput:didOutput... を呼び出している元のキューにアクセスできないことにも言及する必要があります。これは、制御できないフレームワークのスーパークラスにあるためです。

私はその部分を逃した。それは大変です。技術的には、Objective-C ランタイムを介してアクセスできます。ただし、その iVar が名前を変更すると、コードが壊れます。おそらく、あなたの状況では、あなたが考案したソリューションが最適です。でも考えてみます。良い問題です。

于 2012-10-01T23:57:27.783 に答える