Skip to content

Commit

Permalink
Merge pull request #77 from shiguredo/feature/examples-e2e-test
Browse files Browse the repository at this point in the history
examples E2E テスト第二弾
  • Loading branch information
voluntas committed Aug 14, 2024
2 parents 95016f4 + 02933a1 commit 0f0e5f1
Show file tree
Hide file tree
Showing 12 changed files with 418 additions and 86 deletions.
12 changes: 9 additions & 3 deletions .github/workflows/examples-e2e-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,10 @@ jobs:
- run: rye pin ${{ matrix.python_version }}
- run: rye sync
- run: rye run pytest tests/test_openh264.py -s
- run: rye run pytest tests/test_messaging_sendonly.py -s
- run: rye run pytest tests/test_messaging.py -s
- run: rye run pytest tests/test_sendonly_recvonly.py -s
- run: rye run pytest tests/test_vad.py -s
- run: rye run pytest tests/test_switched.py -s

e2e_macos_test:
strategy:
Expand Down Expand Up @@ -69,8 +71,10 @@ jobs:
- run: rye sync
- run: rye run pytest tests/test_macos.py -s
- run: rye run pytest tests/test_openh264.py -s
- run: rye run pytest tests/test_messaging_sendonly.py -s
- run: rye run pytest tests/test_messaging.py -s
- run: rye run pytest tests/test_sendonly_recvonly.py -s
- run: rye run pytest tests/test_vad.py -s
- run: rye run pytest tests/test_switched.py -s

e2e_windows_test:
strategy:
Expand Down Expand Up @@ -99,8 +103,10 @@ jobs:
- uses: eifinger/setup-rye@v4
- run: rye pin ${{ matrix.python_version }}
- run: rye sync
- run: rye run pytest tests/test_messaging_sendonly.py -s
- run: rye run pytest tests/test_messaging.py -s
- run: rye run pytest tests/test_sendonly_recvonly.py -s
- run: rye run pytest tests/test_vad.py -s
- run: rye run pytest tests/test_switched.py -s

slack_notify_succeeded:
needs: [e2e_ubuntu_test, e2e_macos_test, e2e_windows_test]
Expand Down
1 change: 1 addition & 0 deletions examples/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ requires-python = ">= 3.10"
[project.scripts]
media_sendonly = "media.sendonly:sendonly"
media_recvonly = "media.recvonly:recvonly"
media_vad = "media.vad:vad"
messaging_sendrecv = "messaging.sendrecv:sendrecv"
messaging_sendonly = "messaging.sendonly:sendonly"
messaging_recvonly = "messaging.recvonly:recvonly"
Expand Down
92 changes: 68 additions & 24 deletions examples/src/media/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ def __init__(
video: Optional[bool] = None,
video_codec_type: Optional[str] = None,
video_bit_rate: Optional[int] = None,
data_channel_signaling: Optional[bool] = None,
openh264_path: Optional[str] = None,
use_hwa: bool = False,
audio_channels: int = 1,
Expand All @@ -58,38 +59,44 @@ def __init__(
:param audio_sample_rate: 音声サンプリングレート(デフォルト: 16000)
:param video_capture: カメラからのビデオキャプチャ
"""
self.audio_channels: int = audio_channels
self.audio_sample_rate: int = audio_sample_rate
self._signaling_urls: list[str] = signaling_urls
self._channel_id: str = channel_id

self._audio_channels: int = audio_channels
self._audio_sample_rate: int = audio_sample_rate

self._sora: Sora = Sora(openh264=openh264_path, use_hardware_encoder=use_hwa)

self._fake_audio_thread: Optional[threading.Thread] = None
self._fake_video_thread: Optional[threading.Thread] = None

self._audio_source = self._sora.create_audio_source(
self.audio_channels, self.audio_sample_rate
self._audio_channels, self._audio_sample_rate
)
self._video_source = self._sora.create_video_source()

self._connection: SoraConnection = self._sora.create_connection(
signaling_urls=signaling_urls,
role="sendonly",
channel_id=channel_id,
metadata=metadata,
audio=audio,
video=video,
video_codec_type=video_codec_type,
video_bit_rate=video_bit_rate,
metadata=metadata,
data_channel_signaling=data_channel_signaling,
audio_source=self._audio_source,
video_source=self._video_source,
)
self._connection_id: Optional[str] = None

self._connected: Event = Event()
self._switched: bool = False
self._closed: Event = Event()
self._default_connection_timeout_s: float = 10.0

self._connection.on_set_offer = self._on_set_offer
self._connection.on_switched = self._on_switched
self._connection.on_notify = self._on_notify
self._connection.on_disconnect = self._on_disconnect

Expand All @@ -107,12 +114,10 @@ def connect(self, fake_audio=False, fake_video=False) -> None:
if fake_audio:
self._fake_audio_thread = threading.Thread(target=self._fake_audio_loop, daemon=True)
self._fake_audio_thread.start()
print("Fake audio thread started.")

if fake_video:
self._fake_video_thread = threading.Thread(target=self._fake_video_loop, daemon=True)
self._fake_video_thread.start()
print("Fake video thread started.")

assert self._connected.wait(
self._default_connection_timeout_s
Expand All @@ -124,8 +129,15 @@ def disconnect(self) -> None:

def get_stats(self):
raw_stats = self._connection.get_stats()
stats = json.loads(raw_stats)
return stats
return json.loads(raw_stats)

@property
def connected(self) -> bool:
return self._connected.is_set()

@property
def switched(self) -> bool:
return self._switched

def _fake_audio_loop(self):
while not self._closed.is_set():
Expand All @@ -137,6 +149,22 @@ def _fake_video_loop(self):
time.sleep(1.0 / 30)
self._video_source.on_captured(numpy.zeros((480, 640, 3), dtype=numpy.uint8))

def _on_set_offer(self, raw_message: str) -> None:
"""
オファー設定イベントを処理します。
:param raw_message: オファーを含む生のメッセージ
"""
message: dict[str, Any] = json.loads(raw_message)
if message["type"] == "offer":
self._connection_id = message["connection_id"]

def _on_switched(self, raw_message: str) -> None:
message = json.loads(raw_message)
if message["type"] == "switched":
print(f"Switched to DataChannel Signaling: connection_id={self._connection_id}")
self._switched = True

def _on_notify(self, raw_message: str) -> None:
"""
Sora からの通知イベントを処理します。
Expand All @@ -152,16 +180,6 @@ def _on_notify(self, raw_message: str) -> None:
print(f"Connected Sora: connection_id={self._connection_id}")
self._connected.set()

def _on_set_offer(self, raw_message: str) -> None:
"""
オファー設定イベントを処理します。
:param raw_message: オファーを含む生のメッセージ
"""
message: dict[str, Any] = json.loads(raw_message)
if message["type"] == "offer":
self._connection_id = message["connection_id"]

def _on_disconnect(self, error_code: SoraSignalingErrorCode, message: str) -> None:
"""
切断イベントを処理します。
Expand All @@ -179,7 +197,7 @@ def _on_disconnect(self, error_code: SoraSignalingErrorCode, message: str) -> No
if self._fake_video_thread is not None:
self._fake_video_thread.join(timeout=10)

def _callback(
def _sounddevice_input_stream_callback(
self, indata: ndarray, frames: int, time: Any, status: sounddevice.CallbackFlags
) -> None:
"""
Expand All @@ -197,10 +215,10 @@ def run(self) -> None:
ビデオフレームの送信と音声の送信を行うメインループ。
"""
with sounddevice.InputStream(
samplerate=self.audio_sample_rate,
channels=self.audio_channels,
samplerate=self._audio_sample_rate,
channels=self._audio_channels,
dtype="int16",
callback=self._callback,
callback=self._sounddevice_input_stream_callback,
):
self.connect()
try:
Expand All @@ -224,6 +242,7 @@ def __init__(
signaling_urls: list[str],
channel_id: str,
metadata: Optional[dict[str, Any]] = None,
data_channel_signaling: Optional[bool] = None,
openh264_path: Optional[str] = None,
use_hwa: Optional[bool] = False,
output_frequency: int = 16000,
Expand All @@ -242,6 +261,9 @@ def __init__(
:param output_frequency: 音声出力周波数(Hz)、デフォルトは 16000
:param output_channels: 音声出力チャンネル数、デフォルトは 1
"""
self._signaling_urls: list[str] = signaling_urls
self._channel_id: str = channel_id

self._output_frequency: int = output_frequency
self._output_channels: int = output_channels

Expand All @@ -251,10 +273,12 @@ def __init__(
role="recvonly",
channel_id=channel_id,
metadata=metadata,
data_channel_signaling=data_channel_signaling,
)
self._connection_id: Optional[str] = None

self._connected: Event = Event()
self._switched: bool = False
self._closed: Event = Event()
self._default_connection_timeout_s: float = 10.0

Expand All @@ -264,6 +288,7 @@ def __init__(
self._q_out: queue.Queue = queue.Queue()

self._connection.on_set_offer = self._on_set_offer
self._connection.on_switched = self._on_switched
self._connection.on_notify = self._on_notify
self._connection.on_disconnect = self._on_disconnect
self._connection.on_track = self._on_track
Expand All @@ -286,8 +311,21 @@ def disconnect(self) -> None:

def get_stats(self):
raw_stats = self._connection.get_stats()
stats = json.loads(raw_stats)
return stats
return json.loads(raw_stats)

@property
def connected(self) -> bool:
return self._connected.is_set()

@property
def switched(self) -> bool:
"""データチャネルシグナリングへの切り替えが完了しているかどうかを示すブール値。"""
return self._switched

@property
def closed(self):
"""接続が閉じられているかどうかを示すブール値。"""
return self._closed.is_set()

def _on_set_offer(self, raw_message: str) -> None:
"""
Expand All @@ -299,6 +337,12 @@ def _on_set_offer(self, raw_message: str) -> None:
if message["type"] == "offer":
self._connection_id = message["connection_id"]

def _on_switched(self, raw_message: str) -> None:
message = json.loads(raw_message)
if message["type"] == "switched":
print(f"Switched to DataChannel Signaling: connection_id={self._connection_id}")
self._switched = True

def _on_notify(self, raw_message: str) -> None:
"""
Sora からの通知イベントを処理します。
Expand Down
Loading

0 comments on commit 0f0e5f1

Please sign in to comment.