livekit 1.1.20__tar.gz → 1.2.0__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.
- livekit-1.2.0/PKG-INFO +80 -0
- livekit-1.2.0/README.md +51 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/__init__.py +20 -1
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/_ffi_client.py +20 -8
- livekit-1.2.0/livekit/rtc/_proto/capture_pb2.py +70 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/_proto/capture_pb2.pyi +134 -13
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/_utils.py +17 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/audio_mixer.py +53 -6
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/data_stream.py +13 -12
- livekit-1.2.0/livekit/rtc/encoded_video.py +127 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/participant.py +50 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/track.py +4 -1
- livekit-1.2.0/livekit/rtc/version.py +1 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/video_frame.py +4 -3
- livekit-1.2.0/livekit.egg-info/PKG-INFO +80 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit.egg-info/SOURCES.txt +5 -0
- livekit-1.2.0/tests/test_broadcast_queue.py +98 -0
- livekit-1.2.0/tests/test_data_stream_writer.py +84 -0
- livekit-1.2.0/tests/test_ffi_client.py +44 -0
- livekit-1.2.0/tests/test_ffi_handle_lifecycle.py +91 -0
- livekit-1.1.20/PKG-INFO +0 -35
- livekit-1.1.20/README.md +0 -6
- livekit-1.1.20/livekit/rtc/_proto/capture_pb2.py +0 -62
- livekit-1.1.20/livekit/rtc/version.py +0 -1
- livekit-1.1.20/livekit.egg-info/PKG-INFO +0 -35
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/_proto/__init__.py +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/_proto/audio_frame_pb2.py +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/_proto/audio_frame_pb2.pyi +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/_proto/data_stream_pb2.py +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/_proto/data_stream_pb2.pyi +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/_proto/data_track_pb2.py +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/_proto/data_track_pb2.pyi +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/_proto/e2ee_pb2.py +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/_proto/e2ee_pb2.pyi +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/_proto/ffi_pb2.py +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/_proto/ffi_pb2.pyi +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/_proto/handle_pb2.py +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/_proto/handle_pb2.pyi +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/_proto/participant_pb2.py +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/_proto/participant_pb2.pyi +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/_proto/room_pb2.py +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/_proto/room_pb2.pyi +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/_proto/rpc_pb2.py +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/_proto/rpc_pb2.pyi +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/_proto/stats_pb2.py +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/_proto/stats_pb2.pyi +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/_proto/track_pb2.py +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/_proto/track_pb2.pyi +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/_proto/track_publication_pb2.py +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/_proto/track_publication_pb2.pyi +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/_proto/video_frame_pb2.py +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/_proto/video_frame_pb2.pyi +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/apm.py +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/audio_filter.py +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/audio_frame.py +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/audio_resampler.py +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/audio_source.py +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/audio_stream.py +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/data_track.py +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/e2ee.py +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/event_emitter.py +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/frame_processor.py +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/jupyter.py +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/log.py +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/media_devices.py +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/platform_audio.py +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/py.typed +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/resources/__init__.py +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/resources/jupyter-html/index.html +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/room.py +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/rpc.py +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/synchronizer.py +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/track_publication.py +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/transcription.py +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/utils.py +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/video_source.py +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit/rtc/video_stream.py +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit.egg-info/dependency_links.txt +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit.egg-info/requires.txt +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/livekit.egg-info/top_level.txt +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/pyproject.toml +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/setup.cfg +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/setup.py +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/tests/test_audio.py +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/tests/test_audio_stream_init.py +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/tests/test_audio_stream_room_lifecycle.py +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/tests/test_change_video_quality.py +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/tests/test_dc.py +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/tests/test_e2ee.py +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/tests/test_e2ee_per_participant.py +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/tests/test_e2ee_shared_key.py +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/tests/test_rpc_interceptors.py +0 -0
- {livekit-1.1.20 → livekit-1.2.0}/tests/test_video.py +0 -0
livekit-1.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: livekit
|
|
3
|
+
Version: 1.2.0
|
|
4
|
+
Summary: Python Real-time SDK for LiveKit
|
|
5
|
+
Author-email: LiveKit <support@livekit.io>
|
|
6
|
+
License-Expression: Apache-2.0
|
|
7
|
+
Project-URL: Documentation, https://docs.livekit.io
|
|
8
|
+
Project-URL: Website, https://livekit.io/
|
|
9
|
+
Project-URL: Source, https://github.com/livekit/python-sdks/
|
|
10
|
+
Keywords: webrtc,realtime,audio,video,livekit
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Topic :: Multimedia :: Sound/Audio
|
|
13
|
+
Classifier: Topic :: Multimedia :: Video
|
|
14
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
22
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
23
|
+
Requires-Python: >=3.9.0
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
Requires-Dist: protobuf>=5
|
|
26
|
+
Requires-Dist: types-protobuf>=5
|
|
27
|
+
Requires-Dist: aiofiles>=24
|
|
28
|
+
Requires-Dist: numpy>=1.26
|
|
29
|
+
|
|
30
|
+
# LiveKit SDK for Python
|
|
31
|
+
|
|
32
|
+
Python SDK to integrate LiveKit's real-time video, audio, and data capabilities into your Python applications using WebRTC. Designed for use with [LiveKit Agents](https://github.com/livekit/agents) to build powerful voice AI apps.
|
|
33
|
+
|
|
34
|
+
See https://docs.livekit.io/ for more information.
|
|
35
|
+
|
|
36
|
+
## Publishing pre-encoded video
|
|
37
|
+
|
|
38
|
+
Use `EncodedVideoSource` when an upstream encoder already provides compressed
|
|
39
|
+
frames. The source uses the same native passthrough encoder as the Rust SDK;
|
|
40
|
+
Python does not decode or re-encode the video.
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
from livekit import rtc
|
|
44
|
+
|
|
45
|
+
source = rtc.EncodedVideoSource(width=640, height=360)
|
|
46
|
+
track = rtc.LocalVideoTrack.create_video_track("camera", source)
|
|
47
|
+
await room.local_participant.publish_track(
|
|
48
|
+
track,
|
|
49
|
+
rtc.TrackPublishOptions(
|
|
50
|
+
video_codec=rtc.VideoCodec.VP9,
|
|
51
|
+
video_encoder=rtc.VideoEncoderBackend.ENCODER_BACKEND_PRE_ENCODED,
|
|
52
|
+
simulcast=False,
|
|
53
|
+
),
|
|
54
|
+
)
|
|
55
|
+
# Supply each complete encoded frame at its playback time.
|
|
56
|
+
source.capture_frame(rtc.EncodedVideoFrame(
|
|
57
|
+
data=encoded_vp9_frame,
|
|
58
|
+
width=640,
|
|
59
|
+
height=360,
|
|
60
|
+
codec=rtc.VideoCodec.VP9,
|
|
61
|
+
frame_type=rtc.EncodedFrameType.ENCODED_FRAME_KEY,
|
|
62
|
+
timestamp_us=capture_timestamp_us,
|
|
63
|
+
))
|
|
64
|
+
feedback = source.take_feedback()
|
|
65
|
+
# Forward feedback.keyframe_requested and feedback.rate_control to your encoder.
|
|
66
|
+
# At shutdown, unpublish the track and release the source:
|
|
67
|
+
await room.local_participant.unpublish_track(track.sid)
|
|
68
|
+
await source.aclose()
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Supported codecs are H.264, H.265, VP8, VP9 and AV1, subject to receiver support.
|
|
72
|
+
Submit complete access units (Annex B for H.264/H.265), not WebM/MP4 container
|
|
73
|
+
bytes or RTP packets. A container must first be demuxed; demuxing extracts
|
|
74
|
+
compressed frames and does not reconstruct pixels. The codec must match the
|
|
75
|
+
publication and stay fixed, and simulcast must be disabled.
|
|
76
|
+
|
|
77
|
+
The caller controls pacing and handles keyframe and bitrate feedback. In
|
|
78
|
+
particular, a prerecorded file cannot generate a new keyframe on demand for a
|
|
79
|
+
late subscriber or after packet loss. WebM's auxiliary alpha data is not part of
|
|
80
|
+
the VP9 color access unit and is not transported by this API.
|
livekit-1.2.0/README.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# LiveKit SDK for Python
|
|
2
|
+
|
|
3
|
+
Python SDK to integrate LiveKit's real-time video, audio, and data capabilities into your Python applications using WebRTC. Designed for use with [LiveKit Agents](https://github.com/livekit/agents) to build powerful voice AI apps.
|
|
4
|
+
|
|
5
|
+
See https://docs.livekit.io/ for more information.
|
|
6
|
+
|
|
7
|
+
## Publishing pre-encoded video
|
|
8
|
+
|
|
9
|
+
Use `EncodedVideoSource` when an upstream encoder already provides compressed
|
|
10
|
+
frames. The source uses the same native passthrough encoder as the Rust SDK;
|
|
11
|
+
Python does not decode or re-encode the video.
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from livekit import rtc
|
|
15
|
+
|
|
16
|
+
source = rtc.EncodedVideoSource(width=640, height=360)
|
|
17
|
+
track = rtc.LocalVideoTrack.create_video_track("camera", source)
|
|
18
|
+
await room.local_participant.publish_track(
|
|
19
|
+
track,
|
|
20
|
+
rtc.TrackPublishOptions(
|
|
21
|
+
video_codec=rtc.VideoCodec.VP9,
|
|
22
|
+
video_encoder=rtc.VideoEncoderBackend.ENCODER_BACKEND_PRE_ENCODED,
|
|
23
|
+
simulcast=False,
|
|
24
|
+
),
|
|
25
|
+
)
|
|
26
|
+
# Supply each complete encoded frame at its playback time.
|
|
27
|
+
source.capture_frame(rtc.EncodedVideoFrame(
|
|
28
|
+
data=encoded_vp9_frame,
|
|
29
|
+
width=640,
|
|
30
|
+
height=360,
|
|
31
|
+
codec=rtc.VideoCodec.VP9,
|
|
32
|
+
frame_type=rtc.EncodedFrameType.ENCODED_FRAME_KEY,
|
|
33
|
+
timestamp_us=capture_timestamp_us,
|
|
34
|
+
))
|
|
35
|
+
feedback = source.take_feedback()
|
|
36
|
+
# Forward feedback.keyframe_requested and feedback.rate_control to your encoder.
|
|
37
|
+
# At shutdown, unpublish the track and release the source:
|
|
38
|
+
await room.local_participant.unpublish_track(track.sid)
|
|
39
|
+
await source.aclose()
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Supported codecs are H.264, H.265, VP8, VP9 and AV1, subject to receiver support.
|
|
43
|
+
Submit complete access units (Annex B for H.264/H.265), not WebM/MP4 container
|
|
44
|
+
bytes or RTP packets. A container must first be demuxed; demuxing extracts
|
|
45
|
+
compressed frames and does not reconstruct pixels. The codec must match the
|
|
46
|
+
publication and stay fixed, and simulcast must be disabled.
|
|
47
|
+
|
|
48
|
+
The caller controls pacing and handles keyframe and bitrate feedback. In
|
|
49
|
+
particular, a prerecorded file cannot generate a new keyframe on demand for a
|
|
50
|
+
late subscriber or after packet loss. WebM's auxiliary alpha data is not part of
|
|
51
|
+
the VP9 color access unit and is not transported by this API.
|
|
@@ -35,6 +35,7 @@ from ._proto.room_pb2 import (
|
|
|
35
35
|
SimulateScenarioKind,
|
|
36
36
|
TrackPublishOptions,
|
|
37
37
|
VideoEncoding,
|
|
38
|
+
VideoEncoderBackend,
|
|
38
39
|
)
|
|
39
40
|
from ._proto.track_pb2 import (
|
|
40
41
|
FrameMetadataFeature,
|
|
@@ -43,7 +44,13 @@ from ._proto.track_pb2 import (
|
|
|
43
44
|
TrackSource,
|
|
44
45
|
ParticipantTrackPermission,
|
|
45
46
|
)
|
|
46
|
-
from ._proto.video_frame_pb2 import
|
|
47
|
+
from ._proto.video_frame_pb2 import (
|
|
48
|
+
EncodedFrameType,
|
|
49
|
+
FrameMetadata,
|
|
50
|
+
VideoBufferType,
|
|
51
|
+
VideoCodec,
|
|
52
|
+
VideoRotation,
|
|
53
|
+
)
|
|
47
54
|
from ._proto.track_publication_pb2 import VideoQuality
|
|
48
55
|
from .audio_frame import AudioFrame
|
|
49
56
|
from .audio_source import AudioSource
|
|
@@ -95,6 +102,12 @@ from .version import __version__
|
|
|
95
102
|
from .video_frame import (
|
|
96
103
|
VideoFrame,
|
|
97
104
|
)
|
|
105
|
+
from .encoded_video import (
|
|
106
|
+
EncodedVideoFrame,
|
|
107
|
+
EncodedVideoSource,
|
|
108
|
+
EncodedVideoSourceFeedback,
|
|
109
|
+
EncodedRateControl,
|
|
110
|
+
)
|
|
98
111
|
from .video_source import VideoSource
|
|
99
112
|
from .video_stream import VideoFrameEvent, VideoStream
|
|
100
113
|
from .audio_resampler import AudioResampler, AudioResamplerQuality
|
|
@@ -138,6 +151,12 @@ from .data_track import (
|
|
|
138
151
|
from .frame_processor import FrameProcessor
|
|
139
152
|
|
|
140
153
|
__all__ = [
|
|
154
|
+
"EncodedFrameType",
|
|
155
|
+
"EncodedVideoFrame",
|
|
156
|
+
"EncodedVideoSource",
|
|
157
|
+
"EncodedVideoSourceFeedback",
|
|
158
|
+
"EncodedRateControl",
|
|
159
|
+
"VideoEncoderBackend",
|
|
141
160
|
"ConnectionQuality",
|
|
142
161
|
"ConnectionState",
|
|
143
162
|
"DataPacketKind",
|
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
15
17
|
import signal
|
|
16
18
|
import asyncio
|
|
17
19
|
import sys
|
|
@@ -24,6 +26,7 @@ import os
|
|
|
24
26
|
import platform
|
|
25
27
|
import atexit
|
|
26
28
|
import threading
|
|
29
|
+
import weakref
|
|
27
30
|
from typing import Callable, Generic, List, Optional, TypeVar
|
|
28
31
|
|
|
29
32
|
from ._proto import ffi_pb2 as proto_ffi
|
|
@@ -68,10 +71,14 @@ ffi_cb_fnc = ctypes.CFUNCTYPE(None, ctypes.POINTER(ctypes.c_uint8), ctypes.c_siz
|
|
|
68
71
|
INVALID_HANDLE = 0
|
|
69
72
|
|
|
70
73
|
|
|
74
|
+
_live_handles: weakref.WeakSet[FfiHandle] = weakref.WeakSet()
|
|
75
|
+
|
|
76
|
+
|
|
71
77
|
class FfiHandle:
|
|
72
78
|
def __init__(self, handle: int) -> None:
|
|
73
79
|
self.handle = handle
|
|
74
80
|
self._disposed = False
|
|
81
|
+
_live_handles.add(self)
|
|
75
82
|
|
|
76
83
|
def __del__(self) -> None:
|
|
77
84
|
self.dispose()
|
|
@@ -93,7 +100,7 @@ class FfiHandle:
|
|
|
93
100
|
if self.handle != INVALID_HANDLE and not self._disposed:
|
|
94
101
|
self._disposed = True
|
|
95
102
|
ffi = FfiClient._instance
|
|
96
|
-
if ffi is None or ffi._pid != os.getpid():
|
|
103
|
+
if ffi is None or ffi._pid != os.getpid() or getattr(FfiClient, "_ffi_disposed", False):
|
|
97
104
|
return
|
|
98
105
|
dropped = ffi._ffi_lib.livekit_ffi_drop_handle(ctypes.c_uint64(self.handle))
|
|
99
106
|
assert dropped
|
|
@@ -223,6 +230,7 @@ def to_python_level(level: proto_ffi.LogLevel.ValueType) -> Optional[int]:
|
|
|
223
230
|
|
|
224
231
|
class FfiClient:
|
|
225
232
|
_instance: Optional["FfiClient"] = None
|
|
233
|
+
_ffi_disposed: bool = False
|
|
226
234
|
|
|
227
235
|
@classproperty
|
|
228
236
|
def instance(cls) -> "FfiClient":
|
|
@@ -231,6 +239,7 @@ class FfiClient:
|
|
|
231
239
|
return cls._instance
|
|
232
240
|
|
|
233
241
|
def __init__(self) -> None:
|
|
242
|
+
FfiClient._ffi_disposed = False
|
|
234
243
|
self._pid = os.getpid()
|
|
235
244
|
self._lock = threading.RLock()
|
|
236
245
|
self._queue = FfiQueue[proto_ffi.FfiEvent]()
|
|
@@ -266,14 +275,17 @@ class FfiClient:
|
|
|
266
275
|
ffi_event_callback, True, b"python", __version__.encode("ascii")
|
|
267
276
|
)
|
|
268
277
|
|
|
269
|
-
ffi_lib = self._ffi_lib
|
|
270
|
-
init_pid = self._pid
|
|
271
|
-
|
|
272
278
|
@atexit.register
|
|
273
279
|
def _dispose_lk_ffi() -> None:
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
280
|
+
self._dispose()
|
|
281
|
+
|
|
282
|
+
def _dispose(self) -> None:
|
|
283
|
+
if self._pid != os.getpid() or FfiClient._ffi_disposed:
|
|
284
|
+
return
|
|
285
|
+
FfiClient._ffi_disposed = True
|
|
286
|
+
for handle in list(_live_handles):
|
|
287
|
+
handle.mark_consumed()
|
|
288
|
+
self._ffi_lib.livekit_ffi_dispose()
|
|
277
289
|
|
|
278
290
|
@property
|
|
279
291
|
def queue(self) -> FfiQueue[proto_ffi.FfiEvent]:
|
|
@@ -288,7 +300,7 @@ class FfiClient:
|
|
|
288
300
|
)
|
|
289
301
|
proto_data = req.SerializeToString()
|
|
290
302
|
proto_len = len(proto_data)
|
|
291
|
-
data = (ctypes.
|
|
303
|
+
data = ctypes.cast(ctypes.c_char_p(proto_data), ctypes.POINTER(ctypes.c_ubyte))
|
|
292
304
|
|
|
293
305
|
resp_ptr = ctypes.POINTER(ctypes.c_ubyte)()
|
|
294
306
|
resp_len = ctypes.c_size_t()
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
3
|
+
# source: capture.proto
|
|
4
|
+
# Protobuf Python Version: 4.25.1
|
|
5
|
+
"""Generated protocol buffer code."""
|
|
6
|
+
from google.protobuf import descriptor as _descriptor
|
|
7
|
+
from google.protobuf import descriptor_pool as _descriptor_pool
|
|
8
|
+
from google.protobuf import symbol_database as _symbol_database
|
|
9
|
+
from google.protobuf.internal import builder as _builder
|
|
10
|
+
# @@protoc_insertion_point(imports)
|
|
11
|
+
|
|
12
|
+
_sym_db = _symbol_database.Default()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
from . import handle_pb2 as handle__pb2
|
|
16
|
+
from . import room_pb2 as room__pb2
|
|
17
|
+
from . import video_frame_pb2 as video__frame__pb2
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\rcapture.proto\x12\rlivekit.proto\x1a\x0chandle.proto\x1a\nroom.proto\x1a\x11video_frame.proto\"l\n\x14GstreamerRateControl\x12\x0f\n\x07\x65lement\x18\x01 \x02(\t\x12\x10\n\x08property\x18\x02 \x02(\t\x12\x31\n\x04unit\x18\x03 \x02(\x0e\x32#.livekit.proto.GstreamerBitrateUnit\"\xcd\x01\n\x1aGstreamerVideoSourceConfig\x12\x10\n\x08pipeline\x18\x01 \x02(\t\x12(\n\x05\x63odec\x18\x02 \x01(\x0e\x32\x19.livekit.proto.VideoCodec\x12\x38\n\nresolution\x18\x03 \x01(\x0b\x32$.livekit.proto.VideoSourceResolution\x12\x39\n\x0crate_control\x18\x04 \x01(\x0b\x32#.livekit.proto.GstreamerRateControl\"\x94\x01\n\x18PatternVideoSourceConfig\x12\x38\n\nresolution\x18\x01 \x02(\x0b\x32$.livekit.proto.VideoSourceResolution\x12\x15\n\rframerate_fps\x18\x02 \x02(\r\x12\'\n\x07pattern\x18\x03 \x02(\x0e\x32\x16.livekit.proto.Pattern\"i\n\x16\x43lockVideoSourceConfig\x12\x38\n\nresolution\x18\x01 \x02(\x0b\x32$.livekit.proto.VideoSourceResolution\x12\x15\n\rframerate_fps\x18\x02 \x02(\r\"\xa7\x02\n\x11\x43\x61ptureSourceInfo\x12.\n\x04kind\x18\x01 \x02(\x0e\x32 .livekit.proto.CaptureSourceKind\x12\x38\n\nresolution\x18\x02 \x02(\x0b\x32$.livekit.proto.VideoSourceResolution\x12(\n\x05\x63odec\x18\x03 \x01(\x0e\x32\x19.livekit.proto.VideoCodec\x12G\n\x1brecommended_publish_options\x18\x04 \x02(\x0b\x32\".livekit.proto.TrackPublishOptions\x12\x35\n\x0cvideo_source\x18\x05 \x02(\x0b\x32\x1f.livekit.proto.OwnedVideoSource\"s\n\x12OwnedCaptureSource\x12-\n\x06handle\x18\x01 \x02(\x0b\x32\x1d.livekit.proto.FfiOwnedHandle\x12.\n\x04info\x18\x02 \x02(\x0b\x32 .livekit.proto.CaptureSourceInfo\"\xf1\x01\n\x17NewCaptureSourceRequest\x12>\n\tgstreamer\x18\x01 \x01(\x0b\x32).livekit.proto.GstreamerVideoSourceConfigH\x00\x12:\n\x07pattern\x18\x02 \x01(\x0b\x32\'.livekit.proto.PatternVideoSourceConfigH\x00\x12\x36\n\x05\x63lock\x18\x05 \x01(\x0b\x32%.livekit.proto.ClockVideoSourceConfigH\x00\x12\x18\n\x10request_async_id\x18\x03 \x01(\x04\x42\x08\n\x06\x63onfig\",\n\x18NewCaptureSourceResponse\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\"}\n\x18NewCaptureSourceCallback\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\x12\x0f\n\x05\x65rror\x18\x02 \x01(\tH\x00\x12\x33\n\x06source\x18\x03 \x01(\x0b\x32!.livekit.proto.OwnedCaptureSourceH\x00\x42\t\n\x07message\"-\n\x13StartCaptureRequest\x12\x16\n\x0e\x63\x61pture_handle\x18\x01 \x02(\x04\"%\n\x14StartCaptureResponse\x12\r\n\x05\x65rror\x18\x01 \x01(\t\",\n\x12StopCaptureRequest\x12\x16\n\x0e\x63\x61pture_handle\x18\x01 \x02(\x04\"$\n\x13StopCaptureResponse\x12\r\n\x05\x65rror\x18\x01 \x01(\t\"T\n\x0f\x43\x61ptureFinished\x12\x17\n\x0f\x66rames_captured\x18\x01 \x02(\x04\x12(\n\x04\x65xit\x18\x02 \x02(\x0e\x32\x1a.livekit.proto.CaptureExit\"L\n\x0c\x43\x61ptureError\x12\r\n\x05\x65rror\x18\x01 \x02(\t\x12-\n\x04kind\x18\x02 \x02(\x0e\x32\x1f.livekit.proto.CaptureErrorKind\"\x99\x01\n\x12\x43\x61ptureSourceEvent\x12\x16\n\x0e\x63\x61pture_handle\x18\x01 \x02(\x04\x12\x32\n\x08\x66inished\x18\x02 \x01(\x0b\x32\x1e.livekit.proto.CaptureFinishedH\x00\x12,\n\x05\x65rror\x18\x03 \x01(\x0b\x32\x1b.livekit.proto.CaptureErrorH\x00\x42\t\n\x07message*W\n\x14GstreamerBitrateUnit\x12\x1e\n\x1aGSTREAMER_BITRATE_UNIT_BPS\x10\x00\x12\x1f\n\x1bGSTREAMER_BITRATE_UNIT_KBPS\x10\x01*1\n\x07Pattern\x12\x14\n\x10PATTERN_GRADIENT\x10\x00\x12\x10\n\x0cPATTERN_LOGO\x10\x01*I\n\x11\x43\x61ptureSourceKind\x12\x18\n\x14\x43\x41PTURE_SOURCE_PIXEL\x10\x00\x12\x1a\n\x16\x43\x41PTURE_SOURCE_ENCODED\x10\x01*G\n\x0b\x43\x61ptureExit\x12\x18\n\x14\x43\x41PTURE_EXIT_STOPPED\x10\x00\x12\x1e\n\x1a\x43\x41PTURE_EXIT_END_OF_STREAM\x10\x01*n\n\x10\x43\x61ptureErrorKind\x12\x1d\n\x19\x43\x41PTURE_ERROR_KIND_SOURCE\x10\x00\x12\x1a\n\x16\x43\x41PTURE_ERROR_KIND_RTC\x10\x01\x12\x1f\n\x1b\x43\x41PTURE_ERROR_KIND_PANICKED\x10\x02\x42\x10\xaa\x02\rLiveKit.Proto')
|
|
21
|
+
|
|
22
|
+
_globals = globals()
|
|
23
|
+
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
|
24
|
+
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'capture_pb2', _globals)
|
|
25
|
+
if _descriptor._USE_C_DESCRIPTORS == False:
|
|
26
|
+
_globals['DESCRIPTOR']._options = None
|
|
27
|
+
_globals['DESCRIPTOR']._serialized_options = b'\252\002\rLiveKit.Proto'
|
|
28
|
+
_globals['_GSTREAMERBITRATEUNIT']._serialized_start=1975
|
|
29
|
+
_globals['_GSTREAMERBITRATEUNIT']._serialized_end=2062
|
|
30
|
+
_globals['_PATTERN']._serialized_start=2064
|
|
31
|
+
_globals['_PATTERN']._serialized_end=2113
|
|
32
|
+
_globals['_CAPTURESOURCEKIND']._serialized_start=2115
|
|
33
|
+
_globals['_CAPTURESOURCEKIND']._serialized_end=2188
|
|
34
|
+
_globals['_CAPTUREEXIT']._serialized_start=2190
|
|
35
|
+
_globals['_CAPTUREEXIT']._serialized_end=2261
|
|
36
|
+
_globals['_CAPTUREERRORKIND']._serialized_start=2263
|
|
37
|
+
_globals['_CAPTUREERRORKIND']._serialized_end=2373
|
|
38
|
+
_globals['_GSTREAMERRATECONTROL']._serialized_start=77
|
|
39
|
+
_globals['_GSTREAMERRATECONTROL']._serialized_end=185
|
|
40
|
+
_globals['_GSTREAMERVIDEOSOURCECONFIG']._serialized_start=188
|
|
41
|
+
_globals['_GSTREAMERVIDEOSOURCECONFIG']._serialized_end=393
|
|
42
|
+
_globals['_PATTERNVIDEOSOURCECONFIG']._serialized_start=396
|
|
43
|
+
_globals['_PATTERNVIDEOSOURCECONFIG']._serialized_end=544
|
|
44
|
+
_globals['_CLOCKVIDEOSOURCECONFIG']._serialized_start=546
|
|
45
|
+
_globals['_CLOCKVIDEOSOURCECONFIG']._serialized_end=651
|
|
46
|
+
_globals['_CAPTURESOURCEINFO']._serialized_start=654
|
|
47
|
+
_globals['_CAPTURESOURCEINFO']._serialized_end=949
|
|
48
|
+
_globals['_OWNEDCAPTURESOURCE']._serialized_start=951
|
|
49
|
+
_globals['_OWNEDCAPTURESOURCE']._serialized_end=1066
|
|
50
|
+
_globals['_NEWCAPTURESOURCEREQUEST']._serialized_start=1069
|
|
51
|
+
_globals['_NEWCAPTURESOURCEREQUEST']._serialized_end=1310
|
|
52
|
+
_globals['_NEWCAPTURESOURCERESPONSE']._serialized_start=1312
|
|
53
|
+
_globals['_NEWCAPTURESOURCERESPONSE']._serialized_end=1356
|
|
54
|
+
_globals['_NEWCAPTURESOURCECALLBACK']._serialized_start=1358
|
|
55
|
+
_globals['_NEWCAPTURESOURCECALLBACK']._serialized_end=1483
|
|
56
|
+
_globals['_STARTCAPTUREREQUEST']._serialized_start=1485
|
|
57
|
+
_globals['_STARTCAPTUREREQUEST']._serialized_end=1530
|
|
58
|
+
_globals['_STARTCAPTURERESPONSE']._serialized_start=1532
|
|
59
|
+
_globals['_STARTCAPTURERESPONSE']._serialized_end=1569
|
|
60
|
+
_globals['_STOPCAPTUREREQUEST']._serialized_start=1571
|
|
61
|
+
_globals['_STOPCAPTUREREQUEST']._serialized_end=1615
|
|
62
|
+
_globals['_STOPCAPTURERESPONSE']._serialized_start=1617
|
|
63
|
+
_globals['_STOPCAPTURERESPONSE']._serialized_end=1653
|
|
64
|
+
_globals['_CAPTUREFINISHED']._serialized_start=1655
|
|
65
|
+
_globals['_CAPTUREFINISHED']._serialized_end=1739
|
|
66
|
+
_globals['_CAPTUREERROR']._serialized_start=1741
|
|
67
|
+
_globals['_CAPTUREERROR']._serialized_end=1817
|
|
68
|
+
_globals['_CAPTURESOURCEEVENT']._serialized_start=1820
|
|
69
|
+
_globals['_CAPTURESOURCEEVENT']._serialized_end=1973
|
|
70
|
+
# @@protoc_insertion_point(module_scope)
|
|
@@ -33,18 +33,16 @@ else:
|
|
|
33
33
|
|
|
34
34
|
DESCRIPTOR: google.protobuf.descriptor.FileDescriptor
|
|
35
35
|
|
|
36
|
-
class
|
|
36
|
+
class _GstreamerBitrateUnit:
|
|
37
37
|
ValueType = typing.NewType("ValueType", builtins.int)
|
|
38
38
|
V: typing_extensions.TypeAlias = ValueType
|
|
39
39
|
|
|
40
|
-
class
|
|
40
|
+
class _GstreamerBitrateUnitEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_GstreamerBitrateUnit.ValueType], builtins.type):
|
|
41
41
|
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
PATTERN_LOGO: _Pattern.ValueType # 1
|
|
45
|
-
"""Bouncing LiveKit logo."""
|
|
42
|
+
GSTREAMER_BITRATE_UNIT_BPS: _GstreamerBitrateUnit.ValueType # 0
|
|
43
|
+
GSTREAMER_BITRATE_UNIT_KBPS: _GstreamerBitrateUnit.ValueType # 1
|
|
46
44
|
|
|
47
|
-
class
|
|
45
|
+
class GstreamerBitrateUnit(_GstreamerBitrateUnit, metaclass=_GstreamerBitrateUnitEnumTypeWrapper):
|
|
48
46
|
"""Capture sources (livekit-capture)
|
|
49
47
|
|
|
50
48
|
A capture source owns a media producer (e.g. a GStreamer pipeline) and the
|
|
@@ -61,9 +59,27 @@ class Pattern(_Pattern, metaclass=_PatternEnumTypeWrapper):
|
|
|
61
59
|
3. The capture runs until StopCaptureRequest, end of stream, or an error;
|
|
62
60
|
a CaptureSourceEvent is delivered exactly once when it ends.
|
|
63
61
|
|
|
64
|
-
|
|
62
|
+
Bitrate unit expected by a GStreamer encoder property.
|
|
65
63
|
"""
|
|
66
64
|
|
|
65
|
+
GSTREAMER_BITRATE_UNIT_BPS: GstreamerBitrateUnit.ValueType # 0
|
|
66
|
+
GSTREAMER_BITRATE_UNIT_KBPS: GstreamerBitrateUnit.ValueType # 1
|
|
67
|
+
global___GstreamerBitrateUnit = GstreamerBitrateUnit
|
|
68
|
+
|
|
69
|
+
class _Pattern:
|
|
70
|
+
ValueType = typing.NewType("ValueType", builtins.int)
|
|
71
|
+
V: typing_extensions.TypeAlias = ValueType
|
|
72
|
+
|
|
73
|
+
class _PatternEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_Pattern.ValueType], builtins.type):
|
|
74
|
+
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
|
|
75
|
+
PATTERN_GRADIENT: _Pattern.ValueType # 0
|
|
76
|
+
"""Animated color gradient."""
|
|
77
|
+
PATTERN_LOGO: _Pattern.ValueType # 1
|
|
78
|
+
"""Bouncing LiveKit logo."""
|
|
79
|
+
|
|
80
|
+
class Pattern(_Pattern, metaclass=_PatternEnumTypeWrapper):
|
|
81
|
+
"""Test patterns built into livekit-capture."""
|
|
82
|
+
|
|
67
83
|
PATTERN_GRADIENT: Pattern.ValueType # 0
|
|
68
84
|
"""Animated color gradient."""
|
|
69
85
|
PATTERN_LOGO: Pattern.ValueType # 1
|
|
@@ -110,6 +126,104 @@ CAPTURE_EXIT_END_OF_STREAM: CaptureExit.ValueType # 1
|
|
|
110
126
|
"""The producer reached the end of its stream."""
|
|
111
127
|
global___CaptureExit = CaptureExit
|
|
112
128
|
|
|
129
|
+
class _CaptureErrorKind:
|
|
130
|
+
ValueType = typing.NewType("ValueType", builtins.int)
|
|
131
|
+
V: typing_extensions.TypeAlias = ValueType
|
|
132
|
+
|
|
133
|
+
class _CaptureErrorKindEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_CaptureErrorKind.ValueType], builtins.type):
|
|
134
|
+
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
|
|
135
|
+
CAPTURE_ERROR_KIND_SOURCE: _CaptureErrorKind.ValueType # 0
|
|
136
|
+
"""The capture source failed or violated its contract."""
|
|
137
|
+
CAPTURE_ERROR_KIND_RTC: _CaptureErrorKind.ValueType # 1
|
|
138
|
+
"""The RTC video source rejected a frame."""
|
|
139
|
+
CAPTURE_ERROR_KIND_PANICKED: _CaptureErrorKind.ValueType # 2
|
|
140
|
+
"""The pump thread panicked."""
|
|
141
|
+
|
|
142
|
+
class CaptureErrorKind(_CaptureErrorKind, metaclass=_CaptureErrorKindEnumTypeWrapper):
|
|
143
|
+
"""Why a capture ended with an error."""
|
|
144
|
+
|
|
145
|
+
CAPTURE_ERROR_KIND_SOURCE: CaptureErrorKind.ValueType # 0
|
|
146
|
+
"""The capture source failed or violated its contract."""
|
|
147
|
+
CAPTURE_ERROR_KIND_RTC: CaptureErrorKind.ValueType # 1
|
|
148
|
+
"""The RTC video source rejected a frame."""
|
|
149
|
+
CAPTURE_ERROR_KIND_PANICKED: CaptureErrorKind.ValueType # 2
|
|
150
|
+
"""The pump thread panicked."""
|
|
151
|
+
global___CaptureErrorKind = CaptureErrorKind
|
|
152
|
+
|
|
153
|
+
@typing.final
|
|
154
|
+
class GstreamerRateControl(google.protobuf.message.Message):
|
|
155
|
+
"""Binding from WebRTC rate-control targets to a GStreamer encoder property."""
|
|
156
|
+
|
|
157
|
+
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
|
158
|
+
|
|
159
|
+
ELEMENT_FIELD_NUMBER: builtins.int
|
|
160
|
+
PROPERTY_FIELD_NUMBER: builtins.int
|
|
161
|
+
UNIT_FIELD_NUMBER: builtins.int
|
|
162
|
+
element: builtins.str
|
|
163
|
+
"""Name of the encoder element in the pipeline (e.g. `lk_encoder`)."""
|
|
164
|
+
property: builtins.str
|
|
165
|
+
"""Bitrate property to set on the element (e.g. `bitrate` for x264enc,
|
|
166
|
+
`target-bitrate` for vp8enc/vp9enc).
|
|
167
|
+
"""
|
|
168
|
+
unit: global___GstreamerBitrateUnit.ValueType
|
|
169
|
+
"""Unit the property expects."""
|
|
170
|
+
def __init__(
|
|
171
|
+
self,
|
|
172
|
+
*,
|
|
173
|
+
element: builtins.str | None = ...,
|
|
174
|
+
property: builtins.str | None = ...,
|
|
175
|
+
unit: global___GstreamerBitrateUnit.ValueType | None = ...,
|
|
176
|
+
) -> None: ...
|
|
177
|
+
def HasField(self, field_name: typing.Literal["element", b"element", "property", b"property", "unit", b"unit"]) -> builtins.bool: ...
|
|
178
|
+
def ClearField(self, field_name: typing.Literal["element", b"element", "property", b"property", "unit", b"unit"]) -> None: ...
|
|
179
|
+
|
|
180
|
+
global___GstreamerRateControl = GstreamerRateControl
|
|
181
|
+
|
|
182
|
+
@typing.final
|
|
183
|
+
class GstreamerVideoSourceConfig(google.protobuf.message.Message):
|
|
184
|
+
"""Encoded ingest from a GStreamer pipeline."""
|
|
185
|
+
|
|
186
|
+
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
|
187
|
+
|
|
188
|
+
PIPELINE_FIELD_NUMBER: builtins.int
|
|
189
|
+
CODEC_FIELD_NUMBER: builtins.int
|
|
190
|
+
RESOLUTION_FIELD_NUMBER: builtins.int
|
|
191
|
+
RATE_CONTROL_FIELD_NUMBER: builtins.int
|
|
192
|
+
pipeline: builtins.str
|
|
193
|
+
"""GStreamer launch description for the encoded producer pipeline.
|
|
194
|
+
Must contain `appsink name=lk_appsink`, or leave exactly one encoded
|
|
195
|
+
video source pad unlinked for the source to attach one to.
|
|
196
|
+
"""
|
|
197
|
+
codec: video_frame_pb2.VideoCodec.ValueType
|
|
198
|
+
"""Codec expected from the pipeline; inferred from pipeline caps when
|
|
199
|
+
omitted.
|
|
200
|
+
"""
|
|
201
|
+
@property
|
|
202
|
+
def resolution(self) -> video_frame_pb2.VideoSourceResolution:
|
|
203
|
+
"""Encoded frame resolution. When omitted, it is discovered from the
|
|
204
|
+
pipeline's negotiated caps; when set, the pipeline output is verified
|
|
205
|
+
against it.
|
|
206
|
+
"""
|
|
207
|
+
|
|
208
|
+
@property
|
|
209
|
+
def rate_control(self) -> global___GstreamerRateControl:
|
|
210
|
+
"""Forwards WebRTC rate-control targets to an encoder element's bitrate
|
|
211
|
+
property. Without this, the pipeline encodes at a fixed bitrate.
|
|
212
|
+
"""
|
|
213
|
+
|
|
214
|
+
def __init__(
|
|
215
|
+
self,
|
|
216
|
+
*,
|
|
217
|
+
pipeline: builtins.str | None = ...,
|
|
218
|
+
codec: video_frame_pb2.VideoCodec.ValueType | None = ...,
|
|
219
|
+
resolution: video_frame_pb2.VideoSourceResolution | None = ...,
|
|
220
|
+
rate_control: global___GstreamerRateControl | None = ...,
|
|
221
|
+
) -> None: ...
|
|
222
|
+
def HasField(self, field_name: typing.Literal["codec", b"codec", "pipeline", b"pipeline", "rate_control", b"rate_control", "resolution", b"resolution"]) -> builtins.bool: ...
|
|
223
|
+
def ClearField(self, field_name: typing.Literal["codec", b"codec", "pipeline", b"pipeline", "rate_control", b"rate_control", "resolution", b"resolution"]) -> None: ...
|
|
224
|
+
|
|
225
|
+
global___GstreamerVideoSourceConfig = GstreamerVideoSourceConfig
|
|
226
|
+
|
|
113
227
|
@typing.final
|
|
114
228
|
class PatternVideoSourceConfig(google.protobuf.message.Message):
|
|
115
229
|
"""Test pattern rendered on the GPU."""
|
|
@@ -241,24 +355,28 @@ class NewCaptureSourceRequest(google.protobuf.message.Message):
|
|
|
241
355
|
|
|
242
356
|
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
|
243
357
|
|
|
358
|
+
GSTREAMER_FIELD_NUMBER: builtins.int
|
|
244
359
|
PATTERN_FIELD_NUMBER: builtins.int
|
|
245
360
|
CLOCK_FIELD_NUMBER: builtins.int
|
|
246
361
|
REQUEST_ASYNC_ID_FIELD_NUMBER: builtins.int
|
|
247
362
|
request_async_id: builtins.int
|
|
248
363
|
@property
|
|
364
|
+
def gstreamer(self) -> global___GstreamerVideoSourceConfig: ...
|
|
365
|
+
@property
|
|
249
366
|
def pattern(self) -> global___PatternVideoSourceConfig: ...
|
|
250
367
|
@property
|
|
251
368
|
def clock(self) -> global___ClockVideoSourceConfig: ...
|
|
252
369
|
def __init__(
|
|
253
370
|
self,
|
|
254
371
|
*,
|
|
372
|
+
gstreamer: global___GstreamerVideoSourceConfig | None = ...,
|
|
255
373
|
pattern: global___PatternVideoSourceConfig | None = ...,
|
|
256
374
|
clock: global___ClockVideoSourceConfig | None = ...,
|
|
257
375
|
request_async_id: builtins.int | None = ...,
|
|
258
376
|
) -> None: ...
|
|
259
|
-
def HasField(self, field_name: typing.Literal["clock", b"clock", "config", b"config", "pattern", b"pattern", "request_async_id", b"request_async_id"]) -> builtins.bool: ...
|
|
260
|
-
def ClearField(self, field_name: typing.Literal["clock", b"clock", "config", b"config", "pattern", b"pattern", "request_async_id", b"request_async_id"]) -> None: ...
|
|
261
|
-
def WhichOneof(self, oneof_group: typing.Literal["config", b"config"]) -> typing.Literal["pattern", "clock"] | None: ...
|
|
377
|
+
def HasField(self, field_name: typing.Literal["clock", b"clock", "config", b"config", "gstreamer", b"gstreamer", "pattern", b"pattern", "request_async_id", b"request_async_id"]) -> builtins.bool: ...
|
|
378
|
+
def ClearField(self, field_name: typing.Literal["clock", b"clock", "config", b"config", "gstreamer", b"gstreamer", "pattern", b"pattern", "request_async_id", b"request_async_id"]) -> None: ...
|
|
379
|
+
def WhichOneof(self, oneof_group: typing.Literal["config", b"config"]) -> typing.Literal["gstreamer", "pattern", "clock"] | None: ...
|
|
262
380
|
|
|
263
381
|
global___NewCaptureSourceRequest = NewCaptureSourceRequest
|
|
264
382
|
|
|
@@ -397,14 +515,17 @@ class CaptureError(google.protobuf.message.Message):
|
|
|
397
515
|
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
|
398
516
|
|
|
399
517
|
ERROR_FIELD_NUMBER: builtins.int
|
|
518
|
+
KIND_FIELD_NUMBER: builtins.int
|
|
400
519
|
error: builtins.str
|
|
520
|
+
kind: global___CaptureErrorKind.ValueType
|
|
401
521
|
def __init__(
|
|
402
522
|
self,
|
|
403
523
|
*,
|
|
404
524
|
error: builtins.str | None = ...,
|
|
525
|
+
kind: global___CaptureErrorKind.ValueType | None = ...,
|
|
405
526
|
) -> None: ...
|
|
406
|
-
def HasField(self, field_name: typing.Literal["error", b"error"]) -> builtins.bool: ...
|
|
407
|
-
def ClearField(self, field_name: typing.Literal["error", b"error"]) -> None: ...
|
|
527
|
+
def HasField(self, field_name: typing.Literal["error", b"error", "kind", b"kind"]) -> builtins.bool: ...
|
|
528
|
+
def ClearField(self, field_name: typing.Literal["error", b"error", "kind", b"kind"]) -> None: ...
|
|
408
529
|
|
|
409
530
|
global___CaptureError = CaptureError
|
|
410
531
|
|
|
@@ -113,6 +113,19 @@ class Queue(asyncio.Queue[T]):
|
|
|
113
113
|
|
|
114
114
|
self.task_done()
|
|
115
115
|
|
|
116
|
+
def release(self) -> None:
|
|
117
|
+
"""Mark every unfinished item as done.
|
|
118
|
+
|
|
119
|
+
Lets a ``join()`` that is already waiting on this queue return once there
|
|
120
|
+
is no consumer left for it.
|
|
121
|
+
"""
|
|
122
|
+
while True:
|
|
123
|
+
try:
|
|
124
|
+
self.task_done()
|
|
125
|
+
except ValueError:
|
|
126
|
+
# task_done() raises once every item is accounted for
|
|
127
|
+
return
|
|
128
|
+
|
|
116
129
|
|
|
117
130
|
class BroadcastQueue(Generic[T]):
|
|
118
131
|
"""Queue with multiple subscribers."""
|
|
@@ -135,6 +148,10 @@ class BroadcastQueue(Generic[T]):
|
|
|
135
148
|
|
|
136
149
|
def unsubscribe(self, queue: Queue[T]) -> None:
|
|
137
150
|
self._subscribers.remove(queue)
|
|
151
|
+
# A join() may already hold this queue. Events that are still queued, or that
|
|
152
|
+
# the subscriber took but never finished, have no consumer anymore, so
|
|
153
|
+
# release them instead of leaving the joiner waiting forever.
|
|
154
|
+
queue.release()
|
|
138
155
|
|
|
139
156
|
async def join(self) -> None:
|
|
140
157
|
async with self._lock:
|