Skip to content

Commit 7596552

Browse files
Expose data track pipeline options (#186)
* Expose data track pipeline options --------- Co-authored-by: Alan George <alan.george@livekit.io>
1 parent 0f560db commit 7596552

3 files changed

Lines changed: 115 additions & 0 deletions

File tree

include/livekit/remote_data_track.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616

1717
#pragma once
1818

19+
#include <cstdint>
1920
#include <memory>
21+
#include <optional>
2022
#include <string>
2123

2224
#include "livekit/data_track_error.h"
@@ -32,6 +34,21 @@ namespace proto {
3234
class OwnedRemoteDataTrack;
3335
}
3436

37+
/// Track-level options that configure how the incoming-frame pipeline
38+
/// reassembles packets for a remote data track.
39+
///
40+
/// Applied via RemoteDataTrack::setPipelineOptions().
41+
struct DataTrackPipelineOptions {
42+
/// Maximum number of partial frames the depacketizer will track
43+
/// concurrently for this track.
44+
///
45+
/// Defaults to 1. Higher values give more out-of-order tolerance for
46+
/// high-frequency senders at the cost of additional buffering. Zero is
47+
/// not a valid value; if a value of zero is provided, it will be
48+
/// clamped to one. Leave unset to keep the current value.
49+
std::optional<std::uint32_t> max_partial_frames{std::nullopt};
50+
};
51+
3552
/// Represents a data track published by a remote participant.
3653
///
3754
/// Discovered via the DataTrackPublishedEvent room event. Unlike
@@ -65,6 +82,16 @@ class RemoteDataTrack {
6582
/// Whether the track is still published by the remote participant.
6683
LIVEKIT_API bool isPublished() const;
6784

85+
/// @brief Configures options for the pipeline handling incoming packets for
86+
/// this track.
87+
///
88+
/// These options apply to all current and future subscriptions of this
89+
/// track, and may be set at any time. New options take effect with the
90+
/// next received packet.
91+
///
92+
/// @param options Pipeline options to apply to this remote data track.
93+
LIVEKIT_API void setPipelineOptions(const DataTrackPipelineOptions& options);
94+
6895
#ifdef LIVEKIT_TEST_ACCESS
6996
/// Test-only accessor for exercising lower-level FFI subscription paths.
7097
uintptr_t testFfiHandleId() const noexcept { return ffiHandleId(); }

src/remote_data_track.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,22 @@ bool RemoteDataTrack::isPublished() const {
4646
return resp.remote_data_track_is_published().is_published();
4747
}
4848

49+
void RemoteDataTrack::setPipelineOptions(const DataTrackPipelineOptions& options) {
50+
if (!handle_.valid()) {
51+
return;
52+
}
53+
54+
proto::FfiRequest req;
55+
auto* msg = req.mutable_remote_data_track_set_pipeline_options();
56+
msg->set_track_handle(static_cast<uint64_t>(handle_.get()));
57+
auto* opts = msg->mutable_options();
58+
if (options.max_partial_frames) {
59+
opts->set_max_partial_frames(*options.max_partial_frames);
60+
}
61+
62+
FfiClient::instance().sendRequest(req);
63+
}
64+
4965
Result<std::shared_ptr<DataTrackStream>, SubscribeDataTrackError> RemoteDataTrack::subscribe(
5066
const DataTrackStream::Options& options) {
5167
if (!handle_.valid()) {

src/tests/integration/test_data_track.cpp

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,78 @@ TEST_P(DataTrackTransportTest, PublishesAndReceivesFramesEndToEnd) {
367367
EXPECT_TRUE(remote_track_published_after_read);
368368
}
369369

370+
TEST_F(DataTrackE2ETest, ReceivesFramesWithCustomPipelineOptionsEndToEnd) {
371+
constexpr std::size_t payload_len = 1024;
372+
const auto track_name = makeTrackName("pipeline_options");
373+
374+
std::vector<TestRoomConnectionOptions> room_configs(2);
375+
room_configs[0].room_options.single_peer_connection = false;
376+
room_configs[1].room_options.single_peer_connection = false;
377+
378+
DataTrackPublishedDelegate subscriber_delegate;
379+
room_configs[1].delegate = &subscriber_delegate;
380+
381+
auto rooms = testRooms(room_configs);
382+
auto& publisher_room = rooms[0];
383+
const auto publisher_identity = lockLocalParticipant(*publisher_room)->identity();
384+
385+
auto local_track = requirePublishedTrack(publisher_room->localParticipant(), track_name);
386+
ASSERT_TRUE(local_track->isPublished());
387+
EXPECT_FALSE(local_track->info().uses_e2ee);
388+
EXPECT_EQ(local_track->info().name, track_name);
389+
390+
auto remote_track = subscriber_delegate.waitForTrack(kTrackWaitTimeout);
391+
ASSERT_NE(remote_track, nullptr) << "Timed out waiting for remote data track";
392+
EXPECT_TRUE(remote_track->isPublished());
393+
EXPECT_FALSE(remote_track->info().uses_e2ee);
394+
EXPECT_EQ(remote_track->info().name, track_name);
395+
EXPECT_EQ(remote_track->publisherIdentity(), publisher_identity);
396+
397+
DataTrackPipelineOptions pipeline_options;
398+
pipeline_options.max_partial_frames = 4;
399+
remote_track->setPipelineOptions(pipeline_options);
400+
401+
auto subscribe_result = remote_track->subscribe();
402+
if (!subscribe_result) {
403+
FAIL() << describeDataTrackError(subscribe_result.error());
404+
}
405+
auto subscription = subscribe_result.value();
406+
407+
std::atomic<bool> keep_publishing{true};
408+
auto publisher = std::async(std::launch::async, [&]() {
409+
DataTrackFrame frame;
410+
frame.payload.assign(payload_len, kTransportPayloadValue);
411+
while (keep_publishing.load()) {
412+
requirePushSuccess(local_track->tryPush(frame), "Failed to push data frame");
413+
std::this_thread::sleep_for(50ms);
414+
}
415+
});
416+
417+
DataTrackFrame frame;
418+
std::exception_ptr read_error;
419+
try {
420+
frame = readFrameWithTimeout(subscription, kTransportFrameTimeout);
421+
} catch (...) {
422+
read_error = std::current_exception();
423+
}
424+
425+
const bool remote_track_published_after_read = remote_track->isPublished();
426+
keep_publishing.store(false);
427+
subscription->close();
428+
local_track->unpublishDataTrack();
429+
430+
publisher.get();
431+
if (read_error) {
432+
std::rethrow_exception(read_error);
433+
}
434+
435+
ASSERT_EQ(frame.payload.size(), payload_len);
436+
EXPECT_TRUE(std::all_of(frame.payload.begin(), frame.payload.end(),
437+
[](std::uint8_t byte) { return byte == kTransportPayloadValue; }));
438+
EXPECT_FALSE(frame.user_timestamp.has_value());
439+
EXPECT_TRUE(remote_track_published_after_read);
440+
}
441+
370442
TEST_F(DataTrackE2ETest, UnpublishUpdatesPublishedStateEndToEnd) {
371443
const auto track_name = makeTrackName("published_state");
372444

0 commit comments

Comments
 (0)