メイン・コンテンツまでスキップ

MJPEGビデオ

TouchGFXバージョン4.18から、MJPEGビデオの使用がサポートされています。 ビデオを使用して、より生き生きとしたユーザ・インタフェースを作成したり、簡単な手順やユーザガイドを表示したりすることができます。

ビデオはVideoウィジェットを介してユーザ・インタフェースで表示されます。 このウィジェットは他のウィジェットと同じように、TouchGFX Designerで使用でき、ユーザ・インタフェースに追加できます。

Note
オーディオはサポートされていません。 したがって、オーディオ・データをビデオ・データから削除することをお勧めします。

TouchGFX DesignerでのVideoウィジェットの使用

STM32マイクロコントローラでビデオをデコードするには、追加のサポート・ソフトウェアが必要です。 このソフトウェアは、TouchGFX Generatorでビデオ・サポートを有効にすることで、プロジェクトに簡単に含めることができます。 ビデオが有効になっているTouchGFX Board Setup(下のリストを参照)では、通常どおりRun Target(F6)を押すことで、ターゲット上でビデオを簡単に実行できます。

STM32F746DiscoveryによるVideoウィジェットの使用

ターゲット・コードでビデオがサポートされていない場合は、コンパイル・エラーまたはリンカ・エラーが表示されます。

MJPEGビデオ

MJPEGビデオは、コンテナ・ファイル(.avi)にパッキングされた多数のJPEG画像(フレーム)で構成されます。 JPEGフレームは圧縮されているため、フレームバッファに直接コピーすることができません。 ディスプレイに表示できるようにするには、あらかじめ個々のフレームをRGBフォーマット(16、24、または32ビット)に圧縮解除する必要があります。

この圧縮解除は、RGBビットマップを使用する場合と比べて計算に時間がかかり、パフォーマンス(1秒当たりのフレーム数)を低下させます。

JPEG圧縮の利点は、データのサイズが大きく削減されることです。

上のスクリーンショットで使用されているビデオは240 x 135ピクセルです。 つまり、16ビットRGBフォーマットの各フレームは、240 x 135 x 2バイト、つまり64,800バイトを必要とします。 ビデオには178フレーム(およそ7秒間)が含まれています。 したがって、ビットマップで保存されているビデオの合計サイズは、178 x 64,800バイト= 11,534,400バイトになります。 MJPEG AVIファイルはわずか1,242,282バイトで、ビットマップの10.7%です。

このサイズ削減により、MJPEGビデオ・ファイルは短連続のビデオに非常に役立つものとなっています。

サイズの削減によって、圧縮に少し乱れ(エラー)が生じます。 これらは現実世界の映像の多くで許容されるものですが、高コントラスト・グラフィックスでは許容されません。

一部のSTM32マイクロコントローラでは、JPEG画像のハードウェア・アクセラレーションによるデコードがサポートされています(STM32F769やSTM32H750など)。 これによりJPEGのデコード速度がアップし、ビデオの可能なフレームレートが向上します。

JPEGフレームのデコード時間は簡単に16ミリ秒を超える可能性があります(マイクロコントローラとビデオの解像度によります)。 つまり、ほとんどの場合、MJPEGビデオのデコード速度はユーザ・インタフェースの通常のフレームレートを下回るのです。 一部のアプリケーションでは、全体のフレームレートがデコード速度より低いことが許容されます。 しかしそれ以外では、たとえばビデオが20 fpsで実行される場合でも、ユーザ・インタフェースのフレームレートを60 fpsに維持する必要があります。 例として、ビデオの横に進捗状況を示す円のアニメーションが表示されるアプリケーションが挙げられます。 ビデオが20 fpsで新しいフレームを表示する場合でも、60 fpsでアニメーションが実行されると、円は最適な表示になります。

STM32F746による上の例では、個々のJPEGフレームのデコードに18 ~20ミリ秒使っています。

TouchGFXにおけるビデオの使用

TouchGFXでは、ビデオをユーザ・インタフェースの一部として簡単に含めることができます。 このためには、Videoウィジェット、VideoController、そして(当然ながら)MJPEGビデオ・ファイルの3つが必要です。

Videoウィジェットは、他のすべてのウィジェットと同様にユーザ・インタフェース内で使用されます。 ビデオ・コントローラは、完全なTouchGFX環境(HAL、オペレーティング・システム、ドライバなど)を構成する低レベル・ソフトウェアの一部です。

Videoウィジェットとビデオ・コントローラ

VideoControllerは、MJPEGファイルのデコードとバッファ管理を制御するソフトウェアで構成されます。

TouchGFX Designerはすべてのシミュレータ・プロジェクトにビデオ・コントローラを自動的に含めます。 これにより、シミュレータ・プロトタイプでビデオを非常に簡単に使用できるようになります。Videoウィジェットを追加し、ビデオ・ファイルを選択して、"Run Simulator"(F5)を押すだけです。).

ビデオをハードウェア上で使用するには、ターゲット・プロジェクト(IAR、Keil、arm-gcc、CubeIDE)内にもビデオ・コントローラが必要です。 これは一部のTouchGFX Board Specificationパッケージにはすでに追加されていますが(下のリストを参照)、TouchGFX Generatorによって、任意のプロジェクトにビデオ・サポートを追加することができます。 Generatorユーザガイドを参照してください。

ビデオが有効化されたプラットフォームを使用している場合は、DesignerでVideoウィジェットを簡単に追加して構成できます。 DesignerでのVideoウィジェットの使用方法については、こちらをご覧ください。

TouchGFXプロジェクトのビデオ・ファイル

TouchGFX Designerにビデオ・ファイルを含めるときには、.aviファイルがassets/vidoesフォルダにコピーされます。 コード生成中に、ビデオが.binファイルとしてgenerated/videos/binに、.oファイルとしてgenerated/videos/objにコピーされます。 .oと.binには同じデータが含まれますが、.oファイルは(一部のコンパイラやIDEで推奨される)ELFフォーマットです。

コードの生成時に実行されるプロジェクト・アップデータにより、ターゲット・プロジェクトにビデオ・ファイルが含められます。 つまり、ビデオ・ファイルは実行可能ファイルにリンクされており、アプリケーションで使用できるようになっています。

アプリケーション・プログラマは、その他のビデオをassets/videosフォルダに追加できます。 これらもプロジェクトに含められます。

generated/videos/include/videos/VideoDatabase.hppファイルには、アプリケーションにコンパイルされたビデオに関するシンボル的な情報が含まれます。

const uint32_t video_SampleVideo1_240x135_bin_length = 1242282;
#ifdef SIMULATOR
extern const uint8_t* video_SampleVideo1_240x135_bin_start;
#else
extern const uint8_t video_SampleVideo1_240x135_bin_start[];
#endif

これらの宣言を使用して、ユーザ・コード内でVideoウィジェットにビデオを割り当てることができます。

ユーザ・コードからのビデオ・ファイルの使用

プロジェクトによっては、TouchGFX Designer内でビデオを選択するだけでは十分でない場合があります。 たとえば、 起動時に別のビデオを選択したい場合などです。 最初に、そのビデオ・ファイルをassets/videosに追加する必要があります。

assets/videosにビデオを追加

assets/videosフォルダ内のビデオ・ファイルは、コードの生成時(またはmake assetsの実行時)にVideoDatabase.hppに含められます。

const uint32_t video_myVideo_bin_length = 1242282;
#ifdef SIMULATOR
extern const uint8_t* video_myVideo_bin_start;
#else
extern const uint8_t video_myVideo_bin_start[];
#endif

これで、ユーザ・コード内でこれらの宣言を使用して、動画を再生するためのVideoウィジェットを取得できます。

Screen1View.cpp
#include <gui/screen1_screen/Screen1View.hpp>
#include <videos/VideoDatabase.hpp>

Screen1View::Screen1View()
{
}

void Screen1View::setupScreen()
{
Screen1ViewBase::setupScreen();

video.setVideoData(video_myVideo_bin_start, video_myVideo_bin_length);
video.setWidthHeight(240, 136);
video.play();
}

ビデオ・データがアプリケーションにリンクされたことに注目してください。 これを避けるには、ビデオをassets/videos内に配置せず、外部Flashの専用領域にビデオを手動で書き込みます。 次に、Flashアドレスを使用してアドレスと長さのみを渡します。

void Screen1View::setupScreen()
{
...
video.setVideoData((const uint8_t*)0xA0200000, 1242282);
...
}

制限事項

TouchGFXは、縦向きモードのビデオ・デコーディングをサポートしていません。

ビデオ対応の開発キットのリスト

TouchGFX Designerで使用できるTouchGFX Board Setupパッケージ内で、以下の開発キットはデフォルトでビデオに対応しています。

  • STM32F769Discovery(ハードウェア・アクセラレーションによるデコード)
  • STM32H750BDiscovery(ハードウェア・アクセラレーションによるデコード)
  • STM32U5F9Discovery(ハードウェア・アクセラレーションによるデコード)
  • STM32F746Discovery(ソフトウェア・ベースのデコード)

別の開発キットまたはカスタム・ハードウェアを使用する場合には、TouchGFX Generatorでビデオ・サポートを有効にすることを忘れないでください。

MJPEG AVIファイルの作成

ほとんどのビデオはMJPEG AVIファイルには保存されません。これはもう共通のビデオ・フォーマットではないからです。 このため、多くの場合、TouchGFXプロジェクトで使用する前にビデオ・ファイルをMJPEGフォーマットに変換する必要があります。

変換は、FFMPEGなどを使用して簡単に実行できます。 その他のアプリケーションやオンライン・サービスも使用できます。

FFMPEGの使用

FFMPEGのWindowsバイナリは、こちらにあります。

ビデオ・ファイルinput.movをMJPEGに変換するには、次のコマンドを使用します。

ffmpeg -i input.mov -s 480x272 -vcodec mjpeg -qscale 1 -an output.avi

MJPEGビデオはoutput.aviファイルにあります。 このファイルをassets/videosにコピーできます。

ビデオの正確なアスペクト比を維持するために、幅をピクセル単位で指定(ここでは480)し、高さを「-1」(マイナス1)に指定することができます。

ffmpeg -i input.mov -vf scale=480:-1 -vcodec mjpeg -qscale 1 -an output.avi

-ssを使用して開始時間を指定し、-tまたは-toを使用して持続時間または停止時間を指定することで、ビデオのセクションを切り取ることができます。

ffmpeg -ss 3 -i input.mov -t 3 -s 480x272 -vcodec mjpeg -qscale 1 -an output_section.avi

または

ffmpeg -ss 3 -i input.mov -to 5 -s 480x272 -vcodec mjpeg -qscale 1 -an output_section.avi
オプション説明
-s出力ビデオ解像度
-qscale1~31まで(良から悪へ)の品質スケール
-anオーディオを削除
-vfフィルタ・グラフの設定
-ss開始時間(秒)
-t持続時間(秒)
-to停止時間(秒)

デコードの戦略

上記のように、TouchGFXでは、個々のMJPEGフレームをフレームバッファ上で表示する前に、JPEGからRGBに変換する必要があります。 このデコードは、必要に応じて動作中に実行するか、次のフレームをビデオ・バッファにあらかじめデコードすることで非同期に行うことができます。

非同期デコードは、UIタスクではなく、第2タスクによって実行されます。 つまり、場合によってはこのデコードをUIタスクによる描画と並行実行できます。

TouchGFXには3つの戦略があり、それぞれに長所と短所があります。

戦略説明
Direct To Frame Buffer必要に応じて、現在のビデオ・フレームを直接フレームバッファにデコードします。
Dedicated Buffer次のビデオ・フレームをビデオ・バッファにデコードします。 ビデオ・バッファからフレームバッファにコピーします。
Double Buffer次のビデオ・フレームを2番目のビデオ・バッファにデコードします。 デコードが完了したらビデオ・バッファをスワップします。

Designerは、シミュレータ・プロジェクトに対しては常にDirect To Frame Buffer戦略を選択します。 ターゲットで使用される戦略はGeneratorで設定可能です。

次に、各戦略を詳しく説明します。

Direct To Frame Buffer

Direct To Frame Buffer戦略では、TouchGFX Engineの描画フェーズ(「Rendering」を参照)の実行時にJPEGフレームをデコードします。

フレームバッファに直接デコード

更新フェーズ(「Update」を参照)の実行時に、Videoウィジェットが動画を次のフレームに進めるべきかどうかを決定します。 その次の描画フェーズの実行時に、JPEGフレームが「ラインバッファ」に1行ずつデコードされます。 次に、フレームバッファのフォーマットと一致するようにピクセルが変換され、フレームバッファにコピーされます。

ビデオ・ウィジェットが他の塗りつぶしウィジェットによってカバーされる場合は、複数ブロック内で再描画されます(表示部分が長方形に分割されます)。

複数のブロックでデコードされた、塗りつぶしウィジェットのオーバーラップがあるビデオ・フレーム

これらのブロックの各々を描画するには、解凍作業を繰り返す必要があります。 このため、この戦略は、デコード時間が増大するため、ボタンなど他の塗りつぶしUI要素がビデオの前面に配置されるユーザ・インタフェースには不向きです。 追加で必要な解凍作業量を制限できる、設計上の方針があります。 アプローチの1つは、塗りつぶしウィジェットにいくらかの不透明度を持たせるように変更するものです。 これはブロックが1つだけに、つまりビデオ・フレーム全体に適用することになります。 このウィジェットはフレームのコンテンツとブレンドされます。

1つのブロックにデコードされた、ある程度の不透明度があるウィジェットを含むビデオ・フレーム

もう1つのアプローチは、スクリーン内の描画領域を複数のブロックに分割するSMOCアルゴリズムを無効にすることです。 無効化された場合、他の塗りつぶしウィジェットでカバーされる領域も含め、すべてのウィジェットが完全に描画されます。 これはuseSMOCDrawing()を使用して実行されます。 以下の例では、前面に塗りつぶしウィジェットがあるビデオを含むスクリーンのSMOCを無効にし、スクリーンで描画が完了したときに再度有効にします。

void Screen1View::setupScreen()
{
useSMOCDrawing(false);
Screen1ViewBase::setupScreen();
}

void Screen1View::tearDownScreen()
{
useSMOCDrawing(true);
Screen1ViewBase::tearDownScreen();
}

useSMOCDrawing()の呼出しをビデオが描画される場所近くに配置し、スクリーン内の他のウィジェットが完全に描画されるのを防ぐこともできます。

このソリューションの長所は、追加で使用されるメモリ量が非常に少ないことです。

Caution
SMOC描画アルゴリズムを無効にすると、意図せずに他のウィジェットの描画時間が長くなる場合があるため、注意して実行する必要があります。

Dedicated Buffer

Dedicated Bufferまたはシングル・バッファ・デコード戦略では、まずJPEGフレームを専用のRGBバッファにデコードして、後でそのバッファからフレームバッファにコピーします。

別のバッファにデコード

Direct戦略とは対照的に、このデコードは通常のTouchGFXタスクではなく、別のタスク内で実行されます。 Videoウィジェットは、新しい動画フレームを次のユーザ・インタフェース・ティックで表示するべきかどうかを計算し、次のフレームのデコードを開始するようデコード・タスクに信号を送ります。 このデコードの実行中は、ビデオ・バッファをフレームバッファに描画することができません(部分的に更新されるからです)。 デコードの実行中は、Videoウィジェットの再描画を防ぐためにユーザ・インタフェースがブロックされます。 描画が完了したら、続行されます。 ユーザ・インタフェースによるビデオの再描画の必要がない場合、ユーザ・インタフェースは通常どおり続行できます。

新しいビデオが完全にデコードされる場合、ビデオからフレームバッファへのレンダリングのコストは、ビットマップの描画と同じです(低コスト)。 つまりこの戦略では、ビデオ上にボタンやテキストを配置することは問題になりません。

この戦略の短所は、タスクとビデオ・バッファによってメモリが使用されることです。

Double Buffer

Double Bufferデコード戦略では2つのRGBバッファを使用します。 デコードはこれらのバッファの片方に対して行われ、フレームバッファへのレンダリングはもう一方のRGBバッファから行われます。

2つのビデオ・バッファにデコード

フレームがデコードされると、デコード・タスクは、UIがそのビデオ・バッファを表示して前のバッファを解放するのを待ちます。 次のフレームのデコードは、ユーザ・インタフェースでバッファが変更されたらすぐに開始できます。

この戦略の明らかな短所はメモリ使用量で、前の戦略の2倍が使用されます。

ビデオのフレームレートとユーザ・インタフェースのフレームレート

デコード戦略はユーザ・インタフェースのフレームレートにさまざまな影響を及ぼします。 ユーザ・インタフェースのフレームレートとは、1秒ごとにフレームバッファで生成される(異なる)フレームの数です。

Direct To Frame Buffer戦略では、ビデオのデコード速度がユーザ・インタフェースの効果的なフレームレートに容易に影響を与えます。 たとえば、1つのJPEGフレームのデコードに28ミリ秒かかるときに、毎秒20個のビデオ・フレーム(20 fps)を表示させたい場合を考えてみます。 デコード時間の合計は28 ms x 20/s = 560 ms/sになり、これは現実的です。 20 fpsは、3つ単位のフレーム(60 fps)の新しいビデオ・フレームに対応します。 つまり、3つ単位のUIフレーム内に新しいビデオ・フレームを入れようとするものの、デコード時間は描画フェーズの一部なので、このフレームのレンダリングに28ミリ秒かかり、さらに残りのユーザ・インタフェースのレンダリングにおよそ2ミリ秒かかります。 このフレームのレンダリング時間の合計は30ミリ秒で、1ティックを失います。ただし、新しいティック用の新しいフレームを生成する準備はできています。 次のこのフレームではビデオをデコードしないので、ここでのレンダリングは16ミリ秒未満となり制限に適合します。 次に、4番目のティックで2つ目のビデオ・フレームのデコードを開始できます。

20 fpsのビデオ

したがって、ビデオのフレームレートは20 fps、ユーザ・インタフェースのフレームレートは40(60が上限)になります。

結果として、UIの他の要素を60 fpsでアニメーション表示することはできません。ビデオ・デコードがフレームレートを制限するからです。

Double Bufferデコード戦略では、この点が改善されています。 ユーザ・インタフェースには、最新のフレームで使用可能なビデオ・バッファがあります。 このバッファが使用可能で、レンダリング・スレッドがアクティブに描画されていない場合、デコーダ・タスクはこのバッファを(次のフレームを含む)他のバッファとスワップできます。 スワップ後、デコード・タスクは次のフレームのデコードをすぐに開始できます。

20 fpsのビデオ

UIフレーム1と2では、最初にデコードされたビデオ・フレームがUIに表示されます。 その間にデコーダはフレーム2を生成します。 UIフレーム3では、このフレームの準備が完了し、使用されることになります。 デコーダは次のフレームのデコードを自由に開始できます(図には示されていません)。

つまり、ビデオ・デコードでは2ティック単位で新しいフレームを生成することしかできない場合でも、他の要素を含むユーザ・インタフェースを毎フレーム更新させることが可能です。

前述のように、ターゲット・プロジェクトに対するビデオ・サポートはTouchGFX Generatorで設定します。 Generatorユーザガイドを参照してください。

VideoウィジェットはDesignerで利用可能です。 DesignerでのVideoウィジェットの使用方法については、こちらをご覧ください。