Skip to content

Commit

Permalink
Updated readme.
Browse files Browse the repository at this point in the history
  • Loading branch information
kekyo committed Mar 29, 2022
1 parent 82a17e9 commit 270049c
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 12 deletions.
46 changes: 40 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ Also, if you use SkiaSharp, you can pass `ArraySegment<byte>` 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[]` |
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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)

Expand Down
36 changes: 30 additions & 6 deletions README_ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -290,15 +290,15 @@ using var device = await descriptor0.OpenAsync(
});
```

### トランスコードについて
## トランスコードについて

デバイスから得られた「生の画像データ」は、私たちが扱いやすい、JPEGやDIBビットマップではない場合があります。一般的に、動画形式の画像データは、MPEGのような連続ストリームではない場合、"MJPEG" (Motion JPEG)や"YUV"と呼ばれる形式です。

"MJPEG"は、中身が完全にJPEGと同じであるため、FlashCapはそのまま画像データとして返します。対して、"YUV"形式の場合は、データヘッダ形式はDIBビットマップと同じですが、中身は完全に別物です。従って、これをそのまま "output.bmp" のようなファイルで保存しても、多くの画像デコーダはこれを処理できません。

そこで、FlashCapは、"YUV"形式の画像データの場合は、自動的に"RGB" DIB形式に変換します。この処理の事を「トランスコード」と呼んでいます。先ほど、`ReferImage()`は「基本的にコピーが発生しない」と説明しましたが、"YUV"形式の場合は、トランスコードが発生するため、一種のコピーが行われます。(FlashCapはトランスコードをマルチスレッドで処理しますが、それでも画像データが大きい場合は、性能に影響します。)

もし、画像データが"YUV"であっても、そのままで問題ないのであれば、トランスコードを無効化することで、コピー処理を完全に1回のみにする事が出来ます:
もし、画像データが"YUV"形式であっても、そのままで問題ないのであれば、トランスコードを無効化することで、コピー処理を完全に1回のみにする事が出来ます:

```csharp
// トランスコードを無効にしてデバイスを開く:
Expand All @@ -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)

Expand Down

0 comments on commit 270049c

Please sign in to comment.