Skip to content

Commit 6d5663a

Browse files
committed
WIP
1 parent 0f18440 commit 6d5663a

File tree

3 files changed

+131
-13
lines changed

3 files changed

+131
-13
lines changed

fix_vto_codecs.sh

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
#!/bin/bash
2+
#
3+
# This script fixes the VTO audio codecs before supplying the stream url to go2rtc.
4+
#
5+
# Usage: ./fix_vto_codecs.sh [--debug] [--https] <VTO stream URL>
6+
#
7+
# Examples:
8+
#
9+
# ./fix_vto_codecs.sh rtsp://user:[email protected]/cam/realmonitor?channel=1&subtype=0&unicast=true&proto=Onvif
10+
# ./fix_vto_codecs.sh rtsp://user:[email protected]/cam/realmonitor?channel=1&subtype=1
11+
#
12+
# If the VTO has HTTPS enabled:
13+
# ./fix_vto_codecs.sh --https rtsp://user:[email protected]/cam/realmonitor?channel=1&subtype=0
14+
#
15+
# With ffmpeg prefix:
16+
# ./fix_vto_codecs.sh ffmpeg:rtsp://user:[email protected]/cam/realmonitor?channel=1&subtype=0#video=copy#audio=copy
17+
#
18+
19+
set -euo pipefail
20+
21+
usage() {
22+
echo "Usage: ${0} [--debug] [--https] <VTO stream URL>" >&2
23+
exit "${1}"
24+
}
25+
26+
debug=false
27+
protocol="http"
28+
extra_curl_args=()
29+
30+
positional_args=()
31+
while [[ $# -gt 0 ]]; do
32+
case $1 in
33+
--debug)
34+
debug=true
35+
shift
36+
;;
37+
--https)
38+
protocol="https"
39+
extra_curl_args+=("--insecure")
40+
shift
41+
;;
42+
-*)
43+
echo "Unknown option ${1}" >&2
44+
usage 1
45+
;;
46+
*)
47+
positional_args+=("$1")
48+
shift
49+
;;
50+
esac
51+
done
52+
53+
set -- "${positional_args[@]}"
54+
unset positional_args
55+
56+
if [[ $# -ne 1 ]]; then
57+
echo "Expected 1 positional argument, got $#" >&2
58+
usage 1
59+
fi
60+
61+
vto_stream_url="${1}"
62+
63+
if [[ "${vto_stream_url}" != "rtsp://"* && "${vto_stream_url}" != "ffmpeg:rtsp://"* ]]; then
64+
echo "VTO stream URL does not start with rtsp:// or ffmpeg:rtsp://" >&2
65+
usage 1
66+
fi
67+
68+
if [[ "${debug}" == "true" ]]; then
69+
set -x
70+
extra_curl_args+=("--verbose")
71+
fi
72+
73+
vto_host_with_creds="${vto_stream_url#"ffmpeg:"}"
74+
vto_host_with_creds="${vto_host_with_creds#"rtsp://"}"
75+
vto_host_with_creds="${vto_host_with_creds%%"/"*}"
76+
if [[ "${vto_host_with_creds}" =~ :[0-9]+$ ]]; then
77+
vto_host_with_creds="${vto_host_with_creds%":"*}"
78+
fi
79+
80+
query="action=setConfig"
81+
# PCMA: average audio quality, but good for WebRTC and 2-way audio
82+
query+="&Encode[0].MainFormat[0].Audio.Compression=G.711A"
83+
# 16000Hz yields better audio quality, but try 8000Hz if your VTO does not support it
84+
query+="&Encode[0].MainFormat[0].Audio.Frequency=8000"
85+
# AAC: best audio quality, good for Frigate recordings
86+
query+="&Encode[0].ExtraFormat[0].Audio.Compression=AAC"
87+
88+
# PS: the current config can be retrieved with:
89+
# curl -fsSL --digest --globoff \
90+
# http://user:[email protected]/cgi-bin/configManager.cgi?action=getConfig&name=Encode
91+
92+
output=$(
93+
curl --fail --silent --show-error --digest --globoff "${extra_curl_args[@]}" \
94+
"${protocol}://${vto_host_with_creds}/cgi-bin/configManager.cgi?${query}"
95+
)
96+
97+
if [[ "${output}" != $'OK\r' ]]; then
98+
echo "Failed to set VTO codecs. Response:" >&2
99+
echo "${output}" >&2
100+
exit 1
101+
fi
102+
103+
echo -n "${vto_stream_url}"

pkg/dahua/backchannel.go

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,24 @@ func (c *Client) AddTrack(media *core.Media, _ *core.Codec, track *core.Receiver
2727

2828
if c.sender == nil {
2929
c.sender = core.NewSender(media, track.Codec)
30+
31+
// Set up the audio handler
3032
c.sender.Handler = func(packet *rtp.Packet) {
3133
// Get connection in a thread-safe way
3234
conn := c.getConnection()
3335
if conn == nil {
3436
return
3537
}
3638

39+
// For G.711, we should send raw audio data without RTP headers
40+
// The Dahua camera expects pure G.711 audio samples
41+
audioData := packet.Payload
42+
43+
// Skip empty packets
44+
if len(audioData) == 0 {
45+
return
46+
}
47+
3748
// Send multipart boundary with payload
3849
boundary := "\r\n--go2rtc-audio-boundary\r\n"
3950

@@ -49,18 +60,18 @@ func (c *Client) AddTrack(media *core.Media, _ *core.Codec, track *core.Receiver
4960
}
5061

5162
boundary += "Content-Type: " + contentType + "\r\n"
52-
boundary += "Content-Length: " + fmt.Sprintf("%d", len(packet.Payload)) + "\r\n\r\n"
63+
boundary += fmt.Sprintf("Content-Length: %d\r\n\r\n", len(audioData))
5364

54-
// Write boundary first
55-
if _, err := conn.Write([]byte(boundary)); err != nil {
56-
log.Error().Err(err).Msg("[dahua] failed to write boundary")
57-
return
58-
}
65+
// Write boundary and audio data in one operation to reduce fragmentation
66+
fullData := append([]byte(boundary), audioData...)
67+
68+
// Use thread-safe connection writing
69+
c.connMu.Lock()
70+
n, err := conn.Write(fullData)
71+
c.connMu.Unlock()
5972

60-
// Write payload
61-
n, err := conn.Write(packet.Payload)
6273
if err != nil {
63-
log.Error().Err(err).Msg("[dahua] failed to write audio payload")
74+
log.Debug().Err(err).Msg("[dahua] failed to write audio data")
6475
return
6576
}
6677

@@ -73,14 +84,14 @@ func (c *Client) AddTrack(media *core.Media, _ *core.Codec, track *core.Receiver
7384
Msg("[dahua] audio sender created")
7485
}
7586

76-
c.sender.HandleRTP(track)
87+
c.sender.WithParent(track).Start()
7788
return nil
7889
}
7990

8091
// getConnection returns the current connection in a thread-safe way
8192
func (c *Client) getConnection() io.WriteCloser {
82-
// For now, just return the connection directly
83-
// TODO: Add proper synchronization if needed
93+
c.connMu.RLock()
94+
defer c.connMu.RUnlock()
8495
return c.conn
8596
}
8697

@@ -111,7 +122,6 @@ func (c *Client) Stop() (err error) {
111122
if err != nil {
112123
log.Error().Err(err).Msg("[dahua] error during close")
113124
}
114-
// Don't call c.conn.Close() again since c.close() already handles it
115125
log.Debug().Msg("[dahua] connection closed")
116126
}
117127

pkg/dahua/client.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,9 @@ func (c *Client) open() error {
309309
}()
310310

311311
// Store the pipe writer as our connection
312+
c.connMu.Lock()
312313
c.conn = pipeWriter
314+
c.connMu.Unlock()
313315

314316
// Send initial multipart boundary
315317
boundary := "\r\n--go2rtc-audio-boundary\r\n"
@@ -331,6 +333,9 @@ func (c *Client) close() error {
331333
log := app.GetLogger("dahua")
332334
log.Debug().Msg("[dahua] closing audio connection")
333335

336+
c.connMu.Lock()
337+
defer c.connMu.Unlock()
338+
334339
if c.conn != nil {
335340
err := c.conn.Close()
336341
c.conn = nil

0 commit comments

Comments
 (0)