### Start, Stop, and Handle Cloud Recording Events Source: https://context7.com/daily-co/daily-python/llms.txt This example shows how to initiate a cloud recording with custom streaming settings, including video/audio bitrates, dimensions, and maximum duration. It also includes event handlers for recording start, stop, and errors. ```python import threading from daily import Daily, CallClient, EventHandler Daily.init() class RecordingBot(EventHandler): def on_recording_started(self, status): print(f"Recording started! id={status['recordingId']}, stream={status['streamId']}") def on_recording_stopped(self, stream_id: str): print(f"Recording stopped: {stream_id}") def on_recording_error(self, stream_id: str, message: str): print(f"Recording error [{stream_id}]: {message}") client = CallClient(event_handler=RecordingBot()) start_event = threading.Event() client.join( "https://mycompany.daily.co/my-room", meeting_token="OWNER_TOKEN", completion=lambda d, e: start_event.set(), ) start_event.wait() # Start recording with custom settings client.start_recording( streaming_settings={ "video": {"width": 1280, "height": 720, "fps": 30, "bitrate": 2000}, "audio": {"bitrate": 128}, "maxDuration": 3600, }, completion=lambda stream_id, err: print(f"Recording stream: {stream_id}" if not err else f"Error: {err}"), ) input("Press Enter to stop recording...\n") client.stop_recording() input("Press Enter to leave...\n") client.leave() client.release() ``` -------------------------------- ### Install Project Dependencies Source: https://github.com/daily-co/daily-python/blob/main/demos/README.md Install all required Python packages for the demos using pip. ```bash pip3 install -r requirements.txt ``` -------------------------------- ### Install and Upgrade daily-python Source: https://context7.com/daily-co/daily-python/llms.txt Install the Daily Python SDK using pip. Upgrade to the latest version with the `-U` flag. ```bash pip install daily-python ``` ```bash pip install -U daily-python ``` -------------------------------- ### Install daily-python Source: https://github.com/daily-co/daily-python/blob/main/README.md Install the Daily Python SDK using pip. This command installs the latest version of the package. ```bash pip install daily-python ``` -------------------------------- ### Install Daily Python SDK Source: https://github.com/daily-co/daily-python/blob/main/demos/README.md Manually install the 'daily-python' package as it is not included in the requirements.txt file. ```bash pip3 install daily-python ``` -------------------------------- ### Run Flask Application Source: https://github.com/daily-co/daily-python/blob/main/demos/flask/README.md Starts the Flask development server. This server will listen for incoming POST requests to add bots to meetings. ```bash flask run ``` -------------------------------- ### Install Dependencies Source: https://github.com/daily-co/daily-python/blob/main/demos/flask/README.md Installs the necessary Python packages for the Flask and Celery application. Ensure you have Python 3 and pip installed. ```bash pip3 install flask celery redis ``` -------------------------------- ### Start and Stop Live Streaming with RTMP URLs Source: https://context7.com/daily-co/daily-python/llms.txt Push meeting streams to RTMP endpoints. Requires Daily.init() and a CallClient with an EventHandler. ```python import threading from daily import Daily, CallClient, EventHandler Daily.init() class StreamingBot(EventHandler): def on_live_stream_started(self, status): print(f"Stream started: {status['streamId']}") def on_live_stream_stopped(self, stream_id: str): print(f"Stream stopped: {stream_id}") def on_live_stream_error(self, stream_id: str, message: str): print(f"Stream error [{stream_id}]: {message}") client = CallClient(event_handler=StreamingBot()) start_event = threading.Event() client.join( "https://mycompany.daily.co/my-room", meeting_token="OWNER_TOKEN", completion=lambda d, e: start_event.set(), ) start_event.wait() client.start_live_stream_with_rtmp_urls( rtmp_urls=["rtmp://a.rtmp.youtube.com/live2/YOUR_STREAM_KEY"], streaming_settings={ "video": {"width": 1920, "height": 1080, "fps": 30, "bitrate": 4500}, "audio": {"bitrate": 128}, }, completion=lambda err: print("Started" if not err else f"Error: {err}"), ) input("Press Enter to stop stream...\n") client.stop_live_stream() client.leave() client.release() ``` -------------------------------- ### CallClient.start_recording() / stop_recording() / update_recording() Source: https://context7.com/daily-co/daily-python/llms.txt Manage cloud recordings by starting, stopping, and updating their settings. This allows for dynamic control over recording quality, duration, and other parameters. ```APIDOC ## `CallClient.start_recording()` / `stop_recording()` / `update_recording()` — Cloud Recording Start, stop, and dynamically update cloud recordings. Supports configuring video/audio bitrates, dimensions, maximum duration, and streaming layout. ```python import threading from daily import Daily, CallClient, EventHandler Daily.init() class RecordingBot(EventHandler): def on_recording_started(self, status): print(f"Recording started! id={status['recordingId']}, stream={status['streamId']}") def on_recording_stopped(self, stream_id: str): print(f"Recording stopped: {stream_id}") def on_recording_error(self, stream_id: str, message: str): print(f"Recording error [{stream_id}]: {message}") client = CallClient(event_handler=RecordingBot()) start_event = threading.Event() client.join( "https://mycompany.daily.co/my-room", meeting_token="OWNER_TOKEN", completion=lambda d, e: start_event.set(), ) start_event.wait() # Start recording with custom settings client.start_recording( streaming_settings={ "video": {"width": 1280, "height": 720, "fps": 30, "bitrate": 2000}, "audio": {"bitrate": 128}, "maxDuration": 3600, }, completion=lambda stream_id, err: print(f"Recording stream: {stream_id}" if not err else f"Error: {err}"), ) input("Press Enter to stop recording...\n") client.stop_recording() input("Press Enter to leave...\n") client.leave() client.release() ``` ``` -------------------------------- ### Create and Use a Virtual Microphone for Audio Source: https://context7.com/daily-co/daily-python/llms.txt Create a named virtual microphone device using `Daily.create_microphone_device()`. Write raw 16-bit PCM frames to this device to stream audio into the meeting. This example demonstrates sending audio from a WAV file. ```python import wave import threading from daily import Daily, CallClient Daily.init() SAMPLE_RATE = 16000 CHANNELS = 1 mic = Daily.create_microphone_device("my-mic", sample_rate=SAMPLE_RATE, channels=CHANNELS) client = CallClient() start_event = threading.Event() def on_joined(data, error): if error: print(f"Join failed: {error}") start_event.set() client.join( "https://mycompany.daily.co/my-room", client_settings={ "inputs": { "camera": False, "microphone": {"isEnabled": True, "settings": {"deviceId": "my-mic"}}, }, }, completion=on_joined, ) start_event.wait() # Send a WAV file's audio into the meeting with wave.open("audio.wav", "rb") as wav: total = wav.getnframes() sent = 0 while sent < total: frames = wav.readframes(int(SAMPLE_RATE / 10)) # 100ms chunks if frames: mic.write_frames(frames) sent += SAMPLE_RATE // 10 client.leave() client.release() ``` -------------------------------- ### Start and Stop Real-Time Transcription Source: https://context7.com/daily-co/daily-python/llms.txt Enable Deepgram-powered live transcription for meetings. Requires Daily.init() and a CallClient with an EventHandler. ```python import threading from daily import Daily, CallClient, EventHandler Daily.init() class TranscriptionBot(EventHandler): def on_transcription_started(self, status): print(f"Transcription started: instance={status['instanceId']}") def on_transcription_message(self, message): print(f"[{message['participantId']}] {message['timestamp']}: {message['text']}") def on_transcription_stopped(self, stopped_by: str, stopped_by_error: bool): print(f"Transcription stopped by {stopped_by}, error={stopped_by_error}") client = CallClient(event_handler=TranscriptionBot()) start_event = threading.Event() client.join( "https://mycompany.daily.co/my-room", meeting_token="OWNER_TOKEN", completion=lambda d, e: start_event.set(), ) start_event.wait() client.start_transcription( settings={ "language": "en", "model": "nova-2", "punctuate": True, "endpointing": 300, "includeRawResponse": False, }, completion=lambda err: print("Transcription started" if not err else f"Error: {err}"), ) input("Press Enter to stop transcription...\n") client.stop_transcription() client.leave() client.release() ``` -------------------------------- ### Real-Time Transcription Source: https://context7.com/daily-co/daily-python/llms.txt Start Deepgram-powered live transcription for the meeting. Receive transcription messages via `EventHandler.on_transcription_message()`. ```APIDOC ## `CallClient.start_transcription()` / `stop_transcription()` — Real-Time Transcription Start Deepgram-powered live transcription for the meeting. Receive transcription messages via `EventHandler.on_transcription_message()`. ### Method ```python client.start_transcription( settings: dict, completion: Callable[[Optional[str]], None] | None = None ) ``` ### Method ```python client.stop_transcription( completion: Callable[[Optional[str]], None] | None = None ) ``` ### Parameters for `start_transcription` #### Path Parameters None #### Query Parameters None #### Request Body - **settings** (dict) - Required - Transcription settings. - **language** (str) - The language of the audio (e.g., "en"). - **model** (str) - The Deepgram model to use (e.g., "nova-2"). - **punctuate** (bool) - Whether to punctuate the transcription. - **endpointing** (int) - The duration in milliseconds to wait for speech to end before finalizing a transcript. - **includeRawResponse** (bool) - Whether to include the raw Deepgram response. #### Request Example ```python client.start_transcription( settings={ "language": "en", "model": "nova-2", "punctuate": True, "endpointing": 300, "includeRawResponse": False, }, completion=lambda err: print("Transcription started" if not err else f"Error: {err}"), ) ``` ### Response for `start_transcription` #### Success Response (200) No specific response body is detailed, but the `completion` callback will indicate success or failure. Transcription messages are received via the `on_transcription_message` event handler. #### Response Example Callback will receive `None` on success or an error string on failure. ``` -------------------------------- ### Run Celery Worker Source: https://github.com/daily-co/daily-python/blob/main/demos/flask/README.md Starts the Celery worker process in a separate terminal. This worker will handle background tasks, such as synthesizing audio for bots. ```bash celery -A app.celery worker --loglevel INFO ``` -------------------------------- ### Make POST Request to Add Bot Source: https://github.com/daily-co/daily-python/blob/main/demos/flask/README.md Example of how to make a POST request to the Flask application using curl. The request body must be a JSON object containing the bot's name and the meeting URL. ```json { "bot_name": "BOT_NAME", "meeting_url": "DAILY_MEETING_URL" } ``` ```bash curl -d '{"bot_name": "BOT_NAME", "meeting_url":"DAILY_MEETING_URL"}' -H "Content-Type: application/json" -X POST http://localhost:5000 ``` -------------------------------- ### Voice Activity Detection Source: https://context7.com/daily-co/daily-python/llms.txt Create an on-device VAD instance to analyze raw audio frames and get a speech confidence score (0.0–1.0) without sending data to any external service. ```APIDOC ## `Daily.create_native_vad()` / `NativeVad.analyze_frames()` — Voice Activity Detection Create an on-device VAD instance to analyze raw audio frames and get a speech confidence score (0.0–1.0) without sending data to any external service. ### Method ```python Daily.create_native_vad( reset_period_ms: int, sample_rate: int, channels: int ) -> NativeVad ``` ### Method ```python vad.analyze_frames( frames: bytes ) -> float ``` ### Parameters for `create_native_vad` #### Path Parameters None #### Query Parameters None #### Request Body - **reset_period_ms** (int) - Required - The period in milliseconds after which the VAD state is reset if no speech is detected. - **sample_rate** (int) - Required - The sample rate of the audio in Hz. - **channels** (int) - Required - The number of audio channels. #### Request Example ```python vad: NativeVad = Daily.create_native_vad( reset_period_ms=2000, sample_rate=16000, channels=1 ) ``` ### Parameters for `analyze_frames` #### Path Parameters None #### Query Parameters None #### Request Body - **frames** (bytes) - Required - Raw audio frames to analyze. #### Request Example ```python confidence = vad.analyze_frames(buf) ``` ### Response for `analyze_frames` #### Success Response (200) - **confidence** (float) - A speech confidence score between 0.0 and 1.0. ``` -------------------------------- ### Create and Activate Virtual Environment Source: https://github.com/daily-co/daily-python/blob/main/demos/README.md Use a virtual environment to manage Python dependencies for the demos. Activate it using the 'source' command. ```bash python3 -m venv .venv source .venv/bin/activate ``` -------------------------------- ### Daily.init() - Initialize the SDK Source: https://context7.com/daily-co/daily-python/llms.txt Initializes the Daily SDK. Must be called once per process before any other SDK usage. Accepts optional worker thread count and log level. ```APIDOC ## Daily.init() ### Description Initializes the Daily SDK. Must be called once per process before any other SDK usage. Accepts optional worker thread count and log level. ### Method `Daily.init(worker_threads: int = 2, log_level: LogLevel = LogLevel.Off)` ### Parameters * **worker_threads** (int) - Optional - The number of worker threads to use. * **log_level** (LogLevel) - Optional - The logging level for the SDK. ### Usage ```python from daily import Daily, LogLevel # Initialize with default settings Daily.init() # Initialize with custom settings Daily.init(worker_threads=4, log_level=LogLevel.Info) # Cleanly shut down (call before process exit) Daily.deinit() ``` ``` -------------------------------- ### Configure Inputs and Publishing Settings Source: https://context7.com/daily-co/daily-python/llms.txt Enables camera and microphone inputs and configures publishing settings for video and audio. Use this to control media streams after joining a call. ```python client.update_inputs( { "camera": {"isEnabled": True}, "microphone": {"isEnabled": True}, }, completion=lambda err: print("Inputs updated" if not err else f"Error: {err}"), ) client.update_publishing( { "camera": { "isPublishing": True, "sendSettings": { "maxQuality": "high", "preferredCodec": "H264", }, }, "microphone": { "isPublishing": True, "sendSettings": "speech", }, }, completion=lambda err: print("Publishing updated" if not err else f"Error: {err}"), ) # Query current state print("Current inputs:", client.inputs()) print("Current publishing:", client.publishing()) client.leave() client.release() ``` -------------------------------- ### Configure Local Media with `update_inputs()` Source: https://context7.com/daily-co/daily-python/llms.txt Enable or disable local camera and microphone inputs, and control publishing parameters such as video codec, bitrate, and simulcast layers. Ensure `Daily.init()` is called. ```python from daily import Daily, CallClient Daily.init() client = CallClient() start_event = __import__("threading").Event() client.join("https://mycompany.daily.co/my-room", completion=lambda d, e: start_event.set()) start_event.wait() ``` -------------------------------- ### Initialize and Deinitialize the Daily SDK Source: https://context7.com/daily-co/daily-python/llms.txt Initialize the SDK once per process before other usage. Cleanly shut down the SDK using `deinit()` before process exit. ```python from daily import Daily, LogLevel # Initialize with default settings (2 worker threads, logging off) Daily.init() # Initialize with custom settings Daily.init(worker_threads=4, log_level=LogLevel.Info) # Cleanly shut down (call before process exit) Daily.deinit() ``` -------------------------------- ### Send Custom Video Frames with `create_camera_device` Source: https://context7.com/daily-co/daily-python/llms.txt Creates a virtual camera device to stream custom video into a meeting. Push raw pixel buffers (RGBA, RGB, I420, etc.) frame-by-frame to the device using `write_frame()`. Ensure the camera device is specified in `client_settings` when joining the call. ```python import time import threading from daily import Daily, CallClient from PIL import Image Daily.init() img = Image.open("frame.png").convert("RGB") camera = Daily.create_camera_device( "my-camera", width=img.width, height=img.height, color_format="RGB" ) client = CallClient() client.update_subscription_profiles({"base": {"camera": "unsubscribed", "microphone": "unsubscribed"}}) start_event = threading.Event() quit_flag = threading.Event() def on_joined(data, error): if error: print(f"Join failed: {error}") start_event.set() def video_loop(): start_event.wait() frame_bytes = img.tobytes() while not quit_flag.is_set(): camera.write_frame(frame_bytes) time.sleep(1.0 / 30) # 30 fps t = threading.Thread(target=video_loop) t.start() client.join( "https://mycompany.daily.co/my-room", client_settings={ "inputs": { "camera": {"isEnabled": True, "settings": {"deviceId": "my-camera"}}, "microphone": False, } }, completion=on_joined, ) t.join() client.leave() client.release() ``` -------------------------------- ### Receive Meeting Audio with `create_speaker_device` Source: https://context7.com/daily-co/daily-python/llms.txt Creates a virtual speaker device to capture mixed audio output from a meeting. Use `Daily.select_speaker_device()` to activate it and `read_frames()` to retrieve PCM data. The captured audio is saved to 'recorded.wav'. ```python import sys import threading import wave from daily import Daily, CallClient Daily.init() SAMPLE_RATE = 16000 CHANNELS = 1 speaker = Daily.create_speaker_device("my-speaker", sample_rate=SAMPLE_RATE, channels=CHANNELS) Daily.select_speaker_device("my-speaker") client = CallClient() client.update_subscription_profiles({"base": {"camera": "unsubscribed", "microphone": "subscribed"}}) start_event = threading.Event() quit_flag = threading.Event() def on_joined(data, error): if error: print(f"Join failed: {error}", file=sys.stderr) start_event.set() def receive_loop(): start_event.wait() out = wave.open("recorded.wav", "wb") out.setnchannels(CHANNELS) out.setsampwidth(2) # 16-bit PCM out.setframerate(SAMPLE_RATE) while not quit_flag.is_set(): buf = speaker.read_frames(int(SAMPLE_RATE / 10)) # 100ms if buf: out.writeframes(buf) out.close() t = threading.Thread(target=receive_loop) t.start() client.join("https://mycompany.daily.co/my-room", completion=on_joined) t.join() client.leave() client.release() ``` -------------------------------- ### Daily.create_camera_device() / VirtualCameraDevice.write_frame() Source: https://context7.com/daily-co/daily-python/llms.txt Creates a virtual camera device that allows you to send custom video frames into a meeting. You can push raw pixel buffers to stream video at a controlled framerate. ```APIDOC ## `Daily.create_camera_device()` / `VirtualCameraDevice.write_frame()` — Send Video Frames Creates a virtual camera device. Push raw pixel buffers (RGBA, RGB, I420, etc.) frame-by-frame to stream video into the meeting at a controlled framerate. ```python import time import threading from daily import Daily, CallClient from PIL import Image Daily.init() img = Image.open("frame.png").convert("RGB") camera = Daily.create_camera_device( "my-camera", width=img.width, height=img.height, color_format="RGB" ) client = CallClient() client.update_subscription_profiles({"base": {"camera": "unsubscribed", "microphone": "unsubscribed"}}) start_event = threading.Event() quit_flag = threading.Event() def on_joined(data, error): if error: print(f"Join failed: {error}") start_event.set() def video_loop(): start_event.wait() frame_bytes = img.tobytes() while not quit_flag.is_set(): camera.write_frame(frame_bytes) time.sleep(1.0 / 30) # 30 fps t = threading.Thread(target=video_loop) t.start() client.join( "https://mycompany.daily.co/my-room", client_settings={ "inputs": { "camera": {"isEnabled": True, "settings": {"deviceId": "my-camera"}}, "microphone": False, } }, completion=on_joined, ) t.join() client.leave() client.release() ``` ``` -------------------------------- ### Receive Per-Participant Audio with `set_audio_renderer` Source: https://context7.com/daily-co/daily-python/llms.txt Subscribes to raw audio from a specific participant track. The callback receives `AudioData` objects containing PCM frames. Ensure the participant is joined and their audio is subscribed to before setting the renderer. ```python from daily import Daily, CallClient, EventHandler, AudioData Daily.init() class MyHandler(EventHandler): def on_participant_joined(self, participant): pid = participant["id"] if not participant["info"]["isLocal"]: print(f"Setting audio renderer for {pid}") client.set_audio_renderer( pid, callback=on_audio, audio_source="microphone", sample_rate=16000, callback_interval_ms=20, ) def on_audio(participant_id: str, audio_data: AudioData, audio_source: str): print( f"[{participant_id}] {audio_data.num_audio_frames} frames " f"@ {audio_data.sample_rate}Hz / {audio_data.num_channels}ch " f"({len(audio_data.audio_frames)} bytes)" ) handler = MyHandler() client = CallClient(event_handler=handler) start_event = __import__("threading").Event() client.join("https://mycompany.daily.co/my-room", completion=lambda d, e: start_event.set()) start_event.wait() input("Press Enter to leave...\n") client.leave() client.release() ``` -------------------------------- ### Daily.create_speaker_device() / VirtualSpeakerDevice.read_frames() Source: https://context7.com/daily-co/daily-python/llms.txt Creates a virtual speaker device to capture mixed audio output from a meeting. This allows you to process or record all audio streams within a call. ```APIDOC ## `Daily.create_speaker_device()` / `VirtualSpeakerDevice.read_frames()` — Receive Audio from a Meeting Creates a virtual speaker device that captures all mixed audio output from the meeting. Use `Daily.select_speaker_device()` to activate it, then call `read_frames()` in a loop to pull PCM data. ```python import sys import threading import wave from daily import Daily, CallClient Daily.init() SAMPLE_RATE = 16000 CHANNELS = 1 speaker = Daily.create_speaker_device("my-speaker", sample_rate=SAMPLE_RATE, channels=CHANNELS) Daily.select_speaker_device("my-speaker") client = CallClient() client.update_subscription_profiles({"base": {"camera": "unsubscribed", "microphone": "subscribed"}}) start_event = threading.Event() quit_flag = threading.Event() def on_joined(data, error): if error: print(f"Join failed: {error}", file=sys.stderr) start_event.set() def receive_loop(): start_event.wait() out = wave.open("recorded.wav", "wb") out.setnchannels(CHANNELS) out.setsampwidth(2) # 16-bit PCM out.setframerate(SAMPLE_RATE) while not quit_flag.is_set(): buf = speaker.read_frames(int(SAMPLE_RATE / 10)) # 100ms if buf: out.writeframes(buf) out.close() t = threading.Thread(target=receive_loop) t.start() client.join("https://mycompany.daily.co/my-room", completion=on_joined) t.join() client.leave() client.release() ``` ``` -------------------------------- ### Join and Leave a Meeting with CallClient Source: https://context7.com/daily-co/daily-python/llms.txt Instantiate `CallClient` for meeting interactions. Use `join()` to enter a meeting and `leave()` followed by `release()` to exit and free resources. ```python import threading from daily import Daily, CallClient Daily.init() client = CallClient() start_event = threading.Event() def on_joined(data, error): if error: print(f"Join failed: {error}") else: session_id = data["meetingSession"]["id"] print(f"Joined! Session: {session_id}, participants: {data['participants']}") start_event.set() # Join with camera disabled and microphone enabled on a virtual device client.join( "https://mycompany.daily.co/my-room", meeting_token="YOUR_MEETING_TOKEN", # optional client_settings={ "inputs": { "camera": False, "microphone": {"isEnabled": True, "settings": {"deviceId": "my-mic"}}, }, }, completion=on_joined, ) start_event.wait() # wait until joined # ... do work ... client.leave() client.release() # free native resources ``` -------------------------------- ### Upgrade daily-python Source: https://github.com/daily-co/daily-python/blob/main/README.md Upgrade the Daily Python SDK to the latest version using pip. The -U flag ensures the package is updated. ```bash pip install -U daily-python ``` -------------------------------- ### Control Media Subscriptions with `update_subscription_profiles()` Source: https://context7.com/daily-co/daily-python/llms.txt Define named subscription profiles to control which media tracks are received, at what quality, and for which participants. This is useful for managing large calls efficiently. Ensure `Daily.init()` is called. ```python from daily import Daily, CallClient Daily.init() client = CallClient() # Define subscription profiles before joining client.update_subscription_profiles({ "base": { "camera": {"subscriptionState": "subscribed", "settings": {"maxQuality": "low"}}, "microphone": "subscribed", "screenVideo": "unsubscribed", "screenAudio": "unsubscribed", }, "presenter": { "camera": {"subscriptionState": "subscribed", "settings": {"maxQuality": "high"}}, "microphone": "subscribed", }, }) start_event = __import__("threading").Event() client.join("https://mycompany.daily.co/my-room", completion=lambda d, e: start_event.set()) start_event.wait() participants = client.participants() presenter_id = next((k for k in participants if k != "local"), None) if presenter_id: # Upgrade one participant to the "presenter" profile client.update_subscriptions( participant_settings={presenter_id: {"profile": "presenter"}}, completion=lambda err: print("Subscriptions updated" if not err else f"Error: {err}"), ) input("Press Enter to leave...\n") client.leave() client.release() ``` -------------------------------- ### Monitor Network Quality with `CallClient.get_network_stats()` Source: https://context7.com/daily-co/daily-python/llms.txt Implement network quality monitoring by subscribing to `on_network_stats_updated` events and periodically querying network statistics. Requires `threading` and `daily` imports. ```python import threading from daily import Daily, CallClient, EventHandler Daily.init() class NetworkMonitor(EventHandler): def on_network_stats_updated(self, stats): q = stats.get("quality", 0) threshold = stats.get("threshold", "unknown") latest = stats.get("stats", {}).get("latest", {}) print( f"Network quality={q} ({threshold}) | " f"recv={latest.get('receiveBitsPerSecond', 0)//1000}kbps " f"send={latest.get('sendBitsPerSecond', 0)//1000}kbps | " f"pkt_loss_recv={latest.get('totalRecvPacketLoss', 0):.2%} " f"pkt_loss_send={latest.get('totalSendPacketLoss', 0):.2%}" ) client = CallClient(event_handler=NetworkMonitor()) start_event = threading.Event() client.join("https://mycompany.daily.co/my-room", completion=lambda d, e: start_event.set()) start_event.wait() # One-shot query stats = client.get_network_stats() print("Snapshot:", stats) input("Press Enter to leave...\n") client.leave() client.release() ``` -------------------------------- ### Daily.create_microphone_device() - Virtual Microphone for Sending Audio Source: https://context7.com/daily-co/daily-python/llms.txt Creates a named virtual microphone device that can be used as an audio input source. Write raw 16-bit PCM frames into it to stream audio into the meeting. ```APIDOC ## Daily.create_microphone_device() - Virtual Microphone for Sending Audio ### Description Creates a named virtual microphone device that can be used as an audio input source. Write raw 16-bit PCM frames into it to stream audio into the meeting. ### Method `mic = Daily.create_microphone_device(name: str, sample_rate: int, channels: int)` `mic.write_frames(frames: bytes)` ### Parameters * **name** (str) - Required - The name of the virtual microphone device. * **sample_rate** (int) - Required - The sample rate of the audio in Hz. * **channels** (int) - Required - The number of audio channels. * **frames** (bytes) - Required - Raw 16-bit PCM audio frames to write. ### Usage ```python import wave import threading from daily import Daily, CallClient Daily.init() SAMPLE_RATE = 16000 CHANNELS = 1 mic = Daily.create_microphone_device("my-mic", sample_rate=SAMPLE_RATE, channels=CHANNELS) client = CallClient() start_event = threading.Event() def on_joined(data, error): if error: print(f"Join failed: {error}") start_event.set() client.join( "https://mycompany.daily.co/my-room", client_settings={ "inputs": { "camera": False, "microphone": {"isEnabled": True, "settings": {"deviceId": "my-mic"}}, } }, completion=on_joined, ) start_event.wait() # Send a WAV file's audio into the meeting with wave.open("audio.wav", "rb") as wav: total = wav.getnframes() sent = 0 while sent < total: frames = wav.readframes(int(SAMPLE_RATE / 10)) # 100ms chunks if frames: mic.write_frames(frames) sent += SAMPLE_RATE // 10 client.leave() client.release() ``` ``` -------------------------------- ### Update Local Participant Permissions with `CallClient.update_permissions()` Source: https://context7.com/daily-co/daily-python/llms.txt Modify the local participant's media send/receive permissions at runtime. This requires an owner token or specific room configuration. Imports `threading` and `daily`. ```python import threading from daily import Daily, CallClient Daily.init() client = CallClient() start_event = threading.Event() client.join( "https://mycompany.daily.co/my-room", meeting_token="OWNER_TOKEN", completion=lambda d, e: start_event.set(), ) start_event.wait() # Grant the ability to send all media types client.update_permissions( permissions={ "hasPresence": True, "canSend": ["camera", "microphone", "screenVideo", "screenAudio", "customVideo", "customAudio"], "canAdmin": ["participants", "streaming", "transcription"], }, completion=lambda err: print("Permissions updated" if not err else f"Error: {err}"), ) client.leave() client.release() ``` -------------------------------- ### Receive Video Frames with `set_video_renderer()` Source: https://context7.com/daily-co/daily-python/llms.txt Subscribe to decoded video frames from a participant's camera or screen share. The callback receives `VideoFrame` objects containing raw pixel data, dimensions, timestamp, and color format. Ensure `Daily.init()` is called before initializing `CallClient`. ```python from daily import Daily, CallClient, EventHandler, VideoFrame Daily.init() class VideoHandler(EventHandler): def on_participant_joined(self, participant): pid = participant["id"] if not participant["info"]["isLocal"]: client.set_video_renderer( pid, callback=on_video_frame, video_source="camera", color_format="RGBA", ) def on_video_frame(participant_id: str, frame: VideoFrame, video_source: str): print( f"[{participant_id}] {frame.width}x{frame.height} " f"@ {frame.timestamp_us}us, format={frame.color_format}, " f"buffer size={len(frame.buffer)} bytes" ) handler = VideoHandler() client = CallClient(event_handler=handler) start_event = __import__("threading").Event() client.join("https://mycompany.daily.co/my-room", completion=lambda d, e: start_event.set()) start_event.wait() input("Press Enter to leave...\n") client.leave() client.release() ``` -------------------------------- ### Create and Use Native Voice Activity Detection Source: https://context7.com/daily-co/daily-python/llms.txt Analyze raw audio frames for speech confidence without external services. Requires Daily.init(), a CallClient, and a speaker device. ```python import threading from daily import Daily, CallClient, NativeVad Daily.init() SAMPLE_RATE = 16000 CHANNELS = 1 SPEECH_THRESHOLD = 0.90 vad: NativeVad = Daily.create_native_vad( reset_period_ms=2000, sample_rate=SAMPLE_RATE, channels=CHANNELS ) speaker = Daily.create_speaker_device("my-speaker", sample_rate=SAMPLE_RATE, channels=CHANNELS) Daily.select_speaker_device("my-speaker") client = CallClient() client.update_subscription_profiles({"base": {"camera": "unsubscribed", "microphone": "subscribed"}}) start_event = threading.Event() quit_flag = threading.Event() def analyze_loop(): start_event.wait() while not quit_flag.is_set(): buf = speaker.read_frames(int(SAMPLE_RATE / 100)) # 10ms chunks if buf: confidence = vad.analyze_frames(buf) label = "SPEAKING" if confidence > SPEECH_THRESHOLD else "SILENT" print(f"{label}: {confidence:.3f}") t = threading.Thread(target=analyze_loop) t.start() client.join("https://mycompany.daily.co/my-room", completion=lambda d, e: start_event.set()) input("Press Enter to leave...\n") quit_flag.set() t.join() client.leave() client.release() ``` -------------------------------- ### Handle Meeting Lifecycle with `EventHandler` Source: https://context7.com/daily-co/daily-python/llms.txt Subclass `EventHandler` and pass an instance to `CallClient` to receive asynchronous notifications for participant changes, call state, media updates, errors, and more. Ensure `Daily.init()` is called. ```python from daily import Daily, CallClient, EventHandler Daily.init() class MeetingHandler(EventHandler): def on_call_state_updated(self, state: str): # state: "initialized" | "joining" | "joined" | "leaving" | "left" print(f"Call state: {state}") def on_participant_joined(self, participant): info = participant["info"] print(f"Participant joined: {info['userName']} (id={participant['id']}, owner={info['isOwner']})") def on_participant_left(self, participant, reason: str): # reason: "leftCall" | "hidden" print(f"Participant left: {participant['id']} reason={reason}") def on_participant_updated(self, participant): print(f"Participant updated: {participant['id']}, media={participant['media']}") def on_app_message(self, message, sender: str): print(f"Message from {sender}: {message}") def on_error(self, message: str): print(f"Error: {message}") def on_active_speaker_changed(self, participant): print(f"Active speaker: {participant}") client = CallClient(event_handler=MeetingHandler()) start_event = __import__("threading").Event() client.join("https://mycompany.daily.co/my-room", completion=lambda d, e: start_event.set()) start_event.wait() input("Press Enter to leave...\n") client.leave() client.release() ``` -------------------------------- ### CallClient.set_audio_renderer() Source: https://context7.com/daily-co/daily-python/llms.txt Subscribes to raw audio from a specific participant's track. This allows you to receive PCM data for individual participants and process it with a custom callback. ```APIDOC ## `CallClient.set_audio_renderer()` — Per-Participant Audio Callback Subscribe to raw audio from a specific participant track. The callback fires with `AudioData` objects containing PCM frames, sample rate, channel count, and frame count. ```python from daily import Daily, CallClient, EventHandler, AudioData Daily.init() class MyHandler(EventHandler): def on_participant_joined(self, participant): pid = participant["id"] if not participant["info"]["isLocal"]: print(f"Setting audio renderer for {pid}") client.set_audio_renderer( pid, callback=on_audio, audio_source="microphone", sample_rate=16000, callback_interval_ms=20, ) def on_audio(participant_id: str, audio_data: AudioData, audio_source: str): print( f"[{participant_id}] {audio_data.num_audio_frames} frames " f"@ {audio_data.sample_rate}Hz / {audio_data.num_channels}ch " f"({len(audio_data.audio_frames)} bytes)" ) handler = MyHandler() client = CallClient(event_handler=handler) start_event = __import__("threading").Event() client.join("https://mycompany.daily.co/my-room", completion=lambda d, e: start_event.set()) start_event.wait() input("Press Enter to leave...\n") client.leave() client.release() ``` ``` -------------------------------- ### CallClient.update_inputs() / update_publishing() Source: https://context7.com/daily-co/daily-python/llms.txt Enable or disable local camera and microphone inputs and control publishing parameters such as video codec, bitrate, and simulcast layers. ```APIDOC ## `CallClient.update_inputs()` / `update_publishing()` — Configure Local Media Enable or disable local camera/microphone inputs and control publishing parameters such as video codec, bitrate, and simulcast layers. ```python from daily import Daily, CallClient Daily.init() client = CallClient() start_event = __import__("threading").Event() client.join("https://mycompany.daily.co/my-room", completion=lambda d, e: start_event.set()) start_event.wait() # Example: Update publishing settings (this part is not fully detailed in the source) # client.update_publishing({ # "camera": {"maxBitrate": 1000, "simulcastLayers": ["low", "standard"]} # }) # input("Press Enter to leave...\n") # client.leave() # client.release() ``` ``` -------------------------------- ### CallClient - Join and Leave a Meeting Source: https://context7.com/daily-co/daily-python/llms.txt The central class for all meeting interactions. Instantiate one per meeting session, call `join()` with a meeting URL and optional token, then `leave()` and `release()` when done. ```APIDOC ## CallClient - Join and Leave a Meeting ### Description The `CallClient` class is used for all meeting interactions. Instantiate one per meeting session, call `join()` to enter a meeting, and then `leave()` and `release()` when finished. ### Method `client = CallClient()` `client.join(url: str, meeting_token: str = None, client_settings: dict = None, completion: callable = None)` `client.leave()` `client.release()` ### Parameters * **url** (str) - Required - The URL of the meeting to join. * **meeting_token** (str) - Optional - The token for authentication. * **client_settings** (dict) - Optional - Settings for client inputs (camera, microphone). * **completion** (callable) - Optional - A callback function to be called upon joining. ### Usage ```python import threading from daily import Daily, CallClient Daily.init() client = CallClient() start_event = threading.Event() def on_joined(data, error): if error: print(f"Join failed: {error}") else: session_id = data["meetingSession"]["id"] print(f"Joined! Session: {session_id}, participants: {data['participants']}") start_event.set() client.join( "https://mycompany.daily.co/my-room", meeting_token="YOUR_MEETING_TOKEN", # optional client_settings={ "inputs": { "camera": False, "microphone": {"isEnabled": True, "settings": {"deviceId": "my-mic"}}, } }, completion=on_joined, ) start_event.wait() # wait until joined # ... do work ... client.leave() client.release() # free native resources ``` ``` -------------------------------- ### Publish Custom Audio Track in Python Source: https://context7.com/daily-co/daily-python/llms.txt Publish a named custom audio track alongside the standard microphone. Useful for mixing multiple audio streams into a single call session. Ensure the audio file is in WAV format and matches the specified sample rate and channels. ```python import threading import wave from daily import Daily, CallClient, CustomAudioSource, CustomAudioTrack Daily.init() SAMPLE_RATE = 16000 CHANNELS = 1 audio_source = CustomAudioSource(sample_rate=SAMPLE_RATE, channels=CHANNELS, auto_silence=True) audio_track = CustomAudioTrack(audio_source) print(f"Custom track id: {audio_track.id}") client = CallClient() start_event = threading.Event() def on_joined(data, error): if error: print(f"Join failed: {error}") start_event.set() return client.add_custom_audio_track( track_name="sound-effects", audio_track=audio_track, completion=lambda err: print("Track added" if not err else f"Error: {err}"), ) start_event.set() client.join("https://mycompany.daily.co/my-room", completion=on_joined) start_event.wait() # Stream audio into the custom track with wave.open("sfx.wav", "rb") as wav: while True: frames = wav.readframes(int(SAMPLE_RATE / 10)) if not frames: break audio_source.write_frames(frames) client.remove_custom_audio_track("sound-effects") client.leave() client.release() ``` -------------------------------- ### CallClient.set_video_renderer() Source: https://context7.com/daily-co/daily-python/llms.txt Subscribe to decoded video frames from a participant's camera or screen share track. The callback delivers VideoFrame objects with raw pixel buffer, dimensions, timestamp, and color format. ```APIDOC ## `CallClient.set_video_renderer()` — Receive Video Frames from Participants Subscribe to decoded video frames from a participant's camera or screen share track. The callback delivers `VideoFrame` objects with raw pixel buffer, dimensions, timestamp, and color format. ```python from daily import Daily, CallClient, EventHandler, VideoFrame Daily.init() class VideoHandler(EventHandler): def on_participant_joined(self, participant): pid = participant["id"] if not participant["info"]["isLocal"]: client.set_video_renderer( pid, callback=on_video_frame, video_source="camera", color_format="RGBA", ) def on_video_frame(participant_id: str, frame: VideoFrame, video_source: str): print( f"[{participant_id}] {frame.width}x{frame.height} " f"@ {frame.timestamp_us}us, format={frame.color_format}, " f"buffer size={len(frame.buffer)} bytes" ) handler = VideoHandler() client = CallClient(event_handler=handler) start_event = __import__("threading").Event() client.join("https://mycompany.daily.co/my-room", completion=lambda d, e: start_event.set()) start_event.wait() input("Press Enter to leave...\n") client.leave() client.release() ``` ``` -------------------------------- ### CallClient.update_subscription_profiles() / update_subscriptions() Source: https://context7.com/daily-co/daily-python/llms.txt Define named subscription profiles that control which media tracks are received, at what quality, and for which participants. Use profiles to efficiently manage large calls. ```APIDOC ## `CallClient.update_subscription_profiles()` / `update_subscriptions()` — Control Media Subscriptions Define named subscription profiles that control which media tracks are received, at what quality, and for which participants. Use profiles to efficiently manage large calls. ```python from daily import Daily, CallClient Daily.init() client = CallClient() # Define subscription profiles before joining client.update_subscription_profiles({ "base": { "camera": {"subscriptionState": "subscribed", "settings": {"maxQuality": "low"}}, "microphone": "subscribed", "screenVideo": "unsubscribed", "screenAudio": "unsubscribed", }, "presenter": { "camera": {"subscriptionState": "subscribed", "settings": {"maxQuality": "high"}}, "microphone": "subscribed", }, }) start_event = __import__("threading").Event() client.join("https://mycompany.daily.co/my-room", completion=lambda d, e: start_event.set()) start_event.wait() participants = client.participants() presenter_id = next((k for k in participants if k != "local"), None) if presenter_id: # Upgrade one participant to the "presenter" profile client.update_subscriptions( participant_settings={presenter_id: {"profile": "presenter"}}, completion=lambda err: print("Subscriptions updated" if not err else f"Error: {err}"), ) input("Press Enter to leave...\n") client.leave() client.release() ``` ```