sora-sdk 2025.1.0.dev1__tar.gz → 2025.1.0.dev3__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.dev1/src/sora_sdk.egg-info → sora_sdk-2025.1.0.dev3}/PKG-INFO +9 -6
- {sora_sdk-2025.1.0.dev1 → sora_sdk-2025.1.0.dev3}/README.md +6 -4
- {sora_sdk-2025.1.0.dev1 → sora_sdk-2025.1.0.dev3}/buildbase.py +1 -1
- {sora_sdk-2025.1.0.dev1 → sora_sdk-2025.1.0.dev3}/pyproject.toml +5 -4
- {sora_sdk-2025.1.0.dev1 → sora_sdk-2025.1.0.dev3}/src/sora_sdk/sora_sdk_ext.cpython-310-darwin.so +0 -0
- {sora_sdk-2025.1.0.dev1 → sora_sdk-2025.1.0.dev3}/src/sora_sdk/sora_sdk_ext.pyi +10 -1
- {sora_sdk-2025.1.0.dev1 → sora_sdk-2025.1.0.dev3/src/sora_sdk.egg-info}/PKG-INFO +9 -6
- {sora_sdk-2025.1.0.dev1 → sora_sdk-2025.1.0.dev3}/src/sora_sdk.egg-info/SOURCES.txt +3 -0
- sora_sdk-2025.1.0.dev3/tests/test_authz.py +125 -0
- sora_sdk-2025.1.0.dev3/tests/test_authz_simulcast.py +151 -0
- sora_sdk-2025.1.0.dev3/tests/test_degradation_preference.py +217 -0
- {sora_sdk-2025.1.0.dev1 → sora_sdk-2025.1.0.dev3}/tests/test_macos.py +80 -14
- {sora_sdk-2025.1.0.dev1 → sora_sdk-2025.1.0.dev3}/tests/test_openh264.py +75 -13
- {sora_sdk-2025.1.0.dev1 → sora_sdk-2025.1.0.dev3}/tests/test_sendonly_recvonly.py +4 -7
- sora_sdk-2025.1.0.dev3/tests/test_simulcast.py +143 -0
- {sora_sdk-2025.1.0.dev1 → sora_sdk-2025.1.0.dev3}/tests/test_sora_disconnect.py +114 -3
- {sora_sdk-2025.1.0.dev1 → sora_sdk-2025.1.0.dev3}/tests/test_type_disconnect.py +1 -1
- sora_sdk-2025.1.0.dev1/tests/test_simulcast.py +0 -68
- {sora_sdk-2025.1.0.dev1 → sora_sdk-2025.1.0.dev3}/LICENSE +0 -0
- {sora_sdk-2025.1.0.dev1 → sora_sdk-2025.1.0.dev3}/MANIFEST.in +0 -0
- {sora_sdk-2025.1.0.dev1 → sora_sdk-2025.1.0.dev3}/NOTICE.md +0 -0
- {sora_sdk-2025.1.0.dev1 → sora_sdk-2025.1.0.dev3}/pypath.py +0 -0
- {sora_sdk-2025.1.0.dev1 → sora_sdk-2025.1.0.dev3}/run.py +0 -0
- {sora_sdk-2025.1.0.dev1 → sora_sdk-2025.1.0.dev3}/setup.cfg +0 -0
- {sora_sdk-2025.1.0.dev1 → sora_sdk-2025.1.0.dev3}/setup.py +0 -0
- {sora_sdk-2025.1.0.dev1 → sora_sdk-2025.1.0.dev3}/src/sora_sdk/__init__.py +0 -0
- {sora_sdk-2025.1.0.dev1 → sora_sdk-2025.1.0.dev3}/src/sora_sdk/py.typed +0 -0
- {sora_sdk-2025.1.0.dev1 → sora_sdk-2025.1.0.dev3}/src/sora_sdk.egg-info/dependency_links.txt +0 -0
- {sora_sdk-2025.1.0.dev1 → sora_sdk-2025.1.0.dev3}/src/sora_sdk.egg-info/top_level.txt +0 -0
- {sora_sdk-2025.1.0.dev1 → sora_sdk-2025.1.0.dev3}/tests/test_ca_cert.py +0 -0
- {sora_sdk-2025.1.0.dev1 → sora_sdk-2025.1.0.dev3}/tests/test_encoded_transform.py +0 -0
- {sora_sdk-2025.1.0.dev1 → sora_sdk-2025.1.0.dev3}/tests/test_messaging.py +0 -0
- {sora_sdk-2025.1.0.dev1 → sora_sdk-2025.1.0.dev3}/tests/test_messaging_header.py +0 -0
- {sora_sdk-2025.1.0.dev1 → sora_sdk-2025.1.0.dev3}/tests/test_re_offer_re_answer_sdp.py +0 -0
- {sora_sdk-2025.1.0.dev1 → sora_sdk-2025.1.0.dev3}/tests/test_signaling.py +0 -0
- {sora_sdk-2025.1.0.dev1 → sora_sdk-2025.1.0.dev3}/tests/test_signaling_message.py +0 -0
- {sora_sdk-2025.1.0.dev1 → sora_sdk-2025.1.0.dev3}/tests/test_signaling_notify.py +0 -0
- {sora_sdk-2025.1.0.dev1 → sora_sdk-2025.1.0.dev3}/tests/test_type_switched.py +0 -0
- {sora_sdk-2025.1.0.dev1 → sora_sdk-2025.1.0.dev3}/tests/test_vad.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: sora_sdk
|
|
3
|
-
Version: 2025.1.0.
|
|
3
|
+
Version: 2025.1.0.dev3
|
|
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>
|
|
@@ -195,6 +195,7 @@ Requires-Python: >=3.10
|
|
|
195
195
|
Description-Content-Type: text/markdown
|
|
196
196
|
License-File: LICENSE
|
|
197
197
|
License-File: NOTICE.md
|
|
198
|
+
Dynamic: home-page
|
|
198
199
|
|
|
199
200
|
# Sora Python SDK
|
|
200
201
|
|
|
@@ -228,6 +229,8 @@ Please read <https://github.com/shiguredo/oss/blob/master/README.en.md> before u
|
|
|
228
229
|
- Windows / macOS / Linux (Ubuntu) プラットフォームに対応
|
|
229
230
|
- [WebRTC 統計情報](https://www.w3.org/TR/webrtc-stats/) の取得が可能
|
|
230
231
|
- [WebRTC Encoded Transform](https://www.w3.org/TR/webrtc-encoded-transform/) に対応
|
|
232
|
+
- 回線が不安定になった際、解像度とフレームレートどちらを維持するかの設定をする DegradationPreference に対応
|
|
233
|
+
- MAINTAIN_FRAMERATE / MAINTAIN_RESOLUTION / BALANCED が指定できる
|
|
231
234
|
- Intel / Apple / NVIDIA のハードウェアデコーダー/エンコーダーに対応
|
|
232
235
|
- Intel VPL (AV1 / H.264 / H.265)
|
|
233
236
|
- Apple Video Toolbox (H.264 / H.265)
|
|
@@ -282,7 +285,7 @@ PyPI 経由ではインストールできません。
|
|
|
282
285
|
|
|
283
286
|
## システム条件
|
|
284
287
|
|
|
285
|
-
- WebRTC SFU Sora
|
|
288
|
+
- WebRTC SFU Sora 2024.1.0 以降
|
|
286
289
|
- Python 3.10 以上
|
|
287
290
|
|
|
288
291
|
## 対応プラットフォーム
|
|
@@ -347,9 +350,9 @@ Discord へお願いします。
|
|
|
347
350
|
Apache License 2.0
|
|
348
351
|
|
|
349
352
|
```text
|
|
350
|
-
Copyright 2023-
|
|
351
|
-
Copyright 2023-
|
|
352
|
-
Copyright 2023-
|
|
353
|
+
Copyright 2023-2025, tnoho (Original Author)
|
|
354
|
+
Copyright 2023-2025, Wandbox LLC (Original Author)
|
|
355
|
+
Copyright 2023-2025, Shiguredo Inc.
|
|
353
356
|
|
|
354
357
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
355
358
|
you may not use this file except in compliance with the License.
|
|
@@ -30,6 +30,8 @@ Please read <https://github.com/shiguredo/oss/blob/master/README.en.md> before u
|
|
|
30
30
|
- Windows / macOS / Linux (Ubuntu) プラットフォームに対応
|
|
31
31
|
- [WebRTC 統計情報](https://www.w3.org/TR/webrtc-stats/) の取得が可能
|
|
32
32
|
- [WebRTC Encoded Transform](https://www.w3.org/TR/webrtc-encoded-transform/) に対応
|
|
33
|
+
- 回線が不安定になった際、解像度とフレームレートどちらを維持するかの設定をする DegradationPreference に対応
|
|
34
|
+
- MAINTAIN_FRAMERATE / MAINTAIN_RESOLUTION / BALANCED が指定できる
|
|
33
35
|
- Intel / Apple / NVIDIA のハードウェアデコーダー/エンコーダーに対応
|
|
34
36
|
- Intel VPL (AV1 / H.264 / H.265)
|
|
35
37
|
- Apple Video Toolbox (H.264 / H.265)
|
|
@@ -84,7 +86,7 @@ PyPI 経由ではインストールできません。
|
|
|
84
86
|
|
|
85
87
|
## システム条件
|
|
86
88
|
|
|
87
|
-
- WebRTC SFU Sora
|
|
89
|
+
- WebRTC SFU Sora 2024.1.0 以降
|
|
88
90
|
- Python 3.10 以上
|
|
89
91
|
|
|
90
92
|
## 対応プラットフォーム
|
|
@@ -149,9 +151,9 @@ Discord へお願いします。
|
|
|
149
151
|
Apache License 2.0
|
|
150
152
|
|
|
151
153
|
```text
|
|
152
|
-
Copyright 2023-
|
|
153
|
-
Copyright 2023-
|
|
154
|
-
Copyright 2023-
|
|
154
|
+
Copyright 2023-2025, tnoho (Original Author)
|
|
155
|
+
Copyright 2023-2025, Wandbox LLC (Original Author)
|
|
156
|
+
Copyright 2023-2025, Shiguredo Inc.
|
|
155
157
|
|
|
156
158
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
157
159
|
you may not use this file except in compliance with the License.
|
|
@@ -691,7 +691,7 @@ def build_and_install_boost(
|
|
|
691
691
|
):
|
|
692
692
|
version_underscore = version.replace(".", "_")
|
|
693
693
|
archive = download(
|
|
694
|
-
f"https://
|
|
694
|
+
f"https://archives.boost.io/release/{version}/source/boost_{version_underscore}.tar.gz",
|
|
695
695
|
source_dir,
|
|
696
696
|
)
|
|
697
697
|
extract(archive, output_dir=build_dir, output_dirname="boost")
|
|
@@ -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.dev3"
|
|
5
5
|
description = "WebRTC SFU Sora Python SDK"
|
|
6
6
|
readme = "README.md"
|
|
7
7
|
license = { file = "LICENSE" }
|
|
@@ -51,23 +51,24 @@ Discord = "https://discord.gg/shiguredo"
|
|
|
51
51
|
# - https://setuptools.pypa.io/en/latest/build_meta.html
|
|
52
52
|
# - setuptools の build-system サポートについて解説されていて参考になる
|
|
53
53
|
[build-system]
|
|
54
|
-
requires = ["setuptools
|
|
54
|
+
requires = ["setuptools~=75.7", "wheel~=0.45.1"]
|
|
55
55
|
build-backend = "setuptools.build_meta"
|
|
56
56
|
|
|
57
57
|
[tool.uv]
|
|
58
58
|
python-preference = "only-managed"
|
|
59
59
|
dev-dependencies = [
|
|
60
60
|
"nanobind~=2.4.0",
|
|
61
|
-
"setuptools
|
|
61
|
+
"setuptools~=75.7",
|
|
62
62
|
"build~=1.2.2.post1",
|
|
63
63
|
"wheel~=0.45.1",
|
|
64
64
|
"typing-extensions",
|
|
65
65
|
"python-dotenv",
|
|
66
|
-
"numpy
|
|
66
|
+
"numpy",
|
|
67
67
|
"httpx",
|
|
68
68
|
"pytest",
|
|
69
69
|
"ruff",
|
|
70
70
|
"mypy",
|
|
71
|
+
"pyjwt",
|
|
71
72
|
]
|
|
72
73
|
|
|
73
74
|
[tool.ruff]
|
{sora_sdk-2025.1.0.dev1 → sora_sdk-2025.1.0.dev3}/src/sora_sdk/sora_sdk_ext.cpython-310-darwin.so
RENAMED
|
Binary file
|
|
@@ -8,7 +8,7 @@ from numpy.typing import ArrayLike
|
|
|
8
8
|
class Sora:
|
|
9
9
|
def __init__(self, use_hardware_encoder: bool | None = None, openh264: str | None = None) -> None: ...
|
|
10
10
|
|
|
11
|
-
def create_connection(self, signaling_urls: list[str], role: str, channel_id: str, client_id: Optional[str] = None, bundle_id: Optional[str] = None, metadata: Optional[dict] = None, signaling_notify_metadata: Optional[dict] = None, audio_source: Optional[SoraTrackInterface] = None, video_source: Optional[SoraTrackInterface] = None, audio_frame_transformer: Optional[SoraAudioFrameTransformer] = None, video_frame_transformer: Optional[SoraVideoFrameTransformer] = None, audio: Optional[bool] = None, video: Optional[bool] = None, audio_codec_type: Optional[str] = None, video_codec_type: Optional[str] = None, video_bit_rate: Optional[int] = None, audio_bit_rate: Optional[int] = None, video_vp9_params: Optional[dict] = None, video_av1_params: Optional[dict] = None, video_h264_params: Optional[dict] = None, audio_opus_params: Optional[dict] = None, simulcast: Optional[bool] = None, spotlight: Optional[bool] = None, spotlight_number: Optional[int] = None, simulcast_rid: Optional[str] = None, spotlight_focus_rid: Optional[str] = None, spotlight_unfocus_rid: Optional[str] = None, forwarding_filter: Optional[dict] = None, forwarding_filters: Optional[list[dict]] = None, data_channels: Optional[list[dict]] = None, data_channel_signaling: Optional[bool] = None, ignore_disconnect_websocket: Optional[bool] = None, data_channel_signaling_timeout: Optional[int] = None, disconnect_wait_timeout: Optional[int] = None, websocket_close_timeout: Optional[int] = None, websocket_connection_timeout: Optional[int] = None, audio_streaming_language_code: Optional[str] = None, insecure: Optional[bool] = None, client_cert: Optional[bytes] = None, client_key: Optional[bytes] = None, ca_cert: Optional[bytes] = None, proxy_url: Optional[str] = None, proxy_username: Optional[str] = None, proxy_password: Optional[str] = None, proxy_agent: Optional[str] = None) -> SoraConnection: ...
|
|
11
|
+
def create_connection(self, signaling_urls: list[str], role: str, channel_id: str, client_id: Optional[str] = None, bundle_id: Optional[str] = None, metadata: Optional[dict] = None, signaling_notify_metadata: Optional[dict] = None, audio_source: Optional[SoraTrackInterface] = None, video_source: Optional[SoraTrackInterface] = None, audio_frame_transformer: Optional[SoraAudioFrameTransformer] = None, video_frame_transformer: Optional[SoraVideoFrameTransformer] = None, audio: Optional[bool] = None, video: Optional[bool] = None, audio_codec_type: Optional[str] = None, video_codec_type: Optional[str] = None, video_bit_rate: Optional[int] = None, audio_bit_rate: Optional[int] = None, video_vp9_params: Optional[dict] = None, video_av1_params: Optional[dict] = None, video_h264_params: Optional[dict] = None, audio_opus_params: Optional[dict] = None, simulcast: Optional[bool] = None, spotlight: Optional[bool] = None, spotlight_number: Optional[int] = None, simulcast_rid: Optional[str] = None, spotlight_focus_rid: Optional[str] = None, spotlight_unfocus_rid: Optional[str] = None, forwarding_filter: Optional[dict] = None, forwarding_filters: Optional[list[dict]] = None, data_channels: Optional[list[dict]] = None, data_channel_signaling: Optional[bool] = None, ignore_disconnect_websocket: Optional[bool] = None, data_channel_signaling_timeout: Optional[int] = None, disconnect_wait_timeout: Optional[int] = None, websocket_close_timeout: Optional[int] = None, websocket_connection_timeout: Optional[int] = None, audio_streaming_language_code: Optional[str] = None, insecure: Optional[bool] = None, client_cert: Optional[bytes] = None, client_key: Optional[bytes] = None, ca_cert: Optional[bytes] = None, proxy_url: Optional[str] = None, proxy_username: Optional[str] = None, proxy_password: Optional[str] = None, proxy_agent: Optional[str] = None, degradation_preference: Optional[SoraDegradationPreference] = None) -> SoraConnection: ...
|
|
12
12
|
|
|
13
13
|
def create_audio_source(self, channels: int, sample_rate: int) -> SoraAudioSource: ...
|
|
14
14
|
|
|
@@ -156,6 +156,15 @@ class SoraConnection:
|
|
|
156
156
|
@on_data_channel.setter
|
|
157
157
|
def on_data_channel(self, arg: Callable[[str], None], /) -> None: ...
|
|
158
158
|
|
|
159
|
+
class SoraDegradationPreference(enum.IntEnum):
|
|
160
|
+
DISABLED = 0
|
|
161
|
+
|
|
162
|
+
BALANCED = 3
|
|
163
|
+
|
|
164
|
+
MAINTAIN_FRAMERATE = 1
|
|
165
|
+
|
|
166
|
+
MAINTAIN_RESOLUTION = 2
|
|
167
|
+
|
|
159
168
|
class SoraFrameTransformer:
|
|
160
169
|
def enqueue(self, arg: SoraTransformableFrame, /) -> None: ...
|
|
161
170
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: sora_sdk
|
|
3
|
-
Version: 2025.1.0.
|
|
3
|
+
Version: 2025.1.0.dev3
|
|
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>
|
|
@@ -195,6 +195,7 @@ Requires-Python: >=3.10
|
|
|
195
195
|
Description-Content-Type: text/markdown
|
|
196
196
|
License-File: LICENSE
|
|
197
197
|
License-File: NOTICE.md
|
|
198
|
+
Dynamic: home-page
|
|
198
199
|
|
|
199
200
|
# Sora Python SDK
|
|
200
201
|
|
|
@@ -228,6 +229,8 @@ Please read <https://github.com/shiguredo/oss/blob/master/README.en.md> before u
|
|
|
228
229
|
- Windows / macOS / Linux (Ubuntu) プラットフォームに対応
|
|
229
230
|
- [WebRTC 統計情報](https://www.w3.org/TR/webrtc-stats/) の取得が可能
|
|
230
231
|
- [WebRTC Encoded Transform](https://www.w3.org/TR/webrtc-encoded-transform/) に対応
|
|
232
|
+
- 回線が不安定になった際、解像度とフレームレートどちらを維持するかの設定をする DegradationPreference に対応
|
|
233
|
+
- MAINTAIN_FRAMERATE / MAINTAIN_RESOLUTION / BALANCED が指定できる
|
|
231
234
|
- Intel / Apple / NVIDIA のハードウェアデコーダー/エンコーダーに対応
|
|
232
235
|
- Intel VPL (AV1 / H.264 / H.265)
|
|
233
236
|
- Apple Video Toolbox (H.264 / H.265)
|
|
@@ -282,7 +285,7 @@ PyPI 経由ではインストールできません。
|
|
|
282
285
|
|
|
283
286
|
## システム条件
|
|
284
287
|
|
|
285
|
-
- WebRTC SFU Sora
|
|
288
|
+
- WebRTC SFU Sora 2024.1.0 以降
|
|
286
289
|
- Python 3.10 以上
|
|
287
290
|
|
|
288
291
|
## 対応プラットフォーム
|
|
@@ -347,9 +350,9 @@ Discord へお願いします。
|
|
|
347
350
|
Apache License 2.0
|
|
348
351
|
|
|
349
352
|
```text
|
|
350
|
-
Copyright 2023-
|
|
351
|
-
Copyright 2023-
|
|
352
|
-
Copyright 2023-
|
|
353
|
+
Copyright 2023-2025, tnoho (Original Author)
|
|
354
|
+
Copyright 2023-2025, Wandbox LLC (Original Author)
|
|
355
|
+
Copyright 2023-2025, Shiguredo Inc.
|
|
353
356
|
|
|
354
357
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
355
358
|
you may not use this file except in compliance with the License.
|
|
@@ -15,7 +15,10 @@ 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_authz.py
|
|
19
|
+
tests/test_authz_simulcast.py
|
|
18
20
|
tests/test_ca_cert.py
|
|
21
|
+
tests/test_degradation_preference.py
|
|
19
22
|
tests/test_encoded_transform.py
|
|
20
23
|
tests/test_macos.py
|
|
21
24
|
tests/test_messaging.py
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import time
|
|
3
|
+
import uuid
|
|
4
|
+
|
|
5
|
+
import jwt
|
|
6
|
+
import pytest
|
|
7
|
+
from client import SoraClient, SoraRole
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@pytest.mark.skipif(reason="Sora C++ SDK 側の対応が必要")
|
|
11
|
+
def test_sendonly_authz_video_true(setup):
|
|
12
|
+
"""
|
|
13
|
+
- type: connect で audio: true / video: false で繫ぐ
|
|
14
|
+
- 認証成功時の払い出しで audio: false / video: true を払い出す
|
|
15
|
+
"""
|
|
16
|
+
signaling_urls = setup.get("signaling_urls")
|
|
17
|
+
channel_id_prefix = setup.get("channel_id_prefix")
|
|
18
|
+
secret = setup.get("secret")
|
|
19
|
+
|
|
20
|
+
channel_id = f"{channel_id_prefix}_{__name__}_{sys._getframe().f_code.co_name}_{uuid.uuid4()}"
|
|
21
|
+
|
|
22
|
+
access_token = jwt.encode(
|
|
23
|
+
{
|
|
24
|
+
"channel_id": channel_id,
|
|
25
|
+
"audio": False,
|
|
26
|
+
"video": True,
|
|
27
|
+
# 現在時刻 + 300 秒 (5分)
|
|
28
|
+
"exp": int(time.time()) + 300,
|
|
29
|
+
},
|
|
30
|
+
secret,
|
|
31
|
+
algorithm="HS256",
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
sendonly = SoraClient(
|
|
35
|
+
signaling_urls,
|
|
36
|
+
SoraRole.SENDONLY,
|
|
37
|
+
channel_id,
|
|
38
|
+
audio=True,
|
|
39
|
+
video=False,
|
|
40
|
+
metadata={"access_token": access_token},
|
|
41
|
+
)
|
|
42
|
+
sendonly.connect(fake_video=False, fake_audio=True)
|
|
43
|
+
|
|
44
|
+
time.sleep(5)
|
|
45
|
+
|
|
46
|
+
assert sendonly.offer_message is not None
|
|
47
|
+
assert sendonly.offer_message["sdp"] is not None
|
|
48
|
+
assert "VP9" in sendonly.offer_message["sdp"]
|
|
49
|
+
|
|
50
|
+
sendonly_stats = sendonly.get_stats()
|
|
51
|
+
|
|
52
|
+
sendonly.disconnect()
|
|
53
|
+
|
|
54
|
+
# codec が無かったら StopIteration 例外が上がる
|
|
55
|
+
# 統計で video が見つからないので謎挙動になってる
|
|
56
|
+
sendonly_codec_stats = next(s for s in sendonly_stats if s.get("type") == "codec")
|
|
57
|
+
assert sendonly_codec_stats["mimeType"] == "video/VP9"
|
|
58
|
+
|
|
59
|
+
# outbound-rtp が無かったら StopIteration 例外が上がる
|
|
60
|
+
outbound_rtp_stats = next(s for s in sendonly_stats if s.get("type") == "outbound-rtp")
|
|
61
|
+
assert outbound_rtp_stats["encoderImplementation"] == "libvpx"
|
|
62
|
+
assert outbound_rtp_stats["bytesSent"] > 0
|
|
63
|
+
assert outbound_rtp_stats["packetsSent"] > 0
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@pytest.mark.parametrize(
|
|
67
|
+
"video_codec_params",
|
|
68
|
+
[
|
|
69
|
+
# video_codec, encoder_implementation, decoder_implementation
|
|
70
|
+
("VP8", "libvpx"),
|
|
71
|
+
("VP9", "libvpx"),
|
|
72
|
+
("AV1", "libaom"),
|
|
73
|
+
],
|
|
74
|
+
)
|
|
75
|
+
def test_sendonly_authz_video_codec_type(setup, video_codec_params):
|
|
76
|
+
video_codec_type, encoder_implementation = video_codec_params
|
|
77
|
+
|
|
78
|
+
signaling_urls = setup.get("signaling_urls")
|
|
79
|
+
channel_id_prefix = setup.get("channel_id_prefix")
|
|
80
|
+
secret = setup.get("secret")
|
|
81
|
+
|
|
82
|
+
channel_id = f"{channel_id_prefix}_{__name__}_{sys._getframe().f_code.co_name}_{uuid.uuid4()}"
|
|
83
|
+
|
|
84
|
+
access_token = jwt.encode(
|
|
85
|
+
{
|
|
86
|
+
"channel_id": channel_id,
|
|
87
|
+
"video": True,
|
|
88
|
+
"video_codec_type": video_codec_type,
|
|
89
|
+
# 現在時刻 + 300 秒 (5分)
|
|
90
|
+
"exp": int(time.time()) + 300,
|
|
91
|
+
},
|
|
92
|
+
secret,
|
|
93
|
+
algorithm="HS256",
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
sendonly = SoraClient(
|
|
97
|
+
signaling_urls,
|
|
98
|
+
SoraRole.SENDONLY,
|
|
99
|
+
channel_id,
|
|
100
|
+
audio=False,
|
|
101
|
+
video=True,
|
|
102
|
+
metadata={"access_token": access_token},
|
|
103
|
+
)
|
|
104
|
+
sendonly.connect(fake_video=True)
|
|
105
|
+
|
|
106
|
+
time.sleep(5)
|
|
107
|
+
|
|
108
|
+
assert sendonly.offer_message is not None
|
|
109
|
+
assert sendonly.offer_message["sdp"] is not None
|
|
110
|
+
assert video_codec_type in sendonly.offer_message["sdp"]
|
|
111
|
+
|
|
112
|
+
sendonly_stats = sendonly.get_stats()
|
|
113
|
+
|
|
114
|
+
sendonly.disconnect()
|
|
115
|
+
|
|
116
|
+
# codec が無かったら StopIteration 例外が上がる
|
|
117
|
+
# 統計で video が見つからないので謎挙動になってる
|
|
118
|
+
sendonly_codec_stats = next(s for s in sendonly_stats if s.get("type") == "codec")
|
|
119
|
+
assert sendonly_codec_stats["mimeType"] == f"video/{video_codec_type}"
|
|
120
|
+
|
|
121
|
+
# outbound-rtp が無かったら StopIteration 例外が上がる
|
|
122
|
+
outbound_rtp_stats = next(s for s in sendonly_stats if s.get("type") == "outbound-rtp")
|
|
123
|
+
assert outbound_rtp_stats["encoderImplementation"] == encoder_implementation
|
|
124
|
+
assert outbound_rtp_stats["bytesSent"] > 0
|
|
125
|
+
assert outbound_rtp_stats["packetsSent"] > 0
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import time
|
|
3
|
+
import uuid
|
|
4
|
+
|
|
5
|
+
import jwt
|
|
6
|
+
import pytest
|
|
7
|
+
from client import SoraClient, SoraRole
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@pytest.mark.parametrize(
|
|
11
|
+
(
|
|
12
|
+
"video_codec_type",
|
|
13
|
+
"encoder_implementation",
|
|
14
|
+
"video_bit_rate",
|
|
15
|
+
"video_width",
|
|
16
|
+
"video_height",
|
|
17
|
+
),
|
|
18
|
+
[
|
|
19
|
+
# どうやら scaleResolutionDownTo を指定すると規定されたテーブルのビットレートでは足りない模様
|
|
20
|
+
("VP8", "libvpx", 2500, 960, 540),
|
|
21
|
+
("VP9", "libvpx", 2000, 960, 540),
|
|
22
|
+
],
|
|
23
|
+
)
|
|
24
|
+
def test_simulcast_authz_scale_resolution_to(
|
|
25
|
+
setup,
|
|
26
|
+
video_codec_type,
|
|
27
|
+
encoder_implementation,
|
|
28
|
+
video_bit_rate,
|
|
29
|
+
video_width,
|
|
30
|
+
video_height,
|
|
31
|
+
):
|
|
32
|
+
signaling_urls = setup.get("signaling_urls")
|
|
33
|
+
channel_id_prefix = setup.get("channel_id_prefix")
|
|
34
|
+
secret = setup.get("secret")
|
|
35
|
+
|
|
36
|
+
channel_id = f"{channel_id_prefix}_{__name__}_{sys._getframe().f_code.co_name}_{uuid.uuid4()}"
|
|
37
|
+
|
|
38
|
+
simulcast_encodings = [
|
|
39
|
+
{
|
|
40
|
+
"rid": "r0",
|
|
41
|
+
"active": True,
|
|
42
|
+
"scaleResolutionDownTo": {"maxWidth": 640, "maxHeight": 360},
|
|
43
|
+
"scalabilityMode": "L1T1",
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"rid": "r1",
|
|
47
|
+
"active": True,
|
|
48
|
+
"scaleResolutionDownTo": {"maxWidth": 640, "maxHeight": 360},
|
|
49
|
+
"scalabilityMode": "L1T1",
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"rid": "r2",
|
|
53
|
+
"active": True,
|
|
54
|
+
"scaleResolutionDownTo": {"maxWidth": 640, "maxHeight": 360},
|
|
55
|
+
"scalabilityMode": "L1T1",
|
|
56
|
+
},
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
access_token = jwt.encode(
|
|
60
|
+
{
|
|
61
|
+
"channel_id": channel_id,
|
|
62
|
+
"video": True,
|
|
63
|
+
"video_codec_type": video_codec_type,
|
|
64
|
+
"video_bit_rate": video_bit_rate,
|
|
65
|
+
"simulcast": True,
|
|
66
|
+
"simulcast_encodings": simulcast_encodings,
|
|
67
|
+
# 現在時刻 + 300 秒 (5分)
|
|
68
|
+
"exp": int(time.time()) + 300,
|
|
69
|
+
},
|
|
70
|
+
secret,
|
|
71
|
+
algorithm="HS256",
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
sendonly = SoraClient(
|
|
75
|
+
signaling_urls,
|
|
76
|
+
SoraRole.SENDONLY,
|
|
77
|
+
channel_id,
|
|
78
|
+
audio=False,
|
|
79
|
+
video=True,
|
|
80
|
+
metadata={"access_token": access_token},
|
|
81
|
+
video_width=video_width,
|
|
82
|
+
video_height=video_height,
|
|
83
|
+
)
|
|
84
|
+
sendonly.connect(fake_video=True)
|
|
85
|
+
|
|
86
|
+
time.sleep(5)
|
|
87
|
+
|
|
88
|
+
# "type": "offer" の SDP で Simulcast があるかどうか
|
|
89
|
+
assert sendonly.offer_message is not None
|
|
90
|
+
assert sendonly.offer_message["sdp"] is not None
|
|
91
|
+
assert video_codec_type in sendonly.offer_message["sdp"]
|
|
92
|
+
assert "a=simulcast:recv r0;r1;r2" in sendonly.offer_message["sdp"]
|
|
93
|
+
sendonly_stats = sendonly.get_stats()
|
|
94
|
+
|
|
95
|
+
sendonly.disconnect()
|
|
96
|
+
|
|
97
|
+
# "type": "answer" の SDP で Simulcast があるかどうか
|
|
98
|
+
assert sendonly.answer_message is not None
|
|
99
|
+
assert "sdp" in sendonly.answer_message
|
|
100
|
+
assert "a=simulcast:send r0;r1;r2" in sendonly.answer_message["sdp"]
|
|
101
|
+
|
|
102
|
+
# codec が無かったら StopIteration 例外が上がる
|
|
103
|
+
sendonly_codec_stats = next(s for s in sendonly_stats if s.get("type") == "codec")
|
|
104
|
+
assert sendonly_codec_stats["mimeType"] == f"video/{video_codec_type}"
|
|
105
|
+
|
|
106
|
+
# 複数の outbound-rtp 統計情報を取得
|
|
107
|
+
outbound_rtp_stats = [
|
|
108
|
+
s for s in sendonly_stats if s.get("type") == "outbound-rtp" and s.get("kind") == "video"
|
|
109
|
+
]
|
|
110
|
+
# simulcast_count に関係なく統計情報はかならず 3 本出力される
|
|
111
|
+
# これは SDP で rid で ~r0 とかやる減るはず
|
|
112
|
+
assert len(outbound_rtp_stats) == 3
|
|
113
|
+
|
|
114
|
+
# rid でソート
|
|
115
|
+
sorted_stats = sorted(outbound_rtp_stats, key=lambda x: x.get("rid", ""))
|
|
116
|
+
|
|
117
|
+
for i, s in enumerate(sorted_stats):
|
|
118
|
+
assert s["rid"] == f"r{i}"
|
|
119
|
+
assert s["kind"] == "video"
|
|
120
|
+
|
|
121
|
+
# VP8 の場合は scaleResolutionDownTo を指定すると SimulcastEncoderAdapter が無くなる
|
|
122
|
+
# TODO: 念のため他の挙動も確認すること
|
|
123
|
+
if video_codec_type == "VP9":
|
|
124
|
+
assert "SimulcastEncoderAdapter" in s["encoderImplementation"]
|
|
125
|
+
assert encoder_implementation in s["encoderImplementation"]
|
|
126
|
+
|
|
127
|
+
assert s["keyFramesEncoded"] > 0
|
|
128
|
+
assert s["bytesSent"] > 1000
|
|
129
|
+
assert s["packetsSent"] > 20
|
|
130
|
+
|
|
131
|
+
assert s["frameWidth"] == 640
|
|
132
|
+
assert s["frameHeight"] == 352
|
|
133
|
+
|
|
134
|
+
# FIXME:これは libwebrtc 側の挙動を制御できず L1T2 になってしまう
|
|
135
|
+
assert s["scalabilityMode"] == "L1T2"
|
|
136
|
+
|
|
137
|
+
# targetBitrate が指定したビットレートの 90% 以上、100% 以下に収まることを確認
|
|
138
|
+
expected_bitrate = video_bit_rate * 1000
|
|
139
|
+
print(
|
|
140
|
+
s["rid"],
|
|
141
|
+
video_codec_type,
|
|
142
|
+
s["encoderImplementation"],
|
|
143
|
+
expected_bitrate,
|
|
144
|
+
s["targetBitrate"],
|
|
145
|
+
s["frameWidth"],
|
|
146
|
+
s["frameHeight"],
|
|
147
|
+
s["bytesSent"],
|
|
148
|
+
s["packetsSent"],
|
|
149
|
+
)
|
|
150
|
+
# 期待値の 20% 以上、100% 以下に収まることを確認
|
|
151
|
+
assert expected_bitrate * 0.2 <= s["targetBitrate"] <= expected_bitrate
|