Skip to content

Commit 5b6fc96

Browse files
committed
add tests, apply suggestions
1 parent 6db2eda commit 5b6fc96

File tree

3 files changed

+208
-21
lines changed

3 files changed

+208
-21
lines changed

lib/ex_webrtc_recorder.ex

Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -305,14 +305,7 @@ defmodule ExWebRTC.Recorder do
305305

306306
state = %{state | track_data: Map.merge(state.track_data, new_track_data)}
307307

308-
:ok =
309-
File.write!(
310-
state.manifest_path,
311-
state.track_data
312-
|> to_manifest()
313-
|> ExWebRTC.Recorder.Manifest.to_map()
314-
|> Jason.encode!()
315-
)
308+
:ok = write_manifest(state)
316309

317310
{manifest_diff, state}
318311
end
@@ -355,18 +348,9 @@ defmodule ExWebRTC.Recorder do
355348
defp update_codec(state, track_id, codec) do
356349
case get_in(state, [:track_data, track_id, :codec]) do
357350
nil ->
358-
updated_track_data = put_in(state.track_data, [track_id, :codec], codec)
359-
state = %{state | track_data: updated_track_data}
360-
361-
:ok =
362-
File.write!(
363-
state.manifest_path,
364-
state.track_data
365-
|> to_manifest
366-
|> ExWebRTC.Recorder.Manifest.to_map()
367-
|> Jason.encode!()
368-
)
351+
state = put_in(state, [:track_data, track_id, :codec], codec)
369352

353+
:ok = write_manifest(state)
370354
Logger.info("Updated manifest with codec info for track #{track_id}")
371355
state
372356

@@ -388,4 +372,14 @@ defmodule ExWebRTC.Recorder do
388372
:io_lib.format("~4..0w~2..0w~2..0w-~2..0w~2..0w~2..0w", [y, mo, d, h, m, s])
389373
|> to_string()
390374
end
375+
376+
defp write_manifest(state) do
377+
File.write!(
378+
state.manifest_path,
379+
state.track_data
380+
|> to_manifest()
381+
|> ExWebRTC.Recorder.Manifest.to_json!()
382+
|> Jason.encode!()
383+
)
384+
end
391385
end

lib/ex_webrtc_recorder/manifest.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ defmodule ExWebRTC.Recorder.Manifest do
2727
@type t :: %{MediaStreamTrack.id() => track_manifest()}
2828

2929
@doc false
30-
@spec to_map(t()) :: map()
31-
def to_map(manifest) do
30+
@spec to_json!(t()) :: map()
31+
def to_json!(manifest) do
3232
Map.new(manifest, fn {id, entry} ->
3333
{
3434
id,
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
defmodule ExWebRTC.Recorder.Manifest.Test do
2+
use ExUnit.Case, async: true
3+
4+
alias ExWebRTC.Recorder.Manifest
5+
alias ExWebRTC.RTPCodecParameters
6+
alias ExSDP.Attribute.{FMTP, RTCPFeedback}
7+
8+
describe "to_json!/1" do
9+
test "empty manifest" do
10+
manifest = %{}
11+
json = Manifest.to_json!(manifest)
12+
assert json == %{}
13+
end
14+
15+
test "two entries(audio,video) manifest" do
16+
audio_id = 6_264_340_764_417_145_606_381_315_372
17+
video_id = 34_899_663_449_195_684_468_354_913_891
18+
base_dir = "/Users/bernardgawor/Projects/swm/broadcaster/recordings/20250902-163515"
19+
audio_path = Path.join(base_dir, "6264340764417145606381315372.rtpx")
20+
video_path = Path.join(base_dir, "34899663449195684468354913891.rtpx")
21+
streams = ["{13d54720-6d00-45a9-b234-11f0e969f4b7}"]
22+
start_time = ~U[2025-09-02 14:35:15.336022Z]
23+
24+
manifest = %{
25+
audio_id => %{
26+
location: audio_path,
27+
kind: :audio,
28+
streams: streams,
29+
start_time: start_time,
30+
codec: audio_opus_codec(),
31+
rid_map: %{nil: 0}
32+
},
33+
video_id => %{
34+
location: video_path,
35+
kind: :video,
36+
streams: streams,
37+
start_time: start_time,
38+
codec: video_vp8_codec(),
39+
rid_map: %{nil: 0}
40+
}
41+
}
42+
43+
json = Manifest.to_json!(manifest)
44+
45+
assert json ==
46+
%{
47+
audio_id => %{
48+
"codec" => %{
49+
"channels" => 2,
50+
"clock_rate" => 48000,
51+
"mime_type" => "audio/opus",
52+
"payload_type" => 109,
53+
"rtcp_fbs" => nil,
54+
"sdp_fmtp_line" => "109 maxplaybackrate=48000;stereo=1;useinbandfec=1"
55+
},
56+
"kind" => "audio",
57+
"location" => audio_path,
58+
"rid_map" => %{"nil" => 0},
59+
"start_time" => "2025-09-02T14:35:15.336022Z",
60+
"streams" => streams
61+
},
62+
video_id => %{
63+
"codec" => %{
64+
"channels" => nil,
65+
"clock_rate" => 90000,
66+
"mime_type" => "video/VP8",
67+
"payload_type" => 120,
68+
"rtcp_fbs" => ["120 nack", "120 nack pli", "120 ccm fir", "120 transport-cc"],
69+
"sdp_fmtp_line" => "120 max-fs=12288;max-fr=60"
70+
},
71+
"kind" => "video",
72+
"location" => video_path,
73+
"rid_map" => %{"nil" => 0},
74+
"start_time" => "2025-09-02T14:35:15.336022Z",
75+
"streams" => streams
76+
}
77+
}
78+
end
79+
end
80+
81+
describe "from_json!/1" do
82+
test "empty manifest" do
83+
json = %{}
84+
manifest = Manifest.from_json!(json)
85+
assert manifest == %{}
86+
end
87+
88+
test "two entries(audio,video) manifest" do
89+
json =
90+
Jason.decode!(
91+
"""
92+
{
93+
"6264340764417145606381315372": {
94+
"codec": {
95+
"channels": 2,
96+
"clock_rate": 48000,
97+
"mime_type": "audio/opus",
98+
"payload_type": 109,
99+
"rtcp_fbs": null,
100+
"sdp_fmtp_line": "109 maxplaybackrate=48000;stereo=1;useinbandfec=1"
101+
},
102+
"kind": "audio",
103+
"location": "/Users/bernardgawor/Projects/swm/broadcaster/recordings/20250902-163515/6264340764417145606381315372.rtpx",
104+
"rid_map": {
105+
"nil": 0
106+
},
107+
"start_time": "2025-09-02T14:35:15.336022Z",
108+
"streams": [
109+
"{13d54720-6d00-45a9-b234-11f0e969f4b7}"
110+
]
111+
},
112+
"34899663449195684468354913891": {
113+
"codec": {
114+
"channels": null,
115+
"clock_rate": 90000,
116+
"mime_type": "video/VP8",
117+
"payload_type": 120,
118+
"rtcp_fbs": [
119+
"120 nack",
120+
"120 nack pli",
121+
"120 ccm fir",
122+
"120 transport-cc"
123+
],
124+
"sdp_fmtp_line": "120 max-fs=12288;max-fr=60"
125+
},
126+
"kind": "video",
127+
"location": "/Users/bernardgawor/Projects/swm/broadcaster/recordings/20250902-163515/34899663449195684468354913891.rtpx",
128+
"rid_map": {
129+
"nil": 0
130+
},
131+
"start_time": "2025-09-02T14:35:15.336022Z",
132+
"streams": [
133+
"{13d54720-6d00-45a9-b234-11f0e969f4b7}"
134+
]
135+
}
136+
}
137+
"""
138+
)
139+
140+
manifest = Manifest.from_json!(json)
141+
142+
assert manifest ==
143+
%{
144+
"34899663449195684468354913891" => %{
145+
location:
146+
"/Users/bernardgawor/Projects/swm/broadcaster/recordings/20250902-163515/34899663449195684468354913891.rtpx",
147+
kind: :video,
148+
streams: ["{13d54720-6d00-45a9-b234-11f0e969f4b7}"],
149+
start_time: ~U[2025-09-02 14:35:15.336022Z],
150+
codec: video_vp8_codec(),
151+
rid_map: %{nil: 0}
152+
},
153+
"6264340764417145606381315372" => %{
154+
location:
155+
"/Users/bernardgawor/Projects/swm/broadcaster/recordings/20250902-163515/6264340764417145606381315372.rtpx",
156+
kind: :audio,
157+
streams: ["{13d54720-6d00-45a9-b234-11f0e969f4b7}"],
158+
start_time: ~U[2025-09-02 14:35:15.336022Z],
159+
codec: audio_opus_codec(),
160+
rid_map: %{nil: 0}
161+
}
162+
}
163+
end
164+
end
165+
166+
# Helpers
167+
defp audio_opus_codec do
168+
%RTPCodecParameters{
169+
payload_type: 109,
170+
mime_type: "audio/opus",
171+
clock_rate: 48_000,
172+
channels: 2,
173+
sdp_fmtp_line: %FMTP{pt: 109, maxplaybackrate: 48_000, stereo: true, useinbandfec: true},
174+
rtcp_fbs: []
175+
}
176+
end
177+
178+
defp video_vp8_codec do
179+
%RTPCodecParameters{
180+
payload_type: 120,
181+
mime_type: "video/VP8",
182+
clock_rate: 90_000,
183+
channels: nil,
184+
sdp_fmtp_line: %FMTP{pt: 120, max_fs: 12_288, max_fr: 60},
185+
rtcp_fbs: [
186+
%RTCPFeedback{pt: 120, feedback_type: :nack},
187+
%RTCPFeedback{pt: 120, feedback_type: :pli},
188+
%RTCPFeedback{pt: 120, feedback_type: :fir},
189+
%RTCPFeedback{pt: 120, feedback_type: :twcc}
190+
]
191+
}
192+
end
193+
end

0 commit comments

Comments
 (0)