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

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

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
  • 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

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.

PyQt Demo

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 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.

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

  • 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.1.tar.gz (42.3 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.1-py3-none-any.whl (40.7 kB view details)

Uploaded Python 3

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

Hashes for aio_rtsp_toolkit-0.1.1.tar.gz
Algorithm Hash digest
SHA256 86f4938e41769f25159d56a0ef789601d57818948b33c16d58e0b214ac74bf86
MD5 cb7395922da9af3eff4100ff47644203
BLAKE2b-256 0f21c02621c240d70a98940e94de4e01a6dde1f465f17733d33ba24e2f41af73

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for aio_rtsp_toolkit-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 94d0c6c8c1fcbb657418d5b247d3b994e7ea0c83b144746e6964624407b47ac1
MD5 bd1ddaa1659ca7da24fdfad8100b53d9
BLAKE2b-256 402931af7e4c0efdf7dfa00d607998f78c1cd1c2a5f5ec181c49527c66d81db3

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