Support coroutine callbacks
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
import asyncio
|
||||||
import inspect
|
import inspect
|
||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
@@ -42,13 +43,29 @@ class DailyTransportService(EventHandler):
|
|||||||
self.mic_sample_rate = 16000
|
self.mic_sample_rate = 16000
|
||||||
self.camera_enabled = False
|
self.camera_enabled = False
|
||||||
|
|
||||||
|
self.camera_thread = None
|
||||||
|
self.frame_consumer_thread = None
|
||||||
|
|
||||||
self.logger: logging.Logger = logging.getLogger("dailyai")
|
self.logger: logging.Logger = logging.getLogger("dailyai")
|
||||||
|
|
||||||
self.event_handlers = {}
|
self.event_handlers = {}
|
||||||
|
|
||||||
def monkeypatch(self, event_name, *args):
|
try:
|
||||||
|
self.loop = asyncio.get_running_loop()
|
||||||
|
except RuntimeError:
|
||||||
|
self.loop = None
|
||||||
|
|
||||||
|
def patch_method(self, event_name, *args):
|
||||||
for handler in self.event_handlers[event_name]:
|
for handler in self.event_handlers[event_name]:
|
||||||
handler(*args)
|
if inspect.iscoroutinefunction(handler):
|
||||||
|
if self.loop:
|
||||||
|
future = asyncio.run_coroutine_threadsafe(handler(*args), self.loop)
|
||||||
|
#concurrent.futures.wait(future)
|
||||||
|
else:
|
||||||
|
raise Exception("No event loop to run coroutine. In order to use async event handlers, you must run the DailyTransportService in an asyncio event loop.")
|
||||||
|
asyncio.run(handler(*args))
|
||||||
|
else:
|
||||||
|
handler(*args)
|
||||||
|
|
||||||
def add_event_handler(self, event_name: str, handler):
|
def add_event_handler(self, event_name: str, handler):
|
||||||
if not event_name.startswith("on_"):
|
if not event_name.startswith("on_"):
|
||||||
@@ -60,7 +77,7 @@ class DailyTransportService(EventHandler):
|
|||||||
|
|
||||||
if not event_name in self.event_handlers:
|
if not event_name in self.event_handlers:
|
||||||
self.event_handlers[event_name] = [getattr(self, event_name), types.MethodType(handler, self)]
|
self.event_handlers[event_name] = [getattr(self, event_name), types.MethodType(handler, self)]
|
||||||
setattr(self, event_name, partial(self.monkeypatch, event_name))
|
setattr(self, event_name, partial(self.patch_method, event_name))
|
||||||
else:
|
else:
|
||||||
self.event_handlers[event_name].append(types.MethodType(handler, self))
|
self.event_handlers[event_name].append(types.MethodType(handler, self))
|
||||||
|
|
||||||
@@ -191,6 +208,9 @@ class DailyTransportService(EventHandler):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def on_call_state_updated(self, state):
|
||||||
|
pass
|
||||||
|
|
||||||
def on_participant_joined(self, participant):
|
def on_participant_joined(self, participant):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -224,7 +244,6 @@ class DailyTransportService(EventHandler):
|
|||||||
time.sleep(1.0 / 8) # 8 fps
|
time.sleep(1.0 / 8) # 8 fps
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(f"Exception {e} in camera thread.")
|
self.logger.error(f"Exception {e} in camera thread.")
|
||||||
print("exiting run_camera thread")
|
|
||||||
|
|
||||||
def frame_consumer(self):
|
def frame_consumer(self):
|
||||||
self.logger.info("🎬 Starting frame consumer thread")
|
self.logger.info("🎬 Starting frame consumer thread")
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
|
import time
|
||||||
from typing import AsyncGenerator
|
from typing import AsyncGenerator
|
||||||
|
|
||||||
from dailyai.output_queue import OutputQueueFrame, FrameType
|
from dailyai.output_queue import OutputQueueFrame, FrameType
|
||||||
@@ -14,7 +15,7 @@ async def main(room_url):
|
|||||||
#
|
#
|
||||||
# the abstract transport service APIs presumably can map pretty closely
|
# the abstract transport service APIs presumably can map pretty closely
|
||||||
# to the daily-python basic API
|
# to the daily-python basic API
|
||||||
meeting_duration_minutes = 4
|
meeting_duration_minutes = 1
|
||||||
transport = DailyTransportService(
|
transport = DailyTransportService(
|
||||||
room_url,
|
room_url,
|
||||||
None,
|
None,
|
||||||
@@ -26,20 +27,17 @@ async def main(room_url):
|
|||||||
# similarly, create a tts service
|
# similarly, create a tts service
|
||||||
tts = AzureTTSService()
|
tts = AzureTTSService()
|
||||||
|
|
||||||
async def play_audio(transport, audio_generator):
|
|
||||||
async for audio in audio_generator:
|
|
||||||
transport.output_queue.put(OutputQueueFrame(FrameType.AUDIO_FRAME, audio))
|
|
||||||
|
|
||||||
# Get the generator for the audio. This will start running in the background,
|
# Get the generator for the audio. This will start running in the background,
|
||||||
# and when we ask the generator for its items, we'll get what it's generated.
|
# and when we ask the generator for its items, we'll get what it's generated.
|
||||||
audio_generator: AsyncGenerator[bytes, None] = tts.run_tts("hello world")
|
audio_generator: AsyncGenerator[bytes, None] = tts.run_tts("hello world")
|
||||||
|
|
||||||
# Register an event handler so we can play the audio when the participant joins.
|
# Register an event handler so we can play the audio when the participant joins.
|
||||||
@transport.event_handler("on_participant_joined")
|
@transport.event_handler("on_participant_joined")
|
||||||
def on_participant_joined(transport, participant):
|
async def on_participant_joined(transport, participant):
|
||||||
print("participant joined", participant)
|
if participant["info"]["isLocal"]:
|
||||||
asyncio.run(play_audio(transport, audio_generator))
|
return
|
||||||
transport.stop()
|
async for audio in audio_generator:
|
||||||
|
transport.output_queue.put(OutputQueueFrame(FrameType.AUDIO_FRAME, audio))
|
||||||
|
|
||||||
transport.run()
|
transport.run()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user