2024/1/1
只有影像部分,需 ffmpeg 轉檔後才能用
程式參考 https://www.instructables.com/Mini-Retro-TV/
原先為 ESP32 版本,修改為 Pico 版本使用
影像放至 littlefs 空間內
/***
* Pico_Game show mp4 video only by Mason 2024/1/1
*
* Reference : https://www.instructables.com/Mini-Retro-TV/
*
* Required libraries:
* Arduino_GFX: https://github.com/moononournation/Arduino_GFX.git
* libhelix: https://github.com/pschatzmann/arduino-libhelix.git
* JPEGDEC: https://github.com/bitbank2/JPEGDEC.git
*
* ffmpeg -i input.mp4 -vf "fps=12,scale=-1:240:flags=lanczos,crop=288:in_h:(in_w-288)/2:0" -q:v 11 288_12fps.mjpeg
*/
#define MJPEG_FILENAME "/2024_12fps.mjpeg"
#define FPS 12
#define MJPEG_BUFFER_SIZE (288 * 240 * 2 / 10)
#include <FS.h>
#include <LittleFS.h>
/*******************************************************************************
* Start of Arduino_GFX setting
******************************************************************************/
#include <Arduino_GFX_Library.h>
/* Pico_Game ILI9341 320x240 */
#define TFT_DC 20
#define TFT_CS 17
#define TFT_RST 21
#define TFT_SCK 18
#define TFT_MOSI 19
#define TFT_MISO 16
Arduino_DataBus *bus = new Arduino_RPiPicoSPI(TFT_DC, TFT_CS, TFT_SCK, TFT_MOSI, TFT_MISO, spi0);
Arduino_GFX *gfx = new Arduino_ILI9341(bus, TFT_RST);
/*******************************************************************************
* End of Arduino_GFX setting
******************************************************************************/
/* variables */
static int next_frame = 0;
static int skipped_frames = 0;
static unsigned long start_ms, curr_ms, next_frame_ms;
static unsigned long total_read_video_ms = 0;
static unsigned long total_decode_video_ms = 0;
static unsigned long total_show_video_ms = 0;
/* MJPEG Video */
#include "MjpegClass.h"
static MjpegClass mjpeg;
// pixel drawing callback
static int drawMCU(JPEGDRAW *pDraw)
{
// Serial.printf("Draw pos = (%d, %d), size = %d x %d\n", pDraw->x, pDraw->y, pDraw->iWidth, pDraw->iHeight);
unsigned long s = millis();
gfx->draw16bitBeRGBBitmap(pDraw->x, pDraw->y, pDraw->pPixels, pDraw->iWidth, pDraw->iHeight);
total_show_video_ms += millis() - s;
return 1;
} /* drawMCU() */
void setup()
{
Serial.begin(115200);
// Serial.setDebugOutput(true);
// while(!Serial);
Serial.println("Pico_Game_test");
#ifdef GFX_EXTRA_PRE_INIT
GFX_EXTRA_PRE_INIT();
#endif
Serial.println("Init display");
if (!gfx->begin(75000000))
{
Serial.println("Init display failed!");
}
gfx->begin();
gfx->setRotation(1);
gfx->invertDisplay(true);
gfx->fillScreen(BLACK);
#ifdef GFX_BL
pinMode(GFX_BL, OUTPUT);
digitalWrite(GFX_BL, HIGH);
#endif
Serial.println("Init FS");
if (!LittleFS.begin()){
Serial.println(F("fail."));
}else{
Serial.println("Open MJPEG file: " MJPEG_FILENAME);
File vFile = LittleFS.open(F(MJPEG_FILENAME), "r");
if (!vFile || vFile.isDirectory())
{
Serial.println("ERROR: Failed to open " MJPEG_FILENAME " file for reading");
}
else
{
uint8_t *mjpeg_buf = (uint8_t *)malloc(MJPEG_BUFFER_SIZE);
if (!mjpeg_buf)
{
Serial.println("mjpeg_buf malloc failed!");
}
else
{
mjpeg.setup(
&vFile, mjpeg_buf, drawMCU, true /* useBigEndian */,
0 /* x */, 0 /* y */, gfx->width() /* widthLimit */, gfx->height() /* heightLimit */);
start_ms = millis();
curr_ms = millis();
next_frame_ms = start_ms + (++next_frame * 1000 / FPS / 2);
while (vFile.available() && mjpeg.readMjpegBuf()) // Read video
{
total_read_video_ms += millis() - curr_ms;
curr_ms = millis();
if (millis() < next_frame_ms) // check show frame or skip frame
{
// Play video
mjpeg.drawJpg();
total_decode_video_ms += millis() - curr_ms;
curr_ms = millis();
}
else
{
++skipped_frames;
Serial.println("Skip frame");
}
while (millis() < next_frame_ms)
{
// vTaskDelay(pdMS_TO_TICKS(1));
delay(1);
}
curr_ms = millis();
next_frame_ms = start_ms + (++next_frame * 1000 / FPS);
}
int time_used = millis() - start_ms;
int total_frames = next_frame - 1;
Serial.println("MP3 audio MJPEG video end");
vFile.close();
delay(200);
int played_frames = total_frames - skipped_frames;
float fps = 1000.0 * played_frames / time_used;
total_decode_video_ms -= total_show_video_ms;
Serial.printf("Played frames: %d\n", played_frames);
Serial.printf("Skipped frames: %d (%0.1f %%)\n", skipped_frames, 100.0 * skipped_frames / total_frames);
Serial.printf("Time used: %d ms\n", time_used);
Serial.printf("Expected FPS: %d\n", FPS);
Serial.printf("Actual FPS: %0.1f\n", fps);
Serial.printf("Read video: %lu ms (%0.1f %%)\n", total_read_video_ms, 100.0 * total_read_video_ms / time_used);
Serial.printf("Decode video: %lu ms (%0.1f %%)\n", total_decode_video_ms, 100.0 * total_decode_video_ms / time_used);
Serial.printf("Show video: %lu ms (%0.1f %%)\n", total_show_video_ms, 100.0 * total_show_video_ms / time_used);
}
}
}
}
void loop() {
}