Skip to main content

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, and TEARDOWN
  • 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-cli
  • rtsp-client-pyqt5
  • rtsp-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]: installs av for RTSP file serving
  • [decode]: installs numpy and av for audio PCM decode helpers
  • [audio]: installs numpy, sounddevice, and av for 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
  • PCMU is 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 close
  • play_count=2: play twice, then close
  • play_count=0: loop forever
  • play_count<0 or omitted play_count: play once, then close

RTSP Client

Session Notes

  • Use enable_video=False or enable_audio=False for single-media sessions
  • At least one of enable_video or enable_audio must be True
  • iter_events(stop_event) accepts threading.Event or asyncio.Event
  • VideoFrameEvent.frame.data contains 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 of PCMFrame objects
  • PCMFrame.pcm_bytes contains interleaved PCM bytes
  • Set output_sample_rate to resample to a target rate
  • Leave output_sample_rate=None to 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.

PyQt Demo

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 to 16000
  • omit --audio-rate: keep the source sample rate without resampling
  • --save-audio audio.wav: save decoded audio to WAV, default is audio.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

  • ConnectResultEvent
  • RtspMethodEvent
  • RtpPacketEvent
  • VideoFrameEvent
  • AudioFrameEvent
  • ClosedEvent

Exceptions

  • RtspError
  • RtspConnectionError
  • RtspTimeoutError
  • RtspProtocolError
  • RtspResponseError

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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

aio_rtsp_toolkit-0.1.3.tar.gz (62.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

aio_rtsp_toolkit-0.1.3-py3-none-any.whl (68.9 kB view details)

Uploaded Python 3

File details

Details for the file aio_rtsp_toolkit-0.1.3.tar.gz.

File metadata

  • Download URL: aio_rtsp_toolkit-0.1.3.tar.gz
  • Upload date:
  • Size: 62.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.10

File hashes

Hashes for aio_rtsp_toolkit-0.1.3.tar.gz
Algorithm Hash digest
SHA256 d293b9e343c0c952394d8ba9cfb5c25a9f22b75fc7f526ce71dee9dcf65a505b
MD5 43de8488bc3121d4d7a5ee05620bc089
BLAKE2b-256 2f733e5502f26bf96b79bd4875c73fbd2bab6c916355144d02727a3a26b7f0da

See more details on using hashes here.

File details

Details for the file aio_rtsp_toolkit-0.1.3-py3-none-any.whl.

File metadata

File hashes

Hashes for aio_rtsp_toolkit-0.1.3-py3-none-any.whl
Algorithm Hash digest
SHA256 c96fa94bc2e9ba2759fd0228ff05c3445273e395dd061d9880ab2c8a214eb7e7
MD5 e94e0d3ff6dbfc916ad4d26f66ccb839
BLAKE2b-256 a01b7164ff69b8581d6d9acf66f9ab1230bfcf5b9d528e0af695161f17379f2e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page