Skip to main content

Discover ONVIF cameras, resolve RTSP streams, and play backchannel audio without GStreamer

Project description

RTSP Backchannel for Python

English | 한국어

Python library and CLI for discovering ONVIF cameras, resolving profile RTSP URIs, and playing one audio file through an ONVIF RTSP backchannel. GStreamer is not required.

Other implementations:

The package starts a backchannel session, sends the complete file at real-time speed, and closes the session. It calls a separately installed ffmpeg executable to decode input audio. PCMA encoding and RTP/RTSP transport are implemented in Python. FFmpeg is not bundled or installed by this package.

Requirements

  • Python 3.11 or later
  • ffmpeg on PATH for file playback
  • A camera that exposes an ONVIF sendonly PCMA audio backchannel

Discovery and stream URI lookup do not require FFmpeg.

Installation

Install a released version from PyPI:

python3 -m pip install rtsp-backchannel

To install the current master source instead of a registry release:

python3 -m pip install \
  "git+https://github.com/GagaKor/rtsp-backchannel.git#subdirectory=python"

Install FFmpeg separately when playback is required:

# macOS
brew install ffmpeg

# Ubuntu or Debian
sudo apt-get update
sudo apt-get install ffmpeg

On Windows, install a build from the FFmpeg download page and add the directory containing ffmpeg.exe to PATH.

Quick Playback

import os

from rtsp_backchannel import play_file

result = play_file(
    host="camera.local",
    user="admin",
    password=os.environ["ONVIF_PASSWORD"],
    file="/absolute/path/to/event.mp3",
    volume=0.05,
)

print(result.packets_sent, result.duration_seconds)

volume must be between 0.0 and 1.0. The tested default is 0.05.

Complete Workflow

Discovery is optional when the camera address is already known. Stream lookup is useful for inspecting ONVIF Media Profiles, but play_file currently opens the first profile independently and does not accept a StreamUri selected by the caller.

import os

from rtsp_backchannel import (
    discover_devices,
    get_stream_uris,
    play_file,
)

password = os.environ["ONVIF_PASSWORD"]

devices = discover_devices(timeout=3.0)
if not devices:
    raise RuntimeError("no ONVIF device found")
camera = devices[0]

streams = get_stream_uris(
    host=camera.ip,
    user="admin",
    password=password,
    device_urls=camera.xaddrs,
    timeout=8.0,
)

for stream in streams:
    print(stream.profile_token, stream.profile_name, stream.uri)

result = play_file(
    host=camera.ip,
    user="admin",
    password=password,
    file="/absolute/path/to/event.mp3",
    volume=0.05,
)

print(result.codec, result.packets_sent, result.duration_seconds)

Public API

discover_devices

discover_devices(
    *,
    timeout: float = 3.0,
    interfaces: list[str] | None = None,
) -> list[DiscoveredDevice]

Searches selected local IPv4 interfaces with WS-Discovery. Omitting interfaces uses addresses detected from hostname resolution and the default route. Pass every local IPv4 address explicitly when multiple NICs or VLANs must be covered. Each result contains ip, xaddrs, scopes, and optional name, hardware, and endpoint_reference fields.

WS-Discovery multicast is normally not routed, so discovery must run from the same subnet or VLAN as the camera.

get_stream_uris

get_stream_uris(
    *,
    host: str,
    user: str,
    password: str,
    device_urls: list[str] | None = None,
    timeout: float = 8.0,
) -> list[StreamUri]

Authenticates with the ONVIF Device and Media services and returns every Media Profile's profile_token, optional profile_name, and uri. Credentials are not inserted into returned RTSP URIs.

play_file

play_file(
    *,
    host: str,
    user: str,
    password: str,
    file: str,
    volume: float = 0.05,
) -> PlaybackResult

PlaybackResult contains codec, sample_rate, payload_type, rtp_channel, encoded_bytes, packets_sent, and duration_seconds. Invalid arguments, authentication failures, network failures, and unsupported camera SDP are reported as exceptions.

CLI

Read the password without echoing it or placing it in shell history:

printf 'Camera password: '
read -rs ONVIF_PASSWORD
printf '\n'
export ONVIF_PASSWORD

Then use the installed command:

# Discover cameras. Output is one JSON object per line.
rtsp-backchannel discover --timeout-ms 3000

# Search explicit interfaces on a multi-NIC or multi-VLAN host.
rtsp-backchannel discover \
  --interface 192.0.2.20 \
  --interface 198.51.100.20

# Resolve RTSP URIs for all ONVIF Media Profiles.
rtsp-backchannel streams \
  --host camera.local \
  --user admin

# Play one file and close the RTSP session.
rtsp-backchannel play \
  --host camera.local \
  --user admin \
  --file '/absolute/path/to/event.mp3' \
  --volume 0.05

The play word is optional for backward compatibility. --pass is available for manual use, but ONVIF_PASSWORD avoids exposing the password in the process argument list.

Playback Behavior

  • PCMA (G.711 A-law) at 8 kHz mono
  • TCP interleaved RTP
  • 40 ms audio packets with real-time pacing
  • RTSP keepalive during long files
  • RTSP teardown after success or failure

The first ONVIF Media Profile must expose a sendonly PCMA audio track. Audio output and decoder configuration are camera-specific; a successful RTSP session does not override disabled or misrouted camera audio output settings.

Development

From the repository root:

PYTHONPATH=python:. python3 -m unittest discover -s python -p 'test_*.py'
python3 -m build python
python3 -m twine check python/dist/*

Release preparation and registry publishing are documented in RELEASING.md.

License

Licensed under either MIT or Apache-2.0, at your option.

This package does not include or link FFmpeg. If an application bundles or redistributes FFmpeg, review the license terms of that FFmpeg build separately. See FFmpeg Legal and THIRD_PARTY_NOTICES.md.

ONVIF is a trademark of ONVIF, Inc. This independent project is not affiliated with or endorsed by ONVIF, Inc. and does not claim ONVIF Profile conformance.

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

rtsp_backchannel-0.1.0.tar.gz (43.9 kB view details)

Uploaded Source

Built Distribution

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

rtsp_backchannel-0.1.0-py3-none-any.whl (45.4 kB view details)

Uploaded Python 3

File details

Details for the file rtsp_backchannel-0.1.0.tar.gz.

File metadata

  • Download URL: rtsp_backchannel-0.1.0.tar.gz
  • Upload date:
  • Size: 43.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.6

File hashes

Hashes for rtsp_backchannel-0.1.0.tar.gz
Algorithm Hash digest
SHA256 61be9529a02b0f7eb0595e2356589e4344bc3f0832d51f3c0ee338486ca1af60
MD5 0674d199cdf28198332d53952e9a90df
BLAKE2b-256 952a45b7cf4332b571e1579c9239394fae565760d83d4c69b09bae57a9fb5be8

See more details on using hashes here.

File details

Details for the file rtsp_backchannel-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for rtsp_backchannel-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 19e8f356c92d572f9e35d1eca946acdcdab2c0d24c8facc088638f3a33906564
MD5 eb759786baf65e6299ff31e999b75880
BLAKE2b-256 51e50e5f93c78471df0b48b3c83ed544414005902eeeb037511cb256ffdb45b3

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