sora-sdk 2025.1.0.dev12__tar.gz → 2025.1.0.dev14__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.

Files changed (45) hide show
  1. {sora_sdk-2025.1.0.dev12/src/sora_sdk.egg-info → sora_sdk-2025.1.0.dev14}/PKG-INFO +1 -1
  2. {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev14}/pyproject.toml +1 -1
  3. {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev14}/src/sora_sdk/sora_sdk_ext.cpython-311-darwin.so +0 -0
  4. {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev14}/src/sora_sdk/sora_sdk_ext.pyi +8 -0
  5. {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev14/src/sora_sdk.egg-info}/PKG-INFO +1 -1
  6. {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev14}/src/sora_sdk.egg-info/SOURCES.txt +2 -0
  7. sora_sdk-2025.1.0.dev14/tests/test_amd_amf.py +310 -0
  8. {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev14}/tests/test_intel_vpl.py +72 -206
  9. sora_sdk-2025.1.0.dev14/tests/test_memory_leak.py +25 -0
  10. {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev14}/tests/test_nvidia_video_codec_sdk.py +102 -135
  11. {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev14}/LICENSE +0 -0
  12. {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev14}/MANIFEST.in +0 -0
  13. {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev14}/NOTICE.md +0 -0
  14. {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev14}/README.md +0 -0
  15. {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev14}/buildbase.py +0 -0
  16. {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev14}/pypath.py +0 -0
  17. {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev14}/run.py +0 -0
  18. {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev14}/setup.cfg +0 -0
  19. {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev14}/setup.py +0 -0
  20. {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev14}/src/sora_sdk/__init__.py +0 -0
  21. {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev14}/src/sora_sdk/py.typed +0 -0
  22. {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev14}/src/sora_sdk.egg-info/dependency_links.txt +0 -0
  23. {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev14}/src/sora_sdk.egg-info/top_level.txt +0 -0
  24. {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev14}/tests/test_apple_video_toolbox.py +0 -0
  25. {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev14}/tests/test_authz.py +0 -0
  26. {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev14}/tests/test_authz_simulcast.py +0 -0
  27. {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev14}/tests/test_ca_cert.py +0 -0
  28. {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev14}/tests/test_capability.py +0 -0
  29. {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev14}/tests/test_codec_preference.py +0 -0
  30. {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev14}/tests/test_degradation_preference.py +0 -0
  31. {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev14}/tests/test_encoded_transform.py +0 -0
  32. {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev14}/tests/test_messaging.py +0 -0
  33. {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev14}/tests/test_messaging_header.py +0 -0
  34. {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev14}/tests/test_openh264.py +0 -0
  35. {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev14}/tests/test_opus.py +0 -0
  36. {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev14}/tests/test_re_offer_re_answer_sdp.py +0 -0
  37. {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev14}/tests/test_sendonly_recvonly.py +0 -0
  38. {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev14}/tests/test_signaling.py +0 -0
  39. {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev14}/tests/test_signaling_message.py +0 -0
  40. {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev14}/tests/test_signaling_notify.py +0 -0
  41. {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev14}/tests/test_simulcast.py +0 -0
  42. {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev14}/tests/test_sora_disconnect.py +0 -0
  43. {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev14}/tests/test_type_disconnect.py +0 -0
  44. {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev14}/tests/test_type_switched.py +0 -0
  45. {sora_sdk-2025.1.0.dev12 → sora_sdk-2025.1.0.dev14}/tests/test_vad.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: sora_sdk
3
- Version: 2025.1.0.dev12
3
+ Version: 2025.1.0.dev14
4
4
  Summary: WebRTC SFU Sora Python SDK
5
5
  Home-page: https://github.com/shiguredo/sora-python-sdk
6
6
  Author-email: "Shiguredo Inc." <contact+pypi@shiguredo.jp>
@@ -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.dev12"
4
+ version = "2025.1.0.dev14"
5
5
  description = "WebRTC SFU Sora Python SDK"
6
6
  readme = "README.md"
7
7
  license = { file = "LICENSE" }
@@ -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
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: sora_sdk
3
- Version: 2025.1.0.dev12
3
+ Version: 2025.1.0.dev14
4
4
  Summary: WebRTC SFU Sora Python SDK
5
5
  Home-page: https://github.com/shiguredo/sora-python-sdk
6
6
  Author-email: "Shiguredo Inc." <contact+pypi@shiguredo.jp>
@@ -15,6 +15,7 @@ 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
@@ -24,6 +25,7 @@ tests/test_codec_preference.py
24
25
  tests/test_degradation_preference.py
25
26
  tests/test_encoded_transform.py
26
27
  tests/test_intel_vpl.py
28
+ tests/test_memory_leak.py
27
29
  tests/test_messaging.py
28
30
  tests/test_messaging_header.py
29
31
  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