概要
ESP32でmp4動画を再生させる手法を説明します。また、PCからESP32へファイル転送する方法も合わせて紹介します。
動画再生の実行例を以下リンク先でご紹介しております。
動作動画1:https://youtube.com/shorts/8US4eWBNk24
動作動画2:https://youtu.be/QakuN59XoTw
はじめに
ESP32ではmp4動画を直接再生することはできません。よって、mp4ファイルをmjpegファイルに変換する必要があります。つまり、mjpegファイルにして、画像をコマ送りにして動画にするイメージです。
変換するために、ffmpegを使用します。WindowsPCでコンソールを立ち上げ、以下コマンドでインストールしてください。
winget install ffmpeg
次に動画を変換します。以下例はtest.mp4という動画を、フレームレート5、128×128の解像度(ご使用のディスプレイ解像度に合わせてください)で、test.mjpegに変化する例です。
Windowsコンソールで実行し、変換してください。
ffmpeg -i test.mp4 -t 5 -vf "scale=128:128,fps=8" -an -c:v mjpeg -q:v 10 -f mjpeg test.mjpg
尚、変換後の動画ファイルのサイズは、ESP32側のメモリサイズの関係で1MB程度とする必要があります。
次に動画ファイルをESP32へ転送するためのプラグインをAruduinoIDEへインストールします。以下リンクでプラグイン(arduino-littlefs-upload-1.6.3.vsix)をダウンロードします。
https://github.com/earlephilhower/arduino-littlefs-upload/releases
ダウンロードしたファイルを、ご自分のPluginフォルダへコピーしてください。まだPluginフォルダが無い場合は作成してください。
C:\Users\xxxxx\.arduinoIDE\Plugins
次にご自分のプログラムのプロジェクトにdataフォルダを作成し、そこに動画ファイルtest.mjpegをコピーしておきます。以下作成例。

次に、AruduinoIDEを起動します。
Ctrl+Shift+P を押し、「Upload LittleFS to Pico/ESP8266/ESP32」を選択すると転送が開始されます。
※この時シリアルモニタなどを利用している場合は転送できないので、切断してから上記実行してください。
尚、ESP32内のファイルを削除した場合は、dataフォルダ内のファイルを削除し、再度Ctrl+Shift+P を押し、「Upload LittleFS to Pico/ESP8266/ESP32」を選択で、ESP32内のファイルが消えます。
動画再生プログラム例
動画再生プログラム例を紹介いたします。ESP32 devKitにST7735ディスプレイを接続して表示する場合の例になります。
#include <Arduino.h>
#include <LittleFS.h>
#include <TFT_eSPI.h>
#include <TJpg_Decoder.h>
//========== ユーザー設定 ==========
#define TFT_ROTATION 3 // 向き(0-3)
#define FRAME_DELAY 65 // fps調整(ms)
#define JPG_SCALE 1 // 1=等倍 / 2=1/2
// JPEG最大サイズ
#define MAX_JPEG (48 * 1024)
//========== グローバル ==========
TFT_eSPI tft = TFT_eSPI();
uint8_t jpgBuf[MAX_JPEG];
//========== JPEG描画 ==========
bool tft_output(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t *bmp) {
tft.pushImage(x, y, w, h, bmp);
return true;
}
//========== MJPEGから1フレーム読み出し ==========
bool readJpg(File &f, size_t &len) {
len = 0;
int prev = -1;
while (f.available()) {
int c = f.read();
if (c < 0) break;
if (len == 0) {
if (prev == 0xFF && c == 0xD8) { // SOI
jpgBuf[0] = 0xFF;
jpgBuf[1] = 0xD8;
len = 2;
}
} else {
if (len >= MAX_JPEG) return false;
jpgBuf[len++] = c;
if (prev == 0xFF && c == 0xD9) { // EOI
return true;
}
}
prev = c;
}
return false;
}
//========== setup ==========
void setup() {
Serial.begin(115200);
// TFT初期化
tft.init();
tft.setRotation(TFT_ROTATION);
tft.setSwapBytes(true);
tft.fillScreen(TFT_BLACK);
// FS初期化
if (!LittleFS.begin()) {
Serial.println("LittleFS error");
while (1);
}
// JPEGデコーダ設定
TJpgDec.setCallback(tft_output);
TJpgDec.setJpgScale(JPG_SCALE);
Serial.println("MJPEG start");
}
//========== loop ==========
void loop() {
File f = LittleFS.open("/test.mjpg", "r");
if (!f) return;
while (true) {
size_t len;
if (!readJpg(f, len)) break;
TJpgDec.drawJpg(0, 0, jpgBuf, len);
delay(FRAME_DELAY);
}
f.close();
}
