livekit 1.2.1__py3-none-win_arm64.whl
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.
- livekit/rtc/__init__.py +264 -0
- livekit/rtc/_ffi_client.py +321 -0
- livekit/rtc/_proto/__init__.py +0 -0
- livekit/rtc/_proto/audio_frame_pb2.py +159 -0
- livekit/rtc/_proto/audio_frame_pb2.pyi +1560 -0
- livekit/rtc/_proto/capture_pb2.py +70 -0
- livekit/rtc/_proto/capture_pb2.pyi +559 -0
- livekit/rtc/_proto/data_stream_pb2.py +171 -0
- livekit/rtc/_proto/data_stream_pb2.pyi +1498 -0
- livekit/rtc/_proto/data_track_pb2.py +120 -0
- livekit/rtc/_proto/data_track_pb2.pyi +1073 -0
- livekit/rtc/_proto/e2ee_pb2.py +81 -0
- livekit/rtc/_proto/e2ee_pb2.pyi +591 -0
- livekit/rtc/_proto/ffi_pb2.py +55 -0
- livekit/rtc/_proto/ffi_pb2.pyi +1246 -0
- livekit/rtc/_proto/handle_pb2.py +27 -0
- livekit/rtc/_proto/handle_pb2.pyi +51 -0
- livekit/rtc/_proto/participant_pb2.py +45 -0
- livekit/rtc/_proto/participant_pb2.pyi +313 -0
- livekit/rtc/_proto/room_pb2.py +311 -0
- livekit/rtc/_proto/room_pb2.pyi +3136 -0
- livekit/rtc/_proto/rpc_pb2.py +47 -0
- livekit/rtc/_proto/rpc_pb2.pyi +261 -0
- livekit/rtc/_proto/stats_pb2.py +123 -0
- livekit/rtc/_proto/stats_pb2.pyi +1514 -0
- livekit/rtc/_proto/track_pb2.py +76 -0
- livekit/rtc/_proto/track_pb2.pyi +544 -0
- livekit/rtc/_proto/track_publication_pb2.py +39 -0
- livekit/rtc/_proto/track_publication_pb2.pyi +145 -0
- livekit/rtc/_proto/video_frame_pb2.py +97 -0
- livekit/rtc/_proto/video_frame_pb2.pyi +838 -0
- livekit/rtc/_utils.py +187 -0
- livekit/rtc/apm.py +119 -0
- livekit/rtc/audio_filter.py +23 -0
- livekit/rtc/audio_frame.py +242 -0
- livekit/rtc/audio_mixer.py +247 -0
- livekit/rtc/audio_resampler.py +174 -0
- livekit/rtc/audio_source.py +191 -0
- livekit/rtc/audio_stream.py +354 -0
- livekit/rtc/data_stream.py +640 -0
- livekit/rtc/data_track.py +296 -0
- livekit/rtc/e2ee.py +320 -0
- livekit/rtc/encoded_video.py +127 -0
- livekit/rtc/event_emitter.py +215 -0
- livekit/rtc/frame_processor.py +37 -0
- livekit/rtc/jupyter.py +68 -0
- livekit/rtc/log.py +3 -0
- livekit/rtc/media_devices.py +596 -0
- livekit/rtc/participant.py +1171 -0
- livekit/rtc/platform_audio.py +426 -0
- livekit/rtc/py.typed +0 -0
- livekit/rtc/resources/LICENSE.md +4055 -0
- livekit/rtc/resources/__init__.py +1 -0
- livekit/rtc/resources/jupyter-html/index.html +101 -0
- livekit/rtc/resources/livekit_ffi.dll +0 -0
- livekit/rtc/resources/livekit_ffi.h +42 -0
- livekit/rtc/room.py +1304 -0
- livekit/rtc/rpc.py +244 -0
- livekit/rtc/synchronizer.py +202 -0
- livekit/rtc/track.py +264 -0
- livekit/rtc/track_publication.py +145 -0
- livekit/rtc/transcription.py +19 -0
- livekit/rtc/utils.py +138 -0
- livekit/rtc/version.py +1 -0
- livekit/rtc/video_frame.py +347 -0
- livekit/rtc/video_source.py +74 -0
- livekit/rtc/video_stream.py +191 -0
- livekit-1.2.1.dist-info/METADATA +80 -0
- livekit-1.2.1.dist-info/RECORD +71 -0
- livekit-1.2.1.dist-info/WHEEL +5 -0
- livekit-1.2.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
# Copyright 2023 LiveKit, Inc.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""
|
|
16
|
+
PlatformAudio - Platform audio device management via WebRTC's ADM
|
|
17
|
+
|
|
18
|
+
PlatformAudio provides access to the platform's audio devices (microphones and
|
|
19
|
+
speakers) via WebRTC's Audio Device Module (ADM). This is the recommended way
|
|
20
|
+
to handle audio in most applications because:
|
|
21
|
+
|
|
22
|
+
- Built-in echo cancellation (AEC), noise suppression (NS), and auto gain control (AGC)
|
|
23
|
+
- Automatic device enumeration and selection
|
|
24
|
+
- Efficient hardware-accelerated audio processing on supported platforms
|
|
25
|
+
- No need for external audio libraries
|
|
26
|
+
|
|
27
|
+
For custom audio processing pipelines where you need direct access to audio frames,
|
|
28
|
+
use the synthetic mode with AudioSource instead. See AudioSource for more details.
|
|
29
|
+
|
|
30
|
+
# Usage
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
from livekit import rtc
|
|
34
|
+
|
|
35
|
+
# Create PlatformAudio instance (enables ADM)
|
|
36
|
+
platform_audio = rtc.PlatformAudio()
|
|
37
|
+
|
|
38
|
+
# List available devices
|
|
39
|
+
print("Microphones:")
|
|
40
|
+
for device in platform_audio.recording_devices():
|
|
41
|
+
print(f" [{device.index}] {device.name} (ID: {device.id})")
|
|
42
|
+
|
|
43
|
+
print("Speakers:")
|
|
44
|
+
for device in platform_audio.playout_devices():
|
|
45
|
+
print(f" [{device.index}] {device.name} (ID: {device.id})")
|
|
46
|
+
|
|
47
|
+
# Select specific devices (optional - uses default if not called)
|
|
48
|
+
platform_audio.set_recording_device(mic_device.id)
|
|
49
|
+
platform_audio.set_playout_device(speaker_device.id)
|
|
50
|
+
|
|
51
|
+
# Create audio source for publishing
|
|
52
|
+
audio_source = platform_audio.create_audio_source()
|
|
53
|
+
|
|
54
|
+
# Create and publish track
|
|
55
|
+
track = rtc.LocalAudioTrack.create_audio_track("microphone", audio_source)
|
|
56
|
+
await room.local_participant.publish_track(track)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
# Limitations
|
|
60
|
+
|
|
61
|
+
- No direct access to captured audio frames (ADM sends directly to WebRTC)
|
|
62
|
+
- For frame-level processing, use synthetic mode with AudioSource.capture_frame()
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
from __future__ import annotations
|
|
66
|
+
|
|
67
|
+
from dataclasses import dataclass
|
|
68
|
+
from typing import List, Optional
|
|
69
|
+
|
|
70
|
+
from ._ffi_client import FfiHandle, FfiClient
|
|
71
|
+
from ._proto import audio_frame_pb2 as proto_audio_frame
|
|
72
|
+
from ._proto import ffi_pb2 as proto_ffi
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class PlatformAudioError(Exception):
|
|
76
|
+
"""Exception raised when PlatformAudio operations fail."""
|
|
77
|
+
|
|
78
|
+
pass
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@dataclass
|
|
82
|
+
class AudioDeviceInfo:
|
|
83
|
+
"""Information about an audio device.
|
|
84
|
+
|
|
85
|
+
Attributes:
|
|
86
|
+
index: Device index (0-based). Note: indices can change when devices are
|
|
87
|
+
added/removed, so prefer using `id` for device selection.
|
|
88
|
+
name: Device name as reported by the operating system.
|
|
89
|
+
id: Platform-specific unique device identifier (GUID). This is stable across
|
|
90
|
+
device additions/removals and should be preferred over index for device
|
|
91
|
+
selection.
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
index: int
|
|
95
|
+
name: str
|
|
96
|
+
id: str
|
|
97
|
+
|
|
98
|
+
@classmethod
|
|
99
|
+
def _from_proto(cls, proto: proto_audio_frame.AudioDeviceInfo) -> "AudioDeviceInfo":
|
|
100
|
+
return cls(index=proto.index, name=proto.name, id=proto.guid)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@dataclass
|
|
104
|
+
class PlatformAudioOptions:
|
|
105
|
+
"""Audio processing options for PlatformAudio.
|
|
106
|
+
|
|
107
|
+
These options configure WebRTC's audio processing pipeline when using
|
|
108
|
+
PlatformAudio for microphone capture.
|
|
109
|
+
|
|
110
|
+
Attributes:
|
|
111
|
+
echo_cancellation: Enable acoustic echo cancellation (AEC) to remove
|
|
112
|
+
echo from speaker playback. Default: True.
|
|
113
|
+
noise_suppression: Enable noise suppression to remove background noise.
|
|
114
|
+
Default: True.
|
|
115
|
+
auto_gain_control: Enable automatic gain control to normalize audio levels.
|
|
116
|
+
Default: True.
|
|
117
|
+
prefer_hardware: Prefer hardware audio processing when available.
|
|
118
|
+
- iOS: Uses Voice Processing I/O (VPIO) for low-latency processing
|
|
119
|
+
- Android: Hardware support varies by device
|
|
120
|
+
- Desktop: Generally not available
|
|
121
|
+
Default: True on iOS, False on other platforms.
|
|
122
|
+
"""
|
|
123
|
+
|
|
124
|
+
echo_cancellation: bool = True
|
|
125
|
+
noise_suppression: bool = True
|
|
126
|
+
auto_gain_control: bool = True
|
|
127
|
+
prefer_hardware: bool = False
|
|
128
|
+
|
|
129
|
+
def _to_proto(self) -> proto_audio_frame.AudioSourceOptions:
|
|
130
|
+
return proto_audio_frame.AudioSourceOptions(
|
|
131
|
+
echo_cancellation=self.echo_cancellation,
|
|
132
|
+
noise_suppression=self.noise_suppression,
|
|
133
|
+
auto_gain_control=self.auto_gain_control,
|
|
134
|
+
prefer_hardware=self.prefer_hardware,
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class PlatformAudioSource:
|
|
139
|
+
"""Audio source backed by PlatformAudio (WebRTC ADM).
|
|
140
|
+
|
|
141
|
+
This source captures audio from the selected microphone via the platform's
|
|
142
|
+
Audio Device Module. Unlike AudioSource (synthetic mode), frames are captured
|
|
143
|
+
and sent directly by the ADM - there is no `capture_frame()` method.
|
|
144
|
+
|
|
145
|
+
Use this with LocalAudioTrack.create_audio_track() to publish microphone audio.
|
|
146
|
+
|
|
147
|
+
Note: This class is created via PlatformAudio.create_audio_source() and should
|
|
148
|
+
not be instantiated directly.
|
|
149
|
+
|
|
150
|
+
Resource Management:
|
|
151
|
+
Call `close()` when done to immediately release native resources. If not
|
|
152
|
+
called, resources are released when the object is garbage collected, but
|
|
153
|
+
GC timing is non-deterministic and may cause issues on some platforms
|
|
154
|
+
(especially Windows) where audio device handles must be released promptly.
|
|
155
|
+
"""
|
|
156
|
+
|
|
157
|
+
def __init__(self, ffi_handle: FfiHandle, info: proto_audio_frame.AudioSourceInfo):
|
|
158
|
+
self._ffi_handle = ffi_handle
|
|
159
|
+
self._info = info
|
|
160
|
+
|
|
161
|
+
@property
|
|
162
|
+
def _handle(self) -> int:
|
|
163
|
+
"""Internal FFI handle for use with LocalAudioTrack.create_audio_track()."""
|
|
164
|
+
return self._ffi_handle.handle
|
|
165
|
+
|
|
166
|
+
def close(self) -> None:
|
|
167
|
+
"""Release the native audio source resources.
|
|
168
|
+
|
|
169
|
+
Call this method when you are done using the audio source to immediately
|
|
170
|
+
release the underlying native handle. This is especially important on
|
|
171
|
+
Windows where audio device handles must be released promptly to avoid
|
|
172
|
+
interfering with other audio operations.
|
|
173
|
+
|
|
174
|
+
If `close()` is not called, resources will be released when the object
|
|
175
|
+
is garbage collected. However, Python's garbage collection timing is
|
|
176
|
+
non-deterministic, which can lead to:
|
|
177
|
+
- Audio device contention if creating new audio sources before old ones
|
|
178
|
+
are collected
|
|
179
|
+
- Test failures when running multiple audio tests in sequence
|
|
180
|
+
- Resource leaks in long-running applications that frequently create
|
|
181
|
+
and discard audio sources
|
|
182
|
+
|
|
183
|
+
It is safe to call `close()` multiple times; subsequent calls are no-ops.
|
|
184
|
+
"""
|
|
185
|
+
self._ffi_handle.dispose()
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
class PlatformAudio:
|
|
189
|
+
"""Platform audio device management via WebRTC's Audio Device Module (ADM).
|
|
190
|
+
|
|
191
|
+
PlatformAudio provides the recommended way to handle audio capture and playback
|
|
192
|
+
in LiveKit applications. It uses WebRTC's ADM for efficient, hardware-accelerated
|
|
193
|
+
audio processing with built-in echo cancellation, noise suppression, and
|
|
194
|
+
automatic gain control.
|
|
195
|
+
|
|
196
|
+
Key features:
|
|
197
|
+
- Device enumeration: List available microphones and speakers
|
|
198
|
+
- Device selection: Choose specific input/output devices
|
|
199
|
+
- Audio processing: Built-in AEC, NS, and AGC
|
|
200
|
+
- Automatic playout: Received audio is automatically played through speakers
|
|
201
|
+
|
|
202
|
+
Example:
|
|
203
|
+
```python
|
|
204
|
+
# Create PlatformAudio (keeps ADM alive)
|
|
205
|
+
platform_audio = rtc.PlatformAudio()
|
|
206
|
+
|
|
207
|
+
# Enumerate and select devices
|
|
208
|
+
mics = platform_audio.recording_devices()
|
|
209
|
+
platform_audio.set_recording_device(mics[0].id)
|
|
210
|
+
|
|
211
|
+
# Create source and publish track
|
|
212
|
+
source = platform_audio.create_audio_source()
|
|
213
|
+
track = rtc.LocalAudioTrack.create_audio_track("mic", source)
|
|
214
|
+
await room.local_participant.publish_track(track)
|
|
215
|
+
|
|
216
|
+
# When done, close resources
|
|
217
|
+
source.close()
|
|
218
|
+
platform_audio.close()
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Resource Management:
|
|
222
|
+
Call `close()` when done to immediately release native ADM resources.
|
|
223
|
+
If not called, resources are released via garbage collection, but GC
|
|
224
|
+
timing is non-deterministic. On Windows especially, failing to promptly
|
|
225
|
+
release ADM resources can cause audio device contention and interfere
|
|
226
|
+
with subsequent audio operations (including synthetic AudioSource usage).
|
|
227
|
+
"""
|
|
228
|
+
|
|
229
|
+
def __init__(self) -> None:
|
|
230
|
+
"""Initialize PlatformAudio and enable the Audio Device Module.
|
|
231
|
+
|
|
232
|
+
Raises:
|
|
233
|
+
PlatformAudioError: If ADM initialization fails.
|
|
234
|
+
"""
|
|
235
|
+
req = proto_ffi.FfiRequest()
|
|
236
|
+
req.new_platform_audio.SetInParent()
|
|
237
|
+
|
|
238
|
+
resp = FfiClient.instance.request(req)
|
|
239
|
+
|
|
240
|
+
if resp.new_platform_audio.HasField("error"):
|
|
241
|
+
raise PlatformAudioError(
|
|
242
|
+
f"Failed to initialize PlatformAudio: {resp.new_platform_audio.error}"
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
self._info = resp.new_platform_audio.platform_audio.info
|
|
246
|
+
self._ffi_handle = FfiHandle(resp.new_platform_audio.platform_audio.handle.id)
|
|
247
|
+
|
|
248
|
+
def recording_devices(self) -> List[AudioDeviceInfo]:
|
|
249
|
+
"""Get available recording devices (microphones).
|
|
250
|
+
|
|
251
|
+
Returns:
|
|
252
|
+
List of AudioDeviceInfo for each available microphone.
|
|
253
|
+
|
|
254
|
+
Raises:
|
|
255
|
+
PlatformAudioError: If device enumeration fails.
|
|
256
|
+
"""
|
|
257
|
+
req = proto_ffi.FfiRequest()
|
|
258
|
+
req.get_audio_devices.platform_audio_handle = self._ffi_handle.handle
|
|
259
|
+
|
|
260
|
+
resp = FfiClient.instance.request(req)
|
|
261
|
+
|
|
262
|
+
if resp.get_audio_devices.HasField("error") and resp.get_audio_devices.error:
|
|
263
|
+
raise PlatformAudioError(
|
|
264
|
+
f"Failed to enumerate recording devices: {resp.get_audio_devices.error}"
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
return [AudioDeviceInfo._from_proto(d) for d in resp.get_audio_devices.recording_devices]
|
|
268
|
+
|
|
269
|
+
def playout_devices(self) -> List[AudioDeviceInfo]:
|
|
270
|
+
"""Get available playout devices (speakers/headphones).
|
|
271
|
+
|
|
272
|
+
Returns:
|
|
273
|
+
List of AudioDeviceInfo for each available speaker.
|
|
274
|
+
|
|
275
|
+
Raises:
|
|
276
|
+
PlatformAudioError: If device enumeration fails.
|
|
277
|
+
"""
|
|
278
|
+
req = proto_ffi.FfiRequest()
|
|
279
|
+
req.get_audio_devices.platform_audio_handle = self._ffi_handle.handle
|
|
280
|
+
|
|
281
|
+
resp = FfiClient.instance.request(req)
|
|
282
|
+
|
|
283
|
+
if resp.get_audio_devices.HasField("error") and resp.get_audio_devices.error:
|
|
284
|
+
raise PlatformAudioError(
|
|
285
|
+
f"Failed to enumerate playout devices: {resp.get_audio_devices.error}"
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
return [AudioDeviceInfo._from_proto(d) for d in resp.get_audio_devices.playout_devices]
|
|
289
|
+
|
|
290
|
+
def set_recording_device(self, device_id: str) -> None:
|
|
291
|
+
"""Select the recording device (microphone) to use.
|
|
292
|
+
|
|
293
|
+
Call this before creating audio tracks to select which microphone to use.
|
|
294
|
+
If not called, the system default microphone is used.
|
|
295
|
+
|
|
296
|
+
Args:
|
|
297
|
+
device_id: The device ID (GUID) from AudioDeviceInfo.id. Use the ID
|
|
298
|
+
rather than index for stable device selection across hot-plug events.
|
|
299
|
+
|
|
300
|
+
Raises:
|
|
301
|
+
PlatformAudioError: If device selection fails.
|
|
302
|
+
"""
|
|
303
|
+
req = proto_ffi.FfiRequest()
|
|
304
|
+
req.set_recording_device.platform_audio_handle = self._ffi_handle.handle
|
|
305
|
+
req.set_recording_device.device_id = device_id
|
|
306
|
+
|
|
307
|
+
resp = FfiClient.instance.request(req)
|
|
308
|
+
|
|
309
|
+
if resp.set_recording_device.HasField("error") and resp.set_recording_device.error:
|
|
310
|
+
raise PlatformAudioError(
|
|
311
|
+
f"Failed to set recording device: {resp.set_recording_device.error}"
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
def set_playout_device(self, device_id: str) -> None:
|
|
315
|
+
"""Select the playout device (speaker) to use.
|
|
316
|
+
|
|
317
|
+
Call this before connecting to a room to select which speaker to use
|
|
318
|
+
for audio output. If not called, the system default speaker is used.
|
|
319
|
+
|
|
320
|
+
Args:
|
|
321
|
+
device_id: The device ID (GUID) from AudioDeviceInfo.id. Use the ID
|
|
322
|
+
rather than index for stable device selection across hot-plug events.
|
|
323
|
+
|
|
324
|
+
Raises:
|
|
325
|
+
PlatformAudioError: If device selection fails.
|
|
326
|
+
"""
|
|
327
|
+
req = proto_ffi.FfiRequest()
|
|
328
|
+
req.set_playout_device.platform_audio_handle = self._ffi_handle.handle
|
|
329
|
+
req.set_playout_device.device_id = device_id
|
|
330
|
+
|
|
331
|
+
resp = FfiClient.instance.request(req)
|
|
332
|
+
|
|
333
|
+
if resp.set_playout_device.HasField("error") and resp.set_playout_device.error:
|
|
334
|
+
raise PlatformAudioError(
|
|
335
|
+
f"Failed to set playout device: {resp.set_playout_device.error}"
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
def create_audio_source(
|
|
339
|
+
self, options: Optional[PlatformAudioOptions] = None
|
|
340
|
+
) -> PlatformAudioSource:
|
|
341
|
+
"""Create an audio source for publishing microphone audio.
|
|
342
|
+
|
|
343
|
+
The returned source captures audio from the selected microphone (or system
|
|
344
|
+
default) and can be used with LocalAudioTrack.create_audio_track() to
|
|
345
|
+
publish audio.
|
|
346
|
+
|
|
347
|
+
Args:
|
|
348
|
+
options: Audio processing options (AEC, NS, AGC, prefer_hardware).
|
|
349
|
+
If None, defaults are used (all processing enabled).
|
|
350
|
+
|
|
351
|
+
Returns:
|
|
352
|
+
PlatformAudioSource that can be passed to LocalAudioTrack.create_audio_track().
|
|
353
|
+
|
|
354
|
+
Raises:
|
|
355
|
+
PlatformAudioError: If source creation fails.
|
|
356
|
+
|
|
357
|
+
Example:
|
|
358
|
+
```python
|
|
359
|
+
source = platform_audio.create_audio_source(
|
|
360
|
+
PlatformAudioOptions(
|
|
361
|
+
echo_cancellation=True,
|
|
362
|
+
noise_suppression=True,
|
|
363
|
+
auto_gain_control=True,
|
|
364
|
+
)
|
|
365
|
+
)
|
|
366
|
+
track = rtc.LocalAudioTrack.create_audio_track("microphone", source)
|
|
367
|
+
```
|
|
368
|
+
"""
|
|
369
|
+
if options is None:
|
|
370
|
+
options = PlatformAudioOptions()
|
|
371
|
+
|
|
372
|
+
req = proto_ffi.FfiRequest()
|
|
373
|
+
req.new_audio_source.type = proto_audio_frame.AudioSourceType.AUDIO_SOURCE_PLATFORM
|
|
374
|
+
req.new_audio_source.platform_audio_handle = self._ffi_handle.handle
|
|
375
|
+
req.new_audio_source.options.CopyFrom(options._to_proto())
|
|
376
|
+
# For platform audio, the ADM determines the actual sample rate and channels.
|
|
377
|
+
# These fields are ignored but required by older proto versions (will be optional
|
|
378
|
+
# in livekit-ffi >= 0.12.58). Use standard WebRTC defaults.
|
|
379
|
+
req.new_audio_source.sample_rate = 48000
|
|
380
|
+
req.new_audio_source.num_channels = 1
|
|
381
|
+
|
|
382
|
+
resp = FfiClient.instance.request(req)
|
|
383
|
+
source_info = resp.new_audio_source.source
|
|
384
|
+
|
|
385
|
+
return PlatformAudioSource(
|
|
386
|
+
FfiHandle(source_info.handle.id),
|
|
387
|
+
source_info,
|
|
388
|
+
)
|
|
389
|
+
|
|
390
|
+
def close(self) -> None:
|
|
391
|
+
"""Release the native Audio Device Module resources.
|
|
392
|
+
|
|
393
|
+
Call this method when you are done using PlatformAudio to immediately
|
|
394
|
+
release the underlying ADM handle and associated native resources.
|
|
395
|
+
|
|
396
|
+
Why call close() explicitly?
|
|
397
|
+
Python's garbage collection is non-deterministic. If you rely on GC
|
|
398
|
+
to clean up PlatformAudio, the ADM may remain active longer than
|
|
399
|
+
expected. This can cause problems on some platforms (especially
|
|
400
|
+
Windows) where:
|
|
401
|
+
|
|
402
|
+
- Audio device handles are held longer than necessary, preventing
|
|
403
|
+
other applications or audio subsystems from accessing the devices
|
|
404
|
+
- Subsequent audio operations (including synthetic AudioSource) may
|
|
405
|
+
fail or behave unexpectedly due to ADM still being active
|
|
406
|
+
- Tests running in sequence may interfere with each other if the
|
|
407
|
+
previous test's ADM resources haven't been collected yet
|
|
408
|
+
|
|
409
|
+
What happens if close() is not called?
|
|
410
|
+
Resources will eventually be released when the PlatformAudio object
|
|
411
|
+
is garbage collected. This works fine in simple applications where
|
|
412
|
+
the object goes out of scope and is collected promptly. However, in
|
|
413
|
+
these scenarios you should call close() explicitly:
|
|
414
|
+
|
|
415
|
+
- Running multiple tests that use audio
|
|
416
|
+
- Switching between PlatformAudio and synthetic AudioSource
|
|
417
|
+
- Long-running applications that create/destroy audio resources
|
|
418
|
+
- Applications where prompt device release is important
|
|
419
|
+
|
|
420
|
+
It is safe to call `close()` multiple times; subsequent calls are no-ops.
|
|
421
|
+
|
|
422
|
+
Note:
|
|
423
|
+
Always close PlatformAudioSource instances before closing the parent
|
|
424
|
+
PlatformAudio instance.
|
|
425
|
+
"""
|
|
426
|
+
self._ffi_handle.dispose()
|
livekit/rtc/py.typed
ADDED
|
File without changes
|