sora-sdk 2025.1.0.dev12__tar.gz → 2025.1.0.dev15__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of sora-sdk might be problematic. Click here for more details.
- {sora_sdk-2025.1.0.dev12/src/sora_sdk.egg-info → sora_sdk-2025.1.0.dev15}/PKG-INFO +1 -1
- {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev15}/pyproject.toml +1 -1
- {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev15}/src/sora_sdk/sora_sdk_ext.cpython-311-darwin.so +0 -0
- {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev15}/src/sora_sdk/sora_sdk_ext.pyi +10 -0
- {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev15/src/sora_sdk.egg-info}/PKG-INFO +1 -1
- {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev15}/src/sora_sdk.egg-info/SOURCES.txt +2 -1
- sora_sdk-2025.1.0.dev15/tests/test_amd_amf.py +310 -0
- {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev15}/tests/test_intel_vpl.py +72 -206
- sora_sdk-2025.1.0.dev15/tests/test_memory_leak.py +25 -0
- {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev15}/tests/test_nvidia_video_codec_sdk.py +102 -135
- {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev15}/tests/test_openh264.py +73 -0
- sora_sdk-2025.1.0.dev12/tests/test_codec_preference.py +0 -124
- {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev15}/LICENSE +0 -0
- {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev15}/MANIFEST.in +0 -0
- {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev15}/NOTICE.md +0 -0
- {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev15}/README.md +0 -0
- {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev15}/buildbase.py +0 -0
- {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev15}/pypath.py +0 -0
- {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev15}/run.py +0 -0
- {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev15}/setup.cfg +0 -0
- {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev15}/setup.py +0 -0
- {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev15}/src/sora_sdk/__init__.py +0 -0
- {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev15}/src/sora_sdk/py.typed +0 -0
- {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev15}/src/sora_sdk.egg-info/dependency_links.txt +0 -0
- {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev15}/src/sora_sdk.egg-info/top_level.txt +0 -0
- {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev15}/tests/test_apple_video_toolbox.py +0 -0
- {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev15}/tests/test_authz.py +0 -0
- {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev15}/tests/test_authz_simulcast.py +0 -0
- {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev15}/tests/test_ca_cert.py +0 -0
- {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev15}/tests/test_capability.py +0 -0
- {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev15}/tests/test_degradation_preference.py +0 -0
- {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev15}/tests/test_encoded_transform.py +0 -0
- {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev15}/tests/test_messaging.py +0 -0
- {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev15}/tests/test_messaging_header.py +0 -0
- {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev15}/tests/test_opus.py +0 -0
- {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev15}/tests/test_re_offer_re_answer_sdp.py +0 -0
- {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev15}/tests/test_sendonly_recvonly.py +0 -0
- {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev15}/tests/test_signaling.py +0 -0
- {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev15}/tests/test_signaling_message.py +0 -0
- {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev15}/tests/test_signaling_notify.py +0 -0
- {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev15}/tests/test_simulcast.py +0 -0
- {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev15}/tests/test_sora_disconnect.py +0 -0
- {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev15}/tests/test_type_disconnect.py +0 -0
- {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev15}/tests/test_type_switched.py +0 -0
- {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev15}/tests/test_vad.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "sora_sdk"
|
|
3
3
|
authors = [{ name = "Shiguredo Inc.", email = "contact+pypi@shiguredo.jp" }]
|
|
4
|
-
version = "2025.1.0.
|
|
4
|
+
version = "2025.1.0.dev15"
|
|
5
5
|
description = "WebRTC SFU Sora Python SDK"
|
|
6
6
|
readme = "README.md"
|
|
7
7
|
license = { file = "LICENSE" }
|
{sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev15}/src/sora_sdk/sora_sdk_ext.cpython-311-darwin.so
RENAMED
|
Binary file
|
|
@@ -344,6 +344,12 @@ class SoraVideoCodecCapability:
|
|
|
344
344
|
@property
|
|
345
345
|
def nvcodec_gpu_device_name(self) -> str | None: ...
|
|
346
346
|
|
|
347
|
+
@property
|
|
348
|
+
def amf_runtime_version(self) -> str | None: ...
|
|
349
|
+
|
|
350
|
+
@property
|
|
351
|
+
def amf_embedded_version(self) -> str | None: ...
|
|
352
|
+
|
|
347
353
|
class Codec:
|
|
348
354
|
@property
|
|
349
355
|
def type(self) -> SoraVideoCodecType: ...
|
|
@@ -376,6 +382,8 @@ class SoraVideoCodecImplementation(enum.IntEnum):
|
|
|
376
382
|
|
|
377
383
|
NVIDIA_VIDEO_CODEC_SDK = 3
|
|
378
384
|
|
|
385
|
+
AMD_AMF = 4
|
|
386
|
+
|
|
379
387
|
class SoraVideoCodecPreference:
|
|
380
388
|
def __init__(self, codecs: Sequence[SoraVideoCodecPreference.Codec] = []) -> None: ...
|
|
381
389
|
|
|
@@ -480,3 +488,5 @@ def create_video_codec_preference_from_implementation(arg0: SoraVideoCodecCapabi
|
|
|
480
488
|
def enable_libwebrtc_log(arg: SoraLoggingSeverity, /) -> None: ...
|
|
481
489
|
|
|
482
490
|
def get_video_codec_capability(openh264: str | None = None) -> SoraVideoCodecCapability: ...
|
|
491
|
+
|
|
492
|
+
def rtc_log(arg0: SoraLoggingSeverity, arg1: str, /) -> None: ...
|
|
@@ -15,15 +15,16 @@ src/sora_sdk.egg-info/PKG-INFO
|
|
|
15
15
|
src/sora_sdk.egg-info/SOURCES.txt
|
|
16
16
|
src/sora_sdk.egg-info/dependency_links.txt
|
|
17
17
|
src/sora_sdk.egg-info/top_level.txt
|
|
18
|
+
tests/test_amd_amf.py
|
|
18
19
|
tests/test_apple_video_toolbox.py
|
|
19
20
|
tests/test_authz.py
|
|
20
21
|
tests/test_authz_simulcast.py
|
|
21
22
|
tests/test_ca_cert.py
|
|
22
23
|
tests/test_capability.py
|
|
23
|
-
tests/test_codec_preference.py
|
|
24
24
|
tests/test_degradation_preference.py
|
|
25
25
|
tests/test_encoded_transform.py
|
|
26
26
|
tests/test_intel_vpl.py
|
|
27
|
+
tests/test_memory_leak.py
|
|
27
28
|
tests/test_messaging.py
|
|
28
29
|
tests/test_messaging_header.py
|
|
29
30
|
tests/test_nvidia_video_codec_sdk.py
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
import time
|
|
4
|
+
import uuid
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
from client import (
|
|
8
|
+
SoraClient,
|
|
9
|
+
SoraRole,
|
|
10
|
+
codec_type_string_to_codec_type,
|
|
11
|
+
is_codec_supported,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
from sora_sdk import (
|
|
15
|
+
SoraVideoCodecImplementation,
|
|
16
|
+
SoraVideoCodecPreference,
|
|
17
|
+
SoraVideoCodecType,
|
|
18
|
+
get_video_codec_capability,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@pytest.mark.skipif(os.environ.get("AMD_AMF") is None, reason="AMD AMF でのみ実行する")
|
|
23
|
+
def test_amd_amf_available(setup):
|
|
24
|
+
capability = get_video_codec_capability()
|
|
25
|
+
|
|
26
|
+
amd_amf_available = False
|
|
27
|
+
for e in capability.engines:
|
|
28
|
+
if e.name == SoraVideoCodecImplementation.AMD_AMF:
|
|
29
|
+
amd_amf_available = True
|
|
30
|
+
|
|
31
|
+
assert amd_amf_available is True
|
|
32
|
+
|
|
33
|
+
for e in capability.engines:
|
|
34
|
+
if e.name == SoraVideoCodecImplementation.AMD_AMF:
|
|
35
|
+
# 対応コーデックは 5 種類
|
|
36
|
+
assert len(e.codecs) == 5
|
|
37
|
+
|
|
38
|
+
for c in e.codecs:
|
|
39
|
+
match c.type:
|
|
40
|
+
case SoraVideoCodecType.VP8:
|
|
41
|
+
assert c.decoder is False
|
|
42
|
+
assert c.encoder is False
|
|
43
|
+
case SoraVideoCodecType.VP9:
|
|
44
|
+
assert c.decoder is True
|
|
45
|
+
assert c.encoder is False
|
|
46
|
+
case SoraVideoCodecType.AV1:
|
|
47
|
+
# TODO: AV1 decoder は True だが色々課題あり
|
|
48
|
+
assert c.decoder is True
|
|
49
|
+
assert c.encoder is True
|
|
50
|
+
case SoraVideoCodecType.H264:
|
|
51
|
+
assert c.decoder is True
|
|
52
|
+
assert c.encoder is True
|
|
53
|
+
case SoraVideoCodecType.H265:
|
|
54
|
+
assert c.decoder is True
|
|
55
|
+
assert c.encoder is True
|
|
56
|
+
case _:
|
|
57
|
+
pytest.fail(f"未実装の codec_type: {c.type}")
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@pytest.mark.skipif(os.environ.get("AMD_AMF") is None, reason="AMD AMF でのみ実行する")
|
|
61
|
+
@pytest.mark.parametrize(
|
|
62
|
+
"video_codec_type",
|
|
63
|
+
[
|
|
64
|
+
# AV1 は decoder が正常に動作しない
|
|
65
|
+
# "AV1",
|
|
66
|
+
"H264",
|
|
67
|
+
"H265",
|
|
68
|
+
],
|
|
69
|
+
)
|
|
70
|
+
def test_amd_amf_sendonly_recvonly(setup, video_codec_type):
|
|
71
|
+
if not is_codec_supported(video_codec_type, SoraVideoCodecImplementation.AMD_AMF):
|
|
72
|
+
pytest.skip(
|
|
73
|
+
f"このチップでは {video_codec_type} のエンコード/デコードの両方がサポートされていません"
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
signaling_urls = setup.get("signaling_urls")
|
|
77
|
+
channel_id_prefix = setup.get("channel_id_prefix")
|
|
78
|
+
metadata = setup.get("metadata")
|
|
79
|
+
|
|
80
|
+
channel_id = f"{channel_id_prefix}_{__name__}_{sys._getframe().f_code.co_name}_{uuid.uuid4()}"
|
|
81
|
+
|
|
82
|
+
sendonly = SoraClient(
|
|
83
|
+
signaling_urls,
|
|
84
|
+
SoraRole.SENDONLY,
|
|
85
|
+
channel_id,
|
|
86
|
+
audio=False,
|
|
87
|
+
video=True,
|
|
88
|
+
video_codec_type=video_codec_type,
|
|
89
|
+
metadata=metadata,
|
|
90
|
+
video_codec_preference=SoraVideoCodecPreference(
|
|
91
|
+
codecs=[
|
|
92
|
+
SoraVideoCodecPreference.Codec(
|
|
93
|
+
type=codec_type_string_to_codec_type(video_codec_type),
|
|
94
|
+
encoder=SoraVideoCodecImplementation.AMD_AMF,
|
|
95
|
+
),
|
|
96
|
+
]
|
|
97
|
+
),
|
|
98
|
+
)
|
|
99
|
+
sendonly.connect(fake_video=True)
|
|
100
|
+
|
|
101
|
+
recvonly = SoraClient(
|
|
102
|
+
signaling_urls,
|
|
103
|
+
SoraRole.RECVONLY,
|
|
104
|
+
channel_id,
|
|
105
|
+
metadata=metadata,
|
|
106
|
+
video_codec_preference=SoraVideoCodecPreference(
|
|
107
|
+
codecs=[
|
|
108
|
+
SoraVideoCodecPreference.Codec(
|
|
109
|
+
type=codec_type_string_to_codec_type(video_codec_type),
|
|
110
|
+
decoder=SoraVideoCodecImplementation.AMD_AMF,
|
|
111
|
+
),
|
|
112
|
+
]
|
|
113
|
+
),
|
|
114
|
+
)
|
|
115
|
+
recvonly.connect()
|
|
116
|
+
|
|
117
|
+
time.sleep(5)
|
|
118
|
+
|
|
119
|
+
sendonly_stats = sendonly.get_stats()
|
|
120
|
+
recvonly_stats = recvonly.get_stats()
|
|
121
|
+
|
|
122
|
+
sendonly.disconnect()
|
|
123
|
+
recvonly.disconnect()
|
|
124
|
+
|
|
125
|
+
# offer の sdp に video_codec_type が含まれているかどうかを確認している
|
|
126
|
+
assert sendonly.offer_message is not None
|
|
127
|
+
assert "sdp" in sendonly.offer_message
|
|
128
|
+
assert video_codec_type in sendonly.offer_message["sdp"]
|
|
129
|
+
|
|
130
|
+
# answer の sdp に video_codec_type が含まれているかどうかを確認している
|
|
131
|
+
assert sendonly.answer_message is not None
|
|
132
|
+
assert "sdp" in sendonly.answer_message
|
|
133
|
+
assert video_codec_type in sendonly.answer_message["sdp"]
|
|
134
|
+
|
|
135
|
+
# codec が無かったら StopIteration 例外が上がる
|
|
136
|
+
sendonly_codec_stats = next(s for s in sendonly_stats if s.get("type") == "codec")
|
|
137
|
+
# H.264/H.265 が採用されているかどうか確認する
|
|
138
|
+
assert sendonly_codec_stats["mimeType"] == f"video/{video_codec_type}"
|
|
139
|
+
|
|
140
|
+
# outbound-rtp が無かったら StopIteration 例外が上がる
|
|
141
|
+
outbound_rtp_stats = next(s for s in sendonly_stats if s.get("type") == "outbound-rtp")
|
|
142
|
+
assert outbound_rtp_stats["encoderImplementation"] == "AMF"
|
|
143
|
+
assert outbound_rtp_stats["bytesSent"] > 0
|
|
144
|
+
assert outbound_rtp_stats["packetsSent"] > 0
|
|
145
|
+
|
|
146
|
+
# codec が無かったら StopIteration 例外が上がる
|
|
147
|
+
recvonly_codec_stats = next(s for s in recvonly_stats if s.get("type") == "codec")
|
|
148
|
+
# H.264/H.265 が採用されているかどうか確認する
|
|
149
|
+
assert recvonly_codec_stats["mimeType"] == f"video/{video_codec_type}"
|
|
150
|
+
|
|
151
|
+
# inbound-rtp が無かったら StopIteration 例外が上がる
|
|
152
|
+
inbound_rtp_stats = next(s for s in recvonly_stats if s.get("type") == "inbound-rtp")
|
|
153
|
+
assert inbound_rtp_stats["decoderImplementation"] == "AMF"
|
|
154
|
+
assert inbound_rtp_stats["bytesReceived"] > 0
|
|
155
|
+
assert inbound_rtp_stats["packetsReceived"] > 0
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
@pytest.mark.skipif(os.environ.get("AMD_AMF") is None, reason="AMD AMF でのみ実行する")
|
|
159
|
+
@pytest.mark.parametrize(
|
|
160
|
+
(
|
|
161
|
+
"video_codec_type",
|
|
162
|
+
"expected_implementation",
|
|
163
|
+
"video_bit_rate",
|
|
164
|
+
"video_width",
|
|
165
|
+
"video_height",
|
|
166
|
+
"simulcast_count",
|
|
167
|
+
),
|
|
168
|
+
# FIXME: H.265 では、本数が 2 本 や 1 本の場合、エラーになるのでコメントアウトしている
|
|
169
|
+
# FIXME: AV1 では、解像度が一定数より低くなる場合、エラーになるのでコメントアウトしている
|
|
170
|
+
[
|
|
171
|
+
# 1080p
|
|
172
|
+
("AV1", "AMF", 5000, 1920, 1080, 3),
|
|
173
|
+
("H264", "AMF", 5000, 1920, 1080, 3),
|
|
174
|
+
("H265", "AMF", 5000, 1920, 1080, 3),
|
|
175
|
+
# 720p
|
|
176
|
+
("AV1", "AMF", 2500, 1280, 720, 3),
|
|
177
|
+
("H264", "AMF", 2500, 1280, 720, 3),
|
|
178
|
+
("H265", "AMF", 2500, 1280, 720, 3),
|
|
179
|
+
# 540p
|
|
180
|
+
("AV1", "AMF", 1200, 960, 540, 3),
|
|
181
|
+
("H264", "AMF", 1200, 960, 540, 3),
|
|
182
|
+
("H265", "AMF", 1200, 960, 540, 3),
|
|
183
|
+
# 360p
|
|
184
|
+
# ("AV1", "AMF", 700, 640, 360, 2),
|
|
185
|
+
# ("H264", "AMF", 700, 640, 360, 2),
|
|
186
|
+
# ("H265", "AMF", 700, 640, 360, 2),
|
|
187
|
+
# 270p
|
|
188
|
+
# ("AV1", "AMF", 450, 480, 270, 2),
|
|
189
|
+
# ("H264", "AMF", 450, 480, 270, 2),
|
|
190
|
+
# ("H265", "AMF", 257, 480, 270, 2),
|
|
191
|
+
# 180p
|
|
192
|
+
# ("AV1", "AMF", 200, 320, 180, 1),
|
|
193
|
+
# ("H264", "AMF", 200, 320, 180, 1),
|
|
194
|
+
# ("H265", "AMF", 142, 320, 180, 1),
|
|
195
|
+
# 135p
|
|
196
|
+
# ("H265", "AMF", 101, 240, 135, 1),
|
|
197
|
+
],
|
|
198
|
+
)
|
|
199
|
+
def test_amd_amf_simulcast(
|
|
200
|
+
setup,
|
|
201
|
+
video_codec_type,
|
|
202
|
+
expected_implementation,
|
|
203
|
+
video_bit_rate,
|
|
204
|
+
video_width,
|
|
205
|
+
video_height,
|
|
206
|
+
simulcast_count,
|
|
207
|
+
):
|
|
208
|
+
if not is_codec_supported(video_codec_type, SoraVideoCodecImplementation.AMD_AMF):
|
|
209
|
+
pytest.skip(f"このチップでは {video_codec_type} のエンコードがサポートされていません")
|
|
210
|
+
|
|
211
|
+
signaling_urls = setup.get("signaling_urls")
|
|
212
|
+
channel_id_prefix = setup.get("channel_id_prefix")
|
|
213
|
+
metadata = setup.get("metadata")
|
|
214
|
+
|
|
215
|
+
channel_id = f"{channel_id_prefix}_{__name__}_{sys._getframe().f_code.co_name}_{uuid.uuid4()}"
|
|
216
|
+
|
|
217
|
+
sendonly = SoraClient(
|
|
218
|
+
signaling_urls,
|
|
219
|
+
SoraRole.SENDONLY,
|
|
220
|
+
channel_id,
|
|
221
|
+
simulcast=True,
|
|
222
|
+
audio=False,
|
|
223
|
+
video=True,
|
|
224
|
+
video_codec_type=video_codec_type,
|
|
225
|
+
video_bit_rate=video_bit_rate,
|
|
226
|
+
metadata=metadata,
|
|
227
|
+
video_width=video_width,
|
|
228
|
+
video_height=video_height,
|
|
229
|
+
video_codec_preference=SoraVideoCodecPreference(
|
|
230
|
+
codecs=[
|
|
231
|
+
SoraVideoCodecPreference.Codec(
|
|
232
|
+
type=codec_type_string_to_codec_type(video_codec_type),
|
|
233
|
+
encoder=SoraVideoCodecImplementation.AMD_AMF,
|
|
234
|
+
),
|
|
235
|
+
]
|
|
236
|
+
),
|
|
237
|
+
)
|
|
238
|
+
sendonly.connect(fake_video=True)
|
|
239
|
+
|
|
240
|
+
time.sleep(5)
|
|
241
|
+
|
|
242
|
+
sendonly_stats = sendonly.get_stats()
|
|
243
|
+
|
|
244
|
+
sendonly.disconnect()
|
|
245
|
+
|
|
246
|
+
# "type": "answer" の SDP で Simulcast があるかどうか
|
|
247
|
+
assert sendonly.answer_message is not None
|
|
248
|
+
assert "sdp" in sendonly.answer_message
|
|
249
|
+
assert "a=simulcast:send r0;r1;r2" in sendonly.answer_message["sdp"]
|
|
250
|
+
|
|
251
|
+
# codec が無かったら StopIteration 例外が上がる
|
|
252
|
+
sendonly_codec_stats = next(s for s in sendonly_stats if s.get("type") == "codec")
|
|
253
|
+
assert sendonly_codec_stats["mimeType"] == f"video/{video_codec_type}"
|
|
254
|
+
|
|
255
|
+
# 複数の outbound-rtp 統計情報を取得
|
|
256
|
+
outbound_rtp_stats = [
|
|
257
|
+
s for s in sendonly_stats if s.get("type") == "outbound-rtp" and s.get("kind") == "video"
|
|
258
|
+
]
|
|
259
|
+
# simulcast_count に関係なく統計情報はかならず 3 本出力される
|
|
260
|
+
# これは SDP で rid で ~r0 とかやる減るはず
|
|
261
|
+
assert len(outbound_rtp_stats) == 3
|
|
262
|
+
|
|
263
|
+
# rid でソート
|
|
264
|
+
sorted_stats = sorted(outbound_rtp_stats, key=lambda x: x.get("rid", ""))
|
|
265
|
+
|
|
266
|
+
for i, s in enumerate(sorted_stats):
|
|
267
|
+
assert s["rid"] == f"r{i}"
|
|
268
|
+
# simulcast_count が 2 の場合、rid r2 の bytesSent/packetsSent は 0 or 1 になる
|
|
269
|
+
# simulcast_count が 1 の場合、rid r2 と r1 の bytesSent/packetsSent は 0 or 1 になる
|
|
270
|
+
if i < simulcast_count:
|
|
271
|
+
# 1 本になると simulcastEncodingAdapter がなくなる
|
|
272
|
+
# if simulcast_count > 1:
|
|
273
|
+
# assert "SimulcastEncoderAdapter" in s["encoderImplementation"]
|
|
274
|
+
|
|
275
|
+
# targetBitrate が指定したビットレートの 90% 以上、100% 以下に収まることを確認
|
|
276
|
+
expected_bitrate = video_bit_rate * 1000
|
|
277
|
+
|
|
278
|
+
# パケットが一切送られてこない場合は frame_width/frame_height が含まれないので None になる
|
|
279
|
+
frame_width = s.get("frameWidth")
|
|
280
|
+
frame_height = s.get("frameHeight")
|
|
281
|
+
encoder_implementation = s.get("encoderImplementation")
|
|
282
|
+
|
|
283
|
+
print(
|
|
284
|
+
s["rid"],
|
|
285
|
+
video_codec_type,
|
|
286
|
+
expected_bitrate,
|
|
287
|
+
s["targetBitrate"],
|
|
288
|
+
expected_implementation,
|
|
289
|
+
encoder_implementation,
|
|
290
|
+
frame_width,
|
|
291
|
+
frame_height,
|
|
292
|
+
s["bytesSent"],
|
|
293
|
+
s["packetsSent"],
|
|
294
|
+
)
|
|
295
|
+
assert s["bytesSent"] > 1000
|
|
296
|
+
assert s["packetsSent"] > 5
|
|
297
|
+
# 期待値の 20% 以上、100% 以下に収まることを確認
|
|
298
|
+
assert expected_bitrate * 0.2 <= s["targetBitrate"] <= expected_bitrate
|
|
299
|
+
else:
|
|
300
|
+
# 本来は 0 なのだが、simulcast_count が 1 の場合、
|
|
301
|
+
# packetSent が 0 ではなく 1 や 2 になる場合がある
|
|
302
|
+
# byteSent は 0
|
|
303
|
+
print(
|
|
304
|
+
s["rid"],
|
|
305
|
+
video_codec_type,
|
|
306
|
+
s["bytesSent"],
|
|
307
|
+
s["packetsSent"],
|
|
308
|
+
)
|
|
309
|
+
assert s["bytesSent"] == 0
|
|
310
|
+
assert s["packetsSent"] <= 2
|