Skip to content

Commit 74185c8

Browse files
authored
feat: room options & audio resampler (#17)
1 parent e9d674c commit 74185c8

File tree

5 files changed

+48
-16
lines changed

5 files changed

+48
-16
lines changed

examples/basic_room.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,12 @@ def on_local_track_unpublished(publication: livekit.LocalTrackPublication):
3434
logging.info("local track unpublished: %s", publication.sid)
3535

3636
@room.listens_to("track_published")
37-
def on_track_published(publication: livekit.LocalTrackPublication, participant: livekit.RemoteParticipant):
37+
def on_track_published(publication: livekit.RemoteTrackPublication, participant: livekit.RemoteParticipant):
3838
logging.info("track published: %s from participant %s (%s)",
3939
publication.sid, participant.sid, participant.identity)
4040

4141
@room.listens_to("track_unpublished")
42-
def on_track_unpublished(publication: livekit.LocalTrackPublication, participant: livekit.RemoteParticipant):
42+
def on_track_unpublished(publication: livekit.RemoteTrackPublication, participant: livekit.RemoteParticipant):
4343
logging.info("track unpublished: %s", publication.sid)
4444

4545
# Keep a reference to the streams, otherwise they will be disposed

livekit/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from .audio_source import AudioSource
1515
from .audio_stream import AudioStream
1616
from .participant import LocalParticipant, Participant, RemoteParticipant
17-
from .room import ConnectError, Room
17+
from .room import ConnectError, Room, RoomOptions
1818
from .track import (
1919
LocalAudioTrack,
2020
LocalVideoTrack,

livekit/audio_frame.py

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

33
from ._ffi_client import FfiHandle, ffi_client
4-
from ._proto import audio_frame_pb2 as proto_audio_frame
4+
from ._proto import audio_frame_pb2 as proto_audio
55
from ._proto import ffi_pb2 as proto_ffi
66

77

88
class AudioFrame():
9-
def __init__(self, info: proto_audio_frame.AudioFrameBufferInfo, ffi_handle: FfiHandle) -> None:
9+
def __init__(self, info: proto_audio.AudioFrameBufferInfo, ffi_handle: FfiHandle) -> None:
1010
self._info = info
1111
self._ffi_handle = ffi_handle
1212

@@ -29,6 +29,27 @@ def create(sample_rate: int, num_channels: int, samples_per_channel: int) -> 'Au
2929

3030
return AudioFrame(info, ffi_handle)
3131

32+
def remix_and_resample(self, sample_rate: int, num_channels: int) -> 'AudioFrame':
33+
""" Resample the audio frame to the given sample rate and number of channels."""
34+
35+
req = proto_ffi.FfiRequest()
36+
req.new_audio_resampler.CopyFrom(
37+
proto_audio.NewAudioResamplerRequest())
38+
39+
resp = ffi_client.request(req)
40+
resampler_handle = FfiHandle(resp.new_audio_resampler.handle.id)
41+
42+
resample_req = proto_ffi.FfiRequest()
43+
resample_req.remix_and_resample.resampler_handle.id = resampler_handle.handle
44+
resample_req.remix_and_resample.buffer_handle.id = self._ffi_handle.handle
45+
resample_req.remix_and_resample.sample_rate = sample_rate
46+
resample_req.remix_and_resample.num_channels = num_channels
47+
48+
resp = ffi_client.request(resample_req)
49+
info = resp.remix_and_resample.buffer
50+
ffi_handle = FfiHandle(info.handle.id)
51+
return AudioFrame(info, ffi_handle)
52+
3253
@property
3354
def sample_rate(self) -> int:
3455
return self._info.sample_rate

livekit/participant.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,17 @@
2222

2323

2424
class PublishTrackError(Exception):
25-
def __init__(self, message: str):
25+
def __init__(self, message: str) -> None:
2626
self.message = message
2727

2828

2929
class PublishDataError(Exception):
30-
def __init__(self, message: str):
30+
def __init__(self, message: str) -> None:
3131
self.message = message
3232

3333

3434
class Participant():
35-
def __init__(self, info: proto_participant.ParticipantInfo):
35+
def __init__(self, info: proto_participant.ParticipantInfo) -> None:
3636
self._info = info
3737
self.tracks: dict[str, TrackPublication] = {}
3838

@@ -54,7 +54,7 @@ def metadata(self) -> str:
5454

5555

5656
class LocalParticipant(Participant):
57-
def __init__(self, info: proto_participant.ParticipantInfo, room: weakref.ref['Room']):
57+
def __init__(self, info: proto_participant.ParticipantInfo, room: weakref.ref['Room']) -> None:
5858
super().__init__(info)
5959
self._room = room
6060
self.tracks: dict[str, LocalTrackPublication] = {}
@@ -75,8 +75,6 @@ async def publish_data(self,
7575

7676
cdata = (ctypes.c_byte * data_len)(*payload)
7777

78-
79-
8078
req = proto_ffi.FfiRequest()
8179
req.publish_data.room_handle.id = room._ffi_handle.handle
8280
req.publish_data.data_ptr = ctypes.addressof(cdata)
@@ -146,6 +144,6 @@ def on_publish_callback(cb: proto_room.PublishTrackCallback):
146144

147145

148146
class RemoteParticipant(Participant):
149-
def __init__(self, info: proto_participant.ParticipantInfo):
147+
def __init__(self, info: proto_participant.ParticipantInfo) -> None:
150148
super().__init__(info)
151149
self.tracks: dict[str, RemoteTrackPublication] = {}

livekit/room.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import asyncio
22
import ctypes
33
import weakref
4+
from dataclasses import dataclass
45

56
from pyee.asyncio import AsyncIOEventEmitter
67

@@ -16,6 +17,12 @@
1617
from .track_publication import LocalTrackPublication, RemoteTrackPublication
1718

1819

20+
@dataclass
21+
class RoomOptions:
22+
auto_subscribe: bool = True
23+
dynacast: bool = True
24+
25+
1926
class ConnectError(Exception):
2027
def __init__(self, message: str):
2128
self.message = message
@@ -47,14 +54,18 @@ def metadata(self) -> str:
4754
def isconnected(self) -> bool:
4855
return self._ffi_handle is not None and self.connection_state == ConnectionState.CONN_CONNECTED
4956

50-
async def connect(self, url: str, token: str) -> None:
57+
async def connect(self, url: str, token: str, options: RoomOptions = RoomOptions()) -> None:
5158
# TODO(theomonnom): We should be more flexible about the event loop
5259
ffi_client.set_event_loop(asyncio.get_running_loop())
5360

5461
req = proto_ffi.FfiRequest()
5562
req.connect.url = url
5663
req.connect.token = token
5764

65+
# options
66+
req.connect.options.auto_subscribe = options.auto_subscribe
67+
req.connect.options.dynacast = options.dynacast
68+
5869
resp = ffi_client.request(req)
5970
future: asyncio.Future[proto_room.ConnectCallback] = asyncio.Future()
6071

@@ -127,12 +138,14 @@ def _on_room_event(self, event: proto_room.RoomEvent):
127138

128139
if track_info.kind == TrackKind.KIND_VIDEO:
129140
local_video_track = LocalVideoTrack(ffi_handle, track_info)
130-
publication.track = local_video_track
131-
self.emit('local_track_published', publication, local_video_track)
141+
publication.track = local_video_track
142+
self.emit('local_track_published',
143+
publication, local_video_track)
132144
elif track_info.kind == TrackKind.KIND_AUDIO:
133145
local_audio_track = LocalAudioTrack(ffi_handle, track_info)
134146
publication.track = local_audio_track
135-
self.emit('local_track_published', publication, local_audio_track)
147+
self.emit('local_track_published',
148+
publication, local_audio_track)
136149
elif which == 'local_track_unpublished':
137150
publication = self.local_participant.tracks.pop(
138151
event.local_track_unpublished.publication_sid)

0 commit comments

Comments
 (0)