go2rtc icon indicating copy to clipboard operation
go2rtc copied to clipboard

Add audio mixer for backchannel

Open seydx opened this issue 1 month ago • 1 comments

When multiple consumers want to send audio back to a single producer (e.g., IP camera with backchannel), only one consumer could be active at a time. The audio from additional consumers would be lost or cause conflicts.

Solution

This PR introduces an optional audio mixer that combines multiple backchannel audio streams into a single stream before sending it to the producer.

How it works

  • When enabled with the #mix flag, an FFmpeg-based audio mixer is automatically created when the first backchannel consumer connects
  • Additional backchannel consumers are added as parents to the mixer
  • The mixer uses FFmpeg's amix filter to combine all audio streams in real-time
  • The mixed audio is then sent to the underlying producer (camera/device)

Usage

Add #mix to your stream source URL:

streams:
  camera_backchannel: 
    - rtsp://admin:[email protected]/stream#mix

Without #mix, the behavior remains unchanged - only the first backchannel consumer will work.

Features

  • Opt-in: Mixing is disabled by default
  • Automatic lifecycle: FFmpeg process starts when 2+ consumers connect, stops when <2 remain
  • Codec support: Works with all audio codecs
  • Visualization: Mixers are displayed in the /api/streams endpoint and /net graph

Demo

GET /api/streams?src=tapo

{
  "producers": [
    {
      "id": 2148886400,
      "format_name": "tapo",
      "protocol": "http",
      "remote_addr": "192.168.178.128:8800",
      "medias": [
        "video, recvonly, H264",
        "audio, recvonly, PCMA/8000",
        "audio, sendonly, PCMA/8000"
      ],
      "bytes_recv": 14068400,
      "receivers": [
        {
          "id": 2,
          "codec": {
            "codec_name": "h264",
            "codec_type": "video"
          },
          "childs": [
            3,
            10,
            14
          ],
          "bytes": 12457273,
          "packets": 1811
        },
        {
          "id": 4,
          "codec": {
            "codec_name": "pcm_alaw",
            "codec_type": "audio",
            "sample_rate": 8000
          },
          "childs": [
            5,
            12,
            16
          ],
          "bytes": 966656,
          "packets": 944
        }
      ],
      "senders": [
        {
          "id": 8,
          "codec": {
            "codec_name": "pcm_alaw",
            "codec_type": "audio",
            "sample_rate": 8000
          },
          "parent": 7,
          "bytes": 877440,
          "packets": 5484
        }
      ],
      "mixers": [
        {
          "id": 7,
          "codec": {
            "codec_name": "pcm_alaw",
            "codec_type": "audio",
            "sample_rate": 8000
          },
          "parents": [
            6,
            11,
            15
          ],
          "childs": [
            8
          ],
          "bytes": 1532160,
          "packets": 9576
        }
      ]
    }
  ],
  "consumers": [
    {
      "id": 1,
      "format_name": "preload",
      "medias": [
        "video, sendonly, ANY",
        "audio, sendonly, ANY",
        "audio, recvonly, ANY"
      ],
      "bytes_send": 13423929,
      "receivers": [
        {
          "id": 6,
          "codec": {
            "codec_name": "ANY",
            "codec_type": ""
          },
          "childs": [
            7
          ]
        }
      ],
      "senders": [
        {
          "id": 3,
          "codec": {
            "codec_name": "h264",
            "codec_type": "video"
          },
          "parent": 2,
          "bytes": 12457273,
          "packets": 1811
        },
        {
          "id": 5,
          "codec": {
            "codec_name": "pcm_alaw",
            "codec_type": "audio",
            "sample_rate": 8000
          },
          "parent": 4,
          "bytes": 966656,
          "packets": 944
        }
      ]
    },
    {
      "id": 9,
      "format_name": "webrtc",
      "protocol": "ws+udp",
      "remote_addr": "[fd13:1c98:5a6b:0:1023:52e0:c779:9286]:58762 host",
      "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36",
      "medias": [
        "video, sendonly, VP8, VP9, H264, AV1, H265",
        "audio, recvonly, OPUS/48000/2, G722/8000, PCMU/8000, PCMA/8000",
        "audio, sendonly, OPUS/48000/2, G722/8000, PCMU/8000, PCMA/8000, L16, PCML"
      ],
      "bytes_recv": 989647,
      "bytes_send": 12297412,
      "receivers": [
        {
          "id": 11,
          "codec": {
            "codec_name": "pcm_alaw",
            "codec_type": "audio",
            "sample_rate": 8000
          },
          "childs": [
            7
          ],
          "bytes": 878560,
          "packets": 5491
        }
      ],
      "senders": [
        {
          "id": 10,
          "codec": {
            "codec_name": "h264",
            "codec_type": "video"
          },
          "parent": 2,
          "bytes": 11267537,
          "packets": 1647
        },
        {
          "id": 12,
          "codec": {
            "codec_name": "pcm_alaw",
            "codec_type": "audio",
            "sample_rate": 8000
          },
          "parent": 4,
          "bytes": 879616,
          "packets": 859
        }
      ]
    },
    {
      "id": 13,
      "format_name": "webrtc",
      "protocol": "ws+udp",
      "remote_addr": "[fd13:1c98:5a6b:0:1023:52e0:c779:9286]:49303 host",
      "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36",
      "medias": [
        "video, sendonly, VP8, VP9, H264, AV1, H265",
        "audio, recvonly, OPUS/48000/2, G722/8000, PCMU/8000, PCMA/8000",
        "audio, sendonly, OPUS/48000/2, G722/8000, PCMU/8000, PCMA/8000, L16, PCML"
      ],
      "bytes_recv": 736574,
      "bytes_send": 9217431,
      "receivers": [
        {
          "id": 15,
          "codec": {
            "codec_name": "pcm_alaw",
            "codec_type": "audio",
            "sample_rate": 8000
          },
          "childs": [
            7
          ],
          "bytes": 653600,
          "packets": 4085
        }
      ],
      "senders": [
        {
          "id": 14,
          "codec": {
            "codec_name": "h264",
            "codec_type": "video"
          },
          "parent": 2,
          "bytes": 8450403,
          "packets": 1225
        },
        {
          "id": 16,
          "codec": {
            "codec_name": "pcm_alaw",
            "codec_type": "audio",
            "sample_rate": 8000
          },
          "parent": 4,
          "bytes": 654336,
          "packets": 639
        }
      ]
    }
  ]
}

/net.html preview

Related: https://github.com/AlexxIT/go2rtc/issues/1899 https://github.com/AlexxIT/go2rtc/issues/1828

seydx avatar Nov 24 '25 15:11 seydx

This looks awesome!

It fixes https://github.com/AlexxIT/go2rtc/issues/1828.

In future, this could also be reused for https://github.com/AlexxIT/go2rtc/issues/105.

felipecrs avatar Nov 24 '25 15:11 felipecrs