videocore – Support for playing video sources

This module provides the video pipeline shared between video sources and video outputs. It is the video equivalent of the audiocore module.

Video sources, such as mipicsi.Camera, implement the internal videosource protocol. A VideoBuffer tells a video source where to put its frames in a framebuffer, such as mipidsi.Display, and can move it as needed.

Stream a MIPI CSI camera live into the board’s display (a MIPI DSI panel on ESP32-P4-Function-EV, for example):

import board
import displayio
import mipicsi
import sc2336
import videocore

display = board.DISPLAY()
framebuffer = display.framebuffer
sensor = sc2336.SC2336(board.I2C())
sensor.mode = sc2336.Mode.HD_1280x720_RAW10_30FPS
# Trim the sensor output to the display size.
sensor.width = display.width
sensor.height = display.height
camera = mipicsi.Camera(sensor, output_format=mipicsi.PixelFormat.RGB565)

video = videocore.VideoBuffer(framebuffer)
video.play(camera)
root = displayio.Group()
root.append(video)
display.root_group = root

Capture a smaller part of the sensor’s image into a bytearray and let displayio place it in a Group, together with other layers. displayio bulk copies new frames into the group’s area of the display (no color conversion):

import board
import displayio
import mipicsi
import sc2336
import videocore

sensor = sc2336.SC2336(board.I2C())
sensor.mode = sc2336.Mode.VGA_640x480_RAW10_50FPS
# Trim the sensor output to a 320x240 window.
sensor.width = 320
sensor.height = 240
camera = mipicsi.Camera(sensor, output_format=mipicsi.PixelFormat.RGB565)

capture = videocore.VideoBuffer(bytearray(320 * 240 * 2))
capture.play(camera)

root = displayio.Group()
root.append(capture)
capture.x = 350  # position of the live video within the group
capture.y = 180
board.DISPLAY().root_group = root  # live video joins the displayio scene

Available on these boards
  • Adafruit Metro ESP32-P4
  • Adafruit P4 GPIO
  • ESP32-P4 Stamp XL
  • ESP32-P4-Function-EV
  • ESP32-P4X-Function-EV
  • M5Stack Tab5

class videocore.VideoBuffer(framebuffer: circuitpython_typing.FrameBuffer | circuitpython_typing.WriteableBuffer)

Stream a video source into a framebuffer or buffer.

A VideoBuffer connects a video source, such as mipicsi.Camera, to a target: either a framebuffer, such as mipidsi.Display, or a writable buffer such as a bytearray. It is the video counterpart of audioio.AudioOut.

Add a VideoBuffer to a displayio.Group to show it on a display. A framebuffer target only streams while it is shown.

Implementations may restrict size and position. For example, on ESP32-P4 a framebuffer target must be full width because the DMA can’t skip between rows without an intermediate buffer. Setting an unsupported position directly raises an error, but moving a containing Group may give garbled output instead.

When the target is a plain buffer, frames are written compactly (row stride equals width * bits_per_pixel / 8) and can be read from Python at any time, for example to process or save camera pixels.

See the videocore module overview for complete examples.

Create a VideoBuffer that plays video into the given framebuffer

Parameters:

framebuffer – The framebuffer to copy video frames into, or a writable buffer such as a bytearray or memoryview.

play(source: circuitpython_typing.VideoSource) → None

Begin streaming the given video source into the target.

The source streams continuously, writing each new frame where the VideoBuffer tells it to, replacing the previous one. The source’s frame format must be byte aligned. The source’s size and format are then fixed for the duration of the play(); sources that change them require stop() and a new play().

When the target is a framebuffer, its bits per pixel must match the framebuffer color depth and the whole frame must fit within the framebuffer at the current x/y position. An exception may be raised if the video can’t be played directly into the framebuffer.

When the target is a plain buffer, it must be large enough for a whole frame.

Parameters:

source (VideoSource) – The video source to play, such as mipicsi.Camera

stop() → None

Stop playing the video source.

playing: bool

True when a video source is being played, False otherwise. (read-only)

source: circuitpython_typing.VideoSource | None

The video source being played, or None when not playing. (read-only)

x: int

The x position of the video within its Group. Framebuffer targets only support x == 0 and raise ValueError otherwise.

y: int

The y position of the video within its Group.

deinit() → None

Stop playing and stop the video source.

__enter__() → VideoBuffer

No-op used by Context Managers.

__exit__() → None

Automatically deinitializes the hardware when exiting a context. See Lifetime and ContextManagers for more info.