Merge pull request #159 from pipecat-ai/create-pool-executor
transports: run threads in their own ThreadPoolExecutor
This commit is contained in:
@@ -5,6 +5,14 @@ All notable changes to **pipecat** will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fixed an issue where tasks and threads could be paused because the executor
|
||||||
|
didn't have more tasks available. This was causing issues when cancelling and
|
||||||
|
recreating tasks during interruptions.
|
||||||
|
|
||||||
## [0.0.19] - 2024-05-20
|
## [0.0.19] - 2024-05-20
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import queue
|
import queue
|
||||||
|
|
||||||
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
|
|
||||||
from pipecat.processors.frame_processor import FrameDirection, FrameProcessor
|
from pipecat.processors.frame_processor import FrameDirection, FrameProcessor
|
||||||
from pipecat.frames.frames import (
|
from pipecat.frames.frames import (
|
||||||
AudioRawFrame,
|
AudioRawFrame,
|
||||||
@@ -34,7 +36,9 @@ class BaseInputTransport(FrameProcessor):
|
|||||||
self._running = False
|
self._running = False
|
||||||
self._allow_interruptions = False
|
self._allow_interruptions = False
|
||||||
|
|
||||||
# Start media threads.
|
self._in_executor = ThreadPoolExecutor(max_workers=5)
|
||||||
|
|
||||||
|
# Create audio input queue if needed.
|
||||||
if self._params.audio_in_enabled or self._params.vad_enabled:
|
if self._params.audio_in_enabled or self._params.vad_enabled:
|
||||||
self._audio_in_queue = queue.Queue()
|
self._audio_in_queue = queue.Queue()
|
||||||
|
|
||||||
@@ -55,8 +59,10 @@ class BaseInputTransport(FrameProcessor):
|
|||||||
|
|
||||||
if self._params.audio_in_enabled or self._params.vad_enabled:
|
if self._params.audio_in_enabled or self._params.vad_enabled:
|
||||||
loop = self.get_event_loop()
|
loop = self.get_event_loop()
|
||||||
self._audio_in_thread = loop.run_in_executor(None, self._audio_in_thread_handler)
|
self._audio_in_thread = loop.run_in_executor(
|
||||||
self._audio_out_thread = loop.run_in_executor(None, self._audio_out_thread_handler)
|
self._in_executor, self._audio_in_thread_handler)
|
||||||
|
self._audio_out_thread = loop.run_in_executor(
|
||||||
|
self._in_executor, self._audio_out_thread_handler)
|
||||||
|
|
||||||
async def stop(self):
|
async def stop(self):
|
||||||
if not self._running:
|
if not self._running:
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import queue
|
|||||||
import time
|
import time
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
@@ -41,6 +43,8 @@ class BaseOutputTransport(FrameProcessor):
|
|||||||
self._running = False
|
self._running = False
|
||||||
self._allow_interruptions = False
|
self._allow_interruptions = False
|
||||||
|
|
||||||
|
self._out_executor = ThreadPoolExecutor(max_workers=5)
|
||||||
|
|
||||||
# These are the images that we should send to the camera at our desired
|
# These are the images that we should send to the camera at our desired
|
||||||
# framerate.
|
# framerate.
|
||||||
self._camera_images = None
|
self._camera_images = None
|
||||||
@@ -67,9 +71,10 @@ class BaseOutputTransport(FrameProcessor):
|
|||||||
loop = self.get_event_loop()
|
loop = self.get_event_loop()
|
||||||
|
|
||||||
if self._params.camera_out_enabled:
|
if self._params.camera_out_enabled:
|
||||||
self._camera_out_thread = loop.run_in_executor(None, self._camera_out_thread_handler)
|
self._camera_out_thread = loop.run_in_executor(
|
||||||
|
self._out_executor, self._camera_out_thread_handler)
|
||||||
|
|
||||||
self._sink_thread = loop.run_in_executor(None, self._sink_thread_handler)
|
self._sink_thread = loop.run_in_executor(self._out_executor, self._sink_thread_handler)
|
||||||
|
|
||||||
# Create push frame task. This is the task that will push frames in
|
# Create push frame task. This is the task that will push frames in
|
||||||
# order. We also guarantee that all frames are pushed in the same task.
|
# order. We also guarantee that all frames are pushed in the same task.
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
import inspect
|
import inspect
|
||||||
import queue
|
import queue
|
||||||
import time
|
import time
|
||||||
@@ -146,6 +147,8 @@ class DailyTransportClient(EventHandler):
|
|||||||
self._leaving = False
|
self._leaving = False
|
||||||
self._sync_response = {k: queue.Queue() for k in ["join", "leave"]}
|
self._sync_response = {k: queue.Queue() for k in ["join", "leave"]}
|
||||||
|
|
||||||
|
self._executor = ThreadPoolExecutor(max_workers=5)
|
||||||
|
|
||||||
self._client: CallClient = CallClient(event_handler=self)
|
self._client: CallClient = CallClient(event_handler=self)
|
||||||
|
|
||||||
self._camera: VirtualCameraDevice = Daily.create_camera_device(
|
self._camera: VirtualCameraDevice = Daily.create_camera_device(
|
||||||
@@ -195,7 +198,7 @@ class DailyTransportClient(EventHandler):
|
|||||||
self._joining = True
|
self._joining = True
|
||||||
|
|
||||||
loop = asyncio.get_running_loop()
|
loop = asyncio.get_running_loop()
|
||||||
await loop.run_in_executor(None, self._join)
|
await loop.run_in_executor(self._executor, self._join)
|
||||||
|
|
||||||
def _join(self):
|
def _join(self):
|
||||||
logger.info(f"Joining {self._room_url}")
|
logger.info(f"Joining {self._room_url}")
|
||||||
@@ -287,7 +290,7 @@ class DailyTransportClient(EventHandler):
|
|||||||
self._leaving = True
|
self._leaving = True
|
||||||
|
|
||||||
loop = asyncio.get_running_loop()
|
loop = asyncio.get_running_loop()
|
||||||
await loop.run_in_executor(None, self._leave)
|
await loop.run_in_executor(self._executor, self._leave)
|
||||||
|
|
||||||
def _leave(self):
|
def _leave(self):
|
||||||
logger.info(f"Leaving {self._room_url}")
|
logger.info(f"Leaving {self._room_url}")
|
||||||
@@ -318,7 +321,7 @@ class DailyTransportClient(EventHandler):
|
|||||||
|
|
||||||
async def cleanup(self):
|
async def cleanup(self):
|
||||||
loop = asyncio.get_running_loop()
|
loop = asyncio.get_running_loop()
|
||||||
await loop.run_in_executor(None, self._cleanup)
|
await loop.run_in_executor(self._executor, self._cleanup)
|
||||||
|
|
||||||
def _cleanup(self):
|
def _cleanup(self):
|
||||||
if self._client:
|
if self._client:
|
||||||
@@ -438,7 +441,8 @@ class DailyInputTransport(BaseInputTransport):
|
|||||||
await super().start(frame)
|
await super().start(frame)
|
||||||
# Create camera in thread (runs if _running is true).
|
# Create camera in thread (runs if _running is true).
|
||||||
loop = asyncio.get_running_loop()
|
loop = asyncio.get_running_loop()
|
||||||
self._camera_in_thread = loop.run_in_executor(None, self._camera_in_thread_handler)
|
self._camera_in_thread = loop.run_in_executor(
|
||||||
|
self._in_executor, self._camera_in_thread_handler)
|
||||||
|
|
||||||
async def stop(self):
|
async def stop(self):
|
||||||
if not self._running:
|
if not self._running:
|
||||||
|
|||||||
Reference in New Issue
Block a user