Skip to main content

Introduction

Documentation Status Discord Build Status Code Style: Ruff

Play RV565 video files on CircuitPython displays.

RV565 stores video as independently zlib-compressed frames whose decoded bytes are already big-endian RGB565, the native pixel format of ST7789-class SPI panels (format specification, paper: arXiv:2610.35935). This library reads them in pure Python with CircuitPython’s built-in zlib and shows them with displayio, or sends them straight to an ST7789.

It is the easy way to try RV565 on a board without a C toolchain. The RV565 C library (Arduino, ESP-IDF, Pico SDK) is the fast way: it reaches the clip’s full 24 FPS.

Dependencies

This library depends on:

Please ensure all dependencies are available on the CircuitPython filesystem. This is easily achieved by downloading the Adafruit library and driver bundle or individual libraries can be installed using circup.

Performance

Measured on a Raspberry Pi Pico 2 (RP2350, CircuitPython 10.3.1), playing the 180x135, 24 FPS test pattern to a 320x240 ST7789 on SPI at 40 MHz. Colours, orientation and position were correct with both players:

Player

FPS

inflate per frame

draw per frame

Player

8.0

51.0 ms

73.3 ms

DirectPlayer

16.5

47.7 ms

12.6 ms

DirectPlayer’s draw is close to the SPI wire time (9.7 ms for the frame), so inflating with CircuitPython’s zlib (about 48 ms a frame, against about 16 ms for the C library’s tinfl on the same chip) is what limits it. Film clips compress less than the test pattern and inflate more slowly.

Expect a fraction of the clip’s rate, not 24. The C library reaches 24 FPS by inflating on one core while DMA sends the previous frame from the other. Here, inflating and drawing happen one after the other in a single Python thread. Frames that fall behind are skipped, so playback keeps the clip’s timing, and play() returns the frame rate it actually achieved.

Installing from PyPI

On supported GNU/Linux systems like the Raspberry Pi, you can install the library locally from PyPI. To install for current user:

pip3 install circuitpython-rv565

To install system-wide (this may be required in some cases):

sudo pip3 install circuitpython-rv565

To install in a virtual environment in your current project:

mkdir project-name && cd project-name
python3 -m venv .venv
source .venv/bin/activate
pip3 install circuitpython-rv565

The reader (rv565.open(), Clip.frame()) works under CPython too; the players need displayio.

Installing to a Connected CircuitPython Device with Circup

Make sure that you have circup installed in your Python environment. Install it with the following command if necessary:

pip3 install circup

With circup installed and your CircuitPython device connected use the following command to install:

circup install circuitpython_rv565

Or the following command to update an existing version:

circup update

Without circup, copy circuitpython_rv565.mpy from the CircuitPython Community Bundle (or from this repository’s releases) to the lib folder on your CIRCUITPY drive.

Usage Example

Copy examples/testpattern.rv565 (180x135, 24 FPS, 24 frames, 6 KB) to the root of CIRCUITPY, then run this as code.py:

import board

import circuitpython_rv565 as rv565

clip = rv565.open("/testpattern.rv565")  # path, file object or bytes
print(clip.width, clip.height, clip.fps, len(clip))

player = rv565.Player(clip, board.DISPLAY)
stats = player.play()  # paced to clip.fps, late frames skipped
print(stats)  # <PlayStats x.xx of 24 FPS: n shown, m skipped ...>

rv565.open() reads only the 12-byte header. The frame index is built on first use with one pass over the length fields (4 bytes of RAM per frame), which also validates the whole container; call clip.build_index() to do that before playback. Frames are read from the file one at a time, so a clip on CIRCUITPY or an SD card does not have to fit in RAM. clip.frame(i) returns frame i as big-endian RGB565 bytes and raises RV565Error if it does not inflate to exactly 2 * width * height bytes.

The examples folder has:

  • rv565_simpletest.py: any board with board.DISPLAY.

  • rv565_pico2_st7789.py: Raspberry Pi Pico 2 with a 2.0” 320x240 ST7789 on SPI0 (SCK GP18, MOSI GP19, CS GP17, DC GP20, RST GP21, BL GP22).

  • rv565_pico2_direct.py: the same wiring with DirectPlayer.

  • rv565_esp32s3_tft.py: Adafruit ESP32-S3 TFT Feather (built-in 240x135 ST7789).

Making clips

Use the online converter, which runs in your browser, or the Python tool:

pip install rv565
rv565 encode input.mp4 clip.rv565 --size 180x135 --fps 12
rv565 verify clip.rv565

Because this library will not reach 24 FPS, a lower rate such as 8 or 12 FPS wastes less of the file on frames that will be skipped. Keep clips at 180x135 or smaller on boards without PSRAM, such as the Pico 2: a 320x240 frame needs 150 KiB inflated and as much again for the bitmap.

How a frame reaches the display

Player inflates each frame into a displayio.Bitmap with 65536 values, drawn through ColorConverter(input_colorspace=Colorspace.RGB565_SWAPPED). The bitmap stores each value as a little-endian 16-bit word, so copying the file’s big-endian bytes in unchanged gives byte-swapped values, which is exactly what RGB565_SWAPPED describes (the same convention gifio uses). The copy is one bitmaptools.readinto() call; boards without it fall back to a much slower per-pixel loop.

DirectPlayer skips displayio: since RV565 bytes are already what an ST7789 expects, it sets the address window and sends each frame with one bus.send(). It needs the display to be initialised first (for example by adafruit_st7789) and takes the panel over while playing. displayio rotation is done in software and never reaches the controller, so for a landscape panel DirectPlayer sets MADCTL to 0x60 before playing and back to 0xC0 (adafruit_st7789’s value) afterwards; pass madctl= and restore_madctl= for other orientations or drivers. Modules smaller than the controller’s 240x320 RAM need x_offset/y_offset as well.

Testing on a computer

The tests run with CPython and stand-ins for displayio and bitmaptools:

python3 -m pytest

They check the reader against examples/testpattern.rv565 (all 24 frames must equal CPython’s zlib output), every validation rule of the format, that the bitmap shows the right colour for every pixel, frame pacing and skipping, the bytes DirectPlayer sends, and that every example runs.

Documentation

API documentation for this library can be found on Read the Docs.

For information on building library documentation, please check out this guide.

RV565

Contributing

Contributions are welcome! Please read our Code of Conduct before contributing to help this project stay welcoming.

Metadata

Release files for circuitpython-rv565 1.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for circuitpython-rv565 1.2.0
File Size Uploaded Provenance Actions
circuitpython_rv565-1.2.0.tar.gz 43.0 kB With provenance Details for circuitpython_rv565-1.2.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for circuitpython-rv565 1.2.0
File Interpreter ABI Platform Actions
circuitpython_rv565-1.2.0-py3-none-any.whl Python 3 none any Details for circuitpython_rv565-1.2.0-py3-none-any.whl

Total release size: 57.3 kB

Release files / circuitpython_rv565-1.2.0.tar.gz

Download URL circuitpython_rv565-1.2.0.tar.gz
Size 43.0 kB
Tags Source
SHA-256 checksum
How to use checksums
74cb88e0aed2a7baaa77c9235fd66dc4df481aec76ffb6a04ccdf64a161fee9f
BLAKE2b-256 checksum
How to use checksums
74ffa7b2138c503d5f9e85811436fac03537dacda4b97f00dcc4b2daa48cea30
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 11, 2026.

Transparency log

Release files / circuitpython_rv565-1.2.0-py3-none-any.whl

Download URL circuitpython_rv565-1.2.0-py3-none-any.whl
Size 14.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a1084d4e7bdf54bca4b4ce7f7456dbe63840298c9d91bdf0620d0416cb4f3803
BLAKE2b-256 checksum
How to use checksums
9386aec367e05f809b238674e9f74d1ec394477242eb9d47cd220736e75a4e44
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 11, 2026.

Transparency log

Security for circuitpython-rv565 1.2.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for circuitpython_rv565-1.2.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 11, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

1.2.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page