aio-rtsp-toolkit
aio-rtsp-toolkit is an asyncio-based RTSP toolkit for Python. It provides:
- An RTSP client that exposes connection events, RTSP method timing, RTP packets, and assembled audio/video frames
- A lightweight RTSP/TCP server that publishes local media files from a directory tree
It is aimed at cases where "can play" is not enough and you also want visibility into protocol timing, packet flow, frame boundaries.
Highlights
- One async event stream for connect, RTSP, RTP, video, audio, and close events
- Per-method timing for
OPTIONS,DESCRIBE,SETUP,PLAY, andTEARDOWN - H.264 / H.265 video frame splicing to raw Annex B NAL units
- Audio frame extraction for AAC, G.711 A-law, and G.711 mu-law style RTSP streams
- Simple embedded RTSP server for local files
- Session API that works cleanly with
async with
Timing values such as session_elapsed, RTSP elapsed, and RTP recv_tick are process-local monotonic durations. They are not wall-clock timestamps.
Installation
Base install:
pip install aio-rtsp-toolkit
This installs three command-line entry points:
rtsp-client-clirtsp-client-pyqt5rtsp-server
On Windows they are installed as .exe launchers in the active Python Scripts directory. On Linux and macOS they are installed into the active Python bin directory.
Optional extras:
pip install aio-rtsp-toolkit[server]
pip install aio-rtsp-toolkit[decode]
pip install aio-rtsp-toolkit[audio]
pip install aio-rtsp-toolkit[all]
[server]: installsavfor RTSP file serving[decode]: installsnumpyandavfor audio PCM decode helpers[audio]: installsnumpy,sounddevice, andavfor audio playback helpers[all]: installs all optional dependencies
On Linux, audio playback also requires the system PortAudio runtime in addition to the Python sounddevice package. Without PortAudio, rtsp-client-pyqt5 and rtsp-client-cli --audio-mode play will fail at runtime.
On Ubuntu, install it with:
sudo apt update
sudo apt install -y libportaudio2
Quick Start
Client
If the RTSP stream requires authentication, include credentials in the URL, for example rtsp://user:password@192.168.1.122:554/stream. If the username or password contains reserved characters such as @, :, or /, URL-encode them first.
import asyncio
import aio_rtsp_toolkit as aiortsp
async def main():
async with aiortsp.RtspSession("rtsp://127.0.0.1:8554/zhongli.wav", timeout=5) as session:
async for event in session.iter_events():
if isinstance(event, aiortsp.ConnectResultEvent):
print("connected:", event.local_addr, "elapsed:", event.elapsed)
elif isinstance(event, aiortsp.RtspMethodEvent):
print(event.method, event.status_code, event.elapsed, event.session_elapsed)
elif isinstance(event, aiortsp.RtpPacketEvent):
pass
elif isinstance(event, aiortsp.VideoFrameEvent):
print("video ts=", event.frame.timestamp, "size=", len(event.frame.data))
elif isinstance(event, aiortsp.AudioFrameEvent):
print("audio ts=", event.frame.timestamp, "samples=", event.frame.sample_count)
elif isinstance(event, aiortsp.ClosedEvent):
print("closed after", event.session_elapsed, "seconds")
asyncio.run(main())
Server
Publish a directory recursively:
rtsp-server --dir ./media --host 0.0.0.0 --port 8554
Each file becomes an RTSP resource under the same relative path:
rtsp://127.0.0.1:8554/morning_h264.mp4
rtsp://127.0.0.1:8554/subdir/example.wav
Run the server from Python:
import asyncio
import aio_rtsp_toolkit.server as server
async def main():
await server.serve("./media", host="0.0.0.0", port=8554)
asyncio.run(main())
RTSP Server
Current server behavior:
- Transport is RTSP over TCP with interleaved RTP/RTCP only
- Files are resolved relative to the configured root directory
- Files outside the root are rejected
- Unsupported codecs are skipped rather than transcoded
- Session timeout negotiation is not implemented
- The server sends minimal RTCP Sender Report and BYE packets for configured tracks
Supported Inputs
Accepted file extensions: .mp4, .mkv, .wav and .aac.
Supported media handling:
- Video: H.264 and H.265
- AAC audio: served as
mpeg4-generic - WAV audio: PCM WAV input is resampled with PyAV and served as
PCMA/8000/1 PCMUis not advertised by the current server implementation
Loop Control
Use the play_count query parameter:
rtsp://127.0.0.1:8554/zhongli.wav?play_count=1
rtsp://127.0.0.1:8554/zhongli.wav?play_count=2
rtsp://127.0.0.1:8554/zhongli.wav?play_count=0
play_count=1: play once, then closeplay_count=2: play twice, then closeplay_count=0: loop foreverplay_count<0or omittedplay_count: play once, then close
RTSP Client
Session Notes
- Use
enable_video=Falseorenable_audio=Falsefor single-media sessions - At least one of
enable_videoorenable_audiomust beTrue iter_events(stop_event)acceptsthreading.Eventorasyncio.EventVideoFrameEvent.frame.datacontains raw Annex B video data
PyAV Decode Example
Use this pattern if you want to decode video frames after DESCRIBE:
import asyncio
import fractions
import av
import aio_rtsp_toolkit as aiortsp
async def main():
codec = None
time_base = fractions.Fraction(1, 90000)
async with aiortsp.RtspSession("rtsp://127.0.0.1:8554/morning_h264.mp4", timeout=5) as session:
async for event in session.iter_events():
if isinstance(event, aiortsp.RtspMethodEvent) and event.method == "DESCRIBE":
video_sdp = event.response.sdp.get("video", {})
codec_name = video_sdp.get("codec_name", "").lower()
if codec_name:
av_codec_name = aiortsp.HEVCCodecName if codec_name == aiortsp.H265CodecName else codec_name
codec = av.CodecContext.create(av_codec_name, "r")
time_base = fractions.Fraction(1, video_sdp.get("clock_rate", 90000))
for key in ("sps", "pps"):
extra = video_sdp.get(key)
if extra:
codec.parse(extra)
elif isinstance(event, aiortsp.VideoFrameEvent) and codec is not None:
for packet in codec.parse(event.frame.data):
packet.pts = packet.dts = event.frame.timestamp
packet.time_base = time_base
for decoded in codec.decode(packet):
print("decoded video frame pts=", decoded.pts)
asyncio.run(main())
Optional Audio Decode / Playback Helpers
aio_rtsp_toolkit.audio_decode.AudioPCMDecoder can decode audio frames to PCM s16le.
feed()returns a list ofPCMFrameobjectsPCMFrame.pcm_bytescontains interleaved PCM bytes- Set
output_sample_rateto resample to a target rate - Leave
output_sample_rate=Noneto keep the source sample rate
aio_rtsp_toolkit.audio_playback.SoundDeviceAudioPlayer builds on the decoder in audio_decode and plays the decoded PCM immediately.
- Install the
[decode]extra for PCM decode only, or[audio]for decode + playback - G.711 A-law and mu-law playback do not require PyAV
- AAC and AAC-LATM playback do require PyAV
- On Linux, playback also requires the system PortAudio runtime
Demos
PyQt Demo
pyqt_demo.py shows how to consume RtspSession events, decode video with PyAV, and play audio with sounddevice.
It also requires PyQt5 in addition to the [audio] extra.
On Linux it also requires the system PortAudio runtime.
rtsp-client-pyqt5
CLI Demo
cli_demo.py logs RTSP timing, writes raw video to disk, and decodes frames with PyAV.
You can still change the source defaults in the script, but it now also supports runtime control:
rtsp-client-cli -u rtsp://127.0.0.1:8554/morning_h264.mp4
rtsp-client-cli -u rtsp://127.0.0.1:8554/morning_h264.mp4 --audio-mode decode --audio-rate 16000
rtsp-client-cli -u rtsp://127.0.0.1:8554/morning_h264.mp4 --save-audio ""
rtsp-client-cli -u rtsp://127.0.0.1:8554/morning_h264.mp4 --audio-mode play --audio-rate 16000 --session-id s01 --log-prefix cam01
--audio-mode decode: decode audio to PCM--audio-mode play: decode audio and play it--audio-rate 16000: resample output PCM to16000- omit
--audio-rate: keep the source sample rate without resampling --save-audio audio.wav: save decoded audio to WAV, default isaudio.wav--save-audio "": disable audio saving--session-id s01: set the session id used by RTSP/audio logs--log-prefix cam01: set the log prefix used by RTSP/audio logs
Install the [all] extra for PyAV, then install Pillow if you want to save the first decoded frame as an image.
On Linux, --audio-mode play also requires the system PortAudio runtime.
rtsp-client-cli -u rtsp://127.0.0.1:8554/morning_h264.mp4
API Summary
RtspSession(rtsp_url, forward_address=None, timeout=4, session_id=None, enable_video=True, enable_audio=True, log_type=RtspClientMsgType.RTSP, log_prefix='')
High-level reusable RTSP session object. Use it with async with and consume events through iter_events().
forward_address lets you keep the original RTSP URL while connecting through another TCP endpoint, such as a relay, tunnel, or forwarded host.
RtspSession.iter_events(stop_event=None) -> AsyncGenerator[RtspEvent, None]
Starts the session, yields typed events, and closes the socket when iteration ends or fails.
open_session(rtsp_url, forward_address=None, timeout=4, session_id=None, enable_video=True, enable_audio=True, log_type=RtspClientMsgType.RTSP, log_prefix='') -> RtspSession
Convenience helper that returns a RtspSession.
Event Types
ConnectResultEventRtspMethodEventRtpPacketEventVideoFrameEventAudioFrameEventClosedEvent
Exceptions
RtspErrorRtspConnectionErrorRtspTimeoutErrorRtspProtocolErrorRtspResponseError
Notes
If you only need playback, higher-level PyAV or ffmpeg-based wrappers may be simpler. This project is most useful when you need direct access to RTSP/RTP behavior, timing, and frame-level diagnostics.
Metadata
Release files for aio-rtsp-toolkit 0.1.4
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| aio_rtsp_toolkit-0.1.4.tar.gz | 62.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| aio_rtsp_toolkit-0.1.4-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 132.0 kB
Release files / aio_rtsp_toolkit-0.1.4.tar.gz
| Download URL | aio_rtsp_toolkit-0.1.4.tar.gz |
|---|---|
| Size | 62.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
925f6da5ce3698c8ac78310b478776affcdb5356fb02bcfc3c0f0754ad229648
|
|
BLAKE2b-256 checksum How to use checksums |
addec72b25d8c7fa858b3addfc532afde94cf747d9ba3e7435b9906a643517cb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.10
|
Release files / aio_rtsp_toolkit-0.1.4-py3-none-any.whl
| Download URL | aio_rtsp_toolkit-0.1.4-py3-none-any.whl |
|---|---|
| Size | 69.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
301c7a452ef5f067a512c2e0605dbf053e17159c601547964ac02ce9ddee454a
|
|
BLAKE2b-256 checksum How to use checksums |
626605ca4d4120b51eb9ed1e574667d00a6f4169eccfa51dec39afec0343d4cd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.10
|