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

ビデオ・デコーディング

このセクションでは、ビデオ・デコーディング機能を備えたTouchGFX HALを生成するためのTouchGFX Generatorの設定方法について説明します。

このシナリオを読む前に、TouchGFXでのMJPEGビデオの使用に関するドキュメントおよびビデオ・ウィジェット自体に関するドキュメントに目を通してください。

次のシナリオでは、ソフトウェア(LibJPEG)またはハードウェア(JPEG)のいずれかを使用してTouchGFX HALでビデオ・デコーディングをサポートできるようにする方法について詳しく説明します。

特定のプロジェクト向けにマイクロコントローラを設定する前に、必ずこの記事のすべてのセクションをお読みください

通常、LibJPEG設定とJPEG設定は、STM32CubeMXの[Middleware]および[Multimedia]カテゴリに含まれています。

STM32CubeMXのJPEG設定とLibJPEG設定

Tip
  • TouchGFX DesignerのTouchGFX Board Setup(TBS)のいくつかは、MJPEGビデオ・デコーディングをサポートしています。
  • ユーザ固有のマイクロコントローラ上でMJPEGビデオ・デコーディングを有効にするには、これらのサンプル・プロジェクトにあるSTM32CubeMXの設定をヒントとして使用してください。
  • ソフトウェア・デコーディングの例はSTM32H735G-DKのTBSにありますが、ハードウェア・デコーディングはSTM32U5G9J-DK2のTBSで使用されます。

RTOSのサポート

Generatorユーザガイドに記載されているように、シングル・バッファおよびダブル・バッファのビデオ・デコーディング戦略には、FreeRTOSなどのCMSIS準拠のRTOSが必要です。 TouchGFX GeneratorはvideoTaskFunc()エントリ・ポイント関数を生成します。この関数はビデオ・デコーディング・タスクと関連付ける必要があります。 STM32CubeMXでは、FreeRTOSミドルウェア設定の[Tasks and queues]タブでタスクとエントリ・ポイント関数を定義することにより、この設定を生成できます。

ビデオ・タスク・スタック・サイズ(CMSIS V2の場合はワード数で定義)とRTOSヒープ・サイズは2つの重要な要素です。

LibJPEGはダイナミック・メモリ割り当てを使用するため、ソフトウェア・デコーディングではスタック・サイズを慎重に設定する必要があります。 つまり、FreeRTOSヒープは汎用的なアプリケーション + 0xA000(41KB以内)に対して十分な大きさがある必要があります。 ハードウェア・デコーディングでは、メモリをダイナミックに割り当てるソフトウェア・スタックがないため、スタック・サイズを大幅に小さくすることができます。

FreeRTOSタスクの設定

FreeRTOSヒープ・サイズの設定

上記の設定に基づいて、STM32CubeMXはビデオ・タスク用に次のコードを生成します。

main.c
/* Definitions for VideoTask */
osThreadId_t VideoTaskHandle;
const osThreadAttr_t VideoTask_attributes = {
.name = "VideoTask",
.stack_size = 1000 * 4,
.priority = (osPriority_t) osPriorityLow,
};
...
void main()
{
...
/* creation of VideoTask */
VideoTaskHandle = osThreadNew(videoTaskFunc, NULL, &VideoTask_attributes);
...
}
FreeRTOSConfig.h
#define configTOTAL_HEAP_SIZE                    ((size_t)75000)

ソフトウェア・デコーディング

ソフトウェア・デコーディング・ソリューションでは、Generatorユーザガイドで定められているように、LibJPEGミドルウェアをSTM32CubeMXから有効化する必要があります。 ソフトウェア・デコーディングの設定は、すべてのLibJPEG対応マイクロコントローラ(STM32F4、STM32F7、STM32H7)で同じです。

TouchGFX Software Decoderでは、LibJPEGでデコードされるデータのピクセル順序をBGRにする必要があります。 この設定がRGBのままの場合は、R色成分とB色成分がアプリケーション内でスワップされます。

さらに、各ピクセルのサイズは、16ビットまたは24ビットのビデオRGBバッファを使用する場合は3バイト、32ビットのビデオRGBバッファを使用する場合は4バイトにする必要があります。

16ビットまたは24ビットのビデオRGBバッファ用のLibJPEG設定

Caution
  • RGB_ORDERINGの設定はBGRに設定し、ピクセル・サイズは使用するビデオRGBバッファのフォーマットに応じて設定する必要があります。
  • アプリケーションが、ARGB8888(32ビット)アプリケーションのDirect to Framebuffer戦略を使用している場合、ピクセル・サイズは4に設定する必要があります。

ビデオ・データ

TouchGFXはビデオ・データをExtFlashSectionにリンクします。これはリンカ・スクリプトで定義する必要があります。 ExtFlashSectionが非メモリマップドFlashに配置されている場合は、データ・リーダを使用してFlashメモリからデータを読み出し、デコーダに渡す必要があります。

ビデオ・データが非メモリマップド・メモリにあるプロジェクトについては、この記事の後半のFileReaderセクションを参照してください。

ハードウェア・デコーディング

Generatorユーザガイドに記載されているように、ハードウェア・デコーディングを可能にするために、JPEG IPをSTM32CubeMXで有効にする必要があります。

JPEG設定にあるRGB_FORMATの設定は、TouchGFXフレームバッファのピクセル・フォーマットに従う必要があります(下の例ではJPEG_RGB565)。

JPEG IPのパラメータ設定

ハードウェア・デコーディングを使用する場合、JPEG IPへのデータ送信(memory-to-peripheral)と受信(peripheral-to-memory)にはDMAが使用されます。 使用するDMAとその設定方法は、マイクロコントローラ・ファミリ間で異なります。

Tip
次に、TouchGFX Designerで使用できるTouchGFX Board Setupの例をいくつか示します。これはさまざまなタイプのDMAによるJPEGデコーディングの設定方法を示すものです。
  • STM32U5G9J-DK2: JPEG + GPDMA
  • STM32H750B-DK: JPEG + MDMA
  • STM32H7S78-DK: JPEG + HPDMA

STM32H750の例

JPEGデコーディングのためにSTM32H750にDMAを設定する方法の例を以下に示します。 STM32H750の場合、JPEGペリフェラルは、通常のDMAペリフェラルではなくMDMAペリフェラルを使用するように設定する必要があります。 下の画像に示すように、入出力両方のFIFOしきい値信号のMDMA設定を追加します。 この設定は入力バッファと出力バッファのどちらでも同じです。

STM32H750のJPEG DMA設定

FileReaderインタフェース

MJPEGビデオを非メモリ・マップド・メモリに格納すると、開発者は、デコーディング用にデータを設定済みのデコーダ(ソフトウェア / ハードウェア)に渡すためにTouchGFXビデオ・コントローラで使用できるtouchgfx::VideoDataReaderの実装を指定できます。 以下に、ビデオ・データを1つのバッファから別のバッファにコピーするインタフェースなどの簡単な例を示します。

VideoView.cpp
class MyReader : public touchgfx::VideoDataReader
{
public:
MyReader() : position(0) { }
virtual uint32_t getDataLength() { return video_len; }
virtual void seek(uint32_t pos) { position = pos; }
virtual bool readData(void* dst, uint32_t bytes)
{
memcpy(dst, &video_data[position], bytes);
position += bytes;
return true;
}
private:
uint32_t position;
} myReader;

Videoウィジェットでマップド・メモリ内のビデオの開始位置をポイントするかわりに、開発者はデータ・リーダを使用するようにウィジェットを設定できます。

VideoView.cpp
video.setVideoData(myReader);

TBSを移行してビデオ・デコーディングに対応

ビデオ・デコーディングをサポートするマイクロコントローラでTouchGFX Board Setup(TBS)から作成したプロジェクトを、ビデオ・デコーディングがサポートされる前のTouchGFX Designerのバージョンから移行し、そのプロジェクトでTouchGFX Designerの[Run Target]を使用できるようにしたい場合は、GCC Makefileにいくつかの変更を手動で加える必要があります。

必要な変更の概要およびその変更が必要な理由については、後続のセクションで説明します。 常に適用する必要がある一般的な変更と、開発者のアプリケーションの使用内容に応じて、LibJPEG(ソフトウェア・デコーディング)やJPEG(ハードウェア・デコーディング)に固有の変更があります。 変更により、以前のTBSから作成された既存のGCC Makefileが拡張されます。

Makefileの更新以外にも、上記のシナリオで説明したように、STM32CubeMXでビデオ・デコーディングを設定する必要があります。

一般的な変更

LIBJPEGパスをプロジェクトに定義します。

# LibJPEG path
libjpeg_path := $(cubemx_middlewares_path)/Third_Party/LibJPEG

次に、ビデオ・アセット入力パスを定義します。

asset_texts_input  := TouchGFX/assets/texts

asset_videos_input := TouchGFX/assets/videos

ビデオ・アセット出力パスも、既存のアセット出力パスの下に定義する必要があります。

asset_images_output := $(asset_root_path)/images
asset_fonts_output := $(asset_root_path)/fonts
asset_texts_output := $(asset_root_path)/texts
asset_videos_output := $(asset_root_path)/videos

ビデオ出力アセットをコンポーネント・リストに追加します。

all_components := $(components) \
$(asset_fonts_output) \
$(asset_images_output) \
$(asset_texts_output)
$(asset_texts_output) \
$(asset_videos_output)

ビデオ・オブジェクト・ファイルを定義します。 ビデオ・オブジェクト・ファイルは既存のオブジェクトから区別します。

c_source_files := $(call find, $(source_paths),*.c) $(os_source_files) $(board_c_files)
source_files += $(board_cpp_files)

video_object_files := $(call find, $(asset_videos_output),*.o)

ビデオ・コンバータ・スクリプトへのパスを定義します。

textconvert_script_path := $(touchgfx_path)/framework/tools/textconvert
textconvert_executable := $(call find, $(textconvert_script_path), *.rb)
videoconvert_script_path := $(touchgfx_path)/framework/tools/videoconvert

オプションでechoを追加して、すべてのビデオ・オブジェクト・ファイルを表示できます。 ビデオ・オブジェクト・ファイルをリンク設定ステージに追加します。 この行には他のオブジェクト・ファイルとともに追加される$(video_object_files)を指定します。

$(binary_output_path)/$(target_executable): $(object_files) $(object_asm_files)
@echo Video Objects: $(video_object_files)
@echo Linking $(@)
@mkdir -p $(@D)
@mkdir -p $(object_output_path)
@$(file >$(build_root_path)/objects.tmp) $(foreach F,$(object_files) $(video_object_files),$(file >>$(build_root_path)/objects.tmp,$F))

ビデオ・ルールを既存のassetsと.PHONYに追加します。

_assets_: BitmapDatabase TextKeysAndLanguages Videos

.PHONY: BitmapDatabase TextKeysAndLanguages Videos

ビデオ・ルールをビデオ変換とともに追加します。

Videos:
@ruby $(videoconvert_script_path)/videoconvert.rb $(asset_videos_input) $(asset_videos_output)

最後に、クリーンなルールを更新して、ビデオ関連の出力も除去します。

_clean_:
@echo Cleaning: $(board_name)
@rm -rf $(build_root_path)
# Do not remove gui_generated
@rm -rf $(asset_images_output)
@rm -rf $(asset_fonts_output)
@rm -rf $(asset_texts_output)
@rm -rf $(asset_videos_output)
# Create directory to avoid error if it does not exist
@mkdir -p $(asset_root_path)
# Remove assets folder if it is empty (i.e. no gui_generated folder)
@rmdir --ignore-fail-on-non-empty $(asset_root_path)
# Clean bootloader project
@$(MAKE) -r -f ExtMem_Boot/gcc/Makefile -s $(MFLAGS) clean

ソフトウェアの変更

すべてのLIBJPEGパスをインクルード・パスに追加します。

include_paths := $(library_includes) \
$(foreach comp, $(all_components), $(comp)/include) \
$(foreach comp, $(cubemx_components), $(comp)/Inc) \
$(foreach comp, $(touchgfx_generator_components), $(comp)/generated) \
$(framework_includes) \
$(cubemx_middlewares_path) \
$(touchgfx_middlewares_path) \
$(touchgfx_generator_components) \
LIBJPEG/Target \
$(libjpeg_path)/include \
LIBJPEG/App

LIBJPEGソース・パスを定義します。

c_source_files := $(call find, $(source_paths),*.c) $(os_source_files) $(board_c_files)
source_files += $(board_cpp_files)

libjpeg_source_path = Middlewares/Third_Party/LibJPEG/source

次に、すべてのLIBJPEGソース・ファイルをboard_c_filesに追加します。

oard_c_files := \
$(Drivers_path)/BSP/STM32H750B-DK/stm32h750b_discovery_bus.c \
$(Drivers_path)/BSP/STM32H750B-DK/stm32h750b_discovery_qspi.c \
$(Drivers_path)/BSP/STM32H750B-DK/stm32h750b_discovery_sdram.c \
$(Drivers_path)/BSP/STM32H750B-DK/stm32h750b_discovery_ts.c \
$(Drivers_path)/BSP/Components/ft5336/ft5336.c \
$(Drivers_path)/BSP/Components/ft5336/ft5336_reg.c \
$(Drivers_path)/BSP/Components/mt25tl01g/mt25tl01g.c \
$(Drivers_path)/BSP/Components/mt48lc4m32b2/mt48lc4m32b2.c \
$(libjpeg_source_path)/jaricom.c \
$(libjpeg_source_path)/jcomapi.c \
$(libjpeg_source_path)/jdapimin.c \
$(libjpeg_source_path)/jdapistd.c \
$(libjpeg_source_path)/jdarith.c \
$(libjpeg_source_path)/jdatasrc.c \
$(libjpeg_source_path)/jdcoefct.c \
$(libjpeg_source_path)/jdcolor.c \
$(libjpeg_source_path)/jddctmgr.c \
$(libjpeg_source_path)/jdhuff.c \
$(libjpeg_source_path)/jdinput.c \
$(libjpeg_source_path)/jdmainct.c \
$(libjpeg_source_path)/jdmarker.c \
$(libjpeg_source_path)/jdmaster.c \
$(libjpeg_source_path)/jdmerge.c \
$(libjpeg_source_path)/jdpostct.c \
$(libjpeg_source_path)/jdsample.c \
$(libjpeg_source_path)/jdtrans.c \
$(libjpeg_source_path)/jerror.c \
$(libjpeg_source_path)/jidctflt.c \
$(libjpeg_source_path)/jidctfst.c \
$(libjpeg_source_path)/jidctint.c \
$(libjpeg_source_path)/jmemmgr.c \
$(libjpeg_source_path)/jmemnobs.c \
$(libjpeg_source_path)/jquant1.c \
$(libjpeg_source_path)/jquant2.c \
$(libjpeg_source_path)/jutils.c \
LIBJPEG/App/libjpeg.c

さらに、その他のミドルウェア・ソース・ファイルと同じ方法で、LIBJPEGソース・ファイルを既存のオブジェクト・ファイルに追加します。

# Start converting paths
object_files := $(object_files:$(touchgfx_path)/%.cpp=$(object_output_path)/touchgfx/%.o)
object_files := $(object_files:%.cpp=$(object_output_path)/%.o)
object_files := $(object_files:$(touchgfx_middlewares_path)/%.c=$(object_output_path)/$(touchgfx_middlewares_path)/%.o)
object_files := $(object_files:$(cubemx_middlewares_path)/%.c=$(object_output_path)/$(cubemx_middlewares_path)/%.o)
object_files := $(object_files:$(libjpeg_source_path)/%.c=$(object_output_path)/$(libjpeg_source_path)/%.o)
object_files := $(object_files:$(Drivers_path)/%.c=$(object_output_path)/Drivers/%.o)
object_files := $(object_files:%.c=$(object_output_path)/%.o)

ハードウェアの変更

すべてのJPEGパスをインクルード・パスに追加します。

include_paths := $(library_includes) \
$(foreach comp, $(all_components), $(comp)/include) \
$(foreach comp, $(cubemx_components), $(comp)/Inc) \
$(foreach comp, $(touchgfx_generator_components), $(comp)/generated) \
$(framework_includes) \
$(cubemx_middlewares_path) \
$(touchgfx_middlewares_path) \
$(touchgfx_generator_components) \
Utilities/JPEG

次に、すべてのJPEGソース・ファイルをboard_c_filesに追加します。

board_c_files := \
$(Drivers_path)/BSP/STM32H750B-DK/stm32h750b_discovery_bus.c \
$(Drivers_path)/BSP/STM32H750B-DK/stm32h750b_discovery_qspi.c \
$(Drivers_path)/BSP/STM32H750B-DK/stm32h750b_discovery_sdram.c \
$(Drivers_path)/BSP/STM32H750B-DK/stm32h750b_discovery_ts.c \
$(Drivers_path)/BSP/Components/ft5336/ft5336.c \
$(Drivers_path)/BSP/Components/ft5336/ft5336_reg.c \
$(Drivers_path)/BSP/Components/mt25tl01g/mt25tl01g.c \
$(Drivers_path)/BSP/Components/mt48lc4m32b2/mt48lc4m32b2.c \
Utilities/JPEG/jpeg_utils.c

外部メモリへのビデオ・バッファの配置

開発者がSTM32CubeMXを使用して新しいプロジェクトを作成した場合、作成したプロジェクトに関連付けられたリンカ・スクリプトには、TouchGFXで使用されるデフォルトの領域は含まれていません。 このため、ビデオが格納される「ExtFlashSection」は、デフォルトでは内部Flashに配置されます。 内部Flashメモリが不足するのを防ぐため、多くの場合、「ExtFlashSection」は外部Flashメモリに配置する必要があります。

Tip
外部メモリからのビデオ・デコーディングが可能な設定済みのTBSの例を以下に示します。
  1. STM32F746-DISCO
  2. STM32H735G-DK
  3. STM32H750-DK
  4. STM32U5G9J-DK2

選択したビデオ戦略がDirect to Framebufferではない場合、ビデオを有効にすると、TouchGFX GeneratorによってJPEGデコーディング専用のRGBバッファが生成されます。 この定義には、バッファを配置するセクションをリンカに通知するロケーション・プラグマが付随しています。 リンカがこのメモリ領域をリンカ・スクリプトで検出できない場合は、バッファは内部メモリに配置されます。

LOCATION_PRAGMA_NOLOAD("Video_RGB_Buffer")
uint32_t videoRGBBuffer[57600] LOCATION_ATTRIBUTE_NOLOAD("Video_RGB_Buffer");

次のコンパイラ固有のサブセクションでは、STM32F746G-DISCOのSDRAMにバッファを配置するために開発者が行う変更例について説明します。 Video_RGB_Bufferは、ビデオ・デコーディングに使用されるバッファを表しています。 サンプルのリンカ・スクリプトでは、TouchGFXフレームバッファ用にSDRAMのいくらかのスペース(0xC0000000から開始)を確保しています。

  1. EWARM
  2. STM32CubeIDE
  3. MDK-ARM
Further reading
ビデオ・デコーディングのためのプロジェクト設定については、Generatorユーザガイドの該当セクションを参照してください。

以下のすべての例では、STM32F746G-DISCOボードのSDRAMの開始アドレス(0xC0000000->0xC00FF000)からスペースを確保し、リンカがフレームバッファのデータを上書きするリスクなしに、アプリケーションがアドレス(0xC0000000など)を使用してフレームバッファを参照できます。 各例で、リンカは定義されているSDRAM領域にVideo_RGB_Bufferを配置できます。

Tip
直接アドレス指定するのではなく、TouchGFXフレームバッファを割り当てている場合は、リンカ・スクリプトでTouchGFX_FramebufferもSDRAMに配置します。

EWARM(IAR)

stm32f746xx_flash.icf
define symbol __ICFEDIT_region_SDRAM_start__   = 0xC00FF000;
define symbol __ICFEDIT_region_SDRAM_end__ = 0xC0700FFF;

define region SDRAM_region = mem:[from __ICFEDIT_region_SDRAM_start__ to __ICFEDIT_region_SDRAM_end__];

place in SDRAM_region { first section Video_RGB_Buffer };

リンク設定すると、 EWARM\STM32F746G_DISCO\List\STM32F746G_DISCO.mapには、Video_RGB_Bufferに関する次の配置情報が含まれます。

STM32F746G_DISCO.map
Video_RGB_Buffer        zero     0xc00f'f000   0x3'8400  TouchGFXGeneratedHAL.o [2]
- 0xc013'7400 0x3'8400

STM32CubeIDE

STM32F746NGHX_FLASH.ld
MEMORY
{
...
SDRAM (xrw) : ORIGIN = 0xC00FF000, LENGTH = 8M
}

BufferSection (NOLOAD) :
{
*(Video_RGB_Buffer Video_RGB_Buffer.*)
*(.gnu.linkonce.r.*)
. = ALIGN(0x4);
} >SDRAM

コンパイルすると、STM32CubeIDE\Debug\STM32F746G_DISCO.mapには、Video_RGB_Bufferに関する次の配置情報が含まれます。

STM32F746G_DISCO.map
BufferSection   0x00000000c00ff000    0x1c200
*(Video_RGB_Buffer Video_RGB_Buffer.*)
Video_RGB_Buffer
0x00000000c00ff000 0x1c200 Application/User/TouchGFX/target/generated/TouchGFXGeneratedHAL.o
0x00000000c00ff000 videoRGBBuffer

MDK-ARM(Keil)

STM32F746G_DISCO.sct
LR_IROM1 0x08000000 0x00200000  {    ; load region size_region
ER_IROM1 0x08000000 0x00200000 { ; load address = execution address
*.o (RESET, +First)
*(InRoot$$Sections)
.ANY (+RO)
}
RW_IRAM1 0x20000000 0x00050000 { ; RW data
.ANY (+RW +ZI)
}
RW_SDRAM 0xC00FF000 UNINIT 0xC0700FFF {
*.o (Video_RGB_Buffer)
}
}

ビデオバッファを保持するセクションにUNINIT属性を必ず含めてください。 これにより、初期化されていないデータとしてメモリ領域が確実に残されます。 リンク設定すると、 MDK-ARM\STM32F746G_DISCO\STM32F746G_DISCO.mapには、Video_RGB_Bufferに関する次の配置情報が含まれます。

STM32F746G_DISCO.map
Video_RGB_Buffer                         0xc00ff000   Section    115200  touchgfxgeneratedhal.o(Video_RGB_Buffer)