diff --git a/README.md b/README.md index 01a2f46..ebaa218 100644 --- a/README.md +++ b/README.md @@ -274,7 +274,7 @@ Also, if you use SkiaSharp, you can pass `ArraySegment` directly using `SK The following is a list of methods for acquiring image data described up to this point: -| method | speed | out of scope | image type | +| Method | Speed | Out of scope | Image type | |:-----------------|:----------|:--------|:---------------------| | `CopyImage()` | Slow | Safe | `byte[]` | | `ExtractImage()` | Slow in some cases | Danger | `byte[]` | @@ -308,7 +308,7 @@ using var device = await descriptor0.OpenAsync( }); ``` -### About transcoder +## About transcoder The "raw image data" obtained from a device may not be a JPEG or DIB bitmap, which we can easily handle. Typically, video format is called "MJPEG" (Motion JPEG) or "YUV" if it is not a continuous stream such as MPEG. @@ -337,13 +337,47 @@ using var device = await descriptor0.OpenAsync( // ... ``` ----- +## Callback handler and invoke trigger + +The callback handlers described so far are invoked "when a frame is obtained," but this trigger can be selected from several patterns. +This choice can be made with the `HandlerStrategies` enumeration value, which is specified with the overloaded argument of `OpenAsync`: + +```csharp +// Specifies the trigger for invoking the handler: +using var device = await descriptor0.OpenAsync( + descriptor0.Characteristics[0], + true, + HandlerStrategies.Scattering, // Specifying the invoking trigger + async buferScope => + { + // ... + }); + +// ... +``` -## About callback handler and strategies +The following is a list of pattern types: -TODO: rewrite to what is handler strategies. +| Enumeration value | Summary | +|:----|:----| +| `IgnoreDropping` | Default call trigger. Ignore subsequent frames unless the handler returns control. Suitable for general usage. | +| `Queuing` | Subsequent frames are stored in a queue even if the handler does not return control. If computer performance is sufficient, frames are not lost. | +| `Scattering` | Handlers are processed in parallel by multi-threaded workers. Although the order of corresponding frames is not guaranteed, processing can be accelerated if the CPU supports multiple cores. | ----- +The name `IgnoreDropping` seems ominous. +However, this default invocation trigger is appropriate for many cases. +For example, if you choose `Queuing` and the handler is slow, the queue will hold an endless amount of image data, and the process will eventually run out of memory and terminate. +Processing beyond the capability of the computer's processor requires a compromise somewhere. +`IgnoreDropping` can easily handle this situation by intentionally discarding frames that occur when processing is not complete. + +Similarly, `Scattering` is more difficult to master. +The handler you write will be called and processed simultaneously in multi-threaded. +Therefore, at the very least, your handler should be implemented in such a way that it is thread-safe. +Also, multi-threaded invocation means that the buffers to be processed may not necessarily remain in order. +For example, if the handler is a UI display handler, using `Scattering` will cause the video to momentarily go back in time or feel choppy. + +To deal with the frame order confusion with `Scattering`, the `PixelBuffer` class has a `Timestamp` property and a `FrameIndex` property. +By referring to these properties, the order of the frames can be determined even if the order of the frames is not maintained. ## Master for frame processor (Advanced topic) diff --git a/README_ja.md b/README_ja.md index 7e3ada6..512a857 100644 --- a/README_ja.md +++ b/README_ja.md @@ -290,7 +290,7 @@ using var device = await descriptor0.OpenAsync( }); ``` -### トランスコードについて +## トランスコードについて デバイスから得られた「生の画像データ」は、私たちが扱いやすい、JPEGやDIBビットマップではない場合があります。一般的に、動画形式の画像データは、MPEGのような連続ストリームではない場合、"MJPEG" (Motion JPEG)や"YUV"と呼ばれる形式です。 @@ -298,7 +298,7 @@ using var device = await descriptor0.OpenAsync( そこで、FlashCapは、"YUV"形式の画像データの場合は、自動的に"RGB" DIB形式に変換します。この処理の事を「トランスコード」と呼んでいます。先ほど、`ReferImage()`は「基本的にコピーが発生しない」と説明しましたが、"YUV"形式の場合は、トランスコードが発生するため、一種のコピーが行われます。(FlashCapはトランスコードをマルチスレッドで処理しますが、それでも画像データが大きい場合は、性能に影響します。) -もし、画像データが"YUV"であっても、そのままで問題ないのであれば、トランスコードを無効化することで、コピー処理を完全に1回のみにする事が出来ます: +もし、画像データが"YUV"形式であっても、そのままで問題ないのであれば、トランスコードを無効化することで、コピー処理を完全に1回のみにする事が出来ます: ```csharp // トランスコードを無効にしてデバイスを開く: @@ -313,13 +313,37 @@ using var device = await descriptor0.OpenAsync( // ... ``` ----- +## コールバックハンドラと呼び出し契機 + +これまで説明してきたコールバックハンドラは、呼び出される契機が「フレームが得られた時」としていましたが、この契機をいくつかのパターンから選択する事が出来ます。この選択は、`HandlerStrategies`列挙値で指定可能で、`OpenAsync`のオーバーロード引数で指定します: + +```csharp +// ハンドラの呼び出し契機を指定する: +using var device = await descriptor0.OpenAsync( + descriptor0.Characteristics[0], + true, + HandlerStrategies.Scattering, // 呼び出し契機の指定 + async buferScope => + { + // ... + }); + +// ... +``` -## コールバックハンドラと処理方法 +以下に、パターンの種類を示します: -TODO: rewrite to what is handler strategies. +| 列挙値 | 概要 | +|:----|:----| +| `IgnoreDropping` | デフォルトの呼び出し契機。ハンドラが制御を返さない限り、後続のフレームを無視する。一般的な使用方法に最適。 | +| `Queuing` | ハンドラが制御を返さない場合でも、後続のフレームはキューに蓄えられる。コンピューターの性能が十分であれば、フレームを失わない。 | +| `Scattering` | ハンドラは、マルチスレッドワーカーによって、並列処理される。対応するフレームの順序が保障されないが、CPUがマルチコアに対応していれば、処理を高速化出来る。 | ----- +`IgnoreDropping`の名前が、不吉なもののように思えます。しかし、このデフォルトの呼び出し契機は、多くの場合にとって適切です。例えば、`Queuing`を選択した場合、ハンドラの処理が遅いと、キューに際限なく画像データが保持されてしまい、いつかプロセスがメモリ不足で強制終了する事になります。コンピューターのプロセッサの能力を超える処理は、どこかで妥協が必要です。`IgnoreDropping`は、処理が完了しない時に発生するフレームを、わざと捨てることによって、この状況に容易に対処できます。 + +同様に、`Scattering`を使いこなすのは、より難しくなります。あなたが書いたハンドラは、マルチスレッドで同時に呼び出されて、処理されます。従って、少なくともハンドラはスレッドセーフとなるように実装する必要があります。また、マルチスレッドで呼び出されるという事は、処理するバッファは、必ずしも順序が維持されない可能性があるという事です。例えば、UIに表示するハンドラであった場合、`Scattering`を使うと、動画が一瞬過去に戻ったり、ぎこちなく感じるはずです。 + +`Scattering`でフレームの順序が分からなくなる事に対処するため、`PixelBuffer`クラスには、`Timestamp`プロパティと`FrameIndex`プロパティがあります。これらのプロパティを参照すれば、フレームの順序は維持されなくても、順序の判定は行う事が出来ます。 ## フレームプロセッサをマスターする (Advanced topic)