Video player
A box that plays an RTSP stream (an IP camera, a recorder, another board) or an MP4 file from a web address or the SD card, with its sound. Play, pause, stop and seek are an action, so a button, a timer or a slider can drive it. ESP32-P4 with PSRAM, Arduino and ESP-IDF exports. Pro.
The picture must be H.264 in the Baseline profile. The board decodes it in software, and that decoder cannot read the Main or High profile many cameras send by default. Most cameras let one stream (often the second, smaller one) be set to Baseline, and a file can be saved as Baseline. A stream or file in Main, High or H.265 is refused on the board: the serial port says which, and Video lost is sent. A camera that only sends MJPEG belongs in a Camera view, from its http:// address.
One Video player a design. It plays any of the three sources, and the Video player action changes which; a second player is refused in the Problems tab. Each player holds a decoder and its memory, and with three on one board a file on the card would not play once the other two had.
Build it with PlatformIO. The H.264 decoder in the Arduino ESP32 core (esp_h264 1.3.0 in core 3.3.11, 1.3.8 in 3.3.12) crashed on ordinary video on a revision 3 ESP32-P4, a few seconds into some files. The export carries the one from Espressif’s esp_h264 1.4.1 in h264/, and PlatformIO links it in the core’s place. The Arduino IDE always links the core’s own, so there the player builds but may crash; Arduino ESP32 core 4.0 carries 1.4.1. The ESP-IDF export uses esp_h264 1.4.1 as a component.
Keep the picture small: 480 × 272 or close to it. That size plays at 15 frames a second. A 1280 × 720 (720p) picture decodes at only about 6 frames a second in software, so a 720p file plays slowly. Sizes in between have not been tried. A camera’s second stream is usually small enough, and a file can be saved at 480 × 272.
For a bigger picture, use MJPEG. The P4 decodes H.264 in software but JPEG in hardware, so an MJPEG stream in a Camera view plays much larger pictures: 1280 × 720 at about 22 frames a second (of 25 sent) on the 4.3" board over Wi-Fi, where H.264 at that size manages about 6. Many IP cameras offer an MJPEG stream beside their H.264 one. MJPEG has no sound of its own; the Camera view can play an audio stream beside it.
Each picture must come in one slice. The decoder cannot put a picture together from several, which some cameras and encoders send (x264’s -tune zerolatency does), so such a stream is refused with a sentence on the serial port.
In the designer and in Preview it is drawn as the shape the picture will take (16:9 until the board knows better) with where it plays from. The designer fetches nothing.


| Setting | Default | What it does |
|---|---|---|
| Plays | An RTSP stream | Or an MP4 file, on the web or the SD card. Once there is an address, the address decides: rtsp:// is a stream, http:// or https:// a file on the web, /videos/clip.mp4 a file on the card. |
| Address or file | empty | rtsp://user:password@192.168.1.64:554/stream2 for a camera (the user name and password go in the address, and both Basic and Digest are answered); https://example.com/clip.mp4 or /videos/clip.mp4 for a file. |
| Plays when the board starts | on | Off, it waits for the Video player action set to Play. |
| Starts again at the end | off | An MP4 file only. |
| Plays its sound | on | Through the board’s speaker (Hardware > Audio). The picture keeps time with the sound, and while it plays the radio or card player is stopped. AAC, MP3, G.711 and L16 sound is played. |
| When the stream drops, tries again after | 5 seconds | RTSP only. 0 waits for Play instead. |
| The picture | Shows all of it | Shown whole with bars, fills the box and loses its edges, or is stretched to the box. |
| While there is no picture | Keeps the last frame | Or shows the box and its text. |
| While connecting, When it cannot play | Connecting, No signal | The two lines of text the box shows. |
| Looks for a new frame | 30 times a second | How often the screen looks. The video itself sets how many frames there are. |
The Video player action, and its events
| The action | What happens |
|---|---|
| Play | From where it was, or from the start. With Address: This address or file it plays the address typed into the action, and with From a text variable the variable’s, from then on: three buttons can switch one player between a camera, a file on the web and a file on the card, and a roller of cameras can set the variable. |
| Pause | The frame stays up and the sound stops. A live stream is let go and joined again on Play; a file carries on from the frame shown. |
| Stop | Lets the stream or file go. |
| Play or pause | Plays it if it is not playing, pauses it if it is. One button for both. |
| Seek | Moves an MP4 file to a number of seconds, the seconds in a variable, or a slider’s or arc’s value: a slider whose Released event seeks is a scrub bar. It lands on the key frame at or before that time, since H.264 cannot start between them. A live stream ignores it. |
Its events are Video playing (the first frame is up, after Play, a seek, or the stream coming back), Video lost (it could not play: unreachable, dropped, or a picture it cannot decode) and Video ended (an MP4 reached its end and is not set to start again). From your own code, lw_vplay.h has ui_vplay_play(), ui_vplay_pause(), ui_vplay_stop(), ui_vplay_seek() and ui_vplay_status(), which gives “Playing” or why it could not.
Example: a camera on the screen, with its sound
- Start on an ESP32-P4 board with a speaker and a network, such as the Waveshare ESP32-P4-WIFI6-Touch-LCD-4.3. Add a WiFi Setup widget, or use the wired port where the board has one, and choose the audio output under Hardware > Audio.
- Drag a Video player onto the screen. Leave Plays at An RTSP stream and put the camera’s address in Address or file.
- In the camera’s own settings, set that stream to H.264, profile Baseline.
- Add a button and give its Clicked event the Video player action set to Play or pause.
- Export and upload. The serial port says what it is doing, on lines that begin
[video].
What it does not do, and how far it is tested
- H.264 Main and High profile, H.265, MJPEG over RTSP, fragmented MP4 files, RTSP over UDP, or an MP4 a server sends in pieces (chunked) rather than as a plain file. A server that will not send part of a file still plays, but seeking has to read the file up to that point.
- Pictures much larger than 480 × 272 play slowly: 720p decodes at about 6 frames a second (above).
- An ESP32-S3: decoding H.264 in software is too slow on it. The MicroPython and ESPHome exports: each says so in the Problems tab. The ESP-IDF export plays the picture, and MP3, G.711 and L16 sound, but not an AAC sound track: the picture plays without it.
- As of October 2026 it compiles for LVGL 9 and LVGL 8, and the code that reads MP4 files and RTSP packets is tested on a PC against files and packets made for it. On a board (a Waveshare ESP32-P4-WIFI6-Touch-LCD-4.3, Arduino export): an RTSP stream, an MP4 from the web with its sound track, starting again at its end, and an MP4 from the SD card have played, at 480 × 272 and 15 frames a second, one player switched between all three. The sound was decoded and kept time but has not been heard (that board had no speaker fitted), and seek, pause and a real IP camera have not been tried there yet. If it does not do what this page says, Report a problem on the designer’s title bar reaches the person who wrote it.