Merge pull request #159 from pipecat-ai/create-pool-executor

transports: run threads in their own ThreadPoolExecutor
This commit is contained in:
Aleix Conchillo Flaqué
2024-05-22 15:49:03 +08:00
committed by GitHub
4 changed files with 32 additions and 9 deletions

View File

@@ -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/),
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
### Changed

View File

@@ -7,6 +7,8 @@
import asyncio
import queue
from concurrent.futures import ThreadPoolExecutor
from pipecat.processors.frame_processor import FrameDirection, FrameProcessor
from pipecat.frames.frames import (
AudioRawFrame,
@@ -34,7 +36,9 @@ class BaseInputTransport(FrameProcessor):
self._running = 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:
self._audio_in_queue = queue.Queue()
@@ -55,8 +59,10 @@ class BaseInputTransport(FrameProcessor):
if self._params.audio_in_enabled or self._params.vad_enabled:
loop = self.get_event_loop()
self._audio_in_thread = loop.run_in_executor(None, self._audio_in_thread_handler)
self._audio_out_thread = loop.run_in_executor(None, self._audio_out_thread_handler)
self._audio_in_thread = loop.run_in_executor(
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):
if not self._running:

View File

@@ -11,6 +11,8 @@ import queue
import time
import threading
from concurrent.futures import ThreadPoolExecutor
from PIL import Image
from typing import List
@@ -41,6 +43,8 @@ class BaseOutputTransport(FrameProcessor):
self._running = 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
# framerate.
self._camera_images = None
@@ -67,9 +71,10 @@ class BaseOutputTransport(FrameProcessor):
loop = self.get_event_loop()
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
# order. We also guarantee that all frames are pushed in the same task.

View File

@@ -5,6 +5,7 @@
#
import asyncio
from concurrent.futures import ThreadPoolExecutor
import inspect
import queue
import time
@@ -146,6 +147,8 @@ class DailyTransportClient(EventHandler):
self._leaving = False
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._camera: VirtualCameraDevice = Daily.create_camera_device(
@@ -195,7 +198,7 @@ class DailyTransportClient(EventHandler):
self._joining = True
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):
logger.info(f"Joining {self._room_url}")
@@ -287,7 +290,7 @@ class DailyTransportClient(EventHandler):
self._leaving = True
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):
logger.info(f"Leaving {self._room_url}")
@@ -318,7 +321,7 @@ class DailyTransportClient(EventHandler):
async def cleanup(self):
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):
if self._client:
@@ -438,7 +441,8 @@ class DailyInputTransport(BaseInputTransport):
await super().start(frame)
# Create camera in thread (runs if _running is true).
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):
if not self._running: