Skip to content

Commit 065e8e8

Browse files
sdb9696edenhaus
andauthored
Add RTCIceCandidateInit and allow setting sdpMid and sdpMLineIndex (#2)
Co-authored-by: Robert Resch <[email protected]>
1 parent a403f31 commit 065e8e8

File tree

7 files changed

+128
-6
lines changed

7 files changed

+128
-6
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,3 +160,6 @@ cython_debug/
160160
# and can be added to the global gitignore or merged into this file. For a more nuclear
161161
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
162162
#.idea/
163+
164+
# vscode
165+
.vscode

tests/__snapshots__/test_init.ambr

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,20 @@
3535
]),
3636
})
3737
# ---
38-
# name: test_decoding_and_encoding[RTCIceCandidate-RTCIceCandidate_candidate.json][dataclass]
38+
# name: test_decoding_and_encoding[RTCIceCandidateInit-RTCIceCandidateInit_candidate.json][dataclass]
3939
dict({
4040
'candidate': '3932168448 1 udp 1694498815 1.2.3.4 10676 typ srflx raddr 0.0.0.0 rport 37566',
41+
'sdp_m_line_index': 0,
42+
'sdp_mid': None,
43+
'user_fragment': None,
4144
})
4245
# ---
43-
# name: test_decoding_and_encoding[RTCIceCandidate-RTCIceCandidate_end.json][dataclass]
46+
# name: test_decoding_and_encoding[RTCIceCandidateInit-RTCIceCandidateInit_end.json][dataclass]
4447
dict({
4548
'candidate': '',
49+
'sdp_m_line_index': None,
50+
'sdp_mid': None,
51+
'user_fragment': None,
4652
})
4753
# ---
4854
# name: test_decoding_and_encoding[RTCIceServer-RTCIceServer_only_urls_list.json][dataclass]
@@ -79,3 +85,13 @@
7985
'username': 'username',
8086
})
8187
# ---
88+
# name: test_decoding_and_encoding_deprecated[RTCIceCandidate-RTCIceCandidate_candidate.json][dataclass]
89+
dict({
90+
'candidate': '3932168448 1 udp 1694498815 1.2.3.4 10676 typ srflx raddr 0.0.0.0 rport 37566',
91+
})
92+
# ---
93+
# name: test_decoding_and_encoding_deprecated[RTCIceCandidate-RTCIceCandidate_end.json][dataclass]
94+
dict({
95+
'candidate': '',
96+
})
97+
# ---
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"candidate": "3932168448 1 udp 1694498815 1.2.3.4 10676 typ srflx raddr 0.0.0.0 rport 37566",
3+
"sdpMLineIndex": 0
4+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"candidate": ""
3+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"candidate": "3932168448 1 udp 1694498815 1.2.3.4 10676 typ srflx raddr 0.0.0.0 rport 37566",
3+
"sdpMLineIndex": -1
4+
}

tests/test_init.py

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,12 @@
88
import pytest
99

1010
from tests import load_fixture
11-
from webrtc_models import RTCConfiguration, RTCIceCandidate, RTCIceServer
11+
from webrtc_models import (
12+
RTCConfiguration,
13+
RTCIceCandidate,
14+
RTCIceCandidateInit,
15+
RTCIceServer,
16+
)
1217

1318
if TYPE_CHECKING:
1419
from mashumaro.mixins.orjson import DataClassORJSONMixin
@@ -27,9 +32,9 @@
2732
(RTCConfiguration, "RTCConfiguration_empty.json"),
2833
(RTCConfiguration, "RTCConfiguration_one_iceServer.json"),
2934
(RTCConfiguration, "RTCConfiguration_multiple_iceServers.json"),
30-
# RTCIceCandidate
31-
(RTCIceCandidate, "RTCIceCandidate_end.json"),
32-
(RTCIceCandidate, "RTCIceCandidate_candidate.json"),
35+
# RTCIceCandidateInit
36+
(RTCIceCandidateInit, "RTCIceCandidateInit_end.json"),
37+
(RTCIceCandidateInit, "RTCIceCandidateInit_candidate.json"),
3338
],
3439
)
3540
def test_decoding_and_encoding(
@@ -51,3 +56,50 @@ def test_decoding_and_encoding(
5156
# Verify dict
5257
assert instance_dict == file_content_dict
5358
assert instance == clazz.from_dict(instance_dict)
59+
60+
61+
@pytest.mark.parametrize(
62+
("clazz", "filename"),
63+
[
64+
# RTCIceCandidate
65+
(RTCIceCandidate, "RTCIceCandidate_end.json"),
66+
(RTCIceCandidate, "RTCIceCandidate_candidate.json"),
67+
],
68+
)
69+
def test_decoding_and_encoding_deprecated(
70+
snapshot: SnapshotAssertion,
71+
clazz: type[DataClassORJSONMixin],
72+
filename: str,
73+
) -> None:
74+
"""Test decoding/encoding."""
75+
file_content = load_fixture(filename)
76+
with pytest.deprecated_call():
77+
instance = clazz.from_json(file_content)
78+
assert instance == snapshot(name="dataclass")
79+
80+
file_content_dict = orjson.loads(file_content)
81+
instance_dict = instance.to_dict()
82+
83+
# Verify json
84+
assert instance.to_json() == orjson.dumps(file_content_dict).decode()
85+
86+
# Verify dict
87+
assert instance_dict == file_content_dict
88+
with pytest.deprecated_call():
89+
assert instance == clazz.from_dict(instance_dict)
90+
91+
92+
def test_no_mid_and_mlineindex() -> None:
93+
"""Test spd_mid and sdp_multilineindex raises TypeError."""
94+
file_content = load_fixture("RTCIceCandidate_candidate.json")
95+
cand = RTCIceCandidateInit.from_json(file_content)
96+
assert cand.sdp_m_line_index == 0
97+
assert cand.sdp_mid is None
98+
99+
100+
def test_invalid_mlineindex() -> None:
101+
"""Test spd_mid and sdp_multilineindex raises TypeError."""
102+
file_content = load_fixture("RTCIceCandidateInit_invalid.json")
103+
msg = "sdpMLineIndex must be greater than or equal to 0"
104+
with pytest.raises(ValueError, match=msg):
105+
RTCIceCandidateInit.from_json(file_content)

webrtc_models/__init__.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""WebRTC models."""
22

33
from dataclasses import dataclass, field
4+
from warnings import warn
45

56
from mashumaro import field_options
67
from mashumaro.config import BaseConfig
@@ -56,3 +57,42 @@ class RTCIceCandidate(_RTCBaseModel):
5657
"""
5758

5859
candidate: str
60+
61+
def __post_init__(self) -> None:
62+
"""Initialize class."""
63+
msg = "Using RTCIceCandidate is deprecated. Use RTCIceCandidateInit instead"
64+
warn(msg, DeprecationWarning, stacklevel=2)
65+
66+
67+
@dataclass(frozen=True)
68+
class RTCIceCandidateInit(RTCIceCandidate):
69+
"""RTC Ice Candidate Init.
70+
71+
If neither sdp_mid nor sdp_m_line_index are provided and candidate is not an empty
72+
string, sdp_m_line_index is set to 0.
73+
See https://www.w3.org/TR/webrtc/#dom-rtcicecandidateinit
74+
"""
75+
76+
candidate: str
77+
sdp_mid: str | None = field(
78+
metadata=field_options(alias="sdpMid"), default=None, kw_only=True
79+
)
80+
sdp_m_line_index: int | None = field(
81+
metadata=field_options(alias="sdpMLineIndex"), default=None, kw_only=True
82+
)
83+
user_fragment: str | None = field(
84+
metadata=field_options(alias="userFragment"), default=None, kw_only=True
85+
)
86+
87+
def __post_init__(self) -> None:
88+
"""Initialize class."""
89+
if not self.candidate:
90+
# An empty string represents an end-of-candidates indication
91+
# or a peer reflexive remote candidate
92+
return
93+
94+
if self.sdp_mid is None and self.sdp_m_line_index is None:
95+
object.__setattr__(self, "sdp_m_line_index", 0)
96+
elif (sdp := self.sdp_m_line_index) is not None and sdp < 0:
97+
msg = "sdpMLineIndex must be greater than or equal to 0"
98+
raise ValueError(msg)

0 commit comments

Comments
 (0)