Full Code of AlexxIT/go2rtc for AI

master dc1685e9cf7a cached
530 files
1.7 MB
571.1k tokens
3570 symbols
1 requests
Download .txt
Showing preview only (1,900K chars total). Download the full file or copy to clipboard to get everything.
Repository: AlexxIT/go2rtc
Branch: master
Commit: dc1685e9cf7a
Files: 530
Total size: 1.7 MB

Directory structure:
gitextract_ui7an4hw/

├── .github/
│   └── workflows/
│       ├── build.yml
│       ├── gh-pages.yml
│       └── test.yml
├── .gitignore
├── LICENSE
├── README.md
├── docker/
│   ├── Dockerfile
│   ├── README.md
│   ├── hardware.Dockerfile
│   └── rockchip.Dockerfile
├── examples/
│   ├── go2rtc_hass/
│   │   └── main.go
│   ├── go2rtc_mjpeg/
│   │   └── main.go
│   ├── go2rtc_rtsp/
│   │   └── main.go
│   ├── homekit_info/
│   │   └── main.go
│   ├── mdns/
│   │   └── main.go
│   ├── mod_pinggy/
│   │   ├── go.mod
│   │   ├── go.sum
│   │   └── main.go
│   ├── onvif_client/
│   │   ├── README.md
│   │   └── main.go
│   ├── rtsp_client/
│   │   └── main.go
│   └── tutk_decoder/
│       ├── README.md
│       └── main.go
├── go.mod
├── go.sum
├── internal/
│   ├── README.md
│   ├── alsa/
│   │   ├── README.md
│   │   ├── alsa.go
│   │   └── alsa_linux.go
│   ├── api/
│   │   ├── README.md
│   │   ├── api.go
│   │   ├── config.go
│   │   ├── static.go
│   │   └── ws/
│   │       ├── README.md
│   │       └── ws.go
│   ├── app/
│   │   ├── README.md
│   │   ├── app.go
│   │   ├── config.go
│   │   ├── log.go
│   │   └── storage.go
│   ├── bubble/
│   │   ├── README.md
│   │   └── bubble.go
│   ├── debug/
│   │   ├── README.md
│   │   ├── debug.go
│   │   └── stack.go
│   ├── doorbird/
│   │   ├── README.md
│   │   └── doorbird.go
│   ├── dvrip/
│   │   ├── README.md
│   │   └── dvrip.go
│   ├── echo/
│   │   ├── README.md
│   │   └── echo.go
│   ├── eseecloud/
│   │   ├── README.md
│   │   └── eseecloud.go
│   ├── exec/
│   │   ├── README.md
│   │   └── exec.go
│   ├── expr/
│   │   ├── README.md
│   │   └── expr.go
│   ├── ffmpeg/
│   │   ├── README.md
│   │   ├── api.go
│   │   ├── device/
│   │   │   ├── README.md
│   │   │   ├── device_bsd.go
│   │   │   ├── device_darwin.go
│   │   │   ├── device_unix.go
│   │   │   ├── device_windows.go
│   │   │   └── devices.go
│   │   ├── ffmpeg.go
│   │   ├── ffmpeg_test.go
│   │   ├── hardware/
│   │   │   ├── README.md
│   │   │   ├── hardware.go
│   │   │   ├── hardware_bsd.go
│   │   │   ├── hardware_darwin.go
│   │   │   ├── hardware_unix.go
│   │   │   └── hardware_windows.go
│   │   ├── jpeg.go
│   │   ├── jpeg_test.go
│   │   ├── producer.go
│   │   ├── version.go
│   │   └── virtual/
│   │       ├── virtual.go
│   │       └── virtual_test.go
│   ├── flussonic/
│   │   ├── README.md
│   │   └── flussonic.go
│   ├── gopro/
│   │   ├── README.md
│   │   └── gopro.go
│   ├── hass/
│   │   ├── README.md
│   │   ├── api.go
│   │   └── hass.go
│   ├── hls/
│   │   ├── README.md
│   │   ├── hls.go
│   │   ├── session.go
│   │   └── ws.go
│   ├── homekit/
│   │   ├── README.md
│   │   ├── api.go
│   │   ├── homekit.go
│   │   └── server.go
│   ├── http/
│   │   ├── README.md
│   │   └── http.go
│   ├── isapi/
│   │   ├── README.md
│   │   └── init.go
│   ├── ivideon/
│   │   ├── README.md
│   │   └── ivideon.go
│   ├── kasa/
│   │   ├── README.md
│   │   └── kasa.go
│   ├── mjpeg/
│   │   ├── README.md
│   │   └── mjpeg.go
│   ├── mp4/
│   │   ├── README.md
│   │   ├── mp4.go
│   │   └── ws.go
│   ├── mpeg/
│   │   ├── README.md
│   │   └── mpeg.go
│   ├── multitrans/
│   │   ├── README.md
│   │   └── multitrans.go
│   ├── nest/
│   │   ├── README.md
│   │   └── init.go
│   ├── ngrok/
│   │   ├── README.md
│   │   └── ngrok.go
│   ├── onvif/
│   │   ├── README.md
│   │   └── onvif.go
│   ├── pinggy/
│   │   ├── README.md
│   │   └── pinggy.go
│   ├── ring/
│   │   ├── README.md
│   │   └── ring.go
│   ├── roborock/
│   │   ├── README.md
│   │   └── roborock.go
│   ├── rtmp/
│   │   ├── README.md
│   │   └── rtmp.go
│   ├── rtsp/
│   │   ├── README.md
│   │   └── rtsp.go
│   ├── srtp/
│   │   ├── README.md
│   │   └── srtp.go
│   ├── streams/
│   │   ├── README.md
│   │   ├── add_consumer.go
│   │   ├── api.go
│   │   ├── api_test.go
│   │   ├── dot.go
│   │   ├── handlers.go
│   │   ├── helpers.go
│   │   ├── play.go
│   │   ├── preload.go
│   │   ├── producer.go
│   │   ├── publish.go
│   │   ├── stream.go
│   │   ├── stream_test.go
│   │   └── streams.go
│   ├── tapo/
│   │   ├── README.md
│   │   └── tapo.go
│   ├── tuya/
│   │   ├── README.md
│   │   └── tuya.go
│   ├── v4l2/
│   │   ├── README.md
│   │   ├── v4l2.go
│   │   └── v4l2_linux.go
│   ├── webrtc/
│   │   ├── README.md
│   │   ├── candidates.go
│   │   ├── client.go
│   │   ├── client_creality.go
│   │   ├── kinesis.go
│   │   ├── milestone.go
│   │   ├── openipc.go
│   │   ├── server.go
│   │   ├── switchbot.go
│   │   ├── webrtc.go
│   │   └── webrtc_test.go
│   ├── webtorrent/
│   │   ├── README.md
│   │   ├── init.go
│   │   └── tracker.go
│   ├── wyoming/
│   │   ├── README.md
│   │   └── wyoming.go
│   ├── wyze/
│   │   ├── README.md
│   │   └── wyze.go
│   ├── xiaomi/
│   │   ├── README.md
│   │   └── xiaomi.go
│   └── yandex/
│       ├── README.md
│       ├── goloom.go
│       └── yandex.go
├── main.go
├── package.json
├── pkg/
│   ├── README.md
│   ├── aac/
│   │   ├── README.md
│   │   ├── aac.go
│   │   ├── aac_test.go
│   │   ├── adts.go
│   │   ├── consumer.go
│   │   ├── producer.go
│   │   ├── rtp.go
│   │   └── rtp_test.go
│   ├── alsa/
│   │   ├── README.md
│   │   ├── capture_linux.go
│   │   ├── device/
│   │   │   ├── asound_32bit.go
│   │   │   ├── asound_64bit.go
│   │   │   ├── asound_arch.c
│   │   │   ├── asound_mipsle.go
│   │   │   ├── device_linux.go
│   │   │   └── ioctl_linux.go
│   │   ├── open_linux.go
│   │   └── playback_linux.go
│   ├── ascii/
│   │   ├── README.md
│   │   └── ascii.go
│   ├── bits/
│   │   ├── reader.go
│   │   └── writer.go
│   ├── bubble/
│   │   ├── client.go
│   │   └── producer.go
│   ├── core/
│   │   ├── README.md
│   │   ├── codec.go
│   │   ├── connection.go
│   │   ├── core.go
│   │   ├── core_test.go
│   │   ├── helpers.go
│   │   ├── listener.go
│   │   ├── media.go
│   │   ├── media_test.go
│   │   ├── node.go
│   │   ├── readbuffer.go
│   │   ├── readbuffer_test.go
│   │   ├── slices.go
│   │   ├── track.go
│   │   ├── track_test.go
│   │   ├── waiter.go
│   │   ├── worker.go
│   │   └── writebuffer.go
│   ├── creds/
│   │   ├── README.md
│   │   ├── creds.go
│   │   ├── secrets.go
│   │   └── secrets_test.go
│   ├── debug/
│   │   ├── conn.go
│   │   └── debug.go
│   ├── doorbird/
│   │   └── backchannel.go
│   ├── dvrip/
│   │   ├── backchannel.go
│   │   ├── client.go
│   │   ├── dvrip.go
│   │   └── producer.go
│   ├── eseecloud/
│   │   └── eseecloud.go
│   ├── expr/
│   │   ├── expr.go
│   │   └── expr_test.go
│   ├── ffmpeg/
│   │   ├── README.md
│   │   └── ffmpeg.go
│   ├── flussonic/
│   │   └── flussonic.go
│   ├── flv/
│   │   ├── amf/
│   │   │   ├── amf.go
│   │   │   └── amf_test.go
│   │   ├── consumer.go
│   │   ├── flv_test.go
│   │   ├── muxer.go
│   │   └── producer.go
│   ├── gopro/
│   │   ├── discovery.go
│   │   └── producer.go
│   ├── h264/
│   │   ├── README.md
│   │   ├── annexb/
│   │   │   ├── annexb.go
│   │   │   └── annexb_test.go
│   │   ├── avc.go
│   │   ├── avcc.go
│   │   ├── h264.go
│   │   ├── h264_test.go
│   │   ├── mpeg4.go
│   │   ├── payloader.go
│   │   ├── rtp.go
│   │   └── sps.go
│   ├── h265/
│   │   ├── README.md
│   │   ├── avc.go
│   │   ├── avcc.go
│   │   ├── h265_test.go
│   │   ├── helper.go
│   │   ├── mpeg4.go
│   │   ├── payloader.go
│   │   ├── rtp.go
│   │   └── sps.go
│   ├── hap/
│   │   ├── README.md
│   │   ├── accessory.go
│   │   ├── camera/
│   │   │   ├── README.md
│   │   │   ├── accessory.go
│   │   │   ├── accessory_test.go
│   │   │   ├── ch114_supported_video.go
│   │   │   ├── ch115_supported_audio.go
│   │   │   ├── ch116_supported_rtp.go
│   │   │   ├── ch117_selected_stream.go
│   │   │   ├── ch118_setup_endpoints.go
│   │   │   ├── ch120_streaming_status.go
│   │   │   ├── ch130_data_stream_transport.go
│   │   │   ├── ch131_data_stream.go
│   │   │   ├── ch205.go
│   │   │   ├── ch206.go
│   │   │   ├── ch207.go
│   │   │   ├── ch209.go
│   │   │   └── stream.go
│   │   ├── chacha20poly1305/
│   │   │   └── chacha20poly1305.go
│   │   ├── character.go
│   │   ├── client.go
│   │   ├── client_http.go
│   │   ├── client_pairing.go
│   │   ├── conn.go
│   │   ├── curve25519/
│   │   │   └── curve25519.go
│   │   ├── ed25519/
│   │   │   └── ed25519.go
│   │   ├── hds/
│   │   │   ├── hds.go
│   │   │   └── hds_test.go
│   │   ├── helpers.go
│   │   ├── hkdf/
│   │   │   └── hkdf.go
│   │   ├── server.go
│   │   ├── setup/
│   │   │   ├── setup.go
│   │   │   └── setup_test.go
│   │   └── tlv8/
│   │       ├── tlv8.go
│   │       └── tlv8_test.go
│   ├── hass/
│   │   ├── api.go
│   │   └── client.go
│   ├── hls/
│   │   ├── producer.go
│   │   └── reader.go
│   ├── homekit/
│   │   ├── consumer.go
│   │   ├── helpers.go
│   │   ├── log/
│   │   │   └── debug.go
│   │   ├── producer.go
│   │   ├── proxy.go
│   │   └── server.go
│   ├── image/
│   │   └── producer.go
│   ├── ioctl/
│   │   ├── README.md
│   │   ├── ioctl.go
│   │   ├── ioctl_be.go
│   │   ├── ioctl_le.go
│   │   ├── ioctl_linux.go
│   │   └── ioctl_test.go
│   ├── isapi/
│   │   ├── backchannel.go
│   │   └── client.go
│   ├── iso/
│   │   ├── atoms.go
│   │   ├── codecs.go
│   │   ├── iso.go
│   │   └── reader.go
│   ├── ivideon/
│   │   └── ivideon.go
│   ├── kasa/
│   │   └── producer.go
│   ├── magic/
│   │   ├── bitstream/
│   │   │   └── producer.go
│   │   ├── keyframe.go
│   │   ├── mjpeg/
│   │   │   └── producer.go
│   │   └── producer.go
│   ├── mdns/
│   │   ├── README.md
│   │   ├── client.go
│   │   ├── mdns_test.go
│   │   ├── server.go
│   │   ├── syscall.go
│   │   ├── syscall_bsd.go
│   │   └── syscall_windows.go
│   ├── mjpeg/
│   │   ├── README.md
│   │   ├── consumer.go
│   │   ├── helpers.go
│   │   ├── jpeg.go
│   │   ├── mjpeg_test.go
│   │   ├── rfc2435.go
│   │   ├── rtp.go
│   │   └── writer.go
│   ├── mp4/
│   │   ├── README.md
│   │   ├── consumer.go
│   │   ├── demuxer.go
│   │   ├── helpers.go
│   │   ├── keyframe.go
│   │   ├── mime.go
│   │   └── muxer.go
│   ├── mpegts/
│   │   ├── README.md
│   │   ├── checksum.go
│   │   ├── consumer.go
│   │   ├── demuxer.go
│   │   ├── muxer.go
│   │   ├── opus.go
│   │   └── producer.go
│   ├── mpjpeg/
│   │   ├── multipart.go
│   │   └── producer.go
│   ├── mqtt/
│   │   ├── client.go
│   │   └── message.go
│   ├── multitrans/
│   │   └── client.go
│   ├── nest/
│   │   ├── api.go
│   │   └── client.go
│   ├── ngrok/
│   │   └── ngrok.go
│   ├── onvif/
│   │   ├── README.md
│   │   ├── client.go
│   │   ├── envelope.go
│   │   ├── helpers.go
│   │   ├── onvif_test.go
│   │   └── server.go
│   ├── opus/
│   │   ├── README.md
│   │   ├── homekit.go
│   │   └── opus.go
│   ├── pcm/
│   │   ├── backchannel.go
│   │   ├── flac.go
│   │   ├── handlers.go
│   │   ├── pcm.go
│   │   ├── pcm_test.go
│   │   ├── pcma.go
│   │   ├── pcmu.go
│   │   ├── producer.go
│   │   ├── producer_sync.go
│   │   ├── s16le/
│   │   │   └── s16le.go
│   │   └── v1/
│   │       ├── pcm.go
│   │       └── pcm_test.go
│   ├── pinggy/
│   │   └── pinggy.go
│   ├── probe/
│   │   └── consumer.go
│   ├── ring/
│   │   ├── api.go
│   │   ├── client.go
│   │   ├── snapshot.go
│   │   └── ws.go
│   ├── roborock/
│   │   ├── api.go
│   │   ├── client.go
│   │   ├── iot/
│   │   │   ├── client.go
│   │   │   └── crypto.go
│   │   └── producer.go
│   ├── rtmp/
│   │   ├── README.md
│   │   ├── client.go
│   │   ├── conn.go
│   │   ├── flv.go
│   │   └── server.go
│   ├── rtsp/
│   │   ├── README.md
│   │   ├── client.go
│   │   ├── client_test.go
│   │   ├── conn.go
│   │   ├── consumer.go
│   │   ├── helpers.go
│   │   ├── producer.go
│   │   ├── rtsp_test.go
│   │   └── server.go
│   ├── shell/
│   │   ├── command.go
│   │   ├── procattr.go
│   │   ├── procattr_linux.go
│   │   ├── shell.go
│   │   └── shell_test.go
│   ├── srtp/
│   │   ├── server.go
│   │   └── session.go
│   ├── tapo/
│   │   ├── backchannel.go
│   │   ├── client.go
│   │   └── producer.go
│   ├── tcp/
│   │   ├── auth.go
│   │   ├── dial.go
│   │   ├── request.go
│   │   ├── textproto.go
│   │   ├── textproto_test.go
│   │   └── websocket/
│   │       ├── client.go
│   │       └── dial.go
│   ├── tutk/
│   │   ├── codec.go
│   │   ├── conn.go
│   │   ├── crypto.go
│   │   ├── crypto_test.go
│   │   ├── dtls/
│   │   │   ├── auth.go
│   │   │   ├── cipher.go
│   │   │   ├── conn_dtls.go
│   │   │   └── dtls.go
│   │   ├── frame.go
│   │   ├── helpers.go
│   │   ├── session0.go
│   │   ├── session16.go
│   │   └── session25.go
│   ├── tuya/
│   │   ├── README.md
│   │   ├── client.go
│   │   ├── cloud_api.go
│   │   ├── helper.go
│   │   ├── interface.go
│   │   ├── mqtt.go
│   │   └── smart_api.go
│   ├── v4l2/
│   │   ├── device/
│   │   │   ├── README.md
│   │   │   ├── device.go
│   │   │   ├── formats.go
│   │   │   ├── videodev2_386.go
│   │   │   ├── videodev2_arch.c
│   │   │   ├── videodev2_arm.go
│   │   │   ├── videodev2_mipsle.go
│   │   │   └── videodev2_x64.go
│   │   └── producer.go
│   ├── wav/
│   │   ├── backchannel.go
│   │   ├── producer.go
│   │   └── wav.go
│   ├── webrtc/
│   │   ├── README.md
│   │   ├── api.go
│   │   ├── client.go
│   │   ├── client_test.go
│   │   ├── conn.go
│   │   ├── consumer.go
│   │   ├── helpers.go
│   │   ├── producer.go
│   │   ├── server.go
│   │   ├── track.go
│   │   └── webrtc_test.go
│   ├── webtorrent/
│   │   ├── client.go
│   │   ├── crypto.go
│   │   └── server.go
│   ├── wyoming/
│   │   ├── README.md
│   │   ├── api.go
│   │   ├── backchannel.go
│   │   ├── expr.go
│   │   ├── mic.go
│   │   ├── producer.go
│   │   ├── satellite.go
│   │   ├── snd.go
│   │   ├── wakeword.go
│   │   └── wyoming.go
│   ├── wyze/
│   │   ├── backchannel.go
│   │   ├── client.go
│   │   ├── cloud.go
│   │   └── producer.go
│   ├── xiaomi/
│   │   ├── cloud.go
│   │   ├── crypto/
│   │   │   └── crypto.go
│   │   ├── legacy/
│   │   │   ├── client.go
│   │   │   └── producer.go
│   │   ├── miss/
│   │   │   ├── backchannel.go
│   │   │   ├── client.go
│   │   │   ├── cs2/
│   │   │   │   └── conn.go
│   │   │   └── producer.go
│   │   └── producer.go
│   ├── xnet/
│   │   ├── net.go
│   │   └── tls/
│   │       └── tls.go
│   ├── y4m/
│   │   ├── README.md
│   │   ├── consumer.go
│   │   ├── producer.go
│   │   └── y4m.go
│   ├── yaml/
│   │   ├── yaml.go
│   │   └── yaml_test.go
│   └── yandex/
│       └── session.go
├── scripts/
│   ├── README.md
│   ├── build.cmd
│   └── build.sh
├── website/
│   ├── .vitepress/
│   │   └── config.js
│   ├── README.md
│   ├── api/
│   │   ├── index.html
│   │   └── openapi.yaml
│   ├── manifest.json
│   └── webtorrent/
│       └── index.html
└── www/
    ├── README.md
    ├── add.html
    ├── config.html
    ├── hls.html
    ├── index.html
    ├── links.html
    ├── log.html
    ├── main.js
    ├── net.html
    ├── schema.json
    ├── static.go
    ├── stream.html
    ├── video-rtc.js
    ├── video-stream.js
    ├── webrtc-sync.html
    └── webrtc.html

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/workflows/build.yml
================================================
name: Build and Push

on:
  workflow_dispatch:
  push:
    branches:
      - 'master'
    tags:
      - 'v*'

jobs:
  build-binaries:
    name: Build binaries
    runs-on: ubuntu-latest
    env: { CGO_ENABLED: 0 }
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup Go
        uses: actions/setup-go@v5
        with: { go-version: '1.25' }

      - name: Build go2rtc_win64
        env: { GOOS: windows, GOARCH: amd64 }
        run: go build -ldflags "-s -w" -trimpath
      - name: Upload go2rtc_win64
        uses: actions/upload-artifact@v4
        with: { name: go2rtc_win64, path: go2rtc.exe }

      - name: Build go2rtc_win32
        env: { GOOS: windows, GOARCH: 386 }
        run: go build -ldflags "-s -w" -trimpath
      - name: Upload go2rtc_win32
        uses: actions/upload-artifact@v4
        with: { name: go2rtc_win32, path: go2rtc.exe }

      - name: Build go2rtc_win_arm64
        env: { GOOS: windows, GOARCH: arm64 }
        run: go build -ldflags "-s -w" -trimpath
      - name: Upload go2rtc_win_arm64
        uses: actions/upload-artifact@v4
        with: { name: go2rtc_win_arm64, path: go2rtc.exe }

      - name: Build go2rtc_linux_amd64
        env: { GOOS: linux, GOARCH: amd64 }
        run: go build -ldflags "-s -w" -trimpath
      - name: Upload go2rtc_linux_amd64
        uses: actions/upload-artifact@v4
        with: { name: go2rtc_linux_amd64, path: go2rtc }

      - name: Build go2rtc_linux_i386
        env: { GOOS: linux, GOARCH: 386 }
        run: go build -ldflags "-s -w" -trimpath
      - name: Upload go2rtc_linux_i386
        uses: actions/upload-artifact@v4
        with: { name: go2rtc_linux_i386, path: go2rtc }

      - name: Build go2rtc_linux_arm64
        env: { GOOS: linux, GOARCH: arm64 }
        run: go build -ldflags "-s -w" -trimpath
      - name: Upload go2rtc_linux_arm64
        uses: actions/upload-artifact@v4
        with: { name: go2rtc_linux_arm64, path: go2rtc }

      - name: Build go2rtc_linux_arm
        env: { GOOS: linux, GOARCH: arm, GOARM: 7 }
        run: go build -ldflags "-s -w" -trimpath
      - name: Upload go2rtc_linux_arm
        uses: actions/upload-artifact@v4
        with: { name: go2rtc_linux_arm, path: go2rtc }

      - name: Build go2rtc_linux_armv6
        env: { GOOS: linux, GOARCH: arm, GOARM: 6 }
        run: go build -ldflags "-s -w" -trimpath
      - name: Upload go2rtc_linux_armv6
        uses: actions/upload-artifact@v4
        with: { name: go2rtc_linux_armv6, path: go2rtc }

      - name: Build go2rtc_linux_mipsel
        env: { GOOS: linux, GOARCH: mipsle }
        run: go build -ldflags "-s -w" -trimpath
      - name: Upload go2rtc_linux_mipsel
        uses: actions/upload-artifact@v4
        with: { name: go2rtc_linux_mipsel, path: go2rtc }

      - name: Build go2rtc_mac_amd64
        env: { GOOS: darwin, GOARCH: amd64 }
        run: go build -ldflags "-s -w" -trimpath
      - name: Upload go2rtc_mac_amd64
        uses: actions/upload-artifact@v4
        with: { name: go2rtc_mac_amd64, path: go2rtc }

      - name: Build go2rtc_mac_arm64
        env: { GOOS: darwin, GOARCH: arm64 }
        run: go build -ldflags "-s -w" -trimpath
      - name: Upload go2rtc_mac_arm64
        uses: actions/upload-artifact@v4
        with: { name: go2rtc_mac_arm64, path: go2rtc }

      - name: Build go2rtc_freebsd_amd64
        env: { GOOS: freebsd, GOARCH: amd64 }
        run: go build -ldflags "-s -w" -trimpath
      - name: Upload go2rtc_freebsd_amd64
        uses: actions/upload-artifact@v4
        with: { name: go2rtc_freebsd_amd64, path: go2rtc }

      - name: Build go2rtc_freebsd_arm64
        env: { GOOS: freebsd, GOARCH: arm64 }
        run: go build -ldflags "-s -w" -trimpath
      - name: Upload go2rtc_freebsd_arm64
        uses: actions/upload-artifact@v4
        with: { name: go2rtc_freebsd_arm64, path: go2rtc }

  docker-master:
    name: Build docker master
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Docker meta
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: |
            name=${{ github.repository }},enable=${{ github.event.repository.fork == false }}
            ghcr.io/${{ github.repository }}
          tags: |
            type=ref,event=branch
            type=semver,pattern={{version}},enable=false
            type=match,pattern=v(.*),group=1

      - name: Set up QEMU
        uses: docker/setup-qemu-action@v3

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Login to DockerHub
        if: github.event_name == 'push' && github.event.repository.fork == false
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - name: Login to GitHub Container Registry
        if: github.event_name == 'push'
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          context: .
          file: docker/Dockerfile
          platforms: |
            linux/amd64
            linux/386
            linux/arm/v6
            linux/arm/v7
            linux/arm64/v8
          push: ${{ github.event_name != 'pull_request' }}
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

  docker-hardware:
    name: Build docker hardware
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Docker meta
        id: meta-hw
        uses: docker/metadata-action@v5
        with:
          images: |
            name=${{ github.repository }},enable=${{ github.event.repository.fork == false }}
            ghcr.io/${{ github.repository }}
          flavor: |
            suffix=-hardware,onlatest=true
            latest=auto
          tags: |
            type=ref,event=branch
            type=semver,pattern={{version}},enable=false
            type=match,pattern=v(.*),group=1

      - name: Set up QEMU
        uses: docker/setup-qemu-action@v3

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Login to DockerHub
        if: github.event_name == 'push' && github.event.repository.fork == false
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - name: Login to GitHub Container Registry
        if: github.event_name == 'push'
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          context: .
          file: docker/hardware.Dockerfile
          platforms: linux/amd64
          push: ${{ github.event_name != 'pull_request' }}
          tags: ${{ steps.meta-hw.outputs.tags }}
          labels: ${{ steps.meta-hw.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

  docker-rockchip:
    name: Build docker rockchip
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Docker meta
        id: meta-rk
        uses: docker/metadata-action@v5
        with:
          images: |
            name=${{ github.repository }},enable=${{ github.event.repository.fork == false }}
            ghcr.io/${{ github.repository }}
          flavor: |
            suffix=-rockchip,onlatest=true
            latest=auto
          tags: |
            type=ref,event=branch
            type=semver,pattern={{version}},enable=false
            type=match,pattern=v(.*),group=1

      - name: Set up QEMU
        uses: docker/setup-qemu-action@v3

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Login to DockerHub
        if: github.event_name == 'push' && github.event.repository.fork == false
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - name: Login to GitHub Container Registry
        if: github.event_name == 'push'
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          context: .
          file: docker/rockchip.Dockerfile
          platforms: linux/arm64
          push: ${{ github.event_name != 'pull_request' }}
          tags: ${{ steps.meta-rk.outputs.tags }}
          labels: ${{ steps.meta-rk.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max


================================================
FILE: .github/workflows/gh-pages.yml
================================================
# Simple workflow for deploying static content to GitHub Pages
name: Deploy static content to Pages

on:
  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:

# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
  contents: read
  pages: write
  id-token: write

# Allow one concurrent deployment
concurrency:
  group: "pages"
  cancel-in-progress: true

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v5
      - name: Setup Node
        uses: actions/setup-node@v6
        with:
          node-version: 24
          package-manager-cache: false
      - name: Install dependencies
        run: npm install --no-package-lock
      - name: Build docs
        run: npm run docs:build
      - name: Copy docs into website
        run: rsync -a --exclude '.vitepress/' --exclude 'README.md' website/ website/.vitepress/dist/
      - name: Setup Pages
        uses: actions/configure-pages@v5
      - name: Upload artifact
        uses: actions/upload-pages-artifact@v3
        with:
          path: website/.vitepress/dist

  # Single deploy job since we're just deploying
  deploy:
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    runs-on: ubuntu-latest
    needs: build
    steps:
      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v4


================================================
FILE: .github/workflows/test.yml
================================================
name: Test Build and Run

on:
#  push:
#    branches:
#      - '*'
#  pull_request:
#  merge_group:
  workflow_dispatch:

jobs:
  build-test:
    strategy:
      matrix:
        os: [windows-latest, ubuntu-latest, macos-latest]
        arch: [amd64, arm64]

    runs-on: ${{ matrix.os }}
    continue-on-error: true
    env:
      GOARCH: ${{ matrix.arch }}
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup Go
        uses: actions/setup-go@v5
        with:
          go-version: '1.24'

      - name: Build Go binary
        run: go build -ldflags "-s -w" -trimpath -o ./go2rtc

      - name: Test Go binary on linux
        if: matrix.os == 'ubuntu-latest'
        run: |
            if [ "${{ matrix.arch }}" = "amd64" ]; then
              ./go2rtc -version
            else
              sudo apt-get update && sudo apt-get install -y qemu-user-static
              sudo cp /usr/bin/qemu-aarch64-static .
              sudo chown $USER:$USER ./qemu-aarch64-static
              qemu-aarch64-static ./go2rtc -version
            fi
      - name: Test Go binary on macos
        if: matrix.os == 'macos-latest'
        run: |
            if [ "${{ matrix.arch }}" = "amd64" ]; then
              ./go2rtc -version
            else
              echo "ARM64 architecture is not yet supported on macOS"
            fi
      - name: Test Go binary on windows
        if: matrix.os == 'windows-latest'
        run: |
            if ("${{ matrix.arch }}" -eq "amd64") {
                .\go2rtc* -version
            } else {
                Write-Host "ARM64 architecture is not yet supported on Windows"
            }
  docker-test:
    strategy:
      matrix:
        platform:
          - amd64
          - "386"
          - arm/v7
          - arm64/v8
    continue-on-error: true
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Set up QEMU
        uses: docker/setup-qemu-action@v3
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          context: .
          file: docker/Dockerfile
          platforms: linux/${{ matrix.platform }}
          push: false
          load: true
          tags: go2rtc-${{ matrix.platform }}
      - name: test run
        run: |
          docker run --platform=linux/${{ matrix.platform }} --rm go2rtc-${{ matrix.platform }} go2rtc -version

      - name: Build and push Hardware
        if: matrix.platform == 'amd64'
        uses: docker/build-push-action@v5
        with:
          context: .
          file: docker/hardware.Dockerfile
          platforms: linux/amd64
          push: false
          load: true
          tags: go2rtc-${{ matrix.platform }}-hardware
      - name: test run
        if: matrix.platform == 'amd64'
        run: |
          docker run --platform=linux/${{ matrix.platform }} --rm go2rtc-${{ matrix.platform }}-hardware go2rtc -version

================================================
FILE: .gitignore
================================================
.idea/
.tmp/

go2rtc.yaml
go2rtc.json

go2rtc_freebsd*
go2rtc_linux*
go2rtc_mac*
go2rtc_win*

/go2rtc
/go2rtc.exe

0_test.go

.DS_Store

website/.vitepress/cache
website/.vitepress/dist

node_modules
package-lock.json
CLAUDE.md

================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2022 Alexey Khit

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
<h1 align="center">
  <a href="https://github.com/AlexxIT/go2rtc">
    <img src="./website/images/logo.gif" alt="go2rtc - GitHub">
  </a>
</h1>
<p align="center">
  <a href="https://github.com/AlexxIT/go2rtc/stargazers" target="_blank">
    <img style="display: inline" src="https://img.shields.io/github/stars/AlexxIT/go2rtc?style=flat-square&logo=github" alt="go2rtc - GitHub Stars">
  </a>
  <a href="https://hub.docker.com/r/alexxit/go2rtc" target="_blank">
    <img style="display: inline" src="https://img.shields.io/docker/pulls/alexxit/go2rtc?style=flat-square&logo=docker&logoColor=white&label=pulls" alt="go2rtc - Docker Pulls">
  </a>
  <a href="https://github.com/AlexxIT/go2rtc/releases" target="_blank">
    <img style="display: inline" src="https://img.shields.io/github/downloads/AlexxIT/go2rtc/total?color=blue&style=flat-square&logo=github" alt="go2rtc - GitHub Downloads">
  </a>
</p>
<p align="center">
  <a href="https://trendshift.io/repositories/4628" target="_blank">
    <img src="https://trendshift.io/api/badge/repositories/4628" alt="go2rtc - Trendshift"/>
  </a>
</p>

Ultimate camera streaming application with support for dozens formats and protocols.

- zero-dependency [small app](#go2rtc-binary) for all OS (Windows, macOS, Linux, FreeBSD)
- zero-delay for many [supported protocols](#codecs-madness) (lowest possible streaming latency)
- [streaming input](#streaming-input) from dozens formats and protocols
- [streaming output](#streaming-output) in all popular formats
- [streaming ingest](#streaming-ingest) in a number of popular formats
- [publish](#publish-stream) any source to popular streaming services (YouTube, Telegram)
- on-the-fly transcoding only if necessary via [FFmpeg](internal/ffmpeg/README.md)
- [two-way audio](#two-way-audio) support for many formats
- [streaming audio](#stream-to-camera) to all cameras with [two-way audio](#two-way-audio) support
- mixing tracks from different sources to single stream
- [auto-match](www/README.md#javascript-api) client-supported streaming formats and codecs
- [streaming stats](#streaming-stats) for all active connections
- can be [integrated to any project](#projects-using-go2rtc) or be used as [standalone app](#go2rtc-binary)

#### Inspired by

- series of streaming projects from [@deepch](https://github.com/deepch)
- [webrtc](https://github.com/pion/webrtc) go library and whole [@pion](https://github.com/pion) team
- [rtsp-simple-server](https://github.com/aler9/rtsp-simple-server) idea from [@aler9](https://github.com/aler9)
- [GStreamer](https://gstreamer.freedesktop.org/) framework pipeline idea
- [MediaSoup](https://mediasoup.org/) framework routing idea
- HomeKit Accessory Protocol from [@brutella](https://github.com/brutella/hap)
- creator of the project's logo [@v_novoseltsev](https://www.instagram.com/v_novoseltsev)

<br>
<details>
<summary><b>Table of Contents</b></summary>

- [Installation](#installation)
  - [go2rtc: Binary](#go2rtc-binary)
  - [go2rtc: Docker](#go2rtc-docker)
  - [go2rtc: Home Assistant add-on](#go2rtc-home-assistant-add-on)
  - [go2rtc: Home Assistant Integration](#go2rtc-home-assistant-integration)
  - [go2rtc: Master version](#go2rtc-master-version)
- [Configuration](#configuration)
- [Features](#features)
  - [Streaming input](#streaming-input)
  - [Streaming output](#streaming-output)
  - [Streaming ingest](#streaming-ingest)
  - [Two-way audio](#two-way-audio)
  - [Stream to camera](#stream-to-camera)
  - [Publish stream](#publish-stream)
  - [Preload stream](#preload-stream)
  - [Streaming stats](#streaming-stats)
- [Codecs](#codecs)
  - [Codecs filters](#codecs-filters)
  - [Codecs madness](#codecs-madness)
  - [Built-in transcoding](#built-in-transcoding)
  - [Codecs negotiation](#codecs-negotiation)
- [Security](#security)
- [Projects using go2rtc](#projects-using-go2rtc)
- [Camera experience](#camera-experience)
- [Tips](#tips)

</details>

## Installation

1. Download [binary](#go2rtc-binary) or use [Docker](#go2rtc-docker) or Home Assistant [add-on](#go2rtc-home-assistant-add-on) or [integration](#go2rtc-home-assistant-integration)
2. Open web interface: `http://localhost:1984/`
3. Add [streams](#streaming-input) to [config](#configuration)

**Developers:** integrate [HTTP API](internal/api/README.md) into your smart home platform.

### go2rtc: Binary

Download binary for your OS from [latest release](https://github.com/AlexxIT/go2rtc/releases/):

| name                                                                                                            | description                                                                                                                               |
|-----------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------|
| [go2rtc_win64.zip](https://github.com/AlexxIT/go2rtc/releases/latest/download/go2rtc_win64.zip)                 | Windows 10+ 64-bit                                                                                                                        |
| [go2rtc_win32.zip](https://github.com/AlexxIT/go2rtc/releases/latest/download/go2rtc_win32.zip)                 | Windows 10+ 32-bit                                                                                                                        |
| [go2rtc_win_arm64.zip](https://github.com/AlexxIT/go2rtc/releases/latest/download/go2rtc_win_arm64.zip)         | Windows ARM 64-bit                                                                                                                        |
| [go2rtc_linux_amd64](https://github.com/AlexxIT/go2rtc/releases/latest/download/go2rtc_linux_amd64)             | Linux 64-bit                                                                                                                              |
| [go2rtc_linux_i386](https://github.com/AlexxIT/go2rtc/releases/latest/download/go2rtc_linux_i386)               | Linux 32-bit                                                                                                                              |
| [go2rtc_linux_arm64](https://github.com/AlexxIT/go2rtc/releases/latest/download/go2rtc_linux_arm64)             | Linux ARM 64-bit (ex. Raspberry 64-bit OS)                                                                                                |
| [go2rtc_linux_arm](https://github.com/AlexxIT/go2rtc/releases/latest/download/go2rtc_linux_arm)                 | Linux ARM 32-bit (ex. Raspberry 32-bit OS)                                                                                                |
| [go2rtc_linux_armv6](https://github.com/AlexxIT/go2rtc/releases/latest/download/go2rtc_linux_armv6)             | Linux ARMv6 (for old Raspberry 1 and Zero)                                                                                                |
| [go2rtc_linux_mipsel](https://github.com/AlexxIT/go2rtc/releases/latest/download/go2rtc_linux_mipsel)           | Linux MIPS (ex. [Xiaomi Gateway 3](https://github.com/AlexxIT/XiaomiGateway3), [Wyze cameras](https://github.com/gtxaspec/wz_mini_hacks)) |
| [go2rtc_mac_amd64.zip](https://github.com/AlexxIT/go2rtc/releases/latest/download/go2rtc_mac_amd64.zip)         | macOS 11+ Intel 64-bit                                                                                                                    |
| [go2rtc_mac_arm64.zip](https://github.com/AlexxIT/go2rtc/releases/latest/download/go2rtc_mac_arm64.zip)         | macOS ARM 64-bit                                                                                                                          |
| [go2rtc_freebsd_amd64.zip](https://github.com/AlexxIT/go2rtc/releases/latest/download/go2rtc_freebsd_amd64.zip) | FreeBSD 64-bit                                                                                                                            |
| [go2rtc_freebsd_arm64.zip](https://github.com/AlexxIT/go2rtc/releases/latest/download/go2rtc_freebsd_arm64.zip) | FreeBSD ARM 64-bit                                                                                                                        |

Don't forget to fix the rights `chmod +x go2rtc_xxx_xxx` on Linux and Mac.

PS. The application is compiled with the latest versions of the Go language for maximum speed and security. Therefore, the [minimum OS versions](https://go.dev/wiki/MinimumRequirements) depend on the Go language.

### go2rtc: Docker

The Docker containers [`alexxit/go2rtc`](https://hub.docker.com/r/alexxit/go2rtc) and [`ghcr.io/alexxit/go2rtc`](https://github.com/AlexxIT/go2rtc/pkgs/container/go2rtc) support multiple architectures including `386`, `amd64`, `arm/v6`, `arm/v7` and `arm64`.
These containers offer the same functionality as the Home Assistant [add-on](#go2rtc-home-assistant-add-on) but are designed to operate independently of Home Assistant.
It comes preinstalled with [FFmpeg](internal/ffmpeg/README.md) and [Python](internal/echo/README.md).

### go2rtc: Home Assistant add-on

[![Open your Home Assistant instance and show the add add-on repository dialog with a specific repository URL pre-filled.](https://my.home-assistant.io/badges/supervisor_add_addon_repository.svg)](https://my.home-assistant.io/redirect/supervisor_add_addon_repository/?repository_url=https%3A%2F%2Fgithub.com%2FAlexxIT%2Fhassio-addons)

1. Settings > Add-ons > Plus > Repositories > Add
   ```
   https://github.com/AlexxIT/hassio-addons
   ```
2. go2rtc > Install > Start

### go2rtc: Home Assistant Integration

[WebRTC Camera](https://github.com/AlexxIT/WebRTC) custom component can be used on any Home Assistant [installation](https://www.home-assistant.io/installation/), including [HassWP](https://github.com/AlexxIT/HassWP) on Windows. It can automatically download and use the latest version of go2rtc. Or it can connect to an existing version of go2rtc. Addon installation in this case is optional.

### go2rtc: Master version

Latest, but maybe unstable version:

- Binary: [latest master build](https://nightly.link/AlexxIT/go2rtc/workflows/build/master)
- Docker: `alexxit/go2rtc:master` or `alexxit/go2rtc:master-hardware` versions
- Home Assistant add-on: `go2rtc master` or `go2rtc master hardware` versions

## Configuration

This is the `go2rtc.yaml` file in [YAML-format](https://en.wikipedia.org/wiki/YAML).
The configuration can be changed in the [WebUI](www/README.md) at `http://localhost:1984`.
The editor provides syntax highlighting and checking.

![go2rtc webui config](website/images/webui-config.png)

The simplest config looks like this:

```yaml
streams:
  hall-camera: rtsp://admin:password@192.168.1.123/cam/realmonitor?channel=1&subtype=0
```

- by default go2rtc will search `go2rtc.yaml` in the current work directory
- `api` server will start on default **1984 port** (TCP)
- `rtsp` server will start on default **8554 port** (TCP)
- `webrtc` will use port **8555** (TCP/UDP) for connections

More information can be [found here](internal/app/README.md).

## Features

A summary table of all modules and features can be found [here](internal/README.md).

**Core modules**

- [`app`](internal/app/README.md) - Reading [configs](internal/app/README.md) and setting up [logs](internal/app/README.md#log).
- [`api`](internal/api/README.md) - Handle [HTTP](internal/api/README.md) and [WebSocket](internal/api/ws/README.md) API.
- [`streams`](internal/streams/README.md) - Handle a list of streams.

### Streaming input

#### public protocols

- [`mpjpeg`](internal/mjpeg/README.md#mjpeg-client) - The legacy but still used [MJPEG](https://en.wikipedia.org/wiki/Motion_JPEG) protocol for real-time media transmission.
- [`onvif`](internal/onvif/README.md#onvif-client) - A popular [ONVIF](https://en.wikipedia.org/wiki/ONVIF) protocol for receiving media in RTSP format.
- [`rtmp`](internal/rtmp/README.md#rtmp-client) - The legacy but still used [RTMP](https://en.wikipedia.org/wiki/Real-Time_Messaging_Protocol) protocol for real-time media transmission.
- [`rtsp`](internal/rtsp/README.md#rtsp-client) - The most common [RTSP](https://en.wikipedia.org/wiki/Real-Time_Streaming_Protocol) protocol for real-time media transmission.
- [`webrtc`](internal/webrtc/README.md#webrtc-client) - [WebRTC](https://en.wikipedia.org/wiki/WebRTC) web-compatible protocol for real-time media transmission.
- [`yuv4mpegpipe`](internal/http/README.md#tcp) - Raw [YUV](https://en.wikipedia.org/wiki/Y%E2%80%B2UV) frame stream with [YUV4MPEG](https://manned.org/yuv4mpeg) header.

#### private protocols

- [`bubble`](internal/bubble/README.md) - Some NVR from [dvr163.com](http://help.dvr163.com/) and [eseecloud.com](http://www.eseecloud.com/).
- [`doorbird`](internal/doorbird/README.md) - [Doorbird](https://www.doorbird.com/) devices with two-way audio.
- [`dvrip`](internal/dvrip/README.md) - DVR-IP NVR, NetSurveillance, Sofia protocol (XMeye SDK).
- [`eseecloud`](internal/eseecloud/README.md) - Some NVR from [dvr163.com](http://help.dvr163.com/) and [eseecloud.com](http://www.eseecloud.com/).
- [`gopro`](internal/gopro/README.md) - [GoPro](https://gopro.com/) cameras, connected via USB or Wi-Fi.
- [`hass`](internal/hass/README.md) - Import cameras from [Home Assistant](https://www.home-assistant.io/) config files.
- [`homekit`](internal/homekit/README.md) - Cameras with [Apple HomeKit](https://www.apple.com/home-app/accessories/) protocol.
- [`isapi`](internal/isapi/README.md) - Two-way audio for [Hikvision ISAPI](https://tpp.hikvision.com/download/ISAPI_OTAP) protocol.
- [`kasa`](internal/kasa/README.md) - [TP-Link Kasa](https://www.kasasmart.com/) cameras.
- [`multitrans`](internal/multitrans/README.md) - Two-way audio for Chinese version of [TP-Link](https://www.tp-link.com.cn/) cameras.
- [`nest`](internal/nest/README.md) - [Google Nest](https://developers.google.com/nest/device-access/supported-devices) cameras through user-unfriendly and paid APIs.
- [`ring`](internal/ring/README.md) - Ring cameras with two-way audio support.
- [`roborock`](internal/roborock/README.md) - [Roborock](https://roborock.com/) vacuums with cameras with two-way audio support. 
- [`tapo`](internal/tapo/README.md) - [TP-Link Tapo](https://www.tapo.com/) cameras with two-way audio support.
- [`vigi`](internal/tapo/README.md#tp-link-vigi) - TP-Link Vigi cameras.
- [`tuya`](internal/tuya/README.md) - [Tuya](https://www.tuya.com/) ecosystem cameras with two-way audio support.
- [`webtorrent`](internal/webtorrent/README.md) - Stream from another go2rtc via [WebTorrent](https://en.wikipedia.org/wiki/WebTorrent) protocol.
- [`wyze`](internal/wyze/README.md) - [Wyze](https://wyze.com/) cameras using native P2P protocol
- [`xiaomi`](internal/xiaomi/README.md) - [Xiaomi Mi Home](https://home.mi.com/) ecosystem cameras with two-way audio support.

#### devices

- [`alsa`](internal/alsa/README.md) - A [framework](https://en.wikipedia.org/wiki/Advanced_Linux_Sound_Architecture) for receiving audio from devices on Linux OS.
- [`v4l2`](internal/v4l2/README.md) - A [framework](https://en.wikipedia.org/wiki/Video4Linux) for receiving video from devices on Linux OS.

#### files

- [`adts`](internal/http/README.md#tcp) - Audio stream in [AAC](https://en.wikipedia.org/wiki/Advanced_Audio_Coding) codec with Audio Data Transport Stream headers.
- [`flv`](internal/http/README.md#tcp) - The legacy but still used [Flash Video](https://en.wikipedia.org/wiki/Flash_Video) format.
- [`h264`](internal/http/README.md#tcp) - AVC/H.264 bitstream.
- [`hevc`](internal/http/README.md#tcp) - HEVC/H.265 bitstream.
- [`hls`](internal/http/README.md) - A popular [HTTP Live Streaming](https://en.wikipedia.org/wiki/HTTP_Live_Streaming) format.
- [`mjpeg`](internal/http/README.md#tcp) - A continuous sequence of JPEG frames (without HTTP headers).
- [`mpegts`](internal/http/README.md#tcp) - The legacy [MPEG transport stream](https://en.wikipedia.org/wiki/MPEG_transport_stream) format.
- [`wav`](internal/http/README.md#tcp) - Audio stream in [Waveform Audio File](https://en.wikipedia.org/wiki/WAV) format.

#### scripts

- [`echo`](internal/echo/README.md) - If the source has a dynamic link, you can use a bash or python script to get it.
- [`exec`](internal/exec/README.md) - You can run an external application (`ffmpeg`, `gstreamer`, `rpicam`, etc.) and receive a media stream from it.
- [`expr`](internal/expr/README.md) - If the source has a dynamic link, you can use [Expr](https://github.com/expr-lang/expr) language to get it.
- [`ffmpeg`](internal/ffmpeg/README.md) - Use [FFmpeg](https://ffmpeg.org/) as a stream source. Hardware-accelerated transcoding and streaming from USB devices are supported.

#### webrtc

- [`creality`](internal/webrtc/README.md#creality) - [Creality](https://www.creality.com/) 3D printer cameras.
- [`kinesis`](internal/webrtc/README.md#kinesis) - [Amazon Kinesis](https://aws.amazon.com/kinesis/video-streams/) video streams.
- [`openipc`](internal/webrtc/README.md#openipc) - Cameras on open-source [OpenIPC](https://openipc.org/) firmware.
- [`switchbot`](internal/webrtc/README.md#switchbot) - [SwitchBot](https://us.switch-bot.com/) cameras.
- [`whep`](internal/webrtc/README.md#whep) - [WebRTC/WHEP](https://datatracker.ietf.org/doc/draft-murillo-whep/) is replaced by [WebRTC/WISH](https://datatracker.ietf.org/doc/charter-ietf-wish/02/) standard for WebRTC video/audio viewers.
- [`wyze`](internal/webrtc/README.md#wyze) - Legacy method to connect to [Wyze](https://www.wyze.com/) cameras via [docker-wyze-bridge](https://github.com/mrlt8/docker-wyze-bridge).

### Streaming output

- [`adts`](internal/mpeg/README.md) - Output stream in ADTS format with [AAC](https://en.wikipedia.org/wiki/Advanced_Audio_Coding) audio.
- [`ascii`](internal/mjpeg/README.md#ascii) - Just for fun stream as [ASCII to Terminal](https://www.youtube.com/watch?v=sHj_3h_sX7M).
- [`flv`](internal/rtmp/README.md) - Output stream in [Flash Video](https://en.wikipedia.org/wiki/Flash_Video) format.
- [`hls`](internal/hls/README.md) - Output stream in [HTTP Live Streaming](https://en.wikipedia.org/wiki/HTTP_Live_Streaming) format.
- [`homekit`](internal/homekit/README.md#homekit-server) - Output stream to [Apple Home](https://www.apple.com/home-app/) using [HomeKit](https://en.wikipedia.org/wiki/Apple_Home) protocol.
- [`jpeg`](internal/mjpeg/README.md#jpeg) - Output snapshots in [JPEG](https://en.wikipedia.org/wiki/JPEG) format.
- [`mpjpeg`](internal/mjpeg/README.md#mpjpeg) - Output a stream in [MJPEG](https://en.wikipedia.org/wiki/Motion_JPEG) format.
- [`mp4`](internal/mp4/README.md) - Output as [MP4 stream](https://en.wikipedia.org/wiki/Progressive_download) or [Media Source Extensions](https://developer.mozilla.org/en-US/docs/Web/API/Media_Source_Extensions_API) (MSE) compatible format.
- [`mpegts`](internal/mpeg/README.md) - Output stream in [MPEG transport stream](https://en.wikipedia.org/wiki/MPEG_transport_stream) format.
- [`onvif`](internal/onvif/README.md#onvif-server) - Output stream using [ONVIF](https://en.wikipedia.org/wiki/ONVIF) protocol.
- [`rtmp`](internal/rtmp/README.md#rtmp-server) - Output stream using [Real-Time Messaging](https://en.wikipedia.org/wiki/Real-Time_Messaging_Protocol) protocol.
- [`rtsp`](internal/rtsp/README.md#rtsp-server) - Output stream using [Real-Time Streaming](https://en.wikipedia.org/wiki/Real-Time_Streaming_Protocol) protocol.
- [`webrtc`](internal/webrtc/README.md#webrtc-server) - Output stream using [Web Real-Time Communication](https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API) API.
- [`webtorrent`](internal/webtorrent/README.md#webtorrent-server) - Output stream using [WebTorrent](https://en.wikipedia.org/wiki/WebTorrent) protocol.
- [`yuv4mpegpipe`](internal/mjpeg/README.md#yuv4mpegpipe) - Output in raw [YUV](https://en.wikipedia.org/wiki/Y%E2%80%B2UV) frame stream with [YUV4MPEG](https://manned.org/yuv4mpeg) header.

### Streaming ingest

Supported for: 
[`flv`](internal/rtmp/README.md#flv-server), 
[`mjpeg`](internal/mjpeg/README.md#streaming-ingest), 
[`mpegts`](internal/mpeg/README.md#streaming-ingest), 
[`rtmp`](internal/rtmp/README.md#rtmp-server), 
[`rtsp`](internal/rtsp/README.md#streaming-ingest), 
[`webrtc`](internal/webrtc/README.md#streaming-ingest).

This is a feature when go2rtc expects to receive an incoming stream from an external application. The stream transmission is started and stopped by an external application.

- You can push data only to an existing stream (create a stream with empty source in config).
- You can push multiple incoming sources to the same stream.
- You can push data to a non-empty stream, so it will have additional codecs inside.

### Two-way audio

Supported for:
[`doorbird`](internal/doorbird/README.md), 
[`dvrip`](internal/dvrip/README.md), 
[`exec`](internal/exec/README.md), 
[`isapi`](internal/isapi/README.md), 
[`multitrans`](internal/multitrans/README.md), 
[`ring`](internal/ring/README.md), 
[`roborock`](internal/roborock/README.md), 
[`rtsp`](internal/rtsp/README.md#two-way-audio), 
[`tapo`](internal/tapo/README.md), 
[`tuya`](internal/tuya/README.md), 
[`webrtc`](internal/webrtc/README.md), 
[`wyze`](internal/wyze/README.md), 
[`xiaomi`](internal/xiaomi/README.md).

Two-way audio can be used in browser with [WebRTC](internal/webrtc/README.md) technology. The browser will give access to the microphone only for HTTPS sites ([read more](https://stackoverflow.com/questions/52759992/how-to-access-camera-and-microphone-in-chrome-without-https)).

### Stream to camera

You can play audio files or live streams on any camera with [two-way audio](#two-way-audio) support.

[read more](internal/streams/README.md#stream-to-camera)

### Publish stream

You can publish any stream to streaming services (YouTube, Telegram, etc.) via RTMP/RTMPS.

[read more](internal/streams/README.md#publish-stream)

### Preload stream

You can preload any stream on go2rtc start. This is useful for cameras that take a long time to start up.

[read more](internal/streams/README.md#preload-stream)

### Streaming stats

[WebUI](www/README.md) provides detailed information about all active connections, including IP-addresses, formats, protocols, number of packets and bytes transferred. 
Via the [HTTP API](internal/api/README.md) in [`json`](https://en.wikipedia.org/wiki/JSON) or [`dot`](https://en.wikipedia.org/wiki/DOT_(graph_description_language)) format on an interactive connection map.

![go2rtc webui net](website/images/webui-net.png)

## Codecs

If you have questions about why video or audio is not displayed, you need to read the following sections.

| Name                         | FFmpeg   | RTSP          | Aliases     |
|------------------------------|----------|---------------|-------------|
| Advanced Audio Coding        | `aac`    | MPEG4-GENERIC |             |
| Advanced Video Coding        | `h264`   | H264          | AVC, H.264  |
| G.711 PCM (A-law)            | `alaw`   | PCMA          | G711A       |
| G.711 PCM (µ-law)            | `mulaw`  | PCMU          | G711u       |
| High Efficiency Video Coding | `hevc`   | H265          | HEVC, H.265 |
| Motion JPEG                  | `mpjpeg` | JPEG          |             |
| MPEG-1 Audio Layer III       | `mp3`    | MPA           |             |
| Opus Codec                   | `opus`   | OPUS          |             |
| PCM signed 16-bit big-endian | `s16be`  | L16           |             |

### Codecs filters

go2rtc can automatically detect which codecs your device supports for [WebRTC](internal/webrtc/README.md) and [MSE](internal/mp4/README.md) technologies.

But it cannot be done for [RTSP](internal/rtsp/README.md), [HTTP progressive streaming](internal/mp4/README.md), [HLS](internal/hls/README.md) technologies. 
You can manually add a codec filter when you create a link to a stream. 
The filters work the same for all three technologies. 
Filters do not create a new codec, they only select the suitable codec from existing sources. 
You can add new codecs to the stream using the [FFmpeg transcoding](internal/ffmpeg/README.md).

Without filters:

- RTSP will provide only the first video and only the first audio (any codec)
- MP4 will include only compatible codecs (H264, H265, AAC)
- HLS will output in the legacy TS format (H264 without audio)

Some examples:

- `rtsp://192.168.1.123:8554/camera1?mp4` - useful for recording as MP4 files (e.g. Home Assistant or Frigate)
- `rtsp://192.168.1.123:8554/camera1?video=h264,h265&audio=aac` - full version of the filter above
- `rtsp://192.168.1.123:8554/camera1?video=h264&audio=aac&audio=opus` - H264 video codec and two separate audio tracks
- `rtsp://192.168.1.123:8554/camera1?video&audio=all` - any video codec and all audio codecs as separate tracks
- `http://192.168.1.123:1984/api/stream.m3u8?src=camera1&mp4` - HLS stream with MP4 compatible codecs (HLS/fMP4)
- `http://192.168.1.123:1984/api/stream.m3u8?src=camera1&mp4=flac` - HLS stream with PCMA/PCMU/PCM audio support (HLS/fMP4), won't work on old devices
- `http://192.168.1.123:1984/api/stream.mp4?src=camera1&mp4=flac` - MP4 file with PCMA/PCMU/PCM audio support, won't work on old devices (ex. iOS 12)
- `http://192.168.1.123:1984/api/stream.mp4?src=camera1&mp4=all` - MP4 file with non-standard audio codecs, won't work on some players

### Codecs madness

`AVC/H.264` video can be played almost anywhere. But `HEVC/H.265` has many limitations in supporting different devices and browsers.

| Device                                                             | WebRTC                                  | MSE                                     | HTTP*                                        | HLS                         |
|--------------------------------------------------------------------|-----------------------------------------|-----------------------------------------|----------------------------------------------|-----------------------------|
| *latency*                                                          | best                                    | medium                                  | bad                                          | bad                         |
| Desktop Chrome 136+ <br/> Desktop Edge <br/> Android Chrome 136+   | H264, H265* <br/> PCMU, PCMA <br/> OPUS | H264, H265* <br/> AAC, FLAC* <br/> OPUS | H264, H265* <br/> AAC, FLAC* <br/> OPUS, MP3 | no                          |
| Desktop Firefox                                                    | H264 <br/> PCMU, PCMA <br/> OPUS        | H264 <br/> AAC, FLAC* <br/> OPUS        | H264 <br/> AAC, FLAC* <br/> OPUS             | no                          |
| Desktop Safari 14+ <br/> iPad Safari 14+ <br/> iPhone Safari 17.1+ | H264, H265* <br/> PCMU, PCMA <br/> OPUS | H264, H265 <br/> AAC, FLAC*             | **no!**                                      | H264, H265 <br/> AAC, FLAC* |
| iPhone Safari 14+                                                  | H264, H265* <br/> PCMU, PCMA <br/> OPUS | **no!**                                 | **no!**                                      | H264, H265 <br/> AAC, FLAC* |
| macOS [Hass App][1]                                                | no                                      | no                                      | no                                           | H264, H265 <br/> AAC, FLAC* |

[1]: https://apps.apple.com/app/home-assistant/id1099568401

- `HTTP*` - HTTP Progressive Streaming, not related to [progressive download](https://en.wikipedia.org/wiki/Progressive_download), because the file has no size and no end
- `WebRTC H265` - supported in [Chrome 136+](https://developer.chrome.com/release-notes/136), supported in [Safari 18+](https://developer.apple.com/documentation/safari-release-notes/safari-18-release-notes)
- `MSE iPhone` - supported in [iOS 17.1+](https://webkit.org/blog/14735/webkit-features-in-safari-17-1/)

**Audio**

- go2rtc supports [automatic repackaging](#built-in-transcoding) of `PCMA/PCMU/PCM` codecs into `FLAC` for MSE/MP4/HLS so they'll work almost anywhere
- **WebRTC** audio codecs: `PCMU/8000`, `PCMA/8000`, `OPUS/48000/2`
- `OPUS` and `MP3` inside **MP4** are part of the standard, but some players do not support them anyway (especially Apple)

**Apple devices**

- all Apple devices don't support HTTP progressive streaming
- old iPhone firmwares don't support MSE technology because it competes with the HTTP Live Streaming (HLS) technology, invented by Apple
- HLS is the worst technology for **live** streaming, it still exists only because of iPhones

### Built-in transcoding

There are no plans to embed complex transcoding algorithms inside go2rtc. 
[FFmpeg source](internal/ffmpeg/README.md) does a great job with this. 
Including [hardware acceleration](https://github.com/AlexxIT/go2rtc/wiki/Hardware-acceleration) support.

But go2rtc has some simple algorithms. They are turned on automatically; you do not need to set them up additionally.

**PCM for MSE/MP4/HLS**

Go2rtc can pack `PCMA`, `PCMU` and `PCM` codecs into an MP4 container so that they work in all browsers and all built-in players on modern devices. Including Apple QuickTime:

```text
PCMA/PCMU => PCM => FLAC => MSE/MP4/HLS
```

**Resample PCMA/PCMU for WebRTC**

By default WebRTC supports only `PCMA/8000` and `PCMU/8000`. But go2rtc can automatically resample PCMA and PCMU codecs with a different sample rate. Also, go2rtc can transcode `PCM` codec to `PCMA/8000`, so WebRTC can play it:

```text
PCM/xxx => PCMA/8000 => WebRTC
PCMA/xxx => PCMA/8000 => WebRTC
PCMU/xxx => PCMU/8000 => WebRTC
```

**Important**

- FLAC codec not supported in an RTSP stream. If you are using Frigate or Home Assistant for recording MP4 files with PCMA/PCMU/PCM audio, you should set up transcoding to the AAC codec.
- PCMA and PCMU are VERY low-quality codecs. They support only 256! different sounds. Use them only when you have no other options.

### Codecs negotiation

For example, you want to watch an RTSP stream from a [Dahua IPC-K42](https://www.dahuasecurity.com/fr/products/All-Products/Network-Cameras/Wireless-Series/Wi-Fi-Series/4MP/IPC-K42) camera in your Chrome browser.

- this camera supports two-way audio standard **ONVIF Profile T**
- this camera supports codecs **H264, H265** for sending video, and you select `H264` in camera settings
- this camera supports codecs **AAC, PCMU, PCMA** for sending audio (from mic), and you select `AAC/16000` in camera settings
- this camera supports codecs **AAC, PCMU, PCMA** for receiving audio (to speaker), you don't need to select them
- your browser supports codecs **H264, VP8, VP9, AV1** for receiving video, you don't need to select them
- your browser supports codecs **OPUS, PCMU, PCMA** for sending and receiving audio, you don't need to select them
- you can't get the camera audio directly because its audio codecs don't match your browser's codecs
    - so you decide to use transcoding via FFmpeg and add this setting to the config YAML file
    - you have chosen `OPUS/48000/2` codec, because it is higher quality than the `PCMU/8000` or `PCMA/8000`

Now you have a stream with two sources - **RTSP and FFmpeg**:

```yaml
streams:
  dahua:
    - rtsp://admin:password@192.168.1.123/cam/realmonitor?channel=1&subtype=0&unicast=true&proto=Onvif
    - ffmpeg:rtsp://admin:password@192.168.1.123/cam/realmonitor?channel=1&subtype=0#audio=opus
```

**go2rtc** automatically matches codecs for your browser across all of your stream sources. This is called **multi-source two-way codec negotiation**, and it's one of the main features of this app.

**PS.** You can select `PCMU` or `PCMA` codec in camera settings and not use transcoding at all. Or you can select `AAC` codec for main stream and `PCMU` codec for second stream and add both RTSP to YAML config, this also will work fine.

## Security

> [!IMPORTANT]
> If an attacker gains access to the API, you are in danger. Through the API, an attacker can use insecure sources such as echo and exec. And get full access to your server.

For maximum (paranoid) security, go2rtc has special settings:

```yaml
app:
  # use only allowed modules
  modules: [api, rtsp, webrtc, exec, ffmpeg, mjpeg]

api:
  # use only allowed API paths
  allow_paths: [/api, /api/streams, /api/webrtc, /api/frame.jpeg]
  # enable auth for localhost (used together with username and password)
  local_auth: true

exec:
  # use only allowed exec paths
  allow_paths: [ffmpeg]
```

By default, `go2rtc` starts the Web interface on port `1984` and RTSP on port `8554`, as well as uses port `8555` for WebRTC connections. The three ports are accessible from your local network. So anyone on your local network can watch video from your cameras without authorization. The same rule applies to the Home Assistant add-on.

This is not a problem if you trust your local network as much as I do. But you can change this behaviour with a `go2rtc.yaml` config:

```yaml
api:
  listen: "127.0.0.1:1984" # localhost

rtsp:
  listen: "127.0.0.1:8554" # localhost

webrtc:
  listen: ":8555" # external TCP/UDP port
```

- local access to RTSP is not a problem for [FFmpeg](internal/ffmpeg/README.md) integration, because it runs locally on your server
- local access to API is not a problem for the [Home Assistant add-on](#go2rtc-home-assistant-add-on), because Home Assistant runs locally on the same server, and the add-on web UI is protected with Home Assistant authorization ([Ingress feature](https://www.home-assistant.io/blog/2019/04/15/hassio-ingress/))
- external access to WebRTC TCP port is not a problem, because it is used only for transmitting encrypted media data
    - anyway you need to open this port to your local network and to the Internet for WebRTC to work

If you need web interface protection without the Home Assistant add-on, you need to use a reverse proxy, like [Nginx](https://nginx.org/), [Caddy](https://caddyserver.com/), etc.

PS. Additionally, WebRTC will try to use the 8555 UDP port to transmit encrypted media. It works without problems on the local network, and sometimes also works for external access, even if you haven't opened this port on your router ([read more](https://en.wikipedia.org/wiki/UDP_hole_punching)). But for stable external WebRTC access, you need to open the 8555 port on your router for both TCP and UDP.

## Projects using go2rtc

- [Home Assistant](https://www.home-assistant.io/) [2024.11+](https://www.home-assistant.io/integrations/go2rtc/) - top open-source smart home project
- [Frigate](https://frigate.video/) [0.12+](https://docs.frigate.video/guides/configuring_go2rtc/) - open-source NVR built around real-time AI object detection
- [Advanced Camera Card](https://github.com/dermotduffy/advanced-camera-card) - custom card for Home Assistant
- [OpenIPC](https://github.com/OpenIPC/firmware/tree/master/general/package/go2rtc) - alternative IP camera firmware from an open community
- [wz_mini_hacks](https://github.com/gtxaspec/wz_mini_hacks) - custom firmware for Wyze cameras
- [EufyP2PStream](https://github.com/oischinger/eufyp2pstream) - a small project that provides a video/audio stream from Eufy cameras that don't directly support RTSP
- [ioBroker.euSec](https://github.com/bropat/ioBroker.eusec) - [ioBroker](https://www.iobroker.net/) adapter for controlling Eufy security devices
- [MMM-go2rtc](https://github.com/Anonym-tsk/MMM-go2rtc) - MagicMirror² module
- [ring-mqtt](https://github.com/tsightler/ring-mqtt) - Ring-to-MQTT bridge
- [lightNVR](https://github.com/opensensor/lightNVR)

**Distributions**

- [Alpine Linux](https://pkgs.alpinelinux.org/packages?name=go2rtc)
- [Arch User Repository](https://linux-packages.com/aur/package/go2rtc)
- [Gentoo](https://github.com/inode64/inode64-overlay/tree/main/media-video/go2rtc)
- [NixOS](https://search.nixos.org/packages?query=go2rtc)
- [Proxmox Helper Scripts](https://github.com/community-scripts/ProxmoxVE/)
- [QNAP](https://www.myqnap.org/product/go2rtc/)
- [Synology NAS](https://synocommunity.com/package/go2rtc)
- [Unraid](https://unraid.net/community/apps?q=go2rtc)

## Camera experience

- [Dahua](https://www.dahuasecurity.com/) - reference implementation streaming protocols, a lot of settings, high stream quality, multiple streaming clients
- [EZVIZ](https://www.ezviz.com/) - awful RTSP protocol implementation, many bugs in SDP
- [Hikvision](https://www.hikvision.com/) - a lot of proprietary streaming technologies
- [Reolink](https://reolink.com/) - some models have an awful, unusable RTSP implementation and not the best RTMP alternative (I recommend that you contact Reolink support for new firmware), few settings
- [Sonoff](https://sonoff.tech/) - very low stream quality, no settings, not the best protocol implementation
- [TP-Link](https://www.tp-link.com/) - few streaming clients, packet loss?
- Cheap noname cameras, Wyze Cams, Xiaomi cameras with hacks (usually have `/live/ch00_1` in RTSP URL) - awful but usable RTSP protocol implementation, low stream quality, few settings, packet loss?

## Tips

**Using apps for low RTSP delay**

- `ffplay -fflags nobuffer -flags low_delay "rtsp://192.168.1.123:8554/camera1"`
- VLC > Preferences > Input / Codecs > Default Caching Level: Lowest Latency

**Snapshots to Telegram**

[read more](https://github.com/AlexxIT/go2rtc/wiki/Snapshot-to-Telegram)


================================================
FILE: docker/Dockerfile
================================================
# syntax=docker/dockerfile:labs

# 0. Prepare images
ARG PYTHON_VERSION="3.13"
ARG GO_VERSION="1.25"


# 1. Build go2rtc binary
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-alpine AS build
ARG TARGETPLATFORM
ARG TARGETOS
ARG TARGETARCH

ENV GOOS=${TARGETOS}
ENV GOARCH=${TARGETARCH}

WORKDIR /build

RUN apk add git

# Cache dependencies
COPY go.mod go.sum ./
RUN --mount=type=cache,target=/root/.cache/go-build go mod download

COPY . .
RUN --mount=type=cache,target=/root/.cache/go-build CGO_ENABLED=0 go build -ldflags "-s -w" -trimpath


# 2. Final image
FROM python:${PYTHON_VERSION}-alpine AS base

# Install ffmpeg, tini (for signal handling),
# and other common tools for the echo source.
# alsa-plugins-pulse for ALSA support (+0MB)
# font-droid for FFmpeg drawtext filter (+2MB)
RUN apk add --no-cache tini ffmpeg ffplay bash curl jq alsa-plugins-pulse font-droid

# Hardware Acceleration for Intel CPU (+50MB)
ARG TARGETARCH

RUN if [ "${TARGETARCH}" = "amd64" ]; then apk add --no-cache libva-intel-driver intel-media-driver; fi

# Hardware: AMD and NVidia VAAPI (not sure about this)
# RUN libva-glx mesa-va-gallium
# Hardware: AMD and NVidia VDPAU (not sure about this)
# RUN libva-vdpau-driver mesa-vdpau-gallium (+150MB total)

COPY --from=build /build/go2rtc /usr/local/bin/

EXPOSE 1984 8554 8555 8555/udp
ENTRYPOINT ["/sbin/tini", "--"]
VOLUME /config
WORKDIR /config

CMD ["go2rtc", "-config", "/config/go2rtc.yaml"]


================================================
FILE: docker/README.md
================================================
# Docker

Images are built automatically via [GitHub actions](https://github.com/AlexxIT/go2rtc/actions) and published on [Docker Hub](https://hub.docker.com/r/alexxit/go2rtc) and [GitHub](https://github.com/AlexxIT/go2rtc/pkgs/container/go2rtc).

## Versions

- `alexxit/go2rtc:latest` - latest release based on `alpine` (`amd64`, `386`, `arm/v6`, `arm/v7`, `arm64`) with support for hardware transcoding for Intel iGPU and Raspberry
- `alexxit/go2rtc:latest-hardware` - latest release based on `debian 13` (`amd64`) with support for hardware transcoding for Intel iGPU, AMD GPU and NVidia GPU
- `alexxit/go2rtc:latest-rockchip` - latest release based on `debian 12` (`arm64`) with support for hardware transcoding for Rockchip RK35xx
- `alexxit/go2rtc:master` - latest unstable version based on `alpine`
- `alexxit/go2rtc:master-hardware` - latest unstable version based on `debian 13` (`amd64`)
- `alexxit/go2rtc:master-rockchip` - latest unstable version based on `debian 12` (`arm64`)

## Docker compose

```yaml
services:
  go2rtc:
    image: alexxit/go2rtc
    network_mode: host       # important for WebRTC, HomeKit, UDP cameras
    privileged: true         # only for FFmpeg hardware transcoding
    restart: unless-stopped  # autorestart on fail or config change from WebUI
    environment:
      - TZ=Atlantic/Bermuda  # timezone in logs
    volumes:
      - "~/go2rtc:/config"   # folder for go2rtc.yaml file (edit from WebUI)
```

## Basic Deployment

```bash
docker run -d \
  --name go2rtc \
  --network host \
  --privileged \
  --restart unless-stopped \
  -e TZ=Atlantic/Bermuda \
  -v ~/go2rtc:/config \
  alexxit/go2rtc
```

## Deployment with GPU Acceleration

```bash
docker run -d \
  --name go2rtc \
  --network host \
  --privileged \
  --restart unless-stopped \
  -e TZ=Atlantic/Bermuda \
  --gpus all \
  -v ~/go2rtc:/config \
  alexxit/go2rtc:latest-hardware
```


================================================
FILE: docker/hardware.Dockerfile
================================================
# syntax=docker/dockerfile:labs

# 0. Prepare images
# only debian 13 (trixie) has latest ffmpeg
# https://packages.debian.org/trixie/ffmpeg
ARG DEBIAN_VERSION="trixie-slim"
ARG GO_VERSION="1.25-bookworm"


# 1. Build go2rtc binary
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION} AS build
ARG TARGETPLATFORM
ARG TARGETOS
ARG TARGETARCH

ENV GOOS=${TARGETOS}
ENV GOARCH=${TARGETARCH}

WORKDIR /build

# Cache dependencies
COPY go.mod go.sum ./
RUN --mount=type=cache,target=/root/.cache/go-build go mod download

COPY . .
RUN --mount=type=cache,target=/root/.cache/go-build CGO_ENABLED=0 go build -ldflags "-s -w" -trimpath


# 2. Final image
FROM debian:${DEBIAN_VERSION}

# Prepare apt for buildkit cache
RUN rm -f /etc/apt/apt.conf.d/docker-clean \
  && echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' >/etc/apt/apt.conf.d/keep-cache

# Install ffmpeg, tini (for signal handling),
# and other common tools for the echo source.
# non-free for Intel QSV support (not used by go2rtc, just for tests)
# mesa-va-drivers for AMD APU
# libasound2-plugins for ALSA support
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked --mount=type=cache,target=/var/lib/apt,sharing=locked \
    echo 'deb http://deb.debian.org/debian trixie non-free' > /etc/apt/sources.list.d/debian-non-free.list && \
    apt-get -y update && apt-get -y install ffmpeg tini \
        python3 curl jq \
        intel-media-va-driver-non-free \
        mesa-va-drivers \
        libasound2-plugins && \
    apt-get clean && rm -rf /var/lib/apt/lists/*

COPY --from=build /build/go2rtc /usr/local/bin/

EXPOSE 1984 8554 8555 8555/udp
ENTRYPOINT ["/usr/bin/tini", "--"]
VOLUME /config
WORKDIR /config
# https://github.com/NVIDIA/nvidia-docker/wiki/Installation-(Native-GPU-Support)
ENV NVIDIA_VISIBLE_DEVICES all
ENV NVIDIA_DRIVER_CAPABILITIES compute,video,utility

CMD ["go2rtc", "-config", "/config/go2rtc.yaml"]


================================================
FILE: docker/rockchip.Dockerfile
================================================
# syntax=docker/dockerfile:labs

# 0. Prepare images
ARG PYTHON_VERSION="3.13-slim-bookworm"
ARG GO_VERSION="1.25-bookworm"


# 1. Build go2rtc binary
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION} AS build
ARG TARGETPLATFORM
ARG TARGETOS
ARG TARGETARCH

ENV GOOS=${TARGETOS}
ENV GOARCH=${TARGETARCH}

WORKDIR /build

# Cache dependencies
COPY go.mod go.sum ./
RUN --mount=type=cache,target=/root/.cache/go-build go mod download

COPY . .
RUN --mount=type=cache,target=/root/.cache/go-build CGO_ENABLED=0 go build -ldflags "-s -w" -trimpath


# 2. Final image
FROM python:${PYTHON_VERSION}

# Prepare apt for buildkit cache
RUN rm -f /etc/apt/apt.conf.d/docker-clean \
  && echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' >/etc/apt/apt.conf.d/keep-cache

# Install ffmpeg, tini (for signal handling),
# and other common tools for the echo source.
# libasound2-plugins for ALSA support
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked --mount=type=cache,target=/var/lib/apt,sharing=locked \
    apt-get -y update && apt-get -y install tini \
        curl jq \
        libasound2-plugins && \
    apt-get clean && rm -rf /var/lib/apt/lists/*

COPY --from=build /build/go2rtc /usr/local/bin/
ADD --chmod=755 https://github.com/MarcA711/Rockchip-FFmpeg-Builds/releases/download/6.1-8-no_extra_dump/ffmpeg /usr/local/bin

EXPOSE 1984 8554 8555 8555/udp
ENTRYPOINT ["/usr/bin/tini", "--"]
VOLUME /config
WORKDIR /config

CMD ["go2rtc", "-config", "/config/go2rtc.yaml"]


================================================
FILE: examples/go2rtc_hass/main.go
================================================
package main

import (
	"github.com/AlexxIT/go2rtc/internal/api"
	"github.com/AlexxIT/go2rtc/internal/app"
	"github.com/AlexxIT/go2rtc/internal/hass"
	"github.com/AlexxIT/go2rtc/internal/streams"
	"github.com/AlexxIT/go2rtc/pkg/shell"
)

func main() {
	app.Init()
	streams.Init()

	api.Init()

	hass.Init()

	shell.RunUntilSignal()
}


================================================
FILE: examples/go2rtc_mjpeg/main.go
================================================
package main

import (
	"github.com/AlexxIT/go2rtc/internal/api"
	"github.com/AlexxIT/go2rtc/internal/api/ws"
	"github.com/AlexxIT/go2rtc/internal/app"
	"github.com/AlexxIT/go2rtc/internal/ffmpeg"
	"github.com/AlexxIT/go2rtc/internal/mjpeg"
	"github.com/AlexxIT/go2rtc/internal/streams"
	"github.com/AlexxIT/go2rtc/internal/v4l2"
	"github.com/AlexxIT/go2rtc/pkg/shell"
)

func main() {
	app.Init()
	streams.Init()

	api.Init()
	ws.Init()

	ffmpeg.Init()
	mjpeg.Init()
	v4l2.Init()

	shell.RunUntilSignal()
}


================================================
FILE: examples/go2rtc_rtsp/main.go
================================================
package main

import (
	"github.com/AlexxIT/go2rtc/internal/app"
	"github.com/AlexxIT/go2rtc/internal/rtsp"
	"github.com/AlexxIT/go2rtc/internal/streams"
	"github.com/AlexxIT/go2rtc/pkg/shell"
)

func main() {
	app.Init()
	streams.Init()

	rtsp.Init()

	shell.RunUntilSignal()
}


================================================
FILE: examples/homekit_info/main.go
================================================
package main

import (
	"encoding/json"
	"os"

	"github.com/AlexxIT/go2rtc/pkg/hap"
)

var servs = map[string]string{
	"3E":  "Accessory Information",
	"7E":  "Security System",
	"85":  "Motion Sensor",
	"96":  "Battery",
	"A2":  "Protocol Information",
	"110": "Camera RTP Stream Management",
	"112": "Microphone",
	"113": "Speaker",
	"121": "Doorbell",
	"129": "Data Stream Transport Management",
	"204": "Camera Recording Management",
	"21A": "Camera Operating Mode",
	"22A": "Wi-Fi Transport",
	"239": "Accessory Runtime Information",
}

var chars = map[string]string{
	"14":  "Identify",
	"20":  "Manufacturer",
	"21":  "Model",
	"23":  "Name",
	"30":  "Serial Number",
	"52":  "Firmware Revision",
	"53":  "Hardware Revision",
	"220": "Product Data",
	"A6":  "Accessory Flags",

	"22": "Motion Detected",
	"75": "Status Active",

	"11A": "Mute",
	"119": "Volume",

	"B0":  "Active",
	"209": "Selected Camera Recording Configuration",
	"207": "Supported Audio Recording Configuration",
	"205": "Supported Camera Recording Configuration",
	"206": "Supported Video Recording Configuration",
	"226": "Recording Audio Active",

	"223": "Event Snapshots Active",
	"225": "Periodic Snapshots Active",
	"21B": "HomeKit Camera Active",
	"21C": "Third Party Camera Active",
	"21D": "Camera Operating Mode Indicator",
	"11B": "Night Vision",
	//"129": "Supported Data Stream Transport Configuration",
	"37":  "Version",
	"131": "Setup Data Stream Transport",
	"130": "Supported Data Stream Transport Configuration",

	"120": "Streaming Status",
	"115": "Supported Audio Stream Configuration",
	"116": "Supported RTP Configuration",
	"114": "Supported Video Stream Configuration",
	"117": "Selected RTP Stream Configuration",
	"118": "Setup Endpoints",

	"22B": "Current Transport",
	"22C": "Wi-Fi Capabilities",
	"22D": "Wi-Fi Configuration Control",

	"23C": "Ping",

	"68": "Battery Level",
	"79": "Status Low Battery",
	"8F": "Charging State",

	"73":  "Programmable Switch Event",
	"232": "Operating State Response",

	"66": "Security System Current State",
	"67": "Security System Target State",
}

func main() {
	src := os.Args[1]
	dst := os.Args[2]

	f, err := os.Open(src)
	if err != nil {
		panic(err)
	}

	var v hap.JSONAccessories
	if err = json.NewDecoder(f).Decode(&v); err != nil {
		panic(err)
	}

	for _, acc := range v.Value {
		for _, srv := range acc.Services {
			if srv.Desc == "" {
				srv.Desc = servs[srv.Type]
			}
			for _, chr := range srv.Characters {
				if chr.Desc == "" {
					chr.Desc = chars[chr.Type]
				}
			}
		}
	}

	f, err = os.Create(dst)
	if err != nil {
		panic(err)
	}

	enc := json.NewEncoder(f)
	enc.SetIndent("", "  ")
	if err = enc.Encode(v); err != nil {
		panic(err)
	}
}


================================================
FILE: examples/mdns/main.go
================================================
package main

import (
	"log"
	"os"

	"github.com/AlexxIT/go2rtc/pkg/mdns"
)

func main() {
	var service = mdns.ServiceHAP

	if len(os.Args) >= 2 {
		service = os.Args[1]
	}

	onentry := func(entry *mdns.ServiceEntry) bool {
		log.Printf("name=%s, addr=%s, info=%s\n", entry.Name, entry.Addr(), entry.Info)
		return false
	}

	var err error

	if len(os.Args) >= 3 {
		host := os.Args[2]

		log.Printf("run discovery service=%s host=%s\n", service, host)

		err = mdns.QueryOrDiscovery(host, service, onentry)
	} else {
		log.Printf("run discovery service=%s\n", service)

		err = mdns.Discovery(service, onentry)
	}

	if err != nil {
		log.Println(err)
	}
}


================================================
FILE: examples/mod_pinggy/go.mod
================================================
module pinggy

go 1.25

require (
	github.com/Pinggy-io/pinggy-go/pinggy v0.6.9 // indirect
	golang.org/x/crypto v0.8.0 // indirect
	golang.org/x/sys v0.7.0 // indirect
)


================================================
FILE: examples/mod_pinggy/go.sum
================================================
github.com/Pinggy-io/pinggy-go/pinggy v0.6.9 h1:lzZ00JK6BUGQXnpkJZ+cVj8kIkXsmiVBUci9uEkSwEY=
github.com/Pinggy-io/pinggy-go/pinggy v0.6.9/go.mod h1:V1Sxb+4zyr36o9atZiqtT4XhsKtW1RSb2GvsbTbTJYw=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=


================================================
FILE: examples/mod_pinggy/main.go
================================================
package main

import (
	"log"
	"os"

	"github.com/Pinggy-io/pinggy-go/pinggy"
)

func main() {
	tunType := os.Args[1]
	address := os.Args[2]

	log.SetFlags(log.Llongfile | log.LstdFlags)

	config := pinggy.Config{
		Type:              pinggy.TunnelType(tunType),
		TcpForwardingAddr: address,

		//SshOverSsl: true,
		//Stdout:     os.Stderr,
		//Stderr:     os.Stderr,
	}

	if tunType == "http" {
		hman := pinggy.CreateHeaderManipulationAndAuthConfig()
		//hman.SetReverseProxy(address)
		//hman.SetPassPreflight(true)
		//hman.SetNoReverseProxy()
		config.HeaderManipulationAndAuth = hman
	}

	pl, err := pinggy.ConnectWithConfig(config)
	if err != nil {
		log.Panicln(err)
	}
	log.Println("Addrs: ", pl.RemoteUrls())
	//err = pl.InitiateWebDebug("localhost:3424")
	//log.Println(err)
	pl.StartForwarding()
}


================================================
FILE: examples/onvif_client/README.md
================================================
## ONVIF Client

```shell
go run examples/onvif_client/main.go http://admin:password@192.168.10.90 GetAudioEncoderConfigurations
```

================================================
FILE: examples/onvif_client/main.go
================================================
package main

import (
	"log"
	"net/url"
	"os"

	"github.com/AlexxIT/go2rtc/pkg/onvif"
)

func main() {
	var rawURL = os.Args[1]
	var operation = os.Args[2]
	var token string
	if len(os.Args) > 3 {
		token = os.Args[3]
	}

	client, err := onvif.NewClient(rawURL)
	if err != nil {
		log.Panic(err)
	}

	var b []byte

	switch operation {
	case onvif.ServiceGetServiceCapabilities:
		b, err = client.MediaRequest(operation)
	case onvif.DeviceGetCapabilities,
		onvif.DeviceGetDeviceInformation,
		onvif.DeviceGetDiscoveryMode,
		onvif.DeviceGetDNS,
		onvif.DeviceGetHostname,
		onvif.DeviceGetNetworkDefaultGateway,
		onvif.DeviceGetNetworkInterfaces,
		onvif.DeviceGetNetworkProtocols,
		onvif.DeviceGetNTP,
		onvif.DeviceGetScopes,
		onvif.DeviceGetServices,
		onvif.DeviceGetSystemDateAndTime,
		onvif.DeviceSystemReboot:
		b, err = client.DeviceRequest(operation)
	case onvif.MediaGetProfiles,
		onvif.MediaGetVideoEncoderConfigurations,
		onvif.MediaGetVideoSources,
		onvif.MediaGetVideoSourceConfigurations,
		onvif.MediaGetAudioEncoderConfigurations,
		onvif.MediaGetAudioSources,
		onvif.MediaGetAudioSourceConfigurations:
		b, err = client.MediaRequest(operation)
	case onvif.MediaGetProfile:
		b, err = client.GetProfile(token)
	case onvif.MediaGetVideoSourceConfiguration:
		b, err = client.GetVideoSourceConfiguration(token)
	case onvif.MediaGetStreamUri:
		b, err = client.GetStreamUri(token)
	case onvif.MediaGetSnapshotUri:
		b, err = client.GetSnapshotUri(token)
	default:
		log.Printf("unknown action\n")
	}

	if err != nil {
		log.Printf("%s\n", err)
	}

	u, err := url.Parse(rawURL)
	if err != nil {
		log.Fatal(err)
	}

	if err = os.WriteFile(u.Hostname()+"_"+operation+".xml", b, 0644); err != nil {
		log.Printf("%s\n", err)
	}
}


================================================
FILE: examples/rtsp_client/main.go
================================================
package main

import (
	"log"
	"os"

	"github.com/AlexxIT/go2rtc/pkg/core"
	"github.com/AlexxIT/go2rtc/pkg/rtsp"
	"github.com/AlexxIT/go2rtc/pkg/shell"
)

func main() {
	client := rtsp.NewClient(os.Args[1])
	if err := client.Dial(); err != nil {
		log.Panic(err)
	}

	client.Medias = []*core.Media{
		{
			Kind:      core.KindAudio,
			Direction: core.DirectionRecvonly,
			Codecs: []*core.Codec{
				{Name: core.CodecPCMU, ClockRate: 8000},
			},
			ID: "streamid=0",
		},
	}
	if err := client.Announce(); err != nil {
		log.Panic(err)
	}
	if _, err := client.SetupMedia(client.Medias[0]); err != nil {
		log.Panic(err)
	}
	if err := client.Record(); err != nil {
		log.Panic(err)
	}

	shell.RunUntilSignal()
}


================================================
FILE: examples/tutk_decoder/README.md
================================================
# tutk_decoder

1. Wireshark > Select any packet > Follow > UDP Stream
2. Wireshark > File > Export Packet Dissections > As JSON > Displayed, Values
3. `tutk_decoder wireshark.json decoded.txt`


================================================
FILE: examples/tutk_decoder/main.go
================================================
package main

import (
	"encoding/hex"
	"encoding/json"
	"fmt"
	"log"
	"os"
	"strings"

	"github.com/AlexxIT/go2rtc/pkg/tutk"
)

func main() {
	if len(os.Args) != 3 {
		fmt.Println("Usage: tutk_decoder wireshark.json decoded.txt")
		return
	}

	src, err := os.Open(os.Args[1])
	if err != nil {
		log.Fatal(err)
	}
	defer src.Close()

	dst, err := os.Create(os.Args[2])
	if err != nil {
		log.Fatal(err)
	}
	defer dst.Close()

	var items []item
	if err = json.NewDecoder(src).Decode(&items); err != nil {
		log.Fatal(err)
	}

	var b []byte

	for _, v := range items {
		if v.Source.Layers.Data.DataData == "" {
			continue
		}

		s := strings.ReplaceAll(v.Source.Layers.Data.DataData, ":", "")
		b, err = hex.DecodeString(s)
		if err != nil {
			log.Fatal(err)
		}

		tutk.ReverseTransCodePartial(b, b)

		ts := v.Source.Layers.Frame.FrameTimeRelative

		_, _ = fmt.Fprintf(dst, "%8s: %s -> %s [%4d] %x\n",
			ts[:len(ts)-6],
			v.Source.Layers.Ip.IpSrc, v.Source.Layers.Ip.IpDst,
			len(b), b)
	}
}

type item struct {
	Source struct {
		Layers struct {
			Frame struct {
				FrameTimeRelative string `json:"frame.time_relative"`
				FrameNumber       string `json:"frame.number"`
			} `json:"frame"`
			Ip struct {
				IpSrc string `json:"ip.src"`
				IpDst string `json:"ip.dst"`
			} `json:"ip"`
			Udp struct {
				UdpSrcport string `json:"udp.srcport"`
				UdpDstport string `json:"udp.dstport"`
			} `json:"udp"`
			Data struct {
				DataData string `json:"data.data"`
				DataLen  string `json:"data.len"`
			} `json:"data"`
		} `json:"layers"`
	} `json:"_source"`
}


================================================
FILE: go.mod
================================================
module github.com/AlexxIT/go2rtc

go 1.24.0

require (
	github.com/asticode/go-astits v1.14.0
	github.com/eclipse/paho.mqtt.golang v1.5.1
	github.com/expr-lang/expr v1.17.7
	github.com/google/uuid v1.6.0
	github.com/gorilla/websocket v1.5.3
	github.com/mattn/go-isatty v0.0.20
	github.com/miekg/dns v1.1.70
	github.com/pion/dtls/v3 v3.0.10
	github.com/pion/ice/v4 v4.2.0
	github.com/pion/interceptor v0.1.43
	github.com/pion/rtcp v1.2.16
	github.com/pion/rtp v1.10.0
	github.com/pion/sdp/v3 v3.0.17
	github.com/pion/srtp/v3 v3.0.10
	github.com/pion/stun/v3 v3.1.1
	github.com/pion/webrtc/v4 v4.2.3
	github.com/rs/zerolog v1.34.0
	github.com/sigurn/crc16 v0.0.0-20240131213347-83fcde1e29d1
	github.com/sigurn/crc8 v0.0.0-20220107193325-2243fe600f9f
	github.com/stretchr/testify v1.11.1
	github.com/tadglines/go-pkgs v0.0.0-20210623144937-b983b20f54f9
	golang.org/x/crypto v0.47.0
	golang.org/x/net v0.49.0
	gopkg.in/yaml.v3 v3.0.1
)

require (
	github.com/asticode/go-astikit v0.57.1 // indirect
	github.com/davecgh/go-spew v1.1.1 // indirect
	github.com/kr/pretty v0.3.1 // indirect
	github.com/mattn/go-colorable v0.1.14 // indirect
	github.com/pion/datachannel v1.6.0 // indirect
	github.com/pion/logging v0.2.4 // indirect
	github.com/pion/mdns/v2 v2.1.0 // indirect
	github.com/pion/randutil v0.1.0 // indirect
	github.com/pion/sctp v1.9.2 // indirect
	github.com/pion/transport/v3 v3.1.1 // indirect
	github.com/pion/transport/v4 v4.0.1 // indirect
	github.com/pion/turn/v4 v4.1.4 // indirect
	github.com/pmezard/go-difflib v1.0.0 // indirect
	github.com/wlynxg/anet v0.0.5 // indirect
	golang.org/x/mod v0.32.0 // indirect
	golang.org/x/sync v0.19.0 // indirect
	golang.org/x/sys v0.40.0 // indirect
	golang.org/x/time v0.14.0 // indirect
	golang.org/x/tools v0.41.0 // indirect
)


================================================
FILE: go.sum
================================================
github.com/asticode/go-astikit v0.30.0/go.mod h1:h4ly7idim1tNhaVkdVBeXQZEE3L0xblP7fCWbgwipF0=
github.com/asticode/go-astikit v0.57.1 h1:fEykwH98Nny08kcRbk4uer+S8h0rKveCIpG9F6NVLuA=
github.com/asticode/go-astikit v0.57.1/go.mod h1:fV43j20UZYfXzP9oBn33udkvCvDvCDhzjVqoLFuuYZE=
github.com/asticode/go-astits v1.14.0 h1:zkgnZzipx2XX5mWycqsSBeEyDH58+i4HtyF4j2ROb00=
github.com/asticode/go-astits v1.14.0/go.mod h1:QSHmknZ51pf6KJdHKZHJTLlMegIrhega3LPWz3ND/iI=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/eclipse/paho.mqtt.golang v1.5.1 h1:/VSOv3oDLlpqR2Epjn1Q7b2bSTplJIeV2ISgCl2W7nE=
github.com/eclipse/paho.mqtt.golang v1.5.1/go.mod h1:1/yJCneuyOoCOzKSsOTUc0AJfpsItBGWvYpBLimhArU=
github.com/expr-lang/expr v1.17.6 h1:1h6i8ONk9cexhDmowO/A64VPxHScu7qfSl2k8OlINec=
github.com/expr-lang/expr v1.17.6/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4=
github.com/expr-lang/expr v1.17.7 h1:Q0xY/e/2aCIp8g9s/LGvMDCC5PxYlvHgDZRQ4y16JX8=
github.com/expr-lang/expr v1.17.7/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/miekg/dns v1.1.69 h1:Kb7Y/1Jo+SG+a2GtfoFUfDkG//csdRPwRLkCsxDG9Sc=
github.com/miekg/dns v1.1.69/go.mod h1:7OyjD9nEba5OkqQ/hB4fy3PIoxafSZJtducccIelz3g=
github.com/miekg/dns v1.1.70 h1:DZ4u2AV35VJxdD9Fo9fIWm119BsQL5cZU1cQ9s0LkqA=
github.com/miekg/dns v1.1.70/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs=
github.com/pion/datachannel v1.5.10 h1:ly0Q26K1i6ZkGf42W7D4hQYR90pZwzFOjTq5AuCKk4o=
github.com/pion/datachannel v1.5.10/go.mod h1:p/jJfC9arb29W7WrxyKbepTU20CFgyx5oLo8Rs4Py/M=
github.com/pion/datachannel v1.6.0 h1:XecBlj+cvsxhAMZWFfFcPyUaDZtd7IJvrXqlXD/53i0=
github.com/pion/datachannel v1.6.0/go.mod h1:ur+wzYF8mWdC+Mkis5Thosk+u/VOL287apDNEbFpsIk=
github.com/pion/dtls/v3 v3.0.9 h1:4AijfFRm8mAjd1gfdlB1wzJF3fjjR/VPIpJgkEtvYmM=
github.com/pion/dtls/v3 v3.0.9/go.mod h1:abApPjgadS/ra1wvUzHLc3o2HvoxppAh+NZkyApL4Os=
github.com/pion/dtls/v3 v3.0.10 h1:k9ekkq1kaZoxnNEbyLKI8DI37j/Nbk1HWmMuywpQJgg=
github.com/pion/dtls/v3 v3.0.10/go.mod h1:YEmmBYIoBsY3jmG56dsziTv/Lca9y4Om83370CXfqJ8=
github.com/pion/ice/v4 v4.1.0 h1:YlxIii2bTPWyC08/4hdmtYq4srbrY0T9xcTsTjldGqU=
github.com/pion/ice/v4 v4.1.0/go.mod h1:5gPbzYxqenvn05k7zKPIZFuSAufolygiy6P1U9HzvZ4=
github.com/pion/ice/v4 v4.2.0 h1:jJC8S+CvXCCvIQUgx+oNZnoUpt6zwc34FhjWwCU4nlw=
github.com/pion/ice/v4 v4.2.0/go.mod h1:EgjBGxDgmd8xB0OkYEVFlzQuEI7kWSCFu+mULqaisy4=
github.com/pion/interceptor v0.1.42 h1:0/4tvNtruXflBxLfApMVoMubUMik57VZ+94U0J7cmkQ=
github.com/pion/interceptor v0.1.42/go.mod h1:g6XYTChs9XyolIQFhRHOOUS+bGVGLRfgTCUzH29EfVU=
github.com/pion/interceptor v0.1.43 h1:6hmRfnmjogSs300xfkR0JxYFZ9k5blTEvCD7wxEDuNQ=
github.com/pion/interceptor v0.1.43/go.mod h1:BSiC1qKIJt1XVr3l3xQ2GEmCFStk9tx8fwtCZxxgR7M=
github.com/pion/logging v0.2.4 h1:tTew+7cmQ+Mc1pTBLKH2puKsOvhm32dROumOZ655zB8=
github.com/pion/logging v0.2.4/go.mod h1:DffhXTKYdNZU+KtJ5pyQDjvOAh/GsNSyv1lbkFbe3so=
github.com/pion/mdns/v2 v2.1.0 h1:3IJ9+Xio6tWYjhN6WwuY142P/1jA0D5ERaIqawg/fOY=
github.com/pion/mdns/v2 v2.1.0/go.mod h1:pcez23GdynwcfRU1977qKU0mDxSeucttSHbCSfFOd9A=
github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
github.com/pion/rtcp v1.2.16 h1:fk1B1dNW4hsI78XUCljZJlC4kZOPk67mNRuQ0fcEkSo=
github.com/pion/rtcp v1.2.16/go.mod h1:/as7VKfYbs5NIb4h6muQ35kQF/J0ZVNz2Z3xKoCBYOo=
github.com/pion/rtp v1.8.26 h1:VB+ESQFQhBXFytD+Gk8cxB6dXeVf2WQzg4aORvAvAAc=
github.com/pion/rtp v1.8.26/go.mod h1:rF5nS1GqbR7H/TCpKwylzeq6yDM+MM6k+On5EgeThEM=
github.com/pion/rtp v1.10.0 h1:XN/xca4ho6ZEcijpdF2VGFbwuHUfiIMf3ew8eAAE43w=
github.com/pion/rtp v1.10.0/go.mod h1:rF5nS1GqbR7H/TCpKwylzeq6yDM+MM6k+On5EgeThEM=
github.com/pion/sctp v1.8.41 h1:20R4OHAno4Vky3/iE4xccInAScAa83X6nWUfyc65MIs=
github.com/pion/sctp v1.8.41/go.mod h1:2wO6HBycUH7iCssuGyc2e9+0giXVW0pyCv3ZuL8LiyY=
github.com/pion/sctp v1.9.2 h1:HxsOzEV9pWoeggv7T5kewVkstFNcGvhMPx0GvUOUQXo=
github.com/pion/sctp v1.9.2/go.mod h1:OTOlsQ5EDQ6mQ0z4MUGXt2CgQmKyafBEXhUVqLRB6G8=
github.com/pion/sdp/v3 v3.0.16 h1:0dKzYO6gTAvuLaAKQkC02eCPjMIi4NuAr/ibAwrGDCo=
github.com/pion/sdp/v3 v3.0.16/go.mod h1:9tyKzznud3qiweZcD86kS0ff1pGYB3VX+Bcsmkx6IXo=
github.com/pion/sdp/v3 v3.0.17 h1:9SfLAW/fF1XC8yRqQ3iWGzxkySxup4k4V7yN8Fs8nuo=
github.com/pion/sdp/v3 v3.0.17/go.mod h1:9tyKzznud3qiweZcD86kS0ff1pGYB3VX+Bcsmkx6IXo=
github.com/pion/srtp/v3 v3.0.9 h1:lRGF4G61xxj+m/YluB3ZnBpiALSri2lTzba0kGZMrQY=
github.com/pion/srtp/v3 v3.0.9/go.mod h1:E+AuWd7Ug2Fp5u38MKnhduvpVkveXJX6J4Lq4rxUYt8=
github.com/pion/srtp/v3 v3.0.10 h1:tFirkpBb3XccP5VEXLi50GqXhv5SKPxqrdlhDCJlZrQ=
github.com/pion/srtp/v3 v3.0.10/go.mod h1:3mOTIB0cq9qlbn59V4ozvv9ClW/BSEbRp4cY0VtaR7M=
github.com/pion/stun/v3 v3.0.2 h1:BJuGEN2oLrJisiNEJtUTJC4BGbzbfp37LizfqswblFU=
github.com/pion/stun/v3 v3.0.2/go.mod h1:JFJKfIWvt178MCF5H/YIgZ4VX3LYE77vca4b9HP60SA=
github.com/pion/stun/v3 v3.1.1 h1:CkQxveJ4xGQjulGSROXbXq94TAWu8gIX2dT+ePhUkqw=
github.com/pion/stun/v3 v3.1.1/go.mod h1:qC1DfmcCTQjl9PBaMa5wSn3x9IPmKxSdcCsxBcDBndM=
github.com/pion/transport/v3 v3.1.1 h1:Tr684+fnnKlhPceU+ICdrw6KKkTms+5qHMgw6bIkYOM=
github.com/pion/transport/v3 v3.1.1/go.mod h1:+c2eewC5WJQHiAA46fkMMzoYZSuGzA/7E2FPrOYHctQ=
github.com/pion/transport/v4 v4.0.1 h1:sdROELU6BZ63Ab7FrOLn13M6YdJLY20wldXW2Cu2k8o=
github.com/pion/transport/v4 v4.0.1/go.mod h1:nEuEA4AD5lPdcIegQDpVLgNoDGreqM/YqmEx3ovP4jM=
github.com/pion/turn/v4 v4.1.3 h1:jVNW0iR05AS94ysEtvzsrk3gKs9Zqxf6HmnsLfRvlzA=
github.com/pion/turn/v4 v4.1.3/go.mod h1:TD/eiBUf5f5LwXbCJa35T7dPtTpCHRJ9oJWmyPLVT3A=
github.com/pion/turn/v4 v4.1.4 h1:EU11yMXKIsK43FhcUnjLlrhE4nboHZq+TXBIi3QpcxQ=
github.com/pion/turn/v4 v4.1.4/go.mod h1:ES1DXVFKnOhuDkqn9hn5VJlSWmZPaRJLyBXoOeO/BmQ=
github.com/pion/webrtc/v4 v4.1.8 h1:ynkjfiURDQ1+8EcJsoa60yumHAmyeYjz08AaOuor+sk=
github.com/pion/webrtc/v4 v4.1.8/go.mod h1:KVaARG2RN0lZx0jc7AWTe38JpPv+1/KicOZ9jN52J/s=
github.com/pion/webrtc/v4 v4.2.3 h1:RtdWDnkenNQGxUrZqWa5gSkTm5ncsLg5d+zu0M4cXt4=
github.com/pion/webrtc/v4 v4.2.3/go.mod h1:7vsyFzRzaKP5IELUnj8zLcglPyIT6wWwqTppBZ1k6Kc=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/profile v1.4.0/go.mod h1:NWz/XGvpEW1FyYQ7fCx4dqYBLlfTcE+A9FLAkNKqjFE=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
github.com/sigurn/crc16 v0.0.0-20240131213347-83fcde1e29d1 h1:NVK+OqnavpyFmUiKfUMHrpvbCi2VFoWTrcpI7aDaJ2I=
github.com/sigurn/crc16 v0.0.0-20240131213347-83fcde1e29d1/go.mod h1:9/etS5gpQq9BJsJMWg1wpLbfuSnkm8dPF6FdW2JXVhA=
github.com/sigurn/crc8 v0.0.0-20220107193325-2243fe600f9f h1:1R9KdKjCNSd7F8iGTxIpoID9prlYH8nuNYKt0XvweHA=
github.com/sigurn/crc8 v0.0.0-20220107193325-2243fe600f9f/go.mod h1:vQhwQ4meQEDfahT5kd61wLAF5AAeh5ZPLVI4JJ/tYo8=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/tadglines/go-pkgs v0.0.0-20210623144937-b983b20f54f9 h1:aeN+ghOV0b2VCmKKO3gqnDQ8mLbpABZgRR2FVYx4ouI=
github.com/tadglines/go-pkgs v0.0.0-20210623144937-b983b20f54f9/go.mod h1:roo6cZ/uqpwKMuvPG0YmzI5+AmUiMWfjCBZpGXqbTxE=
github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU=
github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA=
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=
golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=
golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI=
golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg=
golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c=
golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU=
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q=
golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg=
golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY=
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA=
golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc=
golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc=
golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=


================================================
FILE: internal/README.md
================================================
# Modules

go2rtc tries to name formats, protocols and codecs the same way they are named in FFmpeg.
Some formats and protocols go2rtc supports exclusively. They have no equivalent in FFmpeg.

- The [`echo`], [`expr`], [`hass`] and [`onvif`] modules receive a link to a stream. They don't know the protocol in advance.
- The [`exec`] and [`ffmpeg`] modules support many formats. They are identical to the [`http`] module.
- The [`api`], [`app`], [`debug`], [`ngrok`], [`pinggy`], [`srtp`], [`streams`] are supporting modules.

**Modules** implement communication APIs: authorization, encryption, command set, structure of media packets.

**Formats** describe the structure of the data being transmitted.

**Protocols** implement transport for data transmission.

| module         | formats         | protocols        | input | output | ingest | two-way |
|----------------|-----------------|------------------|-------|--------|--------|---------|
| [`alsa`]       | `pcm`           | `ioctl`          | yes   |        |        |         |
| [`bubble`]     | -               | `http`           | yes   |        |        |         |
| [`doorbird`]   | `mulaw`         | `http`           | yes   |        |        | yes     |
| [`dvrip`]      | -               | `tcp`            | yes   |        |        | yes     |
| [`echo`]       | *               | *                | yes   |        |        |         |
| [`eseecloud`]  | `rtp`           | `http`           | yes   |        |        |         |
| [`exec`]       | *               | `pipe`, `rtsp`   | yes   |        |        | yes     |
| [`expr`]       | *               | *                | yes   |        |        |         |
| [`ffmpeg`]     | *               | `pipe`, `rtsp`   | yes   |        |        |         |
| [`flussonic`]  | `mp4`           | `ws`             | yes   |        |        |         |
| [`gopro`]      | `mpegts`        | `udp`            | yes   |        |        |         |
| [`hass`]       | *               | *                | yes   |        |        |         |
| [`hls`]        | `mpegts`, `mp4` | `http`           |       | yes    |        |         |
| [`homekit`]    | `srtp`          | `hap`            | yes   | yes    |        | no      |
| [`http`]       | `adts`          | `http`, `tcp`    | yes   |        |        |         |
| [`http`]       | `flv`           | `http`, `tcp`    | yes   |        |        |         |
| [`http`]       | `h264`          | `http`, `tcp`    | yes   |        |        |         |
| [`http`]       | `hevc`          | `http`, `tcp`    | yes   |        |        |         |
| [`http`]       | `hls`           | `http`, `tcp`    | yes   |        |        |         |
| [`http`]       | `mjpeg`         | `http`, `tcp`    | yes   |        |        |         |
| [`http`]       | `mpjpeg`        | `http`           | yes   |        |        |         |
| [`http`]       | `mpegts`        | `http`, `tcp`    | yes   |        |        |         |
| [`http`]       | `wav`           | `http`, `tcp`    | yes   |        |        |         |
| [`http`]       | `yuv4mpegpipe`  | `http`, `tcp`    | yes   |        |        |         |
| [`isapi`]      | `alaw`, `mulaw` | `http`           |       |        |        | yes     |
| [`ivideon`]    | `mp4`           | `ws`             | yes   |        |        |         |
| [`kasa`]       | `h264`, `mulaw` | `http`           | yes   |        |        |         |
| [`mjpeg`]      | `ascii`         | `http`           |       | yes    |        |         |
| [`mjpeg`]      | `jpeg`          | `http`           |       | yes    |        |         |
| [`mjpeg`]      | `mpjpeg`        | `http`           |       | yes    | yes    |         |
| [`mjpeg`]      | `yuv4mpegpipe`  | `http`           |       | yes    |        |         |
| [`mp4`]        | `mp4`           | `http`, `ws`     |       | yes    |        |         |
| [`mpeg`]       | `adts`          | `http`           |       | yes    |        |         |
| [`mpeg`]       | `mpegts`        | `http`           |       | yes    | yes    |         |
| [`multitrans`] | `rtp`           | `tcp`            |       |        |        | yes     |
| [`nest`]       | `srtp`          | `rtsp`, `webrtc` | yes   |        |        | no      |
| [`onvif`]      | `rtp`           | *                | yes   | yes    |        |         |
| [`ring`]       | `srtp`          | `webrtc`         | yes   |        |        | yes     |
| [`roborock`]   | `srtp`          | `webrtc`         | yes   |        |        | yes     |
| [`rtmp`]       | `flv`           | `rtmp`           | yes   | yes    | yes    |         |
| [`rtmp`]       | `flv`           | `http`           |       | yes    | yes    |         |
| [`rtsp`]       | `rtsp`          | `rtsp`           | yes   | yes    | yes    | yes     |
| [`tapo`]       | `mpegts`        | `http`           | yes   |        |        | yes     |
| [`tuya`]       | `srtp`          | `webrtc`         | yes   |        |        | yes     |
| [`v4l2`]       | `rawvideo`      | `ioctl`          | yes   |        |        |         |
| [`webrtc`]     | `srtp`          | `webrtc`         | yes   | yes    | yes    | yes     |
| [`webtorrent`] | `srtp`          | `webrtc`         | yes   | yes    |        |         |
| [`wyoming`]    | `pcm`           | `tcp`            |       | yes    |        |         |
| [`wyze`]       | -               | `tutk`           | yes   |        |        | yes     |
| [`xiaomi`]     | -               | `cs2`, `tutk`    | yes   |        |        | yes     |
| [`yandex`]     | `srtp`          | `webrtc`         | yes   |        |        |         |

[`alsa`]: alsa/README.md
[`api`]: api/README.md
[`app`]: app/README.md
[`bubble`]: bubble/README.md
[`debug`]: debug/README.md
[`doorbird`]: doorbird/README.md
[`dvrip`]: dvrip/README.md
[`echo`]: echo/README.md
[`eseecloud`]: eseecloud/README.md
[`exec`]: exec/README.md
[`expr`]: expr/README.md
[`ffmpeg`]: ffmpeg/README.md
[`flussonic`]: flussonic/README.md
[`gopro`]: gopro/README.md
[`hass`]: hass/README.md
[`hls`]: hls/README.md
[`homekit`]: homekit/README.md
[`http`]: http/README.md
[`isapi`]: isapi/README.md
[`ivideon`]: ivideon/README.md
[`kasa`]: kasa/README.md
[`mjpeg`]: mjpeg/README.md
[`mp4`]: mp4/README.md
[`mpeg`]: mpeg/README.md
[`multitrans`]: multitrans/README.md
[`nest`]: nest/README.md
[`ngrok`]: ngrok/README.md
[`onvif`]: onvif/README.md
[`pinggy`]: pinggy/README.md
[`ring`]: ring/README.md
[`roborock`]: roborock/README.md
[`rtmp`]: rtmp/README.md
[`rtsp`]: rtsp/README.md
[`srtp`]: srtp/README.md
[`streams`]: streams/README.md
[`tapo`]: tapo/README.md
[`tuya`]: tuya/README.md
[`v4l2`]: v4l2/README.md
[`webrtc`]: webrtc/README.md
[`webtorrent`]: webtorrent/README.md
[`wyoming`]: wyze/README.md
[`wyze`]: wyze/README.md
[`xiaomi`]: xiaomi/README.md
[`yandex`]: yandex/README.md


================================================
FILE: internal/alsa/README.md
================================================
# ALSA

[`new in v1.9.10`](https://github.com/AlexxIT/go2rtc/releases/tag/v1.9.10)

> [!WARNING]
> This source is under development and does not always work well.

[Advanced Linux Sound Architecture](https://en.wikipedia.org/wiki/Advanced_Linux_Sound_Architecture) - a framework for receiving audio from devices on Linux OS.

Easy to add via **WebUI > add > ALSA**.

Alternatively, you can use FFmpeg source.


================================================
FILE: internal/alsa/alsa.go
================================================
//go:build !(linux && (386 || amd64 || arm || arm64 || mipsle))

package alsa

func Init() {
	// not supported
}


================================================
FILE: internal/alsa/alsa_linux.go
================================================
//go:build linux && (386 || amd64 || arm || arm64 || mipsle)

package alsa

import (
	"fmt"
	"net/http"
	"os"
	"strconv"
	"strings"

	"github.com/AlexxIT/go2rtc/internal/api"
	"github.com/AlexxIT/go2rtc/internal/streams"
	"github.com/AlexxIT/go2rtc/pkg/alsa"
	"github.com/AlexxIT/go2rtc/pkg/alsa/device"
)

func Init() {
	streams.HandleFunc("alsa", alsa.Open)

	api.HandleFunc("api/alsa", apiAlsa)
}

func apiAlsa(w http.ResponseWriter, r *http.Request) {
	files, err := os.ReadDir("/dev/snd/")
	if err != nil {
		return
	}

	var sources []*api.Source

	for _, file := range files {
		if !strings.HasPrefix(file.Name(), "pcm") {
			continue
		}

		path := "/dev/snd/" + file.Name()

		dev, err := device.Open(path)
		if err != nil {
			continue
		}

		info, err := dev.Info()
		if err == nil {
			formats := formatsToString(dev.ListFormats())
			r1, r2 := dev.RangeRates()
			c1, c2 := dev.RangeChannels()
			source := &api.Source{
				Name: info.ID,
				Info: fmt.Sprintf("Formats: %s, Rates: %d-%d, Channels: %d-%d", formats, r1, r2, c1, c2),
				URL:  "alsa:device?audio=" + path,
			}
			if !strings.Contains(source.Name, info.Name) {
				source.Name += ", " + info.Name
			}
			sources = append(sources, source)
		}

		_ = dev.Close()
	}

	api.ResponseSources(w, sources)
}

func formatsToString(formats []byte) string {
	var s string
	for i, format := range formats {
		if i > 0 {
			s += " "
		}
		switch format {
		case 2:
			s += "s16le"
		case 10:
			s += "s32le"
		default:
			s += strconv.Itoa(int(format))
		}

	}
	return s
}


================================================
FILE: internal/api/README.md
================================================
# HTTP API

The HTTP API is the main part for interacting with the application. Default address: `http://localhost:1984/`.

The HTTP API is described in [OpenAPI](../../website/api/openapi.yaml) format. It can be explored in [interactive viewer](https://go2rtc.org/api/). WebSocket API described [here](ws/README.md).

The project's static HTML and JS files are located in the [www](../../www/README.md) folder. An external developer can use them as a basis for integrating go2rtc into their project or for developing a custom web interface for go2rtc.

The contents of `www` folder are built into go2rtc when building, but you can use configuration to specify an external folder as the source of static files.

## Configuration

**Important!** go2rtc passes requests from localhost and Unix sockets without HTTP authorization, even if you have it configured. It is your responsibility to set up secure external access to the API. If not properly configured, an attacker can gain access to your cameras and even your server.

- you can disable HTTP API with `listen: ""` and use, for example, only RTSP client/server protocol
- you can enable HTTP API only on localhost with `listen: "127.0.0.1:1984"` setting
- you can change the API `base_path` and host go2rtc on your main app webserver suburl
- all files from `static_dir` hosted on root path: `/`
- you can use raw TLS cert/key content or path to files

```yaml
api:
  listen: ":1984"    # default ":1984", HTTP API port ("" - disabled)
  username: "admin"  # default "", Basic auth for WebUI
  password: "pass"   # default "", Basic auth for WebUI
  local_auth: true   # default false, Enable auth check for localhost requests
  base_path: "/rtc"  # default "", API prefix for serving on suburl (/api => /rtc/api)
  static_dir: "www"  # default "", folder for static files (custom web interface)
  origin: "*"        # default "", allow CORS requests (only * supported)
  tls_listen: ":443" # default "", enable HTTPS server
  tls_cert: |        # default "", PEM-encoded fullchain certificate for HTTPS
    -----BEGIN CERTIFICATE-----
    ...
    -----END CERTIFICATE-----
  tls_key: |         # default "", PEM-encoded private key for HTTPS
    -----BEGIN PRIVATE KEY-----
    ...
    -----END PRIVATE KEY-----
  unix_listen: "/tmp/go2rtc.sock"  # default "", unix socket listener for API
```

**PS:**

- MJPEG over WebSocket plays better than native MJPEG because Chrome [bug](https://bugs.chromium.org/p/chromium/issues/detail?id=527446)
- MP4 over WebSocket was created only for Apple iOS because it doesn't support file streaming


================================================
FILE: internal/api/api.go
================================================
package api

import (
	"crypto/tls"
	"encoding/json"
	"fmt"
	"net"
	"net/http"
	"os"
	"slices"
	"strconv"
	"strings"
	"sync"
	"syscall"
	"time"

	"github.com/AlexxIT/go2rtc/internal/app"
	"github.com/rs/zerolog"
)

func Init() {
	var cfg struct {
		Mod struct {
			Listen     string `yaml:"listen"`
			Username   string `yaml:"username"`
			Password   string `yaml:"password"`
			LocalAuth  bool   `yaml:"local_auth"`
			BasePath   string `yaml:"base_path"`
			StaticDir  string `yaml:"static_dir"`
			Origin     string `yaml:"origin"`
			TLSListen  string `yaml:"tls_listen"`
			TLSCert    string `yaml:"tls_cert"`
			TLSKey     string `yaml:"tls_key"`
			UnixListen string `yaml:"unix_listen"`

			AllowPaths []string `yaml:"allow_paths"`
		} `yaml:"api"`
	}

	// default config
	cfg.Mod.Listen = ":1984"

	// load config from YAML
	app.LoadConfig(&cfg)

	if cfg.Mod.Listen == "" && cfg.Mod.UnixListen == "" && cfg.Mod.TLSListen == "" {
		return
	}

	allowPaths = cfg.Mod.AllowPaths
	basePath = cfg.Mod.BasePath
	log = app.GetLogger("api")

	initStatic(cfg.Mod.StaticDir)

	HandleFunc("api", apiHandler)
	HandleFunc("api/config", configHandler)
	HandleFunc("api/exit", exitHandler)
	HandleFunc("api/restart", restartHandler)
	HandleFunc("api/log", logHandler)

	Handler = http.DefaultServeMux // 4th

	if cfg.Mod.Origin == "*" {
		Handler = middlewareCORS(Handler) // 3rd
	}

	if cfg.Mod.Username != "" {
		Handler = middlewareAuth(cfg.Mod.Username, cfg.Mod.Password, cfg.Mod.LocalAuth, Handler) // 2nd
	}

	if log.Trace().Enabled() {
		Handler = middlewareLog(Handler) // 1st
	}

	if cfg.Mod.Listen != "" {
		_, port, _ := net.SplitHostPort(cfg.Mod.Listen)
		Port, _ = strconv.Atoi(port)
		go listen("tcp", cfg.Mod.Listen)
	}

	if cfg.Mod.UnixListen != "" {
		_ = syscall.Unlink(cfg.Mod.UnixListen)
		go listen("unix", cfg.Mod.UnixListen)
	}

	// Initialize the HTTPS server
	if cfg.Mod.TLSListen != "" && cfg.Mod.TLSCert != "" && cfg.Mod.TLSKey != "" {
		go tlsListen("tcp", cfg.Mod.TLSListen, cfg.Mod.TLSCert, cfg.Mod.TLSKey)
	}
}

func listen(network, address string) {
	ln, err := net.Listen(network, address)
	if err != nil {
		log.Error().Err(err).Msg("[api] listen")
		return
	}

	log.Info().Str("addr", address).Msg("[api] listen")

	server := http.Server{
		Handler:           Handler,
		ReadHeaderTimeout: 5 * time.Second, // Example: Set to 5 seconds
	}
	if err = server.Serve(ln); err != nil {
		log.Fatal().Err(err).Msg("[api] serve")
	}
}

func tlsListen(network, address, certFile, keyFile string) {
	var cert tls.Certificate
	var err error
	if strings.IndexByte(certFile, '\n') < 0 && strings.IndexByte(keyFile, '\n') < 0 {
		// check if file path
		cert, err = tls.LoadX509KeyPair(certFile, keyFile)
	} else {
		// if text file content
		cert, err = tls.X509KeyPair([]byte(certFile), []byte(keyFile))
	}
	if err != nil {
		log.Error().Err(err).Caller().Send()
		return
	}

	ln, err := net.Listen(network, address)
	if err != nil {
		log.Error().Err(err).Msg("[api] tls listen")
		return
	}

	log.Info().Str("addr", address).Msg("[api] tls listen")

	server := &http.Server{
		Handler:           Handler,
		TLSConfig:         &tls.Config{Certificates: []tls.Certificate{cert}},
		ReadHeaderTimeout: 5 * time.Second,
	}
	if err = server.ServeTLS(ln, "", ""); err != nil {
		log.Fatal().Err(err).Msg("[api] tls serve")
	}
}

var Port int

const (
	MimeJSON = "application/json"
	MimeText = "text/plain"
)

var Handler http.Handler

// HandleFunc handle pattern with relative path:
// - "api/streams" => "{basepath}/api/streams"
// - "/streams"    => "/streams"
func HandleFunc(pattern string, handler http.HandlerFunc) {
	if len(pattern) == 0 || pattern[0] != '/' {
		pattern = basePath + "/" + pattern
	}
	if allowPaths != nil && !slices.Contains(allowPaths, pattern) {
		log.Trace().Str("path", pattern).Msg("[api] ignore path not in allow_paths")
		return
	}
	log.Trace().Str("path", pattern).Msg("[api] register path")
	http.HandleFunc(pattern, handler)
}

// ResponseJSON important always add Content-Type
// so go won't need to call http.DetectContentType
func ResponseJSON(w http.ResponseWriter, v any) {
	w.Header().Set("Content-Type", MimeJSON)
	_ = json.NewEncoder(w).Encode(v)
}

func ResponsePrettyJSON(w http.ResponseWriter, v any) {
	w.Header().Set("Content-Type", MimeJSON)
	enc := json.NewEncoder(w)
	enc.SetIndent("", "  ")
	_ = enc.Encode(v)
}

func Response(w http.ResponseWriter, body any, contentType string) {
	w.Header().Set("Content-Type", contentType)

	switch v := body.(type) {
	case []byte:
		_, _ = w.Write(v)
	case string:
		_, _ = w.Write([]byte(v))
	default:
		_, _ = fmt.Fprint(w, body)
	}
}

const StreamNotFound = "stream not found"

var allowPaths []string
var basePath string
var log zerolog.Logger

func middlewareLog(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		log.Trace().Msgf("[api] %s %s %s", r.Method, r.URL, r.RemoteAddr)
		next.ServeHTTP(w, r)
	})
}

func isLoopback(remoteAddr string) bool {
	return strings.HasPrefix(remoteAddr, "127.") || strings.HasPrefix(remoteAddr, "[::1]") || remoteAddr == "@"
}

func middlewareAuth(username, password string, localAuth bool, next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if localAuth || !isLoopback(r.RemoteAddr) {
			user, pass, ok := r.BasicAuth()
			if !ok || user != username || pass != password {
				w.Header().Set("Www-Authenticate", `Basic realm="go2rtc"`)
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
		}

		next.ServeHTTP(w, r)
	})
}

func middlewareCORS(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Access-Control-Allow-Origin", "*")
		w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
		w.Header().Set("Access-Control-Allow-Headers", "Authorization, Content-Type")
		next.ServeHTTP(w, r)
	})
}

var mu sync.Mutex

func apiHandler(w http.ResponseWriter, r *http.Request) {
	mu.Lock()
	app.Info["host"] = r.Host
	mu.Unlock()

	ResponseJSON(w, app.Info)
}

func exitHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		http.Error(w, "", http.StatusBadRequest)
		return
	}

	s := r.URL.Query().Get("code")
	code, err := strconv.Atoi(s)

	// https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_08_02
	if err != nil || code < 0 || code > 125 {
		http.Error(w, "Code must be in the range [0, 125]", http.StatusBadRequest)
		return
	}

	os.Exit(code)
}

func restartHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		http.Error(w, "", http.StatusBadRequest)
		return
	}

	path, err := os.Executable()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	log.Debug().Msgf("[api] restart %s", path)

	go syscall.Exec(path, os.Args, os.Environ())
}

func logHandler(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		// Send current state of the log file immediately
		w.Header().Set("Content-Type", "application/jsonlines")
		_, _ = app.MemoryLog.WriteTo(w)
	case "DELETE":
		app.MemoryLog.Reset()
		Response(w, "OK", "text/plain")
	default:
		http.Error(w, "Method not allowed", http.StatusBadRequest)
	}
}

type Source struct {
	ID       string `json:"id,omitempty"`
	Name     string `json:"name,omitempty"`
	Info     string `json:"info,omitempty"`
	URL      string `json:"url,omitempty"`
	Location string `json:"location,omitempty"`
}

func ResponseSources(w http.ResponseWriter, sources []*Source) {
	if len(sources) == 0 {
		http.Error(w, "no sources", http.StatusNotFound)
		return
	}

	var response = struct {
		Sources []*Source `json:"sources"`
	}{
		Sources: sources,
	}
	ResponseJSON(w, response)
}

func Error(w http.ResponseWriter, err error) {
	log.Error().Err(err).Caller(1).Send()

	http.Error(w, err.Error(), http.StatusInsufficientStorage)
}


================================================
FILE: internal/api/config.go
================================================
package api

import (
	"io"
	"net/http"
	"os"

	"github.com/AlexxIT/go2rtc/internal/app"
	"gopkg.in/yaml.v3"
)

func configHandler(w http.ResponseWriter, r *http.Request) {
	if app.ConfigPath == "" {
		http.Error(w, "", http.StatusGone)
		return
	}

	switch r.Method {
	case "GET":
		data, err := os.ReadFile(app.ConfigPath)
		if err != nil {
			http.Error(w, "", http.StatusNotFound)
			return
		}
		// https://www.ietf.org/archive/id/draft-ietf-httpapi-yaml-mediatypes-00.html
		Response(w, data, "application/yaml")

	case "POST", "PATCH":
		data, err := io.ReadAll(r.Body)
		if err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}

		if r.Method == "PATCH" {
			// no need to validate after merge
			data, err = mergeYAML(app.ConfigPath, data)
			if err != nil {
				http.Error(w, err.Error(), http.StatusBadRequest)
				return
			}
		} else {
			// validate config
			if err = yaml.Unmarshal(data, map[string]any{}); err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
		}

		if err = os.WriteFile(app.ConfigPath, data, 0644); err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
	}
}

func mergeYAML(file1 string, yaml2 []byte) ([]byte, error) {
	// Read the contents of the first YAML file
	data1, err := os.ReadFile(file1)
	if err != nil {
		return nil, err
	}

	// Unmarshal the first YAML file into a map
	var config1 map[string]any
	if err = yaml.Unmarshal(data1, &config1); err != nil {
		return nil, err
	}

	// Unmarshal the second YAML document into a map
	var config2 map[string]any
	if err = yaml.Unmarshal(yaml2, &config2); err != nil {
		return nil, err
	}

	// Merge the two maps
	config1 = merge(config1, config2)

	// Marshal the merged map into YAML
	return yaml.Marshal(&config1)
}

func merge(dst, src map[string]any) map[string]any {
	for k, v := range src {
		if vv, ok := dst[k]; ok {
			switch vv := vv.(type) {
			case map[string]any:
				v := v.(map[string]any)
				dst[k] = merge(vv, v)
			case []any:
				v := v.([]any)
				dst[k] = v
			default:
				dst[k] = v
			}
		} else {
			dst[k] = v
		}
	}
	return dst
}


================================================
FILE: internal/api/static.go
================================================
package api

import (
	"net/http"

	"github.com/AlexxIT/go2rtc/www"
)

func initStatic(staticDir string) {
	var root http.FileSystem
	if staticDir != "" {
		log.Info().Str("dir", staticDir).Msg("[api] serve static")
		root = http.Dir(staticDir)
	} else {
		root = http.FS(www.Static)
	}

	base := len(basePath)
	fileServer := http.FileServer(root)

	HandleFunc("", func(w http.ResponseWriter, r *http.Request) {
		if base > 0 {
			r.URL.Path = r.URL.Path[base:]
		}
		fileServer.ServeHTTP(w, r)
	})
}


================================================
FILE: internal/api/ws/README.md
================================================
# WebSocket

Endpoint: `/api/ws`

Query parameters:

- `src` (required) - Stream name

### WebRTC

Request SDP:

```json
{"type":"webrtc/offer","value":"v=0\r\n..."}
```

Response SDP:

```json
{"type":"webrtc/answer","value":"v=0\r\n..."}
```

Request/response candidate:

- empty value also allowed and optional

```json
{"type":"webrtc/candidate","value":"candidate:3277516026 1 udp 2130706431 192.168.1.123 54321 typ host"}
```

### MSE

Request:

- codecs list optional

```json
{"type":"mse","value":"avc1.640029,avc1.64002A,avc1.640033,hvc1.1.6.L153.B0,mp4a.40.2,mp4a.40.5,flac,opus"}
```

Response:

```json
{"type":"mse","value":"video/mp4; codecs=\"avc1.64001F,mp4a.40.2\""}
```

### HLS

Request:

```json
{"type":"hls","value":"avc1.640029,avc1.64002A,avc1.640033,hvc1.1.6.L153.B0,mp4a.40.2,mp4a.40.5,flac"}
```

Response:

- you MUST rewrite full HTTP path to `http://192.168.1.123:1984/api/hls/playlist.m3u8`

```json
{"type":"hls","value":"#EXTM3U\n#EXT-X-STREAM-INF:BANDWIDTH=1000000,CODECS=\"avc1.64001F,mp4a.40.2\"\nhls/playlist.m3u8?id=DvmHdd9w"}
```

### MJPEG

Request/response:

```json
{"type":"mjpeg"}
```


================================================
FILE: internal/api/ws/ws.go
================================================
package ws

import (
	"encoding/json"
	"io"
	"net/http"
	"net/url"
	"strings"
	"sync"
	"time"

	"github.com/AlexxIT/go2rtc/internal/api"
	"github.com/AlexxIT/go2rtc/internal/app"
	"github.com/AlexxIT/go2rtc/pkg/creds"
	"github.com/gorilla/websocket"
	"github.com/rs/zerolog"
)

func Init() {
	var cfg struct {
		Mod struct {
			Origin string `yaml:"origin"`
		} `yaml:"api"`
	}

	app.LoadConfig(&cfg)

	log = app.GetLogger("api")

	initWS(cfg.Mod.Origin)

	api.HandleFunc("api/ws", apiWS)
}

var log zerolog.Logger

// Message - struct for data exchange in Web API
type Message struct {
	Type  string `json:"type"`
	Value any    `json:"value,omitempty"`
}

func (m *Message) String() (value string) {
	if s, ok := m.Value.(string); ok {
		return s
	}
	return
}

func (m *Message) Unmarshal(v any) error {
	b, err := json.Marshal(m.Value)
	if err != nil {
		return err
	}
	return json.Unmarshal(b, v)
}

type WSHandler func(tr *Transport, msg *Message) error

func HandleFunc(msgType string, handler WSHandler) {
	wsHandlers[msgType] = handler
}

var wsHandlers = make(map[string]WSHandler)

func initWS(origin string) {
	wsUp = &websocket.Upgrader{
		ReadBufferSize:  4096,       // for SDP
		WriteBufferSize: 512 * 1024, // 512K
	}

	switch origin {
	case "":
		// same origin + ignore port
		wsUp.CheckOrigin = func(r *http.Request) bool {
			origin := r.Header["Origin"]
			if len(origin) == 0 {
				return true
			}
			o, err := url.Parse(origin[0])
			if err != nil {
				return false
			}
			if o.Host == r.Host {
				return true
			}
			log.Trace().Msgf("[api] ws origin=%s, host=%s", o.Host, r.Host)
			// https://github.com/AlexxIT/go2rtc/issues/118
			if i := strings.IndexByte(o.Host, ':'); i > 0 {
				return o.Host[:i] == r.Host
			}
			return false
		}
	case "*":
		// any origin
		wsUp.CheckOrigin = func(r *http.Request) bool {
			return true
		}
	}
}

func apiWS(w http.ResponseWriter, r *http.Request) {
	ws, err := wsUp.Upgrade(w, r, nil)
	if err != nil {
		origin := r.Header.Get("Origin")
		log.Error().Err(err).Caller().Msgf("host=%s origin=%s", r.Host, origin)
		return
	}

	tr := &Transport{Request: r}
	tr.OnWrite(func(msg any) error {
		_ = ws.SetWriteDeadline(time.Now().Add(time.Second * 5))

		if data, ok := msg.([]byte); ok {
			return ws.WriteMessage(websocket.BinaryMessage, data)
		} else {
			return ws.WriteJSON(msg)
		}
	})

	for {
		msg := new(Message)
		if err = ws.ReadJSON(msg); err != nil {
			if !websocket.IsCloseError(err, websocket.CloseNoStatusReceived) {
				log.Trace().Err(err).Caller().Send()
			}
			_ = ws.Close()
			break
		}

		log.Trace().Str("type", msg.Type).Msg("[api] ws msg")

		if handler := wsHandlers[msg.Type]; handler != nil {
			go func() {
				if err = handler(tr, msg); err != nil {
					errMsg := creds.SecretString(err.Error())
					tr.Write(&Message{Type: "error", Value: msg.Type + ": " + errMsg})
				}
			}()
		}
	}

	tr.Close()
}

var wsUp *websocket.Upgrader

type Transport struct {
	Request *http.Request

	ctx map[any]any

	closed bool
	mx     sync.Mutex
	wrmx   sync.Mutex

	onChange func()
	onWrite  func(msg any) error
	onClose  []func()
}

func (t *Transport) OnWrite(f func(msg any) error) {
	t.mx.Lock()
	if t.onChange != nil {
		t.onChange()
	}
	t.onWrite = f
	t.mx.Unlock()
}

func (t *Transport) Write(msg any) {
	t.wrmx.Lock()
	_ = t.onWrite(msg)
	t.wrmx.Unlock()
}

func (t *Transport) Close() {
	t.mx.Lock()
	for _, f := range t.onClose {
		f()
	}
	t.closed = true
	t.mx.Unlock()
}

func (t *Transport) OnChange(f func()) {
	t.mx.Lock()
	t.onChange = f
	t.mx.Unlock()
}

func (t *Transport) OnClose(f func()) {
	t.mx.Lock()
	if t.closed {
		f()
	} else {
		t.onClose = append(t.onClose, f)
	}
	t.mx.Unlock()
}

// WithContext - run function with Context variable
func (t *Transport) WithContext(f func(ctx map[any]any)) {
	t.mx.Lock()
	if t.ctx == nil {
		t.ctx = map[any]any{}
	}
	f(t.ctx)
	t.mx.Unlock()
}

func (t *Transport) Writer() io.Writer {
	return &writer{t: t}
}

type writer struct {
	t *Transport
}

func (w *writer) Write(p []byte) (n int, err error) {
	w.t.wrmx.Lock()
	if err = w.t.onWrite(p); err == nil {
		n = len(p)
	}
	w.t.wrmx.Unlock()
	return
}


================================================
FILE: internal/app/README.md
================================================
# App

The application module is responsible for reading configuration files, running other modules and setting up [logs](#log).

The configuration can be edited through the application's WebUI with code highlighting, syntax and specification checking.

- By default, go2rtc will search for the `go2rtc.yaml` config file in the current working directory
- go2rtc supports multiple config files:
  - `go2rtc -c config1.yaml -c config2.yaml -c config3.yaml`
- go2rtc supports inline config in multiple formats from the command line:
  - **YAML**: `go2rtc -c '{log: {format: text}}'`
  - **JSON**: `go2rtc -c '{"log":{"format":"text"}}'`
  - **key=value**: `go2rtc -c log.format=text`
- Each subsequent config will overwrite the previous one (but only for defined params)

```
go2rtc -config "{log: {format: text}}" -config /config/go2rtc.yaml -config "{rtsp: {listen: ''}}" -config /usr/local/go2rtc/go2rtc.yaml
```

or a simpler version

```
go2rtc -c log.format=text -c /config/go2rtc.yaml -c rtsp.listen='' -c /usr/local/go2rtc/go2rtc.yaml
```

## Environment variables

There is support for loading external variables into the config. First, they will be loaded from [credential files](https://systemd.io/CREDENTIALS). If `CREDENTIALS_DIRECTORY` is not set, then the key will be loaded from an environment variable. If no environment variable is set, then the string will be left as-is.

```yaml
streams:
  camera1: rtsp://rtsp:${CAMERA_PASSWORD}@192.168.1.123/av_stream/ch0

rtsp:
  username: ${RTSP_USER:admin}   # "admin" if "RTSP_USER" not set
  password: ${RTSP_PASS:secret}  # "secret" if "RTSP_PASS" not set
```

## JSON Schema

Editors like [GoLand](https://www.jetbrains.com/go/) and [VS Code](https://code.visualstudio.com/) support autocomplete and syntax validation.

```yaml
# yaml-language-server: $schema=https://raw.githubusercontent.com/AlexxIT/go2rtc/master/www/schema.json
```

or from a running go2rtc:

```yaml
# yaml-language-server: $schema=http://localhost:1984/schema.json
```

## Defaults

- Default values may change in updates
- FFmpeg module has many presets, they are not listed here because they may also change in updates

```yaml
api:
  listen: ":1984"  # default public port for WebUI and HTTP API

ffmpeg:
  bin: "ffmpeg"    # default binary path for FFmpeg

log:
  level: "info"    # default log level
  output: "stdout"
  time: "UNIXMS"

rtsp:
  listen: ":8554"  # default public port for RTSP server
  default_query: "video&audio"

srtp:
  listen: ":8443"  # default public port for SRTP server (used for HomeKit)

webrtc:
  listen: ":8555"  # default public port for WebRTC server (TCP and UDP)
  ice_servers:
    - urls: [ "stun:stun.cloudflare.com:3478", "stun:stun.l.google.com:19302" ]
```

## Log

You can set different log levels for different modules.

```yaml
log:
  format: ""        # empty (default, autodetect color support), color, json, text 
  level: "info"     # disabled, trace, debug, info (default), warn, error
  output: "stdout"  # empty (only to memory), stderr, stdout (default)
  time: "UNIXMS"    # empty (disable timestamp), UNIXMS (default), UNIXMICRO, UNIXNANO
  
  api: trace   # module name: log level
```

Modules: `api`, `streams`, `rtsp`, `webrtc`, `mp4`, `hls`, `mjpeg`, `hass`, `homekit`, `onvif`, `rtmp`, `webtorrent`, `wyoming`, `echo`, `exec`, `expr`, `ffmpeg`, `wyze`, `xiaomi`.


================================================
FILE: internal/app/app.go
================================================
package app

import (
	"flag"
	"fmt"
	"os"
	"os/exec"
	"runtime"
	"runtime/debug"
)

var (
	Version    string
	Modules    []string
	UserAgent  string
	ConfigPath string
	Info       = make(map[string]any)
)

const usage = `Usage of go2rtc:

  -c, --config   Path to config file or config string as YAML or JSON, support multiple
  -d, --daemon   Run in background
  -v, --version  Print version and exit
`

func Init() {
	var config flagConfig
	var daemon bool
	var version bool

	flag.Var(&config, "config", "")
	flag.Var(&config, "c", "")
	flag.BoolVar(&daemon, "daemon", false, "")
	flag.BoolVar(&daemon, "d", false, "")
	flag.BoolVar(&version, "version", false, "")
	flag.BoolVar(&version, "v", false, "")

	flag.Usage = func() { fmt.Print(usage) }
	flag.Parse()

	revision, vcsTime := readRevisionTime()

	if version {
		fmt.Printf("go2rtc version %s (%s) %s/%s\n", Version, revision, runtime.GOOS, runtime.GOARCH)
		os.Exit(0)
	}

	if daemon && os.Getppid() != 1 {
		if runtime.GOOS == "windows" {
			fmt.Println("Daemon mode is not supported on Windows")
			os.Exit(1)
		}

		// Re-run the program in background and exit
		cmd := exec.Command(os.Args[0], os.Args[1:]...)
		if err := cmd.Start(); err != nil {
			fmt.Println("Failed to start daemon:", err)
			os.Exit(1)
		}
		fmt.Println("Running in daemon mode with PID:", cmd.Process.Pid)
		os.Exit(0)
	}

	UserAgent = "go2rtc/" + Version

	Info["version"] = Version
	Info["revision"] = revision

	initConfig(config)
	initLogger()

	platform := fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH)
	Logger.Info().Str("version", Version).Str("platform", platform).Str("revision", revision).Msg("go2rtc")
	Logger.Debug().Str("version", runtime.Version()).Str("vcs.time", vcsTime).Msg("build")

	if ConfigPath != "" {
		Logger.Info().Str("path", ConfigPath).Msg("config")
	}

	var cfg struct {
		Mod struct {
			Modules []string `yaml:"modules"`
		} `yaml:"app"`
	}

	LoadConfig(&cfg)

	Modules = cfg.Mod.Modules
}

func readRevisionTime() (revision, vcsTime string) {
	if info, ok := debug.ReadBuildInfo(); ok {
		for _, setting := range info.Settings {
			switch setting.Key {
			case "vcs.revision":
				if len(setting.Value) > 7 {
					revision = setting.Value[:7]
				} else {
					revision = setting.Value
				}
			case "vcs.time":
				vcsTime = setting.Value
			case "vcs.modified":
				if setting.Value == "true" {
					revision += ".dirty"
				}
			}
		}

		// Check version from -buildvcs info
		// Format for tagged version : v1.9.13
		// Format for modified code:   v1.9.14-0.20251215184105-753d6617ab58+dirty
		if info.Main.Version != "v"+Version {
			// Format: 1.9.13+dev.753d661[.dirty]
			// Compatible with "awesomeversion" and "packaging.version" from python.
			// Version will be larger than the previous release, but smaller than the next release.
			Version += "+dev." + revision
		}
	}
	return
}


================================================
FILE: internal/app/config.go
================================================
package app

import (
	"errors"
	"os"
	"path/filepath"
	"strings"
	"sync"

	"github.com/AlexxIT/go2rtc/pkg/creds"
	"github.com/AlexxIT/go2rtc/pkg/yaml"
)

func LoadConfig(v any) {
	for _, data := range configs {
		if err := yaml.Unmarshal(data, v); err != nil {
			Logger.Warn().Err(err).Send()
		}
	}
}

var configMu sync.Mutex

func PatchConfig(path []string, value any) error {
	if ConfigPath == "" {
		return errors.New("config file disabled")
	}

	configMu.Lock()
	defer configMu.Unlock()

	// empty config is OK
	b, _ := os.ReadFile(ConfigPath)

	b, err := yaml.Patch(b, path, value)
	if err != nil {
		return err
	}

	return os.WriteFile(ConfigPath, b, 0644)
}

type flagConfig []string

func (c *flagConfig) String() string {
	return strings.Join(*c, " ")
}

func (c *flagConfig) Set(value string) error {
	*c = append(*c, value)
	return nil
}

var configs [][]byte

func initConfig(confs flagConfig) {
	if confs == nil {
		confs = []string{"go2rtc.yaml"}
	}

	for _, conf := range confs {
		if len(conf) == 0 {
			continue
		}
		if conf[0] == '{' {
			// config as raw YAML or JSON
			configs = append(configs, []byte(conf))
		} else if data := parseConfString(conf); data != nil {
			configs = append(configs, data)
		} else {
			// config as file
			if ConfigPath == "" {
				ConfigPath = conf
				initStorage()
			}

			if data, _ = os.ReadFile(conf); data == nil {
				continue
			}

			loadEnv(data)
			data = creds.ReplaceVars(data)
			configs = append(configs, data)
		}
	}

	if ConfigPath != "" {
		if !filepath.IsAbs(ConfigPath) {
			if cwd, err := os.Getwd(); err == nil {
				ConfigPath = filepath.Join(cwd, ConfigPath)
			}
		}
		Info["config_path"] = ConfigPath
	}
}

func parseConfString(s string) []byte {
	i := strings.IndexByte(s, '=')
	if i < 0 {
		return nil
	}

	items := strings.Split(s[:i], ".")
	if len(items) < 2 {
		return nil
	}

	// `log.level=trace` => `{log: {level: trace}}`
	var pre string
	var suf = s[i+1:]
	for _, item := range items {
		pre += "{" + item + ": "
		suf += "}"
	}

	return []byte(pre + suf)
}


================================================
FILE: internal/app/log.go
================================================
package app

import (
	"io"
	"os"
	"strings"
	"sync"

	"github.com/AlexxIT/go2rtc/pkg/creds"
	"github.com/mattn/go-isatty"
	"github.com/rs/zerolog"
)

var MemoryLog = newBuffer()

func GetLogger(module string) zerolog.Logger {
	Logger.Trace().Str("module", module).Msgf("[log] init")

	if s, ok := modules[module]; ok {
		lvl, err := zerolog.ParseLevel(s)
		if err == nil {
			return Logger.Level(lvl)
		}
		Logger.Warn().Err(err).Caller().Send()
	}

	return Logger
}

// initLogger support:
// - output: empty (only to memory), stderr, stdout
// - format: empty (autodetect color support), color, json, text
// - time:   empty (disable timestamp), UNIXMS, UNIXMICRO, UNIXNANO
// - level:  disabled, trace, debug, info, warn, error...
func initLogger() {
	var cfg struct {
		Mod map[string]string `yaml:"log"`
	}

	cfg.Mod = modules // defaults

	LoadConfig(&cfg)

	var writer io.Writer

	switch output, path, _ := strings.Cut(modules["output"], ":"); output {
	case "stderr":
		writer = os.Stderr
	case "stdout":
		writer = os.Stdout
	case "file":
		if path == "" {
			path = "go2rtc.log"
		}
		// if fail - only MemoryLog will be available
		writer, _ = os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	}

	timeFormat := modules["time"]

	if writer != nil {
		if format := modules["format"]; format != "json" {
			console := &zerolog.ConsoleWriter{Out: writer}

			switch format {
			case "text":
				console.NoColor = true
			case "color":
				console.NoColor = false // useless, but anyway
			default:
				// autodetection if output support color
				// go-isatty - dependency for go-colorable - dependency for ConsoleWriter
				console.NoColor = !isatty.IsTerminal(writer.(*os.File).Fd())
			}

			if timeFormat != "" {
				console.TimeFormat = "15:04:05.000"
			} else {
				console.PartsOrder = []string{
					zerolog.LevelFieldName,
					zerolog.CallerFieldName,
					zerolog.MessageFieldName,
				}
			}

			writer = console
		}

		writer = zerolog.MultiLevelWriter(writer, MemoryLog)
	} else {
		writer = MemoryLog
	}

	writer = creds.SecretWriter(writer)

	lvl, _ := zerolog.ParseLevel(modules["level"])
	Logger = zerolog.New(writer).Level(lvl)

	if timeFormat != "" {
		zerolog.TimeFieldFormat = timeFormat
		Logger = Logger.With().Timestamp().Logger()
	}
}

var Logger zerolog.Logger

// modules log levels
var modules = map[string]string{
	"format": "", // useless, but anyway
	"level":  "info",
	"output": "stdout", // TODO: change to stderr someday
	"time":   zerolog.TimeFormatUnixMs,
}

const (
	chunkCount = 16
	chunkSize  = 1 << 16
)

type circularBuffer struct {
	chunks [][]byte
	r, w   int
	mu     sync.Mutex
}

func newBuffer() *circularBuffer {
	b := &circularBuffer{chunks: make([][]byte, 0, chunkCount)}
	// create first chunk
	b.chunks = append(b.chunks, make([]byte, 0, chunkSize))
	return b
}

func (b *circularBuffer) Write(p []byte) (n int, err error) {
	n = len(p)

	b.mu.Lock()
	// check if chunk has size
	if len(b.chunks[b.w])+n > chunkSize {
		// increase write chunk index
		if b.w++; b.w == chunkCount {
			b.w = 0
		}
		// check overflow
		if b.r == b.w {
			// increase read chunk index
			if b.r++; b.r == chunkCount {
				b.r = 0
			}
		}
		// check if current chunk exists
		if b.w == len(b.chunks) {
			// allocate new chunk
			b.chunks = append(b.chunks, make([]byte, 0, chunkSize))
		} else {
			// reset len of current chunk
			b.chunks[b.w] = b.chunks[b.w][:0]
		}
	}

	b.chunks[b.w] = append(b.chunks[b.w], p...)
	b.mu.Unlock()
	return
}

func (b *circularBuffer) WriteTo(w io.Writer) (n int64, err error) {
	buf := make([]byte, 0, chunkCount*chunkSize)

	// use temp buffer inside mutex because w.Write can take some time
	b.mu.Lock()
	for i := b.r; ; {
		buf = append(buf, b.chunks[i]...)
		if i == b.w {
			break
		}
		if i++; i == chunkCount {
			i = 0
		}
	}
	b.mu.Unlock()

	nn, err := w.Write(buf)
	return int64(nn), err
}

func (b *circularBuffer) Reset() {
	b.mu.Lock()
	b.chunks[0] = b.chunks[0][:0]
	b.r = 0
	b.w = 0
	b.mu.Unlock()
}


================================================
FILE: internal/app/storage.go
================================================
package app

import (
	"sync"

	"github.com/AlexxIT/go2rtc/pkg/creds"
	"github.com/AlexxIT/go2rtc/pkg/yaml"
)

func initStorage() {
	storage = &envStorage{data: make(map[string]string)}
	creds.SetStorage(storage)
}

func loadEnv(data []byte) {
	var cfg struct {
		Env map[string]string `yaml:"env"`
	}

	if err := yaml.Unmarshal(data, &cfg); err != nil {
		return
	}

	storage.mu.Lock()
	for name, value := range cfg.Env {
		storage.data[name] = value
		creds.AddSecret(value)
	}
	storage.mu.Unlock()
}

var storage *envStorage

type envStorage struct {
	data map[string]string
	mu   sync.Mutex
}

func (s *envStorage) SetValue(name, value string) error {
	if err := PatchConfig([]string{"env", name}, value); err != nil {
		return err
	}

	s.mu.Lock()
	s.data[name] = value
	s.mu.Unlock()

	return nil
}

func (s *envStorage) GetValue(name string) (value string, ok bool) {
	s.mu.Lock()
	value, ok = s.data[name]
	s.mu.Unlock()
	return
}


================================================
FILE: internal/bubble/README.md
================================================
# Bubble

[`new in v1.6.1`](https://github.com/AlexxIT/go2rtc/releases/tag/v1.6.1)

Private format in some cameras from [dvr163.com](http://help.dvr163.com/) and [eseecloud.com](http://www.eseecloud.com/).

## Configuration

- you can skip `username`, `password`, `port`, `ch` and `stream` if they are default
- set up separate streams for different channels and streams

```yaml
streams:
  camera1: bubble://username:password@192.168.1.123:34567/bubble/live?ch=0&stream=0
```


================================================
FILE: internal/bubble/bubble.go
================================================
package bubble

import (
	"github.com/AlexxIT/go2rtc/internal/streams"
	"github.com/AlexxIT/go2rtc/pkg/bubble"
	"github.com/AlexxIT/go2rtc/pkg/core"
)

func Init() {
	streams.HandleFunc("bubble", func(source string) (core.Producer, error) {
		return bubble.Dial(source)
	})
}


================================================
FILE: internal/debug/README.md
================================================
# Debug

This module provides `GET /api/stack`, with which you can find hanging goroutines


================================================
FILE: internal/debug/debug.go
================================================
package debug

import (
	"github.com/AlexxIT/go2rtc/internal/api"
)

func Init() {
	api.HandleFunc("api/stack", stackHandler)
}


================================================
FILE: internal/debug/stack.go
================================================
package debug

import (
	"bytes"
	"fmt"
	"net/http"
	"runtime"

	"github.com/AlexxIT/go2rtc/internal/api"
)

var stackSkip = [][]byte{
	// main.go
	[]byte("main.main()"),
	[]byte("created by os/signal.Notify"),

	// api/stack.go
	[]byte("github.com/AlexxIT/go2rtc/internal/api.stackHandler"),

	// api/api.go
	[]byte("created by github.com/AlexxIT/go2rtc/internal/api.Init"),
	[]byte("created by net/http.(*connReader).startBackgroundRead"),
	[]byte("created by net/http.(*Server).Serve"), // TODO: why two?

	[]byte("created by github.com/AlexxIT/go2rtc/internal/rtsp.Init"),
	[]byte("created by github.com/AlexxIT/go2rtc/internal/srtp.Init"),

	// homekit
	[]byte("created by github.com/AlexxIT/go2rtc/internal/homekit.Init"),

	// webrtc/api.go
	[]byte("created by github.com/pion/ice/v4.NewTCPMuxDefault"),
	[]byte("created by github.com/pion/ice/v4.NewUDPMuxDefault"),
}

func stackHandler(w http.ResponseWriter, r *http.Request) {
	sep := []byte("\n\n")
	buf := make([]byte, 65535)
	i := 0
	n := runtime.Stack(buf, true)
	skipped := 0
	for _, item := range bytes.Split(buf[:n], sep) {
		for _, skip := range stackSkip {
			if bytes.Contains(item, skip) {
				item = nil
				skipped++
				break
			}
		}
		if item != nil {
			i += copy(buf[i:], item)
			i += copy(buf[i:], sep)
		}
	}
	i += copy(buf[i:], fmt.Sprintf(
		"Total: %d, Skipped: %d", runtime.NumGoroutine(), skipped),
	)

	api.Response(w, buf[:i], api.MimeText)
}


================================================
FILE: internal/doorbird/README.md
================================================
# Doorbird

[`new in v1.9.8`](https://github.com/AlexxIT/go2rtc/releases/tag/v1.9.8)

This source type supports [Doorbird](https://www.doorbird.com/) devices including MJPEG stream, audio stream as well as two-way audio.

It is recommended to create a separate user within your doorbird setup for go2rtc. Minimum permissions for the user are:

- Watch always
- API operator

## Configuration

```yaml
streams:
  doorbird1:
    - rtsp://admin:password@192.168.1.123:8557/mpeg/720p/media.amp  # RTSP stream
    - doorbird://admin:password@192.168.1.123?media=video           # MJPEG stream
    - doorbird://admin:password@192.168.1.123?media=audio           # audio stream
    - doorbird://admin:password@192.168.1.123                       # two-way audio
```


================================================
FILE: internal/doorbird/doorbird.go
================================================
package doorbird

import (
	"net/url"

	"github.com/AlexxIT/go2rtc/internal/streams"
	"github.com/AlexxIT/go2rtc/pkg/core"
	"github.com/AlexxIT/go2rtc/pkg/doorbird"
)

func Init() {
	streams.RedirectFunc("doorbird", func(rawURL string) (string, error) {
		u, err := url.Parse(rawURL)
		if err != nil {
			return "", err
		}

		// https://www.doorbird.com/downloads/api_lan.pdf
		switch u.Query().Get("media") {
		case "video":
			u.Path = "/bha-api/video.cgi"
		case "audio":
			u.Path = "/bha-api/audio-receive.cgi"
		default:
			return "", nil
		}

		u.Scheme = "http"

		return u.String(), nil
	})

	streams.HandleFunc("doorbird", func(source string) (core.Producer, error) {
		return doorbird.Dial(source)
	})
}


================================================
FILE: internal/dvrip/README.md
================================================
# DVR-IP

[`new in v1.2.0`](https://github.com/AlexxIT/go2rtc/releases/tag/v1.2.0)

Private format from DVR-IP NVR, NetSurveillance, Sofia protocol (NETsurveillance ActiveX plugin XMeye SDK).

## Configuration

- you can skip `username`, `password`, `port`, `channel` and `subtype` if they are default
- set up separate streams for different channels
- use `subtype=0` for Main stream, and `subtype=1` for Extra1 stream
- only the TCP protocol is supported

```yaml
streams:
  only_stream: dvrip://username:password@192.168.1.123:34567?channel=0&subtype=0
  only_tts: dvrip://username:password@192.168.1.123:34567?backchannel=1
  two_way_audio:
    - dvrip://username:password@192.168.1.123:34567?channel=0&subtype=0
    - dvrip://username:password@192.168.1.123:34567?backchannel=1
```


================================================
FILE: internal/dvrip/dvrip.go
================================================
package dvrip

import (
	"encoding/hex"
	"encoding/json"
	"fmt"
	"net"
	"net/http"
	"time"

	"github.com/AlexxIT/go2rtc/internal/api"
	"github.com/AlexxIT/go2rtc/internal/streams"
	"github.com/AlexxIT/go2rtc/pkg/dvrip"
)

func Init() {
	streams.HandleFunc("dvrip", dvrip.Dial)

	// DVRIP client autodiscovery
	api.HandleFunc("api/dvrip", apiDvrip)
}

const Port = 34569 // UDP port number for dvrip discovery

func apiDvrip(w http.ResponseWriter, r *http.Request) {
	items, err := discover()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	api.ResponseSources(w, items)
}

func discover() ([]*api.Source, error) {
	addr := &net.UDPAddr{
		Port: Port,
		IP:   net.IP{239, 255, 255, 250},
	}

	conn, err := net.ListenUDP("udp4", addr)
	if err != nil {
		return nil, err
	}

	defer conn.Close()

	go sendBroadcasts(conn)

	var items []*api.Source

	for _, info := range getResponses(conn) {
		if info.HostIP == "" || info.HostName == "" {
			continue
		}

		host, err := hexToDecimalBytes(info.HostIP)
		if err != nil {
			continue
		}

		items = append(items, &api.Source{
			Name: info.HostName,
			URL:  "dvrip://user:pass@" + host + "?channel=0&subtype=0",
		})
	}

	return items, nil
}

func sendBroadcasts(conn *net.UDPConn) {
	// broadcasting the same multiple times because the devies some times don't answer
	data, err := hex.DecodeString("ff00000000000000000000000000fa0500000000")
	if err != nil {
		return
	}

	addr := &net.UDPAddr{
		Port: Port,
		IP:   net.IP{255, 255, 255, 255},
	}

	for i := 0; i < 3; i++ {
		time.Sleep(100 * time.Millisecond)
		_, _ = conn.WriteToUDP(data, addr)
	}
}

type Message struct {
	NetCommon NetCommon `json:"NetWork.NetCommon"`
	Ret       int       `json:"Ret"`
	SessionID string    `json:"SessionID"`
}

type NetCommon struct {
	BuildDate       string `json:"BuildDate"`
	ChannelNum      int    `json:"ChannelNum"`
	DeviceType      int    `json:"DeviceType"`
	GateWay         string `json:"GateWay"`
	HostIP          string `json:"HostIP"`
	HostName        string `json:"HostName"`
	HttpPort        int    `json:"HttpPort"`
	MAC             string `json:"MAC"`
	MonMode         string `json:"MonMode"`
	NetConnectState int    `json:"NetConnectState"`
	OtherFunction   string `json:"OtherFunction"`
	SN              string `json:"SN"`
	SSLPort         int    `json:"SSLPort"`
	Submask         string `json:"Submask"`
	TCPMaxConn      int    `json:"TCPMaxConn"`
	TCPPort         int    `json:"TCPPort"`
	UDPPort         int    `json:"UDPPort"`
	UseHSDownLoad   bool   `json:"UseHSDownLoad"`
	Version         string `json:"Version"`
}

func getResponses(conn *net.UDPConn) (infos []*NetCommon) {
	if err := conn.SetReadDeadline(time.Now().Add(time.Second * 2)); err != nil {
		return
	}

	var ips []net.IP // processed IPs

	b := make([]byte, 4096)
loop:
	for {
		n, addr, err := conn.ReadFromUDP(b)
		if err != nil {
			break
		}

		for _, ip := range ips {
			if ip.Equal(addr.IP) {
				continue loop
			}
		}

		if n <= 20+1 {
			continue
		}

		var msg Message

		if err = json.Unmarshal(b[20:n-1], &msg); err != nil {
			continue
		}

		infos = append(infos, &msg.NetCommon)
		ips = append(ips, addr.IP)
	}

	return
}

func hexToDecimalBytes(hexIP string) (string, error) {
	b, err := hex.DecodeString(hexIP[2:]) // remove the '0x' prefix
	if err != nil {
		return "", err
	}
	return fmt.Sprintf("%d.%d.%d.%d", b[3], b[2], b[1], b[0]), nil
}


================================================
FILE: internal/echo/README.md
================================================
# Echo

Some sources may have a dynamic link. And you will need to get it using a Bash or Python script. Your script should echo a link to the source. RTSP, FFmpeg or any of the supported sources.

**Docker** and **Home Assistant add-on** users have preinstalled `python3`, `curl`, `jq`.

## Configuration

```yaml
streams:
  apple_hls: echo:python3 hls.py https://developer.apple.com/streaming/examples/basic-stream-osx-ios5.html
```

## Install python libraries

**Docker** and **Hass Add-on** users have preinstalled `python3` without any additional libraries, like [requests](https://requests.readthedocs.io/) or others. If you need some additional libraries - you need to install them to folder with your script:

1. Install [SSH & Web Terminal](https://github.com/hassio-addons/addon-ssh)
2. Goto Add-on Web UI
3. Install library: `pip install requests -t /config/echo`
4. Add your script to `/config/echo/myscript.py`
5. Use your script as source `echo:python3 /config/echo/myscript.py`

## Example: Apple HLS

```yaml
streams:
  apple_hls: echo:python3 hls.py https://developer.apple.com/streaming/examples/basic-stream-osx-ios5.html
```

**hls.py**

```python
import re
import sys
from urllib.parse import urljoin
from urllib.request import urlopen

html = urlopen(sys.argv[1]).read().decode("utf-8")
url = re.search(r"https.+?m3u8", html)[0]

html = urlopen(url).read().decode("utf-8")
m = re.search(r"^[a-z0-1/_]+\.m3u8$", html, flags=re.MULTILINE)
url = urljoin(url, m[0])

# ffmpeg:https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_16x9/gear1/prog_index.m3u8#video=copy
print("ffmpeg:" + url + "#video=copy")
```


================================================
FILE: internal/echo/echo.go
================================================
package echo

import (
	"bytes"
	"errors"
	"os/exec"
	"slices"

	"github.com/AlexxIT/go2rtc/internal/app"
	"github.com/AlexxIT/go2rtc/internal/streams"
	"github.com/AlexxIT/go2rtc/pkg/shell"
)

func Init() {
	var cfg struct {
		Mod struct {
			AllowPaths []string `yaml:"allow_paths"`
		} `yaml:"echo"`
	}

	app.LoadConfig(&cfg)

	allowPaths := cfg.Mod.AllowPaths

	log := app.GetLogger("echo")

	streams.RedirectFunc("echo", func(url string) (string, error) {
		args := shell.QuoteSplit(url[5:])

		if allowPaths != nil && !slices.Contains(allowPaths, args[0]) {
			return "", errors.New("echo: bin not in allow_paths: " + args[0])
		}

		b, err := exec.Command(args[0], args[1:]...).Output()
		if err != nil {
			return "", err
		}

		b = bytes.TrimSpace(b)

		log.Debug().Str("url", url).Msgf("[echo] %s", b)

		return string(b), nil
	})
	streams.MarkInsecure("echo")
}


================================================
FILE: internal/eseecloud/README.md
================================================
# EseeCloud

[`new in v1.9.10`](https://github.com/AlexxIT/go2rtc/releases/tag/v1.9.10)

This source is for cameras with a link like this `http://admin:@192.168.1.123:80/livestream/12`. Related [issue](https://github.com/AlexxIT/go2rtc/issues/1690).

## Configuration

```yaml
streams:
  camera1: eseecloud://user:pass@192.168.1.123:80/livestream/12
```


================================================
FILE: internal/eseecloud/eseecloud.go
================================================
package eseecloud

import (
	"github.com/AlexxIT/go2rtc/internal/streams"
	"github.com/AlexxIT/go2rtc/pkg/eseecloud"
)

func Init() {
	streams.HandleFunc("eseecloud", eseecloud.Dial)
}


================================================
FILE: internal/exec/README.md
================================================
# Exec

Exec source can run any external application and expect data from it. Two transports are supported - **pipe** ([`new in v1.5.0`](https://github.com/AlexxIT/go2rtc/releases/tag/v1.5.0)) and **RTSP**.

If you want to use **RTSP** transport, the command must contain the `{output}` argument in any place. On launch, it will be replaced by the local address of the RTSP server.

**pipe** reads data from app stdout in different formats: **MJPEG**, **H.264/H.265 bitstream**, **MPEG-TS**. Also pipe can write data to app stdin in two formats: **PCMA** and **PCM/48000**.

The source can be used with:

- [FFmpeg](https://ffmpeg.org/) - go2rtc ffmpeg source is just a shortcut to exec source
- [FFplay](https://ffmpeg.org/ffplay.html) - play audio on your server
- [GStreamer](https://gstreamer.freedesktop.org/)
- [Raspberry Pi Cameras](https://www.raspberrypi.com/documentation/computers/camera_software.html)
- any of your own software

## Configuration

Pipe commands support parameters (format: `exec:{command}#{param1}#{param2}`):

- `killsignal` - signal which will be sent to stop the process (numeric form)
- `killtimeout` - time in seconds for forced termination with sigkill
- `backchannel` - enable backchannel for two-way audio
- `starttimeout` - time in seconds for waiting first byte from RTSP

```yaml
streams:
  stream: exec:ffmpeg -re -i /media/BigBuckBunny.mp4 -c copy -rtsp_transport tcp -f rtsp {output}
  picam_h264: exec:libcamera-vid -t 0 --inline -o -
  picam_mjpeg: exec:libcamera-vid -t 0 --codec mjpeg -o -
  pi5cam_h264: exec:libcamera-vid -t 0 --libav-format h264 -o -
  canon: exec:gphoto2 --capture-movie --stdout#killsignal=2#killtimeout=5
  play_pcma: exec:ffplay -fflags nobuffer -f alaw -ar 8000 -i -#backchannel=1
  play_pcm48k: exec:ffplay -fflags nobuffer -f s16be -ar 48000 -i -#backchannel=1
```

## Backchannel

- You can check audio card names in the **Go2rtc > WebUI > Add**
- You can specify multiple backchannel lines with different codecs

```yaml
sources:
  two_way_audio_win:
    - exec:ffmpeg -hide_banner -f dshow -i "audio=Microphone (High Definition Audio Device)" -c pcm_s16le -ar 16000 -ac 1 -f wav -
    - exec:ffplay -nodisp -probesize 32 -f s16le -ar 16000 -#backchannel=1#audio=s16le/16000
    - exec:ffplay -nodisp -probesize 32 -f alaw -ar 8000 -#backchannel=1#audio=alaw/8000
```


================================================
FILE: internal/exec/exec.go
================================================
package exec

import (
	"bufio"
	"crypto/md5"
	"encoding/hex"
	"errors"
	"fmt"
	"io"
	"net/url"
	"os"
	"slices"
	"strings"
	"sync"
	"syscall"
	"time"

	"github.com/AlexxIT/go2rtc/internal/app"
	"github.com/AlexxIT/go2rtc/internal/rtsp"
	"github.com/AlexxIT/go2rtc/internal/streams"
	"github.com/AlexxIT/go2rtc/pkg/core"
	"github.com/AlexxIT/go2rtc/pkg/magic"
	"github.com/AlexxIT/go2rtc/pkg/pcm"
	pkg "github.com/AlexxIT/go2rtc/pkg/rtsp"
	"github.com/AlexxIT/go2rtc/pkg/shell"
	"github.com/rs/zerolog"
)

func Init() {
	var cfg struct {
		Mod struct {
			AllowPaths []string `yaml:"allow_paths"`
		} `yaml:"exec"`
	}

	app.LoadConfig(&cfg)

	allowPaths = cfg.Mod.AllowPaths

	rtsp.HandleFunc(func(conn *pkg.Conn) bool {
		waitersMu.Lock()
		waiter := waiters[conn.URL.Path]
		waitersMu.Unlock()

		if waiter == nil {
			return false
		}

		// unblocking write to channel
		select {
		case waiter <- conn:
			return true
		default:
			return false
		}
	})

	streams.HandleFunc("exec", execHandle)
	streams.MarkInsecure("exec")

	log = app.GetLogger("exec")
}

var allowPaths []string

func execHandle(rawURL string) (prod core.Producer, err error) {
	rawURL, rawQuery, _ := strings.Cut(rawURL, "#")
	query := streams.ParseQuery(rawQuery)

	var path string

	// RTSP flow should have `{output}` inside URL
	// pipe flow may have `#{params}` inside URL
	if i := strings.Index(rawURL, "{output}"); i > 0 {
		if rtsp.Port == "" {
			return nil, errors.New("exec: rtsp module disabled")
		}

		sum := md5.Sum([]byte(rawURL))
		path = "/" + hex.EncodeToString(sum[:])
		rawURL = rawURL[:i] + "rtsp://127.0.0.1:" + rtsp.Port + path + rawURL[i+8:]
	}

	cmd := shell.NewCommand(rawURL[5:]) // remove `exec:`
	cmd.Stderr = &logWriter{
		buf:   make([]byte, 512),
		debug: log.Debug().Enabled(),
	}

	if allowPaths != nil && !slices.Contains(allowPaths, cmd.Args[0]) {
		_ = cmd.Close()
		return nil, errors.New("exec: bin not in allow_paths: " + cmd.Args[0])
	}

	if s := query.Get("killsignal"); s != "" {
		sig := syscall.Signal(core.Atoi(s))
		cmd.Cancel = func() error {
			log.Debug().Msgf("[exec] kill with signal=%d", sig)
			return cmd.Process.Signal(sig)
		}
	}

	if s := query.Get("killtimeout"); s != "" {
		cmd.WaitDelay = time.Duration(core.Atoi(s)) * time.Second
	}

	if query.Get("backchannel") == "1" {
		return pcm.NewBackchannel(cmd, query.Get("audio"))
	}

	var timeout time.Duration
	if s := query.Get("starttimeout"); s != "" {
		timeout = time.Duration(core.Atoi(s)) * time.Second
	} else {
		timeout = 30 * time.Second
	}

	if path == "" {
		prod, err = handlePipe(rawURL, cmd)
	} else {
		prod, err = handleRTSP(rawURL, cmd, path, timeout)
	}

	if err != nil {
		_ = cmd.Close()
	}

	return
}

func handlePipe(source string, cmd *shell.Command) (core.Producer, error) {
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return nil, err
	}

	rd := struct {
		io.Reader
		io.Closer
	}{
		// add buffer for pipe reader to reduce syscall
		bufio.NewReaderSize(stdout, core.BufferSize),
		// stop cmd on close pipe call
		cmd,
	}

	log.Debug().Strs("args", cmd.Args).Msg("[exec] run pipe")

	ts := time.Now()

	if err = cmd.Start(); err != nil {
		return nil, err
	}

	prod, err := magic.Open(rd)
	if err != nil {
		return nil, fmt.Errorf("exec/pipe: %w\n%s", err, cmd.Stderr)
	}

	if info, ok := prod.(core.Info); ok {
		info.SetProtocol("pipe")
		setRemoteInfo(info, source, cmd.Args)
	}

	log.Debug().Stringer("launch", time.Since(ts)).Msg("[exec] run pipe")

	return prod, nil
}

func handleRTSP(source string, cmd *shell.Command, path string, timeout time.Duration) (core.Producer, error) {
	if log.Trace().Enabled() {
		cmd.Stdout = os.Stdout
	}

	waiter := make(chan *pkg.Conn, 1)

	waitersMu.Lock()
	waiters[path] = waiter
	waitersMu.Unlock()

	defer func() {
		waitersMu.Lock()
		delete(waiters, path)
		waitersMu.Unlock()
	}()

	log.Debug().Strs("args", cmd.Args).Msg("[exec] run rtsp")

	ts := time.Now()

	if err := cmd.Start(); err != nil {
		log.Error().Err(err).Str("source", source).Msg("[exec]")
		return nil, err
	}

	timer := time.NewTimer(timeout)
	defer timer.Stop()

	select {
	case <-timer.C:
		// haven't received data from app in timeout
		log.Error().Str("source", source).Msg("[exec] timeout")
		return nil, errors.New("exec: timeout")
	case <-cmd.Done():
		// app fail before we receive any data
		return nil, fmt.Errorf("exec/rtsp\n%s", cmd.Stderr)
	case prod := <-waiter:
		// app started successfully
		log.Debug().Stringer("launch", time.Since(ts)).Msg("[exec] run rtsp")
		setRemoteInfo(prod, source, cmd.Args)
		prod.OnClose = cmd.Close
		return prod, nil
	}
}

// internal

var (
	log       zerolog.Logger
	waiters   = make(map[string]chan *pkg.Conn)
	waitersMu sync.Mutex
)

type logWriter struct {
	buf   []byte
	debug bool
	n     int
}

func (l *logWriter) String() string {
	if l.n == len(l.buf) {
		return string(l.buf) + "..."
	}
	return string(l.buf[:l.n])
}

func (l *logWriter) Write(p []byte) (n int, err error) {
	if l.n < cap(l.buf) {
		l.n += copy(l.buf[l.n:], p)
	}
	n = len(p)
	if l.debug {
		if p = trimSpace(p); p != nil {
			log.Debug().Msgf("[exec] %s", p)
		}
	}
	return
}

func trimSpace(b []byte) []byte {
	start := 0
	stop := len(b)
	for ; start < stop; start++ {
		if b[start] >= ' ' {
			break // trim all ASCII before 0x20
		}
	}
	for ; ; stop-- {
		if stop == start {
			return nil // skip empty output
		}
		if b[stop-1] > ' ' {
			break // trim all ASCII before 0x21
		}
	}
	return b[start:stop]
}

func setRemoteInfo(info core.Info, source string, args []string) {
	info.SetSource(source)

	if i := core.Index(args, "-i"); i > 0 && i < len(args)-1 {
		rawURL := args[i+1]
		if u, err := url.Parse(rawURL); err == nil && u.Host != "" {
			info.SetRemoteAddr(u.Host)
			info.SetURL(rawURL)
		}
	}
}


================================================
FILE: internal/expr/README.md
================================================
# Expr

[`new in v1.8.2`](https://github.com/AlexxIT/go2rtc/releases/tag/v1.8.2)

[Expr](https://github.com/antonmedv/expr) - expression language and expression evaluation for Go.

- [language definition](https://expr.medv.io/docs/Language-Definition) - takes best from JS, Python, Jinja2 syntax
- your expression should return a link of any supported source
- expression supports multiple operation, but:
  - all operations must be separated by a semicolon
  - all operations, except the last one, must declare a new variable (`let s = "abc";`)
  - the last operation should return a string
- go2rtc supports additional functions:
  - `fetch` - JS-like HTTP requests
  - `match` - JS-like RegExp queries

## Fetch examples

Multiple fetch requests are executed within a single session. They share the same cookie.

**HTTP GET**

```js
var r = fetch('https://example.org/products.json');
```

**HTTP POST JSON**

```js
var r = fetch('https://example.org/post', {
    method: 'POST',
    // Content-Type: application/json will be set automatically
    json: {username: 'example'}
});
```

**HTTP POST Form**

```js
var r = fetch('https://example.org/post', {
    method: 'POST',
    // Content-Type: application/x-www-form-urlencoded will be set automatically
    data: {username: 'example', password: 'password'}
});
```

## Script examples

**Two way audio for Dahua VTO**

```yaml
streams:
  dahua_vto: |
    expr:
    let host = 'admin:password@192.168.1.123';

    var r = fetch('http://' + host + '/cgi-bin/configManager.cgi?action=setConfig&Encode[0].MainFormat[0].Audio.Compression=G.711A&Encode[0].MainFormat[0].Audio.Frequency=8000');

    'rtsp://' + host + '/cam/realmonitor?channel=1&subtype=0&unicast=true&proto=Onvif'
```

**dom.ru**

You can get credentials from https://github.com/ad/domru

```yaml
streams:
  dom_ru: |
    expr:
    let camera   = '***';
    let token    = '***';
    let operator = '***';

    fetch('https://myhome.proptech.ru/rest/v1/forpost/cameras/' + camera + '/video', {
      headers: {
        'Authorization': 'Bearer ' + token,
        'User-Agent': 'Google sdkgphone64x8664 | Android 14 | erth | 8.26.0 (82600010) | 0 | 0 | 0',
        'Operator': operator
      }
    }).json().data.URL
```

**dom.ufanet.ru**

```yaml
streams:
  ufanet_ru: |
    expr:
    let username = '***';
    let password = '***';
    let cameraid = '***';

    let r1 = fetch('https://ucams.ufanet.ru/api/internal/login/', {
      method: 'POST',
      data: {username: username, password: password}
    });
    let r2 = fetch('https://ucams.ufanet.ru/api/v0/cameras/this/?lang=ru', {
      method: 'POST',
      json: {'fields': ['token_l', 'server'], 'token_l_ttl': 3600, 'numbers': [cameraid]},
    }).json().results[0];

    'rtsp://' + r2.server.domain + '/' + r2.number + '?token=' + r2.token_l
```

**Parse HLS files from Apple**

Same example in two languages - python and expr.

```yaml
streams:
  example_python: |
    echo:python -c 'from urllib.request import urlopen; import re

    # url1 = "https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_16x9/bipbop_16x9_variant.m3u8"
    html1 = urlopen("https://developer.apple.com/streaming/examples/basic-stream-osx-ios5.html").read().decode("utf-8")
    url1 = re.search(r"https.+?m3u8", html1)[0]

    # url2 = "gear1/prog_index.m3u8"
    html2 = urlopen(url1).read().decode("utf-8")
    url2 = re.search(r"^[a-z0-1/_]+\.m3u8$", html2, flags=re.MULTILINE)[0]

    # url3 = "https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_16x9/gear1/prog_index.m3u8"
    url3 = url1[:url1.rindex("/")+1] + url2

    print("ffmpeg:" + url3 + "#video=copy")'

  example_expr: |
    expr:

    let html1 = fetch("https://developer.apple.com/streaming/examples/basic-stream-osx-ios5.html").text;
    let url1 = match(html1, "https.+?m3u8")[0];

    let html2 = fetch(url1).text;
    let url2 = match(html2, "^[a-z0-1/_]+\\.m3u8$", "m")[0];

    let url3 = url1[:lastIndexOf(url1, "/")+1] + url2;

    "ffmpeg:" + url3 + "#video=copy"
```

## Comparison

| expr                         | python                     | js                             |
|------------------------------|----------------------------|--------------------------------|
| let x = 1;                   | x = 1                      | let x = 1                      |
| {a: 1, b: 2}                 | {"a": 1, "b": 2}           | {a: 1, b: 2}                   |
| let r = fetch(url, {method}) | r = request(method, url)   | r = await fetch(url, {method}) |
| r.ok                         | r.ok                       | r.ok                           |
| r.status                     | r.status_code              | r.status                       |
| r.text                       | r.text                     | await r.text()                 |
| r.json()                     | r.json()                   | await r.json()                 |
| r.headers                    | r.headers                  | r.headers                      |
| let m = match(text, "abc")   | m = re.search("abc", text) | let m = text.match(/abc/)      |


================================================
FILE: internal/expr/expr.go
================================================
package expr

import (
	"errors"

	"github.com/AlexxIT/go2rtc/internal/app"
	"github.com/AlexxIT/go2rtc/internal/streams"
	"github.com/AlexxIT/go2rtc/pkg/expr"
)

func Init() {
	log := app.GetLogger("expr")

	streams.RedirectFunc("expr", func(url string) (string, error) {
		v, err := expr.Eval(url[5:], nil)
		if err != nil {
			return "", err
		}

		log.Debug().Msgf("[expr] url=%s", url)

		if url = v.(string); url == "" {
			return "", errors.New("expr: result is empty")
		}

		return url, nil
	})
	streams.MarkInsecure("expr")
}


================================================
FILE: internal/ffmpeg/README.md
================================================
# FFmpeg

You can get any stream, file or device via FFmpeg and push it to go2rtc. The app will automatically start FFmpeg with the proper arguments when someone starts watching the stream.

- FFmpeg preinstalled for **Docker** and **Home Assistant add-on** users
- **Home Assistant add-on** users can target files from [/media](https://www.home-assistant.io/more-info/local-media/setup-media/) folder

## Configuration

Format: `ffmpeg:{input}#{param1}#{param2}#{param3}`. Examples:

```yaml
streams:
  # [FILE] all tracks will be copied without transcoding codecs
  file1: ffmpeg:/media/BigBuckBunny.mp4

  # [FILE] video will be transcoded to H264, audio will be skipped
  file2: ffmpeg:/media/BigBuckBunny.mp4#video=h264

  # [FILE] video will be copied, audio will be transcoded to PCMU
  file3: ffmpeg:/media/BigBuckBunny.mp4#video=copy#audio=pcmu

  # [HLS] video will be copied, audio will be skipped
  hls: ffmpeg:https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_16x9/gear5/prog_index.m3u8#video=copy

  # [MJPEG] video will be transcoded to H264
  mjpeg: ffmpeg:http://185.97.122.128/cgi-bin/faststream.jpg#video=h264

  # [RTSP] video with rotation, should be transcoded, so select H264
  rotate: ffmpeg:rtsp://12345678@192.168.1.123/av_stream/ch0#video=h264#rotate=90
```

All transcoding formats have [built-in templates](ffmpeg.go): `h264`, `h265`, `opus`, `pcmu`, `pcmu/16000`, `pcmu/48000`, `pcma`, `pcma/16000`, `pcma/48000`, `aac`, `aac/16000`.

But you can override them via YAML config. You can also add your own formats to the config and use them with source params.

```yaml
ffmpeg:
  bin: ffmpeg  # path to ffmpeg binary
  global: "-hide_banner"
  timeout: 5  # default timeout in seconds for rtsp inputs
  h264: "-codec:v libx264 -g:v 30 -preset:v superfast -tune:v zerolatency -profile:v main -level:v 4.1"
  mycodec: "-any args that supported by ffmpeg..."
  myinput: "-fflags nobuffer -flags low_delay -timeout {timeout} -i {input}"
  myraw: "-ss 00:00:20"
```

- You can use go2rtc stream name as ffmpeg input (ex. `ffmpeg:camera1#video=h264`)
- You can use `video` and `audio` params multiple times (ex. `#video=copy#audio=copy#audio=pcmu`)
- You can use `rotate` param with `90`, `180`, `270` or `-90` values, important with transcoding (ex. `#video=h264#rotate=90`)
- You can use `width` and/or `height` params, important with transcoding (ex. `#video=h264#width=1280`)
- You can use `drawtext` to add a timestamp (ex. `drawtext=x=2:y=2:fontsize=12:fontcolor=white:box=1:boxcolor=black`)
    - This will greatly increase the CPU of the server, even with hardware acceleration
- You can use `timeout` param to set RTSP input timeout in seconds (ex. `#timeout=10`)
- You can use `raw` param for any additional FFmpeg arguments (ex. `#raw=-vf transpose=1`)
- You can use `input` param to override default input template (ex. `#input=rtsp/udp` will change RTSP transport from TCP to UDP+TCP)
    - You can use raw input value (ex. `#input=-timeout {timeout} -i {input}`)
    - You can add your own input templates

Read more about [hardware acceleration](hardware/README.md).

**PS.** It is recommended to check the available hardware in the WebUI add page.


================================================
FILE: internal/ffmpeg/api.go
================================================
package ffmpeg

import (
	"net/http"
	"strings"

	"github.com/AlexxIT/go2rtc/internal/streams"
)

func apiFFmpeg(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		http.Error(w, "", http.StatusMethodNotAllowed)
		return
	}

	query := r.URL.Query()
	dst := query.Get("dst")
	stream := streams.Get(dst)
	if stream == nil {
		http.Error(w, "", http.StatusNotFound)
		return
	}

	var src string
	if s := query.Get("file"); s != "" {
		if streams.Validate(s) == nil {
			src = "ffmpeg:" + s + "#audio=auto#input=file"
		}
	} else if s = query.Get("live"); s != "" {
		if streams.Validate(s) == nil {
			src = "ffmpeg:" + s + "#audio=auto"
		}
	} else if s = query.Get("text"); s != "" {
		if strings.IndexAny(s, `'"&%$`) < 0 {
			src = "ffmpeg:tts?text=" + s
			if s = query.Get("voice"); s != "" {
				src += "&voice=" + s
			}
			src += "#audio=auto"
		}
	}

	if src == "" {
		http.Error(w, "", http.StatusBadRequest)
		return
	}

	if err := stream.Play(src); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}


================================================
FILE: internal/ffmpeg/device/README.md
================================================
# FFmpeg Device

You can get video from any USB camera or Webcam as RTSP or WebRTC stream. This is part of FFmpeg integration.

- check available devices in web interface
- `video_size` and `framerate` must be supported by your camera!
- for Linux supported only video for now
- for macOS you can stream FaceTime camera or whole desktop!
- for macOS important to set right framerate

## Configuration

Format: `ffmpeg:device?{input-params}#{param1}#{param2}#{param3}`

```yaml
streams:
  linux_usbcam:   ffmpeg:device?video=0&video_size=1280x720#video=h264
  windows_webcam: ffmpeg:device?video=0#video=h264
  macos_facetime: ffmpeg:device?video=0&audio=1&video_size=1280x720&framerate=30#video=h264#audio=pcma
```

**PS.** It is recommended to check the available devices in the WebUI add page.


================================================
FILE: internal/ffmpeg/device/device_bsd.go
================================================
//go:build freebsd || netbsd || openbsd || dragonfly

package device

import (
	"net/url"
	"os"
	"os/exec"
	"regexp"
	"strings"

	"github.com/AlexxIT/go2rtc/internal/api"
	"github.com/AlexxIT/go2rtc/pkg/core"
)

func queryToInput(query url.Values) string {
	if video := query.Get("video"); video != "" {
		// https://ffmpeg.org/ffmpeg-devices.html#video4linux2_002c-v4l2
		input := "-f v4l2"

		for key, value := range query {
			switch key {
			case "resolution":
				input += " -video_size " + value[0]
			case "video_size", "pixel_format", "input_format", "framerate", "use_libv4l2":
				input += " -" + key + " " + value[0]
			}
		}

		return input + " -i " + indexToItem(videos, video)
	}

	if audio := query.Get("audio"); audio != "" {
		input := "-f oss"

		for key, value := range query {
			switch key {
			case "channels", "sample_rate":
				input += " -" + key + " " + value[0]
			}
		}

		return input + " -i " + indexToItem(audios, audio)
	}

	return ""
}

func initDevices() {
	files, err := os.ReadDir("/dev")
	if err != nil {
		return
	}

	for _, file := range files {
		if !strings.HasPrefix(file.Name(), core.KindVideo) {
			continue
		}

		name := "/dev/" + file.Name()

		cmd := exec.Command(
			Bin, "-hide_banner", "-f", "v4l2", "-list_formats", "all", "-i", name,
		)
		b, _ := cmd.CombinedOutput()

		// [video4linux2,v4l2 @ 0x860b92280] Raw       :     yuyv422 :           YUYV 4:2:2 : 640x480 160x120 176x144 320x176 320x240 352x288 432x240 544x288 640x360 752x416 800x448 800x600 864x480 960x544 960x720 1024x576 1184x656 1280x720 1280x960
		// [video4linux2,v4l2 @ 0x860b92280] Compressed:       mjpeg :          Motion-JPEG : 640x480 160x120 176x144 320x176 320x240 352x288 432x240 544x288 640x360 752x416 800x448 800x600 864x480 960x544 960x720 1024x576 1184x656 1280x720 1280x960
		re := regexp.MustCompile("(Raw *|Compressed): +(.+?) : +(.+?) : (.+)")
		m := re.FindAllStringSubmatch(string(b), -1)
		for _, i := range m {
			size, _, _ := strings.Cut(i[4], " ")
			stream := &api.Source{
				Name: i[3],
				Info: i[4],
				URL:  "ffmpeg:device?video=" + name + "&input_format=" + i[2] + "&video_size=" + size,
			}

			if i[1] != "Compressed" {
				stream.URL += "#video=h264#hardware"
			}

			videos = append(videos, name)
			streams = append(streams, stream)
		}
	}

	err = exec.Command(Bin, "-f", "oss", "-i", "/dev/dsp", "-t", "1", "-f", "null", "-").Run()
	if err == nil {
		stream := &api.Source{
			Name: "OSS default",
			Info: " ",
			URL:  "ffmpeg:device?audio=default&channels=1&sample_rate=16000&#audio=opus",
		}

		audios = append(audios, "default")
		streams = append(streams, stream)
	}
}


================================================
FILE: internal/ffmpeg/device/device_darwin.go
================================================
//go:build darwin || ios

package device

import (
	"net/url"
	"os/exec"
	"regexp"
	"strings"

	"github.com/AlexxIT/go2rtc/internal/api"
	"github.com/AlexxIT/go2rtc/pkg/core"
)

func queryToInput(query url.Values) string {
	video := query.Get("video")
	audio := query.Get("audio")

	if video == "" && audio == "" {
		return ""
	}

	// https://ffmpeg.org/ffmpeg-devices.html#avfoundation
	input := "-f avfoundation"

	if video != "" {
		video = indexToItem(videos, video)

		for key, value := range query {
			switch key {
			case "resolution":
				input += " -video_size " + value[0]
			case "pixel_format", "framerate", "video_size", "capture_cursor", "capture_mouse_clicks", "capture_raw_data":
				input += " -" + key + " " + value[0]
			}
		}
	}

	if audio != "" {
		audio = indexToItem(audios, audio)
	}

	return input + ` -i "` + video + `:` + audio + `"`
}

func initDevices() {
	// [AVFoundation indev @ 0x147f04510] AVFoundation video devices:
	// [AVFoundation indev @ 0x147f04510] [0] FaceTime HD Camera
	// [AVFoundation indev @ 0x147f04510] [1] Capture screen 0
	// [AVFoundation indev @ 0x147f04510] AVFoundation audio devices:
	// [AVFoundation indev @ 0x147f04510] [0] MacBook Pro Microphone
	cmd := exec.Command(
		Bin, "-hide_banner", "-list_devices", "true", "-f", "avfoundation", "-i", "",
	)
	b, _ := cmd.CombinedOutput()

	re := regexp.MustCompile(`\[\d+] (.+)`)

	var kind string
	for _, line := range strings.Split(string(b), "\n") {
		switch {
		case strings.HasSuffix(line, "video devices:"):
			kind = core.KindVideo
			continue
		case strings.HasSuffix(line, "audio devices:"):
			kind = core.KindAudio
			continue
		}

		m := re.FindStringSubmatch(line)
		if m == nil {
			continue
		}

		name := m[1]

		switch kind {
		case core.KindVideo:
			videos = append(videos, name)
		case core.KindAudio:
			audios = append(audios, name)
		}

		streams = append(streams, &api.Source{
			Name: name, URL: "ffmpeg:device?" + kind + "=" + name,
		})
	}
}


================================================
FILE: internal/ffmpeg/device/device_unix.go
================================================
//go:build unix && !darwin && !freebsd && !netbsd && !openbsd && !dragonfly

package device

import (
	"net/url"
	"os"
	"os/exec"
	"regexp"
	"strings"

	"github.com/AlexxIT/go2rtc/internal/api"
	"github.com/AlexxIT/go2rtc/pkg/core"
)

func queryToInput(query url.Values) string {
	if video := query.Get("video"); video != "" {
		// https://ffmpeg.org/ffmpeg-devices.html#video4linux2_002c-v4l2
		input := "-f v4l2"

		for key, value := range query {
			switch key {
			case "resolution":
				input += " -video_size " + value[0]
			case "video_size", "pixel_format", "input_format", "framerate", "use_libv4l2":
				input += " -" + key + " " + value[0]
			}
		}

		return input + " -i " + indexToItem(videos, video)
	}

	if audio := query.Get("audio"); audio != "" {
		// https://trac.ffmpeg.org/wiki/Capture/ALSA
		input := "-f alsa"

		for key, value := range query {
			switch key {
			case "channels", "sample_rate":
				input += " -" + key + " " + value[0]
			}
		}

		return input + " -i " + indexToItem(audios, audio)
	}

	return ""
}

func initDevices() {
	files, err := os.ReadDir("/dev")
	if err != nil {
		return
	}

	for _, file := range files {
		if !strings.HasPrefix(file.Name(), core.KindVideo) {
			continue
		}

		name := "/dev/" + file.Name()

		cmd := exec.Command(
			Bin, "-hide_banner", "-f", "v4l2", "-list_formats", "all", "-i", name,
		)
		b, _ := cmd.CombinedOutput()

		// [video4linux2,v4l2 @ 0x204e1c0] Compressed:       mjpeg :          Motion-JPEG : 640x360 1280x720 1920x1080
		// [video4linux2,v4l2 @ 0x204e1c0] Raw       :     yuyv422 :           YUYV 4:2:2 : 640x360 1280x720 1920x1080
		// [video4linux2,v4l2 @ 0x204e1c0] Compressed:        h264 :                H.264 : 640x360 1280x720 1920x1080
		re := regexp.MustCompile("(Raw *|Compressed): +(.+?) : +(.+?) : (.+)")
		m := re.FindAllStringSubmatch(string(b), -1)
		for _, i := range m {
			size, _, _ := strings.Cut(i[4], " ")
			stream := &api.Source{
				Name: i[3],
				Info: i[4],
				URL:  "ffmpeg:device?video=" + name + "&input_format=" + i[2] + "&video_size=" + size,
			}

			if i[1] != "Compressed" {
				stream.URL += "#video=h264#hardware"
			}

			videos = append(videos, name)
			streams = append(streams, stream)
		}
	}

	err = exec.Command(Bin, "-f", "alsa", "-i", "default", "-t", "1", "-f", "null", "-").Run()
	if err == nil {
		stream := &api.Source{
			Name: "ALSA default",
			Info: " ",
			URL:  "ffmpeg:device?audio=default&channels=1&sample_rate=16000&#audio=opus",
		}

		audios = append(audios, "default")
		streams = append(streams, stream)
	}
}


================================================
FILE: internal/ffmpeg/device/device_windows.go
================================================
//go:build windows

package device

import (
	"net/url"
	"os/exec"
	"regexp"

	"github.com/AlexxIT/go2rtc/internal/api"
	"github.com/AlexxIT/go2rtc/pkg/core"
)

func queryToInput(query url.Values) string {
	video := query.Get("video")
	audio := query.Get("audio")

	if video == "" && audio == "" {
		return ""
	}

	// https://ffmpeg.org/ffmpeg-devices.html#dshow
	input := "-f dshow"

	if video != "" {
		video = indexToItem(videos, video)

		for key, value := range query {
			switch key {
			case "resolution":
				input += " -video_size " + value[0]
			case "video_size", "framerate", "pixel_format":
				input += " -" + key + " " + value[0]
			}
		}
	}

	if audio != "" {
		audio = indexToItem(audios, audio)

		for key, value := range query {
			switch key {
			case "sample_rate", "sample_size", "channels", "audio_buffer_size":
				input += " -" + key + " " + value[0]
			}
		}
	}

	if video != "" {
		input += ` -i "video=` + video

		if audio != "" {
			input += `:audio=` + audio
		}

		input += `"`
	} else {
		input += ` -i "audio=` + audio + `"`
	}

	return input
}

func initDevices() {
	cmd := exec.Command(
		Bin, "-hide_banner", "-list_devices", "true", "-f", "dshow", "-i", "",
	)
	b, _ := cmd.CombinedOutput()

	re := regexp.MustCompile(`"([^"]+)" \((video|audio)\)`)
	for _, m := range re.FindAllStringSubmatch(string(b), -1) {
		name := m[1]
		kind := m[2]

		stream := &api.Source{
			Name: name, URL: "ffmpeg:device?" + kind + "=" + name,
		}

		switch kind {
		case core.KindVideo:
			videos = append(videos, name)
			stream.URL += "#video=h264#hardware"
		case core.KindAudio:
			audios = append(audios, name)
			stream.URL += "&channels=1&sample_rate=16000&audio_buffer_size=10"
		}

		streams = append(streams, stream)
	}
}


================================================
FILE: internal/ffmpeg/device/devices.go
================================================
package device

import (
	"net/http"
	"net/url"
	"strconv"
	"sync"

	"github.com/AlexxIT/go2rtc/internal/api"
)

func Init(bin string) {
	Bin = bin

	api.HandleFunc("api/ffmpeg/devices", apiDevices)
}

func GetInput(src string) string {
	query, err := url.ParseQuery(src)
	if err != nil {
		return ""
	}

	runonce.Do(initDevices)

	return queryToInput(query)
}

var Bin string

var videos, audios []string
var streams []*api.Source
var runonce sync.Once

func apiDevices(w http.ResponseWriter, r *http.Request) {
	runonce.Do(initDevices)

	api.ResponseSources(w, streams)
}

func indexToItem(items []string, index string) string {
	if i, err := strconv.Atoi(index); err == nil && i < len(items) {
		return items[i]
	}
	return index
}


================================================
FILE: internal/ffmpeg/ffmpeg.go
================================================
package ffmpeg

import (
	"net/url"
	"strings"

	"github.com/AlexxIT/go2rtc/internal/api"
	"github.com/AlexxIT/go2rtc/internal/app"
	"github.com/AlexxIT/go2rtc/internal/ffmpeg/device"
	"github.com/AlexxIT/go2rtc/internal/ffmpeg/hardware"
	"github.com/AlexxIT/go2rtc/internal/ffmpeg/virtual"
	"github.com/AlexxIT/go2rtc/internal/rtsp"
	"github.com/AlexxIT/go2rtc/internal/streams"
	"github.com/AlexxIT/go2rtc/pkg/core"
	"github.com/AlexxIT/go2rtc/pkg/ffmpeg"
	"github.com/rs/zerolog"
)

func Init() {
	var cfg struct {
		Mod map[string]string `yaml:"ffmpeg"`
		Log struct {
			Level string `yaml:"ffmpeg"`
		} `yaml:"log"`
	}

	cfg.Mod = defaults // will be overriden from yaml
	cfg.Log.Level = "error"

	app.LoadConfig(&cfg)

	log = app.GetLogger("ffmpeg")

	// zerolog levels: trace debug         info warn    error fatal panic disabled
	// FFmpeg  levels: trace debug verbose info warning error fatal panic quiet
	if cfg.Log.Level == "warn" {
		cfg.Log.Level = "warning"
	}
	defaults["global"] += " -v " + cfg.Log.Level

	streams.RedirectFunc("ffmpeg", func(url string) (string, error) {
		if _, err := Version(); err != nil {
			return "", err
		}
		args := parseArgs(url[7:])
		if core.Contains(args.Codecs, "auto") {
			return "", nil // force call streams.HandleFunc("ffmpeg")
		}
		return "exec:" + args.String(), nil
	})

	streams.HandleFunc("ffmpeg", NewProducer)

	api.HandleFunc("api/ffmpeg", apiFFmpeg)

	device.Init(defaults["bin"])
	hardware.Init(defaults["bin"])
}

var defaults = map[string]string{
	"bin":     "ffmpeg",
	"global":  "-hide_banner",
	"timeout": "5",

	// inputs
	"file":     "-re -i {input}",
	"http":     "-fflags nobuffer -flags low_delay -i {input}",
	"rtsp":     "-fflags nobuffer -flags low_delay -timeout {timeout} -user_agent go2rtc/ffmpeg -rtsp_flags prefer_tcp -i {input}",
	"rtsp/udp": "-fflags nobuffer -flags low_delay -timeout {timeout} -user_agent go2rtc/ffmpeg -i {input}",

	// output
	"output":       "-user_agent ffmpeg/go2rtc -rtsp_transport tcp -f rtsp {output}",
	"output/mjpeg": "-f mjpeg -",
	"output/raw":   "-f yuv4mpegpipe -",
	"output/aac":   "-f adts -",
	"output/wav":   "-f wav -",

	// `-preset superfast` - we can't use ultrafast because it doesn't support `-profile main -level 4.1`
	// `-tune zerolatency` - for minimal latency
	// `-profile high -level 4.1` - most used streaming profile
	// `-pix_fmt:v yuv420p` - important for Telegram
	"h264":  "-c:v libx264 -g 50 -profile:v high -level:v 4.1 -preset:v superfast -tune:v zerolatency -pix_fmt:v yuv420p",
	"h265":  "-c:v libx265 -g 50 -profile:v main -x265-params level=5.1:high-tier=0 -preset:v superfast -tune:v zerolatency -pix_fmt:v yuv420p",
	"mjpeg": "-c:v mjpeg",
	//"mjpeg": "-c:v mjpeg -force_duplicated_matrix:v 1 -huffman:v 0 -pix_fmt:v yuvj420p",

	"raw":         "-c:v rawvideo",
	"raw/gray8":   "-c:v rawvideo -pix_fmt:v gray8",
	"raw/yuv420p": "-c:v rawvideo -pix_fmt:v yuv420p",
	"raw/yuv422p": "-c:v rawvideo -pix_fmt:v yuv422p",
	"raw/yuv444p": "-c:v rawvideo -pix_fmt:v yuv444p",

	// https://ffmpeg.org/ffmpeg-codecs.html#libopus-1
	// https://github.com/pion/webrtc/issues/1514
	// https://ffmpeg.org/ffmpeg-resampler.html
	// `-async 1` or `-min_comp 0` - force resampling for static timestamp inc, important for WebRTC audio quality
	"opus":       "-c:a libopus -application:a lowdelay -min_comp 0",
	"opus/16000": "-c:a libopus -application:a lowdelay -min_comp 0 -ar:a 16000 -ac:a 1",
	"pcmu":       "-c:a pcm_mulaw -ar:a 8000 -ac:a 1",
	"pcmu/8000":  "-c:a pcm_mulaw -ar:a 8000 -ac:a 1",
	"pcmu/16000": "-c:a pcm_mulaw -ar:a 16000 -ac:a 1",
	"pcmu/48000": "-c:a pcm_mulaw -ar:a 48000 -ac:a 1",
	"pcma":       "-c:a pcm_alaw -ar:a 8000 -ac:a 1",
	"pcma/8000":  "-c:a pcm_alaw -ar:a 8000 -ac:a 1",
	"pcma/16000": "-c:a pcm_alaw -ar:a 16000 -ac:a 1",
	"pcma/48000": "-c:a pcm_alaw -ar:a 48000 -ac:a 1",
	"aac":        "-c:a aac", // keep sample rate and channels
	"aac/16000":  "-c:a aac -ar:a 16000 -ac:a 1",
	"mp3":        "-c:a libmp3lame -q:a 8",
	"pcm":        "-c:a pcm_s16be -ar:a 8000 -ac:a 1",
	"pcm/8000":   "-c:a pcm_s16be -ar:a 8000 -ac:a 1",
	"pcm/16000":  "-c:a pcm_s16be -ar:a 16000 -ac:a 1",
	"pcm/48000":  "-c:a pcm_s16be -ar:a 48000 -ac:a 1",
	"pcml":       "-c:a pcm_s16le -ar:a 8000 -ac:a 1",
	"pcml/8000":  "-c:a pcm_s16le -ar:a 8000 -ac:a 1",
	"pcml/16000": "-c:a pcm_s16le -ar:a 16000 -ac:a 1",
	"pcml/44100": "-c:a pcm_s16le -ar:a 44100 -ac:a 1",

	// hardware Intel and AMD on Linux
	// better not to set `-async_depth:v 1` like for QSV, because framedrops
	// `-bf 0` - disable B-frames is very important
	"h264/vaapi":  "-c:v h264_vaapi -g 50 -bf 0 -profile:v high -level:v 4.1 -sei:v 0",
	"h265/vaapi":  "-c:v hevc_vaapi -g 50 -bf 0 -profile:v main -level:v 5.1 -sei:v 0",
	"mjpeg/vaapi": "-c:v mjpeg_vaapi",

	// hardware Raspberry
	"h264/v4l2m2m": "-c:v h264_v4l2m2m -g 50 -bf 0",
	"h265/v4l2m2m": "-c:v hevc_v4l2m2m -g 50 -bf 0",

	// hardware Rockchip
	// important to use custom ffmpeg https://github.com/AlexxIT/go2rtc/issues/768
	// hevc - doesn't have a profile setting
	"h264/rkmpp":  "-c:v h264_rkmpp -g 50 -bf 0 -profile:v high -level:v 4.1",
	"h265/rkmpp":  "-c:v hevc_rkmpp -g 50 -bf 0 -profile:v main -level:v 5.1",
	"mjpeg/rkmpp": "-c:v mjpeg_rkmpp",

	// hardware NVidia on Linux and Windows
	// preset=p2 - faster, tune=ll - low latency
	"h264/cuda": "-c:v h264_nvenc -g 50 -bf 0 -profile:v high -level:v auto -preset:v p2 -tune:v ll",
	"h265/cuda": "-c:v hevc_nvenc -g 50 -bf 0 -profile:v main -level:v auto",

	// hardware Intel on Windows
	"h264/dxva2":  "-c:v h264_qsv -g 50 -bf 0 -profile:v high -level:v 4.1 -async_depth:v 1",
	"h265/dxva2":  "-c:v hevc_qsv -g 50 -bf 0 -profile:v main -level:v 5.1 -async_depth:v 1",
	"mjpeg/dxva2": "-c:v mjpeg_qsv",

	// hardware macOS
	"h264/videotoolbox": "-c:v h264_videotoolbox -g 50 -bf 0 -profile:v high -level:v 4.1",
	"h265/videotoolbox": "-c:v hevc_videotoolbox -g 50 -bf 0 -profile:v main -level:v 5.1",
}

var log zerolog.Logger

// configTemplate - return template from config (defaults) if exist or return raw template
func configTemplate(template string) string {
	if s := defaults[template]; s != "" {
		return s
	}
	return template
}

// inputTemplate - select input template from YAML config by template name
// if query has input param - select another template by this name
// if there is no another template - use input param as template
func inputTemplate(name, s string, query url.Values) string {
	var template string
	if input := query.Get("input"); input != "" {
		template = configTemplate(input)
	} else {
		template = defaults[name]
	}
	if strings.Contains(template, "{timeout}") {
		timeout := query.Get("timeout")
		if timeout == "" {
			timeout = defaults["timeout"]
		}
		template = strings.Replace(template, "{timeout}", timeout+"000000", 1)
	}
	return strings.Replace(template, "{input}", s, 1)
}

func parseArgs(s string) *ffmpeg.Args {
	// init FFmpeg arguments
	args := &ffmpeg.Args{
		Bin:     defaults["bin"],
		Global:  defaults["global"],
		Output:  defaults["output"],
		Version: verAV,
	}

	var source = s
	var query url.Values
	if i := strings.IndexByte(s, '#'); i >= 0 {
		query = streams.ParseQuery(s[i+1:])
		args.Video = len(query["video"])
		args.Audio = len(query["audio"])
		s = s[:i]
	}

	// Parse input:
	//   1. Input as xxxx:// link (http or rtsp or any other)
	//   2. Input as stream name
	//   3. Input as FFmpeg device (local USB camera)
	if i := strings.Index(s, "://"); i > 0 {
		switch s[:i] {
		case "http", "https", "rtmp":
			args.Input = inputTemplate("http", s, query)
		case "rtsp", "rtsps":
			// https://ffmpeg.org/ffmpeg-protocols.html#rtsp
			// skip unnecessary input tracks
			switch {
			case (args.Video > 0 && args.Audio > 0) || (args.Video == 0 && args.Audio == 0):
				args.Input = "-allowed_media_types video+audio "
			case args.Video > 0:
				args.Input = "-allowed_media_types video "
			case args.Audio > 0:
				args.Input = "-allowed_media_types audio "
			}

			args.Input += inputTemplate("rtsp", s, query)
		default:
			args.Input = "-i " + s
		}
	} else if streams.Get(s) != nil {
		s = "rtsp://127.0.0.1:" + rtsp.Port + "/" + s
		switch {
		case args.Video > 0 && args.Audio == 0:
			s += "?video"
		case args.Audio > 0 && args.Video == 0:
			s += "?audio"
		default:
			s += "?video&audio"
		}
		s += "&source=ffmpeg:" + url.QueryEscape(source)
		for _, v := range query["query"] {
			s += "&" + v
		}
		args.Input = inputTemplate("rtsp", s, query)
	} else if i = strings.Index(s, "?"); i > 0 {
		switch s[:i] {
		case "device":
			args.Input = device.GetInput(s[i+1:])
		case "virtual":
			args.Input = virtual.GetInput(s[i+1:])
		case "tts":
			args.Input = virtual.GetInputTTS(s[i+1:])
		}
	} else {
		args.Input = inputTemplate("file", s, query)
	}

	if query["async"] != nil {
		args.Input = "-use_wallclock_as_timestamps 1 -async 1 " + args.Input
	}

	// Parse query params:
	//   1. `width`/`height` params
	//   2. `rotate` param
	//   3. `video` params (support multiple)
	//   4. `audio` params (support multiple)
	//   5. `hardware` param
	if query != nil {
		// 1. Process raw params for FFmpeg
		for _, raw := range query["raw"] {
			// support templates https://github.com/AlexxIT/go2rtc/issues/487
			raw = configTemplate(raw)
			args.AddCodec(raw)
		}

		// 2. Process video filters (resize and rotation)
		if query["width"] != nil || query["height"] != nil {
			filter := "scale="
			if query["width"] != nil {
				filter += query["width"][0]
			} else {
				filter += "-1"
			}
			filter += ":"
			if query["height"] != nil {
				filter += query["height"][0]
			} else {
				filter += "-1"
			}
			args.AddFilter(filter)
		}

		if query["rotate"] != nil {
			var filter string
			switch query["rotate"][0] {
			case "90":
				filter = "transpose=1" // 90 degrees clockwise
			case "180":
				filter = "transpose=1,transpose=1"
			case "-90", "270":
				filter = "transpose=2" // 90 degrees counterclockwise
			}
			if filter != "" {
				args.AddFilter(filter)
			}
		}

		for _, drawtext := range query["drawtext"] {
			// support templates https://github.com/AlexxIT/go2rtc/issues/487
			drawtext = configTemplate(drawtext)

			// support default timestamp format
			if !strings.Contains(drawtext, "text=") {
				drawtext += `:text='%{localtime\:%Y-%m-%d %X}'`
			}

			args.AddFilter("drawtext=" + drawtext)
		}

		// 3. Process video codecs
		if args.Video > 0 {
			for _, video := range query["video"] {
				if video != "copy" {
					if codec := defaults[video]; codec != "" {
						args.AddCodec(codec)
					} else {
						args.AddCodec(video)
					}
				} else {
					args.AddCodec("-c:v copy")
				}
			}
		}

		if query["bitrate"] != nil {
			// https://trac.ffmpeg.org/wiki/Limiting%20the%20output%20bitrate
			b := query["bitrate"][0]
			args.AddCodec("-b:v " + b + " -maxrate " + b + " -bufsize " + b)
		}

		// 4. Process audio codecs
		if args.Audio > 0 {
			for _, audio := range query["audio"] {
				if audio != "copy" {
					if codec := defaults[audio]; codec != "" {
						args.AddCodec(codec)
					} else {
						args.AddCodec(audio)
					}
				} else {
					args.AddCodec("-c:a copy")
				}
			}
		}

		if query["hardware"] != nil {
			hardware.MakeHardware(args, query["hardware"][0], defaults)
		}
	}

	switch {
	case args.Video == 0 && args.Audio == 0:
		args.AddCodec("-c copy")
	case args.Video == 0:
		args.AddCodec("-vn")
	case args.Audio == 0:
		args.AddCodec("-an")
	}

	// change otput from RTSP to some other pipe format
	switch {
	case args.Video == 0 && args.Audio == 0:
		// no transcoding from mjpeg input (ffmpeg device with support output as raw MJPEG)
		if strings.Contains(args.Input, " mjpeg ") {
			args.Output = defaults["output/mjpeg"]
		}
	case args.Video == 1 && args.Audio == 0:
		switch core.Before(query.Get("video"), "/") {
		case "mjpeg":
			args.Output = defaults["output/mjpeg"]
		case "raw":
			args.Output = defaults["output/raw"]
		}
	case args.Video == 0 && args.Audio == 1:
		switch core.Before(query.Get("audio"), "/") {
		case "aac":
			args.Output = defaults["output/aac"]
		case "pcma", "pcmu", "pcml":
			args.Output = defaults["output/wav"]
		}
	}

	return args
}


================================================
FILE: internal/ffmpeg/ffmpeg_test.go
================================================
package ffmpeg

import (
	"testing"

	"github.com/AlexxIT/go2rtc/pkg/ffmpeg"
	"github.com/stretchr/testify/require"
)

func TestParseArgsFile(t *testing.T) {
	tests := []struct {
		name   string
		source string
		expect string
	}{
		{
			name:   "[FILE] all tracks will be copied without transcoding codecs",
			source: "/media/bbb.mp4",
			expect: `ffmpeg -hide_banner -re -i /media/bbb.mp4 -c copy -user_agent ffmpeg/go2rtc -rtsp_transport tcp -f rtsp {output}`,
		},
		{
			name:   "[FILE] video will be transcoded to H264, audio will be skipped",
			source: "/media/bbb.mp4#video=h264",
			expect: `ffmpeg -hide_banner -re -i /media/bbb.mp4 -c:v libx264 -g 50 -profile:v high -level:v 4.1 -preset:v superfast -tune:v zerolatency -pix_fmt:v yuv420p -an -user_agent ffmpeg/go2rtc -rtsp_transport tcp -f rtsp {output}`,
		},
		{
			name:   "[FILE] video will be copied, audio will be transcoded to pcmu",
			source: "/media/bbb.mp4#video=copy#audio=pcmu",
			expect: `ffmpeg -hide_banner -re -i /media/bbb.mp4 -c:v copy -c:a pcm_mulaw -ar:a 8000 -ac:a 1 -user_agent ffmpeg/go2rtc -rtsp_transport tcp -f rtsp {output}`,
		},
		{
			name:   "[FILE] video will be transcoded to H265 and rotate 270º, audio will be skipped",
			source: "/media/bbb.mp4#video=h265#rotate=-90",
			expect: `ffmpeg -hide_banner -re -i /media/bbb.mp4 -c:v libx265 -g 50 -profile:v main -level:v 5.1 -preset:v superfast -tune:v zerolatency -pix_fmt:v yuv420p -an -vf "transpose=2" -user_agent ffmpeg/go2rtc -rtsp_transport tcp -f rtsp {output}`,
		},
		{
			name:   "[FILE] video will be output for MJPEG to pipe, audio will be skipped",
			source: "/media/bbb.mp4#video=mjpeg",
			expect: `ffmpeg -hide_banner -re -i /media/bbb.mp4 -c:v mjpeg -an -f mjpeg -`,
		},
		{
			name:   "https://github.com/AlexxIT/go2rtc/issues/509",
			source: "ffmpeg:test.mp4#raw=-ss 00:00:20",
			expect: `ffmpeg -hide_banner -re -i ffmpeg:test.mp4 -ss 00:00:20 -c copy -user_agent ffmpeg/go2rtc -rtsp_transport tcp -f rtsp {output}`,
		},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			args := parseArgs(test.source)
			require.Equal(t, test.expect, args.String())
		})
	}
}

func TestParseArgsDevice(t *testing.T) {
	tests := []struct {
		name   string
		source string
		expect string
	}{
		{
			name:   "[DEVICE] video will be output for MJPEG to pipe, with size 1920x1080",
			source: "device?video=0&video_size=1920x1080",
			expect: `ffmpeg -hide_banner -f dshow -video_size 1920x1080 -i "video=0" -c copy -user_agent ffmpeg/go2rtc -rtsp_transport tcp -f rtsp {output}`,
		},
		{
			name:   "[DEVICE] video will be transcoded to H265 with framerate 20, audio will be skipped",
			source: "device?video=0&framerate=20#video=h265",
			expect: `ffmpeg -hide_banner -f dshow -framerate 20 -i "video=0" -c:v libx265 -g 50 -profile:v main -level:v 5.1 -preset:v superfast -tune:v zerolatency -pix_fmt:v yuv420p -an -user_agent ffmpeg/go2rtc -rtsp_transport tcp -f rtsp {output}`,
		},
		{
			name:   "[DEVICE] video/audio",
			source: "device?video=FaceTime HD Camera&audio=Microphone (High Definition Audio Device)",
			expect: `ffmpeg -hide_banner -f dshow -i "video=FaceTime HD Camera:audio=Microphone (High Definition Audio Device)" -c copy -user_agent ffmpeg/go2rtc -rtsp_transport tcp -f rtsp {output}`,
		},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			args := parseArgs(test.source)
			require.Equal(t, test.expect, args.String())
		})
	}
}

func TestParseArgsIpCam(t *testing.T) {
	tests := []struct {
		name   string
		source string
		expect string
	}{
		{
			name:   "[HTTP] video will be copied",
			source: "http://example.com",
			expect: `ffmpeg -hide_banner -fflags nobuffer -flags low_delay -i http://example.com -c copy -user_agent ffmpeg/go2rtc -rtsp_transport tcp -f rtsp {output}`,
		},
		{
			name:   "[HTTP-MJPEG] video will be transcoded to H264",
			source: "http://example.com#video=h264",
			expect: `ffmpeg -hide_banner -fflags nobuffer -flags low_delay -i http://example.com -c:v libx264 -g 50 -profile:v high -level:v 4.1 -preset:v superfast -tune:v zerolatency -pix_fmt:v yuv420p -an -user_agent ffmpeg/go2rtc -rtsp_transport tcp -f rtsp {output}`,
		},
		{
			name:   "[HLS] video will be copied, audio will be skipped",
			source: "https://example.com#video=copy",
			expect: `ffmpeg -hide_banner -fflags nobuffer -flags low_delay -i https://example.com -c:v copy -an -user_agent ffmpeg/go2rtc -rtsp_transport tcp -f rtsp {output}`,
		},
		{
			name:   "[RTSP] video will be copied without transcoding codecs",
			source: "rtsp://example.com",
			expect: `ffmpeg -hide_banner -allowed_media_types video+audio -fflags nobuffer -flags low_delay -timeout 5000000 -user_agent go2rtc/ffmpeg -rtsp_flags prefer_tcp -i rtsp://example.com -c copy -user_agent ffmpeg/go2rtc -rtsp_transport tcp -f rtsp {output}`,
		},
		{
			name:   "[RTSP] video with resize to 1280x720, should be transcoded, so select H265",
			source: "rtsp://example.com#video=h265#width=1280#height=720",
			expect: `ffmpeg -hide_banner -allowed_media_types video -fflags nobuffer -flags low_delay -timeout 5000000 -user_agent go2rtc/ffmpeg -rtsp_flags prefer_tcp -i rtsp://example.com -c:v libx265 -g 50 -profile:v main -level:v 5.1 -preset:v superfast -tune:v zerolatency -pix_fmt:v yuv420p -an -vf "scale=1280:720" -user_agent ffmpeg/go2rtc -rtsp_transport tcp -f rtsp {output}`,
		},
		{
			name:   "[RTSP] video will be copied, changing RTSP transport from TCP to UDP+TCP",
			source: "rtsp://example.com#input=rtsp/udp",
			expect: `ffmpeg -hide_banner -allowed_media_types video+audio -fflags nobuffer -flags low_delay -timeout 5000000 -user_agent go2rtc/ffmpeg -i rtsp://example.com -c copy -user_agent ffmpeg/go2rtc -rtsp_transport tcp -f rtsp {output}`,
		},
		{
			name:   "[RTMP] video will be copied, changing RTSP transport from TCP to UDP+TCP",
			source: "rtmp://example.com#input=rtsp/udp",
			expect: `ffmpeg -hide_banner -fflags nobuffer -flags low_delay -timeout 5000000 -user_agent go2rtc/ffmpeg -i rtmp://example.com -c copy -user_agent ffmpeg/go2rtc -rtsp_transport tcp -f rtsp {output}`,
		},
		{
			name:   "[RTSP] custom timeout",
			source: "rtsp://example.com#timeout=10",
			expect: `ffmpeg -hide_banner -allowed_media_types video+audio -fflags nobuffer -flags low_delay -timeout 10000000 -user_agent go2rtc/ffmpeg -rtsp_flags prefer_tcp -i rtsp://example.com -c copy -user_agent ffmpeg/go2rtc -rtsp_transport tcp -f rtsp {output}`,
		},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			args := parseArgs(test.source)
			require.Equal(t, test.expect, args.String())
		})
	}
}

func TestParseArgsAudio(t *testing.T) {
	tests := []struct {
		name   string
		source string
		expect string
	}{
		{
			name:   "[AUDIO] audio will be transcoded to AAC, video will be skipped",
			source: "rtsp://example.com#audio=aac",
			expect: `ffmpeg -hide_banner -allowed_media_types audio -fflags nobuffer -flags low_delay -timeout 5000000 -user_agent go2rtc/ffmpeg -rtsp_flags prefer_tcp -i rtsp://example.com -c:a aac -vn -f adts -`,
		},
		{
			name:   "[AUDIO] audio will be transcoded to AAC/16000, video will be skipped",
			source: "rtsp://example.com#audio=aac/16000",
			expect: `ffmpeg -hide_banner -allowed_media_types audio -fflags nobuffer -flags low_delay -timeout 5000000 -user_agent go2rtc/ffmpeg -rtsp_flags prefer_tcp -i rtsp://example.com -c:a aac -ar:a 16000 -ac:a 1 -vn -f adts -`,
		},
		{
			name:   "[AUDIO] audio will be transcoded to OPUS, video will be skipped",
			source: "rtsp://example.com#audio=opus",
			expect: `ffmpeg -hide_banner -allowed_media_types audio -fflags nobuffer -flags low_delay -timeout 5000000 -user_agent go2rtc/ffmpeg -rtsp_flags prefer_tcp -i rtsp://example.com -c:a libopus -application:a lowdelay -min_comp 0 -vn -user_agent ffmpeg/go2rtc -rtsp_transport tcp -f rtsp {output}`,
		},
		{
			name:   "[AUDIO] audio will be transcoded to PCMU, video will be skipped",
			source: "rtsp://example.com#audio=pcmu",
			expect: `ffmpeg -hide_banner -allowed_media_types audio -fflags nobuffer -flags low_delay -timeout 5000000 -user_agent go2rtc/ffmpeg -rtsp_flags prefer_tcp -i rtsp://example.com -c:a pcm_mulaw -ar:a 8000 -ac:a 1 -vn -f wav -`,
		},
		{
			name:   "[AUDIO] audio will be transcoded to PCMU/16000, video will be skipped",
			source: "rtsp://example.com#audio=pcmu/16000",
			expect: `ffmpeg -hide_banner -allowed_media_types audio -fflags nobuffer -flags low_delay -timeout 5000000 -user_agent go2rtc/ffmpeg -rtsp_flags prefer_tcp -i rtsp://example.com -c:a pcm_mulaw -ar:a 16000 -ac:a 1 -vn -f wav -`,
		},
		{
			name:   "[AUDIO] audio will be transcoded to PCMU/48000, video will be skipped",
			source: "rtsp://example.com#audio=pcmu/48000",
			expect: `ffmpeg -hide_banner -allowed_media_types audio -fflags nobuffer -flags low_delay -timeout 5000000 -user_agent go2rtc/ffmpeg -rtsp_flags prefer_tcp -i rtsp://example.com -c:a pcm_mulaw -ar:a 48000 -ac:a 1 -vn -f wav -`,
		},
		{
			name:   "[AUDIO] audio will be transcoded to PCMA, video will be skipped",
			source: "rtsp://example.com#audio=pcma",
			expect: `ffmpeg -hide_banner -allowed_media_types audio -fflags nobuffer -flags low_delay -timeout 5000000 -user_agent go2rtc/ffmpeg -rtsp_flags prefer_tcp -i rtsp://example.com -c:a pcm_alaw -ar:a 8000 -ac:a 1 -vn -f wav -`,
		},
		{
			name:   "[AUDIO] audio will be transcoded to PCMA/16000, video will be skipped",
			source: "rtsp://example.com#audio=pcma/16000",
			expect: `ffmpeg -hide_banner -allowed_media_types audio -fflags nobuffer -flags low_delay -timeout 5000000 -user_agent go2rtc/ffmpeg -rtsp_flags prefer_tcp -i rtsp://example.com -c:a pcm_alaw -ar:a 16000 -ac:a 1 -vn -f wav -`,
		},
		{
			name:   "[AUDIO] audio will be transcoded to PCMA/48000, video will be skipped",
			source: "rtsp://example.com#audio=pcma/48000",
			expect: `ffmpeg -hide_banner -allowed_media_types audio -fflags nobuffer -flags low_delay -timeout 5000000 -user_agent go2rtc/ffmpeg -rtsp_flags prefer_tcp -i rtsp://example.com -c:a pcm_alaw -ar:a 48000 -ac:a 1 -vn -f wav -`,
		},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			args := parseArgs(test.source)
			require.Equal(t, test.expect, args.String())
		})
	}
}

func TestParseArgsHwVaapi(t *testing.T) {
	tests := []struct {
		name   string
		source string
		expect string
	}{
		{
			name:   "[HTTP-MJPEG] video will be transcoded to H264",
			source: "http:///example.com#video=h264#hardware=vaapi",
			expect: `ffmpeg -hide_banner -hwaccel vaapi -hwaccel_output_format vaapi -hwaccel_flags allow_profile_mismatch -fflags nobuffer -flags low_delay -i http:///example.com -c:v h264_vaapi -g 50 -bf 0 -profile:v high -level:v 4.1 -sei:v 0 -an -vf "format=vaapi|nv12,hwupload,scale_vaapi=out_color_matrix=bt709:out_range=tv:format=nv12" -user_agent ffmpeg/go2rtc -rtsp_transport tcp -f rtsp {output}`,
		},
		{
			name:   "[RTSP] video with rotation, should be transcoded, so select H264",
			source: "rtsp://example.com#video=h264#rotate=180#hardware=vaapi",
			expect: `ffmpeg -hide_banner -hwaccel vaapi -hwaccel_output_format vaapi -hwaccel_flags allow_profile_mismatch -allowed_media_types video -fflags nobuffer -flags low_delay -timeout 5000000 -user_agent go2rtc/ffmpeg -rtsp_flags prefer_tcp -i rtsp://example.com -c:v h264_vaapi -g 50 -bf 0 -profile:v high -level:v 4.1 -sei:v 0 -an -vf "format=vaapi|nv12,hwupload,transpose_vaapi=4,scale_vaapi=out_color_matrix=bt709:out_range=tv:format=nv12" -user_agent ffmpeg/go2rtc -rtsp_transport tcp -f rtsp {output}
Download .txt
gitextract_ui7an4hw/

├── .github/
│   └── workflows/
│       ├── build.yml
│       ├── gh-pages.yml
│       └── test.yml
├── .gitignore
├── LICENSE
├── README.md
├── docker/
│   ├── Dockerfile
│   ├── README.md
│   ├── hardware.Dockerfile
│   └── rockchip.Dockerfile
├── examples/
│   ├── go2rtc_hass/
│   │   └── main.go
│   ├── go2rtc_mjpeg/
│   │   └── main.go
│   ├── go2rtc_rtsp/
│   │   └── main.go
│   ├── homekit_info/
│   │   └── main.go
│   ├── mdns/
│   │   └── main.go
│   ├── mod_pinggy/
│   │   ├── go.mod
│   │   ├── go.sum
│   │   └── main.go
│   ├── onvif_client/
│   │   ├── README.md
│   │   └── main.go
│   ├── rtsp_client/
│   │   └── main.go
│   └── tutk_decoder/
│       ├── README.md
│       └── main.go
├── go.mod
├── go.sum
├── internal/
│   ├── README.md
│   ├── alsa/
│   │   ├── README.md
│   │   ├── alsa.go
│   │   └── alsa_linux.go
│   ├── api/
│   │   ├── README.md
│   │   ├── api.go
│   │   ├── config.go
│   │   ├── static.go
│   │   └── ws/
│   │       ├── README.md
│   │       └── ws.go
│   ├── app/
│   │   ├── README.md
│   │   ├── app.go
│   │   ├── config.go
│   │   ├── log.go
│   │   └── storage.go
│   ├── bubble/
│   │   ├── README.md
│   │   └── bubble.go
│   ├── debug/
│   │   ├── README.md
│   │   ├── debug.go
│   │   └── stack.go
│   ├── doorbird/
│   │   ├── README.md
│   │   └── doorbird.go
│   ├── dvrip/
│   │   ├── README.md
│   │   └── dvrip.go
│   ├── echo/
│   │   ├── README.md
│   │   └── echo.go
│   ├── eseecloud/
│   │   ├── README.md
│   │   └── eseecloud.go
│   ├── exec/
│   │   ├── README.md
│   │   └── exec.go
│   ├── expr/
│   │   ├── README.md
│   │   └── expr.go
│   ├── ffmpeg/
│   │   ├── README.md
│   │   ├── api.go
│   │   ├── device/
│   │   │   ├── README.md
│   │   │   ├── device_bsd.go
│   │   │   ├── device_darwin.go
│   │   │   ├── device_unix.go
│   │   │   ├── device_windows.go
│   │   │   └── devices.go
│   │   ├── ffmpeg.go
│   │   ├── ffmpeg_test.go
│   │   ├── hardware/
│   │   │   ├── README.md
│   │   │   ├── hardware.go
│   │   │   ├── hardware_bsd.go
│   │   │   ├── hardware_darwin.go
│   │   │   ├── hardware_unix.go
│   │   │   └── hardware_windows.go
│   │   ├── jpeg.go
│   │   ├── jpeg_test.go
│   │   ├── producer.go
│   │   ├── version.go
│   │   └── virtual/
│   │       ├── virtual.go
│   │       └── virtual_test.go
│   ├── flussonic/
│   │   ├── README.md
│   │   └── flussonic.go
│   ├── gopro/
│   │   ├── README.md
│   │   └── gopro.go
│   ├── hass/
│   │   ├── README.md
│   │   ├── api.go
│   │   └── hass.go
│   ├── hls/
│   │   ├── README.md
│   │   ├── hls.go
│   │   ├── session.go
│   │   └── ws.go
│   ├── homekit/
│   │   ├── README.md
│   │   ├── api.go
│   │   ├── homekit.go
│   │   └── server.go
│   ├── http/
│   │   ├── README.md
│   │   └── http.go
│   ├── isapi/
│   │   ├── README.md
│   │   └── init.go
│   ├── ivideon/
│   │   ├── README.md
│   │   └── ivideon.go
│   ├── kasa/
│   │   ├── README.md
│   │   └── kasa.go
│   ├── mjpeg/
│   │   ├── README.md
│   │   └── mjpeg.go
│   ├── mp4/
│   │   ├── README.md
│   │   ├── mp4.go
│   │   └── ws.go
│   ├── mpeg/
│   │   ├── README.md
│   │   └── mpeg.go
│   ├── multitrans/
│   │   ├── README.md
│   │   └── multitrans.go
│   ├── nest/
│   │   ├── README.md
│   │   └── init.go
│   ├── ngrok/
│   │   ├── README.md
│   │   └── ngrok.go
│   ├── onvif/
│   │   ├── README.md
│   │   └── onvif.go
│   ├── pinggy/
│   │   ├── README.md
│   │   └── pinggy.go
│   ├── ring/
│   │   ├── README.md
│   │   └── ring.go
│   ├── roborock/
│   │   ├── README.md
│   │   └── roborock.go
│   ├── rtmp/
│   │   ├── README.md
│   │   └── rtmp.go
│   ├── rtsp/
│   │   ├── README.md
│   │   └── rtsp.go
│   ├── srtp/
│   │   ├── README.md
│   │   └── srtp.go
│   ├── streams/
│   │   ├── README.md
│   │   ├── add_consumer.go
│   │   ├── api.go
│   │   ├── api_test.go
│   │   ├── dot.go
│   │   ├── handlers.go
│   │   ├── helpers.go
│   │   ├── play.go
│   │   ├── preload.go
│   │   ├── producer.go
│   │   ├── publish.go
│   │   ├── stream.go
│   │   ├── stream_test.go
│   │   └── streams.go
│   ├── tapo/
│   │   ├── README.md
│   │   └── tapo.go
│   ├── tuya/
│   │   ├── README.md
│   │   └── tuya.go
│   ├── v4l2/
│   │   ├── README.md
│   │   ├── v4l2.go
│   │   └── v4l2_linux.go
│   ├── webrtc/
│   │   ├── README.md
│   │   ├── candidates.go
│   │   ├── client.go
│   │   ├── client_creality.go
│   │   ├── kinesis.go
│   │   ├── milestone.go
│   │   ├── openipc.go
│   │   ├── server.go
│   │   ├── switchbot.go
│   │   ├── webrtc.go
│   │   └── webrtc_test.go
│   ├── webtorrent/
│   │   ├── README.md
│   │   ├── init.go
│   │   └── tracker.go
│   ├── wyoming/
│   │   ├── README.md
│   │   └── wyoming.go
│   ├── wyze/
│   │   ├── README.md
│   │   └── wyze.go
│   ├── xiaomi/
│   │   ├── README.md
│   │   └── xiaomi.go
│   └── yandex/
│       ├── README.md
│       ├── goloom.go
│       └── yandex.go
├── main.go
├── package.json
├── pkg/
│   ├── README.md
│   ├── aac/
│   │   ├── README.md
│   │   ├── aac.go
│   │   ├── aac_test.go
│   │   ├── adts.go
│   │   ├── consumer.go
│   │   ├── producer.go
│   │   ├── rtp.go
│   │   └── rtp_test.go
│   ├── alsa/
│   │   ├── README.md
│   │   ├── capture_linux.go
│   │   ├── device/
│   │   │   ├── asound_32bit.go
│   │   │   ├── asound_64bit.go
│   │   │   ├── asound_arch.c
│   │   │   ├── asound_mipsle.go
│   │   │   ├── device_linux.go
│   │   │   └── ioctl_linux.go
│   │   ├── open_linux.go
│   │   └── playback_linux.go
│   ├── ascii/
│   │   ├── README.md
│   │   └── ascii.go
│   ├── bits/
│   │   ├── reader.go
│   │   └── writer.go
│   ├── bubble/
│   │   ├── client.go
│   │   └── producer.go
│   ├── core/
│   │   ├── README.md
│   │   ├── codec.go
│   │   ├── connection.go
│   │   ├── core.go
│   │   ├── core_test.go
│   │   ├── helpers.go
│   │   ├── listener.go
│   │   ├── media.go
│   │   ├── media_test.go
│   │   ├── node.go
│   │   ├── readbuffer.go
│   │   ├── readbuffer_test.go
│   │   ├── slices.go
│   │   ├── track.go
│   │   ├── track_test.go
│   │   ├── waiter.go
│   │   ├── worker.go
│   │   └── writebuffer.go
│   ├── creds/
│   │   ├── README.md
│   │   ├── creds.go
│   │   ├── secrets.go
│   │   └── secrets_test.go
│   ├── debug/
│   │   ├── conn.go
│   │   └── debug.go
│   ├── doorbird/
│   │   └── backchannel.go
│   ├── dvrip/
│   │   ├── backchannel.go
│   │   ├── client.go
│   │   ├── dvrip.go
│   │   └── producer.go
│   ├── eseecloud/
│   │   └── eseecloud.go
│   ├── expr/
│   │   ├── expr.go
│   │   └── expr_test.go
│   ├── ffmpeg/
│   │   ├── README.md
│   │   └── ffmpeg.go
│   ├── flussonic/
│   │   └── flussonic.go
│   ├── flv/
│   │   ├── amf/
│   │   │   ├── amf.go
│   │   │   └── amf_test.go
│   │   ├── consumer.go
│   │   ├── flv_test.go
│   │   ├── muxer.go
│   │   └── producer.go
│   ├── gopro/
│   │   ├── discovery.go
│   │   └── producer.go
│   ├── h264/
│   │   ├── README.md
│   │   ├── annexb/
│   │   │   ├── annexb.go
│   │   │   └── annexb_test.go
│   │   ├── avc.go
│   │   ├── avcc.go
│   │   ├── h264.go
│   │   ├── h264_test.go
│   │   ├── mpeg4.go
│   │   ├── payloader.go
│   │   ├── rtp.go
│   │   └── sps.go
│   ├── h265/
│   │   ├── README.md
│   │   ├── avc.go
│   │   ├── avcc.go
│   │   ├── h265_test.go
│   │   ├── helper.go
│   │   ├── mpeg4.go
│   │   ├── payloader.go
│   │   ├── rtp.go
│   │   └── sps.go
│   ├── hap/
│   │   ├── README.md
│   │   ├── accessory.go
│   │   ├── camera/
│   │   │   ├── README.md
│   │   │   ├── accessory.go
│   │   │   ├── accessory_test.go
│   │   │   ├── ch114_supported_video.go
│   │   │   ├── ch115_supported_audio.go
│   │   │   ├── ch116_supported_rtp.go
│   │   │   ├── ch117_selected_stream.go
│   │   │   ├── ch118_setup_endpoints.go
│   │   │   ├── ch120_streaming_status.go
│   │   │   ├── ch130_data_stream_transport.go
│   │   │   ├── ch131_data_stream.go
│   │   │   ├── ch205.go
│   │   │   ├── ch206.go
│   │   │   ├── ch207.go
│   │   │   ├── ch209.go
│   │   │   └── stream.go
│   │   ├── chacha20poly1305/
│   │   │   └── chacha20poly1305.go
│   │   ├── character.go
│   │   ├── client.go
│   │   ├── client_http.go
│   │   ├── client_pairing.go
│   │   ├── conn.go
│   │   ├── curve25519/
│   │   │   └── curve25519.go
│   │   ├── ed25519/
│   │   │   └── ed25519.go
│   │   ├── hds/
│   │   │   ├── hds.go
│   │   │   └── hds_test.go
│   │   ├── helpers.go
│   │   ├── hkdf/
│   │   │   └── hkdf.go
│   │   ├── server.go
│   │   ├── setup/
│   │   │   ├── setup.go
│   │   │   └── setup_test.go
│   │   └── tlv8/
│   │       ├── tlv8.go
│   │       └── tlv8_test.go
│   ├── hass/
│   │   ├── api.go
│   │   └── client.go
│   ├── hls/
│   │   ├── producer.go
│   │   └── reader.go
│   ├── homekit/
│   │   ├── consumer.go
│   │   ├── helpers.go
│   │   ├── log/
│   │   │   └── debug.go
│   │   ├── producer.go
│   │   ├── proxy.go
│   │   └── server.go
│   ├── image/
│   │   └── producer.go
│   ├── ioctl/
│   │   ├── README.md
│   │   ├── ioctl.go
│   │   ├── ioctl_be.go
│   │   ├── ioctl_le.go
│   │   ├── ioctl_linux.go
│   │   └── ioctl_test.go
│   ├── isapi/
│   │   ├── backchannel.go
│   │   └── client.go
│   ├── iso/
│   │   ├── atoms.go
│   │   ├── codecs.go
│   │   ├── iso.go
│   │   └── reader.go
│   ├── ivideon/
│   │   └── ivideon.go
│   ├── kasa/
│   │   └── producer.go
│   ├── magic/
│   │   ├── bitstream/
│   │   │   └── producer.go
│   │   ├── keyframe.go
│   │   ├── mjpeg/
│   │   │   └── producer.go
│   │   └── producer.go
│   ├── mdns/
│   │   ├── README.md
│   │   ├── client.go
│   │   ├── mdns_test.go
│   │   ├── server.go
│   │   ├── syscall.go
│   │   ├── syscall_bsd.go
│   │   └── syscall_windows.go
│   ├── mjpeg/
│   │   ├── README.md
│   │   ├── consumer.go
│   │   ├── helpers.go
│   │   ├── jpeg.go
│   │   ├── mjpeg_test.go
│   │   ├── rfc2435.go
│   │   ├── rtp.go
│   │   └── writer.go
│   ├── mp4/
│   │   ├── README.md
│   │   ├── consumer.go
│   │   ├── demuxer.go
│   │   ├── helpers.go
│   │   ├── keyframe.go
│   │   ├── mime.go
│   │   └── muxer.go
│   ├── mpegts/
│   │   ├── README.md
│   │   ├── checksum.go
│   │   ├── consumer.go
│   │   ├── demuxer.go
│   │   ├── muxer.go
│   │   ├── opus.go
│   │   └── producer.go
│   ├── mpjpeg/
│   │   ├── multipart.go
│   │   └── producer.go
│   ├── mqtt/
│   │   ├── client.go
│   │   └── message.go
│   ├── multitrans/
│   │   └── client.go
│   ├── nest/
│   │   ├── api.go
│   │   └── client.go
│   ├── ngrok/
│   │   └── ngrok.go
│   ├── onvif/
│   │   ├── README.md
│   │   ├── client.go
│   │   ├── envelope.go
│   │   ├── helpers.go
│   │   ├── onvif_test.go
│   │   └── server.go
│   ├── opus/
│   │   ├── README.md
│   │   ├── homekit.go
│   │   └── opus.go
│   ├── pcm/
│   │   ├── backchannel.go
│   │   ├── flac.go
│   │   ├── handlers.go
│   │   ├── pcm.go
│   │   ├── pcm_test.go
│   │   ├── pcma.go
│   │   ├── pcmu.go
│   │   ├── producer.go
│   │   ├── producer_sync.go
│   │   ├── s16le/
│   │   │   └── s16le.go
│   │   └── v1/
│   │       ├── pcm.go
│   │       └── pcm_test.go
│   ├── pinggy/
│   │   └── pinggy.go
│   ├── probe/
│   │   └── consumer.go
│   ├── ring/
│   │   ├── api.go
│   │   ├── client.go
│   │   ├── snapshot.go
│   │   └── ws.go
│   ├── roborock/
│   │   ├── api.go
│   │   ├── client.go
│   │   ├── iot/
│   │   │   ├── client.go
│   │   │   └── crypto.go
│   │   └── producer.go
│   ├── rtmp/
│   │   ├── README.md
│   │   ├── client.go
│   │   ├── conn.go
│   │   ├── flv.go
│   │   └── server.go
│   ├── rtsp/
│   │   ├── README.md
│   │   ├── client.go
│   │   ├── client_test.go
│   │   ├── conn.go
│   │   ├── consumer.go
│   │   ├── helpers.go
│   │   ├── producer.go
│   │   ├── rtsp_test.go
│   │   └── server.go
│   ├── shell/
│   │   ├── command.go
│   │   ├── procattr.go
│   │   ├── procattr_linux.go
│   │   ├── shell.go
│   │   └── shell_test.go
│   ├── srtp/
│   │   ├── server.go
│   │   └── session.go
│   ├── tapo/
│   │   ├── backchannel.go
│   │   ├── client.go
│   │   └── producer.go
│   ├── tcp/
│   │   ├── auth.go
│   │   ├── dial.go
│   │   ├── request.go
│   │   ├── textproto.go
│   │   ├── textproto_test.go
│   │   └── websocket/
│   │       ├── client.go
│   │       └── dial.go
│   ├── tutk/
│   │   ├── codec.go
│   │   ├── conn.go
│   │   ├── crypto.go
│   │   ├── crypto_test.go
│   │   ├── dtls/
│   │   │   ├── auth.go
│   │   │   ├── cipher.go
│   │   │   ├── conn_dtls.go
│   │   │   └── dtls.go
│   │   ├── frame.go
│   │   ├── helpers.go
│   │   ├── session0.go
│   │   ├── session16.go
│   │   └── session25.go
│   ├── tuya/
│   │   ├── README.md
│   │   ├── client.go
│   │   ├── cloud_api.go
│   │   ├── helper.go
│   │   ├── interface.go
│   │   ├── mqtt.go
│   │   └── smart_api.go
│   ├── v4l2/
│   │   ├── device/
│   │   │   ├── README.md
│   │   │   ├── device.go
│   │   │   ├── formats.go
│   │   │   ├── videodev2_386.go
│   │   │   ├── videodev2_arch.c
│   │   │   ├── videodev2_arm.go
│   │   │   ├── videodev2_mipsle.go
│   │   │   └── videodev2_x64.go
│   │   └── producer.go
│   ├── wav/
│   │   ├── backchannel.go
│   │   ├── producer.go
│   │   └── wav.go
│   ├── webrtc/
│   │   ├── README.md
│   │   ├── api.go
│   │   ├── client.go
│   │   ├── client_test.go
│   │   ├── conn.go
│   │   ├── consumer.go
│   │   ├── helpers.go
│   │   ├── producer.go
│   │   ├── server.go
│   │   ├── track.go
│   │   └── webrtc_test.go
│   ├── webtorrent/
│   │   ├── client.go
│   │   ├── crypto.go
│   │   └── server.go
│   ├── wyoming/
│   │   ├── README.md
│   │   ├── api.go
│   │   ├── backchannel.go
│   │   ├── expr.go
│   │   ├── mic.go
│   │   ├── producer.go
│   │   ├── satellite.go
│   │   ├── snd.go
│   │   ├── wakeword.go
│   │   └── wyoming.go
│   ├── wyze/
│   │   ├── backchannel.go
│   │   ├── client.go
│   │   ├── cloud.go
│   │   └── producer.go
│   ├── xiaomi/
│   │   ├── cloud.go
│   │   ├── crypto/
│   │   │   └── crypto.go
│   │   ├── legacy/
│   │   │   ├── client.go
│   │   │   └── producer.go
│   │   ├── miss/
│   │   │   ├── backchannel.go
│   │   │   ├── client.go
│   │   │   ├── cs2/
│   │   │   │   └── conn.go
│   │   │   └── producer.go
│   │   └── producer.go
│   ├── xnet/
│   │   ├── net.go
│   │   └── tls/
│   │       └── tls.go
│   ├── y4m/
│   │   ├── README.md
│   │   ├── consumer.go
│   │   ├── producer.go
│   │   └── y4m.go
│   ├── yaml/
│   │   ├── yaml.go
│   │   └── yaml_test.go
│   └── yandex/
│       └── session.go
├── scripts/
│   ├── README.md
│   ├── build.cmd
│   └── build.sh
├── website/
│   ├── .vitepress/
│   │   └── config.js
│   ├── README.md
│   ├── api/
│   │   ├── index.html
│   │   └── openapi.yaml
│   ├── manifest.json
│   └── webtorrent/
│       └── index.html
└── www/
    ├── README.md
    ├── add.html
    ├── config.html
    ├── hls.html
    ├── index.html
    ├── links.html
    ├── log.html
    ├── main.js
    ├── net.html
    ├── schema.json
    ├── static.go
    ├── stream.html
    ├── video-rtc.js
    ├── video-stream.js
    ├── webrtc-sync.html
    └── webrtc.html
Download .txt
Showing preview only (283K chars total). Download the full file or copy to clipboard to get everything.
SYMBOL INDEX (3570 symbols across 416 files)

FILE: examples/go2rtc_hass/main.go
  function main (line 11) | func main() {

FILE: examples/go2rtc_mjpeg/main.go
  function main (line 14) | func main() {

FILE: examples/go2rtc_rtsp/main.go
  function main (line 10) | func main() {

FILE: examples/homekit_info/main.go
  function main (line 86) | func main() {

FILE: examples/mdns/main.go
  function main (line 10) | func main() {

FILE: examples/mod_pinggy/main.go
  function main (line 10) | func main() {

FILE: examples/onvif_client/main.go
  function main (line 11) | func main() {

FILE: examples/rtsp_client/main.go
  function main (line 12) | func main() {

FILE: examples/tutk_decoder/main.go
  function main (line 14) | func main() {
  type item (line 61) | type item struct

FILE: internal/alsa/alsa.go
  function Init (line 5) | func Init() {

FILE: internal/alsa/alsa_linux.go
  function Init (line 18) | func Init() {
  function apiAlsa (line 24) | func apiAlsa(w http.ResponseWriter, r *http.Request) {
  function formatsToString (line 66) | func formatsToString(formats []byte) string {

FILE: internal/api/api.go
  function Init (line 21) | func Init() {
  function listen (line 93) | func listen(network, address string) {
  function tlsListen (line 111) | func tlsListen(network, address, certFile, keyFile string) {
  constant MimeJSON (line 147) | MimeJSON = "application/json"
  constant MimeText (line 148) | MimeText = "text/plain"
  function HandleFunc (line 156) | func HandleFunc(pattern string, handler http.HandlerFunc) {
  function ResponseJSON (line 170) | func ResponseJSON(w http.ResponseWriter, v any) {
  function ResponsePrettyJSON (line 175) | func ResponsePrettyJSON(w http.ResponseWriter, v any) {
  function Response (line 182) | func Response(w http.ResponseWriter, body any, contentType string) {
  constant StreamNotFound (line 195) | StreamNotFound = "stream not found"
  function middlewareLog (line 201) | func middlewareLog(next http.Handler) http.Handler {
  function isLoopback (line 208) | func isLoopback(remoteAddr string) bool {
  function middlewareAuth (line 212) | func middlewareAuth(username, password string, localAuth bool, next http...
  function middlewareCORS (line 227) | func middlewareCORS(next http.Handler) http.Handler {
  function apiHandler (line 238) | func apiHandler(w http.ResponseWriter, r *http.Request) {
  function exitHandler (line 246) | func exitHandler(w http.ResponseWriter, r *http.Request) {
  function restartHandler (line 264) | func restartHandler(w http.ResponseWriter, r *http.Request) {
  function logHandler (line 281) | func logHandler(w http.ResponseWriter, r *http.Request) {
  type Source (line 295) | type Source struct
  function ResponseSources (line 303) | func ResponseSources(w http.ResponseWriter, sources []*Source) {
  function Error (line 317) | func Error(w http.ResponseWriter, err error) {

FILE: internal/api/config.go
  function configHandler (line 12) | func configHandler(w http.ResponseWriter, r *http.Request) {
  function mergeYAML (line 57) | func mergeYAML(file1 string, yaml2 []byte) ([]byte, error) {
  function merge (line 83) | func merge(dst, src map[string]any) map[string]any {

FILE: internal/api/static.go
  function initStatic (line 9) | func initStatic(staticDir string) {

FILE: internal/api/ws/ws.go
  function Init (line 19) | func Init() {
  type Message (line 38) | type Message struct
    method String (line 43) | func (m *Message) String() (value string) {
    method Unmarshal (line 50) | func (m *Message) Unmarshal(v any) error {
  type WSHandler (line 58) | type WSHandler
  function HandleFunc (line 60) | func HandleFunc(msgType string, handler WSHandler) {
  function initWS (line 66) | func initWS(origin string) {
  function apiWS (line 102) | func apiWS(w http.ResponseWriter, r *http.Request) {
  type Transport (line 148) | type Transport struct
    method OnWrite (line 162) | func (t *Transport) OnWrite(f func(msg any) error) {
    method Write (line 171) | func (t *Transport) Write(msg any) {
    method Close (line 177) | func (t *Transport) Close() {
    method OnChange (line 186) | func (t *Transport) OnChange(f func()) {
    method OnClose (line 192) | func (t *Transport) OnClose(f func()) {
    method WithContext (line 203) | func (t *Transport) WithContext(f func(ctx map[any]any)) {
    method Writer (line 212) | func (t *Transport) Writer() io.Writer {
  type writer (line 216) | type writer struct
    method Write (line 220) | func (w *writer) Write(p []byte) (n int, err error) {

FILE: internal/app/app.go
  constant usage (line 20) | usage = `Usage of go2rtc:
  function Init (line 27) | func Init() {
  function readRevisionTime (line 92) | func readRevisionTime() (revision, vcsTime string) {

FILE: internal/app/config.go
  function LoadConfig (line 14) | func LoadConfig(v any) {
  function PatchConfig (line 24) | func PatchConfig(path []string, value any) error {
  type flagConfig (line 43) | type flagConfig
    method String (line 45) | func (c *flagConfig) String() string {
    method Set (line 49) | func (c *flagConfig) Set(value string) error {
  function initConfig (line 56) | func initConfig(confs flagConfig) {
  function parseConfString (line 97) | func parseConfString(s string) []byte {

FILE: internal/app/log.go
  function GetLogger (line 16) | func GetLogger(module string) zerolog.Logger {
  function initLogger (line 35) | func initLogger() {
  constant chunkCount (line 116) | chunkCount = 16
  constant chunkSize (line 117) | chunkSize  = 1 << 16
  type circularBuffer (line 120) | type circularBuffer struct
    method Write (line 133) | func (b *circularBuffer) Write(p []byte) (n int, err error) {
    method WriteTo (line 165) | func (b *circularBuffer) WriteTo(w io.Writer) (n int64, err error) {
    method Reset (line 185) | func (b *circularBuffer) Reset() {
  function newBuffer (line 126) | func newBuffer() *circularBuffer {

FILE: internal/app/storage.go
  function initStorage (line 10) | func initStorage() {
  function loadEnv (line 15) | func loadEnv(data []byte) {
  type envStorage (line 34) | type envStorage struct
    method SetValue (line 39) | func (s *envStorage) SetValue(name, value string) error {
    method GetValue (line 51) | func (s *envStorage) GetValue(name string) (value string, ok bool) {

FILE: internal/bubble/bubble.go
  function Init (line 9) | func Init() {

FILE: internal/debug/debug.go
  function Init (line 7) | func Init() {

FILE: internal/debug/stack.go
  function stackHandler (line 36) | func stackHandler(w http.ResponseWriter, r *http.Request) {

FILE: internal/doorbird/doorbird.go
  function Init (line 11) | func Init() {

FILE: internal/dvrip/dvrip.go
  function Init (line 16) | func Init() {
  constant Port (line 23) | Port = 34569
  function apiDvrip (line 25) | func apiDvrip(w http.ResponseWriter, r *http.Request) {
  function discover (line 35) | func discover() ([]*api.Source, error) {
  function sendBroadcasts (line 71) | func sendBroadcasts(conn *net.UDPConn) {
  type Message (line 89) | type Message struct
  type NetCommon (line 95) | type NetCommon struct
  function getResponses (line 117) | func getResponses(conn *net.UDPConn) (infos []*NetCommon) {
  function hexToDecimalBytes (line 155) | func hexToDecimalBytes(hexIP string) (string, error) {

FILE: internal/echo/echo.go
  function Init (line 14) | func Init() {

FILE: internal/eseecloud/eseecloud.go
  function Init (line 8) | func Init() {

FILE: internal/exec/exec.go
  function Init (line 29) | func Init() {
  function execHandle (line 66) | func execHandle(rawURL string) (prod core.Producer, err error) {
  function handlePipe (line 131) | func handlePipe(source string, cmd *shell.Command) (core.Producer, error) {
  function handleRTSP (line 170) | func handleRTSP(source string, cmd *shell.Command, path string, timeout ...
  type logWriter (line 224) | type logWriter struct
    method String (line 230) | func (l *logWriter) String() string {
    method Write (line 237) | func (l *logWriter) Write(p []byte) (n int, err error) {
  function trimSpace (line 250) | func trimSpace(b []byte) []byte {
  function setRemoteInfo (line 269) | func setRemoteInfo(info core.Info, source string, args []string) {

FILE: internal/expr/expr.go
  function Init (line 11) | func Init() {

FILE: internal/ffmpeg/api.go
  function apiFFmpeg (line 10) | func apiFFmpeg(w http.ResponseWriter, r *http.Request) {

FILE: internal/ffmpeg/device/device_bsd.go
  function queryToInput (line 16) | func queryToInput(query url.Values) string {
  function initDevices (line 49) | func initDevices() {

FILE: internal/ffmpeg/device/device_darwin.go
  function queryToInput (line 15) | func queryToInput(query url.Values) string {
  function initDevices (line 46) | func initDevices() {

FILE: internal/ffmpeg/device/device_unix.go
  function queryToInput (line 16) | func queryToInput(query url.Values) string {
  function initDevices (line 50) | func initDevices() {

FILE: internal/ffmpeg/device/device_windows.go
  function queryToInput (line 14) | func queryToInput(query url.Values) string {
  function initDevices (line 64) | func initDevices() {

FILE: internal/ffmpeg/device/devices.go
  function Init (line 12) | func Init(bin string) {
  function GetInput (line 18) | func GetInput(src string) string {
  function apiDevices (line 35) | func apiDevices(w http.ResponseWriter, r *http.Request) {
  function indexToItem (line 41) | func indexToItem(items []string, index string) string {

FILE: internal/ffmpeg/ffmpeg.go
  function Init (line 19) | func Init() {
  function configTemplate (line 155) | func configTemplate(template string) string {
  function inputTemplate (line 165) | func inputTemplate(name, s string, query url.Values) string {
  function parseArgs (line 182) | func parseArgs(s string) *ffmpeg.Args {

FILE: internal/ffmpeg/ffmpeg_test.go
  function TestParseArgsFile (line 10) | func TestParseArgsFile(t *testing.T) {
  function TestParseArgsDevice (line 55) | func TestParseArgsDevice(t *testing.T) {
  function TestParseArgsIpCam (line 85) | func TestParseArgsIpCam(t *testing.T) {
  function TestParseArgsAudio (line 140) | func TestParseArgsAudio(t *testing.T) {
  function TestParseArgsHwVaapi (line 200) | func TestParseArgsHwVaapi(t *testing.T) {
  function _TestParseArgsHwV4l2m2m (line 240) | func _TestParseArgsHwV4l2m2m(t *testing.T) {
  function TestParseArgsHwRKMPP (line 258) | func TestParseArgsHwRKMPP(t *testing.T) {
  function _TestParseArgsHwCuda (line 288) | func _TestParseArgsHwCuda(t *testing.T) {
  function _TestParseArgsHwDxva2 (line 306) | func _TestParseArgsHwDxva2(t *testing.T) {
  function _TestParseArgsHwVideotoolbox (line 328) | func _TestParseArgsHwVideotoolbox(t *testing.T) {
  function TestDeckLink (line 346) | func TestDeckLink(t *testing.T) {
  function TestDrawText (line 351) | func TestDrawText(t *testing.T) {
  function TestVersion (line 378) | func TestVersion(t *testing.T) {

FILE: internal/ffmpeg/hardware/hardware.go
  constant EngineSoftware (line 13) | EngineSoftware     = "software"
  constant EngineVAAPI (line 14) | EngineVAAPI        = "vaapi"
  constant EngineV4L2M2M (line 15) | EngineV4L2M2M      = "v4l2m2m"
  constant EngineCUDA (line 16) | EngineCUDA         = "cuda"
  constant EngineDXVA2 (line 17) | EngineDXVA2        = "dxva2"
  constant EngineVideoToolbox (line 18) | EngineVideoToolbox = "videotoolbox"
  constant EngineRKMPP (line 19) | EngineRKMPP        = "rkmpp"
  function Init (line 22) | func Init(bin string) {
  function MakeHardware (line 30) | func MakeHardware(args *ffmpeg.Args, engine string, defaults map[string]...
  function run (line 164) | func run(bin string, args string) bool {
  function runToString (line 169) | func runToString(bin string, args string) string {
  function cut (line 177) | func cut(s string, sep byte, pos int) string {
  function fixPixelFormat (line 195) | func fixPixelFormat(args *ffmpeg.Args) {

FILE: internal/ffmpeg/hardware/hardware_bsd.go
  constant ProbeV4L2M2MH264 (line 12) | ProbeV4L2M2MH264 = "-f lavfi -i testsrc2 -t 1 -c h264_v4l2m2m -f null -"
  constant ProbeV4L2M2MH265 (line 13) | ProbeV4L2M2MH265 = "-f lavfi -i testsrc2 -t 1 -c hevc_v4l2m2m -f null -"
  constant ProbeRKMPPH264 (line 14) | ProbeRKMPPH264   = "-f lavfi -i testsrc2 -t 1 -c h264_rkmpp_encoder -f n...
  constant ProbeRKMPPH265 (line 15) | ProbeRKMPPH265   = "-f lavfi -i testsrc2 -t 1 -c hevc_rkmpp_encoder -f n...
  function ProbeAll (line 18) | func ProbeAll(bin string) []*api.Source {
  function ProbeHardware (line 39) | func ProbeHardware(bin, name string) string {

FILE: internal/ffmpeg/hardware/hardware_darwin.go
  constant ProbeVideoToolboxH264 (line 9) | ProbeVideoToolboxH264 = "-f lavfi -i testsrc2=size=svga -t 1 -c h264_vid...
  constant ProbeVideoToolboxH265 (line 10) | ProbeVideoToolboxH265 = "-f lavfi -i testsrc2=size=svga -t 1 -c hevc_vid...
  function ProbeAll (line 12) | func ProbeAll(bin string) []*api.Source {
  function ProbeHardware (line 25) | func ProbeHardware(bin, name string) string {

FILE: internal/ffmpeg/hardware/hardware_unix.go
  constant ProbeV4L2M2MH264 (line 12) | ProbeV4L2M2MH264 = "-f lavfi -i testsrc2 -t 1 -c h264_v4l2m2m -f null -"
  constant ProbeV4L2M2MH265 (line 13) | ProbeV4L2M2MH265 = "-f lavfi -i testsrc2 -t 1 -c hevc_v4l2m2m -f null -"
  constant ProbeRKMPPH264 (line 14) | ProbeRKMPPH264   = "-f lavfi -i testsrc2 -t 1 -c h264_rkmpp -f null -"
  constant ProbeRKMPPH265 (line 15) | ProbeRKMPPH265   = "-f lavfi -i testsrc2 -t 1 -c hevc_rkmpp -f null -"
  constant ProbeRKMPPJPEG (line 16) | ProbeRKMPPJPEG   = "-f lavfi -i testsrc2 -t 1 -c mjpeg_rkmpp -f null -"
  constant ProbeVAAPIH264 (line 17) | ProbeVAAPIH264   = "-init_hw_device vaapi -f lavfi -i testsrc2 -t 1 -vf ...
  constant ProbeVAAPIH265 (line 18) | ProbeVAAPIH265   = "-init_hw_device vaapi -f lavfi -i testsrc2 -t 1 -vf ...
  constant ProbeVAAPIJPEG (line 19) | ProbeVAAPIJPEG   = "-init_hw_device vaapi -f lavfi -i testsrc2 -t 1 -vf ...
  constant ProbeCUDAH264 (line 20) | ProbeCUDAH264    = "-init_hw_device cuda -f lavfi -i testsrc2 -t 1 -c h2...
  constant ProbeCUDAH265 (line 21) | ProbeCUDAH265    = "-init_hw_device cuda -f lavfi -i testsrc2 -t 1 -c he...
  function ProbeAll (line 24) | func ProbeAll(bin string) []*api.Source {
  function ProbeHardware (line 74) | func ProbeHardware(bin, name string) string {

FILE: internal/ffmpeg/hardware/hardware_windows.go
  constant ProbeDXVA2H264 (line 7) | ProbeDXVA2H264 = "-init_hw_device dxva2 -f lavfi -i testsrc2 -t 1 -c h26...
  constant ProbeDXVA2H265 (line 8) | ProbeDXVA2H265 = "-init_hw_device dxva2 -f lavfi -i testsrc2 -t 1 -c hev...
  constant ProbeDXVA2JPEG (line 9) | ProbeDXVA2JPEG = "-init_hw_device dxva2 -f lavfi -i testsrc2 -t 1 -c mjp...
  constant ProbeCUDAH264 (line 10) | ProbeCUDAH264 = "-init_hw_device cuda -f lavfi -i testsrc2 -t 1 -c h264_...
  constant ProbeCUDAH265 (line 11) | ProbeCUDAH265 = "-init_hw_device cuda -f lavfi -i testsrc2 -t 1 -c hevc_...
  function ProbeAll (line 13) | func ProbeAll(bin string) []*api.Source {
  function ProbeHardware (line 38) | func ProbeHardware(bin, name string) string {

FILE: internal/ffmpeg/jpeg.go
  function JPEGWithQuery (line 15) | func JPEGWithQuery(b []byte, query url.Values) ([]byte, error) {
  function JPEGWithScale (line 20) | func JPEGWithScale(b []byte, width, height int) ([]byte, error) {
  function transcode (line 26) | func transcode(b []byte, args string) ([]byte, error) {
  function defaultArgs (line 33) | func defaultArgs() *ffmpeg.Args {
  function parseQuery (line 43) | func parseQuery(query url.Values) *ffmpeg.Args {

FILE: internal/ffmpeg/jpeg_test.go
  function TestParseQuery (line 10) | func TestParseQuery(t *testing.T) {

FILE: internal/ffmpeg/producer.go
  type Producer (line 15) | type Producer struct
    method Start (line 61) | func (p *Producer) Start() error {
    method Stop (line 78) | func (p *Producer) Stop() error {
    method MarshalJSON (line 85) | func (p *Producer) MarshalJSON() ([]byte, error) {
    method newURL (line 92) | func (p *Producer) newURL() string {
  function NewProducer (line 23) | func NewProducer(url string) (core.Producer, error) {

FILE: internal/ffmpeg/version.go
  function Version (line 16) | func Version() (string, error) {

FILE: internal/ffmpeg/virtual/virtual.go
  function GetInput (line 7) | func GetInput(src string) string {
  function GetInputTTS (line 64) | func GetInputTTS(src string) string {

FILE: internal/ffmpeg/virtual/virtual_test.go
  function TestGetInput (line 9) | func TestGetInput(t *testing.T) {
  function TestGetInputTTS (line 17) | func TestGetInputTTS(t *testing.T) {

FILE: internal/flussonic/flussonic.go
  function Init (line 8) | func Init() {

FILE: internal/gopro/gopro.go
  function Init (line 12) | func Init() {
  function apiGoPro (line 20) | func apiGoPro(w http.ResponseWriter, r *http.Request) {

FILE: internal/hass/api.go
  function apiOK (line 15) | func apiOK(w http.ResponseWriter, r *http.Request) {
  function apiStream (line 19) | func apiStream(w http.ResponseWriter, r *http.Request) {
  function HassioAddr (line 77) | func HassioAddr() string {
  type addJSON (line 96) | type addJSON struct

FILE: internal/hass/hass.go
  function Init (line 22) | func Init() {
  function importConfig (line 102) | func importConfig(config string) error {
  function importWebRTC (line 193) | func importWebRTC(token string) error {

FILE: internal/hls/hls.go
  function Init (line 18) | func Init() {
  constant keepalive (line 36) | keepalive = 5 * time.Second
  function handlerStream (line 42) | func handlerStream(w http.ResponseWriter, r *http.Request) {
  function handlerPlaylist (line 100) | func handlerPlaylist(w http.ResponseWriter, r *http.Request) {
  function handlerSegmentTS (line 123) | func handlerSegmentTS(w http.ResponseWriter, r *http.Request) {
  function handlerInit (line 155) | func handlerInit(w http.ResponseWriter, r *http.Request) {
  function handlerSegmentMP4 (line 185) | func handlerSegmentMP4(w http.ResponseWriter, r *http.Request) {

FILE: internal/hls/session.go
  type Session (line 14) | type Session struct
    method Write (line 56) | func (s *Session) Write(p []byte) (n int, err error) {
    method Run (line 67) | func (s *Session) Run() {
    method Main (line 71) | func (s *Session) Main() []byte {
    method Playlist (line 85) | func (s *Session) Playlist() []byte {
    method Init (line 89) | func (s *Session) Init() (init []byte) {
    method Segment (line 106) | func (s *Session) Segment() (segment []byte) {
  function NewSession (line 25) | func NewSession(cons core.Consumer) *Session {

FILE: internal/hls/ws.go
  function handlerWSHLS (line 13) | func handlerWSHLS(tr *ws.Transport, msg *ws.Message) error {

FILE: internal/homekit/api.go
  function apiDiscovery (line 18) | func apiDiscovery(w http.ResponseWriter, r *http.Request) {
  function apiHomekit (line 45) | func apiHomekit(w http.ResponseWriter, r *http.Request) {
  function apiHomekitAccessories (line 78) | func apiHomekitAccessories(w http.ResponseWriter, r *http.Request) {
  function discovery (line 109) | func discovery() ([]*api.Source, error) {
  function apiPair (line 140) | func apiPair(id, url string) error {
  function apiUnpair (line 151) | func apiUnpair(id string) error {
  function findHomeKitURLs (line 171) | func findHomeKitURLs() map[string]*url.URL {

FILE: internal/homekit/homekit.go
  function Init (line 20) | func Init() {
  function streamHandler (line 131) | func streamHandler(rawURL string) (core.Producer, error) {
  function resolve (line 148) | func resolve(host string) *server {
  function hapHandler (line 160) | func hapHandler(w http.ResponseWriter, r *http.Request) {
  function findHomeKitURL (line 172) | func findHomeKitURL(sources []string) string {
  function parseBitrate (line 192) | func parseBitrate(s string) int {

FILE: internal/homekit/server.go
  type server (line 32) | type server struct
    method MarshalJSON (line 47) | func (s *server) MarshalJSON() ([]byte, error) {
    method Handle (line 70) | func (s *server) Handle(w http.ResponseWriter, r *http.Request) {
    method AddConn (line 147) | func (s *server) AddConn(v any) {
    method DelConn (line 154) | func (s *server) DelConn(v any) {
    method UpdateStatus (line 163) | func (s *server) UpdateStatus() {
    method pairIndex (line 172) | func (s *server) pairIndex(id string) int {
    method GetPair (line 182) | func (s *server) GetPair(id string) []byte {
    method AddPair (line 194) | func (s *server) AddPair(id string, public []byte, permissions byte) {
    method DelPair (line 208) | func (s *server) DelPair(id string) {
    method PatchConfig (line 220) | func (s *server) PatchConfig() {
    method GetAccessories (line 228) | func (s *server) GetAccessories(_ net.Conn) []*hap.Accessory {
    method GetCharacteristic (line 232) | func (s *server) GetCharacteristic(conn net.Conn, aid uint8, iid uint6...
    method SetCharacteristic (line 260) | func (s *server) SetCharacteristic(conn net.Conn, aid uint8, iid uint6...
    method GetImage (line 327) | func (s *server) GetImage(conn net.Conn, width, height int) []byte {
  type logger (line 131) | type logger struct
    method String (line 135) | func (l logger) String() string {
  function calcName (line 354) | func calcName(name, seed string) string {
  function calcDeviceID (line 362) | func calcDeviceID(deviceID, seed string) string {
  function calcDevicePrivate (line 375) | func calcDevicePrivate(private, seed string) []byte {
  function calcSetupID (line 389) | func calcSetupID(seed string) string {
  function calcCategoryID (line 394) | func calcCategoryID(categoryID string) string {

FILE: internal/http/http.go
  function Init (line 21) | func Init() {
  function handleHTTP (line 31) | func handleHTTP(rawURL string) (core.Producer, error) {
  function do (line 63) | func do(req *http.Request) (core.Producer, error) {
  function handleTCP (line 99) | func handleTCP(rawURL string) (core.Producer, error) {
  function apiStream (line 113) | func apiStream(w http.ResponseWriter, r *http.Request) {

FILE: internal/isapi/init.go
  function Init (line 9) | func Init() {

FILE: internal/ivideon/ivideon.go
  function Init (line 8) | func Init() {

FILE: internal/kasa/kasa.go
  function Init (line 9) | func Init() {

FILE: internal/mjpeg/mjpeg.go
  function Init (line 26) | func Init() {
  function handlerKeyframe (line 39) | func handlerKeyframe(w http.ResponseWriter, r *http.Request) {
  type cacheEntry (line 112) | type cacheEntry struct
  function writeJPEGResponse (line 117) | func writeJPEGResponse(w http.ResponseWriter, b []byte) {
  function handlerStream (line 130) | func handlerStream(w http.ResponseWriter, r *http.Request) {
  function outputMjpeg (line 138) | func outputMjpeg(w http.ResponseWriter, r *http.Request) {
  function inputMjpeg (line 173) | func inputMjpeg(w http.ResponseWriter, r *http.Request) {
  function handlerWS (line 193) | func handlerWS(tr *ws.Transport, _ *ws.Message) error {
  function apiStreamY4M (line 218) | func apiStreamY4M(w http.ResponseWriter, r *http.Request) {

FILE: internal/mp4/mp4.go
  function Init (line 19) | func Init() {
  function handlerKeyframe (line 31) | func handlerKeyframe(w http.ResponseWriter, r *http.Request) {
  function handlerMP4 (line 77) | func handlerMP4(w http.ResponseWriter, r *http.Request) {

FILE: internal/mp4/ws.go
  function handlerWSMSE (line 13) | func handlerWSMSE(tr *ws.Transport, msg *ws.Message) error {
  function handlerWSMP4 (line 45) | func handlerWSMP4(tr *ws.Transport, msg *ws.Message) error {

FILE: internal/mpeg/mpeg.go
  function Init (line 12) | func Init() {
  function apiHandle (line 17) | func apiHandle(w http.ResponseWriter, r *http.Request) {
  function outputMpegTS (line 25) | func outputMpegTS(w http.ResponseWriter, r *http.Request) {
  function inputMpegTS (line 48) | func inputMpegTS(w http.ResponseWriter, r *http.Request) {
  function apiStreamAAC (line 71) | func apiStreamAAC(w http.ResponseWriter, r *http.Request) {

FILE: internal/multitrans/multitrans.go
  function Init (line 8) | func Init() {

FILE: internal/nest/init.go
  function Init (line 13) | func Init() {
  function apiNest (line 21) | func apiNest(w http.ResponseWriter, r *http.Request) {

FILE: internal/ngrok/ngrok.go
  function Init (line 14) | func Init() {
  function ConvertHostToIP (line 68) | func ConvertHostToIP(address string) (string, error) {

FILE: internal/onvif/onvif.go
  function Init (line 22) | func Init() {
  function streamOnvif (line 36) | func streamOnvif(rawURL string) (core.Producer, error) {
  function onvifDeviceService (line 61) | func onvifDeviceService(w http.ResponseWriter, r *http.Request) {
  function apiOnvif (line 161) | func apiOnvif(w http.ResponseWriter, r *http.Request) {

FILE: internal/pinggy/pinggy.go
  function Init (line 11) | func Init() {
  function proxy (line 37) | func proxy(proto, address string) {

FILE: internal/ring/ring.go
  function Init (line 15) | func Init() {
  function apiRing (line 23) | func apiRing(w http.ResponseWriter, r *http.Request) {

FILE: internal/roborock/roborock.go
  function Init (line 13) | func Init() {
  function apiHandle (line 26) | func apiHandle(w http.ResponseWriter, r *http.Request) {

FILE: internal/rtmp/rtmp.go
  function Init (line 18) | func Init() {
  function tcpHandle (line 68) | func tcpHandle(netConn net.Conn) error {
  function streamsHandle (line 129) | func streamsHandle(url string) (core.Producer, error) {
  function streamsConsumerHandle (line 133) | func streamsConsumerHandle(url string) (core.Consumer, func(), error) {
  function apiHandle (line 146) | func apiHandle(w http.ResponseWriter, r *http.Request) {
  function outputFLV (line 154) | func outputFLV(w http.ResponseWriter, r *http.Request) {
  function inputFLV (line 178) | func inputFLV(w http.ResponseWriter, r *http.Request) {

FILE: internal/rtsp/rtsp.go
  function Init (line 18) | func Init() {
  type Handler (line 81) | type Handler
  function HandleFunc (line 83) | func HandleFunc(handler Handler) {
  function rtspHandler (line 95) | func rtspHandler(rawURL string) (core.Producer, error) {
  function tcpHandler (line 146) | func tcpHandler(conn *rtsp.Conn) {
  function ParseQuery (line 290) | func ParseQuery(query map[string][]string) []*core.Media {

FILE: internal/srtp/srtp.go
  function Init (line 8) | func Init() {

FILE: internal/streams/add_consumer.go
  method AddConsumer (line 10) | func (s *Stream) AddConsumer(cons core.Consumer) (err error) {
  function formatError (line 117) | func formatError(consMedias, prodMedias []*core.Media, prodErrors []erro...
  function appendString (line 158) | func appendString(s, elem string) string {

FILE: internal/streams/api.go
  function apiStreams (line 13) | func apiStreams(w http.ResponseWriter, r *http.Request) {
  function apiStreamsDOT (line 109) | func apiStreamsDOT(w http.ResponseWriter, r *http.Request) {
  function apiPreload (line 132) | func apiPreload(w http.ResponseWriter, r *http.Request) {
  function apiSchemes (line 179) | func apiSchemes(w http.ResponseWriter, r *http.Request) {

FILE: internal/streams/api_test.go
  function TestApiSchemes (line 13) | func TestApiSchemes(t *testing.T) {
  function TestApiSchemesNoDuplicates (line 40) | func TestApiSchemesNoDuplicates(t *testing.T) {

FILE: internal/streams/dot.go
  function AppendDOT (line 9) | func AppendDOT(dot []byte, stream *Stream) []byte {
  function marshalConn (line 30) | func marshalConn(v any) (*conn, error) {
  constant bytesK (line 42) | bytesK = "KMGTP"
  function humanBytes (line 44) | func humanBytes(i int) string {
  type node (line 58) | type node struct
    method name (line 70) | func (n *node) name() string {
    method codec (line 77) | func (n *node) codec() []byte {
    method appendDOT (line 90) | func (n *node) appendDOT(dot []byte, group string) []byte {
  type conn (line 98) | type conn struct
    method appendDOT (line 112) | func (c *conn) appendDOT(dot []byte, group string) []byte {
    method host (line 134) | func (c *conn) host() (s string) {
    method label (line 159) | func (c *conn) label() string {

FILE: internal/streams/handlers.go
  type Handler (line 11) | type Handler
  function HandleFunc (line 15) | func HandleFunc(scheme string, handler Handler) {
  function SupportedSchemes (line 19) | func SupportedSchemes() []string {
  function HasProducer (line 34) | func HasProducer(url string) bool {
  function GetProducer (line 50) | func GetProducer(url string) (core.Producer, error) {
  type Redirect (line 73) | type Redirect
  function RedirectFunc (line 77) | func RedirectFunc(scheme string, redirect Redirect) {
  function Location (line 81) | func Location(url string) (string, error) {
  type ConsumerHandler (line 95) | type ConsumerHandler
  function HandleConsumerFunc (line 99) | func HandleConsumerFunc(scheme string, handler ConsumerHandler) {
  function GetConsumer (line 103) | func GetConsumer(url string) (core.Consumer, func(), error) {
  function MarkInsecure (line 117) | func MarkInsecure(scheme string) {
  function Validate (line 123) | func Validate(source string) error {

FILE: internal/streams/helpers.go
  function ParseQuery (line 8) | func ParseQuery(s string) url.Values {

FILE: internal/streams/play.go
  method Play (line 10) | func (s *Stream) Play(urlOrProd any) error {
  method AddInternalProducer (line 111) | func (s *Stream) AddInternalProducer(conn core.Producer) {
  method AddInternalConsumer (line 118) | func (s *Stream) AddInternalConsumer(conn core.Consumer) {
  method RemoveInternalConsumer (line 124) | func (s *Stream) RemoveInternalConsumer(conn core.Consumer) {
  function matchMedia (line 135) | func matchMedia(prod core.Producer, cons core.Consumer) bool {

FILE: internal/streams/preload.go
  type Preload (line 12) | type Preload struct
  function AddPreload (line 21) | func AddPreload(name, rawQuery string) error {
  function DelPreload (line 52) | func DelPreload(name string) error {
  function GetPreloads (line 65) | func GetPreloads() map[string]*Preload {

FILE: internal/streams/producer.go
  type state (line 13) | type state
  constant stateNone (line 16) | stateNone state = iota
  constant stateMedias (line 17) | stateMedias
  constant stateTracks (line 18) | stateTracks
  constant stateStart (line 19) | stateStart
  constant stateExternal (line 20) | stateExternal
  constant stateInternal (line 21) | stateInternal
  type Producer (line 24) | type Producer struct
    method SetSource (line 49) | func (p *Producer) SetSource(s string) {
    method Dial (line 57) | func (p *Producer) Dial() error {
    method GetMedias (line 74) | func (p *Producer) GetMedias() []*core.Media {
    method GetTrack (line 85) | func (p *Producer) GetTrack(media *core.Media, codec *core.Codec) (*co...
    method AddTrack (line 113) | func (p *Producer) AddTrack(media *core.Media, codec *core.Codec, trac...
    method MarshalJSON (line 134) | func (p *Producer) MarshalJSON() ([]byte, error) {
    method start (line 144) | func (p *Producer) start() {
    method worker (line 160) | func (p *Producer) worker(conn core.Producer, workerID int) {
    method reconnect (line 176) | func (p *Producer) reconnect(workerID, retry int) {
    method stop (line 245) | func (p *Producer) stop() {
  constant SourceTemplate (line 39) | SourceTemplate = "{input}"
  function NewProducer (line 41) | func NewProducer(source string) *Producer {

FILE: internal/streams/publish.go
  method Publish (line 5) | func (s *Stream) Publish(url string) error {
  function Publish (line 27) | func Publish(stream *Stream, destination any) {

FILE: internal/streams/stream.go
  type Stream (line 11) | type Stream struct
    method Sources (line 50) | func (s *Stream) Sources() []string {
    method SetSource (line 58) | func (s *Stream) SetSource(source string) {
    method RemoveConsumer (line 64) | func (s *Stream) RemoveConsumer(cons core.Consumer) {
    method AddProducer (line 79) | func (s *Stream) AddProducer(prod core.Producer) {
    method RemoveProducer (line 86) | func (s *Stream) RemoveProducer(prod core.Producer) {
    method stopProducers (line 97) | func (s *Stream) stopProducers() {
    method MarshalJSON (line 121) | func (s *Stream) MarshalJSON() ([]byte, error) {
  function NewStream (line 18) | func NewStream(source any) *Stream {

FILE: internal/streams/stream_test.go
  function TestRecursion (line 11) | func TestRecursion(t *testing.T) {
  function TestTempate (line 30) | func TestTempate(t *testing.T) {

FILE: internal/streams/streams.go
  function Init (line 14) | func Init() {
  function New (line 53) | func New(name string, sources ...string) (*Stream, error) {
  function Patch (line 73) | func Patch(name string, source string) (*Stream, error) {
  function GetOrPatch (line 118) | func GetOrPatch(query url.Values) (*Stream, error) {
  function Get (line 146) | func Get(name string) *Stream {
  function Delete (line 152) | func Delete(name string) {
  function GetAllNames (line 158) | func GetAllNames() []string {
  function GetAllSources (line 168) | func GetAllSources() map[string][]string {

FILE: internal/tapo/tapo.go
  function Init (line 10) | func Init() {

FILE: internal/tuya/tuya.go
  function Init (line 18) | func Init() {
  function apiTuya (line 26) | func apiTuya(w http.ResponseWriter, r *http.Request) {
  function login (line 124) | func login(client *http.Client, serverHost, email, password, countryCode...
  function getLoginToken (line 167) | func getLoginToken(client *http.Client, serverHost, username, countryCod...
  function performLogin (line 210) | func performLogin(client *http.Client, url string, loginReq tuya.Passwor...
  function containsDevice (line 241) | func containsDevice(devices []tuya.Device, deviceID string) bool {

FILE: internal/v4l2/v4l2.go
  function Init (line 5) | func Init() {

FILE: internal/v4l2/v4l2_linux.go
  function Init (line 19) | func Init() {
  function apiV4L2 (line 27) | func apiV4L2(w http.ResponseWriter, r *http.Request) {
  function findFormat (line 84) | func findFormat(fourCC uint32) (name, ffmpeg string) {

FILE: internal/webrtc/candidates.go
  type Address (line 14) | type Address struct
    method Host (line 23) | func (a *Address) Host() string {
    method Marshal (line 34) | func (a *Address) Marshal() string {
  function AddCandidate (line 44) | func AddCandidate(network, address string) {
  function GetCandidates (line 64) | func GetCandidates() (candidates []string) {
  function FilterCandidate (line 74) | func FilterCandidate(candidate *pion.ICECandidate) bool {
  function NetworkType (line 102) | func NetworkType(network, host string) string {
  function asyncCandidates (line 110) | func asyncCandidates(tr *ws.Transport, cons *webrtc.Conn) {
  function candidateHandler (line 132) | func candidateHandler(tr *ws.Transport, msg *ws.Message) error {

FILE: internal/webrtc/client.go
  function streamsHandler (line 26) | func streamsHandler(rawURL string) (core.Producer, error) {
  function go2rtcClient (line 71) | func go2rtcClient(url string) (core.Producer, error) {
  function whepClient (line 185) | func whepClient(url string) (core.Producer, error) {
  function Dial (line 236) | func Dial(rawURL string) (*websocket.Conn, *http.Response, error) {

FILE: internal/webrtc/client_creality.go
  function crealityClient (line 17) | func crealityClient(url string) (core.Producer, error) {
  function offerToB64 (line 79) | func offerToB64(sdp string) (io.Reader, error) {
  function answerFromB64 (line 98) | func answerFromB64(r io.Reader) (string, error) {
  function fixCrealitySDP (line 120) | func fixCrealitySDP(value string) (string, error) {

FILE: internal/webrtc/kinesis.go
  type kinesisRequest (line 18) | type kinesisRequest struct
    method String (line 24) | func (k kinesisRequest) String() string {
  type kinesisResponse (line 28) | type kinesisResponse struct
    method String (line 33) | func (k kinesisResponse) String() string {
  function kinesisClient (line 37) | func kinesisClient(
  type wyzeKVS (line 200) | type wyzeKVS struct
  function wyzeClient (line 208) | func wyzeClient(rawURL string) (core.Producer, error) {

FILE: internal/webrtc/milestone.go
  type milestoneAPI (line 25) | type milestoneAPI struct
    method GetToken (line 32) | func (m *milestoneAPI) GetToken() error {
    method GetOffer (line 80) | func (m *milestoneAPI) GetOffer() (string, error) {
    method SetAnswer (line 139) | func (m *milestoneAPI) SetAnswer(sdp string) error {
  function parseFloat (line 72) | func parseFloat(s string) float64 {
  function milestoneClient (line 178) | func milestoneClient(rawURL string, query url.Values) (core.Producer, er...

FILE: internal/webrtc/openipc.go
  function openIPCClient (line 15) | func openIPCClient(rawURL string, query url.Values) (core.Producer, erro...
  type openIPCReply (line 152) | type openIPCReply struct
    method String (line 157) | func (r openIPCReply) String() string {
  type openIPCReq (line 162) | type openIPCReq struct
    method String (line 167) | func (r openIPCReq) String() string {

FILE: internal/webrtc/server.go
  constant MimeSDP (line 19) | MimeSDP = "application/sdp"
  function syncHandler (line 23) | func syncHandler(w http.ResponseWriter, r *http.Request) {
  function outputWebRTC (line 65) | func outputWebRTC(w http.ResponseWriter, r *http.Request) {
  function inputWebRTC (line 166) | func inputWebRTC(w http.ResponseWriter, r *http.Request) {

FILE: internal/webrtc/switchbot.go
  function switchbotClient (line 10) | func switchbotClient(rawURL string, query url.Values) (core.Producer, er...

FILE: internal/webrtc/webrtc.go
  function Init (line 18) | func Init() {
  function asyncHandler (line 117) | func asyncHandler(tr *ws.Transport, msg *ws.Message) (err error) {
  function ExchangeSDP (line 242) | func ExchangeSDP(stream *streams.Stream, offer, desc, userAgent string) ...
  function IsConsumer (line 301) | func IsConsumer(conn *webrtc.Conn) bool {

FILE: internal/webrtc/webrtc_test.go
  function TestWebRTCAPIv1 (line 13) | func TestWebRTCAPIv1(t *testing.T) {
  function TestWebRTCAPIv2 (line 22) | func TestWebRTCAPIv2(t *testing.T) {
  function TestCrealitySDP (line 41) | func TestCrealitySDP(t *testing.T) {

FILE: internal/webtorrent/init.go
  function Init (line 18) | func Init() {
  function apiHandle (line 91) | func apiHandle(w http.ResponseWriter, r *http.Request) {
  function streamHandle (line 157) | func streamHandle(rawURL string) (core.Producer, error) {

FILE: internal/webtorrent/tracker.go
  function tracker (line 14) | func tracker(w http.ResponseWriter, r *http.Request) {

FILE: internal/wyoming/wyoming.go
  function Init (line 13) | func Init() {
  function serve (line 75) | func serve(srv *wyoming.Server, mode, address string) {
  function handle (line 91) | func handle(srv *wyoming.Server, mode string, conn net.Conn) {

FILE: internal/wyze/wyze.go
  type AccountConfig (line 16) | type AccountConfig struct
  function Init (line 24) | func Init() {
  function getCloud (line 42) | func getCloud(email string) (*wyze.Cloud, error) {
  function apiWyze (line 61) | func apiWyze(w http.ResponseWriter, r *http.Request) {
  function apiDeviceList (line 70) | func apiDeviceList(w http.ResponseWriter, r *http.Request) {
  function apiAuth (line 112) | func apiAuth(w http.ResponseWriter, r *http.Request) {
  function buildStreamURL (line 182) | func buildStreamURL(cam *wyze.Camera) string {
  function isAuthError (line 196) | func isAuthError(err error, target **wyze.AuthError) bool {

FILE: internal/xiaomi/xiaomi.go
  function Init (line 22) | func Init() {
  function getCloud (line 59) | func getCloud(userID string) (*xiaomi.Cloud, error) {
  function cloudRequest (line 79) | func cloudRequest(userID, region, apiURL, params string) ([]byte, error) {
  function cloudUserRequest (line 87) | func cloudUserRequest(user *url.Userinfo, apiURL, params string) ([]byte...
  function getCameraURL (line 93) | func getCameraURL(url *url.URL) (string, error) {
  function getLegacyURL (line 110) | func getLegacyURL(url *url.URL) (string, error) {
  function getMissURL (line 152) | func getMissURL(url *url.URL) (string, error) {
  function getVendorName (line 200) | func getVendorName(i byte) string {
  function wakeUpCamera (line 214) | func wakeUpCamera(url *url.URL) error {
  function apiXiaomi (line 221) | func apiXiaomi(w http.ResponseWriter, r *http.Request) {
  function apiDeviceList (line 230) | func apiDeviceList(w http.ResponseWriter, r *http.Request) {
  type Device (line 284) | type Device struct
    method HasCamera (line 292) | func (d *Device) HasCamera() bool {
  function apiAuth (line 300) | func apiAuth(w http.ResponseWriter, r *http.Request) {
  constant AppXiaomiHome (line 354) | AppXiaomiHome = "xiaomiio"
  function GetBaseURL (line 356) | func GetBaseURL(region string) string {

FILE: internal/yandex/goloom.go
  function goloomClient (line 17) | func goloomClient(serviceURL, serviceName, roomId, participantId, creden...
  type subscriberSdp (line 141) | type subscriberSdp struct
  type webrtcIceCandidate (line 146) | type webrtcIceCandidate struct

FILE: internal/yandex/yandex.go
  function Init (line 11) | func Init() {

FILE: main.go
  function main (line 54) | func main() {

FILE: pkg/aac/aac.go
  constant TypeAACMain (line 12) | TypeAACMain = 1
  constant TypeAACLC (line 13) | TypeAACLC   = 2
  constant TypeAACLD (line 14) | TypeAACLD   = 23
  constant TypeESCAPE (line 15) | TypeESCAPE  = 31
  constant TypeAACELD (line 16) | TypeAACELD  = 39
  constant AUTime (line 18) | AUTime = 1024
  constant FMTP (line 21) | FMTP = "streamtype=5;profile-level-id=1;mode=AAC-hbr;sizelength=13;index...
  function ConfigToCodec (line 29) | func ConfigToCodec(conf []byte) *core.Codec {
  function DecodeConfig (line 61) | func DecodeConfig(b []byte) (objType, sampleFreqIdx, channels byte, samp...
  function EncodeConfig (line 80) | func EncodeConfig(objType byte, sampleRate uint32, channels byte, shortF...
  function indexUint32 (line 119) | func indexUint32(s []uint32, v uint32) int {

FILE: pkg/aac/aac_test.go
  function TestConfigToCodec (line 11) | func TestConfigToCodec(t *testing.T) {
  function TestADTS (line 26) | func TestADTS(t *testing.T) {
  function TestEncodeConfig (line 45) | func TestEncodeConfig(t *testing.T) {

FILE: pkg/aac/adts.go
  constant ADTSHeaderSize (line 11) | ADTSHeaderSize = 7
  function ADTSHeaderLen (line 13) | func ADTSHeaderLen(b []byte) int {
  function IsADTS (line 20) | func IsADTS(b []byte) bool {
  function HasCRC (line 27) | func HasCRC(b []byte) bool {
  function ADTSToCodec (line 33) | func ADTSToCodec(b []byte) *core.Codec {
  function ReadADTSSize (line 76) | func ReadADTSSize(b []byte) uint16 {
  function WriteADTSSize (line 82) | func WriteADTSSize(b []byte, size uint16) {
  function ADTSTimeSize (line 91) | func ADTSTimeSize(b []byte) uint32 {
  function CodecToADTS (line 101) | func CodecToADTS(codec *core.Codec) []byte {
  function EncodeToADTS (line 131) | func EncodeToADTS(codec *core.Codec, handler core.HandlerFunc) core.Hand...

FILE: pkg/aac/consumer.go
  type Consumer (line 10) | type Consumer struct
    method AddTrack (line 37) | func (c *Consumer) AddTrack(media *core.Media, codec *core.Codec, trac...
    method WriteTo (line 57) | func (c *Consumer) WriteTo(wr io.Writer) (int64, error) {
  function NewConsumer (line 15) | func NewConsumer() *Consumer {

FILE: pkg/aac/producer.go
  type Producer (line 12) | type Producer struct
    method Start (line 49) | func (c *Producer) Start() error {
  function Open (line 17) | func Open(r io.Reader) (*Producer, error) {

FILE: pkg/aac/rtp.go
  constant RTPPacketVersionAAC (line 10) | RTPPacketVersionAAC = 0
  function RTPDepay (line 12) | func RTPDepay(handler core.HandlerFunc) core.HandlerFunc {
  function RTPPay (line 66) | func RTPPay(handler core.HandlerFunc) core.HandlerFunc {
  function ADTStoRTP (line 100) | func ADTStoRTP(src []byte) (dst []byte) {
  function RTPTimeSize (line 112) | func RTPTimeSize(b []byte) uint32 {
  function RTPToADTS (line 118) | func RTPToADTS(codec *core.Codec, handler core.HandlerFunc) core.Handler...
  function RTPToCodec (line 151) | func RTPToCodec(b []byte) *core.Codec {

FILE: pkg/aac/rtp_test.go
  function TestBuggy_RTSP_AAC (line 12) | func TestBuggy_RTSP_AAC(t *testing.T) {

FILE: pkg/alsa/capture_linux.go
  type Capture (line 10) | type Capture struct
    method Start (line 37) | func (c *Capture) Start() error {
  function newCapture (line 16) | func newCapture(dev *device.Device) (*Capture, error) {
  function transcodeFunc (line 83) | func transcodeFunc(dst, src *core.Codec) func([]byte) []byte {

FILE: pkg/alsa/device/asound_32bit.go
  constant SNDRV_PCM_STREAM_PLAYBACK (line 14) | SNDRV_PCM_STREAM_PLAYBACK = 0
  constant SNDRV_PCM_STREAM_CAPTURE (line 15) | SNDRV_PCM_STREAM_CAPTURE  = 1
  constant SNDRV_PCM_ACCESS_MMAP_INTERLEAVED (line 17) | SNDRV_PCM_ACCESS_MMAP_INTERLEAVED    = 0
  constant SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED (line 18) | SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED = 1
  constant SNDRV_PCM_ACCESS_MMAP_COMPLEX (line 19) | SNDRV_PCM_ACCESS_MMAP_COMPLEX        = 2
  constant SNDRV_PCM_ACCESS_RW_INTERLEAVED (line 20) | SNDRV_PCM_ACCESS_RW_INTERLEAVED      = 3
  constant SNDRV_PCM_ACCESS_RW_NONINTERLEAVED (line 21) | SNDRV_PCM_ACCESS_RW_NONINTERLEAVED   = 4
  constant SNDRV_PCM_FORMAT_S8 (line 23) | SNDRV_PCM_FORMAT_S8         = 0
  constant SNDRV_PCM_FORMAT_U8 (line 24) | SNDRV_PCM_FORMAT_U8         = 1
  constant SNDRV_PCM_FORMAT_S16_LE (line 25) | SNDRV_PCM_FORMAT_S16_LE     = 2
  constant SNDRV_PCM_FORMAT_S16_BE (line 26) | SNDRV_PCM_FORMAT_S16_BE     = 3
  constant SNDRV_PCM_FORMAT_U16_LE (line 27) | SNDRV_PCM_FORMAT_U16_LE     = 4
  constant SNDRV_PCM_FORMAT_U16_BE (line 28) | SNDRV_PCM_FORMAT_U16_BE     = 5
  constant SNDRV_PCM_FORMAT_S24_LE (line 29) | SNDRV_PCM_FORMAT_S24_LE     = 6
  constant SNDRV_PCM_FORMAT_S24_BE (line 30) | SNDRV_PCM_FORMAT_S24_BE     = 7
  constant SNDRV_PCM_FORMAT_U24_LE (line 31) | SNDRV_PCM_FORMAT_U24_LE     = 8
  constant SNDRV_PCM_FORMAT_U24_BE (line 32) | SNDRV_PCM_FORMAT_U24_BE     = 9
  constant SNDRV_PCM_FORMAT_S32_LE (line 33) | SNDRV_PCM_FORMAT_S32_LE     = 10
  constant SNDRV_PCM_FORMAT_S32_BE (line 34) | SNDRV_PCM_FORMAT_S32_BE     = 11
  constant SNDRV_PCM_FORMAT_U32_LE (line 35) | SNDRV_PCM_FORMAT_U32_LE     = 12
  constant SNDRV_PCM_FORMAT_U32_BE (line 36) | SNDRV_PCM_FORMAT_U32_BE     = 13
  constant SNDRV_PCM_FORMAT_FLOAT_LE (line 37) | SNDRV_PCM_FORMAT_FLOAT_LE   = 14
  constant SNDRV_PCM_FORMAT_FLOAT_BE (line 38) | SNDRV_PCM_FORMAT_FLOAT_BE   = 15
  constant SNDRV_PCM_FORMAT_FLOAT64_LE (line 39) | SNDRV_PCM_FORMAT_FLOAT64_LE = 16
  constant SNDRV_PCM_FORMAT_FLOAT64_BE (line 40) | SNDRV_PCM_FORMAT_FLOAT64_BE = 17
  constant SNDRV_PCM_FORMAT_MU_LAW (line 41) | SNDRV_PCM_FORMAT_MU_LAW     = 20
  constant SNDRV_PCM_FORMAT_A_LAW (line 42) | SNDRV_PCM_FORMAT_A_LAW      = 21
  constant SNDRV_PCM_FORMAT_MPEG (line 43) | SNDRV_PCM_FORMAT_MPEG       = 23
  constant SNDRV_PCM_IOCTL_PVERSION (line 45) | SNDRV_PCM_IOCTL_PVERSION      = 0x80044100
  constant SNDRV_PCM_IOCTL_INFO (line 46) | SNDRV_PCM_IOCTL_INFO          = 0x81204101
  constant SNDRV_PCM_IOCTL_HW_REFINE (line 47) | SNDRV_PCM_IOCTL_HW_REFINE     = 0xc25c4110
  constant SNDRV_PCM_IOCTL_HW_PARAMS (line 48) | SNDRV_PCM_IOCTL_HW_PARAMS     = 0xc25c4111
  constant SNDRV_PCM_IOCTL_SW_PARAMS (line 49) | SNDRV_PCM_IOCTL_SW_PARAMS     = 0xc0684113
  constant SNDRV_PCM_IOCTL_PREPARE (line 50) | SNDRV_PCM_IOCTL_PREPARE       = 0x00004140
  constant SNDRV_PCM_IOCTL_WRITEI_FRAMES (line 51) | SNDRV_PCM_IOCTL_WRITEI_FRAMES = 0x400c4150
  constant SNDRV_PCM_IOCTL_READI_FRAMES (line 52) | SNDRV_PCM_IOCTL_READI_FRAMES  = 0x800c4151
  type snd_pcm_info (line 55) | type snd_pcm_info struct
  type snd_xferi (line 74) | type snd_xferi struct
  constant SNDRV_PCM_HW_PARAM_ACCESS (line 81) | SNDRV_PCM_HW_PARAM_ACCESS     = 0
  constant SNDRV_PCM_HW_PARAM_FORMAT (line 82) | SNDRV_PCM_HW_PARAM_FORMAT     = 1
  constant SNDRV_PCM_HW_PARAM_SUBFORMAT (line 83) | SNDRV_PCM_HW_PARAM_SUBFORMAT  = 2
  constant SNDRV_PCM_HW_PARAM_FIRST_MASK (line 84) | SNDRV_PCM_HW_PARAM_FIRST_MASK = 0
  constant SNDRV_PCM_HW_PARAM_LAST_MASK (line 85) | SNDRV_PCM_HW_PARAM_LAST_MASK  = 2
  constant SNDRV_PCM_HW_PARAM_SAMPLE_BITS (line 87) | SNDRV_PCM_HW_PARAM_SAMPLE_BITS    = 8
  constant SNDRV_PCM_HW_PARAM_FRAME_BITS (line 88) | SNDRV_PCM_HW_PARAM_FRAME_BITS     = 9
  constant SNDRV_PCM_HW_PARAM_CHANNELS (line 89) | SNDRV_PCM_HW_PARAM_CHANNELS       = 10
  constant SNDRV_PCM_HW_PARAM_RATE (line 90) | SNDRV_PCM_HW_PARAM_RATE           = 11
  constant SNDRV_PCM_HW_PARAM_PERIOD_TIME (line 91) | SNDRV_PCM_HW_PARAM_PERIOD_TIME    = 12
  constant SNDRV_PCM_HW_PARAM_PERIOD_SIZE (line 92) | SNDRV_PCM_HW_PARAM_PERIOD_SIZE    = 13
  constant SNDRV_PCM_HW_PARAM_PERIOD_BYTES (line 93) | SNDRV_PCM_HW_PARAM_PERIOD_BYTES   = 14
  constant SNDRV_PCM_HW_PARAM_PERIODS (line 94) | SNDRV_PCM_HW_PARAM_PERIODS        = 15
  constant SNDRV_PCM_HW_PARAM_BUFFER_TIME (line 95) | SNDRV_PCM_HW_PARAM_BUFFER_TIME    = 16
  constant SNDRV_PCM_HW_PARAM_BUFFER_SIZE (line 96) | SNDRV_PCM_HW_PARAM_BUFFER_SIZE    = 17
  constant SNDRV_PCM_HW_PARAM_BUFFER_BYTES (line 97) | SNDRV_PCM_HW_PARAM_BUFFER_BYTES   = 18
  constant SNDRV_PCM_HW_PARAM_TICK_TIME (line 98) | SNDRV_PCM_HW_PARAM_TICK_TIME      = 19
  constant SNDRV_PCM_HW_PARAM_FIRST_INTERVAL (line 99) | SNDRV_PCM_HW_PARAM_FIRST_INTERVAL = 8
  constant SNDRV_PCM_HW_PARAM_LAST_INTERVAL (line 100) | SNDRV_PCM_HW_PARAM_LAST_INTERVAL  = 19
  constant SNDRV_MASK_MAX (line 102) | SNDRV_MASK_MAX = 256
  constant SNDRV_PCM_TSTAMP_NONE (line 104) | SNDRV_PCM_TSTAMP_NONE   = 0
  constant SNDRV_PCM_TSTAMP_ENABLE (line 105) | SNDRV_PCM_TSTAMP_ENABLE = 1
  type snd_mask (line 108) | type snd_mask struct
  type snd_interval (line 112) | type snd_interval struct
  type snd_pcm_hw_params (line 118) | type snd_pcm_hw_params struct
  type snd_pcm_sw_params (line 134) | type snd_pcm_sw_params struct

FILE: pkg/alsa/device/asound_64bit.go
  constant SNDRV_PCM_STREAM_PLAYBACK (line 14) | SNDRV_PCM_STREAM_PLAYBACK = 0
  constant SNDRV_PCM_STREAM_CAPTURE (line 15) | SNDRV_PCM_STREAM_CAPTURE  = 1
  constant SNDRV_PCM_ACCESS_MMAP_INTERLEAVED (line 17) | SNDRV_PCM_ACCESS_MMAP_INTERLEAVED    = 0
  constant SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED (line 18) | SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED = 1
  constant SNDRV_PCM_ACCESS_MMAP_COMPLEX (line 19) | SNDRV_PCM_ACCESS_MMAP_COMPLEX        = 2
  constant SNDRV_PCM_ACCESS_RW_INTERLEAVED (line 20) | SNDRV_PCM_ACCESS_RW_INTERLEAVED      = 3
  constant SNDRV_PCM_ACCESS_RW_NONINTERLEAVED (line 21) | SNDRV_PCM_ACCESS_RW_NONINTERLEAVED   = 4
  constant SNDRV_PCM_FORMAT_S8 (line 23) | SNDRV_PCM_FORMAT_S8         = 0
  constant SNDRV_PCM_FORMAT_U8 (line 24) | SNDRV_PCM_FORMAT_U8         = 1
  constant SNDRV_PCM_FORMAT_S16_LE (line 25) | SNDRV_PCM_FORMAT_S16_LE     = 2
  constant SNDRV_PCM_FORMAT_S16_BE (line 26) | SNDRV_PCM_FORMAT_S16_BE     = 3
  constant SNDRV_PCM_FORMAT_U16_LE (line 27) | SNDRV_PCM_FORMAT_U16_LE     = 4
  constant SNDRV_PCM_FORMAT_U16_BE (line 28) | SNDRV_PCM_FORMAT_U16_BE     = 5
  constant SNDRV_PCM_FORMAT_S24_LE (line 29) | SNDRV_PCM_FORMAT_S24_LE     = 6
  constant SNDRV_PCM_FORMAT_S24_BE (line 30) | SNDRV_PCM_FORMAT_S24_BE     = 7
  constant SNDRV_PCM_FORMAT_U24_LE (line 31) | SNDRV_PCM_FORMAT_U24_LE     = 8
  constant SNDRV_PCM_FORMAT_U24_BE (line 32) | SNDRV_PCM_FORMAT_U24_BE     = 9
  constant SNDRV_PCM_FORMAT_S32_LE (line 33) | SNDRV_PCM_FORMAT_S32_LE     = 10
  constant SNDRV_PCM_FORMAT_S32_BE (line 34) | SNDRV_PCM_FORMAT_S32_BE     = 11
  constant SNDRV_PCM_FORMAT_U32_LE (line 35) | SNDRV_PCM_FORMAT_U32_LE     = 12
  constant SNDRV_PCM_FORMAT_U32_BE (line 36) | SNDRV_PCM_FORMAT_U32_BE     = 13
  constant SNDRV_PCM_FORMAT_FLOAT_LE (line 37) | SNDRV_PCM_FORMAT_FLOAT_LE   = 14
  constant SNDRV_PCM_FORMAT_FLOAT_BE (line 38) | SNDRV_PCM_FORMAT_FLOAT_BE   = 15
  constant SNDRV_PCM_FORMAT_FLOAT64_LE (line 39) | SNDRV_PCM_FORMAT_FLOAT64_LE = 16
  constant SNDRV_PCM_FORMAT_FLOAT64_BE (line 40) | SNDRV_PCM_FORMAT_FLOAT64_BE = 17
  constant SNDRV_PCM_FORMAT_MU_LAW (line 41) | SNDRV_PCM_FORMAT_MU_LAW     = 20
  constant SNDRV_PCM_FORMAT_A_LAW (line 42) | SNDRV_PCM_FORMAT_A_LAW      = 21
  constant SNDRV_PCM_FORMAT_MPEG (line 43) | SNDRV_PCM_FORMAT_MPEG       = 23
  constant SNDRV_PCM_IOCTL_PVERSION (line 45) | SNDRV_PCM_IOCTL_PVERSION      = 0x80044100
  constant SNDRV_PCM_IOCTL_INFO (line 46) | SNDRV_PCM_IOCTL_INFO          = 0x81204101
  constant SNDRV_PCM_IOCTL_HW_REFINE (line 47) | SNDRV_PCM_IOCTL_HW_REFINE     = 0xc2604110
  constant SNDRV_PCM_IOCTL_HW_PARAMS (line 48) | SNDRV_PCM_IOCTL_HW_PARAMS     = 0xc2604111
  constant SNDRV_PCM_IOCTL_SW_PARAMS (line 49) | SNDRV_PCM_IOCTL_SW_PARAMS     = 0xc0884113
  constant SNDRV_PCM_IOCTL_PREPARE (line 50) | SNDRV_PCM_IOCTL_PREPARE       = 0x00004140
  constant SNDRV_PCM_IOCTL_WRITEI_FRAMES (line 51) | SNDRV_PCM_IOCTL_WRITEI_FRAMES = 0x40184150
  constant SNDRV_PCM_IOCTL_READI_FRAMES (line 52) | SNDRV_PCM_IOCTL_READI_FRAMES  = 0x80184151
  type snd_pcm_info (line 55) | type snd_pcm_info struct
  type snd_xferi (line 74) | type snd_xferi struct
  constant SNDRV_PCM_HW_PARAM_ACCESS (line 81) | SNDRV_PCM_HW_PARAM_ACCESS     = 0
  constant SNDRV_PCM_HW_PARAM_FORMAT (line 82) | SNDRV_PCM_HW_PARAM_FORMAT     = 1
  constant SNDRV_PCM_HW_PARAM_SUBFORMAT (line 83) | SNDRV_PCM_HW_PARAM_SUBFORMAT  = 2
  constant SNDRV_PCM_HW_PARAM_FIRST_MASK (line 84) | SNDRV_PCM_HW_PARAM_FIRST_MASK = 0
  constant SNDRV_PCM_HW_PARAM_LAST_MASK (line 85) | SNDRV_PCM_HW_PARAM_LAST_MASK  = 2
  constant SNDRV_PCM_HW_PARAM_SAMPLE_BITS (line 87) | SNDRV_PCM_HW_PARAM_SAMPLE_BITS    = 8
  constant SNDRV_PCM_HW_PARAM_FRAME_BITS (line 88) | SNDRV_PCM_HW_PARAM_FRAME_BITS     = 9
  constant SNDRV_PCM_HW_PARAM_CHANNELS (line 89) | SNDRV_PCM_HW_PARAM_CHANNELS       = 10
  constant SNDRV_PCM_HW_PARAM_RATE (line 90) | SNDRV_PCM_HW_PARAM_RATE           = 11
  constant SNDRV_PCM_HW_PARAM_PERIOD_TIME (line 91) | SNDRV_PCM_HW_PARAM_PERIOD_TIME    = 12
  constant SNDRV_PCM_HW_PARAM_PERIOD_SIZE (line 92) | SNDRV_PCM_HW_PARAM_PERIOD_SIZE    = 13
  constant SNDRV_PCM_HW_PARAM_PERIOD_BYTES (line 93) | SNDRV_PCM_HW_PARAM_PERIOD_BYTES   = 14
  constant SNDRV_PCM_HW_PARAM_PERIODS (line 94) | SNDRV_PCM_HW_PARAM_PERIODS        = 15
  constant SNDRV_PCM_HW_PARAM_BUFFER_TIME (line 95) | SNDRV_PCM_HW_PARAM_BUFFER_TIME    = 16
  constant SNDRV_PCM_HW_PARAM_BUFFER_SIZE (line 96) | SNDRV_PCM_HW_PARAM_BUFFER_SIZE    = 17
  constant SNDRV_PCM_HW_PARAM_BUFFER_BYTES (line 97) | SNDRV_PCM_HW_PARAM_BUFFER_BYTES   = 18
  constant SNDRV_PCM_HW_PARAM_TICK_TIME (line 98) | SNDRV_PCM_HW_PARAM_TICK_TIME      = 19
  constant SNDRV_PCM_HW_PARAM_FIRST_INTERVAL (line 99) | SNDRV_PCM_HW_PARAM_FIRST_INTERVAL = 8
  constant SNDRV_PCM_HW_PARAM_LAST_INTERVAL (line 100) | SNDRV_PCM_HW_PARAM_LAST_INTERVAL  = 19
  constant SNDRV_MASK_MAX (line 102) | SNDRV_MASK_MAX = 256
  constant SNDRV_PCM_TSTAMP_NONE (line 104) | SNDRV_PCM_TSTAMP_NONE   = 0
  constant SNDRV_PCM_TSTAMP_ENABLE (line 105) | SNDRV_PCM_TSTAMP_ENABLE = 1
  type snd_mask (line 108) | type snd_mask struct
  type snd_interval (line 112) | type snd_interval struct
  type snd_pcm_hw_params (line 118) | type snd_pcm_hw_params struct
  type snd_pcm_sw_params (line 134) | type snd_pcm_sw_params struct

FILE: pkg/alsa/device/asound_arch.c
  function main (line 15) | int main() {

FILE: pkg/alsa/device/asound_mipsle.go
  constant SNDRV_PCM_STREAM_PLAYBACK (line 12) | SNDRV_PCM_STREAM_PLAYBACK = 0
  constant SNDRV_PCM_STREAM_CAPTURE (line 13) | SNDRV_PCM_STREAM_CAPTURE  = 1
  constant SNDRV_PCM_ACCESS_MMAP_INTERLEAVED (line 15) | SNDRV_PCM_ACCESS_MMAP_INTERLEAVED    = 0
  constant SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED (line 16) | SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED = 1
  constant SNDRV_PCM_ACCESS_MMAP_COMPLEX (line 17) | SNDRV_PCM_ACCESS_MMAP_COMPLEX        = 2
  constant SNDRV_PCM_ACCESS_RW_INTERLEAVED (line 18) | SNDRV_PCM_ACCESS_RW_INTERLEAVED      = 3
  constant SNDRV_PCM_ACCESS_RW_NONINTERLEAVED (line 19) | SNDRV_PCM_ACCESS_RW_NONINTERLEAVED   = 4
  constant SNDRV_PCM_FORMAT_S8 (line 21) | SNDRV_PCM_FORMAT_S8         = 0
  constant SNDRV_PCM_FORMAT_U8 (line 22) | SNDRV_PCM_FORMAT_U8         = 1
  constant SNDRV_PCM_FORMAT_S16_LE (line 23) | SNDRV_PCM_FORMAT_S16_LE     = 2
  constant SNDRV_PCM_FORMAT_S16_BE (line 24) | SNDRV_PCM_FORMAT_S16_BE     = 3
  constant SNDRV_PCM_FORMAT_U16_LE (line 25) | SNDRV_PCM_FORMAT_U16_LE     = 4
  constant SNDRV_PCM_FORMAT_U16_BE (line 26) | SNDRV_PCM_FORMAT_U16_BE     = 5
  constant SNDRV_PCM_FORMAT_S24_LE (line 27) | SNDRV_PCM_FORMAT_S24_LE     = 6
  constant SNDRV_PCM_FORMAT_S24_BE (line 28) | SNDRV_PCM_FORMAT_S24_BE     = 7
  constant SNDRV_PCM_FORMAT_U24_LE (line 29) | SNDRV_PCM_FORMAT_U24_LE     = 8
  constant SNDRV_PCM_FORMAT_U24_BE (line 30) | SNDRV_PCM_FORMAT_U24_BE     = 9
  constant SNDRV_PCM_FORMAT_S32_LE (line 31) | SNDRV_PCM_FORMAT_S32_LE     = 10
  constant SNDRV_PCM_FORMAT_S32_BE (line 32) | SNDRV_PCM_FORMAT_S32_BE     = 11
  constant SNDRV_PCM_FORMAT_U32_LE (line 33) | SNDRV_PCM_FORMAT_U32_LE     = 12
  constant SNDRV_PCM_FORMAT_U32_BE (line 34) | SNDRV_PCM_FORMAT_U32_BE     = 13
  constant SNDRV_PCM_FORMAT_FLOAT_LE (line 35) | SNDRV_PCM_FORMAT_FLOAT_LE   = 14
  constant SNDRV_PCM_FORMAT_FLOAT_BE (line 36) | SNDRV_PCM_FORMAT_FLOAT_BE   = 15
  constant SNDRV_PCM_FORMAT_FLOAT64_LE (line 37) | SNDRV_PCM_FORMAT_FLOAT64_LE = 16
  constant SNDRV_PCM_FORMAT_FLOAT64_BE (line 38) | SNDRV_PCM_FORMAT_FLOAT64_BE = 17
  constant SNDRV_PCM_FORMAT_MU_LAW (line 39) | SNDRV_PCM_FORMAT_MU_LAW     = 20
  constant SNDRV_PCM_FORMAT_A_LAW (line 40) | SNDRV_PCM_FORMAT_A_LAW      = 21
  constant SNDRV_PCM_FORMAT_MPEG (line 41) | SNDRV_PCM_FORMAT_MPEG       = 23
  constant SNDRV_PCM_IOCTL_PVERSION (line 43) | SNDRV_PCM_IOCTL_PVERSION      = 0x40044100
  constant SNDRV_PCM_IOCTL_INFO (line 44) | SNDRV_PCM_IOCTL_INFO          = 0x41204101
  constant SNDRV_PCM_IOCTL_HW_REFINE (line 45) | SNDRV_PCM_IOCTL_HW_REFINE     = 0xc25c4110
  constant SNDRV_PCM_IOCTL_HW_PARAMS (line 46) | SNDRV_PCM_IOCTL_HW_PARAMS     = 0xc25c4111
  constant SNDRV_PCM_IOCTL_SW_PARAMS (line 47) | SNDRV_PCM_IOCTL_SW_PARAMS     = 0xc0684113
  constant SNDRV_PCM_IOCTL_PREPARE (line 48) | SNDRV_PCM_IOCTL_PREPARE       = 0x20004140
  constant SNDRV_PCM_IOCTL_WRITEI_FRAMES (line 49) | SNDRV_PCM_IOCTL_WRITEI_FRAMES = 0x800c4150
  constant SNDRV_PCM_IOCTL_READI_FRAMES (line 50) | SNDRV_PCM_IOCTL_READI_FRAMES  = 0x400c4151
  type snd_pcm_info (line 53) | type snd_pcm_info struct
  type snd_xferi (line 72) | type snd_xferi struct
  constant SNDRV_PCM_HW_PARAM_ACCESS (line 79) | SNDRV_PCM_HW_PARAM_ACCESS     = 0
  constant SNDRV_PCM_HW_PARAM_FORMAT (line 80) | SNDRV_PCM_HW_PARAM_FORMAT     = 1
  constant SNDRV_PCM_HW_PARAM_SUBFORMAT (line 81) | SNDRV_PCM_HW_PARAM_SUBFORMAT  = 2
  constant SNDRV_PCM_HW_PARAM_FIRST_MASK (line 82) | SNDRV_PCM_HW_PARAM_FIRST_MASK = 0
  constant SNDRV_PCM_HW_PARAM_LAST_MASK (line 83) | SNDRV_PCM_HW_PARAM_LAST_MASK  = 2
  constant SNDRV_PCM_HW_PARAM_SAMPLE_BITS (line 85) | SNDRV_PCM_HW_PARAM_SAMPLE_BITS    = 8
  constant SNDRV_PCM_HW_PARAM_FRAME_BITS (line 86) | SNDRV_PCM_HW_PARAM_FRAME_BITS     = 9
  constant SNDRV_PCM_HW_PARAM_CHANNELS (line 87) | SNDRV_PCM_HW_PARAM_CHANNELS       = 10
  constant SNDRV_PCM_HW_PARAM_RATE (line 88) | SNDRV_PCM_HW_PARAM_RATE           = 11
  constant SNDRV_PCM_HW_PARAM_PERIOD_TIME (line 89) | SNDRV_PCM_HW_PARAM_PERIOD_TIME    = 12
  constant SNDRV_PCM_HW_PARAM_PERIOD_SIZE (line 90) | SNDRV_PCM_HW_PARAM_PERIOD_SIZE    = 13
  constant SNDRV_PCM_HW_PARAM_PERIOD_BYTES (line 91) | SNDRV_PCM_HW_PARAM_PERIOD_BYTES   = 14
  constant SNDRV_PCM_HW_PARAM_PERIODS (line 92) | SNDRV_PCM_HW_PARAM_PERIODS        = 15
  constant SNDRV_PCM_HW_PARAM_BUFFER_TIME (line 93) | SNDRV_PCM_HW_PARAM_BUFFER_TIME    = 16
  constant SNDRV_PCM_HW_PARAM_BUFFER_SIZE (line 94) | SNDRV_PCM_HW_PARAM_BUFFER_SIZE    = 17
  constant SNDRV_PCM_HW_PARAM_BUFFER_BYTES (line 95) | SNDRV_PCM_HW_PARAM_BUFFER_BYTES   = 18
  constant SNDRV_PCM_HW_PARAM_TICK_TIME (line 96) | SNDRV_PCM_HW_PARAM_TICK_TIME      = 19
  constant SNDRV_PCM_HW_PARAM_FIRST_INTERVAL (line 97) | SNDRV_PCM_HW_PARAM_FIRST_INTERVAL = 8
  constant SNDRV_PCM_HW_PARAM_LAST_INTERVAL (line 98) | SNDRV_PCM_HW_PARAM_LAST_INTERVAL  = 19
  constant SNDRV_MASK_MAX (line 100) | SNDRV_MASK_MAX = 256
  constant SNDRV_PCM_TSTAMP_NONE (line 102) | SNDRV_PCM_TSTAMP_NONE   = 0
  constant SNDRV_PCM_TSTAMP_ENABLE (line 103) | SNDRV_PCM_TSTAMP_ENABLE = 1
  type snd_mask (line 106) | type snd_mask struct
  type snd_interval (line 110) | type snd_interval struct
  type snd_pcm_hw_params (line 116) | type snd_pcm_hw_params struct
  type snd_pcm_sw_params (line 132) | type snd_pcm_sw_params struct

FILE: pkg/alsa/device/device_linux.go
  type Device (line 9) | type Device struct
    method Close (line 43) | func (d *Device) Close() error {
    method IsCapture (line 47) | func (d *Device) IsCapture() bool {
    method Info (line 62) | func (d *Device) Info() (*Info, error) {
    method CheckFormat (line 78) | func (d *Device) CheckFormat(format byte) bool {
    method ListFormats (line 82) | func (d *Device) ListFormats() (formats []byte) {
    method RangeRates (line 91) | func (d *Device) RangeRates() (uint32, uint32) {
    method RangeChannels (line 95) | func (d *Device) RangeChannels() (byte, byte) {
    method GetRateNear (line 100) | func (d *Device) GetRateNear(rate uint32) uint32 {
    method GetChannelsNear (line 111) | func (d *Device) GetChannelsNear(channels byte) byte {
    method SetHWParams (line 124) | func (d *Device) SetHWParams(format byte, rate uint32, channels byte) ...
    method Write (line 169) | func (d *Device) Write(b []byte) (n int, err error) {
    method Read (line 184) | func (d *Device) Read(b []byte) (n int, err error) {
    method init (line 194) | func (d *Device) init() {
    method setInterval (line 208) | func (d *Device) setInterval(param, val uint32) {
    method setIntervalMin (line 214) | func (d *Device) setIntervalMin(param, val uint32) {
    method getInterval (line 218) | func (d *Device) getInterval(param uint32) (uint32, uint32) {
    method setMask (line 223) | func (d *Device) setMask(mask, val uint32) {
    method checkMask (line 229) | func (d *Device) checkMask(mask, val uint32) bool {
  function Open (line 17) | func Open(path string) (*Device, error) {
  type Info (line 52) | type Info struct
  constant bufferSize (line 122) | bufferSize = 4096

FILE: pkg/alsa/device/ioctl_linux.go
  function ioctl (line 9) | func ioctl(fd, req uintptr, arg any) error {
  function str (line 21) | func str(b []byte) string {

FILE: pkg/alsa/open_linux.go
  function Open (line 12) | func Open(rawURL string) (core.Producer, error) {

FILE: pkg/alsa/playback_linux.go
  type Playback (line 12) | type Playback struct
    method GetTrack (line 40) | func (p *Playback) GetTrack(media *core.Media, codec *core.Codec) (*co...
    method AddTrack (line 44) | func (p *Playback) AddTrack(media *core.Media, codec *core.Codec, trac...
    method Start (line 77) | func (p *Playback) Start() (err error) {
    method Stop (line 81) | func (p *Playback) Stop() error {
  function newPlayback (line 18) | func newPlayback(dev *device.Device) (*Playback, error) {

FILE: pkg/ascii/ascii.go
  function NewWriter (line 12) | func NewWriter(w io.Writer, foreground, background, text string) io.Writ...
  type writer (line 94) | type writer struct
    method Write (line 107) | func (a *writer) Write(p []byte) (n int, err error) {
    method appendEsc (line 140) | func (a *writer) appendEsc(s string) {
  constant csiClear (line 104) | csiClear = "\033[2J"
  constant csiHome (line 105) | csiHome = "\033[H"
  function gray (line 147) | func gray(r, g, b uint32, k float32) uint8 {
  constant x256r (line 152) | x256r = "\x00\x80\x00\x80\x00\x80\x00\xc0\x80\xff\x00\xff\x00\xff\x00\xf...
  constant x256g (line 153) | x256g = "\x00\x00\x80\x80\x00\x00\x80\xc0\x80\x00\xff\xff\x00\x00\xff\xf...
  constant x256b (line 154) | x256b = "\x00\x00\x00\x00\x80\x80\x80\xc0\x80\x00\x00\x00\xff\xff\xff\xf...
  function xterm256color (line 156) | func xterm256color(r, g, b uint8, n int) (index uint8) {
  function sqDiff (line 169) | func sqDiff(x, y uint8) uint16 {

FILE: pkg/bits/reader.go
  type Reader (line 3) | type Reader struct
    method ReadByte (line 17) | func (r *Reader) ReadByte() byte {
    method ReadUint16 (line 32) | func (r *Reader) ReadUint16() uint16 {
    method ReadUint24 (line 39) | func (r *Reader) ReadUint24() uint32 {
    method ReadUint32 (line 46) | func (r *Reader) ReadUint32() uint32 {
    method ReadBit (line 53) | func (r *Reader) ReadBit() byte {
    method ReadBits (line 64) | func (r *Reader) ReadBits(n byte) (res uint32) {
    method ReadBits8 (line 71) | func (r *Reader) ReadBits8(n byte) (res uint8) {
    method ReadBits16 (line 78) | func (r *Reader) ReadBits16(n byte) (res uint16) {
    method ReadBits64 (line 85) | func (r *Reader) ReadBits64(n byte) (res uint64) {
    method ReadFloat32 (line 92) | func (r *Reader) ReadFloat32() float64 {
    method ReadBytes (line 98) | func (r *Reader) ReadBytes(n int) (b []byte) {
    method ReadUEGolomb (line 118) | func (r *Reader) ReadUEGolomb() uint32 {
    method ReadSEGolomb (line 129) | func (r *Reader) ReadSEGolomb() int32 {
    method Left (line 137) | func (r *Reader) Left() []byte {
    method Pos (line 141) | func (r *Reader) Pos() (int, byte) {
  function NewReader (line 12) | func NewReader(b []byte) *Reader {

FILE: pkg/bits/writer.go
  type Writer (line 3) | type Writer struct
    method WriteByte (line 14) | func (w *Writer) WriteByte(b byte) {
    method WriteBit (line 22) | func (w *Writer) WriteBit(b byte) {
    method WriteBits (line 34) | func (w *Writer) WriteBits(v uint32, n byte) {
    method WriteBits16 (line 40) | func (w *Writer) WriteBits16(v uint16, n byte) {
    method WriteBits8 (line 46) | func (w *Writer) WriteBits8(v, n byte) {
    method WriteAllBits (line 52) | func (w *Writer) WriteAllBits(bit, n byte) {
    method WriteBool (line 58) | func (w *Writer) WriteBool(b bool) {
    method WriteUint16 (line 66) | func (w *Writer) WriteUint16(v uint16) {
    method WriteBytes (line 74) | func (w *Writer) WriteBytes(bytes ...byte) {
    method Bytes (line 84) | func (w *Writer) Bytes() []byte {
    method Len (line 88) | func (w *Writer) Len() int {
    method Reset (line 92) | func (w *Writer) Reset() {
  function NewWriter (line 9) | func NewWriter(buf []byte) *Writer {

FILE: pkg/bubble/client.go
  type Client (line 26) | type Client struct
    method Dial (line 64) | func (c *Client) Dial() (err error) {
    method Write (line 155) | func (c *Client) Write(command byte, timestamp uint32, payload []byte)...
    method Read (line 172) | func (c *Client) Read() (byte, []byte, error) {
    method Play (line 198) | func (c *Client) Play() error {
    method Handle (line 207) | func (c *Client) Handle() error {
    method Close (line 264) | func (c *Client) Close() error {
  function Dial (line 47) | func Dial(rawURL string) (*Client, error) {
  constant SyncByte (line 56) | SyncByte    = 0xAA
  constant PacketAuth (line 57) | PacketAuth  = 0x00
  constant PacketMedia (line 58) | PacketMedia = 0x01
  constant PacketStart (line 59) | PacketStart = 0x0A
  constant Timeout (line 62) | Timeout = time.Second * 5

FILE: pkg/bubble/producer.go
  method GetMedias (line 9) | func (c *Client) GetMedias() []*core.Media {
  method GetTrack (line 32) | func (c *Client) GetTrack(media *core.Media, codec *core.Codec) (*core.R...
  method Start (line 53) | func (c *Client) Start() error {
  method Stop (line 60) | func (c *Client) Stop() error {
  method MarshalJSON (line 67) | func (c *Client) MarshalJSON() ([]byte, error) {

FILE: pkg/core/codec.go
  type Codec (line 13) | type Codec struct
    method MarshalJSON (line 22) | func (c *Codec) MarshalJSON() ([]byte, error) {
    method String (line 82) | func (c *Codec) String() (s string) {
    method IsRTP (line 93) | func (c *Codec) IsRTP() bool {
    method IsVideo (line 97) | func (c *Codec) IsVideo() bool {
    method IsAudio (line 101) | func (c *Codec) IsAudio() bool {
    method Kind (line 105) | func (c *Codec) Kind() string {
    method PrintName (line 109) | func (c *Codec) PrintName() string {
    method Clone (line 121) | func (c *Codec) Clone() *Codec {
    method Match (line 126) | func (c *Codec) Match(remote *Codec) bool {
  function FFmpegCodecName (line 44) | func FFmpegCodecName(name string) string {
  function UnmarshalCodec (line 137) | func UnmarshalCodec(md *sdp.MediaDescription, payloadType string) *Codec {
  function DecodeH264 (line 231) | func DecodeH264(fmtp string) (profile string, level byte) {
  function ParseCodecString (line 253) | func ParseCodecString(s string) *Codec {

FILE: pkg/core/connection.go
  function NewID (line 10) | func NewID() uint32 {
  function ID (line 15) | func ID(v any) uint32 {
  type Info (line 22) | type Info interface
  type Connection (line 36) | type Connection struct
    method GetMedias (line 55) | func (c *Connection) GetMedias() []*Media {
    method GetTrack (line 59) | func (c *Connection) GetTrack(media *Media, codec *Codec) (*Receiver, ...
    method Stop (line 70) | func (c *Connection) Stop() error {
    method Codecs (line 84) | func (c *Connection) Codecs() []*Codec {
    method SetProtocol (line 92) | func (c *Connection) SetProtocol(s string) {
    method SetRemoteAddr (line 96) | func (c *Connection) SetRemoteAddr(s string) {
    method SetSource (line 104) | func (c *Connection) SetSource(s string) {
    method SetURL (line 108) | func (c *Connection) SetURL(s string) {
    method WithRequest (line 112) | func (c *Connection) WithRequest(r *http.Request) {
    method GetSource (line 127) | func (c *Connection) GetSource() string {
  function Create (line 132) | func Create(w io.Writer) (*Connection, error) {
  function Open (line 137) | func Open(r io.Reader) (*Connection, error) {
  function Dial (line 142) | func Dial(rawURL string) (*Connection, error) {

FILE: pkg/core/core.go
  constant DirectionRecvonly (line 6) | DirectionRecvonly = "recvonly"
  constant DirectionSendonly (line 7) | DirectionSendonly = "sendonly"
  constant DirectionSendRecv (line 8) | DirectionSendRecv = "sendrecv"
  constant KindVideo (line 12) | KindVideo = "video"
  constant KindAudio (line 13) | KindAudio = "audio"
  constant CodecH264 (line 17) | CodecH264 = "H264"
  constant CodecH265 (line 18) | CodecH265 = "H265"
  constant CodecVP8 (line 19) | CodecVP8  = "VP8"
  constant CodecVP9 (line 20) | CodecVP9  = "VP9"
  constant CodecAV1 (line 21) | CodecAV1  = "AV1"
  constant CodecJPEG (line 22) | CodecJPEG = "JPEG"
  constant CodecRAW (line 23) | CodecRAW  = "RAW"
  constant CodecPCMU (line 25) | CodecPCMU = "PCMU"
  constant CodecPCMA (line 26) | CodecPCMA = "PCMA"
  constant CodecAAC (line 27) | CodecAAC  = "MPEG4-GENERIC"
  constant CodecOpus (line 28) | CodecOpus = "OPUS"
  constant CodecG722 (line 29) | CodecG722 = "G722"
  constant CodecMP3 (line 30) | CodecMP3  = "MPA"
  constant CodecPCM (line 31) | CodecPCM  = "L16"
  constant CodecPCML (line 33) | CodecPCML = "PCML"
  constant CodecELD (line 35) | CodecELD  = "ELD"
  constant CodecFLAC (line 36) | CodecFLAC = "FLAC"
  constant CodecAll (line 38) | CodecAll = "ALL"
  constant CodecAny (line 39) | CodecAny = "ANY"
  constant PayloadTypeRAW (line 42) | PayloadTypeRAW byte = 255
  type Producer (line 44) | type Producer interface
  type Consumer (line 60) | type Consumer interface
  type Mode (line 72) | type Mode
    method String (line 81) | func (m Mode) String() string {
    method MarshalJSON (line 95) | func (m Mode) MarshalJSON() ([]byte, error) {
  constant ModeActiveProducer (line 75) | ModeActiveProducer Mode = iota + 1
  constant ModePassiveConsumer (line 76) | ModePassiveConsumer
  constant ModePassiveProducer (line 77) | ModePassiveProducer
  constant ModeActiveConsumer (line 78) | ModeActiveConsumer

FILE: pkg/core/core_test.go
  type producer (line 10) | type producer struct
    method GetMedias (line 17) | func (p *producer) GetMedias() []*Media {
    method GetTrack (line 21) | func (p *producer) GetTrack(_ *Media, codec *Codec) (*Receiver, error) {
    method Start (line 32) | func (p *producer) Start() error {
    method Stop (line 38) | func (p *producer) Stop() error {
  type consumer (line 45) | type consumer struct
    method GetMedias (line 52) | func (c *consumer) GetMedias() []*Media {
    method AddTrack (line 56) | func (c *consumer) AddTrack(_ *Media, _ *Codec, track *Receiver) error {
    method Stop (line 67) | func (c *consumer) Stop() error {
    method read (line 74) | func (c *consumer) read() byte {
  function TestName (line 78) | func TestName(t *testing.T) {
  function TestStripUserinfo (line 122) | func TestStripUserinfo(t *testing.T) {

FILE: pkg/core/helpers.go
  constant BufferSize (line 12) | BufferSize      = 64 * 1024
  constant ConnDialTimeout (line 13) | ConnDialTimeout = 5 * time.Second
  constant ConnDeadline (line 14) | ConnDeadline    = 5 * time.Second
  constant ProbeTimeout (line 15) | ProbeTimeout    = 5 * time.Second
  function Now90000 (line 19) | func Now90000() uint32 {
  constant symbols (line 23) | symbols = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXY...
  function RandString (line 27) | func RandString(size, base byte) string {
  function Before (line 41) | func Before(s, sep string) string {
  function Between (line 48) | func Between(s, sub1, sub2 string) string {
  function Atoi (line 62) | func Atoi(s string) (i int) {
  function ParseByte (line 70) | func ParseByte(s string) (b byte) {
  function Assert (line 84) | func Assert(ok bool) {
  function Caller (line 91) | func Caller() string {

FILE: pkg/core/listener.go
  type EventFunc (line 3) | type EventFunc
  type Listener (line 6) | type Listener struct
    method Listen (line 10) | func (l *Listener) Listen(f EventFunc) {
    method Fire (line 14) | func (l *Listener) Fire(msg any) {

FILE: pkg/core/media.go
  type Media (line 14) | type Media struct
    method String (line 22) | func (m *Media) String() string {
    method MarshalJSON (line 36) | func (m *Media) MarshalJSON() ([]byte, error) {
    method Clone (line 40) | func (m *Media) Clone() *Media {
    method MatchMedia (line 49) | func (m *Media) MatchMedia(remote *Media) (codec, remoteCodec *Codec) {
    method MatchCodec (line 68) | func (m *Media) MatchCodec(remote *Codec) *Codec {
    method MatchAll (line 77) | func (m *Media) MatchAll() bool {
    method Equal (line 86) | func (m *Media) Equal(media *Media) bool {
  function GetKind (line 93) | func GetKind(name string) string {
  function MarshalSDP (line 103) | func MarshalSDP(name string, medias []*Media) ([]byte, error) {
  function UnmarshalMedia (line 158) | func UnmarshalMedia(md *sdp.MediaDescription) *Media {
  function ParseQuery (line 179) | func ParseQuery(query map[string][]string) (medias []*Media) {

FILE: pkg/core/media_test.go
  function TestSDP (line 13) | func TestSDP(t *testing.T) {
  function TestParseQuery (line 29) | func TestParseQuery(t *testing.T) {
  function TestClone (line 47) | func TestClone(t *testing.T) {

FILE: pkg/core/node.go
  type HandlerFunc (line 19) | type HandlerFunc
  type Filter (line 22) | type Filter
  type Node (line 25) | type Node struct
    method WithParent (line 37) | func (n *Node) WithParent(parent *Node) *Node {
    method AppendChild (line 42) | func (n *Node) AppendChild(child *Node) {
    method RemoveChild (line 50) | func (n *Node) RemoveChild(child *Node) {
    method Close (line 61) | func (n *Node) Close() {
  function MoveNode (line 75) | func MoveNode(dst, src *Node) {

FILE: pkg/core/readbuffer.go
  constant ProbeSize (line 10) | ProbeSize = 5 * 1024 * 1024
  constant BufferDisable (line 13) | BufferDisable       = 0
  constant BufferDrainAndClear (line 14) | BufferDrainAndClear = -1
  type ReadBuffer (line 23) | type ReadBuffer struct
    method Read (line 39) | func (r *ReadBuffer) Read(p []byte) (n int, err error) {
    method Close (line 70) | func (r *ReadBuffer) Close() error {
    method Seek (line 77) | func (r *ReadBuffer) Seek(offset int64, whence int) (int64, error) {
    method Peek (line 101) | func (r *ReadBuffer) Peek(n int) ([]byte, error) {
    method Reset (line 111) | func (r *ReadBuffer) Reset() {
  function NewReadBuffer (line 32) | func NewReadBuffer(rd io.Reader) *ReadBuffer {

FILE: pkg/core/readbuffer_test.go
  function TestReadSeeker (line 11) | func TestReadSeeker(t *testing.T) {

FILE: pkg/core/slices.go
  function Index (line 8) | func Index[S ~[]E, E comparable](s S, v E) int {
  function Contains (line 18) | func Contains[S ~[]E, E comparable](s S, v E) bool {
  type Ordered (line 22) | type Ordered interface
  function Max (line 32) | func Max[S ~[]E, E Ordered](x S) E {

FILE: pkg/core/track.go
  type Receiver (line 12) | type Receiver struct
    method WriteRTP (line 40) | func (r *Receiver) WriteRTP(packet *rtp.Packet) {
    method Senders (line 45) | func (r *Receiver) Senders() []*Sender {
    method Replace (line 54) | func (r *Receiver) Replace(target *Receiver) {
    method Close (line 58) | func (r *Receiver) Close() {
    method MarshalJSON (line 179) | func (r *Receiver) MarshalJSON() ([]byte, error) {
  function NewReceiver (line 24) | func NewReceiver(media *Media, codec *Codec) *Receiver {
  type Sender (line 62) | type Sender struct
    method HandleRTP (line 118) | func (s *Sender) HandleRTP(parent *Receiver) {
    method Bind (line 124) | func (s *Sender) Bind(parent *Receiver) {
    method WithParent (line 128) | func (s *Sender) WithParent(parent *Receiver) *Sender {
    method Start (line 133) | func (s *Sender) Start() {
    method Wait (line 151) | func (s *Sender) Wait() {
    method State (line 157) | func (s *Sender) State() string {
    method Close (line 167) | func (s *Sender) Close() {
    method MarshalJSON (line 198) | func (s *Sender) MarshalJSON() ([]byte, error) {
  function NewSender (line 78) | func NewSender(media *Media, codec *Codec) *Sender {

FILE: pkg/core/track_test.go
  function TestSenser (line 9) | func TestSenser(t *testing.T) {

FILE: pkg/core/waiter.go
  type Waiter (line 11) | type Waiter struct
    method Add (line 18) | func (w *Waiter) Add(delta int) {
    method Wait (line 27) | func (w *Waiter) Wait() error {
    method Done (line 41) | func (w *Waiter) Done(err error) {
    method WaitChan (line 59) | func (w *Waiter) WaitChan() <-chan error {

FILE: pkg/core/worker.go
  type Worker (line 7) | type Worker struct
    method Do (line 36) | func (w *Worker) Do() {
    method Stop (line 43) | func (w *Worker) Stop() {
  function NewWorker (line 13) | func NewWorker(d time.Duration, f func() time.Duration) *Worker {

FILE: pkg/core/writebuffer.go
  type WriteBuffer (line 14) | type WriteBuffer struct
    method Write (line 29) | func (w *WriteBuffer) Write(p []byte) (n int, err error) {
    method WriteTo (line 43) | func (w *WriteBuffer) WriteTo(wr io.Writer) (n int64, err error) {
    method Close (line 49) | func (w *WriteBuffer) Close() error {
    method Reset (line 59) | func (w *WriteBuffer) Reset(wr io.Writer) {
    method add (line 78) | func (w *WriteBuffer) add() {
    method done (line 85) | func (w *WriteBuffer) done() {
  function NewWriteBuffer (line 22) | func NewWriteBuffer(wr io.Writer) *WriteBuffer {
  constant none (line 73) | none = iota
  constant start (line 74) | start
  constant end (line 75) | end
  type OnceBuffer (line 93) | type OnceBuffer struct
    method Write (line 97) | func (o *OnceBuffer) Write(p []byte) (n int, err error) {
    method WriteTo (line 104) | func (o *OnceBuffer) WriteTo(w io.Writer) (n int64, err error) {
    method Buffer (line 108) | func (o *OnceBuffer) Buffer() []byte {
    method Len (line 112) | func (o *OnceBuffer) Len() int {

FILE: pkg/creds/creds.go
  type Storage (line 11) | type Storage interface
  function SetStorage (line 18) | func SetStorage(s Storage) {
  function SetValue (line 22) | func SetValue(name, value string) error {
  function GetValue (line 33) | func GetValue(name string) (value string, ok bool) {
  function getValue (line 39) | func getValue(name string) (string, bool) {
  function ReplaceVars (line 56) | func ReplaceVars(data []byte) []byte {

FILE: pkg/creds/secrets.go
  function AddSecret (line 12) | func AddSecret(value string) {
  function getReplacer (line 33) | func getReplacer() *strings.Replacer {
  constant unreserved (line 55) | unreserved = `A-Za-z0-9-._~`
  constant subdelims (line 56) | subdelims  = `!$&'()*+,;=`
  constant userinfo (line 57) | userinfo   = unreserved + subdelims + `%:`
  function SecretString (line 60) | func SecretString(s string) string {
  function SecretWrite (line 66) | func SecretWrite(w io.Writer, s string) (n int, err error) {
  function SecretWriter (line 72) | func SecretWriter(w io.Writer) io.Writer {
  type secretWriter (line 76) | type secretWriter struct
    method Write (line 80) | func (s *secretWriter) Write(b []byte) (int, error) {
  function SecretResponse (line 84) | func SecretResponse(w http.ResponseWriter) http.ResponseWriter {
  type secretResponse (line 88) | type secretResponse struct
    method Write (line 92) | func (s *secretResponse) Write(b []byte) (int, error) {

FILE: pkg/creds/secrets_test.go
  function TestString (line 9) | func TestString(t *testing.T) {

FILE: pkg/debug/conn.go
  type badConn (line 9) | type badConn struct
    method Read (line 24) | func (c *badConn) Read(b []byte) (n int, err error) {
  function NewBadConn (line 15) | func NewBadConn(conn net.Conn) net.Conn {
  constant missChance (line 20) | missChance  = 0.05
  constant delayChance (line 21) | delayChance = 0.1

FILE: pkg/debug/debug.go
  function Logger (line 10) | func Logger(include func(packet *rtp.Packet) bool) func(packet *rtp.Pack...

FILE: pkg/doorbird/backchannel.go
  type Client (line 13) | type Client struct
    method GetTrack (line 71) | func (c *Client) GetTrack(media *core.Media, codec *core.Codec) (*core...
    method AddTrack (line 75) | func (c *Client) AddTrack(media *core.Media, codec *core.Codec, track ...
    method Start (line 90) | func (c *Client) Start() (err error) {
  function Dial (line 18) | func Dial(rawURL string) (*Client, error) {

FILE: pkg/dvrip/backchannel.go
  type Backchannel (line 11) | type Backchannel struct
    method GetTrack (line 16) | func (c *Backchannel) GetTrack(media *core.Media, codec *core.Codec) (...
    method Start (line 20) | func (c *Backchannel) Start() error {
    method AddTrack (line 33) | func (c *Backchannel) AddTrack(media *core.Media, _ *core.Codec, track...

FILE: pkg/dvrip/client.go
  constant Login (line 18) | Login          = 1000
  constant OPMonitorClaim (line 19) | OPMonitorClaim = 1413
  constant OPMonitorStart (line 20) | OPMonitorStart = 1410
  constant OPTalkClaim (line 21) | OPTalkClaim    = 1434
  constant OPTalkStart (line 22) | OPTalkStart    = 1430
  constant OPTalkData (line 23) | OPTalkData     = 1432
  type Client (line 26) | type Client struct
    method Dial (line 36) | func (c *Client) Dial(rawURL string) (err error) {
    method Close (line 82) | func (c *Client) Close() error {
    method Login (line 86) | func (c *Client) Login(user, pass string) (err error) {
    method Play (line 100) | func (c *Client) Play() error {
    method Talk (line 116) | func (c *Client) Talk() error {
    method WriteCmd (line 132) | func (c *Client) WriteCmd(cmd uint16, payload []byte) (n int, err erro...
    method ReadChunk (line 150) | func (c *Client) ReadChunk() (b []byte, err error) {
    method ReadPacket (line 175) | func (c *Client) ReadPacket() (pType byte, payload []byte, err error) {
    method ReadJSON (line 219) | func (c *Client) ReadJSON() (res Response, err error) {
  type Response (line 217) | type Response
  function SofiaHash (line 236) | func SofiaHash(password string) string {

FILE: pkg/dvrip/dvrip.go
  function Dial (line 5) | func Dial(url string) (core.Producer, error) {

FILE: pkg/dvrip/producer.go
  type Producer (line 17) | type Producer struct
    method Start (line 30) | func (c *Producer) Start() error {
    method probe (line 95) | func (c *Producer) probe() error {
    method addVideoTrack (line 165) | func (c *Producer) addVideoTrack(mediaCode byte, payload []byte) {
    method addAudioTrack (line 220) | func (c *Producer) addAudioTrack(mediaCode byte, sampleRate byte) {

FILE: pkg/eseecloud/eseecloud.go
  type Producer (line 17) | type Producer struct
    method probe (line 61) | func (p *Producer) probe() error {
    method Start (line 112) | func (p *Producer) Start() error {
    method readPacket (line 155) | func (p *Producer) readPacket() (*core.Packet, error) {
  function Dial (line 24) | func Dial(rawURL string) (core.Producer, error) {
  function Open (line 44) | func Open(r io.Reader) (core.Producer, error) {

FILE: pkg/expr/expr.go
  function newRequest (line 19) | func newRequest(rawURL string, options map[string]any) (*http.Request, e...
  function kvToString (line 68) | func kvToString(kv map[string]any) map[string][]string {
  function regExp (line 76) | func regExp(params ...any) (*regexp.Regexp, error) {
  function Compile (line 90) | func Compile(input string) (*vm.Program, error) {
  function Eval (line 155) | func Eval(input string, env any) (any, error) {
  function Run (line 164) | func Run(program *vm.Program, env any) (any, error) {

FILE: pkg/expr/expr_test.go
  function TestMatchHost (line 9) | func TestMatchHost(t *testing.T) {

FILE: pkg/ffmpeg/ffmpeg.go
  constant Version50 (line 11) | Version50 = "59. 16"
  constant Version51 (line 12) | Version51 = "59. 27"
  constant Version60 (line 13) | Version60 = "60.  3"
  constant Version61 (line 14) | Version61 = "60. 16"
  constant Version70 (line 15) | Version70 = "61.  1"
  type Args (line 18) | type Args struct
    method AddCodec (line 30) | func (a *Args) AddCodec(codec string) {
    method AddFilter (line 34) | func (a *Args) AddFilter(filter string) {
    method InsertFilter (line 38) | func (a *Args) InsertFilter(filter string) {
    method HasFilters (line 42) | func (a *Args) HasFilters(filters ...string) bool {
    method String (line 54) | func (a *Args) String() string {
  function ParseVersion (line 110) | func ParseVersion(b []byte) (ffmpeg string, libavformat string) {

FILE: pkg/flussonic/flussonic.go
  type Producer (line 14) | type Producer struct
    method probe (line 49) | func (p *Producer) probe() error {
    method Start (line 112) | func (p *Producer) Start() error {
  function Dial (line 22) | func Dial(source string) (core.Producer, error) {

FILE: pkg/flv/amf/amf.go
  constant TypeNumber (line 10) | TypeNumber byte = iota
  constant TypeBoolean (line 11) | TypeBoolean
  constant TypeString (line 12) | TypeString
  constant TypeObject (line 13) | TypeObject
  constant TypeNull (line 14) | TypeNull      = 5
  constant TypeEcmaArray (line 15) | TypeEcmaArray = 8
  constant TypeObjectEnd (line 16) | TypeObjectEnd = 9
  type AMF (line 20) | type AMF struct
    method ReadItems (line 31) | func (a *AMF) ReadItems() ([]any, error) {
    method ReadItem (line 43) | func (a *AMF) ReadItem() (any, error) {
    method ReadByte (line 76) | func (a *AMF) ReadByte() (byte, error) {
    method ReadNumber (line 86) | func (a *AMF) ReadNumber() (float64, error) {
    method ReadString (line 96) | func (a *AMF) ReadString() (string, error) {
    method ReadObject (line 114) | func (a *AMF) ReadObject() (map[string]any, error) {
    method ReadEcmaArray (line 138) | func (a *AMF) ReadEcmaArray() (map[string]any, error) {
    method Bytes (line 151) | func (a *AMF) Bytes() []byte {
    method WriteNumber (line 155) | func (a *AMF) WriteNumber(n float64) {
    method WriteBool (line 164) | func (a *AMF) WriteBool(b bool) {
    method WriteString (line 172) | func (a *AMF) WriteString(s string) {
    method WriteObject (line 178) | func (a *AMF) WriteObject(obj map[string]any) {
    method WriteEcmaArray (line 184) | func (a *AMF) WriteEcmaArray(obj map[string]any) {
    method writeKV (line 191) | func (a *AMF) writeKV(obj map[string]any) {
    method WriteNull (line 216) | func (a *AMF) WriteNull() {
  function NewReader (line 27) | func NewReader(b []byte) *AMF {
  function NewWriter (line 147) | func NewWriter() *AMF {
  function EncodeItems (line 220) | func EncodeItems(items ...any) []byte {

FILE: pkg/flv/amf/amf_test.go
  function TestNewReader (line 10) | func TestNewReader(t *testing.T) {

FILE: pkg/flv/consumer.go
  type Consumer (line 12) | type Consumer struct
    method AddTrack (line 48) | func (c *Consumer) AddTrack(media *core.Media, codec *core.Codec, trac...
    method WriteTo (line 88) | func (c *Consumer) WriteTo(wr io.Writer) (int64, error) {
  function NewConsumer (line 18) | func NewConsumer() *Consumer {

FILE: pkg/flv/flv_test.go
  function TestTimeToRTP (line 9) | func TestTimeToRTP(t *testing.T) {

FILE: pkg/flv/muxer.go
  type Muxer (line 13) | type Muxer struct
    method GetInit (line 22) | func (m *Muxer) GetInit() []byte {
    method GetPayloader (line 79) | func (m *Muxer) GetPayloader(codec *core.Codec) func(packet *rtp.Packe...
  constant FlagsVideo (line 18) | FlagsVideo = 0b001
  constant FlagsAudio (line 19) | FlagsAudio = 0b100
  function EncodeTag (line 124) | func EncodeTag(tagType byte, timeMS uint32, payload []byte) []byte {
  function encodeAVData (line 143) | func encodeAVData(codec *core.Codec, isFrame byte) []byte {

FILE: pkg/flv/producer.go
  type Producer (line 17) | type Producer struct
    method GetTrack (line 67) | func (c *Producer) GetTrack(media *core.Media, codec *core.Codec) (*co...
    method Start (line 77) | func (c *Producer) Start() error {
    method probe (line 128) | func (c *Producer) probe() error {
    method readHeader (line 253) | func (c *Producer) readHeader() error {
    method readPacket (line 274) | func (c *Producer) readPacket() (*rtp.Packet, error) {
  function Open (line 24) | func Open(rd io.Reader) (*Producer, error) {
  constant Signature (line 40) | Signature = "FLV"
  constant TagAudio (line 42) | TagAudio = 8
  constant TagVideo (line 43) | TagVideo = 9
  constant TagData (line 44) | TagData  = 18
  constant CodecAAC (line 46) | CodecAAC = 10
  constant CodecH264 (line 48) | CodecH264 = 7
  constant CodecHEVC (line 49) | CodecHEVC = 12
  constant PacketTypeAVCHeader (line 53) | PacketTypeAVCHeader = iota
  constant PacketTypeAVCNALU (line 54) | PacketTypeAVCNALU
  constant PacketTypeAVCEnd (line 55) | PacketTypeAVCEnd
  constant PacketTypeSequenceStart (line 59) | PacketTypeSequenceStart = iota
  constant PacketTypeCodedFrames (line 60) | PacketTypeCodedFrames
  constant PacketTypeSequenceEnd (line 61) | PacketTypeSequenceEnd
  constant PacketTypeCodedFramesX (line 62) | PacketTypeCodedFramesX
  constant PacketTypeMetadata (line 63) | PacketTypeMetadata
  constant PacketTypeMPEG2TSSequenceStart (line 64) | PacketTypeMPEG2TSSequenceStart
  function TimeToRTP (line 303) | func TimeToRTP(timeMS, clockRate uint32) uint32 {
  function isExHeader (line 310) | func isExHeader(data []byte) bool {

FILE: pkg/gopro/discovery.go
  function Discovery (line 9) | func Discovery() (urls []string) {

FILE: pkg/gopro/producer.go
  function Dial (line 14) | func Dial(rawURL string) (*mpegts.Producer, error) {
  type listener (line 45) | type listener struct
    method Read (line 52) | func (r *listener) Read(p []byte) (n int, err error) {
    method Close (line 71) | func (r *listener) Close() error {
    method command (line 75) | func (r *listener) command(api string) error {
    method listen (line 92) | func (r *listener) listen() (err error) {
    method worker (line 103) | func (r *listener) worker() {

FILE: pkg/h264/annexb/annexb.go
  constant StartCode (line 9) | StartCode = "\x00\x00\x00\x01"
  constant startAUD (line 10) | startAUD = StartCode + "\x09\xF0"
  constant startAUDstart (line 11) | startAUDstart = startAUD + StartCode
  function EncodeToAVCC (line 18) | func EncodeToAVCC(annexb []byte) (avc []byte) {
  function isAUD (line 64) | func isAUD(b byte) bool {
  function DecodeAVCC (line 70) | func DecodeAVCC(b []byte, safeClone bool) []byte {
  function DecodeAVCCWithAUD (line 86) | func DecodeAVCCWithAUD(src []byte) []byte {
  constant h264PFrame (line 95) | h264PFrame = 1
  constant h264IFrame (line 96) | h264IFrame = 5
  constant h264SPS (line 97) | h264SPS    = 7
  constant h264PPS (line 98) | h264PPS    = 8
  constant h265VPS (line 100) | h265VPS    = 32
  constant h265PFrame (line 101) | h265PFrame = 1
  function IndexFrame (line 105) | func IndexFrame(b []byte) int {
  function FixAnnexBInAVCC (line 139) | func FixAnnexBInAVCC(b []byte) []byte {

FILE: pkg/h264/annexb/annexb_test.go
  function decode (line 13) | func decode(s string) []byte {
  function naluTypes (line 18) | func naluTypes(avcc []byte) (types []byte) {
  function TestFFmpegH264 (line 32) | func TestFFmpegH264(t *testing.T) {
  function TestFFmpegMPEGTSH264 (line 41) | func TestFFmpegMPEGTSH264(t *testing.T) {
  function TestFFmpegHEVC (line 49) | func TestFFmpegHEVC(t *testing.T) {
  function TestFFmpegHEVC2 (line 57) | func TestFFmpegHEVC2(t *testing.T) {
  function TestFFmpegMPEGTSHEVC (line 65) | func TestFFmpegMPEGTSHEVC(t *testing.T) {
  function TestReolink (line 73) | func TestReolink(t *testing.T) {
  function TestDahua (line 80) | func TestDahua(t *testing.T) {
  function TestUSB (line 87) | func TestUSB(t *testing.T) {

FILE: pkg/h264/avc.go
  constant forbiddenZeroBit (line 8) | forbiddenZeroBit = 0x80
  constant nalUnitType (line 9) | nalUnitType = 0x1F
  function DecodeStream (line 13) | func DecodeStream(annexb []byte) ([]byte, int) {
  function DecodeAnnexB (line 63) | func DecodeAnnexB(b []byte) []byte {
  function IndexFrom (line 111) | func IndexFrom(b []byte, sep []byte, from int) int {

FILE: pkg/h264/avcc.go
  function RepairAVCC (line 14) | func RepairAVCC(codec *core.Codec, handler core.HandlerFunc) core.Handle...
  function JoinNALU (line 31) | func JoinNALU(nalus ...[]byte) (avcc []byte) {
  function SplitNALU (line 53) | func SplitNALU(avcc []byte) [][]byte {
  function NALUTypes (line 71) | func NALUTypes(avcc []byte) []byte {
  function AVCCToCodec (line 86) | func AVCCToCodec(avcc []byte) *core.Codec {

FILE: pkg/h264/h264.go
  constant NALUTypePFrame (line 14) | NALUTypePFrame = 1
  constant NALUTypeIFrame (line 15) | NALUTypeIFrame = 5
  constant NALUTypeSEI (line 16) | NALUTypeSEI    = 6
  constant NALUTypeSPS (line 17) | NALUTypeSPS    = 7
  constant NALUTypePPS (line 18) | NALUTypePPS    = 8
  constant NALUTypeAUD (line 19) | NALUTypeAUD    = 9
  function NALUType (line 22) | func NALUType(b []byte) byte {
  function IsKeyframe (line 27) | func IsKeyframe(b []byte) bool {
  function Join (line 46) | func Join(ps, iframe []byte) []byte {
  constant ProfileBaseline (line 55) | ProfileBaseline    = 0x42
  constant ProfileMain (line 56) | ProfileMain        = 0x4D
  constant ProfileHigh (line 57) | ProfileHigh        = 0x64
  constant CapabilityBaseline (line 58) | CapabilityBaseline = 0xE0
  constant CapabilityMain (line 59) | CapabilityMain     = 0x40
  function GetProfileLevelID (line 65) | func GetProfileLevelID(fmtp string) string {
  function GetParameterSet (line 103) | func GetParameterSet(fmtp string) (sps, pps []byte) {
  function GetFmtpLine (line 125) | func GetFmtpLine(avc []byte) string {

FILE: pkg/h264/h264_test.go
  function TestDecodeConfig (line 11) | func TestDecodeConfig(t *testing.T) {
  function TestDecodeSPS (line 25) | func TestDecodeSPS(t *testing.T) {
  function TestGetProfileLevelID (line 75) | func TestGetProfileLevelID(t *testing.T) {
  function TestDecodeSPS2 (line 87) | func TestDecodeSPS2(t *testing.T) {
  function TestAVCCToCodec (line 105) | func TestAVCCToCodec(t *testing.T) {

FILE: pkg/h264/mpeg4.go
  function DecodeConfig (line 14) | func DecodeConfig(conf []byte) (profile []byte, sps []byte, pps []byte) {
  function EncodeConfig (line 56) | func EncodeConfig(sps, pps []byte) []byte {
  function ConfigToCodec (line 80) | func ConfigToCodec(conf []byte) *core.Codec {

FILE: pkg/h264/payloader.go
  type Payloader (line 6) | type Payloader struct
    method Payload (line 85) | func (p *Payloader) Payload(mtu uint16, payload []byte) [][]byte {
  constant stapaNALUType (line 12) | stapaNALUType  = 24
  constant fuaNALUType (line 13) | fuaNALUType    = 28
  constant fubNALUType (line 14) | fubNALUType    = 29
  constant spsNALUType (line 15) | spsNALUType    = 7
  constant ppsNALUType (line 16) | ppsNALUType    = 8
  constant audNALUType (line 17) | audNALUType    = 9
  constant fillerNALUType (line 18) | fillerNALUType = 12
  constant fuaHeaderSize (line 20) | fuaHeaderSize = 2
  constant naluTypeBitmask (line 24) | naluTypeBitmask   = 0x1F
  constant naluRefIdcBitmask (line 25) | naluRefIdcBitmask = 0x60
  constant outputStapAHeader (line 29) | outputStapAHeader = 0x78
  function EmitNalus (line 34) | func EmitNalus(nals []byte, isAVC bool, emit func([]byte)) {
  function min (line 190) | func min(a, b int) int {

FILE: pkg/h264/rtp.go
  constant RTPPacketVersionAVC (line 12) | RTPPacketVersionAVC = 0
  constant PSMaxSize (line 14) | PSMaxSize = 128
  function RTPDepay (line 16) | func RTPDepay(codec *core.Codec, handler core.HandlerFunc) core.HandlerF...
  function RTPPay (line 107) | func RTPPay(mtu uint16, handler core.HandlerFunc) core.HandlerFunc {

FILE: pkg/h264/sps.go
  type SPS (line 13) | type SPS struct
    method Width (line 75) | func (s *SPS) Width() uint16 {
    method Height (line 81) | func (s *SPS) Height() uint16 {
    method scaling_list (line 225) | func (s *SPS) scaling_list(r *bits.Reader, sizeOfScalingList int) {
    method Profile (line 239) | func (s *SPS) Profile() string {
    method PixFmt (line 253) | func (s *SPS) PixFmt() string {
    method String (line 270) | func (s *SPS) String() string {
  function DecodeSPS (line 90) | func DecodeSPS(sps []byte) *SPS {
  function FixPixFmt (line 279) | func FixPixFmt(sps []byte) {

FILE: pkg/h265/avc.go
  constant forbiddenZeroBit (line 5) | forbiddenZeroBit = 0x80
  constant nalUnitType (line 6) | nalUnitType = 0x3F
  function DecodeStream (line 10) | func DecodeStream(annexb []byte) ([]byte, int) {

FILE: pkg/h265/avcc.go
  function RepairAVCC (line 14) | func RepairAVCC(codec *core.Codec, handler core.HandlerFunc) core.Handle...
  function AVCCToCodec (line 30) | func AVCCToCodec(avcc []byte) *core.Codec {

FILE: pkg/h265/h265_test.go
  function TestDecodeSPS (line 10) | func TestDecodeSPS(t *testing.T) {
  function TestDecodeSPS2 (line 21) | func TestDecodeSPS2(t *testing.T) {

FILE: pkg/h265/helper.go
  constant NALUTypePFrame (line 11) | NALUTypePFrame    = 1
  constant NALUTypeIFrame (line 12) | NALUTypeIFrame    = 19
  constant NALUTypeIFrame2 (line 13) | NALUTypeIFrame2   = 20
  constant NALUTypeIFrame3 (line 14) | NALUTypeIFrame3   = 21
  constant NALUTypeVPS (line 15) | NALUTypeVPS       = 32
  constant NALUTypeSPS (line 16) | NALUTypeSPS       = 33
  constant NALUTypePPS (line 17) | NALUTypePPS       = 34
  constant NALUTypePrefixSEI (line 18) | NALUTypePrefixSEI = 39
  constant NALUTypeSuffixSEI (line 19) | NALUTypeSuffixSEI = 40
  constant NALUTypeFU (line 20) | NALUTypeFU        = 49
  function NALUType (line 23) | func NALUType(b []byte) byte {
  function IsKeyframe (line 27) | func IsKeyframe(b []byte) bool {
  function Types (line 46) | func Types(data []byte) []byte {
  function GetParameterSet (line 61) | func GetParameterSet(fmtp string) (vps, sps, pps []byte) {

FILE: pkg/h265/mpeg4.go
  function DecodeConfig (line 12) | func DecodeConfig(conf []byte) (profile, vps, sps, pps []byte) {
  function EncodeConfig (line 39) | func EncodeConfig(vps, sps, pps []byte) []byte {
  function ConfigToCodec (line 75) | func ConfigToCodec(conf []byte) *core.Codec {

FILE: pkg/h265/payloader.go
  constant h265NaluHeaderSize (line 16) | h265NaluHeaderSize = 2
  constant h265NaluAggregationPacketType (line 18) | h265NaluAggregationPacketType = 48
  constant h265NaluFragmentationUnitType (line 20) | h265NaluFragmentationUnitType = 49
  constant h265NaluPACIPacketType (line 22) | h265NaluPACIPacketType = 50
  type H265NALUHeader (line 33) | type H265NALUHeader
    method F (line 40) | func (h H265NALUHeader) F() bool {
    method Type (line 45) | func (h H265NALUHeader) Type() uint8 {
    method IsTypeVCLUnit (line 52) | func (h H265NALUHeader) IsTypeVCLUnit() bool {
    method LayerID (line 59) | func (h H265NALUHeader) LayerID() uint8 {
    method TID (line 66) | func (h H265NALUHeader) TID() uint8 {
    method IsAggregationPacket (line 72) | func (h H265NALUHeader) IsAggregationPacket() bool {
    method IsFragmentationUnit (line 77) | func (h H265NALUHeader) IsFragmentationUnit() bool {
    method IsPACIPacket (line 82) | func (h H265NALUHeader) IsPACIPacket() bool {
  function newH265NALUHeader (line 35) | func newH265NALUHeader(highByte, lowByte uint8) H265NALUHeader {
  constant h265FragmentationUnitHeaderSize (line 92) | h265FragmentationUnitHeaderSize = 1
  type H265FragmentationUnitHeader (line 101) | type H265FragmentationUnitHeader
    method S (line 104) | func (h H265FragmentationUnitHeader) S() bool {
    method E (line 110) | func (h H265FragmentationUnitHeader) E() bool {
    method FuType (line 116) | func (h H265FragmentationUnitHeader) FuType() uint8 {
  type Payloader (line 122) | type Payloader struct
    method Payload (line 129) | func (p *Payloader) Payload(mtu uint16, payload []byte) [][]byte {

FILE: pkg/h265/rtp.go
  function RTPDepay (line 11) | func RTPDepay(codec *core.Codec, handler core.HandlerFunc) core.HandlerF...
  function RTPPay (line 117) | func RTPPay(mtu uint16, handler core.HandlerFunc) core.HandlerFunc {
  function SafariPay (line 151) | func SafariPay(mtu uint16, handler core.HandlerFunc) core.HandlerFunc {

FILE: pkg/h265/sps.go
  type SPS (line 12) | type SPS struct
    method Width (line 34) | func (s *SPS) Width() uint16 {
    method Height (line 38) | func (s *SPS) Height() uint16 {
    method profile_tier_level (line 78) | func (s *SPS) profile_tier_level(r *bits.Reader) bool {
  function DecodeSPS (line 42) | func DecodeSPS(nalu []byte) *SPS {

FILE: pkg/hap/accessory.go
  constant FormatString (line 9) | FormatString = "string"
  constant FormatBool (line 10) | FormatBool   = "bool"
  constant FormatFloat (line 11) | FormatFloat  = "float"
  constant FormatUInt8 (line 12) | FormatUInt8  = "uint8"
  constant FormatUInt16 (line 13) | FormatUInt16 = "uint16"
  constant FormatUInt32 (line 14) | FormatUInt32 = "uint32"
  constant FormatInt32 (line 15) | FormatInt32  = "int32"
  constant FormatUInt64 (line 16) | FormatUInt64 = "uint64"
  constant FormatData (line 17) | FormatData   = "data"
  constant FormatTLV8 (line 18) | FormatTLV8   = "tlv8"
  constant UnitPercentage (line 20) | UnitPercentage = "percentage"
  type Accessory (line 29) | type Accessory struct
    method InitIID (line 34) | func (a *Accessory) InitIID() {
    method GetService (line 64) | func (a *Accessory) GetService(servType string) *Service {
    method GetCharacter (line 73) | func (a *Accessory) GetCharacter(charType string) *Character {
    method GetCharacterByID (line 84) | func (a *Accessory) GetCharacterByID(iid uint64) *Character {
  type Service (line 95) | type Service struct
    method GetCharacter (line 105) | func (s *Service) GetCharacter(charType string) *Character {
  function ServiceAccessoryInformation (line 114) | func ServiceAccessoryInformation(manuf, model, name, serial, firmware st...
  function ServiceHAPProtocolInformation (line 162) | func ServiceHAPProtocolInformation() *Service {

FILE: pkg/hap/camera/accessory.go
  function NewAccessory (line 8) | func NewAccessory(manuf, model, name, serial, firmware string) *hap.Acce...
  function ServiceMicrophone (line 22) | func ServiceMicrophone() *hap.Service {
  function ServiceCameraRTPStreamManagement (line 48) | func ServiceCameraRTPStreamManagement() *hap.Service {

FILE: pkg/hap/camera/accessory_test.go
  function TestNilCharacter (line 12) | func TestNilCharacter(t *testing.T) {
  type testTLV8 (line 20) | type testTLV8 struct
    method run (line 28) | func (test testTLV8) run(t *testing.T) {
  function TestAqaraG3 (line 53) | func TestAqaraG3(t *testing.T) {
  function TestHomebridge (line 128) | func TestHomebridge(t *testing.T) {
  function TestScrypted (line 176) | func TestScrypted(t *testing.T) {
  function TestHass (line 240) | func TestHass(t *testing.T) {

FILE: pkg/hap/camera/ch114_supported_video.go
  constant TypeSupportedVideoStreamConfiguration (line 3) | TypeSupportedVideoStreamConfiguration = "114"
  type SupportedVideoStreamConfiguration (line 5) | type SupportedVideoStreamConfiguration struct
  type VideoCodecConfiguration (line 9) | type VideoCodecConfiguration struct
  constant VideoCodecTypeH264 (line 18) | VideoCodecTypeH264 = 0
  constant VideoCodecProfileConstrainedBaseline (line 20) | VideoCodecProfileConstrainedBaseline = 0
  constant VideoCodecProfileMain (line 21) | VideoCodecProfileMain                = 1
  constant VideoCodecProfileHigh (line 22) | VideoCodecProfileHigh                = 2
  constant VideoCodecLevel31 (line 24) | VideoCodecLevel31 = 0
  constant VideoCodecLevel32 (line 25) | VideoCodecLevel32 = 1
  constant VideoCodecLevel40 (line 26) | VideoCodecLevel40 = 2
  constant VideoCodecPacketizationModeNonInterleaved (line 28) | VideoCodecPacketizationModeNonInterleaved = 0
  constant VideoCodecCvoNotSuppported (line 30) | VideoCodecCvoNotSuppported = 0
  constant VideoCodecCvoSuppported (line 31) | VideoCodecCvoSuppported    = 1
  type VideoCodecParameters (line 34) | type VideoCodecParameters struct
  type VideoCodecAttributes (line 42) | type VideoCodecAttributes struct

FILE: pkg/hap/camera/ch115_supported_audio.go
  constant TypeSupportedAudioStreamConfiguration (line 3) | TypeSupportedAudioStreamConfiguration = "115"
  type SupportedAudioStreamConfiguration (line 5) | type SupportedAudioStreamConfiguration struct
  constant AudioCodecTypePCMU (line 12) | AudioCodecTypePCMU   = 0
  constant AudioCodecTypePCMA (line 13) | AudioCodecTypePCMA   = 1
  constant AudioCodecTypeAACELD (line 14) | AudioCodecTypeAACELD = 2
  constant AudioCodecTypeOpus (line 15) | AudioCodecTypeOpus   = 3
  constant AudioCodecTypeMSBC (line 16) | AudioCodecTypeMSBC   = 4
  constant AudioCodecTypeAMR (line 17) | AudioCodecTypeAMR    = 5
  constant AudioCodecTypeARMWB (line 18) | AudioCodecTypeARMWB  = 6
  constant AudioCodecBitrateVariable (line 20) | AudioCodecBitrateVariable = 0
  constant AudioCodecBitrateConstant (line 21) | AudioCodecBitrateConstant = 1
  constant AudioCodecSampleRate8Khz (line 23) | AudioCodecSampleRate8Khz  = 0
  constant AudioCodecSampleRate16Khz (line 24) | AudioCodecSampleRate16Khz = 1
  constant AudioCodecSampleRate24Khz (line 25) | AudioCodecSampleRate24Khz = 2
  constant RTPTimeAACELD8 (line 27) | RTPTimeAACELD8  = 60
  constant RTPTimeAACELD16 (line 28) | RTPTimeAACELD16 = 30
  constant RTPTimeAACELD24 (line 29) | RTPTimeAACELD24 = 20
  constant RTPTimeAACLD16 (line 30) | RTPTimeAACLD16  = 60
  constant RTPTimeAACLD24 (line 31) | RTPTimeAACLD24  = 40
  type AudioCodecConfiguration (line 34) | type AudioCodecConfiguration struct
  type AudioCodecParameters (line 41) | type AudioCodecParameters struct

FILE: pkg/hap/camera/ch116_supported_rtp.go
  constant TypeSupportedRTPConfiguration (line 3) | TypeSupportedRTPConfiguration = "116"
  constant CryptoAES_CM_128_HMAC_SHA1_80 (line 7) | CryptoAES_CM_128_HMAC_SHA1_80 = 0
  constant CryptoAES_CM_256_HMAC_SHA1_80 (line 8) | CryptoAES_CM_256_HMAC_SHA1_80 = 1
  constant CryptoDisabled (line 9) | CryptoDisabled                = 2
  type SupportedRTPConfiguration (line 12) | type SupportedRTPConfiguration struct

FILE: pkg/hap/camera/ch117_selected_stream.go
  constant TypeSelectedStreamConfiguration (line 3) | TypeSelectedStreamConfiguration = "117"
  type SelectedStreamConfiguration (line 5) | type SelectedStreamConfiguration struct
  constant SessionCommandEnd (line 13) | SessionCommandEnd         = 0
  constant SessionCommandStart (line 14) | SessionCommandStart       = 1
  constant SessionCommandSuspend (line 15) | SessionCommandSuspend     = 2
  constant SessionCommandResume (line 16) | SessionCommandResume      = 3
  constant SessionCommandReconfigure (line 17) | SessionCommandReconfigure = 4
  type SessionControl (line 20) | type SessionControl struct
  type RTPParams (line 25) | type RTPParams struct

FILE: pkg/hap/camera/ch118_setup_endpoints.go
  constant TypeSetupEndpoints (line 3) | TypeSetupEndpoints = "118"
  type SetupEndpointsRequest (line 5) | type SetupEndpointsRequest struct
  type SetupEndpointsResponse (line 12) | type SetupEndpointsResponse struct
  type Address (line 22) | type Address struct
  type SRTPCryptoSuite (line 29) | type SRTPCryptoSuite struct

FILE: pkg/hap/camera/ch120_streaming_status.go
  constant TypeStreamingStatus (line 3) | TypeStreamingStatus = "120"
  type StreamingStatus (line 5) | type StreamingStatus struct
  constant StreamingStatusAvailable (line 11) | StreamingStatusAvailable   = 0
  constant StreamingStatusInUse (line 12) | StreamingStatusInUse       = 1
  constant StreamingStatusUnavailable (line 13) | StreamingStatusUnavailable = 2

FILE: pkg/hap/camera/ch130_data_stream_transport.go
  constant TypeSupportedDataStreamTransportConfiguration (line 3) | TypeSupportedDataStreamTransportConfiguration = "130"
  type SupportedDataStreamTransportConfiguration (line 5) | type SupportedDataStreamTransportConfiguration struct
  type TransferTransportConfiguration (line 9) | type TransferTransportConfiguration struct

FILE: pkg/hap/camera/ch131_data_stream.go
  constant TypeSetupDataStreamTransport (line 3) | TypeSetupDataStreamTransport = "131"
  type SetupDataStreamTransportRequest (line 5) | type SetupDataStreamTransportRequest struct
  type SetupDataStreamTransportResponse (line 11) | type SetupDataStreamTransportResponse struct

FILE: pkg/hap/camera/ch205.go
  constant TypeSupportedCameraRecordingConfiguration (line 3) | TypeSupportedCameraRecordingConfiguration = "205"
  type SupportedCameraRecordingConfiguration (line 5) | type SupportedCameraRecordingConfiguration struct
  type MediaContainerConfigurations (line 11) | type MediaContainerConfigurations struct
  type MediaContainerParameters (line 16) | type MediaContainerParameters struct

FILE: pkg/hap/camera/ch206.go
  constant TypeSupportedVideoRecordingConfiguration (line 3) | TypeSupportedVideoRecordingConfiguration = "206"
  type SupportedVideoRecordingConfiguration (line 5) | type SupportedVideoRecordingConfiguration struct
  type VideoRecordingCodecConfiguration (line 9) | type VideoRecordingCodecConfiguration struct
  type VideoRecordingCodecParameters (line 15) | type VideoRecordingCodecParameters struct

FILE: pkg/hap/camera/ch207.go
  constant TypeSupportedAudioRecordingConfiguration (line 3) | TypeSupportedAudioRecordingConfiguration = "207"
  type SupportedAudioRecordingConfiguration (line 5) | type SupportedAudioRecordingConfiguration struct
  type AudioRecordingCodecConfiguration (line 9) | type AudioRecordingCodecConfiguration struct
  type AudioRecordingCodecParameters (line 14) | type AudioRecordingCodecParameters struct

FILE: pkg/hap/camera/ch209.go
  constant TypeSelectedCameraRecordingConfiguration (line 3) | TypeSelectedCameraRecordingConfiguration = "209"
  type SelectedCameraRecordingConfiguration (line 5) | type SelectedCameraRecordingConfiguration struct

FILE: pkg/hap/camera/stream.go
  type Stream (line 11) | type Stream struct
    method GetFreeStream (line 80) | func (s *Stream) GetFreeStream() error {
    method ExchangeEndpoints (line 105) | func (s *Stream) ExchangeEndpoints(videoSession, audioSession *srtp.Se...
    method SetStreamConfig (line 159) | func (s *Stream) SetStreamConfig(config *SelectedStreamConfiguration) ...
    method Close (line 171) | func (s *Stream) Close() error {
  function NewStream (line 17) | func NewStream(

FILE: pkg/hap/chacha20poly1305/chacha20poly1305.go
  function Decrypt (line 12) | func Decrypt(key32 []byte, nonce8 string, ciphertext []byte) ([]byte, er...
  function Encrypt (line 17) | func Encrypt(key32 []byte, nonce8 string, plaintext []byte) ([]byte, err...
  function DecryptAndVerify (line 21) | func DecryptAndVerify(key32, dst, nonce8, ciphertext, verify []byte) ([]...
  function EncryptAndSeal (line 37) | func EncryptAndSeal(key32, dst, nonce8, plaintext, verify []byte) ([]byt...

FILE: pkg/hap/character.go
  type Character (line 16) | type Character struct
    method AddListener (line 35) | func (c *Character) AddListener(w io.Writer) {
    method RemoveListener (line 43) | func (c *Character) RemoveListener(w io.Writer) {
    method NotifyListeners (line 51) | func (c *Character) NotifyListeners(ignore io.Writer) error {
    method GenerateEvent (line 75) | func (c *Character) GenerateEvent() (data []byte, err error) {
    method Set (line 104) | func (c *Character) Set(v any) (err error) {
    method Write (line 112) | func (c *Character) Write(v any) (err error) {
    method ReadTLV8 (line 129) | func (c *Character) ReadTLV8(v any) (err error) {
    method ReadBool (line 136) | func (c *Character) ReadBool() (bool, error) {
    method String (line 143) | func (c *Character) String() string {

FILE: pkg/hap/client.go
  constant ConnDialTimeout (line 26) | ConnDialTimeout = time.Second * 3
  constant ConnDeadline (line 27) | ConnDeadline    = time.Second * 3
  type Client (line 31) | type Client struct
    method ClientPublic (line 70) | func (c *Client) ClientPublic() []byte {
    method URL (line 74) | func (c *Client) URL() string {
    method DeviceHost (line 81) | func (c *Client) DeviceHost() string {
    method Dial (line 88) | func (c *Client) Dial() (err error) {
    method Close (line 235) | func (c *Client) Close() error {
    method eventsReader (line 242) | func (c *Client) eventsReader() {
    method GetAccessories (line 268) | func (c *Client) GetAccessories() ([]*Accessory, error) {
    method GetFirstAccessory (line 282) | func (c *Client) GetFirstAccessory() (*Accessory, error) {
    method GetCharacters (line 293) | func (c *Client) GetCharacters(query string) ([]JSONCharacter, error) {
    method GetCharacter (line 311) | func (c *Client) GetCharacter(char *Character) error {
    method PutCharacters (line 321) | func (c *Client) PutCharacters(characters ...*Character) error {
    method GetImage (line 346) | func (c *Client) GetImage(width, height int) ([]byte, error) {
    method LocalIP (line 358) | func (c *Client) LocalIP() string {
  function Dial (line 48) | func Dial(rawURL string) (*Client, error) {
  function DecodeKey (line 366) | func DecodeKey(s string) []byte {

FILE: pkg/hap/client_http.go
  constant MimeTLV8 (line 11) | MimeTLV8 = "application/pairing+tlv8"
  constant MimeJSON (line 12) | MimeJSON = "application/hap+json"
  constant PathPairSetup (line 14) | PathPairSetup       = "/pair-setup"
  constant PathPairVerify (line 15) | PathPairVerify      = "/pair-verify"
  constant PathPairings (line 16) | PathPairings        = "/pairings"
  constant PathAccessories (line 17) | PathAccessories     = "/accessories"
  constant PathCharacteristics (line 18) | PathCharacteristics = "/characteristics"
  constant PathResource (line 19) | PathResource        = "/resource"
  method Do (line 22) | func (c *Client) Do(req *http.Request) (*http.Response, error) {
  method Request (line 32) | func (c *Client) Request(method, path, contentType string, body io.Reade...
  method Get (line 50) | func (c *Client) Get(path string) (*http.Response, error) {
  method Post (line 54) | func (c *Client) Post(path, contentType string, body io.Reader) (*http.R...
  method Put (line 58) | func (c *Client) Put(path, contentType string, body io.Reader) (*http.Re...
  constant ProtoEvent (line 62) | ProtoEvent = "EVENT/1.0"
  function ReadResponse (line 64) | func ReadResponse(r *bufio.Reader, req *http.Request) (*http.Response, e...
  function WriteEvent (line 86) | func WriteEvent(w io.Writer, res *http.Response) error {
  type eventWriter (line 90) | type eventWriter struct
    method Write (line 95) | func (e *eventWriter) Write(p []byte) (n int, err error) {

FILE: pkg/hap/client_pairing.go
  function Pair (line 18) | func Pair(rawURL string) (*Client, error) {
  function Unpair (line 47) | func Unpair(rawURL string) error {
  method Pair (line 75) | func (c *Client) Pair(feature, pin string) (err error) {
  method ListPairings (line 278) | func (c *Client) ListPairings() error {
  method PairingsAdd (line 305) | func (c *Client) PairingsAdd(clientID string, clientPublic []byte, admin...
  method DeletePairing (line 338) | func (c *Client) DeletePairing(id string) error {
  function newPairingError (line 366) | func newPairingError(code byte) error {
  function keyDerivativeFuncRFC2945 (line 390) | func keyDerivativeFuncRFC2945(username []byte) srp.KeyDerivationFunc {

FILE: pkg/hap/conn.go
  type Conn (line 18) | type Conn struct
    method MarshalJSON (line 35) | func (c *Conn) MarshalJSON() ([]byte, error) {
    method Read (line 83) | func (c *Conn) Read(b []byte) (n int, err error) {
    method Write (line 110) | func (c *Conn) Write(b []byte) (n int, err error) {
    method Close (line 151) | func (c *Conn) Close() error {
    method LocalAddr (line 155) | func (c *Conn) LocalAddr() net.Addr {
    method RemoteAddr (line 159) | func (c *Conn) RemoteAddr() net.Addr {
    method SetDeadline (line 163) | func (c *Conn) SetDeadline(t time.Time) error {
    method SetReadDeadline (line 167) | func (c *Conn) SetReadDeadline(t time.Time) error {
    method SetWriteDeadline (line 171) | func (c *Conn) SetWriteDeadline(t time.Time) error {
  function NewConn (line 47) | func NewConn(conn net.Conn, rw *bufio.ReadWriter, sharedKey []byte, isCl...
  constant packetSizeMax (line 76) | packetSizeMax = 0x400
  constant VerifySize (line 78) | VerifySize = 2
  constant NonceSize (line 79) | NonceSize  = 8
  constant Overhead (line 80) | Overhead   = 16

FILE: pkg/hap/curve25519/curve25519.go
  function GenerateKeyPair (line 9) | func GenerateKeyPair() ([]byte, []byte) {
  function SharedSecret (line 16) | func SharedSecret(privateKey, otherPublicKey []byte) ([]byte, error) {

FILE: pkg/hap/ed25519/ed25519.go
  function ValidateSignature (line 10) | func ValidateSignature(key, data, signature []byte) bool {
  function Signature (line 18) | func Signature(key, data []byte) ([]byte, error) {

FILE: pkg/hap/hds/hds.go
  function NewConn (line 19) | func NewConn(conn net.Conn, key []byte, salt string, controller bool) (*...
  type Conn (line 45) | type Conn struct
    method MarshalJSON (line 60) | func (c *Conn) MarshalJSON() ([]byte, error) {
    method read (line 72) | func (c *Conn) read() (b []byte, err error) {
    method Read (line 94) | func (c *Conn) Read(p []byte) (n int, err error) {
    method WriteTo (line 106) | func (c *Conn) WriteTo(w io.Writer) (int64, error) {
    method Write (line 122) | func (c *Conn) Write(b []byte) (n int, err error) {
    method Close (line 154) | func (c *Conn) Close() error {
    method LocalAddr (line 158) | func (c *Conn) LocalAddr() net.Addr {
    method RemoteAddr (line 162) | func (c *Conn) RemoteAddr() net.Addr {
    method SetDeadline (line 166) | func (c *Conn) SetDeadline(t time.Time) error {
    method SetReadDeadline (line 170) | func (c *Conn) SetReadDeadline(t time.Time) error {
    method SetWriteDeadline (line 174) | func (c *Conn) SetWriteDeadline(t time.Time) error {

FILE: pkg/hap/hds/hds_test.go
  function TestEncryption (line 12) | func TestEncryption(t *testing.T) {

FILE: pkg/hap/helpers.go
  constant TXTConfigNumber (line 15) | TXTConfigNumber = "c#"
  constant TXTDeviceID (line 16) | TXTDeviceID     = "id"
  constant TXTModel (line 17) | TXTModel        = "md"
  constant TXTProtoVersion (line 18) | TXTProtoVersion = "pv"
  constant TXTStateNumber (line 19) | TXTStateNumber  = "s#"
  constant TXTCategory (line 20) | TXTCategory     = "ci"
  constant TXTSetupHash (line 21) | TXTSetupHash    = "sh"
  constant TXTFeatureFlags (line 26) | TXTFeatureFlags = "ff"
  constant TXTStatusFlags (line 31) | TXTStatusFlags = "sf"
  constant StatusPaired (line 33) | StatusPaired    = "0"
  constant StatusNotPaired (line 34) | StatusNotPaired = "1"
  constant CategoryBridge (line 36) | CategoryBridge   = "2"
  constant CategoryCamera (line 37) | CategoryCamera   = "17"
  constant CategoryDoorbell (line 38) | CategoryDoorbell = "18"
  constant StateM1 (line 40) | StateM1 = 1
  constant StateM2 (line 41) | StateM2 = 2
  constant StateM3 (line 42) | StateM3 = 3
  constant StateM4 (line 43) | StateM4 = 4
  constant StateM5 (line 44) | StateM5 = 5
  constant StateM6 (line 45) | StateM6 = 6
  constant MethodPair (line 47) | MethodPair          = 0
  constant MethodPairMFi (line 48) | MethodPairMFi       = 1
  constant MethodVerifyPair (line 49) | MethodVerifyPair    = 2
  constant MethodAddPairing (line 50) | MethodAddPairing    = 3
  constant MethodDeletePairing (line 51) | MethodDeletePairing = 4
  constant MethodListPairings (line 52) | MethodListPairings  = 5
  constant PermissionUser (line 54) | PermissionUser  = 0
  constant PermissionAdmin (line 55) | PermissionAdmin = 1
  constant DeviceAID (line 58) | DeviceAID = 1
  type JSONAccessories (line 60) | type JSONAccessories struct
  type JSONCharacters (line 64) | type JSONCharacters struct
  type JSONCharacter (line 68) | type JSONCharacter struct
  constant insecurePINs (line 77) | insecurePINs = "00000000 11111111 22222222 33333333 44444444 55555555 66...
  function SanitizePin (line 79) | func SanitizePin(pin string) (string, error) {
  function GenerateKey (line 91) | func GenerateKey() []byte {
  function GenerateUUID (line 96) | func GenerateUUID() string {
  function SetupHash (line 104) | func SetupHash(setupID, deviceID string) string {
  function Append (line 110) | func Append(items ...any) (b []byte) {
  function newRequestError (line 124) | func newRequestError(req any) error {
  function newResponseError (line 128) | func newResponseError(req, res any) error {

FILE: pkg/hap/hkdf/hkdf.go
  function Sha512 (line 10) | func Sha512(key []byte, salt, info string) ([]byte, error) {

FILE: pkg/hap/server.go
  type Server (line 18) | type Server struct
    method ServerPublic (line 27) | func (s *Server) ServerPublic() []byte {
    method PairSetup (line 38) | func (s *Server) PairSetup(req *http.Request, rw *bufio.ReadWriter) (i...
    method PairVerify (line 240) | func (s *Server) PairVerify(req *http.Request, rw *bufio.ReadWriter) (...
  function WriteResponse (line 371) | func WriteResponse(w *bufio.Writer, statusCode int, contentType string, ...

FILE: pkg/hap/setup/setup.go
  constant FlagNFC (line 9) | FlagNFC = 1
  constant FlagIP (line 10) | FlagIP  = 2
  constant FlagBLE (line 11) | FlagBLE = 4
  constant FlagWAC (line 12) | FlagWAC = 8
  function GenerateSetupURI (line 15) | func GenerateSetupURI(category, pin, setupID string) string {
  function FormatInt36 (line 23) | func FormatInt36(value int64, n int) string {
  constant digits (line 32) | digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"

FILE: pkg/hap/setup/setup_test.go
  function TestFormatAlphaNum (line 12) | func TestFormatAlphaNum(t *testing.T) {

FILE: pkg/hap/tlv8/tlv8.go
  type errReader (line 15) | type errReader struct
    method Read (line 19) | func (e *errReader) Read([]byte) (int, error) {
  function MarshalBase64 (line 23) | func MarshalBase64(v any) (string, error) {
  function MarshalReader (line 31) | func MarshalReader(v any) io.Reader {
  function Marshal (line 39) | func Marshal(v any) ([]byte, error) {
  constant separator (line 60) | separator = 0xFF
  function appendSlice (line 62) | func appendSlice(b []byte, value reflect.Value) ([]byte, error) {
  function appendStruct (line 75) | func appendStruct(b []byte, value reflect.Value) ([]byte, error) {
  function appendValue (line 99) | func appendValue(b []byte, tag byte, value reflect.Value) ([]byte, error) {
  function UnmarshalBase64 (line 168) | func UnmarshalBase64(in any, out any) error {
  function UnmarshalReader (line 177) | func UnmarshalReader(r io.Reader, n int64, v any) error {
  function Unmarshal (line 194) | func Unmarshal(data []byte, v any) error {
  function unmarshalTLV (line 227) | func unmarshalTLV(b []byte, value reflect.Value) ([]byte, error) {
  function unmarshalSlice (line 272) | func unmarshalSlice(b []byte, value reflect.Value) error {
  function unmarshalStruct (line 287) | func unmarshalStruct(b []byte, value reflect.Value) error {
  function unmarshalValue (line 297) | func unmarshalValue(v []byte, value reflect.Value) error {
  function getStructField (line 354) | func getStructField(value reflect.Value, tag string) (reflect.Value, boo...
  function growSlice (line 368) | func growSlice(value reflect.Value) int {

FILE: pkg/hap/tlv8/tlv8_test.go
  function TestMarshal (line 11) | func TestMarshal(t *testing.T) {
  function TestBytes (line 42) | func TestBytes(t *testing.T) {
  function TestVideoCodecParams (line 66) | func TestVideoCodecParams(t *testing.T) {
  function TestInterface (line 88) | func TestInterface(t *testing.T) {
  function TestSlice1 (line 112) | func TestSlice1(t *testing.T) {
  function TestSlice2 (line 136) | func TestSlice2(t *testing.T) {

FILE: pkg/hass/api.go
  type API (line 10) | type API struct
    method Auth (line 29) | func (a *API) Auth(token string) error {
    method Close (line 53) | func (a *API) Close() error {
    method ExchangeSDP (line 57) | func (a *API) ExchangeSDP(entityID, offer string) (string, error) {
    method GetWebRTCEntities (line 80) | func (a *API) GetWebRTCEntities() (map[string]string, error) {
  function NewAPI (line 14) | func NewAPI(url, token string) (*API, error) {
  type ResponseAuth (line 105) | type ResponseAuth struct
  type ResponseStates (line 109) | type ResponseStates struct
  type ResponseOffer (line 133) | type ResponseOffer struct
  function SupervisorToken (line 142) | func SupervisorToken() string {

FILE: pkg/hass/client.go
  type Client (line 12) | type Client struct
    method GetMedias (line 96) | func (c *Client) GetMedias() []*core.Media {
    method GetTrack (line 100) | func (c *Client) GetTrack(media *core.Media, codec *core.Codec) (*core...
    method AddTrack (line 104) | func (c *Client) AddTrack(media *core.Media, codec *core.Codec, track ...
    method Start (line 108) | func (c *Client) Start() error {
    method Stop (line 112) | func (c *Client) Stop() error {
    method MarshalJSON (line 116) | func (c *Client) MarshalJSON() ([]byte, error) {
  function NewClient (line 16) | func NewClient(rawURL string) (*Client, error) {

FILE: pkg/hls/producer.go
  function OpenURL (line 10) | func OpenURL(u *url.URL, body io.ReadCloser) (*mpegts.Producer, error) {

FILE: pkg/hls/reader.go
  type reader (line 14) | type reader struct
    method Read (line 58) | func (r *reader) Read(dst []byte) (n int, err error) {
    method Close (line 81) | func (r *reader) Close() error {
    method RoundTrip (line 86) | func (r *reader) RoundTrip(_ *http.Request) (*http.Response, error) {
    method getSegment (line 90) | func (r *reader) getSegment() ([]byte, error) {
  function NewReader (line 25) | func NewReader(u *url.URL, body io.ReadCloser) (io.Reader, error) {
  function getSegment (line 148) | func getSegment(src []byte) []byte {

FILE: pkg/homekit/consumer.go
  type Consumer (line 18) | type Consumer struct
    method SessionID (line 62) | func (c *Consumer) SessionID() string {
    method SetOffer (line 66) | func (c *Consumer) SetOffer(offer *camera.SetupEndpointsRequest) {
    method GetAnswer (line 86) | func (c *Consumer) GetAnswer() *camera.SetupEndpointsResponse {
    method SetConfig (line 111) | func (c *Consumer) SetConfig(conf *camera.SelectedStreamConfiguration)...
    method AddTrack (line 133) | func (c *Consumer) AddTrack(media *core.Media, codec *core.Codec, trac...
    method WriteTo (line 177) | func (c *Consumer) WriteTo(io.Writer) (int64, error) {
    method Stop (line 184) | func (c *Consumer) Stop() error {
    method srtpEndpoint (line 191) | func (c *Consumer) srtpEndpoint() *srtp.Endpoint {
  function NewConsumer (line 31) | func NewConsumer(conn net.Conn, server *srtp.Server) *Consumer {
  function toDuration (line 202) | func toDuration(seconds float32) time.Duration {

FILE: pkg/homekit/helpers.go
  function videoToMedia (line 16) | func videoToMedia(codecs []camera.VideoCodecConfiguration) *core.Media {
  function audioToMedia (line 42) | func audioToMedia(codecs []camera.AudioCodecConfiguration) *core.Media {
  function trackToVideo (line 70) | func trackToVideo(track *core.Receiver, video0 *camera.VideoCodecConfigu...
  function trackToAudio (line 114) | func trackToAudio(track *core.Receiver, audio0 *camera.AudioCodecConfigu...

FILE: pkg/homekit/log/debug.go
  function Debug (line 10) | func Debug(v any) {

FILE: pkg/homekit/producer.go
  type Client (line 18) | type Client struct
    method Conn (line 59) | func (c *Client) Conn() net.Conn {
    method GetMedias (line 63) | func (c *Client) GetMedias() []*core.Media {
    method Start (line 110) | func (c *Client) Start() error {
    method Stop (line 169) | func (c *Client) Stop() error {
    method trackByKind (line 180) | func (c *Client) trackByKind(kind string) *core.Receiver {
    method startMJPEG (line 189) | func (c *Client) startMJPEG() error {
    method srtpEndpoint (line 208) | func (c *Client) srtpEndpoint() *srtp.Endpoint {
  function Dial (line 37) | func Dial(rawURL string, server *srtp.Server) (*Client, error) {
  function timekeeper (line 218) | func timekeeper(handler core.HandlerFunc) core.HandlerFunc {

FILE: pkg/homekit/proxy.go
  type ServerProxy (line 18) | type ServerProxy interface
  function ProxyHandler (line 24) | func ProxyHandler(srv ServerProxy, acc net.Conn) HandlerFunc {
  type Proxy (line 42) | type Proxy struct
    method handleCon (line 48) | func (p *Proxy) handleCon(srv ServerProxy) error {
    method handleAcc (line 142) | func (p *Proxy) handleAcc() error {
    method listenHDS (line 168) | func (p *Proxy) listenHDS(srv ServerProxy, accPort int, salt string) (...

FILE: pkg/homekit/server.go
  type HandlerFunc (line 18) | type HandlerFunc
  type Server (line 20) | type Server interface
  type ServerPair (line 25) | type ServerPair interface
  type ServerAccessory (line 31) | type ServerAccessory interface
  function ServerHandler (line 38) | func ServerHandler(server Server) HandlerFunc {
  function handleRequest (line 108) | func handleRequest(handle func(conn net.Conn, req *http.Request) (*http....
  function handlePairings (line 135) | func handlePairings(req *http.Request, srv ServerPair) (*http.Response, ...
  function makeResponse (line 164) | func makeResponse(mime string, v any) (*http.Response, error) {

FILE: pkg/image/producer.go
  type Producer (line 13) | type Producer struct
    method Start (line 46) | func (c *Producer) Start() error {
    method Stop (line 89) | func (c *Producer) Stop() error {
  function Open (line 20) | func Open(res *http.Response) (*Producer, error) {

FILE: pkg/ioctl/ioctl.go
  function Str (line 7) | func Str(b []byte) string {
  function io (line 14) | func io(mode byte, type_ byte, number byte, size uint16) uintptr {
  function IOR (line 18) | func IOR(type_ byte, number byte, size uint16) uintptr {
  function IOW (line 22) | func IOW(type_ byte, number byte, size uint16) uintptr {
  function IORW (line 26) | func IORW(type_ byte, number byte, size uint16) uintptr {

FILE: pkg/ioctl/ioctl_be.go
  constant write (line 6) | write = 1
  constant read (line 7) | read  = 2

FILE: pkg/ioctl/ioctl_le.go
  constant read (line 6) | read  = 1
  constant write (line 7) | write = 2

FILE: pkg/ioctl/ioctl_linux.go
  function Ioctl (line 8) | func Ioctl(fd int, req uint, arg unsafe.Pointer) error {

FILE: pkg/ioctl/ioctl_test.go
  function TestIOR (line 10) | func TestIOR(t *testing.T) {

FILE: pkg/isapi/backchannel.go
  method GetMedias (line 10) | func (c *Client) GetMedias() []*core.Media {
  method GetTrack (line 14) | func (c *Client) GetTrack(media *core.Media, codec *core.Codec) (*core.R...
  method AddTrack (line 18) | func (c *Client) AddTrack(media *core.Media, _ *core.Codec, track *core....
  method Start (line 34) | func (c *Client) Start() (err error) {
  method Stop (line 41) | func (c *Client) Stop() (err error) {
  method MarshalJSON (line 54) | func (c *Client) MarshalJSON() ([]byte, error) {

FILE: pkg/isapi/client.go
  type Client (line 15) | type Client struct
    method Dial (line 44) | func (c *Client) Dial() (err error) {
    method Open (line 92) | func (c *Client) Open() (err error) {
    method Close (line 139) | func (c *Client) Close() (err error) {
  function Dial (line 27) | func Dial(rawURL string) (*Client, error) {

FILE: pkg/iso/atoms.go
  constant Ftyp (line 4) | Ftyp                        = "ftyp"
  constant Moov (line 5) | Moov                        = "moov"
  constant MoovMvhd (line 6) | MoovMvhd                    = "mvhd"
  constant MoovTrak (line 7) | MoovTrak                    = "trak"
  constant MoovTrakTkhd (line 8) | MoovTrakTkhd                = "tkhd"
  constant MoovTrakMdia (line 9) | MoovTrakMdia                = "mdia"
  constant MoovTrakMdiaMdhd (line 10) | MoovTrakMdiaMdhd            = "mdhd"
  constant MoovTrakMdiaHdlr (line 11) | MoovTrakMdiaHdlr            = "hdlr"
  constant MoovTrakMdiaMinf (line 12) | MoovTrakMdiaMinf            = "minf"
  constant MoovTrakMdiaMinfVmhd (line 13) | MoovTrakMdiaMinfVmhd        = "vmhd"
  constant MoovTrakMdiaMinfSmhd (line 14) | MoovTrakMdiaMinfSmhd        = "smhd"
  constant MoovTrakMdiaMinfDinf (line 15) | MoovTrakMdiaMinfDinf        = "dinf"
  constant MoovTrakMdiaMinfDinfDref (line 16) | MoovTrakMdiaMinfDinfDref    = "dref"
  constant MoovTrakMdiaMinfDinfDrefUrl (line 17) | MoovTrakMdiaMinfDinfDrefUrl = "url "
  constant MoovTrakMdiaMinfStbl (line 18) | MoovTrakMdiaMinfStbl        = "stbl"
  constant MoovTrakMdiaMinfStblStsd (line 19) | MoovTrakMdiaMinfStblStsd    = "stsd"
  constant MoovTrakMdiaMinfStblStts (line 20) | MoovTrakMdiaMinfStblStts    = "stts"
  constant MoovTrakMdiaMinfStblStsc (line 21) | MoovTrakMdiaMinfStblStsc    = "stsc"
  constant MoovTrakMdiaMinfStblStsz (line 22) | MoovTrakMdiaMinfStblStsz    = "stsz"
  constant MoovTrakMdiaMinfStblStco (line 23) | MoovTrakMdiaMinfStblStco    = "stco"
  constant MoovMvex (line 24) | MoovMvex                    = "mvex"
  constant MoovMvexTrex (line 25) | MoovMvexTrex                = "trex"
  constant Moof (line 26) | Moof                        = "moof"
  constant MoofMfhd (line 27) | MoofMfhd                    = "mfhd"
  constant MoofTraf (line 28) | MoofTraf                    = "traf"
  constant MoofTrafTfhd (line 29) | MoofTrafTfhd                = "tfhd"
  constant MoofTrafTfdt (line 30) | MoofTrafTfdt                = "tfdt"
  constant MoofTrafTrun (line 31) | MoofTrafTrun                = "trun"
  constant Mdat (line 32) | Mdat                        = "mdat"
  constant sampleIsNonSync (line 36) | sampleIsNonSync  = 0x10000
  constant sampleDependsOn1 (line 37) | sampleDependsOn1 = 0x1000000
  constant sampleDependsOn2 (line 38) | sampleDependsOn2 = 0x2000000
  constant SampleVideoIFrame (line 40) | SampleVideoIFrame    = sampleDependsOn2
  constant SampleVideoNonIFrame (line 41) | SampleVideoNonIFrame = sampleDependsOn1 | sampleIsNonSync
  constant SampleAudio (line 42) | SampleAudio          = sampleDependsOn2
  method WriteFileType (line 45) | func (m *Movie) WriteFileType() {
  method WriteMovieHeader (line 55) | func (m *Movie) WriteMovieHeader() {
  method WriteTrackHeader (line 72) | func (m *Movie) WriteTrackHeader(id uint32, width, height uint16) {
  method WriteMediaHeader (line 110) | func (m *Movie) WriteMediaHeader(timescale uint32) {
  method WriteMediaHandler (line 124) | func (m *Movie) WriteMediaHandler(s, name string) {
  method WriteVideoMediaInfo (line 137) | func (m *Movie) WriteVideoMediaInfo() {
  method WriteAudioMediaInfo (line 147) | func (m *Movie) WriteAudioMediaInfo() {
  method WriteDataInfo (line 155) | func (m *Movie) WriteDataInfo() {
  method WriteSampleTable (line 172) | func (m *Movie) WriteSampleTable(writeSampleDesc func()) {
  method WriteTrackExtend (line 211) | func (m *Movie) WriteTrackExtend(id uint32) {
  method WriteVideoTrack (line 223) | func (m *Movie) WriteVideoTrack(id uint32, codec string, timescale uint3...
  method WriteAudioTrack (line 243) | func (m *Movie) WriteAudioTrack(id uint32, codec string, timescale uint3...
  constant TfhdDefaultSampleDuration (line 264) | TfhdDefaultSampleDuration = 0x000008
  constant TfhdDefaultSampleSize (line 265) | TfhdDefaultSampleSize     = 0x000010
  constant TfhdDefaultSampleFlags (line 266) | TfhdDefaultSampleFlags    = 0x000020
  constant TfhdDefaultBaseIsMoof (line 267) | TfhdDefaultBaseIsMoof     = 0x020000
  constant TrunDataOffset (line 271) | TrunDataOffset       = 0x000001
  constant TrunFirstSampleFlags (line 272) | TrunFirstSampleFlags = 0x000004
  constant TrunSampleDuration (line 273) | TrunSampleDuration   = 0x0000100
  constant TrunSampleSize (line 274) | TrunSampleSize       = 0x0000200
  constant TrunSampleFlags (line 275) | TrunSampleFlags      = 0x0000400
  constant TrunSampleCTS (line 276) | TrunSampleCTS        = 0x0000800
  method WriteMovieFragment (line 279) | func (m *Movie) WriteMovieFragment(seq, tid, duration, size, flags uint3...
  method WriteData (line 335) | func (m *Movie) WriteData(b []byte) {

FILE: pkg/iso/codecs.go
  method WriteVideo (line 8) | func (m *Movie) WriteVideo(codec string, width, height uint16, conf []by...
  method WriteAudio (line 52) | func (m *Movie) WriteAudio(codec string, channels uint16, sampleRate uin...
  method WriteEsdsAAC (line 104) | func (m *Movie) WriteEsdsAAC(conf []byte) {
  method WriteEsdsMP3 (line 140) | func (m *Movie) WriteEsdsMP3() {
  method WriteOpus (line 171) | func (m *Movie) WriteOpus(channels uint16, sampleRate uint32) {

FILE: pkg/iso/iso.go
  type Movie (line 8) | type Movie struct
    method Bytes (line 17) | func (m *Movie) Bytes() []byte {
    method StartAtom (line 21) | func (m *Movie) StartAtom(name string) {
    method EndAtom (line 27) | func (m *Movie) EndAtom() {
    method Write (line 37) | func (m *Movie) Write(b []byte) {
    method WriteBytes (line 41) | func (m *Movie) WriteBytes(b ...byte) {
    method WriteString (line 45) | func (m *Movie) WriteString(s string) {
    method Skip (line 49) | func (m *Movie) Skip(n int) {
    method WriteUint16 (line 53) | func (m *Movie) WriteUint16(v uint16) {
    method WriteUint24 (line 57) | func (m *Movie) WriteUint24(v uint32) {
    method WriteUint32 (line 61) | func (m *Movie) WriteUint32(v uint32) {
    method WriteUint64 (line 65) | func (m *Movie) WriteUint64(v uint64) {
    method WriteFloat16 (line 69) | func (m *Movie) WriteFloat16(f float64) {
    method WriteFloat32 (line 75) | func (m *Movie) WriteFloat32(f float64) {
    method WriteMatrix (line 81) | func (m *Movie) WriteMatrix() {
  function NewMovie (line 13) | func NewMovie(size int) *Movie {

FILE: pkg/iso/reader.go
  type Atom (line 11) | type Atom struct
  type AtomTkhd (line 16) | type AtomTkhd struct
  type AtomMdhd (line 20) | type AtomMdhd struct
  type AtomVideo (line 24) | type AtomVideo struct
  type AtomAudio (line 29) | type AtomAudio struct
  type AtomMfhd (line 36) | type AtomMfhd struct
  type AtomMdat (line 40) | type AtomMdat struct
  type AtomTfhd (line 44) | type AtomTfhd struct
  type AtomTfdt (line 50) | type AtomTfdt struct
  type AtomTrun (line 54) | type AtomTrun struct
  function DecodeAtom (line 63) | func DecodeAtom(b []byte) (any, error) {
  function DecodeAtoms (line 185) | func DecodeAtoms(b []byte) (atoms []any, err error) {

FILE: pkg/ivideon/ivideon.go
  type Producer (line 16) | type Producer struct
    method Start (line 87) | func (p *Producer) Start() error {
    method probe (line 147) | func (p *Producer) probe() (err error) {
  function Dial (line 25) | func Dial(source string) (core.Producer, error) {
  function GetLiveStream (line 59) | func GetLiveStream(id string) (string, error) {
  type message (line 177) | type message struct

FILE: pkg/kasa/producer.go
  type Producer (line 20) | type Producer struct
    method Start (line 83) | func (c *Producer) Start() error {
    method probe (line 143) | func (c *Producer) probe() error {
  function Dial (line 27) | func Dial(url string) (*Producer, error) {
  constant MimeVideo (line 139) | MimeVideo = "video/x-h264"
  constant MimeG711U (line 140) | MimeG711U = "audio/g711u"
  function GetTimestamp (line 207) | func GetTimestamp(header http.Header) float64 {

FILE: pkg/magic/bitstream/producer.go
  type Producer (line 15) | type Producer struct
    method Start (line 62) | func (c *Producer) Start() error {
  function Open (line 20) | func Open(r io.Reader) (*Producer, error) {

FILE: pkg/magic/keyframe.go
  type Keyframe (line 14) | type Keyframe struct
    method AddTrack (line 45) | func (k *Keyframe) AddTrack(media *core.Media, _ *core.Codec, track *c...
    method CodecName (line 107) | func (k *Keyframe) CodecName() string {
    method WriteTo (line 114) | func (k *Keyframe) WriteTo(wr io.Writer) (int64, error) {
  function NewKeyframe (line 20) | func NewKeyframe() *Keyframe {

FILE: pkg/magic/mjpeg/producer.go
  type Producer (line 11) | type Producer struct
    method Start (line 41) | func (c *Producer) Start() error {
  function Open (line 16) | func Open(rd io.Reader) (*Producer, error) {

FILE: pkg/magic/producer.go
  function Open (line 21) | func Open(r io.Reader) (core.Producer, error) {

FILE: pkg/mdns/client.go
  constant ServiceDNSSD (line 18) | ServiceDNSSD = "_services._dns-sd._udp.local."
  constant ServiceHAP (line 19) | ServiceHAP   = "_hap._tcp.local."
  type ServiceEntry (line 22) | type ServiceEntry struct
    method String (line 29) | func (e *ServiceEntry) String() string {
    method TXT (line 37) | func (e *ServiceEntry) TXT() []string {
    method Complete (line 45) | func (e *ServiceEntry) Complete() bool {
    method Addr (line 49) | func (e *ServiceEntry) Addr() string {
    method Host (line 53) | func (e *ServiceEntry) Host(service string) string {
    method name (line 57) | func (e *ServiceEntry) name() string {
  constant sendTimeout (line 73) | sendTimeout = time.Millisecond * 505
  constant respTimeout (line 74) | respTimeout = time.Second * 3
  function BasicDiscovery (line 78) | func BasicDiscovery(service string, onentry func(*ServiceEntry) bool) er...
  function Discovery (line 99) | func Discovery(service string, onentry func(*ServiceEntry) bool) error {
  function Query (line 117) | func Query(host, service string) (entry *ServiceEntry, err error) {
  function QueryOrDiscovery (line 147) | func QueryOrDiscovery(host, service string, onentry func(*ServiceEntry) ...
  type Browser (line 156) | type Browser struct
    method ListenMulticastUDP (line 171) | func (b *Browser) ListenMulticastUDP() error {
    method Browse (line 235) | func (b *Browser) Browse(onentry func(*ServiceEntry) bool) error {
    method Close (line 296) | func (b *Browser) Close() error {
  function GetPTR (line 306) | func GetPTR(msg *dns.Msg, service string) string {
  function NewServiceEntries (line 315) | func NewServiceEntries(msg *dns.Msg, ip net.IP) (entries []*ServiceEntry) {

FILE: pkg/mdns/mdns_test.go
  function TestDiscovery (line 9) | func TestDiscovery(t *testing.T) {

FILE: pkg/mdns/server.go
  constant ClassCacheFlush (line 10) | ClassCacheFlush = 0x8001
  function Serve (line 12) | func Serve(service string, entries []*ServiceEntry) error {
  method Serve (line 22) | func (b *Browser) Serve(entries []*ServiceEntry) error {
  method MatchLocalIP (line 91) | func (b *Browser) MatchLocalIP(remote net.IP) net.IP {
  function AppendDNSSD (line 100) | func AppendDNSSD(msg *dns.Msg, service string) {
  function AppendEntry (line 115) | func AppendEntry(msg *dns.Msg, entry *ServiceEntry, service string, ip n...

FILE: pkg/mdns/syscall.go
  function SetsockoptInt (line 9) | func SetsockoptInt(fd uintptr, level, opt int, value int) (err error) {
  function SetsockoptIPMreq (line 13) | func SetsockoptIPMreq(fd uintptr, level, opt int, mreq *syscall.IPMreq) ...

FILE: pkg/mdns/syscall_bsd.go
  function SetsockoptInt (line 9) | func SetsockoptInt(fd uintptr, level, opt int, value int) (err error) {
  function SetsockoptIPMreq (line 24) | func SetsockoptIPMreq(fd uintptr, level, opt int, mreq *syscall.IPMreq) ...

FILE: pkg/mdns/syscall_windows.go
  function SetsockoptInt (line 7) | func SetsockoptInt(fd uintptr, level, opt int, value int) (err error) {
  function SetsockoptIPMreq (line 11) | func SetsockoptIPMreq(fd uintptr, level, opt int, mreq *syscall.IPMreq) ...

FILE: pkg/mjpeg/consumer.go
  type Consumer (line 10) | type Consumer struct
    method AddTrack (line 38) | func (c *Consumer) AddTrack(media *core.Media, _ *core.Codec, track *c...
    method WriteTo (line 57) | func (c *Consumer) WriteTo(wr io.Writer) (int64, error) {
  function NewConsumer (line 15) | func NewConsumer() *Consumer {

FILE: pkg/mjpeg/helpers.go
  function FixJPEG (line 12) | func FixJPEG(b []byte) []byte {
  function Encoder (line 57) | func Encoder(codec *core.Codec, skipEmpty int, handler core.HandlerFunc)...
  constant dhtSize (line 79) | dhtSize = 432
  function InjectDHT (line 81) | func InjectDHT(b []byte) []byte {

FILE: pkg/mjpeg/jpeg.go
  constant markerSOF (line 4) | markerSOF = 0xC0
  constant markerSOI (line 5) | markerSOI = 0xD8
  constant markerEOI (line 6) | markerEOI = 0xD9
  constant markerSOS (line 7) | markerSOS = 0xDA
  constant markerDQT (line 8) | markerDQT = 0xDB
  constant markerDHT (line 9) | markerDHT = 0xC4

FILE: pkg/mjpeg/mjpeg_test.go
  function TestRFC2435 (line 9) | func TestRFC2435(t *testing.T) {

FILE: pkg/mjpeg/rfc2435.go
  function MakeTables (line 29) | func MakeTables(q byte) (lqt, cqt []byte) {
  function MakeHeaders (line 144) | func MakeHeaders(p []byte, t byte, w, h uint16, lqt, cqt []byte) []byte {
  function MakeQuantHeader (line 191) | func MakeQuantHeader(p []byte, qt []byte, tableNo byte) []byte {
  function MakeHuffmanHeader (line 196) | func MakeHuffmanHeader(p, codelens, symbols []byte, tableNo, tableClass ...
  function MakeHuffmanHeaders (line 205) | func MakeHuffmanHeaders(p []byte) []byte {

FILE: pkg/mjpeg/rtp.go
  function RTPDepay (line 13) | func RTPDepay(handlerFunc core.HandlerFunc) core.HandlerFunc {
  function cutSize (line 88) | func cutSize(size uint16) uint16 {
  function RTPPay (line 92) | func RTPPay(handlerFunc core.HandlerFunc) core.HandlerFunc {
  function Transcode (line 186) | func Transcode(b []byte) ([]byte, error) {

FILE: pkg/mjpeg/writer.go
  function NewWriter (line 9) | func NewWriter(w io.Writer) io.Writer {
  constant header (line 15) | header = "--frame\r\nContent-Type: image/jpeg\r\nContent-Length: "
  type writer (line 17) | type writer struct
    method Write (line 22) | func (w *writer) Write(p []byte) (n int, err error) {

FILE: pkg/mp4/consumer.go
  type Consumer (line 16) | type Consumer struct
    method AddTrack (line 63) | func (c *Consumer) AddTrack(media *core.Media, _ *core.Codec, track *c...
    method WriteTo (line 167) | func (c *Consumer) WriteTo(wr io.Writer) (int64, error) {
  function NewConsumer (line 28) | func NewConsumer(medias []*core.Media) *Consumer {

FILE: pkg/mp4/demuxer.go
  type Demuxer (line 11) | type Demuxer struct
    method Probe (line 16) | func (d *Demuxer) Probe(init []byte) (medias []*core.Media) {
    method GetTrackID (line 60) | func (d *Demuxer) GetTrackID(codec *core.Codec) uint32 {
    method Demux (line 69) | func (d *Demuxer) Demux(data2 []byte) (trackID uint32, packets []*core...

FILE: pkg/mp4/helpers.go
  function ParseQuery (line 12) | func ParseQuery(query map[string][]string) []*core.Media {
  function ParseCodecs (line 58) | func ParseCodecs(codecs string, parseAudio bool) (medias []*core.Media) {
  function PatchVideoRotate (line 111) | func PatchVideoRotate(init []byte, degrees int) bool {
  function PatchVideoScale (line 155) | func PatchVideoScale(init []byte, scaleX, scaleY int) bool {

FILE: pkg/mp4/keyframe.go
  type Keyframe (line 12) | type Keyframe struct
    method AddTrack (line 47) | func (c *Keyframe) AddTrack(media *core.Media, _ *core.Codec, track *c...
    method WriteTo (line 102) | func (c *Keyframe) WriteTo(wr io.Writer) (int64, error) {
  function NewKeyframe (line 19) | func NewKeyframe(medias []*core.Media) *Keyframe {

FILE: pkg/mp4/mime.go
  constant MimeH264 (line 9) | MimeH264 = "avc1.640029"
  constant MimeH265 (line 10) | MimeH265 = "hvc1.1.6.L153.B0"
  constant MimeAAC (line 11) | MimeAAC  = "mp4a.40.2"
  constant MimeFlac (line 12) | MimeFlac = "flac"
  constant MimeOpus (line 13) | MimeOpus = "opus"
  function MimeCodecs (line 16) | func MimeCodecs(codecs []*core.Codec) string {
  function ContentType (line 43) | func ContentType(codecs []*core.Codec) string {

FILE: pkg/mp4/muxer.go
  type Muxer (line 13) | type Muxer struct
    method AddTrack (line 20) | func (m *Muxer) AddTrack(codec *core.Codec) {
    method GetInit (line 26) | func (m *Muxer) GetInit() ([]byte, error) {
    method Reset (line 113) | func (m *Muxer) Reset() {
    method GetPayload (line 121) | func (m *Muxer) GetPayload(trackID byte, packet *rtp.Packet) []byte {

FILE: pkg/mpegts/checksum.go
  function checksum (line 51) | func checksum(data []byte) uint32 {

FILE: pkg/mpegts/consumer.go
  type Consumer (line 13) | type Consumer struct
    method AddTrack (line 50) | func (c *Consumer) AddTrack(media *core.Media, codec *core.Codec, trac...
    method WriteTo (line 110) | func (c *Consumer) WriteTo(wr io.Writer) (int64, error) {
  function NewConsumer (line 19) | func NewConsumer() *Consumer {

FILE: pkg/mpegts/demuxer.go
  type Demuxer (line 14) | type Demuxer struct
    method ReadPacket (line 32) | func (d *Demuxer) ReadPacket(rd io.Reader) (*rtp.Packet, error) {
    method readPacketHeader (line 69) | func (d *Demuxer) readPacketHeader() (pid uint16, start bool, err erro...
    method skip (line 98) | func (d *Demuxer) skip(i byte) {
    method readBytes (line 102) | func (d *Demuxer) readBytes(i byte) []byte {
    method readPSIHeader (line 107) | func (d *Demuxer) readPSIHeader() {
    method readPAT (line 129) | func (d *Demuxer) readPAT() {
    method readPMT (line 147) | func (d *Demuxer) readPMT() {
    method readPES (line 180) | func (d *Demuxer) readPES(pid uint16, start bool) *rtp.Packet {
    method reset (line 252) | func (d *Demuxer) reset() {
    method readByte (line 259) | func (d *Demuxer) readByte() byte {
    method readBit (line 269) | func (d *Demuxer) readBit() byte {
    method readBits (line 280) | func (d *Demuxer) readBits(n byte) (res uint32) {
    method readBits16 (line 287) | func (d *Demuxer) readBits16(n byte) (res uint16) {
    method readTime (line 294) | func (d *Demuxer) readTime() uint32 {
    method bytes (line 307) | func (d *Demuxer) bytes() []byte {
    method left (line 311) | func (d *Demuxer) left() byte {
    method setSize (line 315) | func (d *Demuxer) setSize(size byte) {
  function NewDemuxer (line 26) | func NewDemuxer() *Demuxer {
  constant skipRead (line 30) | skipRead = 0xFF
  constant PacketSize (line 320) | PacketSize = 188
  constant SyncByte (line 321) | SyncByte   = 0x47
  constant ClockRate (line 322) | ClockRate  = 90000
  constant StreamTypeMetadata (line 327) | StreamTypeMetadata    = 0
  constant StreamTypePrivate (line 328) | StreamTypePrivate     = 0x06
  constant StreamTypeAAC (line 329) | StreamTypeAAC         = 0x0F
  constant StreamTypeH264 (line 330) | StreamTypeH264        = 0x1B
  constant StreamTypeH265 (line 331) | StreamTypeH265        = 0x24
  constant StreamTypePCMATapo (line 332) | StreamTypePCMATapo    = 0x90
  constant StreamTypePCMUTapo (line 333) | StreamTypePCMUTapo    = 0x91
  constant StreamTypePrivateOPUS (line 334) | StreamTypePrivateOPUS = 0xEB
  type PES (line 338) | type PES struct
    method SetBuffer (line 351) | func (p *PES) SetBuffer(size uint16, b []byte) {
    method AppendBuffer (line 357) | func (p *PES) AppendBuffer(b []byte) {
    method GetPacket (line 361) | func (p *PES) GetPacket() (pkt *rtp.Packet) {

FILE: pkg/mpegts/muxer.go
  type Muxer (line 10) | type Muxer struct
    method AddTrack (line 20) | func (m *Muxer) AddTrack(streamType byte) (pid uint16) {
    method GetHeader (line 37) | func (m *Muxer) GetHeader() []byte {
    method GetPayload (line 45) | func (m *Muxer) GetPayload(pid uint16, timestamp uint32, payload []byt...
    method writePAT (line 100) | func (m *Muxer) writePAT(wr *bits.Writer) {
    method writePMT (line 115) | func (m *Muxer) writePMT(wr *bits.Writer) {
    method writePES (line 146) | func (m *Muxer) writePES(wr *bits.Writer, pid uint16, pes *PES) {
    method writeHeader (line 179) | func (m *Muxer) writeHeader(wr *bits.Writer, pid uint16) {
    method writePSIHeader (line 193) | func (m *Muxer) writePSIHeader(wr *bits.Writer, tableID byte, size uin...
    method WriteTail (line 213) | func (m *Muxer) WriteTail(wr *bits.Writer) {
  function NewMuxer (line 14) | func NewMuxer() *Muxer {
  constant patPID (line 96) | patPID = 0
  constant pmtPID (line 97) | pmtPID = 0x1000
  constant pes0PID (line 98) | pes0PID = 0x100
  function WriteTime (line 218) | func WriteTime(b []byte, t uint32) {

FILE: pkg/mpegts/opus.go
  constant opusDT (line 8) | opusDT = 960 * ClockRate / 48000
  function CutOPUSPacket (line 18) | func CutOPUSPacket(b []byte) (packet []byte, left []byte) {
  function opus_control_header (line 32) | func opus_control_header(r *bits.Reader) int {

FILE: pkg/mpegts/producer.go
  type Producer (line 15) | type Producer struct
    method GetTrack (line 35) | func (c *Producer) GetTrack(media *core.Media, codec *core.Codec) (*co...
    method Start (line 41) | func (c *Producer) Start() error {
    method probe (line 64) | func (c *Producer) probe() error {
  function Open (line 20) | func Open(rd io.Reader) (*Producer, error) {
  function StreamType (line 156) | func StreamType(codec *core.Codec) uint8 {
  function TimestampToRTP (line 172) | func TimestampToRTP(rtp *rtp.Packet, codec *core.Codec) {

FILE: pkg/mpjpeg/multipart.go
  function Next (line 13) | func Next(rd *bufio.Reader) (http.Header, []byte, error) {

FILE: pkg/mpjpeg/producer.go
  type Producer (line 12) | type Producer struct
    method Start (line 40) | func (c *Producer) Start() error {
  function Open (line 17) | func Open(rd io.Reader) (*Producer, error) {

FILE: pkg/mqtt/client.go
  constant Timeout (line 12) | Timeout = time.Second * 5
  type Client (line 14) | type Client struct
    method Connect (line 23) | func (c *Client) Connect(clientID, username, password string) (err err...
    method Subscribe (line 45) | func (c *Client) Subscribe(topic string) (err error) {
    method Publish (line 56) | func (c *Client) Publish(topic string, payload []byte) (err error) {
    method Read (line 67) | func (c *Client) Read() (string, []byte, error) {
    method Close (line 109) | func (c *Client) Close() error {
  function NewClient (line 19) | func NewClient(conn net.Conn) *Client {

FILE: pkg/mqtt/message.go
  type Message (line 7) | type Message struct
    method WriteByte (line 22) | func (m *Message) WriteByte(b byte) {
    method WriteBytes (line 26) | func (m *Message) WriteBytes(b []byte) {
    method WriteUint16 (line 30) | func (m *Message) WriteUint16(i uint16) {
    method WriteLen (line 34) | func (m *Message) WriteLen(i int) {
    method WriteString (line 44) | func (m *Message) WriteString(s string) {
    method Bytes (line 49) | func (m *Message) Bytes() []byte {
  constant CONNECT (line 13) | CONNECT   = 0x10
  constant CONNACK (line 14) | CONNACK   = 0x20
  constant PUBLISH (line 15) | PUBLISH   = 0x30
  constant PUBACK (line 16) | PUBACK    = 0x40
  constant SUBSCRIBE (line 17) | SUBSCRIBE = 0x82
  constant SUBACK (line 18) | SUBACK    = 0x90
  constant QOS1 (line 19) | QOS1      = 0x02
  constant flagCleanStart (line 54) | flagCleanStart = 0x02
  constant flagUsername (line 55) | flagUsername   = 0x80
  constant flagPassword (line 56) | flagPassword   = 0x40
  function NewConnect (line 59) | func NewConnect(clientID, username, password string) *Message {
  function NewSubscribe (line 75) | func NewSubscribe(mid uint16, topic string, qos byte) *Message {
  function NewPublish (line 86) | func NewPublish(topic string, payload []byte) *Message {
  function NewPublishQOS1 (line 96) | func NewPublishQOS1(mid uint16, topic string, payload []byte) *Message {
  function ReadLen (line 107) | func ReadLen(r io.Reader) (uint32, error) {

FILE: pkg/multitrans/client.go
  type Client (line 18) | type Client struct
    method AddTrack (line 69) | func (c *Client) AddTrack(media *core.Media, _ *core.Codec, track *cor...
    method handshake (line 104) | func (c *Client) handshake(u *url.URL) error {
    method openTalkChannel (line 164) | func (c *Client) openTalkChannel(uri, session string) error {
    method GetTrack (line 191) | func (c *Client) GetTrack(media *core.Media, codec *core.Codec) (*core...
    method Start (line 195) | func (c *Client) Start() error {
    method Stop (line 200) | func (c *Client) Stop() error {
  function Dial (line 25) | func Dial(rawURL string) (core.Producer, error) {

FILE: pkg/nest/api.go
  type API (line 14) | type API struct
    method GetDevices (line 95) | func (a *API) GetDevices(projectID string) ([]DeviceInfo, error) {
    method ExchangeSDP (line 152) | func (a *API) ExchangeSDP(projectID, deviceID, offer string) (string, ...
    method refreshToken (line 230) | func (a *API) refreshToken() error {
    method ExtendStream (line 265) | func (a *API) ExtendStream() error {
    method GenerateRtspStream (line 330) | func (a *API) GenerateRtspStream(projectID, deviceID string) (string, ...
    method StopRTSPStream (line 387) | func (a *API) StopRTSPStream() error {
    method StartExtendStreamTimer (line 467) | func (a *API) StartExtendStreamTimer() {
    method StopExtendStreamTimer (line 481) | func (a *API) StopExtendStreamTimer() {
  type Auth (line 32) | type Auth struct
  type DeviceInfo (line 36) | type DeviceInfo struct
  function NewAPI (line 45) | func NewAPI(clientID, clientSecret, refreshToken string) (*API, error) {
  type Device (line 433) | type Device struct

FILE: pkg/nest/client.go
  type WebRTCClient (line 15) | type WebRTCClient struct
    method GetMedias (line 73) | func (c *WebRTCClient) GetMedias() []*core.Media {
    method GetTrack (line 77) | func (c *WebRTCClient) GetTrack(media *core.Media, codec *core.Codec) ...
    method AddTrack (line 81) | func (c *WebRTCClient) AddTrack(media *core.Media, codec *core.Codec, ...
    method Start (line 85) | func (c *WebRTCClient) Start() error {
    method Stop (line 90) | func (c *WebRTCClient) Stop() error {
    method MarshalJSON (line 95) | func (c *WebRTCClient) MarshalJSON() ([]byte, error) {
  type RTSPClient (line 20) | type RTSPClient struct
    method GetMedias (line 175) | func (c *RTSPClient) GetMedias() []*core.Media {
    method GetTrack (line 180) | func (c *RTSPClient) GetTrack(media *core.Media, codec *core.Codec) (*...
    method Start (line 184) | func (c *RTSPClient) Start() error {
    method Stop (line 189) | func (c *RTSPClient) Stop() error {
    method MarshalJSON (line 195) | func (c *RTSPClient) MarshalJSON() ([]byte, error) {
  function Dial (line 25) | func Dial(rawURL string) (core.Producer, error) {
  function rtcConn (line 99) | func rtcConn(nestAPI *API, rawURL, projectID, deviceID string) (*WebRTCC...
  function rtspConn (line 158) | func rtspConn(nestAPI *API, rawURL, projectID, deviceID string) (*RTSPCl...

FILE: pkg/ngrok/ngrok.go
  type Ngrok (line 13) | type Ngrok struct
    method Serve (line 59) | func (n *Ngrok) Serve() error {
  type Message (line 21) | type Message struct
  function NewNgrok (line 28) | func NewNgrok(command any) (*Ngrok, error) {

FILE: pkg/onvif/client.go
  constant PathDevice (line 15) | PathDevice = "/onvif/device_service"
  type Client (line 17) | type Client struct
    method GetURI (line 50) | func (c *Client) GetURI() (string, error) {
    method GetName (line 92) | func (c *Client) GetName() (string, error) {
    method GetProfilesTokens (line 101) | func (c *Client) GetProfilesTokens() ([]string, error) {
    method HasSnapshots (line 117) | func (c *Client) HasSnapshots() bool {
    method GetProfile (line 125) | func (c *Client) GetProfile(token string) ([]byte, error) {
    method GetVideoSourceConfiguration (line 131) | func (c *Client) GetVideoSourceConfiguration(token string) ([]byte, er...
    method GetStreamUri (line 137) | func (c *Client) GetStreamUri(token string) ([]byte, error) {
    method GetSnapshotUri (line 147) | func (c *Client) GetSnapshotUri(token string) ([]byte, error) {
    method GetServiceCapabilities (line 153) | func (c *Client) GetServiceCapabilities() ([]byte, error) {
    method DeviceRequest (line 160) | func (c *Client) DeviceRequest(operation string) ([]byte, error) {
    method MediaRequest (line 172) | func (c *Client) MediaRequest(operation string) ([]byte, error) {
    method Request (line 177) | func (c *Client) Request(url, body string) ([]byte, error) {
  function NewClient (line 25) | func NewClient(rawURL string) (*Client, error) {

FILE: pkg/onvif/envelope.go
  type Envelope (line 13) | type Envelope struct
    method Append (line 61) | func (e *Envelope) Append(args ...string) {
    method Appendf (line 67) | func (e *Envelope) Appendf(format string, args ...any) {
    method Bytes (line 71) | func (e *Envelope) Bytes() []byte {
  constant prefix1 (line 18) | prefix1 = `<?xml version="1.0" encoding="utf-8"?><s:Envelope xmlns:s="ht...
  constant prefix2 (line 19) | prefix2 = `<s:Body>`
  constant suffix (line 20) | suffix  = `</s:Body></s:Envelope>`
  function NewEnvelope (line 23) | func NewEnvelope() *Envelope {
  function NewEnvelopeWithUser (line 29) | func NewEnvelopeWithUser(user *url.Userinfo) *Envelope {

FILE: pkg/onvif/helpers.go
  type DiscoveryDevice (line 15) | type DiscoveryDevice struct
  function FindTagValue (line 21) | func FindTagValue(b []byte, tag string) string {
  function UUID (line 31) | func UUID() string {
  function DiscoveryStreamingDevices (line 37) | func DiscoveryStreamingDevices() ([]DiscoveryDevice, error) {
  function findScope (line 114) | func findScope(s, prefix string) string {
  function atoi (line 120) | func atoi(s string) int {
  function GetPosixTZ (line 131) | func GetPosixTZ(current time.Time) string {
  function GetPath (line 153) | func GetPath(urlOrPath, defPath string) string {

FILE: pkg/onvif/onvif_test.go
  function TestGetStreamUri (line 12) | func TestGetStreamUri(t *testing.T) {
  function TestGetCapabilities (line 128) | func TestGetCapabilities(t *testing.T) {

FILE: pkg/onvif/server.go
  constant ServiceGetServiceCapabilities (line 9) | ServiceGetServiceCapabilities = "GetServiceCapabilities"
  constant DeviceGetCapabilities (line 12) | DeviceGetCapabilities          = "GetCapabilities"
  constant DeviceGetDeviceInformation (line 13) | DeviceGetDeviceInformation     = "GetDeviceInformation"
  constant DeviceGetDiscoveryMode (line 14) | DeviceGetDiscoveryMode         = "GetDiscoveryMode"
  constant DeviceGetDNS (line 15) | DeviceGetDNS                   = "GetDNS"
  constant DeviceGetHostname (line 16) | DeviceGetHostname              = "GetHostname"
  constant DeviceGetNetworkDefaultGateway (line 17) | DeviceGetNetworkDefaultGateway = "GetNetworkDefaultGateway"
  constant DeviceGetNetworkInterfaces (line 18) | DeviceGetNetworkInterfaces     = "GetNetworkInterfaces"
  constant DeviceGetNetworkProtocols (line 19) | DeviceGetNetworkProtocols      = "GetNetworkProtocols"
  constant DeviceGetNTP (line 20) | DeviceGetNTP                   = "GetNTP"
  constant DeviceGetScopes (line 21) | DeviceGetScopes                = "GetScopes"
  constant DeviceGetServices (line 22) | DeviceGetServices              = "GetServices"
  constant DeviceGetSystemDateAndTime (line 23) | DeviceGetSystemDateAndTime     = "GetSystemDateAndTime"
  constant DeviceSetSystemDateAndTime (line 24) | DeviceSetSystemDateAndTime     = "SetSystemDateAndTime"
  constant DeviceSystemReboot (line 25) | DeviceSystemReboot             = "SystemReboot"
  constant MediaGetAudioEncoderConfigurations (line 29) | MediaGetAudioEncoderConfigurations       = "GetAudioEncoderConfigurations"
  constant MediaGetAudioSources (line 30) | MediaGetAudioSources                     = "GetAudioSources"
  constant MediaGetAudioSourceConfigurations (line 31) | MediaGetAudioSourceConfigurations        = "GetAudioSourceConfigurations"
  constant MediaGetProfile (line 32) | MediaGetProfile                          = "GetProfile"
  constant MediaGetProfiles (line 33) | MediaGetProfiles                         = "GetProfiles"
  constant MediaGetSnapshotUri (line 34) | MediaGetSnapshotUri                      = "GetSnapshotUri"
  constant MediaGetStreamUri (line 35) | MediaGetStreamUri                        = "GetStreamUri"
  constant MediaGetVideoEncoderConfiguration (line 36) | MediaGetVideoEncoderConfiguration        = "GetVideoEncoderConfiguration"
  constant MediaGetVideoEncoderConfigurations (line 37) | MediaGetVideoEncoderConfigurations       = "GetVideoEncoderConfigurations"
  constant MediaGetVideoEncoderConfigurationOptions (line 38) | MediaGetVideoEncoderConfigurationOptions = "GetVideoEncoderConfiguration...
  constant MediaGetVideoSources (line 39) | MediaGetVideoSources                     = "GetVideoSources"
  constant MediaGetVideoSourceConfiguration (line 40) | MediaGetVideoSourceConfiguration         = "GetVideoSourceConfiguration"
  constant MediaGetVideoSourceConfigurations (line 41) | MediaGetVideoSourceConfigurations        = "GetVideoSourceConfigurations"
  function GetRequestAction (line 44) | func GetRequestAction(b []byte) string {
  function GetCapabilitiesResponse (line 58) | func GetCapabilitiesResponse(host string) []byte {
  function GetServicesResponse (line 78) | func GetServicesResponse(host string) []byte {
  function GetSystemDateAndTimeResponse (line 95) | func GetSystemDateAndTimeResponse() []byte {
  function GetDeviceInformationResponse (line 124) | func GetDeviceInformationResponse(manuf, model, firmware, serial string)...
  function GetProfilesResponse (line 136) | func GetProfilesResponse(names []string) []byte {
  function GetProfileResponse (line 146) | func GetProfileResponse(name string) []byte {
  function appendProfile (line 154) | func appendProfile(e *Envelope, tag, name string) {
  function GetVideoSourcesResponse (line 163) | func GetVideoSourcesResponse(names []string) []byte {
  function GetVideoSourceConfigurationsResponse (line 177) | func GetVideoSourceConfigurationsResponse(names []string) []byte {
  function GetVideoSourceConfigurationResponse (line 187) | func GetVideoSourceConfigurationResponse(name string) []byte {
  function appendVideoSourceConfiguration (line 195) | func appendVideoSourceConfiguration(e *Envelope, tag, name string) {
  function GetVideoEncoderConfigurationsResponse (line 204) | func GetVideoEncoderConfigurationsResponse() []byte {
  function GetVideoEncoderConfigurationResponse (line 212) | func GetVideoEncoderConfigurationResponse() []byte {
  function appendVideoEncoderConfiguration (line 220) | func appendVideoEncoderConfiguration(e *Envelope, tag string) {
  function GetStreamUriResponse (line 234) | func GetStreamUriResponse(uri string) []byte {
  function GetSnapshotUriResponse (line 240) | func GetSnapshotUriResponse(uri string) []byte {
  function StaticResponse (line 246) | func StaticResponse(operation string) []byte {

FILE: pkg/opus/homekit.go
  function RepackToHAP (line 23) | func RepackToHAP(rtpTime byte, handler core.HandlerFunc) core.HandlerFunc {
  constant timestamp20 (line 35) | timestamp20 = 16000 * 0.020
  constant timestamp60 (line 36) | timestamp60 = 16000 * 0.060
  function repackToHAP20 (line 40) | func repackToHAP20(handler core.HandlerFunc) core.HandlerFunc {
  function repackToHAP60 (line 54) | func repackToHAP60(handler core.HandlerFunc) core.HandlerFunc {

FILE: pkg/opus/opus.go
  type Header (line 7) | type Header struct
  function UnmarshalHeader (line 15) | func UnmarshalHeader(b []byte) *Header {
  function parseMode (line 28) | func parseMode(config byte) string {
  function parseSampleRate (line 38) | func parseSampleRate(config byte) uint16 {
  function parseFrameSize (line 54) | func parseFrameSize(config byte) time.Duration {
  function parseChannels (line 72) | func parseChannels(s byte) byte {
  function parseFrames (line 79) | func parseFrames(c byte) byte {
  function JoinFrames (line 89) | func JoinFrames(b1, b2 []byte) []byte {

FILE: pkg/pcm/backchannel.go
  type Backchannel (line 11) | type Backchannel struct
    method GetTrack (line 46) | func (c *Backchannel) GetTrack(media *core.Media, codec *core.Codec) (...
    method AddTrack (line 50) | func (c *Backchannel) AddTrack(media *core.Media, codec *core.Codec, t...
    method Start (line 67) | func (c *Backchannel) Start() error {
  function NewBackchannel (line 16) | func NewBackchannel(cmd *shell.Command, audio string) (core.Producer, er...

FILE: pkg/pcm/flac.go
  function FLACHeader (line 17) | func FLACHeader(magic bool, sampleRate uint32) []byte {
  function FLACEncoder (line 51) | func FLACEncoder(codecName string, clockRate uint32, handler core.Handle...

FILE: pkg/pcm/handlers.go
  function RepackG711 (line 15) | func RepackG711(zeroTS bool, handler core.HandlerFunc) core.HandlerFunc {
  function LittleToBig (line 63) | func LittleToBig(handler core.HandlerFunc) core.HandlerFunc {
  function TranscodeHandler (line 71) | func TranscodeHandler(dst, src *core.Codec, handler core.HandlerFunc) co...
  function BytesPerSample (line 86) | func BytesPerSample(codec *core.Codec) int {
  function BytesPerFrame (line 96) | func BytesPerFrame(codec *core.Codec) int {
  function FramesPerDuration (line 103) | func FramesPerDuration(codec *core.Codec, duration time.Duration) int {
  function BytesPerDuration (line 107) | func BytesPerDuration(codec *core.Codec, duration time.Duration) int {

FILE: pkg/pcm/pcm.go
  function ceil (line 9) | func ceil(x float32) int {
  function Downsample (line 17) | func Downsample(k float32) func([]int16) []int16 {
  function Upsample (line 38) | func Upsample(k float32) func([]int16) []int16 {
  function FlipEndian (line 57) | func FlipEndian(src []byte) (dst []byte) {
  function Transcode (line 73) | func Transcode(dst, src *core.Codec) func([]byte) []byte {
  function ConsumerCodecs (line 201) | func ConsumerCodecs() []*core.Codec {
  function ProducerCodecs (line 210) | func ProducerCodecs() []*core.Codec {

FILE: pkg/pcm/pcm_test.go
  function TestTranscode (line 12) | func TestTranscode(t *testing.T) {

FILE: pkg/pcm/pcma.go
  constant alawMax (line 5) | alawMax = 0x7FFF
  function PCMAtoPCM (line 7) | func PCMAtoPCM(alaw byte) int16 {
  function PCMtoPCMA (line 29) | func PCMtoPCMA(pcm int16) byte {

FILE: pkg/pcm/pcmu.go
  constant bias (line 5) | bias = 0x84
  constant ulawMax (line 6) | ulawMax = alawMax - bias
  function PCMUtoPCM (line 8) | func PCMUtoPCM(ulaw byte) int16 {
  function PCMtoPCMU (line 24) | func PCMtoPCMU(pcm int16) byte {

FILE: pkg/pcm/producer.go
  type Producer (line 10) | type Producer struct
    method Start (line 36) | func (c *Producer) Start() error {
  function Open (line 15) | func Open(rd io.Reader) (*Producer, error) {

FILE: pkg/pcm/producer_sync.go
  type ProducerSync (line 11) | type ProducerSync struct
    method OnClose (line 39) | func (p *ProducerSync) OnClose(f func()) {
    method Start (line 43) | func (p *ProducerSync) Start() error {
  function OpenSync (line 18) | func OpenSync(codec *core.Codec, rd io.Reader) *ProducerSync {

FILE: pkg/pcm/s16le/s16le.go
  function PeaksRMS (line 3) | func PeaksRMS(b []byte) int16 {

FILE: pkg/pcm/v1/pcm.go
  constant cBias (line 5) | cBias = 0x84
  constant cClip (line 6) | cClip = 32635
  function LinearToMuLawSample (line 27) | func LinearToMuLawSample(sample int16) byte {
  function LinearToALawSample (line 65) | func LinearToALawSample(sample int16) byte {

FILE: pkg/pcm/v1/pcm_test.go
  function TestPCMUtoPCM (line 10) | func TestPCMUtoPCM(t *testing.T) {
  function TestPCMAtoPCM (line 18) | func TestPCMAtoPCM(t *testing.T) {
  function TestPCMtoPCMU (line 26) | func TestPCMtoPCMU(t *testing.T) {
  function TestPCMtoPCMA (line 34) | func TestPCMtoPCMA(t *testing.T) {

FILE: pkg/pinggy/pinggy.go
  type Client (line 15) | type Client struct
    method Close (line 70) | func (c *Client) Close() error {
    method NewSession (line 74) | func (c *Client) NewSession() error {
    method GetURLs (line 82) | func (c *Client) GetURLs() ([]string, error) {
    method Proxy (line 100) | func (c *Client) Proxy(address string) error {
  function NewClient (line 21) | func NewClient(proto string) (*Client, error) {
  function proxy (line 112) | func proxy(conn1 net.Conn, address string) {

FILE: pkg/probe/consumer.go
  type Probe (line 10) | type Probe struct
    method AddTrack (line 41) | func (p *Probe) AddTrack(media *core.Media, codec *core.Codec, track *...
    method Start (line 51) | func (p *Probe) Start() error {
  function Create (line 14) | func Create(name string, query url.Values) *Probe {

FILE: pkg/ring/api.go
  type RefreshTokenAuth (line 21) | type RefreshTokenAuth struct
  type EmailAuth (line 25) | type EmailAuth struct
  type AuthConfig (line 30) | type AuthConfig struct
  type AuthTokenResponse (line 35) | type AuthTokenResponse struct
  type Auth2faResponse (line 43) | type Auth2faResponse struct
  type SocketTicketResponse (line 51) | type SocketTicketResponse struct
  type SessionResponse (line 56) | type SessionResponse struct
  type RingApi (line 65) | type RingApi struct
    method GetAuth (line 227) | func (c *RingApi) GetAuth(twoFactorAuthCode string) (*AuthTokenRespons...
    method FetchRingDevices (line 337) | func (c *RingApi) FetchRingDevices() (*RingDevicesResponse, error) {
    method GetSocketTicket (line 389) | func (c *RingApi) GetSocketTicket() (*SocketTicketResponse, error) {
    method Request (line 403) | func (c *RingApi) Request(method, url string, body interface{}) ([]byt...
    method ensureSession (line 512) | func (c *RingApi) ensureSession() error {
    method ensureAuth (line 580) | func (c *RingApi) ensureAuth() error {
  type CameraKind (line 83) | type CameraKind
  type CameraData (line 85) | type CameraData struct
  type RingDeviceType (line 93) | type RingDeviceType
  type RingDevicesResponse (line 95) | type RingDevicesResponse struct
  constant Doorbot (line 105) | Doorbot             CameraKind = "doorbot"
  constant Doorbell (line 106) | Doorbell            CameraKind = "doorbell"
  constant DoorbellV3 (line 107) | DoorbellV3          CameraKind = "doorbell_v3"
  constant DoorbellV4 (line 108) | DoorbellV4          CameraKind = "doorbell_v4"
  constant DoorbellV5 (line 109) | DoorbellV5          CameraKind = "doorbell_v5"
  constant DoorbellOyster (line 110) | DoorbellOyster      CameraKind = "doorbell_oyster"
  constant DoorbellPortal (line 111) | DoorbellPortal      CameraKind = "doorbell_portal"
  constant DoorbellScallop (line 112) | DoorbellScallop     CameraKind = "doorbell_scallop"
  constant DoorbellScallopLite (line 113) | DoorbellScallopLite CameraKind = "doorbell_scallop_lite"
  constant DoorbellGraham (line 114) | DoorbellGraham      CameraKind = "doorbell_graham_cracker"
  constant LpdV1 (line 115) | LpdV1               CameraKind = "lpd_v1"
  constant LpdV2 (line 116) | LpdV2               CameraKind = "lpd_v2"
  constant LpdV4 (line 117) | LpdV4               CameraKind = "lpd_v4"
  constant JboxV1 (line 118) | JboxV1              CameraKind = "jbox_v1"
  constant StickupCam (line 119) | StickupCam          CameraKind = "stickup_cam"
  constant StickupCamV3 (line 120) | StickupCamV3        CameraKind = "stickup_cam_v3"
  constant StickupCamElite (line 121) | StickupCamElite     CameraKind = "stickup_cam_elite"
  constant StickupCamLongfin (line 122) | StickupCamLongfin   CameraKind = "stickup_cam_longfin"
  constant StickupCamLunar (line 123) | StickupCamLunar     CameraKind = "stickup_cam_lunar"
  constant SpotlightV2 (line 124) | SpotlightV2         CameraKind = "spotlightw_v2"
  constant HpCamV1 (line 125) | HpCamV1             CameraKind = "hp_cam_v1"
  constant HpCamV2 (line 126) | HpCamV2             CameraKind = "hp_cam_v2"
  constant StickupCamV4 (line 127) | StickupCamV4        CameraKind = "stickup_cam_v4"
  constant FloodlightV1 (line 128) | FloodlightV1        CameraKind = "floodlight_v1"
  constant FloodlightV2 (line 129) | FloodlightV2        CameraKind = "floodlight_v2"
  constant FloodlightPro (line 130) | FloodlightPro       CameraKind = "floodlight_pro"
  constant CocoaCamera (line 131) | CocoaCamera         CameraKind = "cocoa_camera"
  constant CocoaDoorbell (line 132) | CocoaDoorbell       CameraKind = "cocoa_doorbell"
  constant CocoaFloodlight (line 133) | CocoaFloodlight     CameraKind = "cocoa_floodlight"
  constant CocoaSpotlight (line 134) | CocoaSpotlight      CameraKind = "cocoa_spotlight"
  constant StickupCamMini (line 135) | StickupCamMini      CameraKind = "stickup_cam_mini"
  constant OnvifCamera (line 136) | OnvifCamera         CameraKind = "onvif_camera"
  constant IntercomHandsetAudio (line 140) | IntercomHandsetAudio RingDeviceType = "intercom_handset_audio"
  constant OnvifCameraType (line 141) | OnvifCameraType      RingDeviceType = "onvif_camera"
  constant clientAPIBaseURL (line 145) | clientAPIBaseURL   = "https://api.ring.com/clients_api/"
  constant deviceAPIBaseURL (line 146) | deviceAPIBaseURL   = "https://api.ring.com/devices/v1/"
  constant commandsAPIBaseURL (line 147) | commandsAPIBaseURL = "https://api.ring.com/commands/v1/"
  constant appAPIBaseURL (line 148) | appAPIBaseURL      = "https://prd-api-us.prd.rings.solutions/api/v1/"
  constant oauthURL (line 149) | oauthURL           = "https://oauth.ring.com/oauth/token"
  constant apiVersion (line 150) | apiVersion         = 11
  constant defaultTimeout (line 151) | defaultTimeout     = 20 * time.Second
  constant maxRetries (line 152) | maxRetries         = 3
  constant sessionValidTime (line 153) | sessionValidTime   = 12 * time.Hour
  function NewRestClient (line 156) | func NewRestClient(auth interface{}, onTokenRefresh func(string)) (*Ring...
  function ClientAPI (line 211) | func ClientAPI(path string) string {
  function DeviceAPI (line 215) | func DeviceAPI(path string) string {
  function CommandsAPI (line 219) | func CommandsAPI(path string) string {
  function AppAPI (line 223) | func AppAPI(path string) string {
  function parseAuthConfig (line 663) | func parseAuthConfig(refreshToken string) (*AuthConfig, error) {
  function encodeAuthConfig (line 678) | func encodeAuthConfig(config *AuthConfig) string {
  function generateHardwareID (line 683) | func generateHardwareID() string {
  function interfaceSlice (line 689) | func interfaceSlice(slice interface{}) []CameraData {

FILE: pkg/ring/client.go
  type Client (line 16) | type Client struct
    method onWSMessage (line 225) | func (c *Client) onWSMessage(msg WSMessage) {
    method GetMedias (line 308) | func (c *Client) GetMedias() []*core.Media {
    method GetTrack (line 312) | func (c *Client) GetTrack(media *core.Media, codec *core.Codec) (*core...
    method AddTrack (line 316) | func (c *Client) AddTrack(media *core.Media, codec *core.Codec, track ...
    method Start (line 331) | func (c *Client) Start() error {
    method Stop (line 335) | func (c *Client) Stop() error {
    method MarshalJSON (line 353) | func (c *Client) MarshalJSON() ([]byte, error) {
  function Dial (line 26) | func Dial(rawURL string) (*Client, error) {

FILE: pkg/ring/snapshot.go
  type SnapshotProducer (line 10) | type SnapshotProducer struct
    method Start (line 43) | func (p *SnapshotProducer) Start() error {
    method Stop (line 59) | func (p *SnapshotProducer) Stop() error {
  function NewSnapshotProducer (line 17) | func NewSnapshotProducer(client *RingApi, cameraID int) *SnapshotProducer {

FILE: pkg/ring/ws.go
  type SessionBody (line 14) | type SessionBody struct
  type AnswerMessage (line 19) | type AnswerMessage struct
  type IceCandidateMessage (line 28) | type IceCandidateMessage struct
  type SessionMessage (line 37) | type SessionMessage struct
  type PongMessage (line 42) | type PongMessage struct
  type NotificationMessage (line 47) | type NotificationMessage struct
  type StreamInfoMessage (line 56) | type StreamInfoMessage struct
  type CloseRequest (line 65) | type CloseRequest struct
  type WSMessage (line 76) | type WSMessage struct
  type WSClient (line 81) | type WSClient struct
    method Close (line 137) | func (c *WSClient) Close() error {
    method startPingLoop (line 157) | func (c *WSClient) startPingLoop() {
    method startMessageLoop (line 173) | func (c *WSClient) startMessageLoop() {
    method activateSession (line 196) | func (c *WSClient) activateSession() error {
    method sendSessionMessage (line 213) | func (c *WSClient) sendSessionMessage(method string, payload map[strin...
    method onWsMessage (line 249) | func (c *WSClient) onWsMessage(msg WSMessage) {
    method onWsError (line 255) | func (c *WSClient) onWsError(err error) {
    method onWsClose (line 261) | func (c *WSClient) onWsClose() {
  constant CloseReasonNormalClose (line 97) | CloseReasonNormalClose          = 0
  constant CloseReasonAuthenticationFailed (line 98) | CloseReasonAuthenticationFailed = 5
  constant CloseReasonTimeout (line 99) | CloseReasonTimeout              = 6
  function StartWebsocket (line 102) | func StartWebsocket(cameraID int, api *RingApi) (*WSClient, error) {

FILE: pkg/roborock/api.go
  type UserInfo (line 18) | type UserInfo struct
  function GetBaseURL (line 32) | func GetBaseURL(username string) (string, error) {
  function Login (line 60) | func Login(baseURL, username, password string) (*UserInfo, error) {
  function GetHomeID (line 91) | func GetHomeID(baseURL, token string) (int, error) {
  type DeviceInfo (line 122) | type DeviceInfo struct
  function GetDevices (line 128) | func GetDevices(ui *UserInfo, homeID int) ([]DeviceInfo, error) {

FILE: pkg/roborock/client.go
  type Client (line 22) | type Client struct
    method Dial (line 49) | func (c *Client) Dial() error {
    method Connect (line 68) | func (c *Client) Connect() error {
    method CheckHomesecPassword (line 202) | func (c *Client) CheckHomesecPassword() (err error) {
    method GetHomesecConnectStatus (line 217) | func (c *Client) GetHomesecConnectStatus() (clientID string, err error) {
    method StartCameraPreview (line 235) | func (c *Client) StartCameraPreview() error {
    method StopCameraPreview (line 240) | func (c *Client) StopCameraPreview(clientID string) error {
    method GetTurnServer (line 245) | func (c *Client) GetTurnServer() (turn *pion.ICEServer, err error) {
    method SendSDPtoRobot (line 270) | func (c *Client) SendSDPtoRobot(offer *pion.SessionDescription) (err e...
    method SendICEtoRobot (line 280) | func (c *Client) SendICEtoRobot(candidate string, mid string) (err err...
    method GetDeviceSDP (line 287) | func (c *Client) GetDeviceSDP() (sd *pion.SessionDescription, err erro...
    method GetDeviceICE (line 313) | func (c *Client) GetDeviceICE() (ice []string, err error) {
    method StartVoiceChat (line 342) | func (c *Client) StartVoiceChat() error {
    method SwitchVideoQuality (line 348) | func (c *Client) SwitchVideoQuality(hd bool) error {
    method SetVoiceChatVolume (line 356) | func (c *Client) SetVoiceChatVolume(volume int) error {
    method EnableHomesecVoice (line 361) | func (c *Client) EnableHomesecVoice(enable bool) error {
    method Request (line 369) | func (c *Client) Request(method string, args any) (err error) {
  function Dial (line 38) | func Dial(rawURL string) (*Client, error) {

FILE: pkg/roborock/iot/client.go
  type Codec (line 17) | type Codec struct
    method WriteRequest (line 43) | func (c *Codec) WriteRequest(r *rpc.Request, v any) error {
    method ReadResponseHeader (line 66) | func (c *Codec) ReadResponseHeader(r *rpc.Response) error {
    method ReadResponseBody (line 114) | func (c *Codec) ReadResponseBody(v any) error {
    method Close (line 126) | func (c *Codec) Close() error {
  type dps (line 26) | type dps struct
  type response (line 34) | type response struct
  function Dial (line 130) | func Dial(rawURL string) (*rpc.Client, error) {

FILE: pkg/roborock/iot/crypto.go
  method key (line 12) | func (c *Codec) key(timestamp uint32) []byte {
  method Decrypt (line 18) | func (c *Codec) Decrypt(cipherText []byte) ([]byte, error) {
  method Encrypt (line 36) | func (c *Codec) Encrypt(plainText []byte, seq, random, timestamp uint32)...
  function encodeTimestamp (line 58) | func encodeTimestamp(i uint32) string {
  function pad (line 69) | func pad(plainText []byte, blockSize int) []byte {
  function unpad (line 77) | func unpad(paddedText []byte) []byte {
  function encryptECB (line 82) | func encryptECB(plainText, key []byte) []byte {
  function decryptECB (line 100) | func decryptECB(cipherText, key []byte) []byte {

FILE: pkg/roborock/producer.go
  method GetMedias (line 7) | func (c *Client) GetMedias() []*core.Media {
  method GetTrack (line 11) | func (c *Client) GetTrack(media *core.Media, codec *core.Codec) (*core.R...
  method AddTrack (line 19) | func (c *Client) AddTrack(media *core.Media, codec *core.Codec, track *c...
  method Start (line 24) | func (c *Client) Start() error {
  method Stop (line 39) | func (c *Client) Stop() error {
  method MarshalJSON (line 44) | func (c *Client) MarshalJSON() ([]byte, error) {

FILE: pkg/rtmp/client.go
  function DialPlay (line 15) | func DialPlay(rawURL string) (*flv.Producer, error) {
  function DialPublish (line 38) | func DialPublish(rawURL string, cons *flv.Consumer) (io.Writer, error) {
  function NewClient (line 66) | func NewClient(conn net.Conn, u *url.URL) (*Conn, error) {
  method clienHandshake (line 100) | func (c *Conn) clienHandshake() error {
  method play (line 123) | func (c *Conn) play() error {
  method publish (line 136) | func (c *Conn) publish() error {

FILE: pkg/rtmp/conn.go
  constant TypeSetPacketSize (line 15) | TypeSetPacketSize   = 1
  constant TypeServerBandwidth (line 16) | TypeServerBandwidth = 5
  constant TypeClientBandwidth (line 17) | TypeClientBandwidth = 6
  constant TypeAudio (line 18) | TypeAudio           = 8
  constant TypeVideo (line 19) | TypeVideo           = 9
  constant TypeData (line 20) | TypeData            = 18
  constant TypeCommand (line 21) | TypeCommand         = 20
  type Conn (line 24) | type Conn struct
    method Close (line 45) | func (c *Conn) Close() error {
    method readResponse (line 49) | func (c *Conn) readResponse(wait func(items []any) bool) ([]any, error) {
    method readMessage (line 126) | func (c *Conn) readMessage() (byte, uint32, []byte, error) {
    method writeMessage (line 187) | func (c *Conn) writeMessage(chunkID, tagType byte, timeMS uint32, payl...
    method resetBuffer (line 217) | func (c *Conn) resetBuffer() {
    method appendType0 (line 221) | func (c *Conn) appendType0(chunkID, tagType byte, timeMS, size uint32,...
    method appendType3 (line 233) | func (c *Conn) appendType3(chunkID byte, payload []byte) {
    method writePacketSize (line 238) | func (c *Conn) writePacketSize() error {
    method writeConnect (line 243) | func (c *Conn) writeConnect() error {
    method writeReleaseStream (line 268) | func (c *Conn) writeReleaseStream() error {
    method writeCreateStream (line 279) | func (c *Conn) writeCreateStream() error {
    method writePublish (line 302) | func (c *Conn) writePublish() error {
    method writePlay (line 324) | func (c *Conn) writePlay() error {
    method readSize (line 346) | func (c *Conn) readSize(n uint32) ([]byte, error) {
  type chunk (line 69) | type chunk struct
    method readHeader (line 78) | func (c *chunk) readHeader(typ byte) error {
    method readExtendedTime (line 115) | func (c *chunk) readExtendedTime() uint32 {
  function PutUint24 (line 354) | func PutUint24(b []byte, v uint32) {
  function Uint24 (line 361) | func Uint24(b []byte) uint32 {
  function getString (line 366) | func getString(v []any, i int, key string) string {

FILE: pkg/rtmp/flv.go
  method Producer (line 7) | func (c *Conn) Producer() (*flv.Producer, error) {
  method Read (line 29) | func (c *Conn) Read(p []byte) (n int, err error) {
  function encodeFLV (line 57) | func encodeFLV(b []byte, msgType byte, time uint32, payload []byte) {
  method Write (line 73) | func (c *Conn) Write(p []byte) (n int, err error) {

FILE: pkg/rtmp/server.go
  function NewServer (line 17) | func NewServer(conn net.Conn) (*Conn, error) {
  method serverHandshake (line 39) | func (c *Conn) serverHandshake() error {
  method ReadCommands (line 91) | func (c *Conn) ReadCommands() error {
  constant CommandConnect (line 116) | CommandConnect       = "connect"
  constant CommandReleaseStream (line 117) | CommandReleaseStream = "releaseStream"
  constant CommandFCPublish (line 118) | CommandFCPublish     = "FCPublish"
  constant CommandCreateStream (line 119) | CommandCreateStream  = "createStream"
  constant CommandPublish (line 120) | CommandPublish       = "publish"
  constant CommandPlay (line 121) | CommandPlay          = "play"
  method acceptCommand (line 124) | func (c *Conn) acceptCommand(b []byte) error {
  method WriteStart (line 187) | func (c *Conn) WriteStart() error {
  function nowMS (line 199) | func nowMS() uint32 {

FILE: pkg/rtsp/client.go
  function NewClient (line 23) | func NewClient(uri string) *Conn {
  method Dial (line 33) | func (c *Conn) Dial() (err error) {
  method Do (line 84) | func (c *Conn) Do(req *tcp.Request) (*tcp.Response, error) {
  method Options (line 143) | func (c *Conn) Options() error {
  method Describe (line 161) | func (c *Conn) Describe() error {
  method Announce (line 223) | func (c *Conn) Announce() (err error) {
  method Record (line 241) | func (c *Conn) Record() (err error) {
  method SetupMedia (line 254) | func (c *Conn) SetupMedia(media *core.Media) (byte, error) {
  method Play (line 386) | func (c *Conn) Play() (err error) {
  method Teardown (line 391) | func (c *Conn) Teardown() (err error) {
  method Close (line 397) | func (c *Conn) Close() error {
  method WriteToUDP (line 410) | func (c *Conn) WriteToUDP(b []byte, channel byte) (int, error) {
  constant listenUDPAttemps (line 414) | listenUDPAttemps = 10
  function ListenUDPPair (line 418) | func ListenUDPPair() (*net.UDPConn, *net.UDPConn, error) {

FILE: pkg/rtsp/client_test.go
  function TestTimeout (line 12) | func TestTimeout(t *testing.T) {
  function TestMissedControl (line 28) | func TestMissedControl(t *testing.T) {

FILE: pkg/rtsp/conn.go
  type Conn (line 20) | type Conn struct
    method Handle (line 91) | func (c *Conn) Handle() (err error) {
    method handleKeepalive (line 154) | func (c *Conn) handleKeepalive(ctx context.Context, d time.Duration) {
    method handleUDPData (line 169) | func (c *Conn) handleUDPData(channel byte) {
    method handleTCPData (line 188) | func (c *Conn) handleTCPData() error {
    method handleRawPacket (line 289) | func (c *Conn) handleRawPacket(channel byte, buf []byte) error {
    method WriteRequest (line 321) | func (c *Conn) WriteRequest(req *tcp.Request) error {
    method ReadRequest (line 355) | func (c *Conn) ReadRequest() (*tcp.Request, error) {
    method WriteResponse (line 362) | func (c *Conn) WriteResponse(res *tcp.Response) error {
    method ReadResponse (line 404) | func (c *Conn) ReadResponse() (*tcp.Response, error) {
  constant ProtoRTSP (line 57) | ProtoRTSP      = "RTSP/1.0"
  constant MethodOptions (line 58) | MethodOptions  = "OPTIONS"
  constant MethodSetup (line 59) | MethodSetup    = "SETUP"
  constant MethodTeardown (line 60) | MethodTeardown = "TEARDOWN"
  constant MethodDescribe (line 61) | MethodDescribe = "DESCRIBE"
  constant MethodPlay (line 62) | MethodPlay     = "PLAY"
  constant MethodPause (line 63) | MethodPause    = "PAUSE"
  constant MethodAnnounce (line 64) | MethodAnnounce = "ANNOUNCE"
  constant MethodRecord (line 65) | MethodRecord   = "RECORD"
  type State (line 68) | type State
    method String (line 70) | func (s State) String() string {
  constant StateNone (line 85) | StateNone State = iota
  constant StateConn (line 86) | StateConn
  constant StateSetup (line 87) | StateSetup
  constant StatePlay (line 88) | StatePlay

FILE: pkg/rtsp/consumer.go
  method GetMedias (line 15) | func (c *Conn) GetMedias() []*core.Media {
  method AddTrack (line 20) | func (c *Conn) AddTrack(media *core.Media, codec *core.Codec, track *cor...
  constant startVideoBuf (line 69) | startVideoBuf = 32 * 1024
  constant startAudioBuf (line 70) | startAudioBuf = 2 * 1024
  constant maxBuf (line 71) | maxBuf        = 1024 * 1024
  constant rtpHdr (line 72) | rtpHdr        = 12
  constant intHdr (line 73) | intHdr        = 4
  method packetWriter (line 76) | func (c *Conn) packetWriter(codec *core.Codec, channel, payloadType uint...
  method writeInterleavedData (line 178) | func (c *Conn) writeInterleavedData(data []byte) error {

FILE: pkg/rtsp/helpers.go
  type RTCP (line 16) | type RTCP struct
  constant sdpHeader (line 22) | sdpHeader = `v=0
  function UnmarshalSDP (line 27) | func UnmarshalSDP(rawSDP []byte) ([]*core.Media, error) {
  function findFmtpLine (line 105) | func findFmtpLine(payloadType uint8, descriptions []*sdp.MediaDescriptio...
  function urlParse (line 120) | func urlParse(rawURL string) (*url.URL, error) {
  function indexN (line 141) | func indexN(s string, c byte, n int) int {

FILE: pkg/rtsp/producer.go
  method GetTrack (line 10) | func (c *Conn) GetTrack(media *core.Media, codec *core.Codec) (*core.Rec...
  method Start (line 53) | func (c *Conn) Start() (err error) {
  method Stop (line 94) | func (c *Conn) Stop() (err error) {
  method MarshalJSON (line 112) | func (c *Conn) MarshalJSON() ([]byte, error) {
  method Reconnect (line 116) | func (c *Conn) Reconnect() error {

FILE: pkg/rtsp/rtsp_test.go
  function TestURLParse (line 10) | func TestURLParse(t *testing.T) {
  function TestBugSDP1 (line 30) | func TestBugSDP1(t *testing.T) {
  function TestBugSDP2 (line 63) | func TestBugSDP2(t *testing.T) {
  function TestBugSDP3 (line 85) | func TestBugSDP3(t *testing.T) {
  function TestBugSDP4 (line 119) | func TestBugSDP4(t *testing.T) {
  function TestBugSDP5 (line 143) | func TestBugSDP5(t *testing.T) {
  function TestBugSDP6 (line 170) | func TestBugSDP6(t *testing.T) {
  function TestBugSDP7 (line 212) | func TestBugSDP7(t *testing.T) {
  function TestHikvisionPCM (line 246) | func TestHikvisionPCM(t *testing.T) {

FILE: pkg/rtsp/server.go
  function NewServer (line 18) | func NewServer(conn net.Conn) *Conn {
  method Auth (line 31) | func (c *Conn) Auth(username, password string) {
  method Accept (line 36) | func (c *Conn) Accept() error {
  function reqTrackID (line 222) | func reqTrackID(req *tcp.Request) int {

FILE: pkg/shell/command.go
  type Command (line 12) | type Command struct
    method Start (line 27) | func (c *Command) Start() error {
    method Wait (line 40) | func (c *Command) Wait() error {
    method Run (line 45) | func (c *Command) Run() error {
    method Done (line 52) | func (c *Command) Done() <-chan struct{} {
    method Close (line 56) | func (c *Command) Close() error {
  function NewCommand (line 19) | func NewCommand(s string) *Command {

FILE: pkg/shell/shell.go
  function QuoteSplit (line 10) | func QuoteSplit(s string) []string {
  function RunUntilSignal (line 39) | func RunUntilSignal() {

FILE: pkg/shell/shell_test.go
  function TestQuoteSplit (line 9) | func TestQuoteSplit(t *testing.T) {

FILE: pkg/srtp/server.go
  type Server (line 10) | type Server struct
    method Port (line 24) | func (s *Server) Port() int {
    method AddSession (line 34) | func (s *Server) AddSession(session *Session) {
    method DelSession (line 55) | func (s *Server) DelSession(session *Session) {
    method GetSession (line 68) | func (s *Server) GetSession(ssrc uint32) (session *Session) {
    method handle (line 75) | func (s *Server) handle() error {
  function NewServer (line 17) | func NewServer(address string) *Server {

FILE: pkg/srtp/session.go
  type Session (line 12) | type Session struct
    method init (line 57) | func (s *Session) init() error {
    method WriteRTP (line 71) | func (s *Session) WriteRTP(packet *rtp.Packet) (int, error) {
    method WriteRTCP (line 110) | func (s *Session) WriteRTCP(packet rtcp.Packet) (int, error) {
    method ReadRTP (line 122) | func (s *Session) ReadRTP(b []byte) {
    method ReadRTCP (line 139) | func (s *Session) ReadRTCP(b []byte) {
  type Endpoint (line 30) | type Endpoint struct
    method init (line 41) | func (e *Endpoint) init() (err error) {
  function profile (line 47) | func profile(key []byte) srtp.ProtectionProfile {

FILE: pkg/tapo/backchannel.go
  method AddTrack (line 12) | func (c *Client) AddTrack(media *core.Media, _ *core.Codec, track *core....
  method SetupBackchannel (line 35) | func (c *Client) SetupBackchannel() (err error) {
  method WriteBackchannel (line 50) | func (c *Client) WriteBackchannel(body []byte) (err error) {

FILE: pkg/tapo/client.go
  type Client (line 28) | type Client struct
    method newConn (line 80) | func (c *Client) newConn() (net.Conn, error) {
    method newDectypter (line 131) | func (c *Client) newDectypter(res *http.Response, brand, username, pas...
    method SetupStream (line 174) | func (c *Client) SetupStream() (err error) {
    method Handle (line 185) | func (c *Client) Handle() error {
    method Close (line 260) | func (c *Client) Close() (err error) {
    method Request (line 270) | func (c *Client) Request(conn net.Conn, body []byte) (string, error) {
  type cbcMode (line 51) | type cbcMode interface
  function Dial (line 63) | func Dial(rawURL string) (*Client, error) {
  function dial (line 305) | func dial(req *http.Request, brand, username, password string) (net.Conn...
  constant keyShort (line 376) | keyShort = "RDpbLfCPsJZ7fiv"
  constant keyLong (line 377) | keyLong  = "yLwVl0zKqws7LgKPRQ84Mdt708T1qQ3Ha7xv3H7NyU84p21BriUWBU43odz3...
  function securityEncode (line 380) | func securityEncode(s string) string {

FILE: pkg/tapo/producer.go
  method GetMedias (line 10) | func (c *Client) GetMedias() []*core.Media {
  method GetTrack (line 41) | func (c *Client) GetTrack(media *core.Media, codec *core.Codec) (*core.R...
  method Start (line 63) | func (c *Client) Start() error {
  method Stop (line 67) | func (c *Client) Stop() error {
  method MarshalJSON (line 77) | func (c *Client) MarshalJSON() ([]byte, error) {

FILE: pkg/tcp/auth.go
  type Auth (line 12) | type Auth struct
    method Read (line 38) | func (a *Auth) Read(res *Response) bool {
    method Write (line 65) | func (a *Auth) Writ
Condensed preview — 530 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (2,011K chars).
[
  {
    "path": ".github/workflows/build.yml",
    "chars": 9113,
    "preview": "name: Build and Push\n\non:\n  workflow_dispatch:\n  push:\n    branches:\n      - 'master'\n    tags:\n      - 'v*'\n\njobs:\n  bu"
  },
  {
    "path": ".github/workflows/gh-pages.yml",
    "chars": 1454,
    "preview": "# Simple workflow for deploying static content to GitHub Pages\nname: Deploy static content to Pages\n\non:\n  # Allows you "
  },
  {
    "path": ".github/workflows/test.yml",
    "chars": 3020,
    "preview": "name: Test Build and Run\n\non:\n#  push:\n#    branches:\n#      - '*'\n#  pull_request:\n#  merge_group:\n  workflow_dispatch:"
  },
  {
    "path": ".gitignore",
    "chars": 227,
    "preview": ".idea/\n.tmp/\n\ngo2rtc.yaml\ngo2rtc.json\n\ngo2rtc_freebsd*\ngo2rtc_linux*\ngo2rtc_mac*\ngo2rtc_win*\n\n/go2rtc\n/go2rtc.exe\n\n0_tes"
  },
  {
    "path": "LICENSE",
    "chars": 1068,
    "preview": "MIT License\n\nCopyright (c) 2022 Alexey Khit\n\nPermission is hereby granted, free of charge, to any person obtaining a cop"
  },
  {
    "path": "README.md",
    "chars": 37392,
    "preview": "<h1 align=\"center\">\n  <a href=\"https://github.com/AlexxIT/go2rtc\">\n    <img src=\"./website/images/logo.gif\" alt=\"go2rtc "
  },
  {
    "path": "docker/Dockerfile",
    "chars": 1444,
    "preview": "# syntax=docker/dockerfile:labs\n\n# 0. Prepare images\nARG PYTHON_VERSION=\"3.13\"\nARG GO_VERSION=\"1.25\"\n\n\n# 1. Build go2rtc"
  },
  {
    "path": "docker/README.md",
    "chars": 1893,
    "preview": "# Docker\n\nImages are built automatically via [GitHub actions](https://github.com/AlexxIT/go2rtc/actions) and published o"
  },
  {
    "path": "docker/hardware.Dockerfile",
    "chars": 1903,
    "preview": "# syntax=docker/dockerfile:labs\n\n# 0. Prepare images\n# only debian 13 (trixie) has latest ffmpeg\n# https://packages.debi"
  },
  {
    "path": "docker/rockchip.Dockerfile",
    "chars": 1490,
    "preview": "# syntax=docker/dockerfile:labs\n\n# 0. Prepare images\nARG PYTHON_VERSION=\"3.13-slim-bookworm\"\nARG GO_VERSION=\"1.25-bookwo"
  },
  {
    "path": "examples/go2rtc_hass/main.go",
    "chars": 334,
    "preview": "package main\n\nimport (\n\t\"github.com/AlexxIT/go2rtc/internal/api\"\n\t\"github.com/AlexxIT/go2rtc/internal/app\"\n\t\"github.com/"
  },
  {
    "path": "examples/go2rtc_mjpeg/main.go",
    "chars": 508,
    "preview": "package main\n\nimport (\n\t\"github.com/AlexxIT/go2rtc/internal/api\"\n\t\"github.com/AlexxIT/go2rtc/internal/api/ws\"\n\t\"github.c"
  },
  {
    "path": "examples/go2rtc_rtsp/main.go",
    "chars": 279,
    "preview": "package main\n\nimport (\n\t\"github.com/AlexxIT/go2rtc/internal/app\"\n\t\"github.com/AlexxIT/go2rtc/internal/rtsp\"\n\t\"github.com"
  },
  {
    "path": "examples/homekit_info/main.go",
    "chars": 2717,
    "preview": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"os\"\n\n\t\"github.com/AlexxIT/go2rtc/pkg/hap\"\n)\n\nvar servs = map[string]string{\n\t\""
  },
  {
    "path": "examples/mdns/main.go",
    "chars": 658,
    "preview": "package main\n\nimport (\n\t\"log\"\n\t\"os\"\n\n\t\"github.com/AlexxIT/go2rtc/pkg/mdns\"\n)\n\nfunc main() {\n\tvar service = mdns.ServiceH"
  },
  {
    "path": "examples/mod_pinggy/go.mod",
    "chars": 171,
    "preview": "module pinggy\n\ngo 1.25\n\nrequire (\n\tgithub.com/Pinggy-io/pinggy-go/pinggy v0.6.9 // indirect\n\tgolang.org/x/crypto v0.8.0 "
  },
  {
    "path": "examples/mod_pinggy/go.sum",
    "chars": 3658,
    "preview": "github.com/Pinggy-io/pinggy-go/pinggy v0.6.9 h1:lzZ00JK6BUGQXnpkJZ+cVj8kIkXsmiVBUci9uEkSwEY=\ngithub.com/Pinggy-io/pinggy"
  },
  {
    "path": "examples/mod_pinggy/main.go",
    "chars": 812,
    "preview": "package main\n\nimport (\n\t\"log\"\n\t\"os\"\n\n\t\"github.com/Pinggy-io/pinggy-go/pinggy\"\n)\n\nfunc main() {\n\ttunType := os.Args[1]\n\ta"
  },
  {
    "path": "examples/onvif_client/README.md",
    "chars": 132,
    "preview": "## ONVIF Client\n\n```shell\ngo run examples/onvif_client/main.go http://admin:password@192.168.10.90 GetAudioEncoderConfig"
  },
  {
    "path": "examples/onvif_client/main.go",
    "chars": 1750,
    "preview": "package main\n\nimport (\n\t\"log\"\n\t\"net/url\"\n\t\"os\"\n\n\t\"github.com/AlexxIT/go2rtc/pkg/onvif\"\n)\n\nfunc main() {\n\tvar rawURL = os"
  },
  {
    "path": "examples/rtsp_client/main.go",
    "chars": 712,
    "preview": "package main\n\nimport (\n\t\"log\"\n\t\"os\"\n\n\t\"github.com/AlexxIT/go2rtc/pkg/core\"\n\t\"github.com/AlexxIT/go2rtc/pkg/rtsp\"\n\t\"githu"
  },
  {
    "path": "examples/tutk_decoder/README.md",
    "chars": 194,
    "preview": "# tutk_decoder\n\n1. Wireshark > Select any packet > Follow > UDP Stream\n2. Wireshark > File > Export Packet Dissections >"
  },
  {
    "path": "examples/tutk_decoder/main.go",
    "chars": 1574,
    "preview": "package main\n\nimport (\n\t\"encoding/hex\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"strings\"\n\n\t\"github.com/AlexxIT/go2rtc/pkg/"
  },
  {
    "path": "go.mod",
    "chars": 1787,
    "preview": "module github.com/AlexxIT/go2rtc\n\ngo 1.24.0\n\nrequire (\n\tgithub.com/asticode/go-astits v1.14.0\n\tgithub.com/eclipse/paho.m"
  },
  {
    "path": "go.sum",
    "chars": 12909,
    "preview": "github.com/asticode/go-astikit v0.30.0/go.mod h1:h4ly7idim1tNhaVkdVBeXQZEE3L0xblP7fCWbgwipF0=\ngithub.com/asticode/go-ast"
  },
  {
    "path": "internal/README.md",
    "chars": 6851,
    "preview": "# Modules\n\ngo2rtc tries to name formats, protocols and codecs the same way they are named in FFmpeg.\nSome formats and pr"
  },
  {
    "path": "internal/alsa/README.md",
    "chars": 409,
    "preview": "# ALSA\n\n[`new in v1.9.10`](https://github.com/AlexxIT/go2rtc/releases/tag/v1.9.10)\n\n> [!WARNING]\n> This source is under "
  },
  {
    "path": "internal/alsa/alsa.go",
    "chars": 113,
    "preview": "//go:build !(linux && (386 || amd64 || arm || arm64 || mipsle))\n\npackage alsa\n\nfunc Init() {\n\t// not supported\n}\n"
  },
  {
    "path": "internal/alsa/alsa_linux.go",
    "chars": 1539,
    "preview": "//go:build linux && (386 || amd64 || arm || arm64 || mipsle)\n\npackage alsa\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"os\"\n\t\"strconv\""
  },
  {
    "path": "internal/api/README.md",
    "chars": 2592,
    "preview": "# HTTP API\n\nThe HTTP API is the main part for interacting with the application. Default address: `http://localhost:1984/"
  },
  {
    "path": "internal/api/api.go",
    "chars": 7977,
    "preview": "package api\n\nimport (\n\t\"crypto/tls\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/http\"\n\t\"os\"\n\t\"slices\"\n\t\"strconv\"\n\t\"strings\"\n\t\"s"
  },
  {
    "path": "internal/api/config.go",
    "chars": 2149,
    "preview": "package api\n\nimport (\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\n\t\"github.com/AlexxIT/go2rtc/internal/app\"\n\t\"gopkg.in/yaml.v3\"\n)\n\nfunc con"
  },
  {
    "path": "internal/api/static.go",
    "chars": 501,
    "preview": "package api\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/AlexxIT/go2rtc/www\"\n)\n\nfunc initStatic(staticDir string) {\n\tvar root htt"
  },
  {
    "path": "internal/api/ws/README.md",
    "chars": 1130,
    "preview": "# WebSocket\n\nEndpoint: `/api/ws`\n\nQuery parameters:\n\n- `src` (required) - Stream name\n\n### WebRTC\n\nRequest SDP:\n\n```json"
  },
  {
    "path": "internal/api/ws/ws.go",
    "chars": 4160,
    "preview": "package ws\n\nimport (\n\t\"encoding/json\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/AlexxIT/go2r"
  },
  {
    "path": "internal/app/README.md",
    "chars": 3359,
    "preview": "# App\n\nThe application module is responsible for reading configuration files, running other modules and setting up [logs"
  },
  {
    "path": "internal/app/app.go",
    "chars": 2873,
    "preview": "package app\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"os\"\n\t\"os/exec\"\n\t\"runtime\"\n\t\"runtime/debug\"\n)\n\nvar (\n\tVersion    string\n\tModules  "
  },
  {
    "path": "internal/app/config.go",
    "chars": 2051,
    "preview": "package app\n\nimport (\n\t\"errors\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/AlexxIT/go2rtc/pkg/creds\"\n\t\"gith"
  },
  {
    "path": "internal/app/log.go",
    "chars": 4008,
    "preview": "package app\n\nimport (\n\t\"io\"\n\t\"os\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/AlexxIT/go2rtc/pkg/creds\"\n\t\"github.com/mattn/go-isatt"
  },
  {
    "path": "internal/app/storage.go",
    "chars": 939,
    "preview": "package app\n\nimport (\n\t\"sync\"\n\n\t\"github.com/AlexxIT/go2rtc/pkg/creds\"\n\t\"github.com/AlexxIT/go2rtc/pkg/yaml\"\n)\n\nfunc init"
  },
  {
    "path": "internal/bubble/README.md",
    "chars": 477,
    "preview": "# Bubble\n\n[`new in v1.6.1`](https://github.com/AlexxIT/go2rtc/releases/tag/v1.6.1)\n\nPrivate format in some cameras from "
  },
  {
    "path": "internal/bubble/bubble.go",
    "chars": 276,
    "preview": "package bubble\n\nimport (\n\t\"github.com/AlexxIT/go2rtc/internal/streams\"\n\t\"github.com/AlexxIT/go2rtc/pkg/bubble\"\n\t\"github."
  },
  {
    "path": "internal/debug/README.md",
    "chars": 91,
    "preview": "# Debug\n\nThis module provides `GET /api/stack`, with which you can find hanging goroutines\n"
  },
  {
    "path": "internal/debug/debug.go",
    "chars": 128,
    "preview": "package debug\n\nimport (\n\t\"github.com/AlexxIT/go2rtc/internal/api\"\n)\n\nfunc Init() {\n\tapi.HandleFunc(\"api/stack\", stackHan"
  },
  {
    "path": "internal/debug/stack.go",
    "chars": 1431,
    "preview": "package debug\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"runtime\"\n\n\t\"github.com/AlexxIT/go2rtc/internal/api\"\n)\n\nvar stackSk"
  },
  {
    "path": "internal/doorbird/README.md",
    "chars": 759,
    "preview": "# Doorbird\n\n[`new in v1.9.8`](https://github.com/AlexxIT/go2rtc/releases/tag/v1.9.8)\n\nThis source type supports [Doorbir"
  },
  {
    "path": "internal/doorbird/doorbird.go",
    "chars": 716,
    "preview": "package doorbird\n\nimport (\n\t\"net/url\"\n\n\t\"github.com/AlexxIT/go2rtc/internal/streams\"\n\t\"github.com/AlexxIT/go2rtc/pkg/cor"
  },
  {
    "path": "internal/dvrip/README.md",
    "chars": 787,
    "preview": "# DVR-IP\n\n[`new in v1.2.0`](https://github.com/AlexxIT/go2rtc/releases/tag/v1.2.0)\n\nPrivate format from DVR-IP NVR, NetS"
  },
  {
    "path": "internal/dvrip/dvrip.go",
    "chars": 3433,
    "preview": "package dvrip\n\nimport (\n\t\"encoding/hex\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/AlexxIT/go2rtc/"
  },
  {
    "path": "internal/echo/README.md",
    "chars": 1644,
    "preview": "# Echo\n\nSome sources may have a dynamic link. And you will need to get it using a Bash or Python script. Your script sho"
  },
  {
    "path": "internal/echo/echo.go",
    "chars": 873,
    "preview": "package echo\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"os/exec\"\n\t\"slices\"\n\n\t\"github.com/AlexxIT/go2rtc/internal/app\"\n\t\"github.com/A"
  },
  {
    "path": "internal/eseecloud/README.md",
    "chars": 354,
    "preview": "# EseeCloud\n\n[`new in v1.9.10`](https://github.com/AlexxIT/go2rtc/releases/tag/v1.9.10)\n\nThis source is for cameras with"
  },
  {
    "path": "internal/eseecloud/eseecloud.go",
    "chars": 185,
    "preview": "package eseecloud\n\nimport (\n\t\"github.com/AlexxIT/go2rtc/internal/streams\"\n\t\"github.com/AlexxIT/go2rtc/pkg/eseecloud\"\n)\n\n"
  },
  {
    "path": "internal/exec/README.md",
    "chars": 2344,
    "preview": "# Exec\n\nExec source can run any external application and expect data from it. Two transports are supported - **pipe** (["
  },
  {
    "path": "internal/exec/exec.go",
    "chars": 5772,
    "preview": "package exec\n\nimport (\n\t\"bufio\"\n\t\"crypto/md5\"\n\t\"encoding/hex\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/url\"\n\t\"os\"\n\t\"slices\"\n\t\"string"
  },
  {
    "path": "internal/expr/README.md",
    "chars": 5096,
    "preview": "# Expr\n\n[`new in v1.8.2`](https://github.com/AlexxIT/go2rtc/releases/tag/v1.8.2)\n\n[Expr](https://github.com/antonmedv/ex"
  },
  {
    "path": "internal/expr/expr.go",
    "chars": 536,
    "preview": "package expr\n\nimport (\n\t\"errors\"\n\n\t\"github.com/AlexxIT/go2rtc/internal/app\"\n\t\"github.com/AlexxIT/go2rtc/internal/streams"
  },
  {
    "path": "internal/ffmpeg/README.md",
    "chars": 3204,
    "preview": "# FFmpeg\n\nYou can get any stream, file or device via FFmpeg and push it to go2rtc. The app will automatically start FFmp"
  },
  {
    "path": "internal/ffmpeg/api.go",
    "chars": 1054,
    "preview": "package ffmpeg\n\nimport (\n\t\"net/http\"\n\t\"strings\"\n\n\t\"github.com/AlexxIT/go2rtc/internal/streams\"\n)\n\nfunc apiFFmpeg(w http."
  },
  {
    "path": "internal/ffmpeg/device/README.md",
    "chars": 796,
    "preview": "# FFmpeg Device\n\nYou can get video from any USB camera or Webcam as RTSP or WebRTC stream. This is part of FFmpeg integr"
  },
  {
    "path": "internal/ffmpeg/device/device_bsd.go",
    "chars": 2640,
    "preview": "//go:build freebsd || netbsd || openbsd || dragonfly\n\npackage device\n\nimport (\n\t\"net/url\"\n\t\"os\"\n\t\"os/exec\"\n\t\"regexp\"\n\t\"s"
  },
  {
    "path": "internal/ffmpeg/device/device_darwin.go",
    "chars": 1974,
    "preview": "//go:build darwin || ios\n\npackage device\n\nimport (\n\t\"net/url\"\n\t\"os/exec\"\n\t\"regexp\"\n\t\"strings\"\n\n\t\"github.com/AlexxIT/go2r"
  },
  {
    "path": "internal/ffmpeg/device/device_unix.go",
    "chars": 2563,
    "preview": "//go:build unix && !darwin && !freebsd && !netbsd && !openbsd && !dragonfly\n\npackage device\n\nimport (\n\t\"net/url\"\n\t\"os\"\n\t"
  },
  {
    "path": "internal/ffmpeg/device/device_windows.go",
    "chars": 1751,
    "preview": "//go:build windows\n\npackage device\n\nimport (\n\t\"net/url\"\n\t\"os/exec\"\n\t\"regexp\"\n\n\t\"github.com/AlexxIT/go2rtc/internal/api\"\n"
  },
  {
    "path": "internal/ffmpeg/device/devices.go",
    "chars": 734,
    "preview": "package device\n\nimport (\n\t\"net/http\"\n\t\"net/url\"\n\t\"strconv\"\n\t\"sync\"\n\n\t\"github.com/AlexxIT/go2rtc/internal/api\"\n)\n\nfunc In"
  },
  {
    "path": "internal/ffmpeg/ffmpeg.go",
    "chars": 12165,
    "preview": "package ffmpeg\n\nimport (\n\t\"net/url\"\n\t\"strings\"\n\n\t\"github.com/AlexxIT/go2rtc/internal/api\"\n\t\"github.com/AlexxIT/go2rtc/in"
  },
  {
    "path": "internal/ffmpeg/ffmpeg_test.go",
    "chars": 25744,
    "preview": "package ffmpeg\n\nimport (\n\t\"testing\"\n\n\t\"github.com/AlexxIT/go2rtc/pkg/ffmpeg\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nf"
  },
  {
    "path": "internal/ffmpeg/hardware/README.md",
    "chars": 4650,
    "preview": "# Hardware\n\nYou **DON'T** need hardware acceleration if:\n\n- you're not using the [FFmpeg source](../README.md)\n- you're "
  },
  {
    "path": "internal/ffmpeg/hardware/hardware.go",
    "chars": 6036,
    "preview": "package hardware\n\nimport (\n\t\"net/http\"\n\t\"os/exec\"\n\t\"strings\"\n\n\t\"github.com/AlexxIT/go2rtc/internal/api\"\n\t\"github.com/Ale"
  },
  {
    "path": "internal/ffmpeg/hardware/hardware_bsd.go",
    "chars": 1437,
    "preview": "//go:build freebsd || netbsd || openbsd || dragonfly\n\npackage hardware\n\nimport (\n\t\"runtime\"\n\n\t\"github.com/AlexxIT/go2rtc"
  },
  {
    "path": "internal/ffmpeg/hardware/hardware_darwin.go",
    "chars": 879,
    "preview": "//go:build darwin || ios\n\npackage hardware\n\nimport (\n\t\"github.com/AlexxIT/go2rtc/internal/api\"\n)\n\nconst ProbeVideoToolbo"
  },
  {
    "path": "internal/ffmpeg/hardware/hardware_unix.go",
    "chars": 3257,
    "preview": "//go:build unix && !darwin && !freebsd && !netbsd && !openbsd && !dragonfly\n\npackage hardware\n\nimport (\n\t\"runtime\"\n\n\t\"gi"
  },
  {
    "path": "internal/ffmpeg/hardware/hardware_windows.go",
    "chars": 1603,
    "preview": "//go:build windows\n\npackage hardware\n\nimport \"github.com/AlexxIT/go2rtc/internal/api\"\n\nconst ProbeDXVA2H264 = \"-init_hw_"
  },
  {
    "path": "internal/ffmpeg/jpeg.go",
    "chars": 1723,
    "preview": "package ffmpeg\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"net/url\"\n\t\"os/exec\"\n\n\t\"github.com/AlexxIT/go2rtc/internal/ffmpeg/hardware\"\n\t\""
  },
  {
    "path": "internal/ffmpeg/jpeg_test.go",
    "chars": 718,
    "preview": "package ffmpeg\n\nimport (\n\t\"net/url\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestParseQuery(t *testing"
  },
  {
    "path": "internal/ffmpeg/producer.go",
    "chars": 3079,
    "preview": "package ffmpeg\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"net/url\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/AlexxIT/go2rtc/intern"
  },
  {
    "path": "internal/ffmpeg/version.go",
    "chars": 794,
    "preview": "package ffmpeg\n\nimport (\n\t\"errors\"\n\t\"os/exec\"\n\t\"sync\"\n\n\t\"github.com/AlexxIT/go2rtc/pkg/ffmpeg\"\n)\n\nvar verMu sync.Mutex\nv"
  },
  {
    "path": "internal/ffmpeg/virtual/virtual.go",
    "chars": 1581,
    "preview": "package virtual\n\nimport (\n\t\"net/url\"\n)\n\nfunc GetInput(src string) string {\n\tquery, err := url.ParseQuery(src)\n\tif err !="
  },
  {
    "path": "internal/ffmpeg/virtual/virtual_test.go",
    "chars": 483,
    "preview": "package virtual\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestGetInput(t *testing.T) {\n\ts := "
  },
  {
    "path": "internal/flussonic/README.md",
    "chars": 218,
    "preview": "# Flussonic\n\n[`new in v1.9.10`](https://github.com/AlexxIT/go2rtc/releases/tag/v1.9.10)\n\nSupport streams from [Flussonic"
  },
  {
    "path": "internal/flussonic/flussonic.go",
    "chars": 185,
    "preview": "package flussonic\n\nimport (\n\t\"github.com/AlexxIT/go2rtc/internal/streams\"\n\t\"github.com/AlexxIT/go2rtc/pkg/flussonic\"\n)\n\n"
  },
  {
    "path": "internal/gopro/README.md",
    "chars": 932,
    "preview": "# GoPro\n\n[`new in v1.8.3`](https://github.com/AlexxIT/go2rtc/releases/tag/v1.8.3)\n\nSupport streaming from [GoPro](https:"
  },
  {
    "path": "internal/gopro/gopro.go",
    "chars": 602,
    "preview": "package gopro\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/AlexxIT/go2rtc/internal/api\"\n\t\"github.com/AlexxIT/go2rtc/internal/stre"
  },
  {
    "path": "internal/hass/README.md",
    "chars": 1799,
    "preview": "# Hass\n\nSupport import camera links from [Home Assistant](https://www.home-assistant.io/) config files:\n\n- [Generic Came"
  },
  {
    "path": "internal/hass/api.go",
    "chars": 2268,
    "preview": "package hass\n\nimport (\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"net\"\n\t\"net/http\"\n\t\"strings\"\n\n\t\"github.com/AlexxIT/go2rtc/in"
  },
  {
    "path": "internal/hass/hass.go",
    "chars": 5580,
    "preview": "package hass\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"os\"\n\t\"path\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/Alexx"
  },
  {
    "path": "internal/hls/README.md",
    "chars": 741,
    "preview": "# HLS\n\n[`new in v1.1.0`](https://github.com/AlexxIT/go2rtc/releases/tag/v1.1.0)\n\n[HLS](https://en.wikipedia.org/wiki/HTT"
  },
  {
    "path": "internal/hls/hls.go",
    "chars": 4755,
    "preview": "package hls\n\nimport (\n\t\"net/http\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/AlexxIT/go2rtc/internal/api\"\n\t\"github.com/AlexxIT/go2rtc"
  },
  {
    "path": "internal/hls/session.go",
    "chars": 2390,
    "preview": "package hls\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/AlexxIT/go2rtc/pkg/core\"\n\t\"github.com/AlexxI"
  },
  {
    "path": "internal/hls/ws.go",
    "chars": 1102,
    "preview": "package hls\n\nimport (\n\t\"errors\"\n\t\"time\"\n\n\t\"github.com/AlexxIT/go2rtc/internal/api\"\n\t\"github.com/AlexxIT/go2rtc/internal/"
  },
  {
    "path": "internal/homekit/README.md",
    "chars": 3909,
    "preview": "# Apple HomeKit\n\nThis module supports both client and server for the [Apple HomeKit](https://www.apple.com/home-app/acce"
  },
  {
    "path": "internal/homekit/api.go",
    "chars": 3911,
    "preview": "package homekit\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n\n\t\"github.com/AlexxIT/go2rtc/internal"
  },
  {
    "path": "internal/homekit/homekit.go",
    "chars": 4850,
    "preview": "package homekit\n\nimport (\n\t\"errors\"\n\t\"net/http\"\n\t\"strings\"\n\n\t\"github.com/AlexxIT/go2rtc/internal/api\"\n\t\"github.com/Alexx"
  },
  {
    "path": "internal/homekit/server.go",
    "chars": 9551,
    "preview": "package homekit\n\nimport (\n\t\"crypto/ed25519\"\n\t\"crypto/sha512\"\n\t\"encoding/hex\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"n"
  },
  {
    "path": "internal/http/README.md",
    "chars": 2178,
    "preview": "# HTTP\n\nThis source supports receiving a stream via an HTTP link.\n\nIt can determine the source format from the`Content-T"
  },
  {
    "path": "internal/http/http.go",
    "chars": 2952,
    "preview": "package http\n\nimport (\n\t\"errors\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n\n\t\"github.com/AlexxIT/go2rtc/internal/api\"\n\t\"g"
  },
  {
    "path": "internal/isapi/README.md",
    "chars": 469,
    "preview": "# Hikvision ISAPI\n\n[`new in v1.3.0`](https://github.com/AlexxIT/go2rtc/releases/tag/v1.3.0)\n\nThis source type supports o"
  },
  {
    "path": "internal/isapi/init.go",
    "chars": 272,
    "preview": "package isapi\n\nimport (\n\t\"github.com/AlexxIT/go2rtc/internal/streams\"\n\t\"github.com/AlexxIT/go2rtc/pkg/core\"\n\t\"github.com"
  },
  {
    "path": "internal/ivideon/README.md",
    "chars": 176,
    "preview": "# Ivideon\n\nSupport public cameras from the service [Ivideon](https://tv.ivideon.com/).\n\n## Configuration\n\n```yaml\nstream"
  },
  {
    "path": "internal/ivideon/ivideon.go",
    "chars": 177,
    "preview": "package ivideon\n\nimport (\n\t\"github.com/AlexxIT/go2rtc/internal/streams\"\n\t\"github.com/AlexxIT/go2rtc/pkg/ivideon\"\n)\n\nfunc"
  },
  {
    "path": "internal/kasa/README.md",
    "chars": 514,
    "preview": "# TP-Link Kasa\n\n[`new in v1.7.0`](https://github.com/AlexxIT/go2rtc/releases/tag/v1.7.0)\n\n[TP-Link Kasa](https://www.kas"
  },
  {
    "path": "internal/kasa/kasa.go",
    "chars": 268,
    "preview": "package kasa\n\nimport (\n\t\"github.com/AlexxIT/go2rtc/internal/streams\"\n\t\"github.com/AlexxIT/go2rtc/pkg/core\"\n\t\"github.com/"
  },
  {
    "path": "internal/mjpeg/README.md",
    "chars": 4345,
    "preview": "# Motion JPEG\n\n- This module can provide and receive streams in MJPEG format.\n- This module is also responsible for rece"
  },
  {
    "path": "internal/mjpeg/mjpeg.go",
    "chars": 5155,
    "preview": "package mjpeg\n\nimport (\n\t\"errors\"\n\t\"io\"\n\t\"net/http\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/AlexxIT/go2rtc/i"
  },
  {
    "path": "internal/mp4/README.md",
    "chars": 2638,
    "preview": "# MP4\n\nThis module provides several features:\n\n1. MSE stream (fMP4 over WebSocket)\n2. Camera snapshots in MP4 format (si"
  },
  {
    "path": "internal/mp4/mp4.go",
    "chars": 3589,
    "preview": "package mp4\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/AlexxIT/go2rtc/internal/api\"\n\t\""
  },
  {
    "path": "internal/mp4/ws.go",
    "chars": 1712,
    "preview": "package mp4\n\nimport (\n\t\"errors\"\n\n\t\"github.com/AlexxIT/go2rtc/internal/api\"\n\t\"github.com/AlexxIT/go2rtc/internal/api/ws\"\n"
  },
  {
    "path": "internal/mpeg/README.md",
    "chars": 489,
    "preview": "# MPEG\n\nThis module provides an [HTTP API](../api/README.md) for:\n \n- Streaming output in `mpegts` format.\n- Streaming o"
  },
  {
    "path": "internal/mpeg/mpeg.go",
    "chars": 1947,
    "preview": "package mpeg\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/AlexxIT/go2rtc/internal/api\"\n\t\"github.com/AlexxIT/go2rtc/internal/strea"
  },
  {
    "path": "internal/multitrans/README.md",
    "chars": 629,
    "preview": "# TP-Link MULTITRANS\n\n[`new in v1.9.14`](https://github.com/AlexxIT/go2rtc/releases/tag/v1.9.14) by [@forrestsocool](htt"
  },
  {
    "path": "internal/multitrans/multitrans.go",
    "chars": 189,
    "preview": "package multitrans\n\nimport (\n\t\"github.com/AlexxIT/go2rtc/internal/streams\"\n\t\"github.com/AlexxIT/go2rtc/pkg/multitrans\"\n)"
  },
  {
    "path": "internal/nest/README.md",
    "chars": 428,
    "preview": "# Google Nest\n\n[`new in v1.6.0`](https://github.com/AlexxIT/go2rtc/releases/tag/v1.6.0)\n\nFor simplicity, it is recommend"
  },
  {
    "path": "internal/nest/init.go",
    "chars": 1209,
    "preview": "package nest\n\nimport (\n\t\"net/http\"\n\t\"strings\"\n\n\t\"github.com/AlexxIT/go2rtc/internal/api\"\n\t\"github.com/AlexxIT/go2rtc/int"
  },
  {
    "path": "internal/ngrok/README.md",
    "chars": 2089,
    "preview": "# ngrok\n\nWith the ngrok integration, you can get external access to your streams when your Internet connection is behind"
  },
  {
    "path": "internal/ngrok/ngrok.go",
    "chars": 1670,
    "preview": "package ngrok\n\nimport (\n\t\"fmt\"\n\t\"net\"\n\t\"strings\"\n\n\t\"github.com/AlexxIT/go2rtc/internal/app\"\n\t\"github.com/AlexxIT/go2rtc/"
  },
  {
    "path": "internal/onvif/README.md",
    "chars": 1121,
    "preview": "# ONVIF\n\n## ONVIF Client\n\n[`new in v1.5.0`](https://github.com/AlexxIT/go2rtc/releases/tag/v1.5.0)\n\nThe source is not ve"
  },
  {
    "path": "internal/onvif/onvif.go",
    "chars": 6062,
    "preview": "package onvif\n\nimport (\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/AlexxIT/go"
  },
  {
    "path": "internal/pinggy/README.md",
    "chars": 1516,
    "preview": "# Pinggy\n\n[Pinggy](https://pinggy.io/) - nice service for public tunnels to your local services.\n\n**Features:**\n\n- A fre"
  },
  {
    "path": "internal/pinggy/pinggy.go",
    "chars": 917,
    "preview": "package pinggy\n\nimport (\n\t\"net/url\"\n\n\t\"github.com/AlexxIT/go2rtc/internal/app\"\n\t\"github.com/AlexxIT/go2rtc/pkg/pinggy\"\n\t"
  },
  {
    "path": "internal/ring/README.md",
    "chars": 616,
    "preview": "# Ring\n\n[`new in v1.9.13`](https://github.com/AlexxIT/go2rtc/releases/tag/v1.9.13) by [@seydx](https://github.com/seydx)"
  },
  {
    "path": "internal/ring/ring.go",
    "chars": 2515,
    "preview": "package ring\n\nimport (\n\t\"net/http\"\n\t\"net/url\"\n\n\t\"fmt\"\n\n\t\"github.com/AlexxIT/go2rtc/internal/api\"\n\t\"github.com/AlexxIT/go"
  },
  {
    "path": "internal/roborock/README.md",
    "chars": 931,
    "preview": "# Roborock\n\n[`new in v1.3.0`](https://github.com/AlexxIT/go2rtc/releases/tag/v1.3.0)\n\nThis source type supports Roborock"
  },
  {
    "path": "internal/roborock/roborock.go",
    "chars": 2136,
    "preview": "package roborock\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\n\t\"github.com/AlexxIT/go2rtc/internal/api\"\n\t\"github.com/AlexxIT/go2rtc/int"
  },
  {
    "path": "internal/rtmp/README.md",
    "chars": 2625,
    "preview": "# Real-Time Messaging Protocol\n\nThis module provides the following features for the RTMP protocol:\n\n- Streaming input - "
  },
  {
    "path": "internal/rtmp/rtmp.go",
    "chars": 3803,
    "preview": "package rtmp\n\nimport (\n\t\"errors\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\n\t\"github.com/AlexxIT/go2rtc/internal/api\"\n\t\"github.com/AlexxI"
  },
  {
    "path": "internal/rtsp/README.md",
    "chars": 5042,
    "preview": "# Real Time Streaming Protocol\n\nThis module provides the following features for the RTSP protocol:\n\n - Streaming input -"
  },
  {
    "path": "internal/rtsp/rtsp.go",
    "chars": 6955,
    "preview": "package rtsp\n\nimport (\n\t\"errors\"\n\t\"io\"\n\t\"net\"\n\t\"net/url\"\n\t\"strings\"\n\n\t\"github.com/AlexxIT/go2rtc/internal/app\"\n\t\"github."
  },
  {
    "path": "internal/srtp/README.md",
    "chars": 253,
    "preview": "# SRTP\n\nThis is a support module for the [HomeKit](../homekit/README.md) module.\n\n> [!NOTE]\n> This module can be removed"
  },
  {
    "path": "internal/srtp/srtp.go",
    "chars": 486,
    "preview": "package srtp\n\nimport (\n\t\"github.com/AlexxIT/go2rtc/internal/app\"\n\t\"github.com/AlexxIT/go2rtc/pkg/srtp\"\n)\n\nfunc Init() {\n"
  },
  {
    "path": "internal/streams/README.md",
    "chars": 6818,
    "preview": "# Streams\n\nThis core module is responsible for managing the stream list.\n\n## Stream to camera\n\n[`new in v1.3.0`](https:/"
  },
  {
    "path": "internal/streams/add_consumer.go",
    "chars": 4260,
    "preview": "package streams\n\nimport (\n\t\"errors\"\n\t\"strings\"\n\n\t\"github.com/AlexxIT/go2rtc/pkg/core\"\n)\n\nfunc (s *Stream) AddConsumer(co"
  },
  {
    "path": "internal/streams/api.go",
    "chars": 4336,
    "preview": "package streams\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/AlexxIT/go2rtc/internal/api\"\n\t\"github.com/AlexxIT/go2rtc/internal/ap"
  },
  {
    "path": "internal/streams/api_test.go",
    "chars": 1845,
    "preview": "package streams\n\nimport (\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\t\"github.com/AlexxIT/go2rtc/pkg/"
  },
  {
    "path": "internal/streams/dot.go",
    "chars": 4078,
    "preview": "package streams\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"strings\"\n)\n\nfunc AppendDOT(dot []byte, stream *Stream) []byte {\n\tfor"
  },
  {
    "path": "internal/streams/handlers.go",
    "chars": 2824,
    "preview": "package streams\n\nimport (\n\t\"errors\"\n\t\"regexp\"\n\t\"strings\"\n\n\t\"github.com/AlexxIT/go2rtc/pkg/core\"\n)\n\ntype Handler func(sou"
  },
  {
    "path": "internal/streams/helpers.go",
    "chars": 362,
    "preview": "package streams\n\nimport (\n\t\"net/url\"\n\t\"strings\"\n)\n\nfunc ParseQuery(s string) url.Values {\n\tif len(s) == 0 {\n\t\treturn nil"
  },
  {
    "path": "internal/streams/play.go",
    "chars": 3018,
    "preview": "package streams\n\nimport (\n\t\"errors\"\n\t\"time\"\n\n\t\"github.com/AlexxIT/go2rtc/pkg/core\"\n)\n\nfunc (s *Stream) Play(urlOrProd an"
  },
  {
    "path": "internal/streams/preload.go",
    "chars": 1317,
    "preview": "package streams\n\nimport (\n\t\"fmt\"\n\t\"maps\"\n\t\"net/url\"\n\t\"sync\"\n\n\t\"github.com/AlexxIT/go2rtc/pkg/probe\"\n)\n\ntype Preload stru"
  },
  {
    "path": "internal/streams/producer.go",
    "chars": 4703,
    "preview": "package streams\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/AlexxIT/go2rtc/pkg/core\"\n)"
  },
  {
    "path": "internal/streams/publish.go",
    "chars": 607,
    "preview": "package streams\n\nimport \"time\"\n\nfunc (s *Stream) Publish(url string) error {\n\tcons, run, err := GetConsumer(url)\n\tif err"
  },
  {
    "path": "internal/streams/stream.go",
    "chars": 2608,
    "preview": "package streams\n\nimport (\n\t\"encoding/json\"\n\t\"sync\"\n\t\"sync/atomic\"\n\n\t\"github.com/AlexxIT/go2rtc/pkg/core\"\n)\n\ntype Stream "
  },
  {
    "path": "internal/streams/stream_test.go",
    "chars": 1196,
    "preview": "package streams\n\nimport (\n\t\"net/url\"\n\t\"testing\"\n\n\t\"github.com/AlexxIT/go2rtc/pkg/core\"\n\t\"github.com/stretchr/testify/req"
  },
  {
    "path": "internal/streams/streams.go",
    "chars": 3683,
    "preview": "package streams\n\nimport (\n\t\"errors\"\n\t\"net/url\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/AlexxIT/go2rtc/internal/api\"\n\t\"github.com/A"
  },
  {
    "path": "internal/tapo/README.md",
    "chars": 2226,
    "preview": "# TP-Link Tapo\n\n[`new in v1.2.0`](https://github.com/AlexxIT/go2rtc/releases/tag/v1.2.0)\n\n[TP-Link Tapo](https://www.tap"
  },
  {
    "path": "internal/tapo/tapo.go",
    "chars": 516,
    "preview": "package tapo\n\nimport (\n\t\"github.com/AlexxIT/go2rtc/internal/streams\"\n\t\"github.com/AlexxIT/go2rtc/pkg/core\"\n\t\"github.com/"
  },
  {
    "path": "internal/tuya/README.md",
    "chars": 2112,
    "preview": "# Tuya\n\n[`new in v1.9.13`](https://github.com/AlexxIT/go2rtc/releases/tag/v1.9.13) by [@seydx](https://github.com/seydx)"
  },
  {
    "path": "internal/tuya/tuya.go",
    "chars": 6147,
    "preview": "package tuya\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strconv\"\n\n\t\"github.com/AlexxI"
  },
  {
    "path": "internal/v4l2/README.md",
    "chars": 1822,
    "preview": "# Video4Linux\n\n[`new in v1.9.9`](https://github.com/AlexxIT/go2rtc/releases/tag/v1.9.9)\n\nWhat you should to know about ["
  },
  {
    "path": "internal/v4l2/v4l2.go",
    "chars": 113,
    "preview": "//go:build !(linux && (386 || arm || mipsle || amd64 || arm64))\n\npackage v4l2\n\nfunc Init() {\n\t// not supported\n}\n"
  },
  {
    "path": "internal/v4l2/v4l2_linux.go",
    "chars": 1917,
    "preview": "//go:build linux && (386 || arm || mipsle || amd64 || arm64)\n\npackage v4l2\n\nimport (\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"net/htt"
  },
  {
    "path": "internal/webrtc/README.md",
    "chars": 11161,
    "preview": "# WebRTC\n\n## WebRTC Client\n\n[`new in v1.3.0`](https://github.com/AlexxIT/go2rtc/releases/tag/v1.3.0)\n\nThis source type s"
  },
  {
    "path": "internal/webrtc/candidates.go",
    "chars": 3610,
    "preview": "package webrtc\n\nimport (\n\t\"net\"\n\t\"strings\"\n\n\t\"github.com/AlexxIT/go2rtc/internal/api/ws\"\n\t\"github.com/AlexxIT/go2rtc/pkg"
  },
  {
    "path": "internal/webrtc/client.go",
    "chars": 6091,
    "preview": "package webrtc\n\nimport (\n\t\"encoding/base64\"\n\t\"errors\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github."
  },
  {
    "path": "internal/webrtc/client_creality.go",
    "chars": 3057,
    "preview": "package webrtc\n\nimport (\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/AlexxIT/"
  },
  {
    "path": "internal/webrtc/kinesis.go",
    "chars": 5076,
    "preview": "package webrtc\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"time\"\n\n\t\"github.com/AlexxIT/go"
  },
  {
    "path": "internal/webrtc/milestone.go",
    "chars": 5158,
    "preview": "package webrtc\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/"
  },
  {
    "path": "internal/webrtc/openipc.go",
    "chars": 3529,
    "preview": "package webrtc\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"io\"\n\t\"net/url\"\n\n\t\"github.com/AlexxIT/go2rtc/pkg/core\"\n\t\"github.com"
  },
  {
    "path": "internal/webrtc/server.go",
    "chars": 5638,
    "preview": "package webrtc\n\nimport (\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"io\"\n\t\"net/http\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.c"
  },
  {
    "path": "internal/webrtc/switchbot.go",
    "chars": 930,
    "preview": "package webrtc\n\nimport (\n\t\"net/url\"\n\n\t\"github.com/AlexxIT/go2rtc/pkg/core\"\n\t\"github.com/AlexxIT/go2rtc/pkg/webrtc\"\n)\n\nfu"
  },
  {
    "path": "internal/webrtc/webrtc.go",
    "chars": 7775,
    "preview": "package webrtc\n\nimport (\n\t\"errors\"\n\t\"net\"\n\t\"strings\"\n\n\t\"github.com/AlexxIT/go2rtc/internal/api\"\n\t\"github.com/AlexxIT/go2"
  },
  {
    "path": "internal/webrtc/webrtc_test.go",
    "chars": 2142,
    "preview": "package webrtc\n\nimport (\n\t\"encoding/json\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/AlexxIT/go2rtc/internal/api/ws\"\n\tpion \"git"
  },
  {
    "path": "internal/webtorrent/README.md",
    "chars": 1571,
    "preview": "# WebTorrent\n\n> [!NOTE]\n> This section needs some improvement.\n\n## WebTorrent Client\n\n[`new in v1.3.0`](https://github.c"
  },
  {
    "path": "internal/webtorrent/init.go",
    "chars": 3914,
    "preview": "package webtorrent\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/url\"\n\n\t\"github.com/AlexxIT/go2rtc/internal/api\"\n\t\"github"
  },
  {
    "path": "internal/webtorrent/tracker.go",
    "chars": 2461,
    "preview": "package webtorrent\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\n\t\"github.com/AlexxIT/go2rtc/pkg/webtorrent\"\n\t\"github.com/gorilla/websoc"
  },
  {
    "path": "internal/wyoming/README.md",
    "chars": 9649,
    "preview": "# Wyoming\n\n> [!NOTE]\n> The format is under development and does not yet work stably.\n\nThis module provide [Wyoming Proto"
  },
  {
    "path": "internal/wyoming/wyoming.go",
    "chars": 2334,
    "preview": "package wyoming\n\nimport (\n\t\"net\"\n\n\t\"github.com/AlexxIT/go2rtc/internal/app\"\n\t\"github.com/AlexxIT/go2rtc/internal/streams"
  },
  {
    "path": "internal/wyze/README.md",
    "chars": 5603,
    "preview": "# Wyze\n\n[`new in v1.9.14`](https://github.com/AlexxIT/go2rtc/releases/tag/v1.9.14) by [@seydx](https://github.com/seydx)"
  },
  {
    "path": "internal/wyze/wyze.go",
    "chars": 4366,
    "preview": "package wyze\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/url\"\n\n\t\"github.com/AlexxIT/go2rtc/internal/api\"\n\t\"githu"
  },
  {
    "path": "internal/xiaomi/README.md",
    "chars": 2316,
    "preview": "# Xiaomi Mi Home\n\n[`new in v1.9.13`](https://github.com/AlexxIT/go2rtc/releases/tag/v1.9.13)\n\nThis source allows you to "
  },
  {
    "path": "internal/xiaomi/xiaomi.go",
    "chars": 8065,
    "preview": "package xiaomi\n\nimport (\n\t\"encoding/hex\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n\t\"sync\"\n\n\t\""
  },
  {
    "path": "internal/yandex/README.md",
    "chars": 724,
    "preview": "# Yandex\n\nSource for receiving stream from new [Yandex IP camera](https://alice.yandex.ru/smart-home/security/ipcamera)."
  },
  {
    "path": "internal/yandex/goloom.go",
    "chars": 3955,
    "preview": "package yandex\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/AlexxIT/go2rtc/internal/webrtc\"\n\t\"githu"
  },
  {
    "path": "internal/yandex/yandex.go",
    "chars": 982,
    "preview": "package yandex\n\nimport (\n\t\"net/url\"\n\n\t\"github.com/AlexxIT/go2rtc/internal/streams\"\n\t\"github.com/AlexxIT/go2rtc/pkg/core\""
  },
  {
    "path": "main.go",
    "chars": 4156,
    "preview": "package main\n\nimport (\n\t\"slices\"\n\n\t\"github.com/AlexxIT/go2rtc/internal/alsa\"\n\t\"github.com/AlexxIT/go2rtc/internal/api\"\n\t"
  },
  {
    "path": "package.json",
    "chars": 907,
    "preview": "{\n  \"devDependencies\": {\n    \"@types/node\": \"^25.2.0\",\n    \"eslint\": \"^9.39.2\",\n    \"eslint-plugin-html\": \"^8.1.4\",\n    "
  },
  {
    "path": "pkg/README.md",
    "chars": 9822,
    "preview": "# Notes\n\ngo2rtc tries to name formats, protocols and codecs the same way they are named in FFmpeg.\nSome formats and prot"
  },
  {
    "path": "pkg/aac/README.md",
    "chars": 869,
    "preview": "## AAC-LD and AAC-ELD\n\n| Codec   | Rate  | QuickTime | ffmpeg | VLC |\n|---------|-------|-----------|--------|-----|\n| A"
  },
  {
    "path": "pkg/aac/aac.go",
    "chars": 2942,
    "preview": "package aac\n\nimport (\n\t\"encoding/hex\"\n\t\"fmt\"\n\n\t\"github.com/AlexxIT/go2rtc/pkg/bits\"\n\t\"github.com/AlexxIT/go2rtc/pkg/core"
  },
  {
    "path": "pkg/aac/aac_test.go",
    "chars": 1440,
    "preview": "package aac\n\nimport (\n\t\"encoding/hex\"\n\t\"testing\"\n\n\t\"github.com/AlexxIT/go2rtc/pkg/core\"\n\t\"github.com/stretchr/testify/re"
  },
  {
    "path": "pkg/aac/adts.go",
    "chars": 5044,
    "preview": "package aac\n\nimport (\n\t\"encoding/hex\"\n\n\t\"github.com/AlexxIT/go2rtc/pkg/bits\"\n\t\"github.com/AlexxIT/go2rtc/pkg/core\"\n\t\"git"
  },
  {
    "path": "pkg/aac/consumer.go",
    "chars": 1135,
    "preview": "package aac\n\nimport (\n\t\"io\"\n\n\t\"github.com/AlexxIT/go2rtc/pkg/core\"\n\t\"github.com/pion/rtp\"\n)\n\ntype Consumer struct {\n\tcor"
  },
  {
    "path": "pkg/aac/producer.go",
    "chars": 1473,
    "preview": "package aac\n\nimport (\n\t\"bufio\"\n\t\"errors\"\n\t\"io\"\n\n\t\"github.com/AlexxIT/go2rtc/pkg/core\"\n\t\"github.com/pion/rtp\"\n)\n\ntype Pro"
  },
  {
    "path": "pkg/aac/rtp.go",
    "chars": 3743,
    "preview": "package aac\n\nimport (\n\t\"encoding/binary\"\n\n\t\"github.com/AlexxIT/go2rtc/pkg/core\"\n\t\"github.com/pion/rtp\"\n)\n\nconst RTPPacke"
  },
  {
    "path": "pkg/aac/rtp_test.go",
    "chars": 1710,
    "preview": "package aac\n\nimport (\n\t\"encoding/hex\"\n\t\"testing\"\n\n\t\"github.com/AlexxIT/go2rtc/pkg/core\"\n\t\"github.com/pion/rtp\"\n\t\"github."
  },
  {
    "path": "pkg/alsa/README.md",
    "chars": 815,
    "preview": "## Build\n\n```shell\nx86_64-linux-gnu-gcc -w -static asound_arch.c -o asound_amd64\ni686-linux-gnu-gcc -w -static asound_ar"
  },
  {
    "path": "pkg/alsa/capture_linux.go",
    "chars": 1790,
    "preview": "package alsa\n\nimport (\n\t\"github.com/AlexxIT/go2rtc/pkg/alsa/device\"\n\t\"github.com/AlexxIT/go2rtc/pkg/core\"\n\t\"github.com/A"
  },
  {
    "path": "pkg/alsa/device/asound_32bit.go",
    "chars": 6048,
    "preview": "//go:build 386 || arm\n\npackage device\n\ntype unsigned_char = byte\ntype signed_int = int32\ntype unsigned_int = uint32\ntype"
  },
  {
    "path": "pkg/alsa/device/asound_64bit.go",
    "chars": 6053,
    "preview": "//go:build amd64 || arm64\n\npackage device\n\ntype unsigned_char = byte\ntype signed_int = int32\ntype unsigned_int = uint32\n"
  },
  {
    "path": "pkg/alsa/device/asound_arch.c",
    "chars": 7616,
    "preview": "//go:build ignore\n#include <stdio.h>\n#include <stddef.h>\n#include <sys/ioctl.h>\n#include <sound/asound.h>\n\n#define print"
  },
  {
    "path": "pkg/alsa/device/asound_mipsle.go",
    "chars": 6025,
    "preview": "package device\n\ntype unsigned_char = byte\ntype signed_int = int32\ntype unsigned_int = uint32\ntype signed_long = int64\nty"
  },
  {
    "path": "pkg/alsa/device/device_linux.go",
    "chars": 5885,
    "preview": "package device\n\nimport (\n\t\"fmt\"\n\t\"syscall\"\n\t\"unsafe\"\n)\n\ntype Device struct {\n\tfd   uintptr\n\tpath string\n\n\thwparams   snd"
  },
  {
    "path": "pkg/alsa/device/ioctl_linux.go",
    "chars": 404,
    "preview": "package device\n\nimport (\n\t\"bytes\"\n\t\"reflect\"\n\t\"syscall\"\n)\n\nfunc ioctl(fd, req uintptr, arg any) error {\n\tvar ptr uintptr"
  },
  {
    "path": "pkg/alsa/open_linux.go",
    "chars": 893,
    "preview": "package alsa\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"net/url\"\n\n\t\"github.com/AlexxIT/go2rtc/pkg/alsa/device\"\n\t\"github.com/AlexxIT/go"
  },
  {
    "path": "pkg/alsa/playback_linux.go",
    "chars": 1974,
    "preview": "package alsa\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/AlexxIT/go2rtc/pkg/alsa/device\"\n\t\"github.com/AlexxIT/go2rtc/pkg/core\"\n\t\"gith"
  },
  {
    "path": "pkg/ascii/README.md",
    "chars": 200,
    "preview": "## Useful links\n\n- https://en.wikipedia.org/wiki/ANSI_escape_code\n- https://paulbourke.net/dataformats/asciiart/\n- https"
  },
  {
    "path": "pkg/ascii/ascii.go",
    "chars": 6718,
    "preview": "package ascii\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"image/jpeg\"\n\t\"io\"\n\t\"net/http\"\n\t\"unicode/utf8\"\n)\n\nfunc NewWriter(w io.Writer, f"
  },
  {
    "path": "pkg/bits/reader.go",
    "chars": 2624,
    "preview": "package bits\n\ntype Reader struct {\n\tEOF bool // if end of buffer raised during reading\n\n\tbuf  []byte // total buf\n\tbyte "
  },
  {
    "path": "pkg/bits/writer.go",
    "chars": 1524,
    "preview": "package bits\n\ntype Writer struct {\n\tbuf  []byte // total buf\n\tbyte *byte  // pointer to current byte\n\tbits byte   // bit"
  },
  {
    "path": "pkg/bubble/client.go",
    "chars": 5700,
    "preview": "// Package bubble, because:\n// Request URL: /bubble/live?ch=0&stream=0\n// Response Conten-Type: video/bubble\n// https://"
  },
  {
    "path": "pkg/bubble/producer.go",
    "chars": 1529,
    "preview": "package bubble\n\nimport (\n\t\"encoding/json\"\n\n\t\"github.com/AlexxIT/go2rtc/pkg/core\"\n)\n\nfunc (c *Client) GetMedias() []*core"
  }
]

// ... and 330 more files (download for full content)

About this extraction

This page contains the full source code of the AlexxIT/go2rtc GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 530 files (1.7 MB), approximately 571.1k tokens, and a symbol index with 3570 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!