Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions include/livekit/livekit.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

#pragma once

#include <chrono>

#include "livekit/audio_frame.h"
#include "livekit/audio_processing_module.h"
#include "livekit/audio_source.h"
Expand Down Expand Up @@ -63,4 +65,11 @@ LIVEKIT_API bool initialize(const LogLevel& level = LogLevel::Info);
/// After shutdown, you may call initialize() again.
LIVEKIT_API void shutdown();

/// Delay of remote audio that the application renders itself after the SDK
/// hands it over (its own buffers and output device), for applications that
/// disable the SDK's platform playout. WebRTC's audio/video synchronization
/// subtracts it from the audio playout position, so pictures wait for the
/// sound actually heard. Process-wide; clamped to 0..1000 ms.
LIVEKIT_API void setAudioPlayoutDelay(std::chrono::milliseconds delay);

} // namespace livekit
4 changes: 4 additions & 0 deletions include/livekit/room_event_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,10 @@ struct TrackPublishOptions {
/// Controls how the encoder trades off between resolution and framerate
/// when bandwidth is constrained. If not set, the server defaults apply.
std::optional<DegradationPreference> degradation_preference;

/// Encode a two-channel audio source as stereo Opus. The server then
/// negotiates stereo with the publisher and its subscribers.
std::optional<bool> stereo;
};

// ---------------------------------------------------------
Expand Down
362 changes: 352 additions & 10 deletions patches/client-sdk-rust/0001-native-v2-runtime.patch

Large diffs are not rendered by default.

13 changes: 13 additions & 0 deletions src/audio_source.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,19 @@ void AudioSource::captureFrame(const AudioFrame& frame, int timeout_ms) {

// Build AudioFrameBufferInfo from the wrapper
const proto::AudioFrameBufferInfo buf = frame.toProto();
if (queue_size_ms_ == 0) {
// Unbuffered: WebRTC consumes the frame in this thread. No runtime task
// or callback thread can delay a realtime capture thread under CPU load.
proto::FfiRequest req;
auto* msg = req.mutable_capture_audio_frame();
msg->set_source_handle(handle_.get());
msg->set_synchronous(true);
msg->mutable_buffer()->CopyFrom(buf);
if (!FfiClient::instance().sendRequest(req).has_capture_audio_frame()) {
throw std::runtime_error("AudioSource::captureFrame: FFI response missing capture result");
}
return;
}
// Use async FFI API and block until the callback completes
auto fut = FfiClient::instance().captureAudioFrameAsync(handle_.get(), buf);
if (timeout_ms == 0) {
Expand Down
7 changes: 7 additions & 0 deletions src/livekit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

#include "livekit/livekit.h"

#include <algorithm>

#include "ffi_client.h"
#include "livekit_ffi.h"
#include "lk_log.h"
Expand All @@ -34,6 +36,11 @@ void setExternalVideoDecoder(const LkExternalVideoDecoder* decoder) {
livekit_ffi_set_external_video_decoder(decoder);
}

void setAudioPlayoutDelay(std::chrono::milliseconds delay) {
livekit_ffi_set_audio_playout_delay(static_cast<std::uint32_t>(std::clamp<std::chrono::milliseconds::rep>(
delay.count(), 0, 1000)));
}

bool isInitialized() { return FfiClient::instance().isInitialized(); }

void shutdown() {
Expand Down
6 changes: 6 additions & 0 deletions src/room_proto_converter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,9 @@ proto::TrackPublishOptions toProto(const TrackPublishOptions& in) {
if (in.degradation_preference) {
msg.set_degradation_preference(static_cast<proto::DegradationPreference>(*in.degradation_preference));
}
if (in.stereo) {
msg.set_stereo(*in.stereo);
}
return msg;
}

Expand Down Expand Up @@ -582,6 +585,9 @@ TrackPublishOptions fromProto(const proto::TrackPublishOptions& in) {
if (in.has_degradation_preference()) {
out.degradation_preference = static_cast<DegradationPreference>(in.degradation_preference());
}
if (in.has_stereo()) {
out.stereo = in.stereo();
}
return out;
}

Expand Down
14 changes: 14 additions & 0 deletions src/tests/unit/test_audio_source.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,20 @@ TEST_F(AudioSourceTest, BufferedIngressTimeoutIsNotReportedAsSuccess) {
source.clearQueue();
}

TEST_F(AudioSourceTest, UnbufferedCaptureCompletesInTheCallingThread) {
auto source = std::make_shared<AudioSource>(48000, 2, 0);
auto track = LocalAudioTrack::createLocalAudioTrack("direct-input", source);
FreshAudioStream stream(track);
auto input = AudioFrame::create(48000, 2, 480);
// No timeout applies: the frame is in the sink when captureFrame returns.
source->captureFrame(input, 0);
FreshAudioFrame output;
EXPECT_EQ(stream.tryRead(output, 1'000'000), FreshAudioReadResult::frame);
// Errors surface as the call's own exception, not a later callback.
auto partial = AudioFrame::create(48000, 2, 240);
EXPECT_THROW(source->captureFrame(partial, 0), std::runtime_error);
}

TEST_F(AudioSourceTest, FreshSinkBoundsQueueAndPreservesStereo) {
auto source = std::make_shared<AudioSource>(48000, 2, 0);
auto track = LocalAudioTrack::createLocalAudioTrack("fresh-input", source);
Expand Down
1 change: 1 addition & 0 deletions src/tests/unit/test_room_event_types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ TEST(RoomEventTypesTest, TrackPublishOptionsDefaults) {
EXPECT_FALSE(options.packet_trailer_features.frame_id);
EXPECT_FALSE(options.packet_trailer_features.user_data);
EXPECT_FALSE(options.degradation_preference.has_value());
EXPECT_FALSE(options.stereo.has_value());
}

TEST(RoomEventTypesTest, UserPacketDataDefaults) {
Expand Down
14 changes: 14 additions & 0 deletions src/tests/unit/test_video_frame_metadata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,20 @@ TEST(TrackPublishOptionsTest, DegradationPreferenceRoundTrip) {
EXPECT_EQ(*round_trip.degradation_preference, DegradationPreference::MaintainFramerateAndResolution);
}

TEST(TrackPublishOptionsTest, StereoRoundTrip) {
EXPECT_FALSE(toProto(TrackPublishOptions{}).has_stereo());

TrackPublishOptions options;
options.stereo = true;
const proto::TrackPublishOptions proto_options = toProto(options);
ASSERT_TRUE(proto_options.has_stereo());
EXPECT_TRUE(proto_options.stereo());

const TrackPublishOptions round_trip = fromProto(proto_options);
ASSERT_TRUE(round_trip.stereo.has_value());
EXPECT_TRUE(*round_trip.stereo);
}

TEST(TrackPublishOptionsTest, PreEncodedBackendRoundTrip) {
TrackPublishOptions options;
options.video_encoder = VideoEncoderBackend::PreEncoded;
Expand Down
Loading