Skip to content

Commit 2f6b687

Browse files
committed
improve type hints
1 parent 95f8318 commit 2f6b687

File tree

14 files changed

+53
-86
lines changed

14 files changed

+53
-86
lines changed

examples/publish_hue.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ async def publish_frames(source: livekit.VideoSource):
2525
0, livekit.VideoRotation.VIDEO_ROTATION_0, argb_frame.to_i420())
2626

2727
rgb = colorsys.hsv_to_rgb(hue, 1.0, 1.0)
28-
rgb = [int(x * 255) for x in rgb]
28+
rgb = [(x * 255) for x in rgb] # type: ignore
2929

3030
argb_color = np.array(rgb + [255], dtype=np.uint8)
3131
arr.flat[::4] = argb_color[0]

examples/publish_wave.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ async def main() -> None:
5050
logging.info("connected to room %s", room.name)
5151
except livekit.ConnectError as e:
5252
logging.error("failed to connect to the room: %s", e)
53-
return False
53+
return
5454

5555
# publish a track
5656
source = livekit.AudioSource()

livekit/_ffi_client.py

Lines changed: 13 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -33,59 +33,47 @@
3333

3434
INVALID_HANDLE = 0
3535

36-
37-
@ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.POINTER(ctypes.c_uint8), ctypes.c_size_t)
38-
def ffi_event_callback(data_ptr: ctypes.POINTER(ctypes.c_uint8), data_len: ctypes.c_size_t) -> None: # type: ignore
36+
@ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.POINTER(ctypes.c_uint8), ctypes.c_size_t)
37+
def ffi_event_callback(data_ptr: ctypes.POINTER(ctypes.c_uint8), data_len: ctypes.c_size_t) -> None: # type: ignore
3938
event_data = bytes(data_ptr[:int(data_len)])
4039
event = proto_ffi.FfiEvent()
4140
event.ParseFromString(event_data)
4241

43-
ffi_client = FfiClient()
4442
with ffi_client._lock:
4543
loop = ffi_client._event_loop
4644

4745
loop.call_soon_threadsafe(dispatch_event, event)
4846

4947

5048
def dispatch_event(event: proto_ffi.FfiEvent) -> None:
51-
ffi_client = FfiClient()
5249
which = str(event.WhichOneof('message'))
5350
ffi_client.emit(which, getattr(event, which))
5451

5552

56-
class Singleton(type):
57-
_instances = {}
58-
59-
def __call__(cls, *args, **kwargs):
60-
if cls not in cls._instances:
61-
cls._instances[cls] = super(
62-
Singleton, cls).__call__(*args, **kwargs)
63-
return cls._instances[cls]
64-
65-
66-
class FfiClient(EventEmitter, metaclass=Singleton):
53+
class FfiClient(EventEmitter):
6754
def __init__(self) -> None:
6855
super().__init__()
6956
self._lock = threading.Lock()
7057

7158
req = proto_ffi.FfiRequest()
72-
req.initialize.event_callback_ptr = ctypes.cast(
73-
ffi_event_callback, ctypes.c_void_p).value
59+
cb_callback = int(ctypes.cast(
60+
ffi_event_callback, ctypes.c_void_p).value) # type: ignore
61+
req.initialize.event_callback_ptr = cb_callback
7462
self.request(req)
7563

7664
def set_event_loop(self, loop: asyncio.AbstractEventLoop) -> None:
7765
with self._lock:
7866
self._event_loop = loop
7967

8068
def request(self, req: proto_ffi.FfiRequest) -> proto_ffi.FfiResponse:
81-
data = req.SerializeToString()
82-
data_len = len(data)
83-
data = (ctypes.c_ubyte * data_len)(*data)
69+
proto_data = req.SerializeToString()
70+
proto_len = len(proto_data)
71+
data = (ctypes.c_ubyte * proto_len)(*proto_data)
8472

8573
resp_ptr = ctypes.POINTER(ctypes.c_ubyte)()
8674
resp_len = ctypes.c_size_t()
8775
handle = ffi_lib.livekit_ffi_request(
88-
data, data_len, ctypes.byref(resp_ptr), ctypes.byref(resp_len))
76+
data, proto_len, ctypes.byref(resp_ptr), ctypes.byref(resp_len))
8977

9078
resp_data = bytes(resp_ptr[:resp_len.value])
9179
resp = proto_ffi.FfiResponse()
@@ -94,7 +82,6 @@ def request(self, req: proto_ffi.FfiRequest) -> proto_ffi.FfiResponse:
9482
FfiHandle(handle)
9583
return resp
9684

97-
9885
class FfiHandle:
9986
handle = INVALID_HANDLE
10087

@@ -105,3 +92,6 @@ def __del__(self):
10592
if self.handle != INVALID_HANDLE:
10693
assert ffi_lib.livekit_ffi_drop_handle(
10794
ctypes.c_size_t(self.handle))
95+
96+
97+
ffi_client = FfiClient()

livekit/audio_frame.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import ctypes
22

3-
from ._ffi_client import FfiClient, FfiHandle
3+
from ._ffi_client import FfiHandle, ffi_client
44
from ._proto import audio_frame_pb2 as proto_audio_frame
55
from ._proto import ffi_pb2 as proto_ffi
66

@@ -22,7 +22,6 @@ def create(sample_rate: int, num_channels: int, samples_per_channel: int) -> 'Au
2222
req.alloc_audio_buffer.num_channels = num_channels
2323
req.alloc_audio_buffer.samples_per_channel = samples_per_channel
2424

25-
ffi_client = FfiClient()
2625
resp = ffi_client.request(req)
2726

2827
info = resp.alloc_audio_buffer.buffer

livekit/audio_source.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from livekit import AudioFrame
22

3-
from ._ffi_client import FfiClient, FfiHandle
3+
from ._ffi_client import FfiHandle, ffi_client
44
from ._proto import audio_frame_pb2 as proto_audio_frame
55
from ._proto import ffi_pb2 as proto_ffi
66

@@ -10,7 +10,6 @@ def __init__(self) -> None:
1010
req = proto_ffi.FfiRequest()
1111
req.new_audio_source.type = proto_audio_frame.AudioSourceType.AUDIO_SOURCE_NATIVE
1212

13-
ffi_client = FfiClient()
1413
resp = ffi_client.request(req)
1514
self._info = resp.new_audio_source.source
1615
self._ffi_handle = FfiHandle(self._info.handle.id)
@@ -21,5 +20,4 @@ def capture_frame(self, frame: AudioFrame) -> None:
2120
req.capture_audio_frame.source_handle.id = self._ffi_handle.handle
2221
req.capture_audio_frame.buffer_handle.id = frame._ffi_handle.handle
2322

24-
ffi_client = FfiClient()
2523
ffi_client.request(req)

livekit/audio_stream.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
from livekit import Track
66

7-
from ._ffi_client import FfiClient, FfiHandle
7+
from ._ffi_client import FfiHandle, ffi_client
88
from ._proto import audio_frame_pb2 as proto_audio_frame
99
from ._proto import ffi_pb2 as proto_ffi
1010
from .audio_frame import AudioFrame
@@ -20,18 +20,17 @@ def initalize(cls) -> None:
2020
return
2121

2222
cls._initialized = True
23-
ffi_client = FfiClient()
2423
# See VideoStream for the reason we don't use the instance method for the listener
2524
ffi_client.add_listener('audio_stream_event',
2625
cls._on_audio_stream_event)
2726

2827
@classmethod
2928
def _on_audio_stream_event(cls, event: proto_audio_frame.AudioStreamEvent) -> None:
30-
stream = cls._streams.get(event.handle.id)
31-
if stream is None:
29+
weak_stream = cls._streams.get(event.handle.id)
30+
if weak_stream is None:
3231
return
3332

34-
stream = stream()
33+
stream = weak_stream()
3534
if stream is None:
3635
return
3736

@@ -51,7 +50,6 @@ def __init__(self, track: Track) -> None:
5150
new_audio_stream.track_handle.id = track._ffi_handle.handle
5251
new_audio_stream.type = proto_audio_frame.AudioStreamType.AUDIO_STREAM_NATIVE
5352

54-
ffi_client = FfiClient()
5553
resp = ffi_client.request(req)
5654
stream_info = resp.new_audio_stream.stream
5755

livekit/participant.py

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,21 @@
11
import asyncio
22
import ctypes
33
import weakref
4-
from typing import TYPE_CHECKING
4+
from typing import TYPE_CHECKING, List, Optional, Union
5+
from weakref import ref
56

67
from livekit import DataPacketKind, TrackPublishOptions
78

8-
from ._ffi_client import FfiClient
9+
from ._ffi_client import ffi_client
910
from ._proto import ffi_pb2 as proto_ffi
1011
from ._proto import participant_pb2 as proto_participant
1112
from ._proto import room_pb2 as proto_room
1213
from .track import LocalAudioTrack, LocalVideoTrack, Track
13-
from .track_publication import TrackPublication
14+
from .track_publication import (
15+
LocalTrackPublication,
16+
RemoteTrackPublication,
17+
TrackPublication,
18+
)
1419

1520
if TYPE_CHECKING:
1621
from livekit import Room
@@ -52,12 +57,12 @@ class LocalParticipant(Participant):
5257
def __init__(self, info: proto_participant.ParticipantInfo, room: weakref.ref['Room']):
5358
super().__init__(info)
5459
self._room = room
60+
self.tracks: dict[str, LocalTrackPublication] = {}
5561

5662
async def publish_data(self,
57-
# TODO(theomonnom): Allow ctypes.Array as payload?
58-
payload: bytes or str,
63+
payload: Union[bytes, str],
5964
kind: DataPacketKind.ValueType = DataPacketKind.KIND_RELIABLE,
60-
destination_sids: list[str] or list['RemoteParticipant'] = []) -> None:
65+
destination_sids: Optional[Union[List[str], List['RemoteParticipant']]] = None) -> None:
6166

6267
room = self._room()
6368
if room is None:
@@ -70,21 +75,24 @@ async def publish_data(self,
7075

7176
cdata = (ctypes.c_byte * data_len)(*payload)
7277

73-
sids = []
74-
for p in destination_sids:
75-
if isinstance(p, RemoteParticipant):
76-
sids.append(p.sid)
77-
else:
78-
sids.append(p)
78+
7979

8080
req = proto_ffi.FfiRequest()
8181
req.publish_data.room_handle.id = room._ffi_handle.handle
8282
req.publish_data.data_ptr = ctypes.addressof(cdata)
8383
req.publish_data.data_size = data_len
8484
req.publish_data.kind = kind
85-
req.publish_data.destination_sids.extend(sids)
8685

87-
ffi_client = FfiClient()
86+
if destination_sids is not None:
87+
sids = []
88+
for p in destination_sids:
89+
if isinstance(p, RemoteParticipant):
90+
sids.append(p.sid)
91+
else:
92+
sids.append(p)
93+
94+
req.publish_data.destination_sids.extend(sids)
95+
8896
resp = ffi_client.request(req)
8997
future: asyncio.Future[proto_room.PublishDataCallback] = asyncio.Future(
9098
)
@@ -113,8 +121,6 @@ async def publish_track(self, track: Track, options: TrackPublishOptions) -> Tra
113121
req.publish_track.room_handle.id = room._ffi_handle.handle
114122
req.publish_track.options.CopyFrom(options)
115123

116-
ffi_client = FfiClient()
117-
118124
resp = ffi_client.request(req)
119125

120126
future: asyncio.Future[proto_room.PublishTrackCallback] = asyncio.Future(
@@ -132,7 +138,7 @@ def on_publish_callback(cb: proto_room.PublishTrackCallback):
132138
if cb.error:
133139
raise PublishTrackError(cb.error)
134140

135-
track_publication = TrackPublication(cb.publication)
141+
track_publication = LocalTrackPublication(cb.publication, ref(self))
136142
track_publication.track = track
137143
self.tracks[track_publication.sid] = track_publication
138144
# TODO: Update track info
@@ -142,3 +148,4 @@ def on_publish_callback(cb: proto_room.PublishTrackCallback):
142148
class RemoteParticipant(Participant):
143149
def __init__(self, info: proto_participant.ParticipantInfo):
144150
super().__init__(info)
151+
self.tracks: dict[str, RemoteTrackPublication] = {}

livekit/room.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
from livekit import ConnectionState, TrackKind
88

9-
from ._ffi_client import FfiClient, FfiHandle
9+
from ._ffi_client import FfiHandle, ffi_client
1010
from ._proto import ffi_pb2 as proto_ffi
1111
from ._proto import participant_pb2 as proto_participant
1212
from ._proto import room_pb2 as proto_room
@@ -26,11 +26,9 @@ def __init__(self) -> None:
2626
self.participants: dict[str, RemoteParticipant] = {}
2727
self.connection_state = ConnectionState.CONN_DISCONNECTED
2828

29-
ffi_client = FfiClient()
3029
ffi_client.add_listener('room_event', self._on_room_event)
3130

3231
def __del__(self):
33-
ffi_client = FfiClient()
3432
ffi_client.remove_listener('room_event', self._on_room_event)
3533

3634
@property
@@ -50,7 +48,6 @@ def isconnected(self) -> bool:
5048

5149
async def connect(self, url: str, token: str) -> None:
5250
# TODO(theomonnom): We should be more flexible about the event loop
53-
ffi_client = FfiClient()
5451
ffi_client.set_event_loop(asyncio.get_running_loop())
5552

5653
req = proto_ffi.FfiRequest()
@@ -84,8 +81,6 @@ async def disconnect(self) -> None:
8481
if not self.isconnected():
8582
return
8683

87-
ffi_client = FfiClient()
88-
8984
req = proto_ffi.FfiRequest()
9085
req.disconnect.room_handle.id = self._ffi_handle.handle
9186

livekit/track.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from typing import TYPE_CHECKING
22

3-
from ._ffi_client import FfiClient, FfiHandle
3+
from ._ffi_client import FfiHandle, ffi_client
44
from ._proto import ffi_pb2 as proto_ffi
55
from ._proto import track_pb2 as proto_track
66

@@ -47,7 +47,6 @@ def create_audio_track(name: str, source: 'AudioSource') -> 'LocalAudioTrack':
4747
req.create_audio_track.name = name
4848
req.create_audio_track.source_handle.id = source._ffi_handle.handle
4949

50-
ffi_client = FfiClient()
5150
resp = ffi_client.request(req)
5251
track_info = resp.create_audio_track.track
5352
ffi_handle = FfiHandle(track_info.handle.id)
@@ -64,7 +63,6 @@ def create_video_track(name: str, source: 'VideoSource') -> 'LocalVideoTrack':
6463
req.create_video_track.name = name
6564
req.create_video_track.source_handle.id = source._ffi_handle.handle
6665

67-
ffi_client = FfiClient()
6866
resp = ffi_client.request(req)
6967
track_info = resp.create_video_track.track
7068
ffi_handle = FfiHandle(track_info.handle.id)

livekit/track_publication.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import weakref
2-
from typing import TYPE_CHECKING
2+
from typing import TYPE_CHECKING, Optional
33

44
from livekit._proto import track_pb2 as proto_track
55

6-
from ._ffi_client import FfiClient
6+
from ._ffi_client import ffi_client
77
from ._proto import ffi_pb2 as proto_ffi
8-
from ._proto import track_pb2 as proto_track
98
from .track import Track
109

1110
if TYPE_CHECKING:
@@ -15,7 +14,7 @@
1514
class TrackPublication():
1615
def __init__(self, info: proto_track.TrackPublicationInfo):
1716
self._info = info
18-
self.track: Track = None
17+
self.track: Optional[Track] = None
1918

2019
@property
2120
def sid(self) -> str:
@@ -76,5 +75,4 @@ def set_subscribed(self, subscribed: bool):
7675
req.set_subscribed.participant_sid = participant.sid
7776
req.set_subscribed.subscribe = subscribed
7877

79-
ffi_client = FfiClient()
8078
ffi_client.request(req)

0 commit comments

Comments
 (0)