Skip to content
Draft
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
3 changes: 3 additions & 0 deletions include/livekit/local_track_publication.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ class LIVEKIT_API LocalTrackPublication : public TrackPublication {
/// Note, this LocalTrackPublication is constructed internally only;
/// safe to accept proto::OwnedTrackPublication.
explicit LocalTrackPublication(const proto::OwnedTrackPublication& owned);

private:
friend class Room;
};

} // namespace livekit
38 changes: 38 additions & 0 deletions src/ffi_client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ std::optional<FfiClient::AsyncId> ExtractAsyncId(const proto::FfiEvent& event) {
return event.get_stats().async_id();
case E::kGetSessionStats:
return event.get_session_stats().async_id();
case E::kSimulateScenario:
return event.simulate_scenario().async_id();
case E::kPublishSipDtmf:
return event.publish_sip_dtmf().async_id();
case E::kChatMessage:
Expand Down Expand Up @@ -655,6 +657,42 @@ std::future<SessionStats> FfiClient::getSessionStatsAsync(uintptr_t room_handle)
return fut;
}

std::future<void> FfiClient::simulateScenarioAsync(uintptr_t room_handle, int scenario) {
const AsyncId async_id = generateAsyncId();

auto fut = registerAsync<void>(
async_id,
[async_id](const proto::FfiEvent& event) {
return event.has_simulate_scenario() && event.simulate_scenario().async_id() == async_id;
},
[](const proto::FfiEvent& event, std::promise<void>& pr) {
const auto& cb = event.simulate_scenario();
if (cb.has_error() && !cb.error().empty()) {
pr.set_exception(std::make_exception_ptr(std::runtime_error(cb.error())));
return;
}
pr.set_value();
});

proto::FfiRequest req;
auto* msg = req.mutable_simulate_scenario();
msg->set_room_handle(room_handle);
msg->set_scenario(static_cast<proto::SimulateScenarioKind>(scenario));
msg->set_request_async_id(async_id);

try {
const proto::FfiResponse resp = sendRequest(req);
if (!resp.has_simulate_scenario()) {
logAndThrow("FfiResponse missing simulate_scenario");
}
} catch (...) {
cancelPendingByAsyncId(async_id);
throw;
}

return fut;
}

// Participant APIs Implementation
std::future<proto::OwnedTrackPublication> FfiClient::publishTrackAsync(std::uint64_t local_participant_handle,
std::uint64_t track_handle,
Expand Down
2 changes: 2 additions & 0 deletions src/ffi_client.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@

std::future<SessionStats> getSessionStatsAsync(uintptr_t room_handle);

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,
Expand Down Expand Up @@ -229,17 +231,17 @@
bool cancelPendingByAsyncId(AsyncId async_id);

/// Map of listener IDs to listener slots
std::unordered_map<ListenerId, std::shared_ptr<ListenerSlot>> listeners_;

Check warning on line 234 in src/ffi_client.h

View workflow job for this annotation

GitHub Actions / Builds / Build (windows-x64)

'livekit::FfiClient::listeners_': 'std::unordered_map<livekit::FfiClient::ListenerId,std::shared_ptr<livekit::FfiClient::ListenerSlot>,std::hash<int>,std::equal_to<livekit::FfiClient::ListenerId>,std::allocator<std::pair<const livekit::FfiClient::ListenerId,std::shared_ptr<livekit::FfiClient::ListenerSlot>>>>' needs to have dll-interface to be used by clients of 'livekit::FfiClient' [D:\a\client-sdk-cpp\client-sdk-cpp\build-release\livekit.vcxproj]

Check warning on line 234 in src/ffi_client.h

View workflow job for this annotation

GitHub Actions / Builds / Build (windows-x64)

'livekit::FfiClient::listeners_': 'std::unordered_map<livekit::FfiClient::ListenerId,std::shared_ptr<livekit::FfiClient::ListenerSlot>,std::hash<int>,std::equal_to<livekit::FfiClient::ListenerId>,std::allocator<std::pair<const livekit::FfiClient::ListenerId,std::shared_ptr<livekit::FfiClient::ListenerSlot>>>>' needs to have dll-interface to be used by clients of 'livekit::FfiClient' [D:\a\client-sdk-cpp\client-sdk-cpp\build-release\livekit.vcxproj]

Check warning on line 234 in src/ffi_client.h

View workflow job for this annotation

GitHub Actions / Tests / Test (windows-x64)

'livekit::FfiClient::listeners_': 'std::unordered_map<livekit::FfiClient::ListenerId,std::shared_ptr<livekit::FfiClient::ListenerSlot>,std::hash<int>,std::equal_to<livekit::FfiClient::ListenerId>,std::allocator<std::pair<const livekit::FfiClient::ListenerId,std::shared_ptr<livekit::FfiClient::ListenerSlot>>>>' needs to have dll-interface to be used by clients of 'livekit::FfiClient' [D:\a\client-sdk-cpp\client-sdk-cpp\build-release\livekit.vcxproj]

Check warning on line 234 in src/ffi_client.h

View workflow job for this annotation

GitHub Actions / Tests / Test (windows-x64)

'livekit::FfiClient::listeners_': 'std::unordered_map<livekit::FfiClient::ListenerId,std::shared_ptr<livekit::FfiClient::ListenerSlot>,std::hash<int>,std::equal_to<livekit::FfiClient::ListenerId>,std::allocator<std::pair<const livekit::FfiClient::ListenerId,std::shared_ptr<livekit::FfiClient::ListenerSlot>>>>' needs to have dll-interface to be used by clients of 'livekit::FfiClient' [D:\a\client-sdk-cpp\client-sdk-cpp\build-release\livekit.vcxproj]
/// Next listener ID to generate
std::atomic<ListenerId> next_listener_id{1};

Check warning on line 236 in src/ffi_client.h

View workflow job for this annotation

GitHub Actions / Builds / Build (windows-x64)

'livekit::FfiClient::next_listener_id': 'std::atomic<int>' needs to have dll-interface to be used by clients of 'livekit::FfiClient' [D:\a\client-sdk-cpp\client-sdk-cpp\build-release\livekit.vcxproj]

Check warning on line 236 in src/ffi_client.h

View workflow job for this annotation

GitHub Actions / Builds / Build (windows-x64)

'livekit::FfiClient::next_listener_id': 'std::atomic<int>' needs to have dll-interface to be used by clients of 'livekit::FfiClient' [D:\a\client-sdk-cpp\client-sdk-cpp\build-release\livekit.vcxproj]

Check warning on line 236 in src/ffi_client.h

View workflow job for this annotation

GitHub Actions / Tests / Test (windows-x64)

'livekit::FfiClient::next_listener_id': 'std::atomic<int>' needs to have dll-interface to be used by clients of 'livekit::FfiClient' [D:\a\client-sdk-cpp\client-sdk-cpp\build-release\livekit.vcxproj]

Check warning on line 236 in src/ffi_client.h

View workflow job for this annotation

GitHub Actions / Tests / Test (windows-x64)

'livekit::FfiClient::next_listener_id': 'std::atomic<int>' needs to have dll-interface to be used by clients of 'livekit::FfiClient' [D:\a\client-sdk-cpp\client-sdk-cpp\build-release\livekit.vcxproj]
mutable std::mutex lock_;
/// Map of async IDs to pending operations
mutable std::unordered_map<AsyncId, std::unique_ptr<PendingBase>> pending_by_id_;

Check warning on line 239 in src/ffi_client.h

View workflow job for this annotation

GitHub Actions / Builds / Build (windows-x64)

'livekit::FfiClient::pending_by_id_': 'std::unordered_map<livekit::FfiClient::AsyncId,std::unique_ptr<livekit::FfiClient::PendingBase,std::default_delete<livekit::FfiClient::PendingBase>>,std::hash<livekit::FfiClient::AsyncId>,std::equal_to<livekit::FfiClient::AsyncId>,std::allocator<std::pair<const livekit::FfiClient::AsyncId,std::unique_ptr<livekit::FfiClient::PendingBase,std::default_delete<livekit::FfiClient::PendingBase>>>>>' needs to have dll-interface to be used by clients of 'livekit::FfiClient' [D:\a\client-sdk-cpp\client-sdk-cpp\build-release\livekit.vcxproj]

Check warning on line 239 in src/ffi_client.h

View workflow job for this annotation

GitHub Actions / Tests / Test (windows-x64)

'livekit::FfiClient::pending_by_id_': 'std::unordered_map<livekit::FfiClient::AsyncId,std::unique_ptr<livekit::FfiClient::PendingBase,std::default_delete<livekit::FfiClient::PendingBase>>,std::hash<livekit::FfiClient::AsyncId>,std::equal_to<livekit::FfiClient::AsyncId>,std::allocator<std::pair<const livekit::FfiClient::AsyncId,std::unique_ptr<livekit::FfiClient::PendingBase,std::default_delete<livekit::FfiClient::PendingBase>>>>>' needs to have dll-interface to be used by clients of 'livekit::FfiClient' [D:\a\client-sdk-cpp\client-sdk-cpp\build-release\livekit.vcxproj]
/// Next async ID to generate
std::atomic<AsyncId> next_async_id_{1};

Check warning on line 241 in src/ffi_client.h

View workflow job for this annotation

GitHub Actions / Builds / Build (windows-x64)

'livekit::FfiClient::next_async_id_': 'std::atomic<unsigned __int64>' needs to have dll-interface to be used by clients of 'livekit::FfiClient' [D:\a\client-sdk-cpp\client-sdk-cpp\build-release\livekit.vcxproj]

Check warning on line 241 in src/ffi_client.h

View workflow job for this annotation

GitHub Actions / Tests / Test (windows-x64)

'livekit::FfiClient::next_async_id_': 'std::atomic<unsigned __int64>' needs to have dll-interface to be used by clients of 'livekit::FfiClient' [D:\a\client-sdk-cpp\client-sdk-cpp\build-release\livekit.vcxproj]

void pushEvent(const proto::FfiEvent& event) const;
friend void ffiEventCallback(const uint8_t* buf, size_t len);
std::atomic<LifecycleState> lifecycle_state_{LifecycleState::Uninitialized};

Check warning on line 245 in src/ffi_client.h

View workflow job for this annotation

GitHub Actions / Builds / Build (windows-x64)

'livekit::FfiClient::lifecycle_state_': 'std::atomic<livekit::FfiClient::LifecycleState>' needs to have dll-interface to be used by clients of 'livekit::FfiClient' [D:\a\client-sdk-cpp\client-sdk-cpp\build-release\livekit.vcxproj]

Check warning on line 245 in src/ffi_client.h

View workflow job for this annotation

GitHub Actions / Tests / Test (windows-x64)

'livekit::FfiClient::lifecycle_state_': 'std::atomic<livekit::FfiClient::LifecycleState>' needs to have dll-interface to be used by clients of 'livekit::FfiClient' [D:\a\client-sdk-cpp\client-sdk-cpp\build-release\livekit.vcxproj]
};
} // namespace livekit
62 changes: 62 additions & 0 deletions src/room.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@
#include "ffi_client.h"
#include "livekit/audio_stream.h"
#include "livekit/e2ee.h"
#include "livekit/local_audio_track.h"
#include "livekit/local_participant.h"
#include "livekit/local_track_publication.h"
#include "livekit/local_video_track.h"
#include "livekit/remote_audio_track.h"
#include "livekit/remote_data_track.h"
#include "livekit/remote_participant.h"
Expand All @@ -47,6 +50,33 @@

namespace {

std::shared_ptr<LocalTrackPublication> localTrackPublication(const std::shared_ptr<Track>& track) {
if (!track) {
return nullptr;
}
if (auto video = std::dynamic_pointer_cast<LocalVideoTrack>(track)) {
return video->publication();
}
if (auto audio = std::dynamic_pointer_cast<LocalAudioTrack>(track)) {
return audio->publication();
}
return nullptr;
}

void updateLocalTrackPublicationInfo(LocalTrackPublication& publication, const proto::TrackPublicationInfo& info) {
publication.sid_ = info.sid();

Check failure on line 67 in src/room.cpp

View workflow job for this annotation

GitHub Actions / Builds / Build (windows-x64)

'livekit::TrackPublication::sid_': cannot access protected member declared in class 'livekit::TrackPublication' [D:\a\client-sdk-cpp\client-sdk-cpp\build-release\livekit.vcxproj]

Check failure on line 67 in src/room.cpp

View workflow job for this annotation

GitHub Actions / Tests / Test (windows-x64)

'livekit::TrackPublication::sid_': cannot access protected member declared in class 'livekit::TrackPublication' [D:\a\client-sdk-cpp\client-sdk-cpp\build-release\livekit.vcxproj]

Check failure on line 67 in src/room.cpp

View workflow job for this annotation

GitHub Actions / C++ Tools / clang-tidy

clang-tidy (clang-diagnostic-error)

'sid_' is a protected member of 'livekit::TrackPublication'
publication.name_ = info.name();

Check failure on line 68 in src/room.cpp

View workflow job for this annotation

GitHub Actions / Builds / Build (windows-x64)

'livekit::TrackPublication::name_': cannot access protected member declared in class 'livekit::TrackPublication' [D:\a\client-sdk-cpp\client-sdk-cpp\build-release\livekit.vcxproj]

Check failure on line 68 in src/room.cpp

View workflow job for this annotation

GitHub Actions / Tests / Test (windows-x64)

'livekit::TrackPublication::name_': cannot access protected member declared in class 'livekit::TrackPublication' [D:\a\client-sdk-cpp\client-sdk-cpp\build-release\livekit.vcxproj]

Check failure on line 68 in src/room.cpp

View workflow job for this annotation

GitHub Actions / C++ Tools / clang-tidy

clang-tidy (clang-diagnostic-error)

'name_' is a protected member of 'livekit::TrackPublication'
publication.kind_ = fromProto(info.kind());

Check failure on line 69 in src/room.cpp

View workflow job for this annotation

GitHub Actions / Builds / Build (windows-x64)

'livekit::TrackPublication::kind_': cannot access protected member declared in class 'livekit::TrackPublication' [D:\a\client-sdk-cpp\client-sdk-cpp\build-release\livekit.vcxproj]

Check failure on line 69 in src/room.cpp

View workflow job for this annotation

GitHub Actions / Tests / Test (windows-x64)

'livekit::TrackPublication::kind_': cannot access protected member declared in class 'livekit::TrackPublication' [D:\a\client-sdk-cpp\client-sdk-cpp\build-release\livekit.vcxproj]

Check failure on line 69 in src/room.cpp

View workflow job for this annotation

GitHub Actions / C++ Tools / clang-tidy

clang-tidy (clang-diagnostic-error)

'kind_' is a protected member of 'livekit::TrackPublication'
publication.source_ = fromProto(info.source());

Check failure on line 70 in src/room.cpp

View workflow job for this annotation

GitHub Actions / Builds / Build (windows-x64)

'livekit::TrackPublication::source_': cannot access protected member declared in class 'livekit::TrackPublication' [D:\a\client-sdk-cpp\client-sdk-cpp\build-release\livekit.vcxproj]

Check failure on line 70 in src/room.cpp

View workflow job for this annotation

GitHub Actions / Tests / Test (windows-x64)

'livekit::TrackPublication::source_': cannot access protected member declared in class 'livekit::TrackPublication' [D:\a\client-sdk-cpp\client-sdk-cpp\build-release\livekit.vcxproj]

Check failure on line 70 in src/room.cpp

View workflow job for this annotation

GitHub Actions / C++ Tools / clang-tidy

clang-tidy (clang-diagnostic-error)

'source_' is a protected member of 'livekit::TrackPublication'
publication.simulcasted_ = info.simulcasted();

Check failure on line 71 in src/room.cpp

View workflow job for this annotation

GitHub Actions / Builds / Build (windows-x64)

'livekit::TrackPublication::simulcasted_': cannot access protected member declared in class 'livekit::TrackPublication' [D:\a\client-sdk-cpp\client-sdk-cpp\build-release\livekit.vcxproj]

Check failure on line 71 in src/room.cpp

View workflow job for this annotation

GitHub Actions / Tests / Test (windows-x64)

'livekit::TrackPublication::simulcasted_': cannot access protected member declared in class 'livekit::TrackPublication' [D:\a\client-sdk-cpp\client-sdk-cpp\build-release\livekit.vcxproj]

Check failure on line 71 in src/room.cpp

View workflow job for this annotation

GitHub Actions / C++ Tools / clang-tidy

clang-tidy (clang-diagnostic-error)

'simulcasted_' is a protected member of 'livekit::TrackPublication'
publication.width_ = info.width();

Check failure on line 72 in src/room.cpp

View workflow job for this annotation

GitHub Actions / Builds / Build (windows-x64)

'livekit::TrackPublication::width_': cannot access protected member declared in class 'livekit::TrackPublication' [D:\a\client-sdk-cpp\client-sdk-cpp\build-release\livekit.vcxproj]

Check failure on line 72 in src/room.cpp

View workflow job for this annotation

GitHub Actions / Tests / Test (windows-x64)

'livekit::TrackPublication::width_': cannot access protected member declared in class 'livekit::TrackPublication' [D:\a\client-sdk-cpp\client-sdk-cpp\build-release\livekit.vcxproj]

Check failure on line 72 in src/room.cpp

View workflow job for this annotation

GitHub Actions / C++ Tools / clang-tidy

clang-tidy (clang-diagnostic-error)

'width_' is a protected member of 'livekit::TrackPublication'
publication.height_ = info.height();

Check failure on line 73 in src/room.cpp

View workflow job for this annotation

GitHub Actions / Builds / Build (windows-x64)

'livekit::TrackPublication::height_': cannot access protected member declared in class 'livekit::TrackPublication' [D:\a\client-sdk-cpp\client-sdk-cpp\build-release\livekit.vcxproj]

Check failure on line 73 in src/room.cpp

View workflow job for this annotation

GitHub Actions / Tests / Test (windows-x64)

'livekit::TrackPublication::height_': cannot access protected member declared in class 'livekit::TrackPublication' [D:\a\client-sdk-cpp\client-sdk-cpp\build-release\livekit.vcxproj]

Check failure on line 73 in src/room.cpp

View workflow job for this annotation

GitHub Actions / C++ Tools / clang-tidy

clang-tidy (clang-diagnostic-error)

'height_' is a protected member of 'livekit::TrackPublication'
publication.mime_type_ = info.mime_type();

Check failure on line 74 in src/room.cpp

View workflow job for this annotation

GitHub Actions / Builds / Build (windows-x64)

'livekit::TrackPublication::mime_type_': cannot access protected member declared in class 'livekit::TrackPublication' [D:\a\client-sdk-cpp\client-sdk-cpp\build-release\livekit.vcxproj]

Check failure on line 74 in src/room.cpp

View workflow job for this annotation

GitHub Actions / Tests / Test (windows-x64)

'livekit::TrackPublication::mime_type_': cannot access protected member declared in class 'livekit::TrackPublication' [D:\a\client-sdk-cpp\client-sdk-cpp\build-release\livekit.vcxproj]

Check failure on line 74 in src/room.cpp

View workflow job for this annotation

GitHub Actions / C++ Tools / clang-tidy

clang-tidy (clang-diagnostic-error)

'mime_type_' is a protected member of 'livekit::TrackPublication'
publication.muted_ = info.muted();

Check failure on line 75 in src/room.cpp

View workflow job for this annotation

GitHub Actions / Builds / Build (windows-x64)

'livekit::TrackPublication::muted_': cannot access protected member declared in class 'livekit::TrackPublication' [D:\a\client-sdk-cpp\client-sdk-cpp\build-release\livekit.vcxproj]

Check failure on line 75 in src/room.cpp

View workflow job for this annotation

GitHub Actions / Tests / Test (windows-x64)

'livekit::TrackPublication::muted_': cannot access protected member declared in class 'livekit::TrackPublication' [D:\a\client-sdk-cpp\client-sdk-cpp\build-release\livekit.vcxproj]

Check failure on line 75 in src/room.cpp

View workflow job for this annotation

GitHub Actions / C++ Tools / clang-tidy

clang-tidy (clang-diagnostic-error)

'muted_' is a protected member of 'livekit::TrackPublication'
publication.encryption_type_ = static_cast<EncryptionType>(info.encryption_type());

Check failure on line 76 in src/room.cpp

View workflow job for this annotation

GitHub Actions / Builds / Build (windows-x64)

'livekit::TrackPublication::encryption_type_': cannot access protected member declared in class 'livekit::TrackPublication' [D:\a\client-sdk-cpp\client-sdk-cpp\build-release\livekit.vcxproj]

Check failure on line 76 in src/room.cpp

View workflow job for this annotation

GitHub Actions / Tests / Test (windows-x64)

'livekit::TrackPublication::encryption_type_': cannot access protected member declared in class 'livekit::TrackPublication' [D:\a\client-sdk-cpp\client-sdk-cpp\build-release\livekit.vcxproj]

Check failure on line 76 in src/room.cpp

View workflow job for this annotation

GitHub Actions / C++ Tools / clang-tidy

clang-tidy (clang-diagnostic-error)

'encryption_type_' is a protected member of 'livekit::TrackPublication'
publication.audio_features_ = convertAudioFeatures(info.audio_features());
}

std::shared_ptr<livekit::RemoteParticipant> createRemoteParticipant(const proto::OwnedParticipant& owned) {
const auto& pinfo = owned.info();
std::unordered_map<std::string, std::string> attrs;
Expand Down Expand Up @@ -593,6 +623,38 @@
}
break;
}
case proto::RoomEvent::kLocalTrackRepublished: {
const std::scoped_lock<std::mutex> guard(lock_);
if (!local_participant_) {
LK_LOG_ERROR("kLocalTrackRepublished: local_participant_ is nullptr");
break;
}
const auto& ltr = re.local_track_republished();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: variable name, maybe local_track_repub

const std::string& previous_sid = ltr.previous_sid();

auto& published = local_participant_->published_tracks_by_sid_;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: variable name for the data type, maybe published_list or published_set (depending on what published_tracks_by_sid_ is)

auto it = published.find(previous_sid);
if (it == published.end()) {
LK_LOG_WARN("local_track_republished for unknown previous sid: {}", previous_sid);
break;
}
auto track = it->second.lock();
if (!track) {
published.erase(it);
LK_LOG_WARN("local_track_republished for expired previous sid: {}", previous_sid);
break;
}
auto publication = localTrackPublication(track);
if (!publication) {
LK_LOG_WARN("local_track_republished missing publication for sid: {}", previous_sid);
break;
}
Comment on lines +636 to +651

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In these cases do we just lose the track entirely given the break on each? Is that intentional?

updateLocalTrackPublicationInfo(*publication, ltr.info());
published.erase(it);
published[publication->sid()] = track;
track->setPublication(publication);
break;
}
case proto::RoomEvent::kLocalTrackSubscribed: {
LocalTrackSubscribedEvent ev;
{
Expand Down
49 changes: 49 additions & 0 deletions src/tests/common/room_test_access.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright 2026 LiveKit
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#pragma once

#include <livekit/livekit.h>

#include <memory>
#include <mutex>
#include <stdexcept>

#include "ffi_client.h"
#include "room.pb.h"

namespace livekit {

struct RoomTestAccess {
static int listenerId(const Room& room) {
const std::scoped_lock<std::mutex> guard(room.lock_);
return room.listener_id_;
}

static void simulateScenario(Room& room, proto::SimulateScenarioKind scenario) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: Do we need this?

std::shared_ptr<FfiHandle> handle;
{
const std::scoped_lock<std::mutex> guard(room.lock_);
handle = room.room_handle_;
}
if (!handle) {
throw std::runtime_error("cannot simulate scenario for a disconnected room");
}
FfiClient::instance().simulateScenarioAsync(handle->get(), static_cast<int>(scenario)).get();
}
};

} // namespace livekit
47 changes: 47 additions & 0 deletions src/tests/integration/test_local_track_publish_sid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,21 @@

#include <gtest/gtest.h>

#include <chrono>
#include <memory>
#include <string>
#include <thread>

#include "../common/audio_utils.h"
#include "../common/room_test_access.h"
#include "../common/test_common.h"
#include "room.pb.h"

namespace livekit::test {
namespace {

using namespace std::chrono_literals;

void expectTrackSidAssigned(const Track& track, const LocalTrackPublication& publication) {
const std::string& track_sid = track.sid();
const std::string& publication_sid = publication.sid();
Expand All @@ -33,6 +39,17 @@ void expectTrackSidAssigned(const Track& track, const LocalTrackPublication& pub
EXPECT_EQ(track_sid, publication_sid);
}

bool waitForSidChange(const Track& track, const std::string& previous_sid, std::chrono::milliseconds timeout) {
const auto deadline = std::chrono::steady_clock::now() + timeout;
while (std::chrono::steady_clock::now() < deadline) {
if (track.sid() != previous_sid && track.sid() != "TR_unknown") {
return true;
}
std::this_thread::sleep_for(50ms);
}
return track.sid() != previous_sid && track.sid() != "TR_unknown";
}

} // namespace

class LocalTrackPublishSidTest : public LiveKitTestBase {};
Expand Down Expand Up @@ -73,4 +90,34 @@ TEST_F(LocalTrackPublishSidTest, PublishAudioTrackAssignsSid) {
lockLocalParticipant(room)->unpublishTrack(track->publication()->sid());
}

TEST_F(LocalTrackPublishSidTest, FullReconnectUpdatesPublishedSid) {
failIfNotConfigured();

Room room;
const RoomOptions room_options;
ASSERT_TRUE(room.connect(config_.url, config_.token_a, room_options));

auto source = std::make_shared<EncodedVideoSource>(VideoCodec::H264, 16, 16);
std::shared_ptr<LocalVideoTrack> track;
ASSERT_NO_THROW(
track = lockLocalParticipant(room)->publishVideoTrack("republish-sid-check", source, TrackSource::SOURCE_CAMERA));
ASSERT_NE(track, nullptr);
ASSERT_NE(track->publication(), nullptr);
expectTrackSidAssigned(*track, *track->publication());

const std::string previous_sid = track->sid();
ASSERT_NO_THROW(RoomTestAccess::simulateScenario(room, proto::SIMULATE_FULL_RECONNECT));
ASSERT_TRUE(waitForSidChange(*track, previous_sid, 30s)) << "Timed out waiting for republished track SID";

ASSERT_NE(track->publication(), nullptr);
expectTrackSidAssigned(*track, *track->publication());
EXPECT_NE(track->sid(), previous_sid);

const auto pubs = lockLocalParticipant(room)->trackPublications();
EXPECT_EQ(pubs.count(previous_sid), 0u);
EXPECT_EQ(pubs.count(track->sid()), 1u);

lockLocalParticipant(room)->unpublishTrack(track->sid());
}

} // namespace livekit::test
12 changes: 1 addition & 11 deletions src/tests/integration/test_room.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,21 +29,11 @@
#include <thread>
#include <vector>

#include "../common/room_test_access.h"
#include "../common/test_common.h"

using namespace std::chrono_literals;

namespace livekit {

struct RoomTestAccess {
static int listenerId(const Room& room) {
const std::scoped_lock<std::mutex> guard(room.lock_);
return room.listener_id_;
}
};

} // namespace livekit

namespace livekit::test {

// Server-dependent tests - require LIVEKIT_URL and LIVEKIT_TOKEN_A env vars
Expand Down
Loading