Skip to content

Commit 0de41d9

Browse files
committed
Add bambu-stream demo
1 parent 0cdfb48 commit 0de41d9

File tree

10 files changed

+1522
-10
lines changed

10 files changed

+1522
-10
lines changed

Cargo.lock

Lines changed: 466 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,10 @@ multer = { version = "3.1.0", features = ["json"] }
2929
opentelemetry = "0.24.0"
3030
opentelemetry-otlp = "0.17.0"
3131
opentelemetry_sdk = { version = "0.24.1", features = ["rt-tokio"] }
32-
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] }
32+
reqwest = { version = "0.12", default-features = false, features = [
33+
"json",
34+
"rustls-tls",
35+
] }
3336
schemars = { version = "0.8", features = ["chrono", "uuid1", "bigdecimal"] }
3437
serde = { version = "1.0", features = ["derive"] }
3538
serde_json = "1.0"
@@ -41,11 +44,25 @@ slog-json = "2.6.1"
4144
slog-term = "2.9.1"
4245
tempdir = "0.3.7"
4346
thiserror = "1.0.63"
44-
tokio = { version = "1", features = ["rt-multi-thread", "macros", "time", "net"] }
47+
tokio = { version = "1", features = [
48+
"rt-multi-thread",
49+
"macros",
50+
"time",
51+
"net",
52+
] }
4553
toml = "0.8.19"
4654
tracing = "0.1"
4755
tracing-opentelemetry = "0.25.0"
48-
tracing-subscriber = { version = "0.3.18", features = ["registry", "std", "fmt", "smallvec", "ansi", "tracing-log", "json", "env-filter"] }
56+
tracing-subscriber = { version = "0.3.18", features = [
57+
"registry",
58+
"std",
59+
"fmt",
60+
"smallvec",
61+
"ansi",
62+
"tracing-log",
63+
"json",
64+
"env-filter",
65+
] }
4966
uuid = "1.10.0"
5067

5168
[dev-dependencies]
@@ -62,8 +79,4 @@ testresult = "0.4.1"
6279
debug = ["dep:delouse", "dep:console-subscriber"]
6380

6481
[workspace]
65-
members = [
66-
"bambulabs",
67-
"moonraker"
68-
]
69-
82+
members = ["bambulabs", "moonraker", "bambu-stream"]

bambu-stream/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/target
2+
*.log

bambu-stream/Cargo.toml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
[package]
2+
name = "bambu-stream"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
7+
tokio-rustls = "0.26.0"
8+
rcgen = { version = "0.13", features = ["pem"] }
9+
tokio = { version = "1.0", features = ["full"] }
10+
futures-util = "0.3.1"
11+
lazy_static = "1.1"
12+
webpki-roots = "0.26"
13+
rustls-pemfile = "2"
14+
env_logger = "0.11.5"
15+
log = "0.4.22"
16+
rustls = "0.23.12"
17+
url = "2.5.2"
18+
anyhow = "1.0.86"
19+
http-auth = "0.1.9"
20+
openh264 = "0.6.2"
21+
sdl2 = "0.37.0"
22+
nom = "7.1.3"
23+
maplit = "1.0.2"
24+
rtp-types = "0.1.1"
25+
rtp = "0.11.0"
26+
bytes = "1.7.1"
27+
webrtc-util = "0.9.0"

bambu-stream/NOTES.md

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
# RTP
2+
3+
<https://www.rfc-editor.org/rfc/rfc3550#page-12>
4+
5+
Stuff is sent in network byte order, i.e. big-endian.
6+
7+
To be able to decrypt RTSP frames in Wireshark, we can use `ffplay`:
8+
9+
```bash
10+
export SSLKEYLOGFILE=~/ssl-keys.log
11+
ffplay "rtsps://bblp:[email protected]:322/streaming/live/1"
12+
```
13+
14+
Then go into the Wireshark TLS settings and select the log file to use.
15+
16+
## Payload type 96: "dynamic"
17+
18+
<https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml>,
19+
[RFC3551](https://www.rfc-editor.org/rfc/rfc3551.html).
20+
21+
## Header
22+
23+
The original diagram in the spec is really annoying as it works in base 10. I want bytes! So here
24+
they are:
25+
26+
```
27+
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
28+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
29+
|V=2|P|X| CC |M| PT | sequence number |
30+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
31+
| timestamp |
32+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
33+
| synchronization source (SSRC) identifier |
34+
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
35+
| contributing source (CSRC) identifiers |
36+
| .... |
37+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
38+
```
39+
40+
It seems that most frames apart from a couple at the start begin with `80 60 c3`, then what I
41+
_reckon_ is the low byte of the sequence counter in the next byte. This smells like an RTP header,
42+
although none of the fixed values (like `V=2`) seem to match up. The preceding stuff like
43+
`24 00 05 ac` might be some kind of frame delimiter? Or some custom stuff by the LIVE555 streaming
44+
crap? I dunno.
45+
46+
# Figuring out the stream format
47+
48+
The normal `00 00 00 01` NALU packet delimiter doesn't exist in any of the data I've captured from
49+
either my Rust code or `ffplay`.
50+
51+
A Wireshark dump shows small frames like this from `ffplay`:
52+
53+
```
54+
24 00 05 ac
55+
```
56+
57+
`05 ac` is `1452` in decimal, which is the length of the next chunk of data captured. Does this mean
58+
that the stream is in fact AVCC? The `24 00` part is still a mystery.
59+
60+
Lining up `24 00` with the header diagram above we get:
61+
62+
```
63+
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
64+
|V=2|P|X| CC |M| PT |
65+
66+
Rust print:
67+
0 0 1 0 0 1 0 0 0 0 0 0 0 0 0 0
68+
69+
Each byte mirrored:
70+
0 0 1 0 0 1 0 0 0 0 0 0 0 0 0 0
71+
```
72+
73+
Which doesn't make much sense...
74+
75+
What about `80 60`?
76+
77+
```
78+
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
79+
|V=2|P|X| CC |M| PT |
80+
81+
Rust print:
82+
1 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0
83+
84+
Each byte mirrored:
85+
0 0 0 0 0 0 0 1 0 0 0 0 0 1 1 0
86+
```
87+
88+
---
89+
90+
Every chunk seems to have a common header of `80, 60, 5d, a2, 4a, d4, 97, f9, d4, 9e, 7f, e1` after
91+
the weird 4 byte header.
92+
93+
---
94+
95+
According to <https://stackoverflow.com/a/75748093>:
96+
97+
> AVCC is best option for: a stored file (with known sizes & offsets).
98+
99+
But how much do you want to bet that Bambu just threw it into their live streaming setup? The
100+
LIVE555 website implies it prefers streaming files, so maybe that's why.
101+
102+
First 4 bytes of an AVCC frame are called "extradata" or "sequence header"
103+
<https://stackoverflow.com/a/24890903>
104+
105+
---
106+
107+
From ffmpeg/ffplay in the DESCRIBE response:
108+
109+
```
110+
v=0
111+
o=- 1723111495901673 1 IN IP4 192.168.0.96
112+
s=rtsp stream server
113+
i=Thu Aug 8 11:04:55 2024
114+
115+
t=0 0
116+
a=tool:LIVE555 Streaming Media v2023.03.30
117+
a=type:broadcast
118+
a=control:*
119+
a=range:npt=now-
120+
a=x-qt-text-nam:rtsp stream server
121+
a=x-qt-text-inf:Thu Aug 8 11:04:55 2024
122+
123+
m=video 0 RTP/AVP 96
124+
c=IN IP4 0.0.0.0
125+
b=AS:17186
126+
a=rtpmap:96 H264/90000
127+
a=fmtp:96 packetization-mode=1;profile-level-id=42C01F;sprop-parameter-sets=Z0LAH42NUCSC2TZAAAADAEAAAA8jwiEagA==,aM4xsg==
128+
a=control:track1
129+
```
130+
131+
This is an SDP (`application/sdp`) formatted response. I can test them with VLC:
132+
<https://stackoverflow.com/q/20634476>
133+
134+
The last little base64 `aM4xsg==` decodes to (hex):
135+
136+
```
137+
68,CE,31,B2
138+
```
139+
140+
This is present in the bytes I get in the Rust code! No idea what this means...
141+
142+
---
143+
144+
The longer `Z0LAH42NUCSC2TZAAAADAEAAAA8jwiEagA==` (SPS and PPS?) is:
145+
146+
```
147+
67,42,C0,1F,8D,8D,50,24,82,D9,36,40,00,00,03,00,40,00,00,0F,23,C2,21,1A,80
148+
```
149+
150+
which is indeed present as well.
151+
152+
Some more info here <https://www.cardinalpeak.com/blog/the-h-264-sequence-parameter-set>
153+
154+
The received frame starts like this:
155+
156+
```
157+
[ 24, 00, 00, 25 ], 80, 60, 07, 8f, 84, b4, 4d, 39, 5b, 6e, 5f, 94, [ 67, 42, c0, 1f, 8d, 8d, 50, 24, 82, d9, 36, 40, 00, 00, 03, 00, 40, 00, 00, 0f, 23, c2, 21 ]
158+
```
159+
160+
---
161+
162+
`ffmpeg` says
163+
164+
```
165+
Input #0, rtsp, from 'rtsps://bblp:[email protected]:322/streaming/live/1':
166+
Metadata:
167+
title : rtsp stream server
168+
comment : Sat Aug 10 00:25:18 2024
169+
Duration: N/A, start: 0.031978, bitrate: N/A
170+
Stream #0:0, 21, 1/90000: Video: h264 (Constrained Baseline), 1 reference frame, yuvj420p(pc, progressive, left), 1168x720, 0/1, 30 fps, 30 tbr, 90k tb
171+
```
172+
173+
Profile is `66d` with constrained bit set to 1

bambu-stream/README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Bambu X1 Carbon RTSP streaming
2+
3+
Needs SDL.
4+
5+
```bash
6+
cargo run --release
7+
```
8+
9+
`RUST_LOG=debug` can be useful for debugging RTSP auth issues.

bambu-stream/sample-handshake.txt

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
From `ffplay "rtsps://bblp:[email protected]:322/streaming/live/1"`
2+
3+
OPTIONS rtsps://192.168.0.96:322/streaming/live/1 RTSP/1.0
4+
CSeq: 1
5+
User-Agent: Lavf60.16.100
6+
7+
RTSP/1.0 200 OK
8+
CSeq: 1
9+
Date: Thu, Aug 08 2024 10:57:17 GMT
10+
Public: OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE, GET_PARAMETER, SET_PARAMETER
11+
12+
DESCRIBE rtsps://192.168.0.96:322/streaming/live/1 RTSP/1.0
13+
Accept: application/sdp
14+
CSeq: 2
15+
User-Agent: Lavf60.16.100
16+
17+
RTSP/1.0 401 Unauthorized
18+
CSeq: 2
19+
Date: Thu, Aug 08 2024 10:57:17 GMT
20+
WWW-Authenticate: Digest realm="LIVE555 Streaming Media", nonce="db860ca0377a9fe8769112644ba5db76"
21+
22+
DESCRIBE rtsps://192.168.0.96:322/streaming/live/1 RTSP/1.0
23+
Accept: application/sdp
24+
CSeq: 3
25+
User-Agent: Lavf60.16.100
26+
Authorization: Digest username="bblp", realm="LIVE555 Streaming Media", nonce="db860ca0377a9fe8769112644ba5db76", uri="rtsps://192.168.0.96:322/streaming/live/1", response="142ead4ed9c3a58148d54eee6b4d0715"
27+
28+
RTSP/1.0 200 OK
29+
CSeq: 3
30+
Date: Thu, Aug 08 2024 10:57:17 GMT
31+
Content-Base: rtsps://192.168.0.96/streaming/live/1/
32+
Content-Type: application/sdp
33+
Content-Length: 496
34+
35+
v=0
36+
o=- 1723111495901673 1 IN IP4 192.168.0.96
37+
s=rtsp stream server
38+
i=Thu Aug 8 11:04:55 2024
39+
40+
t=0 0
41+
a=tool:LIVE555 Streaming Media v2023.03.30
42+
a=type:broadcast
43+
a=control:*
44+
a=range:npt=now-
45+
a=x-qt-text-nam:rtsp stream server
46+
a=x-qt-text-inf:Thu Aug 8 11:04:55 2024
47+
48+
m=video 0 RTP/AVP 96
49+
c=IN IP4 0.0.0.0
50+
b=AS:17186
51+
a=rtpmap:96 H264/90000
52+
a=fmtp:96 packetization-mode=1;profile-level-id=42C01F;sprop-parameter-sets=Z0LAH42NUCSC2TZAAAADAEAAAA8jwiEagA==,aM4xsg==
53+
a=control:track1
54+
55+
SETUP rtsps://192.168.0.96/streaming/live/1/track1 RTSP/1.0
56+
Transport: RTP/AVP/TCP;unicast;interleaved=0-1
57+
CSeq: 4
58+
User-Agent: Lavf60.16.100
59+
Authorization: Digest username="bblp", realm="LIVE555 Streaming Media", nonce="db860ca0377a9fe8769112644ba5db76", uri="rtsps://192.168.0.96/streaming/live/1/track1", response="5bb1433eef289f517fbcce998eb868ec"
60+
61+
RTSP/1.0 200 OK
62+
CSeq: 4
63+
Date: Thu, Aug 08 2024 10:57:17 GMT
64+
Transport: RTP/AVP/TCP;unicast;destination=192.168.0.74;source=192.168.0.96;interleaved=0-1
65+
Session: 061546E4;timeout=10
66+
67+
PLAY rtsps://192.168.0.96/streaming/live/1/ RTSP/1.0
68+
Range: npt=0.000-
69+
CSeq: 5
70+
User-Agent: Lavf60.16.100
71+
Session: 061546E4
72+
Authorization: Digest username="bblp", realm="LIVE555 Streaming Media", nonce="db860ca0377a9fe8769112644ba5db76", uri="rtsps://192.168.0.96/streaming/live/1/", response="a5d9483ecc37519ee2c1f270b2a38e94"
73+
74+
// Some binary crap here, maybe video frames that start coming in?
75+
76+
RTSP/1.0 200 OK
77+
CSeq: 5
78+
Date: Thu, Aug 08 2024 10:57:17 GMT
79+
Range: npt=0.000-
80+
Session: 061546E4
81+
RTP-Info: url=rtsps://192.168.0.96/streaming/live/1/track1;seq=61243;rtptime=665680901

0 commit comments

Comments
 (0)