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
19 changes: 14 additions & 5 deletions src/ffi_client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <cassert>
#include <csignal>
#include <cstdio>
#include <exception>
#include <string>
#include <type_traits>

Expand Down Expand Up @@ -694,9 +695,9 @@ std::future<void> FfiClient::simulateScenarioAsync(uintptr_t room_handle, int sc
}

// Participant APIs Implementation
std::future<proto::OwnedTrackPublication> FfiClient::publishTrackAsync(std::uint64_t local_participant_handle,
std::uint64_t track_handle,
const TrackPublishOptions& options) {
std::future<proto::OwnedTrackPublication> FfiClient::publishTrackAsync(
std::uint64_t local_participant_handle, std::uint64_t track_handle, const TrackPublishOptions& options,
std::function<void(const proto::OwnedTrackPublication&)> on_success) {
// Generate client-side async_id first
const AsyncId async_id = generateAsyncId();

Expand All @@ -708,7 +709,8 @@ std::future<proto::OwnedTrackPublication> FfiClient::publishTrackAsync(std::uint
return event.has_publish_track() && event.publish_track().async_id() == async_id;
},
// Handler: resolve with publication or throw error
[](const proto::FfiEvent& event, std::promise<proto::OwnedTrackPublication>& pr) {
[on_success = std::move(on_success)](const proto::FfiEvent& event,
std::promise<proto::OwnedTrackPublication>& pr) {
const auto& cb = event.publish_track();

// Oneof message { string error = 2; OwnedTrackPublication publication =
Expand All @@ -723,7 +725,14 @@ std::future<proto::OwnedTrackPublication> FfiClient::publishTrackAsync(std::uint
}

const proto::OwnedTrackPublication& pub = cb.publication();
pr.set_value(pub);
try {
if (on_success) {
on_success(pub);
}
pr.set_value(pub);
} catch (...) {
pr.set_exception(std::current_exception());
}
});

// Build and send the request
Expand Down
6 changes: 3 additions & 3 deletions src/ffi_client.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,9 @@ class LIVEKIT_INTERNAL_API FfiClient {
std::future<void> simulateScenarioAsync(uintptr_t room_handle, int scenario);

// Participant APIs
std::future<proto::OwnedTrackPublication> publishTrackAsync(std::uint64_t local_participant_handle,
std::uint64_t track_handle,
const TrackPublishOptions& options);
std::future<proto::OwnedTrackPublication> publishTrackAsync(
std::uint64_t local_participant_handle, std::uint64_t track_handle, const TrackPublishOptions& options,
std::function<void(const proto::OwnedTrackPublication&)> on_success = {});
std::future<void> unpublishTrackAsync(std::uint64_t local_participant_handle, const std::string& track_sid,
bool stop_on_unpublish);
std::future<void> publishDataAsync(std::uint64_t local_participant_handle, const std::uint8_t* data_ptr,
Expand Down
27 changes: 12 additions & 15 deletions src/local_participant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -191,21 +191,18 @@ void LocalParticipant::publishTrack(const std::shared_ptr<Track>& track, const T
if (track_handle == 0) {
throw std::runtime_error("LocalParticipant::publishTrack: invalid track FFI handle");
}
auto fut = FfiClient::instance().publishTrackAsync(static_cast<std::uint64_t>(participant_handle),
static_cast<std::uint64_t>(track_handle), options);

// Will throw if the async op fails (error in callback).
const proto::OwnedTrackPublication owned_pub = fut.get();

// Construct a LocalTrackPublication from the proto publication.
auto publication = std::make_shared<LocalTrackPublication>(owned_pub);

const std::string sid = publication->sid();
{
const std::scoped_lock<std::mutex> guard(published_tracks_mutex_);
published_tracks_by_sid_[sid] = std::weak_ptr<Track>(track);
track->setPublication(publication);
}
auto fut = FfiClient::instance().publishTrackAsync(
static_cast<std::uint64_t>(participant_handle), static_cast<std::uint64_t>(track_handle), options,
[this, track](const proto::OwnedTrackPublication& owned_pub) {
auto publication = std::make_shared<LocalTrackPublication>(owned_pub);
const std::scoped_lock<std::mutex> guard(published_tracks_mutex_);
published_tracks_by_sid_[publication->sid()] = track;
track->setPublication(publication);
});

// Publication state is installed on the FFI thread before this future is
// completed and before Rust can emit LocalTrackPublished.
(void)fut.get();
}

std::shared_ptr<LocalVideoTrack> LocalParticipant::publishVideoTrack(const std::string& name,
Expand Down
33 changes: 33 additions & 0 deletions src/tests/integration/test_local_track_publish_sid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,12 @@
#include <gtest/gtest.h>

#include <chrono>
#include <condition_variable>
#include <memory>
#include <mutex>
#include <string>
#include <thread>
#include <unordered_set>

#include "../common/audio_utils.h"
#include "../common/room_test_access.h"
Expand All @@ -31,6 +34,30 @@ namespace {

using namespace std::chrono_literals;

class LocalPublicationDelegate final : public RoomDelegate {
public:
void onLocalTrackPublished(Room&, const LocalTrackPublishedEvent& event) override {
if (!event.publication) {
return;
}
{
const std::scoped_lock<std::mutex> guard(mutex_);
published_sids_.insert(event.publication->sid());
}
cv_.notify_all();
}

bool waitForPublished(const std::string& sid, std::chrono::milliseconds timeout) {
std::unique_lock<std::mutex> lock(mutex_);
return cv_.wait_for(lock, timeout, [&] { return published_sids_.count(sid) != 0; });
}

private:
std::mutex mutex_;
std::condition_variable cv_;
std::unordered_set<std::string> published_sids_;
};

void expectTrackSidAssigned(const Track& track, const LocalTrackPublication& publication) {
const std::string& track_sid = track.sid();
const std::string& publication_sid = publication.sid();
Expand Down Expand Up @@ -58,6 +85,8 @@ TEST_F(LocalTrackPublishSidTest, PublishVideoTrackAssignsSid) {
failIfNotConfigured();

Room room;
LocalPublicationDelegate delegate;
room.setDelegate(&delegate);
const RoomOptions room_options;
ASSERT_TRUE(room.connect(config_.url, config_.token_a, room_options));

Expand All @@ -69,13 +98,16 @@ TEST_F(LocalTrackPublishSidTest, PublishVideoTrackAssignsSid) {
ASSERT_NE(track->publication(), nullptr);

expectTrackSidAssigned(*track, *track->publication());
EXPECT_TRUE(delegate.waitForPublished(track->sid(), 5s));
lockLocalParticipant(room)->unpublishTrack(track->publication()->sid());
}

TEST_F(LocalTrackPublishSidTest, PublishAudioTrackAssignsSid) {
failIfNotConfigured();

Room room;
LocalPublicationDelegate delegate;
room.setDelegate(&delegate);
const RoomOptions room_options;
ASSERT_TRUE(room.connect(config_.url, config_.token_a, room_options));

Expand All @@ -87,6 +119,7 @@ TEST_F(LocalTrackPublishSidTest, PublishAudioTrackAssignsSid) {
ASSERT_NE(track->publication(), nullptr);

expectTrackSidAssigned(*track, *track->publication());
EXPECT_TRUE(delegate.waitForPublished(track->sid(), 5s));
lockLocalParticipant(room)->unpublishTrack(track->publication()->sid());
}

Expand Down
Loading