Skip to content

Commit fea55c5

Browse files
PlatformAudio (#140)
1 parent de00859 commit fea55c5

10 files changed

Lines changed: 995 additions & 19 deletions

File tree

AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ All `RoomDelegate` callbacks and stream handler callbacks (e.g., `registerTextSt
6060
| `SubscriptionThreadDispatcher` | Yes | Internal `std::mutex` protects registrations and active readers. Thread joins happen outside the lock. |
6161
| `AudioStream` / `VideoStream` / `DataTrackStream` | Yes | Internal `std::mutex` + `condition_variable` coordinate the FFI producer thread and the consumer reader thread. |
6262
| `AudioSource::captureFrame` | No | Not safe to call concurrently from multiple threads. |
63+
| `PlatformAudio` / `PlatformAudioSource` | Yes | Thin `sendRequest` wrappers over immutable FFI handle state; destruction and move operations must be externally synchronized. |
6364
| `VideoSource::captureFrame` | No | Not safe to call concurrently from multiple threads. |
6465
| `LocalAudioTrack` / `LocalVideoTrack` | No | Thin `sendRequest` wrappers with no internal synchronization. |
6566
| `LocalDataTrack::tryPush` | No | Thin `sendRequest` wrapper with no internal synchronization. |

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,7 @@ add_library(livekit SHARED
385385
src/logging.cpp
386386
src/local_audio_track.cpp
387387
src/local_data_track.cpp
388+
src/platform_audio.cpp
388389
src/remote_audio_track.cpp
389390
src/remote_data_track.cpp
390391
src/room.cpp

include/livekit/livekit.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "livekit/local_video_track.h"
2929
#include "livekit/logging.h"
3030
#include "livekit/participant.h"
31+
#include "livekit/platform_audio.h"
3132
#include "livekit/remote_participant.h"
3233
#include "livekit/remote_track_publication.h"
3334
#include "livekit/room.h"
@@ -60,4 +61,4 @@ LIVEKIT_API bool initialize(const LogLevel& level = LogLevel::Info);
6061
/// After shutdown, you may call initialize() again.
6162
LIVEKIT_API void shutdown();
6263

63-
} // namespace livekit
64+
} // namespace livekit

include/livekit/local_audio_track.h

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,15 @@ class OwnedTrack;
3131
}
3232

3333
class AudioSource;
34+
class PlatformAudioSource;
3435

3536
/// Represents a user-provided audio track sourced from the local device.
3637
///
37-
/// `LocalAudioTrack` is used to publish microphone audio (or any custom
38-
/// audio source) to a LiveKit room. It wraps a platform-specific audio
39-
/// source and exposes simple controls such as `mute()` and `unmute()`.
38+
/// `LocalAudioTrack` is used to publish microphone audio or any custom audio
39+
/// source to a LiveKit room. It wraps a platform-specific audio
40+
/// source and exposes simple controls such as `mute()` and `unmute()`.
41+
/// Muting a local audio track stops transmitting audio to the room, but
42+
/// the underlying source may continue capturing depending on platform behavior.
4043
///
4144
/// Typical usage:
4245
///
@@ -63,30 +66,54 @@ class LIVEKIT_API LocalAudioTrack : public Track {
6366
/// directly for frame capture.
6467
///
6568
/// @return A shared pointer to the newly constructed `LocalAudioTrack`.
69+
/// @throws std::invalid_argument If \p source is null.
70+
/// @throws std::runtime_error If the FFI request fails.
6671
static std::shared_ptr<LocalAudioTrack> createLocalAudioTrack(const std::string& name,
6772
const std::shared_ptr<AudioSource>& source);
6873

74+
/// Creates a new local audio track backed by the given `PlatformAudioSource`.
75+
///
76+
/// @param name Human-readable name for the track. This may appear to
77+
/// remote participants and in analytics/debug logs.
78+
/// @param source The platform source that captures microphone audio
79+
/// automatically through WebRTC's Audio Device Module.
80+
///
81+
/// @return A shared pointer to the newly constructed `LocalAudioTrack`.
82+
/// @throws std::invalid_argument If \p source is null.
83+
/// @throws std::runtime_error If the FFI request fails.
84+
///
85+
/// @note This operation is not thread-safe.
86+
static std::shared_ptr<LocalAudioTrack> createLocalAudioTrack(const std::string& name,
87+
const std::shared_ptr<PlatformAudioSource>& source);
88+
6989
/// Mutes the audio track.
7090
///
7191
/// A muted track stops sending audio to the room, but the track remains
7292
/// published and can be unmuted later without renegotiation.
93+
///
94+
/// @throws std::runtime_error If the FFI request fails.
7395
void mute();
7496

75-
/// Unmutes the audio track and resumes sending audio to the room.
97+
/// Unmute the audio track.
98+
///
99+
/// Resumes sending audio to the room.
100+
///
101+
/// @throws std::runtime_error If the FFI request fails.
76102
void unmute();
77103

78-
/// Returns a human-readable string representation of the track,
79-
/// including its SID and name. Useful for debugging and logging.
104+
/// Return a human-readable string representation of the track.
105+
///
106+
/// @return String containing the track SID and name.
80107
std::string toString() const;
81108

82109
/// Returns the publication that owns this track, or nullptr if the track is
83110
/// not published.
84111
std::shared_ptr<LocalTrackPublication> publication() const noexcept { return local_publication_; }
85112

86-
/// Sets the publication that owns this track.
87-
/// Note: std::move on a const& silently falls back to a copy, so we assign
88-
/// directly. Changing the virtual signature to take by value would enable
89-
/// a true move but is an API-breaking change hence left for a future revision.
113+
/// Set the publication that owns this track.
114+
///
115+
/// @param publication Publication that owns this track, or nullptr to clear
116+
/// the association.
90117
void setPublication(const std::shared_ptr<LocalTrackPublication>& publication) noexcept override {
91118
local_publication_ = publication;
92119
}

include/livekit/platform_audio.h

Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
/*
2+
* Copyright 2026 LiveKit
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#pragma once
18+
19+
#include <cstdint>
20+
#include <memory>
21+
#include <stdexcept>
22+
#include <string>
23+
#include <vector>
24+
25+
#include "livekit/ffi_handle.h"
26+
#include "livekit/visibility.h"
27+
28+
namespace livekit {
29+
30+
/// Internal shared state for platform audio handles.
31+
///
32+
/// This forward declaration is exposed only so public wrapper types can hold a
33+
/// shared implementation pointer.
34+
///
35+
/// @note This object is thread-safe.
36+
struct PlatformAudioState;
37+
38+
/// Information about a platform audio device.
39+
///
40+
/// @note Device indices may change when audio hardware is added or removed. Use
41+
/// the stable `id` value when selecting a device.
42+
struct AudioDeviceInfo {
43+
/// Current device index.
44+
std::uint32_t index = 0;
45+
46+
/// Device name reported by the operating system.
47+
std::string name;
48+
49+
/// Platform-specific stable device identifier.
50+
std::string id;
51+
};
52+
53+
/// Audio processing options for platform microphone capture.
54+
///
55+
/// The default values enable WebRTC's voice processing path for typical
56+
/// microphone publishing.
57+
struct PlatformAudioOptions {
58+
/// Enable acoustic echo cancellation.
59+
bool echo_cancellation = true;
60+
61+
/// Enable background noise suppression.
62+
bool noise_suppression = true;
63+
64+
/// Enable automatic gain control.
65+
bool auto_gain_control = true;
66+
67+
/// Prefer hardware audio processing when the platform provides it.
68+
bool prefer_hardware = false;
69+
};
70+
71+
/// Error raised when platform audio setup or device operations fail.
72+
class LIVEKIT_API PlatformAudioError : public std::runtime_error {
73+
public:
74+
/// Create a platform audio error.
75+
///
76+
/// @param message Human-readable error message.
77+
explicit PlatformAudioError(const std::string& message) : std::runtime_error(message) {}
78+
};
79+
80+
/// Audio source backed by WebRTC's platform Audio Device Module.
81+
///
82+
/// A PlatformAudioSource captures microphone audio automatically. Unlike
83+
/// AudioSource, callers do not push frames with captureFrame().
84+
class LIVEKIT_API PlatformAudioSource {
85+
public:
86+
/// Copy construction is disabled.
87+
PlatformAudioSource(const PlatformAudioSource& other) = delete;
88+
89+
/// Copy assignment is disabled.
90+
PlatformAudioSource& operator=(const PlatformAudioSource& other) = delete;
91+
92+
/// Move the platform audio source.
93+
///
94+
/// @param other Source to move from.
95+
///
96+
/// @note This operation is not thread-safe.
97+
PlatformAudioSource(PlatformAudioSource&& other) noexcept = default;
98+
99+
/// Move-assign the platform audio source.
100+
///
101+
/// @param other Source to move from.
102+
/// @return Reference to this source.
103+
///
104+
/// @note This operation is not thread-safe.
105+
PlatformAudioSource& operator=(PlatformAudioSource&& other) noexcept = default;
106+
107+
/// Return the underlying FFI handle ID used in FFI requests.
108+
///
109+
/// @note This operation is thread-safe.
110+
std::uint64_t ffiHandleId() const noexcept { return static_cast<std::uint64_t>(handle_.get()); }
111+
112+
private:
113+
friend class PlatformAudio;
114+
115+
PlatformAudioSource(FfiHandle handle, std::shared_ptr<PlatformAudioState> platform_audio) noexcept;
116+
117+
FfiHandle handle_;
118+
std::shared_ptr<PlatformAudioState> platform_audio_;
119+
};
120+
121+
/// Platform audio device manager backed by WebRTC's Audio Device Module.
122+
///
123+
/// Use PlatformAudio for microphone capture when built-in echo
124+
/// cancellation, noise suppression, automatic gain control, and speaker playout
125+
/// are desired. Use AudioSource instead when the application needs direct access
126+
/// to raw PCM frames or custom audio generation.
127+
class LIVEKIT_API PlatformAudio {
128+
public:
129+
/// Create a platform audio manager.
130+
///
131+
/// Enables WebRTC's platform Audio Device Module for microphone capture and
132+
/// speaker playout.
133+
///
134+
/// @throws PlatformAudioError If the FFI response is malformed or the
135+
/// platform Audio Device Module cannot be created.
136+
///
137+
/// @note This operation is thread-safe.
138+
PlatformAudio();
139+
140+
/// Copy the platform audio manager.
141+
///
142+
/// The copy shares the same underlying platform audio handle.
143+
///
144+
/// @param other Manager to copy from.
145+
///
146+
/// @note This operation is not thread-safe.
147+
PlatformAudio(const PlatformAudio& other) = default;
148+
149+
/// Copy-assign the platform audio manager.
150+
///
151+
/// The assigned instance shares the same underlying platform audio handle.
152+
///
153+
/// @param other Manager to copy from.
154+
/// @return Reference to this manager.
155+
///
156+
/// @note This operation is not thread-safe.
157+
PlatformAudio& operator=(const PlatformAudio& other) = default;
158+
159+
/// Move the platform audio manager.
160+
///
161+
/// @param other Manager to move from.
162+
///
163+
/// @note This operation is not thread-safe.
164+
PlatformAudio(PlatformAudio&& other) noexcept = default;
165+
166+
/// Move-assign the platform audio manager.
167+
///
168+
/// @param other Manager to move from.
169+
/// @return Reference to this manager.
170+
///
171+
/// @note This operation is not thread-safe.
172+
PlatformAudio& operator=(PlatformAudio&& other) noexcept = default;
173+
174+
/// Return the current number of recording devices.
175+
///
176+
/// @throws PlatformAudioError If the FFI response is malformed or device
177+
/// enumeration fails.
178+
///
179+
/// @note This operation is thread-safe.
180+
std::int32_t recordingDeviceCount() const;
181+
182+
/// Return the current number of playout devices.
183+
///
184+
/// @throws PlatformAudioError If the FFI response is malformed or device
185+
/// enumeration fails.
186+
///
187+
/// @note This operation is thread-safe.
188+
std::int32_t playoutDeviceCount() const;
189+
190+
/// Enumerate available microphones.
191+
///
192+
/// @return List of available recording devices.
193+
/// @throws PlatformAudioError If the FFI response is malformed or device
194+
/// enumeration fails.
195+
///
196+
/// @note This operation is thread-safe.
197+
std::vector<AudioDeviceInfo> recordingDevices() const;
198+
199+
/// Enumerate available speakers/headphones.
200+
///
201+
/// @return List of available playout devices.
202+
/// @throws PlatformAudioError If the FFI response is malformed or device
203+
/// enumeration fails.
204+
///
205+
/// @note This operation is thread-safe.
206+
std::vector<AudioDeviceInfo> playoutDevices() const;
207+
208+
/// Select the microphone by device ID.
209+
///
210+
/// @param device_id Stable device identifier from AudioDeviceInfo::id.
211+
/// @throws PlatformAudioError If the FFI response is malformed or device
212+
/// selection fails.
213+
///
214+
/// @note This operation is thread-safe.
215+
void setRecordingDevice(const std::string& device_id) const;
216+
217+
/// Select the speaker/headphones by device ID.
218+
///
219+
/// @param device_id Stable device identifier from AudioDeviceInfo::id.
220+
/// @throws PlatformAudioError If the FFI response is malformed or device
221+
/// selection fails.
222+
///
223+
/// @note This operation is thread-safe.
224+
void setPlayoutDevice(const std::string& device_id) const;
225+
226+
/// Create an automatically captured microphone source for LocalAudioTrack.
227+
///
228+
/// Each call returns a new track source handle backed by the shared platform
229+
/// Audio Device Module; it does not create a separate ADM instance.
230+
///
231+
/// @param options Audio processing options for the platform microphone path.
232+
/// @return Platform-backed audio source suitable for LocalAudioTrack.
233+
/// @throws PlatformAudioError If the FFI response is malformed or source
234+
/// creation fails.
235+
///
236+
/// @note This operation is thread-safe.
237+
std::shared_ptr<PlatformAudioSource> createAudioSource(const PlatformAudioOptions& options = {}) const;
238+
239+
private:
240+
std::shared_ptr<PlatformAudioState> state_;
241+
};
242+
243+
} // namespace livekit

0 commit comments

Comments
 (0)