Async RTSP client with protocol timing, RTP and frame events, plus a lightweight RTSP/TCP file server
Project description
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
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
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:
python server_demo.py --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 as aiortsp
async def main():
await aiortsp.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
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.
python pyqt_demo.py
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:
python cli_demo.py -u rtsp://127.0.0.1:8554/morning_h264.mp4
python cli_demo.py -u rtsp://127.0.0.1:8554/morning_h264.mp4 --audio-mode decode --audio-rate 16000
python cli_demo.py -u rtsp://127.0.0.1:8554/morning_h264.mp4 --save-audio ""
python cli_demo.py -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.
python cli_demo.py -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.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file aio_rtsp_toolkit-0.1.1.tar.gz.
File metadata
- Download URL: aio_rtsp_toolkit-0.1.1.tar.gz
- Upload date:
- Size: 42.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
86f4938e41769f25159d56a0ef789601d57818948b33c16d58e0b214ac74bf86
|
|
| MD5 |
cb7395922da9af3eff4100ff47644203
|
|
| BLAKE2b-256 |
0f21c02621c240d70a98940e94de4e01a6dde1f465f17733d33ba24e2f41af73
|
File details
Details for the file aio_rtsp_toolkit-0.1.1-py3-none-any.whl.
File metadata
- Download URL: aio_rtsp_toolkit-0.1.1-py3-none-any.whl
- Upload date:
- Size: 40.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
94d0c6c8c1fcbb657418d5b247d3b994e7ea0c83b144746e6964624407b47ac1
|
|
| MD5 |
bd1ddaa1659ca7da24fdfad8100b53d9
|
|
| BLAKE2b-256 |
402931af7e4c0efdf7dfa00d607998f78c1cd1c2a5f5ec181c49527c66d81db3
|