diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..7707fc51 --- /dev/null +++ b/.clang-format @@ -0,0 +1,10 @@ +--- +BasedOnStyle: Google +Language: Cpp +Standard: c++17 + +# LiveKit modifications to Google style below + +ColumnLimit: 120 # more width on modern screens +SpacesBeforeTrailingComments: 1 # one space for trailing namespace comments, e.g. `} // namespace foo` (Google uses 2) +AccessModifierOffset: -2 # left-align public/protected/private with the class keyword (Google indents by 1) diff --git a/.github/workflows/builds.yml b/.github/workflows/builds.yml index 77fc58c5..c3f7c79b 100644 --- a/.github/workflows/builds.yml +++ b/.github/workflows/builds.yml @@ -17,6 +17,8 @@ on: - docker/Dockerfile.base - docker/Dockerfile.sdk - .github/workflows/** + - .clang-format + - scripts/clang-format.sh pull_request: branches: ["main"] paths: @@ -33,6 +35,8 @@ on: - docker/Dockerfile.base - docker/Dockerfile.sdk - .github/workflows/** + - .clang-format + - scripts/clang-format.sh workflow_dispatch: permissions: @@ -513,6 +517,45 @@ jobs: cmake --build build --parallel ' + clang-format: + name: clang-format + runs-on: ubuntu-latest + continue-on-error: false + permissions: + contents: read + + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + # No submodules: scripts/clang-format.sh only walks our own src/ tree + fetch-depth: 1 + + - name: Install clang-format 19 + run: | + set -eux + # Pin clang-format 19 to match the clang-tidy job and keep CI + # output deterministic across runs. Ubuntu 24.04's default + # clang-format ships with LLVM 18; pull 19 from the upstream + # LLVM apt repository so a developer running clang-format-19 + # locally gets the same result CI does. + sudo install -m 0755 -d /etc/apt/keyrings + wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key \ + | sudo tee /etc/apt/keyrings/llvm.asc >/dev/null + sudo chmod a+r /etc/apt/keyrings/llvm.asc + codename=$(lsb_release -cs) + echo "deb [signed-by=/etc/apt/keyrings/llvm.asc] http://apt.llvm.org/${codename}/ llvm-toolchain-${codename}-19 main" \ + | sudo tee /etc/apt/sources.list.d/llvm-19.list >/dev/null + sudo apt-get update + sudo apt-get install -y clang-format-19 + sudo ln -sf /usr/bin/clang-format-19 /usr/local/bin/clang-format + clang-format --version + + - name: Run clang-format + env: + FORMAT_BLOB_SHA: ${{ github.event.pull_request.head.sha || github.sha }} + run: ./scripts/clang-format.sh + clang-tidy: name: clang-tidy runs-on: ubuntu-latest diff --git a/AGENTS.md b/AGENTS.md index 4a7051f4..0fd31fbd 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -169,8 +169,10 @@ All source files must have the LiveKit Apache 2.0 copyright header. Use the curr ### Readability and Performance Code should be easy to read and understand. If a sacrifice is made for performance or readability, it should be documented. +Adhere to clang-format checks configured in `.clang-format`. Run `./scripts/clang-format.sh` if needed to confirm code styling. + ### Static Analysis -Adhere to clang-tidy checks configured in `.clang-tidy`. +Adhere to clang-tidy checks configured in `.clang-tidy`. Run `./scripts/clang-tidy.sh` if needed to confirm code quality. ## Dependencies diff --git a/README.md b/README.md index 8165d61a..357253e4 100644 --- a/README.md +++ b/README.md @@ -521,7 +521,7 @@ This SDK leverages various tools and checks to ensure the highest quality of the ### Clang Tools - `clang-tidy`: static analysis checks to catch common C++ pitfalls. See [.clang-tidy](./.clang-tidy) for the list of current checks (enforced in CI on PR) -- `clang-format`: (coming soon) code formatting and style consistency +- `clang-format`: code formatting and style consistency. See [.clang-format](./.clang-format) for the list of style configurations (enforced in CI on PR) > **Note**: clang-tidy is not currently supported on Windows for this project because the Visual Studio CMake generator does not produce the compile_commands.json database that clang-tidy requires. @@ -542,7 +542,13 @@ This installs `clang-format`, `clang-tidy`, and `run-clang-tidy`. Homebrew may a sudo apt-get install clang-format clang-tidy clang-tools ``` -To run: +**Pre-commit hook** + +```bash +printf '#!/bin/sh\nFILES=$(git diff --cached --name-only --diff-filter=ACMR -- "*.c" "*.cc" "*.cpp" "*.cxx" "*.h" "*.hpp" "*.hxx")\n[ -z "$FILES" ] && exit 0\necho "$FILES" | xargs ./scripts/clang-format.sh --fix\necho "$FILES" | xargs git add\n' > .git/hooks/pre-commit && chmod +x .git/hooks/pre-commit +``` + +To run `clang-tidy`: 1. Generate `compile_commands.json` and the protobuf headers via a release build: @@ -566,6 +572,24 @@ The wrapper forwards extra arguments to `run-clang-tidy`, examples below: Output is captured to `clang-tidy.log` at the repo root. This is done as a convenience feature, as often times the terminal buffer is not large enough for all the output. +To run `clang-format`: + +```bash +./scripts/clang-format.sh +``` + +With no arguments, runs `clang-format` against every relevant file in the repository against defined `.clang-format` rules. + +Pass flags or paths as needed, examples below: + +```bash +./scripts/clang-format.sh --fix # Rewrite files in place +./scripts/clang-format.sh src/room.cpp include/livekit/room.h # Check just these files +./scripts/clang-format.sh --fix src/room.cpp # Fix just this file +``` + +Output is captured to `clang-format.log` at the repo root. + ### Memory Checks Run valgrind on various examples or tests to check for memory leaks and other issues. diff --git a/benchmarks/data_track_throughput/common.h b/benchmarks/data_track_throughput/common.h index e0f43e2d..0c775b5b 100644 --- a/benchmarks/data_track_throughput/common.h +++ b/benchmarks/data_track_throughput/common.h @@ -35,9 +35,9 @@ namespace data_track_throughput { using json = nlohmann::json; -constexpr const char *kDefaultTrackName = "data-track-throughput"; -constexpr const char *kPrepareRpcMethod = "throughput.prepare"; -constexpr const char *kFinishRpcMethod = "throughput.finish"; +constexpr const char* kDefaultTrackName = "data-track-throughput"; +constexpr const char* kPrepareRpcMethod = "throughput.prepare"; +constexpr const char* kFinishRpcMethod = "throughput.finish"; constexpr std::size_t kMinimumPayloadBytes = 1024; constexpr std::size_t kMaximumPayloadBytes = 256ull * 1024ull * 1024ull; constexpr double kMinimumRateHz = 1.0; @@ -47,38 +47,31 @@ constexpr std::uint32_t kFrameMagic = 0x31545444u; // "DTT1" constexpr std::uint32_t kFrameVersion = 1; constexpr std::size_t kFrameHeaderBytes = 56; -inline std::string getenvOrEmpty(const char *name) { - const char *value = std::getenv(name); +inline std::string getenvOrEmpty(const char* name) { + const char* value = std::getenv(name); return value == nullptr ? std::string{} : std::string(value); } inline std::uint64_t nowSystemUs() { using namespace std::chrono; - return static_cast( - duration_cast(system_clock::now().time_since_epoch()) - .count()); + return static_cast(duration_cast(system_clock::now().time_since_epoch()).count()); } inline std::string toLower(std::string value) { - std::transform( - value.begin(), value.end(), value.begin(), - [](unsigned char c) { return static_cast(std::tolower(c)); }); + std::transform(value.begin(), value.end(), value.begin(), + [](unsigned char c) { return static_cast(std::tolower(c)); }); return value; } inline std::string trim(std::string value) { auto is_space = [](unsigned char c) { return std::isspace(c) != 0; }; - value.erase(value.begin(), - std::find_if(value.begin(), value.end(), - [&](unsigned char c) { return !is_space(c); })); - value.erase(std::find_if(value.rbegin(), value.rend(), - [&](unsigned char c) { return !is_space(c); }) - .base(), + value.erase(value.begin(), std::find_if(value.begin(), value.end(), [&](unsigned char c) { return !is_space(c); })); + value.erase(std::find_if(value.rbegin(), value.rend(), [&](unsigned char c) { return !is_space(c); }).base(), value.end()); return value; } -inline std::size_t parseByteSize(const std::string &text) { +inline std::size_t parseByteSize(const std::string& text) { const std::string lowered = toLower(trim(text)); if (lowered.empty()) { throw std::runtime_error("Empty byte size"); @@ -86,8 +79,7 @@ inline std::size_t parseByteSize(const std::string &text) { std::size_t split = 0; while (split < lowered.size() && - (std::isdigit(static_cast(lowered[split])) != 0 || - lowered[split] == '.')) { + (std::isdigit(static_cast(lowered[split])) != 0 || lowered[split] == '.')) { ++split; } @@ -111,7 +103,7 @@ inline std::size_t parseByteSize(const std::string &text) { } inline std::string humanBytes(std::size_t bytes) { - static constexpr const char *kUnits[] = {"B", "KiB", "MiB", "GiB"}; + static constexpr const char* kUnits[] = {"B", "KiB", "MiB", "GiB"}; double value = static_cast(bytes); std::size_t unit_index = 0; while (value >= 1024.0 && unit_index + 1 < std::size(kUnits)) { @@ -120,12 +112,11 @@ inline std::string humanBytes(std::size_t bytes) { } std::ostringstream oss; - oss << std::fixed << std::setprecision(unit_index == 0 ? 0 : 2) << value - << kUnits[unit_index]; + oss << std::fixed << std::setprecision(unit_index == 0 ? 0 : 2) << value << kUnits[unit_index]; return oss.str(); } -inline std::string csvEscape(const std::string &value) { +inline std::string csvEscape(const std::string& value) { if (value.find_first_of(",\"\n") == std::string::npos) { return value; } @@ -142,8 +133,7 @@ inline std::string csvEscape(const std::string &value) { return escaped; } -inline void ensureCsvHeader(const std::filesystem::path &path, - const std::string &header) { +inline void ensureCsvHeader(const std::filesystem::path& path, const std::string& header) { if (std::filesystem::exists(path)) { return; } @@ -186,24 +176,19 @@ struct FrameHeader { std::uint64_t send_time_us = 0; }; -inline void writeLe32(std::vector &buffer, std::size_t offset, - std::uint32_t value) { +inline void writeLe32(std::vector& buffer, std::size_t offset, std::uint32_t value) { for (int idx = 0; idx < 4; ++idx) { - buffer[offset + idx] = - static_cast((value >> (idx * 8)) & 0xFF); + buffer[offset + idx] = static_cast((value >> (idx * 8)) & 0xFF); } } -inline void writeLe64(std::vector &buffer, std::size_t offset, - std::uint64_t value) { +inline void writeLe64(std::vector& buffer, std::size_t offset, std::uint64_t value) { for (int idx = 0; idx < 8; ++idx) { - buffer[offset + idx] = - static_cast((value >> (idx * 8)) & 0xFF); + buffer[offset + idx] = static_cast((value >> (idx * 8)) & 0xFF); } } -inline std::uint32_t readLe32(const std::vector &buffer, - std::size_t offset) { +inline std::uint32_t readLe32(const std::vector& buffer, std::size_t offset) { std::uint32_t value = 0; for (int idx = 0; idx < 4; ++idx) { value |= static_cast(buffer[offset + idx]) << (idx * 8); @@ -211,8 +196,7 @@ inline std::uint32_t readLe32(const std::vector &buffer, return value; } -inline std::uint64_t readLe64(const std::vector &buffer, - std::size_t offset) { +inline std::uint64_t readLe64(const std::vector& buffer, std::size_t offset) { std::uint64_t value = 0; for (int idx = 0; idx < 8; ++idx) { value |= static_cast(buffer[offset + idx]) << (idx * 8); @@ -220,15 +204,13 @@ inline std::uint64_t readLe64(const std::vector &buffer, return value; } -inline std::vector makePayload(const ScenarioRequest &request, - std::uint64_t sequence, +inline std::vector makePayload(const ScenarioRequest& request, std::uint64_t sequence, std::uint64_t send_time_us) { if (request.packet_size_bytes < kFrameHeaderBytes) { throw std::runtime_error("Payload too small for frame header"); } - std::vector payload(request.packet_size_bytes, - static_cast(sequence & 0xFF)); + std::vector payload(request.packet_size_bytes, static_cast(sequence & 0xFF)); writeLe32(payload, 0, kFrameMagic); writeLe32(payload, 4, kFrameVersion); writeLe64(payload, 8, request.run_id); @@ -240,13 +222,11 @@ inline std::vector makePayload(const ScenarioRequest &request, return payload; } -inline std::optional -parseHeader(const std::vector &payload) { +inline std::optional parseHeader(const std::vector& payload) { if (payload.size() < kFrameHeaderBytes) { return std::nullopt; } - if (readLe32(payload, 0) != kFrameMagic || - readLe32(payload, 4) != kFrameVersion) { + if (readLe32(payload, 0) != kFrameMagic || readLe32(payload, 4) != kFrameVersion) { return std::nullopt; } @@ -260,8 +240,7 @@ parseHeader(const std::vector &payload) { return header; } -inline std::string defaultScenarioName(std::size_t packet_size_bytes, - double rate_hz) { +inline std::string defaultScenarioName(std::size_t packet_size_bytes, double rate_hz) { std::ostringstream oss; oss << humanBytes(packet_size_bytes); oss << "_" << std::fixed << std::setprecision(0) << rate_hz << "Hz"; @@ -275,11 +254,9 @@ inline std::vector defaultPayloadSizesBytes() { 128ull * 1024ull, 256ull * 1024ull, 512ull * 1024ull}; } -inline std::vector defaultRatesHz() { - return {1.0, 5.0, 10.0, 25.0, 50.0, 100.0, 200.0, 500.0, 1000.0}; -} +inline std::vector defaultRatesHz() { return {1.0, 5.0, 10.0, 25.0, 50.0, 100.0, 200.0, 500.0, 1000.0}; } -inline json toJson(const ScenarioRequest &request) { +inline json toJson(const ScenarioRequest& request) { return json{ {"run_id", request.run_id}, {"scenario_name", request.scenario_name}, @@ -291,7 +268,7 @@ inline json toJson(const ScenarioRequest &request) { }; } -inline ScenarioRequest scenarioRequestFromJson(const json &value) { +inline ScenarioRequest scenarioRequestFromJson(const json& value) { ScenarioRequest request; request.run_id = value.at("run_id").get(); request.scenario_name = value.at("scenario_name").get(); @@ -303,7 +280,7 @@ inline ScenarioRequest scenarioRequestFromJson(const json &value) { return request; } -inline json toJson(const ProducerStats &stats) { +inline json toJson(const ProducerStats& stats) { return json{ {"run_id", stats.run_id}, {"scenario_name", stats.scenario_name}, @@ -317,53 +294,43 @@ inline json toJson(const ProducerStats &stats) { }; } -inline ProducerStats producerStatsFromJson(const json &value) { +inline ProducerStats producerStatsFromJson(const json& value) { ProducerStats stats; stats.run_id = value.at("run_id").get(); stats.scenario_name = value.at("scenario_name").get(); stats.attempted_count = value.at("attempted_count").get(); - stats.enqueue_success_count = - value.at("enqueue_success_count").get(); - stats.enqueue_failure_count = - value.at("enqueue_failure_count").get(); + stats.enqueue_success_count = value.at("enqueue_success_count").get(); + stats.enqueue_failure_count = value.at("enqueue_failure_count").get(); stats.attempted_bytes = value.at("attempted_bytes").get(); stats.enqueued_bytes = value.at("enqueued_bytes").get(); - stats.first_send_time_us = - value.at("first_send_time_us").get(); + stats.first_send_time_us = value.at("first_send_time_us").get(); stats.last_send_time_us = value.at("last_send_time_us").get(); return stats; } -inline bool exceedsDataRateBudget(const ScenarioRequest &request) { - return static_cast(request.packet_size_bytes) * - request.desired_rate_hz > - kMaximumDataRateBytesPerSec; +inline bool exceedsDataRateBudget(const ScenarioRequest& request) { + return static_cast(request.packet_size_bytes) * request.desired_rate_hz > kMaximumDataRateBytesPerSec; } -inline void validateScenario(const ScenarioRequest &request) { +inline void validateScenario(const ScenarioRequest& request) { if (request.message_count == 0) { throw std::runtime_error("message_count must be greater than zero"); } - if (request.desired_rate_hz < kMinimumRateHz || - request.desired_rate_hz > kMaximumRateHz) { + if (request.desired_rate_hz < kMinimumRateHz || request.desired_rate_hz > kMaximumRateHz) { throw std::runtime_error("desired_rate_hz must be between 1 and 50000"); } - if (request.packet_size_bytes < kMinimumPayloadBytes || - request.packet_size_bytes > kMaximumPayloadBytes) { - throw std::runtime_error( - "packet_size_bytes must be between 1 KiB and 256 MiB"); + if (request.packet_size_bytes < kMinimumPayloadBytes || request.packet_size_bytes > kMaximumPayloadBytes) { + throw std::runtime_error("packet_size_bytes must be between 1 KiB and 256 MiB"); } if (exceedsDataRateBudget(request)) { throw std::runtime_error("scenario exceeds data-rate budget of 10 Gbps"); } } -inline std::vector -makeScenarioPlan(const std::string &producer_identity, - const std::string &track_name, - std::size_t messages_per_scenario, - const std::vector &payload_sizes, - const std::vector &rates) { +inline std::vector makeScenarioPlan(const std::string& producer_identity, + const std::string& track_name, std::size_t messages_per_scenario, + const std::vector& payload_sizes, + const std::vector& rates) { std::vector scenarios; std::uint64_t run_id = 1; @@ -387,12 +354,11 @@ makeScenarioPlan(const std::string &producer_identity, return scenarios; } -inline std::vector -makeDefaultScenarioPlan(const std::string &producer_identity, - const std::string &track_name, - std::size_t messages_per_scenario) { - return makeScenarioPlan(producer_identity, track_name, messages_per_scenario, - defaultPayloadSizesBytes(), defaultRatesHz()); +inline std::vector makeDefaultScenarioPlan(const std::string& producer_identity, + const std::string& track_name, + std::size_t messages_per_scenario) { + return makeScenarioPlan(producer_identity, track_name, messages_per_scenario, defaultPayloadSizesBytes(), + defaultRatesHz()); } } // namespace data_track_throughput diff --git a/benchmarks/data_track_throughput/consumer.cpp b/benchmarks/data_track_throughput/consumer.cpp index 01fced70..a4a7683f 100644 --- a/benchmarks/data_track_throughput/consumer.cpp +++ b/benchmarks/data_track_throughput/consumer.cpp @@ -12,10 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "common.h" - -#include "livekit/livekit.h" - #include #include #include @@ -34,6 +30,9 @@ #include #include +#include "common.h" +#include "livekit/livekit.h" + using namespace livekit; using namespace data_track_throughput; using namespace std::chrono_literals; @@ -72,7 +71,7 @@ struct RunState { void handleSignal(int) { g_running.store(false); } -void printUsage(const char *prog) { +void printUsage(const char* prog) { std::cout << "Usage:\n" << " " << prog << " \n" << " " << prog << " --url --token \n\n" @@ -80,8 +79,7 @@ void printUsage(const char *prog) { << " --output CSV output directory. Default: " "data_track_throughput_results\n" << " --output-dir Alias for --output\n" - << " --track-name Data track name. Default: " - << kDefaultTrackName << "\n" + << " --track-name Data track name. Default: " << kDefaultTrackName << "\n" << " --quiet-period-ms No-new-message window before " "finalizing. Default: 2000\n" << " --max-finish-wait-ms Hard cap for run finalization. " @@ -90,8 +88,8 @@ void printUsage(const char *prog) { << " LIVEKIT_URL, LIVEKIT_TOKEN" << std::endl; } -bool parseArgs(int argc, char *argv[], ConsumerOptions &options) { - auto readFlagValue = [&](const std::string &flag, int &index) -> std::string { +bool parseArgs(int argc, char* argv[], ConsumerOptions& options) { + auto readFlagValue = [&](const std::string& flag, int& index) -> std::string { const std::string arg = argv[index]; const std::string prefix = flag + "="; if (arg == flag && index + 1 < argc) { @@ -102,10 +100,9 @@ bool parseArgs(int argc, char *argv[], ConsumerOptions &options) { } return {}; }; - auto matchingOutputFlag = [](const std::string &arg) -> const char * { - static constexpr const char *kOutputFlags[] = {"--output-dir", "--output", - "--ouput"}; - for (const char *flag : kOutputFlags) { + auto matchingOutputFlag = [](const std::string& arg) -> const char* { + static constexpr const char* kOutputFlags[] = {"--output-dir", "--output", "--ouput"}; + for (const char* flag : kOutputFlags) { if (arg.rfind(flag, 0) == 0) { return flag; } @@ -123,17 +120,14 @@ bool parseArgs(int argc, char *argv[], ConsumerOptions &options) { options.url = readFlagValue("--url", index); } else if (arg.rfind("--token", 0) == 0) { options.token = readFlagValue("--token", index); - } else if (const char *output_flag = matchingOutputFlag(arg); - output_flag != nullptr) { + } else if (const char* output_flag = matchingOutputFlag(arg); output_flag != nullptr) { options.output_dir = readFlagValue(output_flag, index); } else if (arg.rfind("--track-name", 0) == 0) { options.track_name = readFlagValue("--track-name", index); } else if (arg.rfind("--quiet-period-ms", 0) == 0) { - options.quiet_period = std::chrono::milliseconds( - std::stoll(readFlagValue("--quiet-period-ms", index))); + options.quiet_period = std::chrono::milliseconds(std::stoll(readFlagValue("--quiet-period-ms", index))); } else if (arg.rfind("--max-finish-wait-ms", 0) == 0) { - options.max_finish_wait = std::chrono::milliseconds( - std::stoll(readFlagValue("--max-finish-wait-ms", index))); + options.max_finish_wait = std::chrono::milliseconds(std::stoll(readFlagValue("--max-finish-wait-ms", index))); } } @@ -168,41 +162,38 @@ bool parseArgs(int argc, char *argv[], ConsumerOptions &options) { class ThroughputConsumer { public: - explicit ThroughputConsumer(const ConsumerOptions &options) + explicit ThroughputConsumer(const ConsumerOptions& options) : options_(options), summary_csv_path_(options.output_dir / "throughput_summary.csv"), message_csv_path_(options.output_dir / "throughput_messages.csv") { std::filesystem::create_directories(options_.output_dir); - ensureCsvHeader( - summary_csv_path_, - "run_id,scenario_name,desired_rate_hz,packet_size_bytes," - "messages_requested,messages_attempted,messages_enqueued," - "messages_enqueue_failed,messages_received,messages_missed," - "duplicate_messages,attempted_bytes,enqueued_bytes,received_bytes," - "first_send_time_us,last_send_time_us," - "first_arrival_time_us,last_arrival_time_us"); + ensureCsvHeader(summary_csv_path_, + "run_id,scenario_name,desired_rate_hz,packet_size_bytes," + "messages_requested,messages_attempted,messages_enqueued," + "messages_enqueue_failed,messages_received,messages_missed," + "duplicate_messages,attempted_bytes,enqueued_bytes,received_bytes," + "first_send_time_us,last_send_time_us," + "first_arrival_time_us,last_arrival_time_us"); ensureCsvHeader(message_csv_path_, "run_id,sequence,payload_bytes,send_time_us," "arrival_time_us,is_duplicate"); } ~ThroughputConsumer() { - if (room_ != nullptr && - callback_id_ != std::numeric_limits::max()) { + if (room_ != nullptr && callback_id_ != std::numeric_limits::max()) { room_->removeOnDataFrameCallback(callback_id_); } } - void setRoom(Room &room) { room_ = &room; } + void setRoom(Room& room) { room_ = &room; } - void ensureDataCallbackRegistered(const ScenarioRequest &request) { + void ensureDataCallbackRegistered(const ScenarioRequest& request) { if (room_ == nullptr) { throw std::runtime_error("Room not attached to throughput consumer"); } if (callback_id_ != std::numeric_limits::max() && - callback_producer_identity_ == request.producer_identity && - callback_track_name_ == request.track_name) { + callback_producer_identity_ == request.producer_identity && callback_track_name_ == request.track_name) { return; } @@ -213,8 +204,7 @@ class ThroughputConsumer { callback_id_ = room_->addOnDataFrameCallback( request.producer_identity, request.track_name, - [this](const std::vector &payload, - std::optional user_timestamp) { + [this](const std::vector& payload, std::optional user_timestamp) { this->onDataFrame(payload, user_timestamp); }); if (callback_id_ == std::numeric_limits::max()) { @@ -224,14 +214,12 @@ class ThroughputConsumer { callback_producer_identity_ = request.producer_identity; callback_track_name_ = request.track_name; - std::cout << "Listening for throughput data track '" << request.track_name - << "' from " << request.producer_identity << std::endl; + std::cout << "Listening for throughput data track '" << request.track_name << "' from " << request.producer_identity + << std::endl; } - std::optional - handlePrepareRpc(const RpcInvocationData &invocation) { - const ScenarioRequest request = - scenarioRequestFromJson(json::parse(invocation.payload)); + std::optional handlePrepareRpc(const RpcInvocationData& invocation) { + const ScenarioRequest request = scenarioRequestFromJson(json::parse(invocation.payload)); validateScenario(request); ensureDataCallbackRegistered(request); @@ -240,19 +228,13 @@ class ThroughputConsumer { state.request = request; runs_[request.run_id] = std::move(state); - std::cout << "Prepared " << request.scenario_name - << " rate=" << request.desired_rate_hz << "Hz" - << " packet_size=" << request.packet_size_bytes - << " messages=" << request.message_count << std::endl; + std::cout << "Prepared " << request.scenario_name << " rate=" << request.desired_rate_hz << "Hz" + << " packet_size=" << request.packet_size_bytes << " messages=" << request.message_count << std::endl; - return json{{"ready", true}, - {"scenario_name", request.scenario_name}, - {"track_name", options_.track_name}} - .dump(); + return json{{"ready", true}, {"scenario_name", request.scenario_name}, {"track_name", options_.track_name}}.dump(); } - std::optional - handleFinishRpc(const RpcInvocationData &invocation) { + std::optional handleFinishRpc(const RpcInvocationData& invocation) { const auto payload = json::parse(invocation.payload); const ProducerStats stats = producerStatsFromJson(payload.at("stats")); @@ -260,10 +242,7 @@ class ThroughputConsumer { std::lock_guard lock(mutex_); auto it = runs_.find(stats.run_id); if (it == runs_.end()) { - return json{{"ready", false}, - {"message", "Unknown run_id in finish RPC"}, - {"run_id", stats.run_id}} - .dump(); + return json{{"ready", false}, {"message", "Unknown run_id in finish RPC"}, {"run_id", stats.run_id}}.dump(); } it->second.producer_stats = stats; @@ -275,8 +254,7 @@ class ThroughputConsumer { } private: - void onDataFrame(const std::vector &payload, - std::optional user_timestamp) { + void onDataFrame(const std::vector& payload, std::optional user_timestamp) { const std::uint64_t arrival_time_us = nowSystemUs(); const auto header = parseHeader(payload); if (!header) { @@ -287,12 +265,11 @@ class ThroughputConsumer { std::lock_guard lock(mutex_); auto run_it = runs_.find(header->run_id); if (run_it == runs_.end()) { - std::cerr << "Ignored frame for unknown run_id=" << header->run_id - << std::endl; + std::cerr << "Ignored frame for unknown run_id=" << header->run_id << std::endl; return; } - RunState &run = run_it->second; + RunState& run = run_it->second; const bool duplicate = !run.seen_sequences.insert(header->sequence).second; if (duplicate) { @@ -300,24 +277,18 @@ class ThroughputConsumer { } if (payload.size() != run.request.packet_size_bytes) { - std::cerr << "WARN: payload size mismatch run_id=" << header->run_id - << " seq=" << header->sequence - << " expected=" << run.request.packet_size_bytes - << " got=" << payload.size() << std::endl; + std::cerr << "WARN: payload size mismatch run_id=" << header->run_id << " seq=" << header->sequence + << " expected=" << run.request.packet_size_bytes << " got=" << payload.size() << std::endl; } - if (header->payload_size_bytes != payload.size() || - header->message_count != run.request.message_count || + if (header->payload_size_bytes != payload.size() || header->message_count != run.request.message_count || header->packet_size_bytes != run.request.packet_size_bytes) { - std::cerr << "WARN: header field mismatch run_id=" << header->run_id - << " seq=" << header->sequence << std::endl; + std::cerr << "WARN: header field mismatch run_id=" << header->run_id << " seq=" << header->sequence << std::endl; } if (user_timestamp.has_value() && *user_timestamp != header->send_time_us) { - std::cerr << "WARN: timestamp mismatch run_id=" << header->run_id - << " seq=" << header->sequence << std::endl; + std::cerr << "WARN: timestamp mismatch run_id=" << header->run_id << " seq=" << header->sequence << std::endl; } if (header->sequence >= run.request.message_count) { - std::cerr << "WARN: unexpected sequence run_id=" << header->run_id - << " seq=" << header->sequence + std::cerr << "WARN: unexpected sequence run_id=" << header->run_id << " seq=" << header->sequence << " max=" << run.request.message_count << std::endl; } @@ -345,27 +316,21 @@ class ThroughputConsumer { throw std::runtime_error("Run missing during finalization"); } - const auto deadline = - std::chrono::steady_clock::now() + options_.max_finish_wait; + const auto deadline = std::chrono::steady_clock::now() + options_.max_finish_wait; while (g_running.load()) { auto now = std::chrono::steady_clock::now(); - if (it->second.seen_sequences.size() >= - it->second.request.message_count) { + if (it->second.seen_sequences.size() >= it->second.request.message_count) { break; } if (now >= deadline) { break; } if (it->second.producer_finished) { - const std::uint64_t reference_time_us = - it->second.last_arrival_us != 0 - ? it->second.last_arrival_us - : it->second.producer_stats.last_send_time_us; + const std::uint64_t reference_time_us = it->second.last_arrival_us != 0 + ? it->second.last_arrival_us + : it->second.producer_stats.last_send_time_us; if (reference_time_us != 0 && - nowSystemUs() > - reference_time_us + static_cast( - options_.quiet_period.count()) * - 1000ull) { + nowSystemUs() > reference_time_us + static_cast(options_.quiet_period.count()) * 1000ull) { break; } } @@ -385,16 +350,15 @@ class ThroughputConsumer { appendMessageRows(completed); appendSummaryRow(summary); - std::cout << "Wrote scenario " << completed.request.scenario_name << " to " - << summary_csv_path_.string() << " and " + std::cout << "Wrote scenario " << completed.request.scenario_name << " to " << summary_csv_path_.string() << " and " << message_csv_path_.string() << std::endl; return summary.dump(); } - json buildSummaryJson(const RunState &run) const { + json buildSummaryJson(const RunState& run) const { std::size_t unique_count = 0; std::uint64_t received_bytes = 0; - for (const auto &obs : run.observations) { + for (const auto& obs : run.observations) { if (!obs.duplicate) { ++unique_count; received_bytes += obs.payload_bytes; @@ -402,9 +366,7 @@ class ThroughputConsumer { } const std::size_t messages_missed = - run.request.message_count > unique_count - ? run.request.message_count - unique_count - : 0; + run.request.message_count > unique_count ? run.request.message_count - unique_count : 0; json summary; summary["run_id"] = run.request.run_id; @@ -414,8 +376,7 @@ class ThroughputConsumer { summary["messages_requested"] = run.request.message_count; summary["messages_attempted"] = run.producer_stats.attempted_count; summary["messages_enqueued"] = run.producer_stats.enqueue_success_count; - summary["messages_enqueue_failed"] = - run.producer_stats.enqueue_failure_count; + summary["messages_enqueue_failed"] = run.producer_stats.enqueue_failure_count; summary["messages_received"] = unique_count; summary["messages_missed"] = messages_missed; summary["duplicate_messages"] = run.duplicate_count; @@ -429,37 +390,34 @@ class ThroughputConsumer { return summary; } - void appendSummaryRow(const json &summary) const { + void appendSummaryRow(const json& summary) const { std::ofstream out; out.open(summary_csv_path_, std::ios::app); if (!out) { throw std::runtime_error("Failed to open summary CSV for append"); } - out << summary.at("run_id").get() << ',' - << csvEscape(summary.at("scenario_name").get()) << ',' - << summary.at("desired_rate_hz").get() << ',' + out << summary.at("run_id").get() << ',' << csvEscape(summary.at("scenario_name").get()) + << ',' << summary.at("desired_rate_hz").get() << ',' << summary.at("packet_size_bytes").get() << ',' << summary.at("messages_requested").get() << ',' << summary.at("messages_attempted").get() << ',' << summary.at("messages_enqueued").get() << ',' << summary.at("messages_enqueue_failed").get() << ',' - << summary.at("messages_received").get() << ',' - << summary.at("messages_missed").get() << ',' - << summary.at("duplicate_messages").get() << ',' + << summary.at("messages_received").get() << ',' << summary.at("messages_missed").get() + << ',' << summary.at("duplicate_messages").get() << ',' << summary.at("attempted_bytes").get() << ',' - << summary.at("enqueued_bytes").get() << ',' - << summary.at("received_bytes").get() << ',' - << summary.at("first_send_time_us").get() << ',' + << summary.at("enqueued_bytes").get() << ',' << summary.at("received_bytes").get() + << ',' << summary.at("first_send_time_us").get() << ',' << summary.at("last_send_time_us").get() << ',' << summary.at("first_arrival_time_us").get() << ',' << summary.at("last_arrival_time_us").get() << '\n'; } - void appendMessageRows(const RunState &run) const { + void appendMessageRows(const RunState& run) const { std::vector observations = run.observations; std::sort(observations.begin(), observations.end(), - [](const MessageObservation &lhs, const MessageObservation &rhs) { + [](const MessageObservation& lhs, const MessageObservation& rhs) { return lhs.arrival_time_us < rhs.arrival_time_us; }); @@ -469,11 +427,9 @@ class ThroughputConsumer { throw std::runtime_error("Failed to open message CSV for append"); } - for (const auto &message : observations) { - out << run.request.run_id << ',' << message.sequence << ',' - << message.payload_bytes << ',' << message.send_time_us << ',' - << message.arrival_time_us << ',' << (message.duplicate ? 1 : 0) - << '\n'; + for (const auto& message : observations) { + out << run.request.run_id << ',' << message.sequence << ',' << message.payload_bytes << ',' + << message.send_time_us << ',' << message.arrival_time_us << ',' << (message.duplicate ? 1 : 0) << '\n'; } } @@ -482,9 +438,8 @@ class ThroughputConsumer { std::filesystem::path message_csv_path_; mutable std::mutex mutex_; std::condition_variable cv_; - Room *room_ = nullptr; - DataFrameCallbackId callback_id_ = - std::numeric_limits::max(); + Room* room_ = nullptr; + DataFrameCallbackId callback_id_ = std::numeric_limits::max(); std::string callback_producer_identity_; std::string callback_track_name_; std::unordered_map runs_; @@ -492,14 +447,14 @@ class ThroughputConsumer { } // namespace -int main(int argc, char *argv[]) { +int main(int argc, char* argv[]) { ConsumerOptions options; try { if (!parseArgs(argc, argv, options)) { printUsage(argv[0]); return 1; } - } catch (const std::exception &e) { + } catch (const std::exception& e) { std::cerr << "Argument error: " << e.what() << std::endl; printUsage(argv[0]); return 1; @@ -526,28 +481,24 @@ int main(int argc, char *argv[]) { throw std::runtime_error("Failed to connect to LiveKit room"); } - auto *local_participant = room.localParticipant(); + auto* local_participant = room.localParticipant(); if (local_participant == nullptr) { throw std::runtime_error("Local participant unavailable after connect"); } - local_participant->registerRpcMethod( - kPrepareRpcMethod, [&consumer](const RpcInvocationData &invocation) { - return consumer.handlePrepareRpc(invocation); - }); - local_participant->registerRpcMethod( - kFinishRpcMethod, [&consumer](const RpcInvocationData &invocation) { - return consumer.handleFinishRpc(invocation); - }); + local_participant->registerRpcMethod(kPrepareRpcMethod, [&consumer](const RpcInvocationData& invocation) { + return consumer.handlePrepareRpc(invocation); + }); + local_participant->registerRpcMethod(kFinishRpcMethod, [&consumer](const RpcInvocationData& invocation) { + return consumer.handleFinishRpc(invocation); + }); - std::cout << "Ready. CSV output directory: " - << std::filesystem::absolute(options.output_dir).string() - << std::endl; + std::cout << "Ready. CSV output directory: " << std::filesystem::absolute(options.output_dir).string() << std::endl; while (g_running.load()) { std::this_thread::sleep_for(250ms); } - } catch (const std::exception &e) { + } catch (const std::exception& e) { std::cerr << e.what() << std::endl; livekit::shutdown(); return 1; diff --git a/benchmarks/data_track_throughput/producer.cpp b/benchmarks/data_track_throughput/producer.cpp index cd966079..e326fbad 100644 --- a/benchmarks/data_track_throughput/producer.cpp +++ b/benchmarks/data_track_throughput/producer.cpp @@ -12,10 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "common.h" - -#include "livekit/livekit.h" - #include #include #include @@ -27,6 +23,9 @@ #include #include +#include "common.h" +#include "livekit/livekit.h" + using namespace livekit; using namespace data_track_throughput; @@ -49,37 +48,33 @@ struct ProducerOptions { void handleSignal(int) { g_running.store(false); } -void printUsage(const char *prog) { - std::cout - << "Usage:\n" - << " " << prog << " [--consumer ]\n" - << " " << prog - << " --url --token [--consumer ]\n\n" - << "Optional flags:\n" - << " --track-name Data track name. Default: " - << kDefaultTrackName << "\n" - << " --messages-per-scenario Default sweep message count. " - "Default: 30\n" - << " --sizes_kb Comma-separated sweep sizes in " - "KiB. Default: 1,4,16,64,128,256,512\n" - << " --freq_hz Comma-separated sweep rates in " - "Hz. Default: 1,5,10,25,50,100,200,500,1000\n" - << " --rate-hz Run a single scenario instead of " - "the full sweep\n" - << " --packet-size Single-scenario packet size, e.g. " - "1mb\n" - << " --num-msgs Single-scenario message count\n\n" - << "Env fallbacks:\n" - << " LIVEKIT_URL, LIVEKIT_TOKEN" << std::endl; +void printUsage(const char* prog) { + std::cout << "Usage:\n" + << " " << prog << " [--consumer ]\n" + << " " << prog << " --url --token [--consumer ]\n\n" + << "Optional flags:\n" + << " --track-name Data track name. Default: " << kDefaultTrackName << "\n" + << " --messages-per-scenario Default sweep message count. " + "Default: 30\n" + << " --sizes_kb Comma-separated sweep sizes in " + "KiB. Default: 1,4,16,64,128,256,512\n" + << " --freq_hz Comma-separated sweep rates in " + "Hz. Default: 1,5,10,25,50,100,200,500,1000\n" + << " --rate-hz Run a single scenario instead of " + "the full sweep\n" + << " --packet-size Single-scenario packet size, e.g. " + "1mb\n" + << " --num-msgs Single-scenario message count\n\n" + << "Env fallbacks:\n" + << " LIVEKIT_URL, LIVEKIT_TOKEN" << std::endl; } -std::vector splitCommaSeparated(const std::string &text) { +std::vector splitCommaSeparated(const std::string& text) { std::vector values; std::size_t start = 0; while (start < text.size()) { const std::size_t end = text.find(',', start); - const std::size_t count = - end == std::string::npos ? std::string::npos : end - start; + const std::size_t count = end == std::string::npos ? std::string::npos : end - start; values.push_back(trim(text.substr(start, count))); if (end == std::string::npos) { break; @@ -89,9 +84,9 @@ std::vector splitCommaSeparated(const std::string &text) { return values; } -std::vector parseRateListHz(const std::string &text) { +std::vector parseRateListHz(const std::string& text) { std::vector rates; - for (const auto &value : splitCommaSeparated(text)) { + for (const auto& value : splitCommaSeparated(text)) { if (value.empty()) { throw std::runtime_error("--freq_hz contains an empty value"); } @@ -103,9 +98,9 @@ std::vector parseRateListHz(const std::string &text) { return rates; } -std::vector parseSizeListKb(const std::string &text) { +std::vector parseSizeListKb(const std::string& text) { std::vector sizes; - for (const auto &value : splitCommaSeparated(text)) { + for (const auto& value : splitCommaSeparated(text)) { if (value.empty()) { throw std::runtime_error("--sizes_kb contains an empty value"); } @@ -117,8 +112,8 @@ std::vector parseSizeListKb(const std::string &text) { return sizes; } -bool parseArgs(int argc, char *argv[], ProducerOptions &options) { - auto readFlagValue = [&](const std::string &flag, int &index) -> std::string { +bool parseArgs(int argc, char* argv[], ProducerOptions& options) { + auto readFlagValue = [&](const std::string& flag, int& index) -> std::string { const std::string arg = argv[index]; const std::string prefix = flag + "="; if (arg == flag && index + 1 < argc) { @@ -145,21 +140,18 @@ bool parseArgs(int argc, char *argv[], ProducerOptions &options) { } else if (arg.rfind("--track-name", 0) == 0) { options.track_name = readFlagValue("--track-name", index); } else if (arg.rfind("--messages-per-scenario", 0) == 0) { - options.messages_per_scenario = static_cast( - std::stoull(readFlagValue("--messages-per-scenario", index))); + options.messages_per_scenario = + static_cast(std::stoull(readFlagValue("--messages-per-scenario", index))); } else if (arg.rfind("--sizes_kb", 0) == 0) { - options.payload_sizes_bytes = - parseSizeListKb(readFlagValue("--sizes_kb", index)); + options.payload_sizes_bytes = parseSizeListKb(readFlagValue("--sizes_kb", index)); } else if (arg.rfind("--freq_hz", 0) == 0) { options.rates_hz = parseRateListHz(readFlagValue("--freq_hz", index)); } else if (arg.rfind("--rate-hz", 0) == 0) { options.single_rate_hz = std::stod(readFlagValue("--rate-hz", index)); } else if (arg.rfind("--packet-size", 0) == 0) { - options.single_packet_size_bytes = - parseByteSize(readFlagValue("--packet-size", index)); + options.single_packet_size_bytes = parseByteSize(readFlagValue("--packet-size", index)); } else if (arg.rfind("--num-msgs", 0) == 0) { - options.single_message_count = static_cast( - std::stoull(readFlagValue("--num-msgs", index))); + options.single_message_count = static_cast(std::stoull(readFlagValue("--num-msgs", index))); } } @@ -189,25 +181,22 @@ bool parseArgs(int argc, char *argv[], ProducerOptions &options) { options.token = getenvOrEmpty("LIVEKIT_TOKEN"); } - const bool single_mode_requested = - options.single_rate_hz.has_value() || - options.single_packet_size_bytes.has_value() || - options.single_message_count.has_value(); - const bool single_mode_complete = - options.single_rate_hz.has_value() && - options.single_packet_size_bytes.has_value() && - options.single_message_count.has_value(); + const bool single_mode_requested = options.single_rate_hz.has_value() || + options.single_packet_size_bytes.has_value() || + options.single_message_count.has_value(); + const bool single_mode_complete = options.single_rate_hz.has_value() && + options.single_packet_size_bytes.has_value() && + options.single_message_count.has_value(); if (single_mode_requested && !single_mode_complete) { - throw std::runtime_error("Single-scenario mode requires --rate-hz, " - "--packet-size, and --num-msgs"); + throw std::runtime_error( + "Single-scenario mode requires --rate-hz, " + "--packet-size, and --num-msgs"); } return !(options.url.empty() || options.token.empty()); } -std::string waitForConsumerIdentity(Room &room, - const std::string &requested_identity, - std::chrono::seconds timeout) { +std::string waitForConsumerIdentity(Room& room, const std::string& requested_identity, std::chrono::seconds timeout) { const auto deadline = std::chrono::steady_clock::now() + timeout; while (g_running.load() && std::chrono::steady_clock::now() < deadline) { if (!requested_identity.empty()) { @@ -224,20 +213,16 @@ std::string waitForConsumerIdentity(Room &room, std::this_thread::sleep_for(std::chrono::milliseconds(100)); } - throw std::runtime_error( - requested_identity.empty() - ? "Timed out waiting for exactly one remote consumer participant" - : "Timed out waiting for consumer identity: " + requested_identity); + throw std::runtime_error(requested_identity.empty() + ? "Timed out waiting for exactly one remote consumer participant" + : "Timed out waiting for consumer identity: " + requested_identity); } -std::vector -buildScenarioPlan(const ProducerOptions &options, - const std::string &producer_identity) { +std::vector buildScenarioPlan(const ProducerOptions& options, const std::string& producer_identity) { if (options.single_rate_hz.has_value()) { ScenarioRequest request; request.run_id = 1; - request.scenario_name = defaultScenarioName( - *options.single_packet_size_bytes, *options.single_rate_hz); + request.scenario_name = defaultScenarioName(*options.single_packet_size_bytes, *options.single_rate_hz); request.producer_identity = producer_identity; request.track_name = options.track_name; request.desired_rate_hz = *options.single_rate_hz; @@ -247,27 +232,23 @@ buildScenarioPlan(const ProducerOptions &options, return {request}; } - auto scenarios = makeScenarioPlan( - producer_identity, options.track_name, options.messages_per_scenario, - options.payload_sizes_bytes, options.rates_hz); - for (const auto &scenario : scenarios) { + auto scenarios = makeScenarioPlan(producer_identity, options.track_name, options.messages_per_scenario, + options.payload_sizes_bytes, options.rates_hz); + for (const auto& scenario : scenarios) { validateScenario(scenario); } return scenarios; } -ProducerStats runScenario(LocalDataTrack &track, - const ScenarioRequest &request) { +ProducerStats runScenario(LocalDataTrack& track, const ScenarioRequest& request) { ProducerStats stats; stats.run_id = request.run_id; stats.scenario_name = request.scenario_name; - const auto frame_interval = - std::chrono::duration(1.0 / request.desired_rate_hz); + const auto frame_interval = std::chrono::duration(1.0 / request.desired_rate_hz); auto next_send = std::chrono::steady_clock::now(); - for (std::size_t index = 0; index < request.message_count && g_running.load(); - ++index) { + for (std::size_t index = 0; index < request.message_count && g_running.load(); ++index) { const std::uint64_t send_time_us = nowSystemUs(); if (stats.first_send_time_us == 0) { @@ -277,47 +258,41 @@ ProducerStats runScenario(LocalDataTrack &track, ++stats.attempted_count; stats.attempted_bytes += request.packet_size_bytes; - auto payload = - makePayload(request, static_cast(index), send_time_us); + auto payload = makePayload(request, static_cast(index), send_time_us); auto push_result = track.tryPush(std::move(payload), send_time_us); if (push_result) { ++stats.enqueue_success_count; stats.enqueued_bytes += request.packet_size_bytes; } else { ++stats.enqueue_failure_count; - std::cerr << "tryPush failed for scenario " << request.scenario_name - << " seq=" << index << " reason=" << push_result.error().message - << std::endl; + std::cerr << "tryPush failed for scenario " << request.scenario_name << " seq=" << index + << " reason=" << push_result.error().message << std::endl; } - next_send += - std::chrono::duration_cast( - frame_interval); + next_send += std::chrono::duration_cast(frame_interval); std::this_thread::sleep_until(next_send); } return stats; } -void printScenarioSummary(const json &summary) { +void printScenarioSummary(const json& summary) { std::cout << "Summary " << summary.value("scenario_name", "") - << ": received=" << summary.value("messages_received", 0) << "/" - << summary.value("messages_requested", 0) + << ": received=" << summary.value("messages_received", 0) << "/" << summary.value("messages_requested", 0) << " missed=" << summary.value("messages_missed", 0) - << " received_bytes=" << summary.value("received_bytes", 0) - << std::endl; + << " received_bytes=" << summary.value("received_bytes", 0) << std::endl; } } // namespace -int main(int argc, char *argv[]) { +int main(int argc, char* argv[]) { ProducerOptions options; try { if (!parseArgs(argc, argv, options)) { printUsage(argv[0]); return 1; } - } catch (const std::exception &e) { + } catch (const std::exception& e) { std::cerr << "Argument error: " << e.what() << std::endl; printUsage(argv[0]); return 1; @@ -341,58 +316,53 @@ int main(int argc, char *argv[]) { throw std::runtime_error("Failed to connect to LiveKit room"); } - auto *local_participant = room.localParticipant(); + auto* local_participant = room.localParticipant(); if (local_participant == nullptr) { throw std::runtime_error("Local participant unavailable after connect"); } - const auto publish_result = - local_participant->publishDataTrack(options.track_name); + const auto publish_result = local_participant->publishDataTrack(options.track_name); if (!publish_result) { - throw std::runtime_error("Failed to publish data track: " + - publish_result.error().message); + throw std::runtime_error("Failed to publish data track: " + publish_result.error().message); } auto track = publish_result.value(); - const std::string consumer_identity = waitForConsumerIdentity( - room, options.consumer_identity, std::chrono::seconds(30)); - const auto scenarios = - buildScenarioPlan(options, local_participant->identity()); + const std::string consumer_identity = + waitForConsumerIdentity(room, options.consumer_identity, std::chrono::seconds(30)); + const auto scenarios = buildScenarioPlan(options, local_participant->identity()); std::cout << "Target consumer: " << consumer_identity << std::endl; std::cout << "Running " << scenarios.size() << " scenario(s)" << std::endl; - for (const auto &scenario : scenarios) { + for (const auto& scenario : scenarios) { if (!g_running.load()) { break; } - std::cout << "Preparing " << scenario.scenario_name - << " rate=" << scenario.desired_rate_hz << "Hz" - << " packet_size=" << humanBytes(scenario.packet_size_bytes) - << " messages=" << scenario.message_count << std::endl; + std::cout << "Preparing " << scenario.scenario_name << " rate=" << scenario.desired_rate_hz << "Hz" + << " packet_size=" << humanBytes(scenario.packet_size_bytes) << " messages=" << scenario.message_count + << std::endl; const auto prepare_payload = toJson(scenario).dump(); - const std::string prepare_response = local_participant->performRpc( - consumer_identity, kPrepareRpcMethod, prepare_payload, 30.0); + const std::string prepare_response = + local_participant->performRpc(consumer_identity, kPrepareRpcMethod, prepare_payload, 30.0); const json prepare_json = json::parse(prepare_response); if (!prepare_json.value("ready", false)) { - throw std::runtime_error( - "Consumer rejected scenario " + scenario.scenario_name + ": " + - prepare_json.value("message", "unknown error")); + throw std::runtime_error("Consumer rejected scenario " + scenario.scenario_name + ": " + + prepare_json.value("message", "unknown error")); } const ProducerStats stats = runScenario(*track, scenario); json finish_payload; finish_payload["stats"] = toJson(stats); - const std::string finish_response = local_participant->performRpc( - consumer_identity, kFinishRpcMethod, finish_payload.dump(), 60.0); + const std::string finish_response = + local_participant->performRpc(consumer_identity, kFinishRpcMethod, finish_payload.dump(), 60.0); const json summary = json::parse(finish_response); printScenarioSummary(summary); } track->unpublishDataTrack(); - } catch (const std::exception &e) { + } catch (const std::exception& e) { std::cerr << e.what() << std::endl; livekit::shutdown(); return 1; diff --git a/include/livekit/audio_frame.h b/include/livekit/audio_frame.h index 2db2b9be..139804df 100644 --- a/include/livekit/audio_frame.h +++ b/include/livekit/audio_frame.h @@ -47,26 +47,24 @@ class AudioFrame { * Throws std::invalid_argument if the data size is inconsistent with * num_channels * samples_per_channel. */ - AudioFrame(std::vector data, int sample_rate, int num_channels, - int samples_per_channel); + AudioFrame(std::vector data, int sample_rate, int num_channels, int samples_per_channel); AudioFrame(); // Default constructor virtual ~AudioFrame() = default; /** * Create a new zero-initialized AudioFrame instance. */ - static AudioFrame create(int sample_rate, int num_channels, - int samples_per_channel); + static AudioFrame create(int sample_rate, int num_channels, int samples_per_channel); /** * Construct an AudioFrame by copying data out of an OwnedAudioFrameBuffer. */ - static AudioFrame fromOwnedInfo(const proto::OwnedAudioFrameBuffer &owned); + static AudioFrame fromOwnedInfo(const proto::OwnedAudioFrameBuffer& owned); // ---- Accessors ---- - const std::vector &data() const noexcept { return data_; } - std::vector &data() noexcept { return data_; } + const std::vector& data() const noexcept { return data_; } + std::vector& data() noexcept { return data_; } /// Number of samples in the buffer (per all channels). std::size_t total_samples() const noexcept { return data_.size(); } diff --git a/include/livekit/audio_processing_module.h b/include/livekit/audio_processing_module.h index 29eab332..5f5f204c 100644 --- a/include/livekit/audio_processing_module.h +++ b/include/livekit/audio_processing_module.h @@ -81,17 +81,17 @@ class AudioProcessingModule { * @param options Configuration for which processing features to enable. * @throws std::runtime_error if the APM could not be created. */ - explicit AudioProcessingModule(const Options &options); + explicit AudioProcessingModule(const Options& options); virtual ~AudioProcessingModule() = default; // Non-copyable - AudioProcessingModule(const AudioProcessingModule &) = delete; - AudioProcessingModule &operator=(const AudioProcessingModule &) = delete; + AudioProcessingModule(const AudioProcessingModule&) = delete; + AudioProcessingModule& operator=(const AudioProcessingModule&) = delete; // Movable - AudioProcessingModule(AudioProcessingModule &&) noexcept = default; - AudioProcessingModule &operator=(AudioProcessingModule &&) noexcept = default; + AudioProcessingModule(AudioProcessingModule&&) noexcept = default; + AudioProcessingModule& operator=(AudioProcessingModule&&) noexcept = default; /** * @brief Process the forward (near-end/microphone) audio stream. @@ -109,7 +109,7 @@ class AudioProcessingModule { * * @note The frame must contain exactly 10ms of audio. */ - void processStream(AudioFrame &frame); + void processStream(AudioFrame& frame); /** * @brief Process the reverse (far-end/speaker) audio stream. @@ -127,7 +127,7 @@ class AudioProcessingModule { * * @note The frame must contain exactly 10ms of audio. */ - void processReverseStream(AudioFrame &frame); + void processReverseStream(AudioFrame& frame); /** * @brief Set the estimated delay between the reverse and forward streams. @@ -159,9 +159,7 @@ class AudioProcessingModule { bool valid() const noexcept { return handle_.valid(); } /// Get the underlying FFI handle ID (used internally). - std::uint64_t ffi_handle_id() const noexcept { - return static_cast(handle_.get()); - } + std::uint64_t ffi_handle_id() const noexcept { return static_cast(handle_.get()); } FfiHandle handle_; }; diff --git a/include/livekit/audio_source.h b/include/livekit/audio_source.h index 4fbc58cc..e5d1d5e0 100644 --- a/include/livekit/audio_source.h +++ b/include/livekit/audio_source.h @@ -71,10 +71,10 @@ class AudioSource { AudioSource(int sample_rate, int num_channels, int queue_size_ms = 0); virtual ~AudioSource() = default; - AudioSource(const AudioSource &) = delete; - AudioSource &operator=(const AudioSource &) = delete; - AudioSource(AudioSource &&) noexcept = default; - AudioSource &operator=(AudioSource &&) noexcept = default; + AudioSource(const AudioSource&) = delete; + AudioSource& operator=(const AudioSource&) = delete; + AudioSource(AudioSource&&) noexcept = default; + AudioSource& operator=(AudioSource&&) noexcept = default; /// The sample rate of the audio source in Hz. int sample_rate() const noexcept { return sample_rate_; } @@ -83,9 +83,7 @@ class AudioSource { int num_channels() const noexcept { return num_channels_; } /// Underlying FFI handle ID used in FFI requests. - std::uint64_t ffi_handle_id() const noexcept { - return static_cast(handle_.get()); - } + std::uint64_t ffi_handle_id() const noexcept { return static_cast(handle_.get()); } /// Current duration of queued audio (in seconds). double queuedDuration() const noexcept; @@ -139,7 +137,7 @@ class AudioSource { * - the FFI reports an error * - a timeout occurs in bounded-wait mode */ - void captureFrame(const AudioFrame &frame, int timeout_ms = 20); + void captureFrame(const AudioFrame& frame, int timeout_ms = 20); private: // Internal helper to reset the local queue tracking (like _release_waiter). diff --git a/include/livekit/audio_stream.h b/include/livekit/audio_stream.h index d6dce270..c7dba834 100644 --- a/include/livekit/audio_stream.h +++ b/include/livekit/audio_stream.h @@ -87,21 +87,19 @@ class AudioStream { }; /// Factory: create an AudioStream bound to a specific Track - static std::shared_ptr - fromTrack(const std::shared_ptr &track, const Options &options); + static std::shared_ptr fromTrack(const std::shared_ptr& track, const Options& options); /// Factory: create an AudioStream from a Participant + TrackSource - static std::shared_ptr fromParticipant(Participant &participant, - TrackSource track_source, - const Options &options); + static std::shared_ptr fromParticipant(Participant& participant, TrackSource track_source, + const Options& options); virtual ~AudioStream(); /// No copy, assignment constructors. - AudioStream(const AudioStream &) = delete; - AudioStream &operator=(const AudioStream &) = delete; - AudioStream(AudioStream &&) noexcept; - AudioStream &operator=(AudioStream &&) noexcept; + AudioStream(const AudioStream&) = delete; + AudioStream& operator=(const AudioStream&) = delete; + AudioStream(AudioStream&&) noexcept; + AudioStream& operator=(AudioStream&&) noexcept; /// Blocking read: waits until there is an AudioFrameEvent available in the /// internal queue, or the stream reaches EOS / is closed. @@ -109,7 +107,7 @@ class AudioStream { /// \param out_event On success, filled with the next audio frame. /// \return true if a frame was delivered; false if the stream ended /// (end-of-stream or close()) and no more data is available. - bool read(AudioFrameEvent &out_event); + bool read(AudioFrameEvent& out_event); /// Signal that we are no longer interested in audio frames. /// @@ -121,16 +119,14 @@ class AudioStream { private: AudioStream() = default; - void initFromTrack(const std::shared_ptr &track, - const Options &options); - void initFromParticipant(Participant &participant, TrackSource track_source, - const Options &options); + void initFromTrack(const std::shared_ptr& track, const Options& options); + void initFromParticipant(Participant& participant, TrackSource track_source, const Options& options); // FFI event handler (registered with FfiClient) - void onFfiEvent(const proto::FfiEvent &event); + void onFfiEvent(const proto::FfiEvent& event); // Queue helpers - void pushFrame(AudioFrameEvent &&ev); + void pushFrame(AudioFrameEvent&& ev); void pushEos(); mutable std::mutex mutex_; diff --git a/include/livekit/data_stream.h b/include/livekit/data_stream.h index 2d5b3f0d..ddd65c67 100644 --- a/include/livekit/data_stream.h +++ b/include/livekit/data_stream.h @@ -80,29 +80,29 @@ class TextStreamReader { /// Construct a reader from initial stream metadata. explicit TextStreamReader(TextStreamInfo info); - TextStreamReader(const TextStreamReader &) = delete; - TextStreamReader &operator=(const TextStreamReader &) = delete; + TextStreamReader(const TextStreamReader&) = delete; + TextStreamReader& operator=(const TextStreamReader&) = delete; /// Blocking read of next text chunk. /// Returns false when the stream has ended. - bool readNext(std::string &out); + bool readNext(std::string& out); /// Convenience: read entire stream into a single string. /// Blocks until the stream is closed. std::string readAll(); /// Metadata associated with this stream. - const TextStreamInfo &info() const noexcept { return info_; } + const TextStreamInfo& info() const noexcept { return info_; } private: friend class Room; /// Called by the Room when a new chunk arrives. - void onChunkUpdate(const std::string &text); + void onChunkUpdate(const std::string& text); /// Called by the Room when the stream is closed. /// Additional trailer attributes are merged into info().attributes. - void onStreamClose(const std::map &trailer_attrs); + void onStreamClose(const std::map& trailer_attrs); TextStreamInfo info_; @@ -120,25 +120,25 @@ class ByteStreamReader { /// Construct a reader from initial stream metadata. explicit ByteStreamReader(ByteStreamInfo info); - ByteStreamReader(const ByteStreamReader &) = delete; - ByteStreamReader &operator=(const ByteStreamReader &) = delete; + ByteStreamReader(const ByteStreamReader&) = delete; + ByteStreamReader& operator=(const ByteStreamReader&) = delete; /// Blocking read of next byte chunk. /// Returns false when the stream has ended. - bool readNext(std::vector &out); + bool readNext(std::vector& out); /// Metadata associated with this stream. - const ByteStreamInfo &info() const noexcept { return info_; } + const ByteStreamInfo& info() const noexcept { return info_; } private: friend class Room; /// Called by the Room when a new chunk arrives. - void onChunkUpdate(const std::vector &bytes); + void onChunkUpdate(const std::vector& bytes); /// Called by the Room when the stream is closed. /// Additional trailer attributes are merged into info().attributes. - void onStreamClose(const std::map &trailer_attrs); + void onStreamClose(const std::map& trailer_attrs); ByteStreamInfo info_; @@ -156,13 +156,13 @@ class BaseStreamWriter { virtual ~BaseStreamWriter() = default; /// Stream id assigned to this writer. - const std::string &streamId() const noexcept { return stream_id_; } + const std::string& streamId() const noexcept { return stream_id_; } /// Topic of this stream. - const std::string &topic() const noexcept { return topic_; } + const std::string& topic() const noexcept { return topic_; } /// MIME type for this stream. - const std::string &mimeType() const noexcept { return mime_type_; } + const std::string& mimeType() const noexcept { return mime_type_; } /// Timestamp (ms) when the stream was created. std::int64_t timestampMs() const noexcept { return timestamp_ms_; } @@ -172,22 +172,17 @@ class BaseStreamWriter { /// Close the stream with optional reason and attributes. /// Throws on FFI error or if already closed. - void close(const std::string &reason = "", - const std::map &attributes = {}); + void close(const std::string& reason = "", const std::map& attributes = {}); protected: - BaseStreamWriter(LocalParticipant &local_participant, - std::string topic = "", - std::map attributes = {}, - std::string stream_id = "", - std::optional total_size = std::nullopt, - std::string mime_type = "", - std::vector destination_identities = {}, - std::string sender_identity = ""); + BaseStreamWriter(LocalParticipant& local_participant, std::string topic = "", + std::map attributes = {}, std::string stream_id = "", + std::optional total_size = std::nullopt, std::string mime_type = "", + std::vector destination_identities = {}, std::string sender_identity = ""); enum class StreamKind { kUnknown, kText, kByte }; - LocalParticipant &local_participant_; + LocalParticipant& local_participant_; // Public-ish metadata (mirrors BaseStreamInfo, but kept simple here) std::string stream_id_; @@ -212,33 +207,29 @@ class BaseStreamWriter { /// Send a raw chunk of bytes. /// Throws on error or if stream is closed. - void sendChunk(const std::vector &content); + void sendChunk(const std::vector& content); /// Send the trailer with given reason and attributes. /// Throws on error. - void sendTrailer(const std::string &reason, - const std::map &attributes); + void sendTrailer(const std::string& reason, const std::map& attributes); }; /// Writer for outgoing text streams. class TextStreamWriter : public BaseStreamWriter { public: - TextStreamWriter(LocalParticipant &local_participant, - const std::string &topic = "", - const std::map &attributes = {}, - const std::string &stream_id = "", - std::optional total_size = std::nullopt, - const std::string &reply_to_id = "", - const std::vector &destination_identities = {}, - const std::string &sender_identity = ""); + TextStreamWriter(LocalParticipant& local_participant, const std::string& topic = "", + const std::map& attributes = {}, const std::string& stream_id = "", + std::optional total_size = std::nullopt, const std::string& reply_to_id = "", + const std::vector& destination_identities = {}, + const std::string& sender_identity = ""); /// Write a UTF-8 string to the stream. /// Data will be split into chunks of at most kStreamChunkSize bytes. /// Throws on error or if the stream is closed. - void write(const std::string &text); + void write(const std::string& text); /// Metadata associated with this stream. - const TextStreamInfo &info() const noexcept { return info_; } + const TextStreamInfo& info() const noexcept { return info_; } private: TextStreamInfo info_; @@ -248,22 +239,20 @@ class TextStreamWriter : public BaseStreamWriter { /// Writer for outgoing byte streams. class ByteStreamWriter : public BaseStreamWriter { public: - ByteStreamWriter(LocalParticipant &local_participant, const std::string &name, - const std::string &topic = "", - const std::map &attributes = {}, - const std::string &stream_id = "", + ByteStreamWriter(LocalParticipant& local_participant, const std::string& name, const std::string& topic = "", + const std::map& attributes = {}, const std::string& stream_id = "", std::optional total_size = std::nullopt, - const std::string &mime_type = "application/octet-stream", - const std::vector &destination_identities = {}, - const std::string &sender_identity = ""); + const std::string& mime_type = "application/octet-stream", + const std::vector& destination_identities = {}, + const std::string& sender_identity = ""); /// Write binary data to the stream. /// Data will be chunked into kStreamChunkSize-sized chunks. /// Throws on error or if the stream is closed. - void write(const std::vector &data); + void write(const std::vector& data); /// Metadata associated with this stream. - const ByteStreamInfo &info() const noexcept { return info_; } + const ByteStreamInfo& info() const noexcept { return info_; } private: ByteStreamInfo info_; @@ -277,8 +266,7 @@ class ByteStreamWriter : public BaseStreamWriter { * user spawns a background thread to consume the stream). */ using TextStreamHandler = - std::function, - const std::string &participant_identity)>; + std::function, const std::string& participant_identity)>; /* Callback invoked when a new incoming byte stream is opened. * @@ -287,7 +275,6 @@ using TextStreamHandler = * or background processing). */ using ByteStreamHandler = - std::function, - const std::string &participant_identity)>; + std::function, const std::string& participant_identity)>; } // namespace livekit diff --git a/include/livekit/data_track_error.h b/include/livekit/data_track_error.h index 5ec8f97f..e8519a64 100644 --- a/include/livekit/data_track_error.h +++ b/include/livekit/data_track_error.h @@ -45,8 +45,7 @@ struct PublishDataTrackError { PublishDataTrackErrorCode code{PublishDataTrackErrorCode::UNKNOWN}; std::string message; - static PublishDataTrackError - fromProto(const proto::PublishDataTrackError &error); + static PublishDataTrackError fromProto(const proto::PublishDataTrackError& error); }; enum class LocalDataTrackTryPushErrorCode : std::uint32_t { @@ -61,8 +60,7 @@ struct LocalDataTrackTryPushError { LocalDataTrackTryPushErrorCode code{LocalDataTrackTryPushErrorCode::UNKNOWN}; std::string message; - static LocalDataTrackTryPushError - fromProto(const proto::LocalDataTrackTryPushError &error); + static LocalDataTrackTryPushError fromProto(const proto::LocalDataTrackTryPushError& error); }; enum class SubscribeDataTrackErrorCode : std::uint32_t { @@ -79,8 +77,7 @@ struct SubscribeDataTrackError { SubscribeDataTrackErrorCode code{SubscribeDataTrackErrorCode::UNKNOWN}; std::string message; - static SubscribeDataTrackError - fromProto(const proto::SubscribeDataTrackError &error); + static SubscribeDataTrackError fromProto(const proto::SubscribeDataTrackError& error); }; } // namespace livekit diff --git a/include/livekit/data_track_frame.h b/include/livekit/data_track_frame.h index d4f08e6e..fa37c490 100644 --- a/include/livekit/data_track_frame.h +++ b/include/livekit/data_track_frame.h @@ -45,14 +45,12 @@ struct DataTrackFrame { */ std::optional user_timestamp; DataTrackFrame() = default; - DataTrackFrame(const DataTrackFrame &) = default; - DataTrackFrame(DataTrackFrame &&) noexcept = default; - DataTrackFrame &operator=(const DataTrackFrame &) = default; - DataTrackFrame &operator=(DataTrackFrame &&) noexcept = default; + DataTrackFrame(const DataTrackFrame&) = default; + DataTrackFrame(DataTrackFrame&&) noexcept = default; + DataTrackFrame& operator=(const DataTrackFrame&) = default; + DataTrackFrame& operator=(DataTrackFrame&&) noexcept = default; - explicit DataTrackFrame( - std::vector &&p, - std::optional ts = std::nullopt) noexcept + explicit DataTrackFrame(std::vector&& p, std::optional ts = std::nullopt) noexcept : payload(std::move(p)), user_timestamp(ts) {} /** @@ -62,7 +60,7 @@ struct DataTrackFrame { * @param owned The proto::DataTrackFrame to create a DataTrackFrame from. * @return The created DataTrackFrame. */ - static DataTrackFrame fromOwnedInfo(const proto::DataTrackFrame &owned); + static DataTrackFrame fromOwnedInfo(const proto::DataTrackFrame& owned); }; } // namespace livekit diff --git a/include/livekit/data_track_stream.h b/include/livekit/data_track_stream.h index cb81054e..5a3345f0 100644 --- a/include/livekit/data_track_stream.h +++ b/include/livekit/data_track_stream.h @@ -16,9 +16,6 @@ #pragma once -#include "livekit/data_track_frame.h" -#include "livekit/ffi_handle.h" - #include #include #include @@ -26,6 +23,9 @@ #include #include +#include "livekit/data_track_frame.h" +#include "livekit/ffi_handle.h" + namespace livekit { namespace proto { @@ -61,14 +61,14 @@ class DataTrackStream { virtual ~DataTrackStream(); - DataTrackStream(const DataTrackStream &) = delete; - DataTrackStream &operator=(const DataTrackStream &) = delete; + DataTrackStream(const DataTrackStream&) = delete; + DataTrackStream& operator=(const DataTrackStream&) = delete; // The FFI listener captures `this`, so moving the object would leave the // registered callback pointing at the old address. - DataTrackStream(DataTrackStream &&) noexcept = delete; + DataTrackStream(DataTrackStream&&) noexcept = delete; // Instances are created and returned as std::shared_ptr, so value-move // support is not required by the current API. - DataTrackStream &operator=(DataTrackStream &&) noexcept = delete; + DataTrackStream& operator=(DataTrackStream&&) noexcept = delete; /** * Blocking read: waits until a DataTrackFrame is available, or the @@ -77,7 +77,7 @@ class DataTrackStream { * @param out On success, filled with the next data frame. * @return true if a frame was delivered; false if the stream ended. */ - bool read(DataTrackFrame &out); + bool read(DataTrackFrame& out); /** * End the stream early. @@ -95,10 +95,10 @@ class DataTrackStream { void init(FfiHandle subscription_handle); /// FFI event handler, called by FfiClient. - void onFfiEvent(const proto::FfiEvent &event); + void onFfiEvent(const proto::FfiEvent& event); /// Push a received DataTrackFrame to the internal storage. - void pushFrame(DataTrackFrame &&frame); + void pushFrame(DataTrackFrame&& frame); /// Push an end-of-stream signal (EOS). void pushEos(); diff --git a/include/livekit/e2ee.h b/include/livekit/e2ee.h index e6473ae8..1260aaf2 100644 --- a/include/livekit/e2ee.h +++ b/include/livekit/e2ee.h @@ -33,7 +33,7 @@ enum class EncryptionType { }; /* Defaults (match other SDKs / Python defaults). */ -inline constexpr const char *kDefaultRatchetSalt = "LKFrameEncryptionKey"; +inline constexpr const char* kDefaultRatchetSalt = "LKFrameEncryptionKey"; inline constexpr int kDefaultRatchetWindowSize = 16; inline constexpr int kDefaultFailureTolerance = -1; @@ -61,8 +61,7 @@ struct KeyProviderOptions { /// /// If empty, the underlying implementation default is used. std::vector ratchet_salt = std::vector( - kDefaultRatchetSalt, kDefaultRatchetSalt + std::char_traits::length( - kDefaultRatchetSalt)); + kDefaultRatchetSalt, kDefaultRatchetSalt + std::char_traits::length(kDefaultRatchetSalt)); /// Controls how many previous keys are retained during ratcheting. int ratchet_window_size = kDefaultRatchetWindowSize; @@ -118,16 +117,16 @@ class E2EEManager { public: ~KeyProvider() = default; - KeyProvider(const KeyProvider &) = delete; - KeyProvider &operator=(const KeyProvider &) = delete; - KeyProvider(KeyProvider &&) noexcept = default; - KeyProvider &operator=(KeyProvider &&) noexcept = default; + KeyProvider(const KeyProvider&) = delete; + KeyProvider& operator=(const KeyProvider&) = delete; + KeyProvider(KeyProvider&&) noexcept = default; + KeyProvider& operator=(KeyProvider&&) noexcept = default; /// Returns the options used to initialize this KeyProvider. - const KeyProviderOptions &options() const; + const KeyProviderOptions& options() const; /// Sets the shared key for the given key slot. - void setSharedKey(const std::vector &key, int key_index = 0); + void setSharedKey(const std::vector& key, int key_index = 0); /// Exports the shared key for a given key slot. std::vector exportSharedKey(int key_index = 0) const; @@ -136,16 +135,13 @@ class E2EEManager { std::vector ratchetSharedKey(int key_index = 0); /// Sets a key for a specific participant identity. - void setKey(const std::string &participant_identity, - const std::vector &key, int key_index = 0); + void setKey(const std::string& participant_identity, const std::vector& key, int key_index = 0); /// Exports a participant-specific key. - std::vector exportKey(const std::string &participant_identity, - int key_index = 0) const; + std::vector exportKey(const std::string& participant_identity, int key_index = 0) const; /// Ratchets a participant-specific key and returns the new key. - std::vector - ratchetKey(const std::string &participant_identity, int key_index = 0); + std::vector ratchetKey(const std::string& participant_identity, int key_index = 0); private: friend class E2EEManager; @@ -156,15 +152,14 @@ class E2EEManager { class FrameCryptor { public: - FrameCryptor(std::uint64_t room_handle, std::string participant_identity, - int key_index, bool enabled); + FrameCryptor(std::uint64_t room_handle, std::string participant_identity, int key_index, bool enabled); ~FrameCryptor() = default; - FrameCryptor(const FrameCryptor &) = delete; - FrameCryptor &operator=(const FrameCryptor &) = delete; - FrameCryptor(FrameCryptor &&) noexcept = default; - FrameCryptor &operator=(FrameCryptor &&) noexcept = default; + FrameCryptor(const FrameCryptor&) = delete; + FrameCryptor& operator=(const FrameCryptor&) = delete; + FrameCryptor(FrameCryptor&&) noexcept = default; + FrameCryptor& operator=(FrameCryptor&&) noexcept = default; - const std::string &participantIdentity() const; + const std::string& participantIdentity() const; int keyIndex() const; bool enabled() const; @@ -182,10 +177,10 @@ class E2EEManager { }; ~E2EEManager() = default; - E2EEManager(const E2EEManager &) = delete; - E2EEManager &operator=(const E2EEManager &) = delete; - E2EEManager(E2EEManager &&) noexcept = delete; - E2EEManager &operator=(E2EEManager &&) noexcept = delete; + E2EEManager(const E2EEManager&) = delete; + E2EEManager& operator=(const E2EEManager&) = delete; + E2EEManager(E2EEManager&&) noexcept = delete; + E2EEManager& operator=(E2EEManager&&) noexcept = delete; /// Returns whether E2EE is currently enabled for this room at runtime. bool enabled() const; @@ -199,15 +194,15 @@ class E2EEManager { /// Returns the key provider if E2EE was configured for the room; otherwise /// nullptr. - KeyProvider *keyProvider(); - const KeyProvider *keyProvider() const; + KeyProvider* keyProvider(); + const KeyProvider* keyProvider() const; /// Retrieves the current list of frame cryptors from the underlying runtime. std::vector frameCryptors() const; protected: /// Internal constructor used by Room when E2EEOptions are provided. - explicit E2EEManager(std::uint64_t room_handle, const E2EEOptions &options); + explicit E2EEManager(std::uint64_t room_handle, const E2EEOptions& options); friend class Room; private: diff --git a/include/livekit/ffi_handle.h b/include/livekit/ffi_handle.h index 55ef6b64..7cd10466 100644 --- a/include/livekit/ffi_handle.h +++ b/include/livekit/ffi_handle.h @@ -32,12 +32,12 @@ class FfiHandle { ~FfiHandle(); // Non-copyable - FfiHandle(const FfiHandle &) = delete; - FfiHandle &operator=(const FfiHandle &) = delete; + FfiHandle(const FfiHandle&) = delete; + FfiHandle& operator=(const FfiHandle&) = delete; // Movable - FfiHandle(FfiHandle &&other) noexcept; - FfiHandle &operator=(FfiHandle &&other) noexcept; + FfiHandle(FfiHandle&& other) noexcept; + FfiHandle& operator=(FfiHandle&& other) noexcept; // Replace the current handle with a new one, dropping the old if needed void reset(uintptr_t new_handle = 0) noexcept; diff --git a/include/livekit/livekit.h b/include/livekit/livekit.h index b662b2a8..fb668611 100644 --- a/include/livekit/livekit.h +++ b/include/livekit/livekit.h @@ -59,8 +59,7 @@ enum class LogSink { /// @param log_sink The log sink to use for SDK messages (default: Console). /// @returns true if initialization happened on this call, false if it was /// already initialized. -bool initialize(const LogLevel &level = LogLevel::Info, - const LogSink &log_sink = LogSink::kConsole); +bool initialize(const LogLevel& level = LogLevel::Info, const LogSink& log_sink = LogSink::kConsole); /// Shut down the LiveKit SDK. /// diff --git a/include/livekit/local_audio_track.h b/include/livekit/local_audio_track.h index d8ad51e4..72359efa 100644 --- a/include/livekit/local_audio_track.h +++ b/include/livekit/local_audio_track.h @@ -16,11 +16,12 @@ #pragma once +#include +#include + #include "audio_frame.h" #include "local_track_publication.h" #include "track.h" -#include -#include namespace livekit { @@ -61,9 +62,8 @@ class LocalAudioTrack : public Track { /// directly for frame capture. /// /// @return A shared pointer to the newly constructed `LocalAudioTrack`. - static std::shared_ptr - createLocalAudioTrack(const std::string &name, - const std::shared_ptr &source); + static std::shared_ptr createLocalAudioTrack(const std::string& name, + const std::shared_ptr& source); /// Mutes the audio track. /// @@ -80,21 +80,18 @@ class LocalAudioTrack : public Track { /// Returns the publication that owns this track, or nullptr if the track is /// not published. - std::shared_ptr publication() const noexcept { - return local_publication_; - } + std::shared_ptr publication() const noexcept { return local_publication_; } /// Sets the publication that owns this track. /// Note: std::move on a const& silently falls back to a copy, so we assign /// directly. Changing the virtual signature to take by value would enable /// a true move but is an API-breaking change left for a future revision. - void setPublication(const std::shared_ptr - &publication) noexcept override { + void setPublication(const std::shared_ptr& publication) noexcept override { local_publication_ = publication; } private: - explicit LocalAudioTrack(FfiHandle handle, const proto::OwnedTrack &track); + explicit LocalAudioTrack(FfiHandle handle, const proto::OwnedTrack& track); /// The publication that owns this track. This is a nullptr until the track /// is published, and then points to the publication that owns this track. diff --git a/include/livekit/local_data_track.h b/include/livekit/local_data_track.h index 6d128de9..1e4365d7 100644 --- a/include/livekit/local_data_track.h +++ b/include/livekit/local_data_track.h @@ -16,18 +16,18 @@ #pragma once -#include "livekit/data_track_error.h" -#include "livekit/data_track_frame.h" -#include "livekit/data_track_info.h" -#include "livekit/ffi_handle.h" -#include "livekit/result.h" - #include #include #include #include #include +#include "livekit/data_track_error.h" +#include "livekit/data_track_frame.h" +#include "livekit/data_track_info.h" +#include "livekit/ffi_handle.h" +#include "livekit/result.h" + namespace livekit { namespace proto { @@ -59,11 +59,11 @@ class LocalDataTrack { public: ~LocalDataTrack() = default; - LocalDataTrack(const LocalDataTrack &) = delete; - LocalDataTrack &operator=(const LocalDataTrack &) = delete; + LocalDataTrack(const LocalDataTrack&) = delete; + LocalDataTrack& operator=(const LocalDataTrack&) = delete; /// Metadata about this data track. - const DataTrackInfo &info() const noexcept { return info_; } + const DataTrackInfo& info() const noexcept { return info_; } /** * Try to push a frame to all subscribers of this track. @@ -71,7 +71,7 @@ class LocalDataTrack { * @return success on delivery acceptance, or a typed error describing why * the frame could not be queued. */ - Result tryPush(const DataTrackFrame &frame); + Result tryPush(const DataTrackFrame& frame); /** * Try to push a frame to all subscribers of this track. @@ -79,9 +79,8 @@ class LocalDataTrack { * @return success on delivery acceptance, or a typed error describing why * the frame could not be queued. */ - Result - tryPush(std::vector &&payload, - std::optional user_timestamp = std::nullopt); + Result tryPush(std::vector&& payload, + std::optional user_timestamp = std::nullopt); /// Whether the track is still published in the room. bool isPublished() const; @@ -96,7 +95,7 @@ class LocalDataTrack { private: friend class LocalParticipant; - explicit LocalDataTrack(const proto::OwnedLocalDataTrack &owned); + explicit LocalDataTrack(const proto::OwnedLocalDataTrack& owned); uintptr_t ffi_handle_id() const noexcept { return handle_.get(); } diff --git a/include/livekit/local_participant.h b/include/livekit/local_participant.h index a7b855d2..52253d7e 100644 --- a/include/livekit/local_participant.h +++ b/include/livekit/local_participant.h @@ -16,14 +16,6 @@ #pragma once -#include "livekit/ffi_handle.h" -#include "livekit/local_audio_track.h" -#include "livekit/local_data_track.h" -#include "livekit/local_video_track.h" -#include "livekit/participant.h" -#include "livekit/room_event_types.h" -#include "livekit/rpc_error.h" - #include #include #include @@ -34,6 +26,14 @@ #include #include +#include "livekit/ffi_handle.h" +#include "livekit/local_audio_track.h" +#include "livekit/local_data_track.h" +#include "livekit/local_video_track.h" +#include "livekit/participant.h" +#include "livekit/room_event_types.h" +#include "livekit/rpc_error.h" + namespace livekit { struct ParticipantTrackPermission; @@ -56,8 +56,7 @@ struct RpcInvocationData { */ class LocalParticipant : public Participant { public: - using PublicationMap = - std::unordered_map>; + using PublicationMap = std::unordered_map>; using TrackMap = std::unordered_map>; /** @@ -70,13 +69,11 @@ class LocalParticipant : public Participant { * Returning std::nullopt means "no payload" and results in an empty * response body being sent back to the caller. */ - using RpcHandler = - std::function(const RpcInvocationData &)>; + using RpcHandler = std::function(const RpcInvocationData&)>; - LocalParticipant(FfiHandle handle, std::string sid, std::string name, - std::string identity, std::string metadata, - std::unordered_map attributes, - ParticipantKind kind, DisconnectReason reason); + LocalParticipant(FfiHandle handle, std::string sid, std::string name, std::string identity, std::string metadata, + std::unordered_map attributes, ParticipantKind kind, + DisconnectReason reason); /** * Track publications for this participant, keyed by publication SID. @@ -96,10 +93,8 @@ class LocalParticipant : public Participant { * * Throws std::runtime_error if FFI reports an error (if you wire that up). */ - void publishData(const std::vector &payload, - bool reliable = true, - const std::vector &destination_identities = {}, - const std::string &topic = {}); + void publishData(const std::vector& payload, bool reliable = true, + const std::vector& destination_identities = {}, const std::string& topic = {}); /** * Publish a SIP DTMF (phone keypad) tone into the room. @@ -110,16 +105,15 @@ class LocalParticipant : public Participant { * @param code DTMF code (0-15). * @param digit Human-readable digit string (e.g. "5", "#"). */ - void publishDtmf(int code, const std::string &digit); + void publishDtmf(int code, const std::string& digit); // ------------------------------------------------------------------------- // Metadata APIs (set metadata / name / attributes) // ------------------------------------------------------------------------- - void setMetadata(const std::string &metadata); - void setName(const std::string &name); - void - setAttributes(const std::unordered_map &attributes); + void setMetadata(const std::string& metadata); + void setName(const std::string& name); + void setAttributes(const std::unordered_map& attributes); /** * Set track subscription permissions for this participant. @@ -127,18 +121,15 @@ class LocalParticipant : public Participant { * @param allow_all_participants If true, all participants may subscribe. * @param participant_permissions Optional participant-specific permissions. */ - void - setTrackSubscriptionPermissions(bool allow_all_participants, - const std::vector - &participant_permissions = {}); + void setTrackSubscriptionPermissions(bool allow_all_participants, + const std::vector& participant_permissions = {}); /** * Publish a local track to the room. * * Throws std::runtime_error on error (e.g. publish failure). */ - void publishTrack(const std::shared_ptr &track, - const TrackPublishOptions &options); + void publishTrack(const std::shared_ptr& track, const TrackPublishOptions& options); /** * Create a \ref LocalVideoTrack backed by the given \ref VideoSource, @@ -147,10 +138,9 @@ class LocalParticipant : public Participant { * The caller retains ownership of \p source and should use it directly * for frame capture on the video thread. */ - std::shared_ptr - publishVideoTrack(const std::string &name, - const std::shared_ptr &source, - TrackSource track_source); + std::shared_ptr publishVideoTrack(const std::string& name, + const std::shared_ptr& source, + TrackSource track_source); /** * Create a \ref LocalAudioTrack backed by the given \ref AudioSource, @@ -159,17 +149,16 @@ class LocalParticipant : public Participant { * The caller retains ownership of \p source and should use it directly * for frame capture on the audio thread. */ - std::shared_ptr - publishAudioTrack(const std::string &name, - const std::shared_ptr &source, - TrackSource track_source); + std::shared_ptr publishAudioTrack(const std::string& name, + const std::shared_ptr& source, + TrackSource track_source); /** * Unpublish a track from the room by SID. * * If the publication exists in the local map, it is removed. */ - void unpublishTrack(const std::string &track_sid); + void unpublishTrack(const std::string& track_sid); /** * Publish a data track to the room. @@ -184,8 +173,7 @@ class LocalParticipant : public Participant { * @return The published track on success, or a typed error describing why * publication failed. */ - Result, PublishDataTrackError> - publishDataTrack(const std::string &name); + Result, PublishDataTrackError> publishDataTrack(const std::string& name); /** * Unpublish a data track from the room. @@ -195,7 +183,7 @@ class LocalParticipant : public Participant { * * @param track The data track to unpublish. Null is ignored. */ - void unpublishDataTrack(const std::shared_ptr &track); + void unpublishDataTrack(const std::shared_ptr& track); /** * Initiate an RPC call to a remote participant. @@ -214,10 +202,8 @@ class LocalParticipant : public Participant { * @throws std::runtime_error If the underlying FFI handle is invalid or * the FFI call fails unexpectedly. */ - std::string - performRpc(const std::string &destination_identity, const std::string &method, - const std::string &payload, - const std::optional &response_timeout = std::nullopt); + std::string performRpc(const std::string& destination_identity, const std::string& method, const std::string& payload, + const std::optional& response_timeout = std::nullopt); /** * Register a handler for an incoming RPC method. @@ -235,7 +221,7 @@ class LocalParticipant : public Participant { * replaced by the new handler. */ - void registerRpcMethod(const std::string &method_name, RpcHandler handler); + void registerRpcMethod(const std::string& method_name, RpcHandler handler); /** * Unregister a previously registered RPC method handler. @@ -248,7 +234,7 @@ class LocalParticipant : public Participant { * If no handler is registered for this name, the call * is a no-op. */ - void unregisterRpcMethod(const std::string &method_name); + void unregisterRpcMethod(const std::string& method_name); protected: /** @@ -261,15 +247,11 @@ class LocalParticipant : public Participant { // Called by Room when an rpc_method_invocation event is received from the // SFU. This is internal plumbing and not intended to be called directly by // SDK users. - void handleRpcMethodInvocation(std::uint64_t invocation_id, - const std::string &method, - const std::string &request_id, - const std::string &caller_identity, - const std::string &payload, + void handleRpcMethodInvocation(std::uint64_t invocation_id, const std::string& method, const std::string& request_id, + const std::string& caller_identity, const std::string& payload, double response_timeout); // Called by Room events like kTrackMuted. - std::shared_ptr - findTrackPublication(const std::string &sid) const override; + std::shared_ptr findTrackPublication(const std::string& sid) const override; friend class Room; private: @@ -289,8 +271,7 @@ class LocalParticipant : public Participant { int active_invocations = 0; bool shutting_down = false; }; - std::shared_ptr rpc_state_ = - std::make_shared(); + std::shared_ptr rpc_state_ = std::make_shared(); }; } // namespace livekit diff --git a/include/livekit/local_track_publication.h b/include/livekit/local_track_publication.h index f2ccd346..51d1820a 100644 --- a/include/livekit/local_track_publication.h +++ b/include/livekit/local_track_publication.h @@ -28,7 +28,7 @@ class LocalTrackPublication : public TrackPublication { public: /// Note, this LocalTrackPublication is constructed internally only; /// safe to accept proto::OwnedTrackPublication. - explicit LocalTrackPublication(const proto::OwnedTrackPublication &owned); + explicit LocalTrackPublication(const proto::OwnedTrackPublication& owned); }; } // namespace livekit diff --git a/include/livekit/local_video_track.h b/include/livekit/local_video_track.h index 93e27837..7fc316b8 100644 --- a/include/livekit/local_video_track.h +++ b/include/livekit/local_video_track.h @@ -16,12 +16,12 @@ #pragma once -#include "local_track_publication.h" -#include "track.h" - #include #include +#include "local_track_publication.h" +#include "track.h" + namespace livekit { namespace proto { @@ -62,9 +62,8 @@ class LocalVideoTrack : public Track { /// directly for frame capture. /// /// @return A shared pointer to the newly constructed `LocalVideoTrack`. - static std::shared_ptr - createLocalVideoTrack(const std::string &name, - const std::shared_ptr &source); + static std::shared_ptr createLocalVideoTrack(const std::string& name, + const std::shared_ptr& source); /// Mutes the video track. /// @@ -81,21 +80,18 @@ class LocalVideoTrack : public Track { /// Returns the publication that owns this track, or nullptr if the track is /// not published. - std::shared_ptr publication() const noexcept { - return local_publication_; - } + std::shared_ptr publication() const noexcept { return local_publication_; } /// Sets the publication that owns this track. /// Note: std::move on a const& silently falls back to a copy, so we assign /// directly. Changing the virtual signature to take by value would enable /// a true move but is a API-breaking change hence left for a future revision. - void setPublication(const std::shared_ptr - &publication) noexcept override { + void setPublication(const std::shared_ptr& publication) noexcept override { local_publication_ = publication; } private: - explicit LocalVideoTrack(FfiHandle handle, const proto::OwnedTrack &track); + explicit LocalVideoTrack(FfiHandle handle, const proto::OwnedTrack& track); /// The publication that owns this track. This is a nullptr until the track /// is published, and then points to the publication that owns this track. diff --git a/include/livekit/logging.h b/include/livekit/logging.h index 9d2cc564..4ba93401 100644 --- a/include/livekit/logging.h +++ b/include/livekit/logging.h @@ -50,9 +50,7 @@ LogLevel getLogLevel(); /// The callback is invoked sequentially (never concurrently) from the /// thread that generated the log message. Implementations must not block /// for extended periods. -using LogCallback = - std::function; +using LogCallback = std::function; /// Install a custom log callback, replacing the default stderr sink. /// diff --git a/include/livekit/participant.h b/include/livekit/participant.h index 5987963e..98c67617 100644 --- a/include/livekit/participant.h +++ b/include/livekit/participant.h @@ -30,25 +30,24 @@ enum class ParticipantKind { Standard = 0, Ingress, Egress, Sip, Agent }; class Participant { public: - Participant(FfiHandle handle, std::string sid, std::string name, - std::string identity, std::string metadata, - std::unordered_map attributes, - ParticipantKind kind, DisconnectReason reason) - : handle_(std::move(handle)), sid_(std::move(sid)), - name_(std::move(name)), identity_(std::move(identity)), - metadata_(std::move(metadata)), attributes_(std::move(attributes)), - kind_(kind), reason_(reason) {} + Participant(FfiHandle handle, std::string sid, std::string name, std::string identity, std::string metadata, + std::unordered_map attributes, ParticipantKind kind, DisconnectReason reason) + : handle_(std::move(handle)), + sid_(std::move(sid)), + name_(std::move(name)), + identity_(std::move(identity)), + metadata_(std::move(metadata)), + attributes_(std::move(attributes)), + kind_(kind), + reason_(reason) {} virtual ~Participant() = default; // Plain getters (caller ensures threading) - const std::string &sid() const noexcept { return sid_; } - const std::string &name() const noexcept { return name_; } - const std::string &identity() const noexcept { return identity_; } - const std::string &metadata() const noexcept { return metadata_; } - const std::unordered_map & - attributes() const noexcept { - return attributes_; - } + const std::string& sid() const noexcept { return sid_; } + const std::string& name() const noexcept { return name_; } + const std::string& identity() const noexcept { return identity_; } + const std::string& metadata() const noexcept { return metadata_; } + const std::unordered_map& attributes() const noexcept { return attributes_; } ParticipantKind kind() const noexcept { return kind_; } DisconnectReason disconnectReason() const noexcept { return reason_; } @@ -56,25 +55,15 @@ class Participant { // Setters (caller ensures threading) void set_name(std::string name) noexcept { name_ = std::move(name); } - void set_metadata(std::string metadata) noexcept { - metadata_ = std::move(metadata); - } - void - set_attributes(std::unordered_map attrs) noexcept { - attributes_ = std::move(attrs); - } - void set_attribute(const std::string &key, const std::string &value) { - attributes_[key] = value; - } - void remove_attribute(const std::string &key) { attributes_.erase(key); } + void set_metadata(std::string metadata) noexcept { metadata_ = std::move(metadata); } + void set_attributes(std::unordered_map attrs) noexcept { attributes_ = std::move(attrs); } + void set_attribute(const std::string& key, const std::string& value) { attributes_[key] = value; } + void remove_attribute(const std::string& key) { attributes_.erase(key); } void set_kind(ParticipantKind kind) noexcept { kind_ = kind; } - void set_disconnect_reason(DisconnectReason reason) noexcept { - reason_ = reason; - } + void set_disconnect_reason(DisconnectReason reason) noexcept { reason_ = reason; } protected: - virtual std::shared_ptr - findTrackPublication(const std::string &sid) const = 0; + virtual std::shared_ptr findTrackPublication(const std::string& sid) const = 0; friend class Room; private: diff --git a/include/livekit/remote_audio_track.h b/include/livekit/remote_audio_track.h index 572e62c1..cfe8761c 100644 --- a/include/livekit/remote_audio_track.h +++ b/include/livekit/remote_audio_track.h @@ -16,10 +16,11 @@ #pragma once -#include "track.h" #include #include +#include "track.h" + namespace livekit { namespace proto { @@ -45,7 +46,7 @@ class RemoteAudioTrack : public Track { /// Constructs a `RemoteAudioTrack` from an internal protocol-level /// `OwnedTrack` description provided by the signaling/FFI layer. /// This constructor is intended for internal SDK use only. - explicit RemoteAudioTrack(const proto::OwnedTrack &track); + explicit RemoteAudioTrack(const proto::OwnedTrack& track); /// Returns a concise, human-readable string summarizing the track, /// including its SID and name. Useful for debugging and logging. diff --git a/include/livekit/remote_data_track.h b/include/livekit/remote_data_track.h index 762140b7..a0b40515 100644 --- a/include/livekit/remote_data_track.h +++ b/include/livekit/remote_data_track.h @@ -16,15 +16,15 @@ #pragma once +#include +#include + #include "livekit/data_track_error.h" #include "livekit/data_track_info.h" #include "livekit/data_track_stream.h" #include "livekit/ffi_handle.h" #include "livekit/result.h" -#include -#include - namespace livekit { namespace proto { @@ -54,16 +54,14 @@ class RemoteDataTrack { public: ~RemoteDataTrack() = default; - RemoteDataTrack(const RemoteDataTrack &) = delete; - RemoteDataTrack &operator=(const RemoteDataTrack &) = delete; + RemoteDataTrack(const RemoteDataTrack&) = delete; + RemoteDataTrack& operator=(const RemoteDataTrack&) = delete; /// Metadata about this data track. - const DataTrackInfo &info() const noexcept { return info_; } + const DataTrackInfo& info() const noexcept { return info_; } /// Identity of the remote participant who published this track. - const std::string &publisherIdentity() const noexcept { - return publisher_identity_; - } + const std::string& publisherIdentity() const noexcept { return publisher_identity_; } /// Whether the track is still published by the remote participant. bool isPublished() const; @@ -79,13 +77,13 @@ class RemoteDataTrack { * Returns a DataTrackStream that delivers frames via blocking * read(). Destroy the stream to unsubscribe. */ - Result, SubscribeDataTrackError> - subscribe(const DataTrackStream::Options &options = {}); + Result, SubscribeDataTrackError> subscribe( + const DataTrackStream::Options& options = {}); private: friend class Room; - explicit RemoteDataTrack(const proto::OwnedRemoteDataTrack &owned); + explicit RemoteDataTrack(const proto::OwnedRemoteDataTrack& owned); uintptr_t ffi_handle_id() const noexcept { return handle_.get(); } /** RAII wrapper for the Rust-owned FFI resource. */ diff --git a/include/livekit/remote_participant.h b/include/livekit/remote_participant.h index 367061fe..938086a0 100644 --- a/include/livekit/remote_participant.h +++ b/include/livekit/remote_participant.h @@ -16,43 +16,36 @@ #pragma once -#include "participant.h" - #include #include #include +#include "participant.h" + namespace livekit { class RemoteTrackPublication; class RemoteParticipant : public Participant { public: - using PublicationMap = - std::unordered_map>; + using PublicationMap = std::unordered_map>; - RemoteParticipant(FfiHandle handle, std::string sid, std::string name, - std::string identity, std::string metadata, - std::unordered_map attributes, - ParticipantKind kind, DisconnectReason reason); + RemoteParticipant(FfiHandle handle, std::string sid, std::string name, std::string identity, std::string metadata, + std::unordered_map attributes, ParticipantKind kind, + DisconnectReason reason); // A dictionary of track publications associated with the participant. - const PublicationMap &trackPublications() const noexcept { - return track_publications_; - } + const PublicationMap& trackPublications() const noexcept { return track_publications_; } // Optional: non-const access if you want to mutate in-place. - PublicationMap &mutableTrackPublications() noexcept { - return track_publications_; - } + PublicationMap& mutableTrackPublications() noexcept { return track_publications_; } std::string to_string() const; protected: // Called by Room events like kTrackMuted. This is internal plumbing and not // intended to be called directly by SDK users. - std::shared_ptr - findTrackPublication(const std::string &sid) const override; + std::shared_ptr findTrackPublication(const std::string& sid) const override; friend class Room; private: @@ -60,7 +53,6 @@ class RemoteParticipant : public Participant { }; // Convenience for logging / streaming -std::ostream &operator<<(std::ostream &os, - const RemoteParticipant &participant); +std::ostream& operator<<(std::ostream& os, const RemoteParticipant& participant); } // namespace livekit diff --git a/include/livekit/remote_track_publication.h b/include/livekit/remote_track_publication.h index 212eabd3..5ad4c0fe 100644 --- a/include/livekit/remote_track_publication.h +++ b/include/livekit/remote_track_publication.h @@ -28,8 +28,8 @@ class RemoteTrackPublication : public TrackPublication { public: /// Note, this RemoteTrackPublication is constructed internally only; /// safe to accept proto::OwnedTrackPublication. - explicit RemoteTrackPublication(const proto::OwnedTrackPublication &owned); - + explicit RemoteTrackPublication(const proto::OwnedTrackPublication& owned); + bool subscribed() const noexcept { return subscribed_; } void setSubscribed(bool subscribed); diff --git a/include/livekit/remote_video_track.h b/include/livekit/remote_video_track.h index e7a2dada..891d8fbd 100644 --- a/include/livekit/remote_video_track.h +++ b/include/livekit/remote_video_track.h @@ -16,10 +16,11 @@ #pragma once -#include "track.h" #include #include +#include "track.h" + namespace livekit { namespace proto { @@ -45,7 +46,7 @@ class RemoteVideoTrack : public Track { /// Constructs a `RemoteVideoTrack` from an internal protocol-level /// `OwnedTrack` description provided by the signaling/FFI layer. /// This constructor is intended for internal SDK use only. - explicit RemoteVideoTrack(const proto::OwnedTrack &track); + explicit RemoteVideoTrack(const proto::OwnedTrack& track); /// Returns a concise, human-readable string summarizing the track, /// including its SID and name. Useful for debugging and logging. diff --git a/include/livekit/result.h b/include/livekit/result.h index dce0e874..635d8930 100644 --- a/include/livekit/result.h +++ b/include/livekit/result.h @@ -39,22 +39,19 @@ namespace livekit { * result, or `error()` on a success result, is a programmer error and will * trip the debug assertion. */ -template class [[nodiscard]] Result { +template +class [[nodiscard]] Result { public: /// Construct a successful result containing a value. - template ::value>> - static Result success(U &&value) { - return Result( - std::variant(std::in_place_index<0>, std::forward(value))); + template ::value>> + static Result success(U&& value) { + return Result(std::variant(std::in_place_index<0>, std::forward(value))); } /// Construct a failed result containing an error. - template ::value>> - static Result failure(F &&error) { - return Result( - std::variant(std::in_place_index<1>, std::forward(error))); + template ::value>> + static Result failure(F&& error) { + return Result(std::variant(std::in_place_index<1>, std::forward(error))); } /// True when the result contains a success value. @@ -64,63 +61,63 @@ template class [[nodiscard]] Result { /// Allows `if (result)` style success checks. explicit operator bool() const noexcept { return ok(); } - // TODO (AEG): clang-tidy flagged these accessors because the signatures are marked - // noexcept, but std::get can throw a std::bad_variant_access exception on - // std::variant specifically. Investigate if this is actually a concern or if the types - // are safe within this class (unit test ideal). + // TODO (AEG): clang-tidy flagged these accessors because the signatures are + // marked noexcept, but std::get can throw a std::bad_variant_access exception + // on std::variant specifically. Investigate if this is actually a concern or + // if the types are safe within this class (unit test ideal). /// Access the success value. Requires `ok() == true`. // NOLINTNEXTLINE(bugprone-exception-escape) - T &value() & noexcept { + T& value() & noexcept { assert(ok()); return std::get<0>(storage_); } /// Access the success value. Requires `ok() == true`. // NOLINTNEXTLINE(bugprone-exception-escape) - const T &value() const & noexcept { + const T& value() const& noexcept { assert(ok()); return std::get<0>(storage_); } /// Move the success value out. Requires `ok() == true`. // NOLINTNEXTLINE(bugprone-exception-escape) - T &&value() && noexcept { + T&& value() && noexcept { assert(ok()); return std::get<0>(std::move(storage_)); } /// Move the success value out. Requires `ok() == true`. // NOLINTNEXTLINE(bugprone-exception-escape) - const T &&value() const && noexcept { + const T&& value() const&& noexcept { assert(ok()); return std::get<0>(std::move(storage_)); } /// Access the error value. Requires `has_error() == true`. // NOLINTNEXTLINE(bugprone-exception-escape) - E &error() & noexcept { + E& error() & noexcept { assert(has_error()); return std::get<1>(storage_); } /// Access the error value. Requires `has_error() == true`. // NOLINTNEXTLINE(bugprone-exception-escape) - const E &error() const & noexcept { + const E& error() const& noexcept { assert(has_error()); return std::get<1>(storage_); } /// Move the error value out. Requires `has_error() == true`. // NOLINTNEXTLINE(bugprone-exception-escape) - E &&error() && noexcept { + E&& error() && noexcept { assert(has_error()); return std::get<1>(std::move(storage_)); } /// Move the error value out. Requires `has_error() == true`. // NOLINTNEXTLINE(bugprone-exception-escape) - const E &&error() const && noexcept { + const E&& error() const&& noexcept { assert(has_error()); return std::get<1>(std::move(storage_)); } @@ -137,15 +134,15 @@ template class [[nodiscard]] Result { * This keeps the same calling style as `Result` without forcing callers * to invent a dummy success payload. */ -template class [[nodiscard]] Result { +template +class [[nodiscard]] Result { public: /// Construct a successful result with no payload. static Result success() { return Result(std::nullopt); } /// Construct a failed result containing an error. - template ::value>> - static Result failure(F &&error) { + template ::value>> + static Result failure(F&& error) { return Result(std::optional(std::forward(error))); } @@ -160,25 +157,25 @@ template class [[nodiscard]] Result { void value() const noexcept { assert(ok()); } /// Access the error value. Requires `has_error() == true`. - E &error() & noexcept { + E& error() & noexcept { assert(has_error()); return *error_; } /// Access the error value. Requires `has_error() == true`. - const E &error() const & noexcept { + const E& error() const& noexcept { assert(has_error()); return *error_; } /// Move the error value out. Requires `has_error() == true`. - E &&error() && noexcept { + E&& error() && noexcept { assert(has_error()); return std::move(*error_); } /// Move the error value out. Requires `has_error() == true`. - const E &&error() const && noexcept { + const E&& error() const&& noexcept { assert(has_error()); return std::move(*error_); } diff --git a/include/livekit/room.h b/include/livekit/room.h index e65d7d1f..932e01cd 100644 --- a/include/livekit/room.h +++ b/include/livekit/room.h @@ -17,16 +17,16 @@ #ifndef LIVEKIT_ROOM_H #define LIVEKIT_ROOM_H +#include +#include +#include + #include "livekit/data_stream.h" #include "livekit/e2ee.h" #include "livekit/ffi_handle.h" #include "livekit/room_event_types.h" #include "livekit/subscription_thread_dispatcher.h" -#include -#include -#include - namespace livekit { class RoomDelegate; @@ -109,7 +109,7 @@ class Room { * Room room; * room.setDelegate(&del); */ - void setDelegate(RoomDelegate *delegate); + void setDelegate(RoomDelegate* delegate); /* Connect to a LiveKit room using the given URL and token, applying the * supplied connection options. @@ -131,8 +131,7 @@ class Room { * Without auto_subscribe enabled, remote tracks will NOT be subscribed * automatically, and no remote audio/video will ever arrive. */ - bool Connect(const std::string &url, const std::string &token, - const RoomOptions &options); + bool Connect(const std::string& url, const std::string& token, const RoomOptions& options); // Accessors @@ -155,7 +154,7 @@ class Room { * Return value: * Non-null pointer after successful Connect(). */ - LocalParticipant *localParticipant() const; + LocalParticipant* localParticipant() const; /* Look up a remote participant by identity. * @@ -168,7 +167,7 @@ class Room { * - track publications * - callbacks for track subscribed/unsubscribed, muted/unmuted */ - RemoteParticipant *remoteParticipant(const std::string &identity) const; + RemoteParticipant* remoteParticipant(const std::string& identity) const; /// Returns a snapshot of all current remote participants. std::vector> remoteParticipants() const; @@ -191,14 +190,13 @@ class Room { * Throws: * std::runtime_error if a handler is already registered for the topic. */ - void registerTextStreamHandler(const std::string &topic, - TextStreamHandler handler); + void registerTextStreamHandler(const std::string& topic, TextStreamHandler handler); /* Unregister the text stream handler for the given topic. * * If no handler exists for the topic, this function is a no-op. */ - void unregisterTextStreamHandler(const std::string &topic); + void unregisterTextStreamHandler(const std::string& topic); /* Register a handler for incoming byte streams on a specific topic. * @@ -217,14 +215,13 @@ class Room { * Throws: * std::runtime_error if a handler is already registered for the topic. */ - void registerByteStreamHandler(const std::string &topic, - ByteStreamHandler handler); + void registerByteStreamHandler(const std::string& topic, ByteStreamHandler handler); /* Unregister the byte stream handler for the given topic. * * If no handler exists for the topic, this function is a no-op. */ - void unregisterByteStreamHandler(const std::string &topic); + void unregisterByteStreamHandler(const std::string& topic); /** * Returns the room's E2EE manager, or nullptr if E2EE was not enabled at @@ -234,7 +231,7 @@ class Room { * - The manager is created after a successful Connect(). * - If E2EE was not configured in RoomOptions, this will return nullptr. */ - E2EEManager *e2eeManager() const; + E2EEManager* e2eeManager() const; // --------------------------------------------------------------- // Frame callbacks @@ -243,72 +240,58 @@ class Room { /** * @brief Sets the audio frame callback via SubscriptionThreadDispatcher. */ - void setOnAudioFrameCallback(const std::string &participant_identity, - TrackSource source, AudioFrameCallback callback, - const AudioStream::Options &opts = {}); + void setOnAudioFrameCallback(const std::string& participant_identity, TrackSource source, AudioFrameCallback callback, + const AudioStream::Options& opts = {}); /** * @brief Sets the audio frame callback via SubscriptionThreadDispatcher. */ - void setOnAudioFrameCallback(const std::string &participant_identity, - const std::string &track_name, - AudioFrameCallback callback, - const AudioStream::Options &opts = {}); + void setOnAudioFrameCallback(const std::string& participant_identity, const std::string& track_name, + AudioFrameCallback callback, const AudioStream::Options& opts = {}); /** * @brief Sets the video frame callback via SubscriptionThreadDispatcher. */ - void setOnVideoFrameCallback(const std::string &participant_identity, - TrackSource source, VideoFrameCallback callback, - const VideoStream::Options &opts = {}); + void setOnVideoFrameCallback(const std::string& participant_identity, TrackSource source, VideoFrameCallback callback, + const VideoStream::Options& opts = {}); /** * @brief Sets the video frame callback via SubscriptionThreadDispatcher. */ - void setOnVideoFrameCallback(const std::string &participant_identity, - const std::string &track_name, - VideoFrameCallback callback, - const VideoStream::Options &opts = {}); + void setOnVideoFrameCallback(const std::string& participant_identity, const std::string& track_name, + VideoFrameCallback callback, const VideoStream::Options& opts = {}); /** * @brief Sets the video frame event callback via * SubscriptionThreadDispatcher. */ - void setOnVideoFrameEventCallback(const std::string &participant_identity, - const std::string &track_name, - VideoFrameEventCallback callback, - const VideoStream::Options &opts = {}); + void setOnVideoFrameEventCallback(const std::string& participant_identity, const std::string& track_name, + VideoFrameEventCallback callback, const VideoStream::Options& opts = {}); /** * @brief Clears the audio frame callback via SubscriptionThreadDispatcher. */ - void clearOnAudioFrameCallback(const std::string &participant_identity, - TrackSource source); + void clearOnAudioFrameCallback(const std::string& participant_identity, TrackSource source); /** * @brief Clears the audio frame callback via SubscriptionThreadDispatcher. */ - void clearOnAudioFrameCallback(const std::string &participant_identity, - const std::string &track_name); + void clearOnAudioFrameCallback(const std::string& participant_identity, const std::string& track_name); /** * @brief Clears the video frame callback via SubscriptionThreadDispatcher. */ - void clearOnVideoFrameCallback(const std::string &participant_identity, - TrackSource source); + void clearOnVideoFrameCallback(const std::string& participant_identity, TrackSource source); /** * @brief Clears the video frame callback via SubscriptionThreadDispatcher. */ - void clearOnVideoFrameCallback(const std::string &participant_identity, - const std::string &track_name); + void clearOnVideoFrameCallback(const std::string& participant_identity, const std::string& track_name); /** * @brief Adds a data frame callback via SubscriptionThreadDispatcher. */ - DataFrameCallbackId - addOnDataFrameCallback(const std::string &participant_identity, - const std::string &track_name, - DataFrameCallback callback); + DataFrameCallbackId addOnDataFrameCallback(const std::string& participant_identity, const std::string& track_name, + DataFrameCallback callback); /** * @brief Removes the data frame callback via SubscriptionThreadDispatcher. @@ -320,19 +303,16 @@ class Room { mutable std::mutex lock_; ConnectionState connection_state_ = ConnectionState::Disconnected; - RoomDelegate *delegate_ = nullptr; // Not owned + RoomDelegate* delegate_ = nullptr; // Not owned RoomInfoData room_info_; std::shared_ptr room_handle_; std::unique_ptr local_participant_; - std::unordered_map> - remote_participants_; + std::unordered_map> remote_participants_; // Data stream std::unordered_map text_stream_handlers_; std::unordered_map byte_stream_handlers_; - std::unordered_map> - text_stream_readers_; - std::unordered_map> - byte_stream_readers_; + std::unordered_map> text_stream_readers_; + std::unordered_map> byte_stream_readers_; // E2EE std::unique_ptr e2ee_manager_; std::shared_ptr subscription_thread_dispatcher_; @@ -340,7 +320,7 @@ class Room { // FfiClient listener ID (0 means no listener registered) int listener_id_{0}; - void OnEvent(const proto::FfiEvent &event); + void OnEvent(const proto::FfiEvent& event); }; } // namespace livekit diff --git a/include/livekit/room_delegate.h b/include/livekit/room_delegate.h index 2621c92c..addc90c4 100644 --- a/include/livekit/room_delegate.h +++ b/include/livekit/room_delegate.h @@ -42,15 +42,12 @@ class RoomDelegate { /** * Called when a new remote participant joins the room. */ - virtual void onParticipantConnected(Room &, - const ParticipantConnectedEvent &) {} + virtual void onParticipantConnected(Room&, const ParticipantConnectedEvent&) {} /** * Called when a remote participant leaves the room. */ - virtual void onParticipantDisconnected(Room &, - const ParticipantDisconnectedEvent &) { - } + virtual void onParticipantDisconnected(Room&, const ParticipantDisconnectedEvent&) {} // ------------------------------------------------------------------ // Local track publication events @@ -59,20 +56,17 @@ class RoomDelegate { /** * Called when a local track is successfully published. */ - virtual void onLocalTrackPublished(Room &, const LocalTrackPublishedEvent &) { - } + virtual void onLocalTrackPublished(Room&, const LocalTrackPublishedEvent&) {} /** * Called when a local track is unpublished. */ - virtual void onLocalTrackUnpublished(Room &, - const LocalTrackUnpublishedEvent &) {} + virtual void onLocalTrackUnpublished(Room&, const LocalTrackUnpublishedEvent&) {} /** * Called when a local track gains its first subscriber. */ - virtual void onLocalTrackSubscribed(Room &, - const LocalTrackSubscribedEvent &) {} + virtual void onLocalTrackSubscribed(Room&, const LocalTrackSubscribedEvent&) {} // ------------------------------------------------------------------ // Remote track publication/subscription @@ -81,39 +75,37 @@ class RoomDelegate { /** * Called when a remote participant publishes a track. */ - virtual void onTrackPublished(Room &, const TrackPublishedEvent &) {} + virtual void onTrackPublished(Room&, const TrackPublishedEvent&) {} /** * Called when a remote participant unpublishes a track. */ - virtual void onTrackUnpublished(Room &, const TrackUnpublishedEvent &) {} + virtual void onTrackUnpublished(Room&, const TrackUnpublishedEvent&) {} /** * Called when a remote track is successfully subscribed. */ - virtual void onTrackSubscribed(Room &, const TrackSubscribedEvent &) {} + virtual void onTrackSubscribed(Room&, const TrackSubscribedEvent&) {} /** * Called when a remote track is unsubscribed. */ - virtual void onTrackUnsubscribed(Room &, const TrackUnsubscribedEvent &) {} + virtual void onTrackUnsubscribed(Room&, const TrackUnsubscribedEvent&) {} /** * Called when subscribing to a remote track fails. */ - virtual void onTrackSubscriptionFailed(Room &, - const TrackSubscriptionFailedEvent &) { - } + virtual void onTrackSubscriptionFailed(Room&, const TrackSubscriptionFailedEvent&) {} /** * Called when a track is muted. */ - virtual void onTrackMuted(Room &, const TrackMutedEvent &) {} + virtual void onTrackMuted(Room&, const TrackMutedEvent&) {} /** * Called when a track is unmuted. */ - virtual void onTrackUnmuted(Room &, const TrackUnmutedEvent &) {} + virtual void onTrackUnmuted(Room&, const TrackUnmutedEvent&) {} // ------------------------------------------------------------------ // Active speakers @@ -122,8 +114,7 @@ class RoomDelegate { /** * Called when the list of active speakers changes. */ - virtual void onActiveSpeakersChanged(Room &, - const ActiveSpeakersChangedEvent &) {} + virtual void onActiveSpeakersChanged(Room&, const ActiveSpeakersChangedEvent&) {} // ------------------------------------------------------------------ // Room info / metadata @@ -132,23 +123,22 @@ class RoomDelegate { /** * Called when the room's metadata changes. */ - virtual void onRoomMetadataChanged(Room &, const RoomMetadataChangedEvent &) { - } + virtual void onRoomMetadataChanged(Room&, const RoomMetadataChangedEvent&) {} /** * Called when the room SID changes (e.g., after migration). */ - virtual void onRoomSidChanged(Room &, const RoomSidChangedEvent &) {} + virtual void onRoomSidChanged(Room&, const RoomSidChangedEvent&) {} /** * Called when any room info is updated. */ - virtual void onRoomUpdated(Room &, const RoomUpdatedEvent &) {} + virtual void onRoomUpdated(Room&, const RoomUpdatedEvent&) {} /** * Called when the participant is moved to another room. */ - virtual void onRoomMoved(Room &, const RoomMovedEvent &) {} + virtual void onRoomMoved(Room&, const RoomMovedEvent&) {} // ------------------------------------------------------------------ // Participant info changes @@ -157,28 +147,22 @@ class RoomDelegate { /** * Called when a participant's metadata is updated. */ - virtual void - onParticipantMetadataChanged(Room &, - const ParticipantMetadataChangedEvent &) {} + virtual void onParticipantMetadataChanged(Room&, const ParticipantMetadataChangedEvent&) {} /** * Called when a participant's name is changed. */ - virtual void onParticipantNameChanged(Room &, - const ParticipantNameChangedEvent &) {} + virtual void onParticipantNameChanged(Room&, const ParticipantNameChangedEvent&) {} /** * Called when a participant's attributes are updated. */ - virtual void - onParticipantAttributesChanged(Room &, - const ParticipantAttributesChangedEvent &) {} + virtual void onParticipantAttributesChanged(Room&, const ParticipantAttributesChangedEvent&) {} /** * Called when a participant's encryption status changes. */ - virtual void onParticipantEncryptionStatusChanged( - Room &, const ParticipantEncryptionStatusChangedEvent &) {} + virtual void onParticipantEncryptionStatusChanged(Room&, const ParticipantEncryptionStatusChangedEvent&) {} // ------------------------------------------------------------------ // Connection quality / state @@ -187,29 +171,27 @@ class RoomDelegate { /** * Called when a participant's connection quality changes. */ - virtual void - onConnectionQualityChanged(Room &, const ConnectionQualityChangedEvent &) {} + virtual void onConnectionQualityChanged(Room&, const ConnectionQualityChangedEvent&) {} /** * Called when the room's connection state changes. */ - virtual void onConnectionStateChanged(Room &, - const ConnectionStateChangedEvent &) {} + virtual void onConnectionStateChanged(Room&, const ConnectionStateChangedEvent&) {} /** * Called when the room is disconnected. */ - virtual void onDisconnected(Room &, const DisconnectedEvent &) {} + virtual void onDisconnected(Room&, const DisconnectedEvent&) {} /** * Called before the SDK attempts to reconnect. */ - virtual void onReconnecting(Room &, const ReconnectingEvent &) {} + virtual void onReconnecting(Room&, const ReconnectingEvent&) {} /** * Called after the SDK successfully reconnects. */ - virtual void onReconnected(Room &, const ReconnectedEvent &) {} + virtual void onReconnected(Room&, const ReconnectedEvent&) {} // ------------------------------------------------------------------ // E2EE @@ -218,7 +200,7 @@ class RoomDelegate { /** * Called when a participant's end-to-end encryption state changes. */ - virtual void onE2eeStateChanged(Room &, const E2eeStateChangedEvent &) {} + virtual void onE2eeStateChanged(Room&, const E2eeStateChangedEvent&) {} // ------------------------------------------------------------------ // EOS @@ -228,7 +210,7 @@ class RoomDelegate { * Called when the room reaches end-of-stream and will not emit further * events. */ - virtual void onRoomEos(Room &, const RoomEosEvent &) {} + virtual void onRoomEos(Room&, const RoomEosEvent&) {} // ------------------------------------------------------------------ // Data / transcription / chat @@ -237,12 +219,12 @@ class RoomDelegate { /** * Called when a user data packet (non-SIP) is received. */ - virtual void onUserPacketReceived(Room &, const UserDataPacketEvent &) {} + virtual void onUserPacketReceived(Room&, const UserDataPacketEvent&) {} /** * Called when a SIP DTMF packet is received. */ - virtual void onSipDtmfReceived(Room &, const SipDtmfReceivedEvent &) {} + virtual void onSipDtmfReceived(Room&, const SipDtmfReceivedEvent&) {} // ------------------------------------------------------------------ // Data streams @@ -251,27 +233,23 @@ class RoomDelegate { /** * Called when a data stream header is received. */ - virtual void - onDataStreamHeaderReceived(Room &, const DataStreamHeaderReceivedEvent &) {} + virtual void onDataStreamHeaderReceived(Room&, const DataStreamHeaderReceivedEvent&) {} /** * Called when a data stream chunk is received. */ - virtual void onDataStreamChunkReceived(Room &, - const DataStreamChunkReceivedEvent &) { - } + virtual void onDataStreamChunkReceived(Room&, const DataStreamChunkReceivedEvent&) {} /** * Called when a data stream trailer is received. */ - virtual void - onDataStreamTrailerReceived(Room &, const DataStreamTrailerReceivedEvent &) {} + virtual void onDataStreamTrailerReceived(Room&, const DataStreamTrailerReceivedEvent&) {} /** * Called when a data channel's buffered amount falls below its low threshold. */ virtual void onDataChannelBufferedAmountLowThresholdChanged( - Room &, const DataChannelBufferedAmountLowThresholdChangedEvent &) {} + Room&, const DataChannelBufferedAmountLowThresholdChangedEvent&) {} // ------------------------------------------------------------------ // High-level byte/text streams @@ -280,12 +258,12 @@ class RoomDelegate { /** * Called when a high-level byte stream reader is opened. */ - virtual void onByteStreamOpened(Room &, const ByteStreamOpenedEvent &) {} + virtual void onByteStreamOpened(Room&, const ByteStreamOpenedEvent&) {} /** * Called when a high-level text stream reader is opened. */ - virtual void onTextStreamOpened(Room &, const TextStreamOpenedEvent &) {} + virtual void onTextStreamOpened(Room&, const TextStreamOpenedEvent&) {} // ------------------------------------------------------------------ // Data tracks @@ -297,13 +275,12 @@ class RoomDelegate { * Data tracks are independent of the audio/video track hierarchy and * require an explicit subscribe() call to start receiving frames. */ - virtual void onDataTrackPublished(Room &, const DataTrackPublishedEvent &) {} + virtual void onDataTrackPublished(Room&, const DataTrackPublishedEvent&) {} /** * Called when a remote participant unpublishes a data track. */ - virtual void onDataTrackUnpublished(Room &, - const DataTrackUnpublishedEvent &) {} + virtual void onDataTrackUnpublished(Room&, const DataTrackUnpublishedEvent&) {} // ------------------------------------------------------------------ // Participants snapshot @@ -312,8 +289,7 @@ class RoomDelegate { /** * Called when a snapshot of participants has been updated. */ - virtual void onParticipantsUpdated(Room &, const ParticipantsUpdatedEvent &) { - } + virtual void onParticipantsUpdated(Room&, const ParticipantsUpdatedEvent&) {} }; } // namespace livekit diff --git a/include/livekit/room_event_types.h b/include/livekit/room_event_types.h index 809f9efc..d30d925b 100644 --- a/include/livekit/room_event_types.h +++ b/include/livekit/room_event_types.h @@ -189,8 +189,7 @@ struct AttributeEntry { AttributeEntry() = default; - AttributeEntry(std::string k, std::string v) - : key(std::move(k)), value(std::move(v)) {} + AttributeEntry(std::string k, std::string v) : key(std::move(k)), value(std::move(v)) {} }; /** @@ -362,7 +361,7 @@ struct TrackPublishOptions { */ struct ParticipantConnectedEvent { /** The newly connected remote participant (owned by Room). */ - RemoteParticipant *participant = nullptr; + RemoteParticipant* participant = nullptr; }; /** @@ -370,7 +369,7 @@ struct ParticipantConnectedEvent { */ struct ParticipantDisconnectedEvent { /** The participant that disconnected (owned by Room). */ - RemoteParticipant *participant = nullptr; + RemoteParticipant* participant = nullptr; /** Reason for the disconnect, if known. */ DisconnectReason reason = DisconnectReason::Unknown; @@ -411,7 +410,7 @@ struct TrackPublishedEvent { std::shared_ptr publication; /** Remote participant who owns this track (owned by Room). */ - RemoteParticipant *participant = nullptr; + RemoteParticipant* participant = nullptr; }; /** @@ -422,7 +421,7 @@ struct TrackUnpublishedEvent { std::shared_ptr publication; /** Remote participant who owned this track (owned by Room). */ - RemoteParticipant *participant = nullptr; + RemoteParticipant* participant = nullptr; }; /** @@ -436,7 +435,7 @@ struct TrackSubscribedEvent { std::shared_ptr publication; /** Remote participant who owns the track (owned by Room). */ - RemoteParticipant *participant = nullptr; + RemoteParticipant* participant = nullptr; }; /** @@ -450,7 +449,7 @@ struct TrackUnsubscribedEvent { std::shared_ptr publication; /** Remote participant who owns the track (owned by Room). */ - RemoteParticipant *participant = nullptr; + RemoteParticipant* participant = nullptr; }; /** @@ -458,7 +457,7 @@ struct TrackUnsubscribedEvent { */ struct TrackSubscriptionFailedEvent { /** Remote participant for which the subscription failed (owned by Room). */ - RemoteParticipant *participant = nullptr; + RemoteParticipant* participant = nullptr; /** SID of the track that failed to subscribe. */ std::string track_sid; @@ -472,7 +471,7 @@ struct TrackSubscriptionFailedEvent { */ struct TrackMutedEvent { /** Local or remote participant who owns the track (owned by Room). */ - Participant *participant = nullptr; + Participant* participant = nullptr; /** Publication that was muted. */ std::shared_ptr publication; @@ -483,7 +482,7 @@ struct TrackMutedEvent { */ struct TrackUnmutedEvent { /** Local or remote participant who owns the track (owned by Room). */ - Participant *participant = nullptr; + Participant* participant = nullptr; /** Publication that was unmuted. */ std::shared_ptr publication; @@ -494,7 +493,7 @@ struct TrackUnmutedEvent { */ struct ActiveSpeakersChangedEvent { /** Participants currently considered active speakers (owned by Room). */ - std::vector speakers; + std::vector speakers; }; /** @@ -521,7 +520,7 @@ struct RoomSidChangedEvent { */ struct ParticipantMetadataChangedEvent { /** Participant whose metadata changed (owned by Room). */ - Participant *participant = nullptr; + Participant* participant = nullptr; /** Old metadata value. */ std::string old_metadata; @@ -535,7 +534,7 @@ struct ParticipantMetadataChangedEvent { */ struct ParticipantNameChangedEvent { /** Participant whose name changed (owned by Room). */ - Participant *participant = nullptr; + Participant* participant = nullptr; /** Previous name. */ std::string old_name; @@ -549,7 +548,7 @@ struct ParticipantNameChangedEvent { */ struct ParticipantAttributesChangedEvent { /** Participant whose attributes changed (owned by Room). */ - Participant *participant = nullptr; + Participant* participant = nullptr; /** Set of attributes that changed (key/value pairs). */ std::vector changed_attributes; @@ -560,7 +559,7 @@ struct ParticipantAttributesChangedEvent { */ struct ParticipantEncryptionStatusChangedEvent { /** Participant whose encryption status changed (owned by Room). */ - Participant *participant = nullptr; + Participant* participant = nullptr; /** True if the participant is now fully encrypted. */ bool is_encrypted = false; @@ -571,7 +570,7 @@ struct ParticipantEncryptionStatusChangedEvent { */ struct ConnectionQualityChangedEvent { /** Participant whose connection quality changed (owned by Room). */ - Participant *participant = nullptr; + Participant* participant = nullptr; /** New connection quality. */ ConnectionQuality quality = ConnectionQuality::Good; @@ -589,7 +588,7 @@ struct UserDataPacketEvent { /** Remote participant that sent this packet, or nullptr if server (owned by * Room). */ - RemoteParticipant *participant = nullptr; + RemoteParticipant* participant = nullptr; /** Optional topic associated with this data (may be empty). */ std::string topic; @@ -606,7 +605,7 @@ struct SipDtmfReceivedEvent { std::string digit; /** Remote participant that sent the DTMF (owned by Room). */ - RemoteParticipant *participant = nullptr; + RemoteParticipant* participant = nullptr; }; /** @@ -727,7 +726,7 @@ struct RoomMovedEvent { */ struct ParticipantsUpdatedEvent { /** Participants updated in this event (owned by Room). */ - std::vector participants; + std::vector participants; }; /** @@ -735,7 +734,7 @@ struct ParticipantsUpdatedEvent { */ struct E2eeStateChangedEvent { /** Local or remote participant whose state changed (owned by Room). */ - Participant *participant = nullptr; + Participant* participant = nullptr; /** New encryption state. */ EncryptionState state = EncryptionState::New; diff --git a/include/livekit/rpc_error.h b/include/livekit/rpc_error.h index 2efb988c..3756907e 100644 --- a/include/livekit/rpc_error.h +++ b/include/livekit/rpc_error.h @@ -87,13 +87,13 @@ class RpcError : public std::runtime_error { /** * Human-readable error message. */ - const std::string &message() const noexcept; + const std::string& message() const noexcept; /** * Optional extra data associated with the error (JSON recommended). * May be an empty string if no data was provided. */ - const std::string &data() const noexcept; + const std::string& data() const noexcept; /** * Create a built-in RpcError using a predefined ErrorCode and default @@ -102,18 +102,18 @@ class RpcError : public std::runtime_error { * @param code Built-in error code. * @param data Optional extra data payload (JSON recommended). */ - static RpcError builtIn(ErrorCode code, const std::string &data = {}); + static RpcError builtIn(ErrorCode code, const std::string& data = {}); protected: // ----- Protected: only used by LocalParticipant (internal SDK code) ----- proto::RpcError toProto() const; - static RpcError fromProto(const proto::RpcError &err); + static RpcError fromProto(const proto::RpcError& err); friend class LocalParticipant; friend class FfiClient; private: - static const char *defaultMessageFor(ErrorCode code); + static const char* defaultMessageFor(ErrorCode code); std::uint32_t code_; std::string message_; diff --git a/include/livekit/stats.h b/include/livekit/stats.h index de56bc05..70a526f8 100644 --- a/include/livekit/stats.h +++ b/include/livekit/stats.h @@ -300,14 +300,14 @@ struct VideoSourceStats { * useful for monitoring audio quality and detecting issues like underruns. */ struct AudioPlayoutStats { - std::string kind; ///< The type of media ("audio"). - double synthesized_samples_duration; ///< Duration of synthesized samples in - ///< seconds. + std::string kind; ///< The type of media ("audio"). + double synthesized_samples_duration; ///< Duration of synthesized samples in + ///< seconds. std::uint32_t synthesized_samples_events; ///< Number of synthesis events ///< (e.g., concealment). - double total_samples_duration; ///< Total duration of all samples in seconds. - double total_playout_delay; ///< Cumulative playout delay in seconds. - std::uint64_t total_samples_count; ///< Total number of samples played out. + double total_samples_duration; ///< Total duration of all samples in seconds. + double total_playout_delay; ///< Cumulative playout delay in seconds. + std::uint64_t total_samples_count; ///< Total number of samples played out. }; struct PeerConnectionStats { @@ -490,11 +490,9 @@ struct RtcStreamStats { // Deprecated Track omitted on purpose. using RtcStatsVariant = - std::variant; struct RtcStats { @@ -505,34 +503,32 @@ struct RtcStats { // fromProto declarations // ---------------------- -RtcStatsData fromProto(const proto::RtcStatsData &); - -CodecStats fromProto(const proto::CodecStats &); -RtpStreamStats fromProto(const proto::RtpStreamStats &); -ReceivedRtpStreamStats fromProto(const proto::ReceivedRtpStreamStats &); -InboundRtpStreamStats fromProto(const proto::InboundRtpStreamStats &); -SentRtpStreamStats fromProto(const proto::SentRtpStreamStats &); -OutboundRtpStreamStats fromProto(const proto::OutboundRtpStreamStats &); -RemoteInboundRtpStreamStats -fromProto(const proto::RemoteInboundRtpStreamStats &); -RemoteOutboundRtpStreamStats -fromProto(const proto::RemoteOutboundRtpStreamStats &); -MediaSourceStats fromProto(const proto::MediaSourceStats &); -AudioSourceStats fromProto(const proto::AudioSourceStats &); -VideoSourceStats fromProto(const proto::VideoSourceStats &); -AudioPlayoutStats fromProto(const proto::AudioPlayoutStats &); -PeerConnectionStats fromProto(const proto::PeerConnectionStats &); -DataChannelStats fromProto(const proto::DataChannelStats &); -TransportStats fromProto(const proto::TransportStats &); -CandidatePairStats fromProto(const proto::CandidatePairStats &); -IceCandidateStats fromProto(const proto::IceCandidateStats &); -CertificateStats fromProto(const proto::CertificateStats &); -StreamStats fromProto(const proto::StreamStats &); +RtcStatsData fromProto(const proto::RtcStatsData&); + +CodecStats fromProto(const proto::CodecStats&); +RtpStreamStats fromProto(const proto::RtpStreamStats&); +ReceivedRtpStreamStats fromProto(const proto::ReceivedRtpStreamStats&); +InboundRtpStreamStats fromProto(const proto::InboundRtpStreamStats&); +SentRtpStreamStats fromProto(const proto::SentRtpStreamStats&); +OutboundRtpStreamStats fromProto(const proto::OutboundRtpStreamStats&); +RemoteInboundRtpStreamStats fromProto(const proto::RemoteInboundRtpStreamStats&); +RemoteOutboundRtpStreamStats fromProto(const proto::RemoteOutboundRtpStreamStats&); +MediaSourceStats fromProto(const proto::MediaSourceStats&); +AudioSourceStats fromProto(const proto::AudioSourceStats&); +VideoSourceStats fromProto(const proto::VideoSourceStats&); +AudioPlayoutStats fromProto(const proto::AudioPlayoutStats&); +PeerConnectionStats fromProto(const proto::PeerConnectionStats&); +DataChannelStats fromProto(const proto::DataChannelStats&); +TransportStats fromProto(const proto::TransportStats&); +CandidatePairStats fromProto(const proto::CandidatePairStats&); +IceCandidateStats fromProto(const proto::IceCandidateStats&); +CertificateStats fromProto(const proto::CertificateStats&); +StreamStats fromProto(const proto::StreamStats&); // High-level: -RtcStats fromProto(const proto::RtcStats &); +RtcStats fromProto(const proto::RtcStats&); // helper if you have repeated RtcStats in proto: -std::vector fromProto(const std::vector &); +std::vector fromProto(const std::vector&); } // namespace livekit diff --git a/include/livekit/subscription_thread_dispatcher.h b/include/livekit/subscription_thread_dispatcher.h index 8e5fa65c..dfe53d32 100644 --- a/include/livekit/subscription_thread_dispatcher.h +++ b/include/livekit/subscription_thread_dispatcher.h @@ -17,9 +17,6 @@ #ifndef LIVEKIT_SUBSCRIPTION_THREAD_DISPATCHER_H #define LIVEKIT_SUBSCRIPTION_THREAD_DISPATCHER_H -#include "livekit/audio_stream.h" -#include "livekit/video_stream.h" - #include #include #include @@ -30,6 +27,9 @@ #include #include +#include "livekit/audio_stream.h" +#include "livekit/video_stream.h" + namespace livekit { class AudioFrame; @@ -40,24 +40,22 @@ class VideoFrame; /// Callback type for incoming audio frames. /// Invoked on a dedicated reader thread per (participant, source) pair. -using AudioFrameCallback = std::function; +using AudioFrameCallback = std::function; /// Callback type for incoming video frames. /// Invoked on a dedicated reader thread per (participant, source) pair. -using VideoFrameCallback = - std::function; +using VideoFrameCallback = std::function; /// Callback type for incoming video frame events. /// Invoked on a dedicated reader thread per (participant, track_name) pair. -using VideoFrameEventCallback = std::function; +using VideoFrameEventCallback = std::function; /// Callback type for incoming data track frames. /// Invoked on a dedicated reader thread per subscription. /// @param payload Raw binary data received. /// @param user_timestamp Optional application-defined timestamp from sender. using DataFrameCallback = - std::function &payload, - std::optional user_timestamp)>; + std::function& payload, std::optional user_timestamp)>; /// Opaque identifier returned by addOnDataFrameCallback, used to remove an /// individual subscription via removeOnDataFrameCallback. @@ -106,9 +104,8 @@ class SubscriptionThreadDispatcher { * @param opts Options used when creating the backing * \ref AudioStream. */ - void setOnAudioFrameCallback(const std::string &participant_identity, - TrackSource source, AudioFrameCallback callback, - const AudioStream::Options &opts = {}); + void setOnAudioFrameCallback(const std::string& participant_identity, TrackSource source, AudioFrameCallback callback, + const AudioStream::Options& opts = {}); /** * Register or replace an audio frame callback for a remote subscription. @@ -123,10 +120,8 @@ class SubscriptionThreadDispatcher { * @param opts Options used when creating the backing * \ref AudioStream. */ - void setOnAudioFrameCallback(const std::string &participant_identity, - const std::string &track_name, - AudioFrameCallback callback, - const AudioStream::Options &opts = {}); + void setOnAudioFrameCallback(const std::string& participant_identity, const std::string& track_name, + AudioFrameCallback callback, const AudioStream::Options& opts = {}); /** * Register or replace a video frame callback for a remote subscription. @@ -141,9 +136,8 @@ class SubscriptionThreadDispatcher { * @param opts Options used when creating the backing * \ref VideoStream. */ - void setOnVideoFrameCallback(const std::string &participant_identity, - TrackSource source, VideoFrameCallback callback, - const VideoStream::Options &opts = {}); + void setOnVideoFrameCallback(const std::string& participant_identity, TrackSource source, VideoFrameCallback callback, + const VideoStream::Options& opts = {}); /** * Register or replace a video frame callback for a remote subscription. @@ -158,10 +152,8 @@ class SubscriptionThreadDispatcher { * @param opts Options used when creating the backing * \ref VideoStream. */ - void setOnVideoFrameCallback(const std::string &participant_identity, - const std::string &track_name, - VideoFrameCallback callback, - const VideoStream::Options &opts = {}); + void setOnVideoFrameCallback(const std::string& participant_identity, const std::string& track_name, + VideoFrameCallback callback, const VideoStream::Options& opts = {}); /** * Register or replace a rich video frame event callback for a remote @@ -178,10 +170,8 @@ class SubscriptionThreadDispatcher { * @param opts Options used when creating the backing * \ref VideoStream. */ - void setOnVideoFrameEventCallback(const std::string &participant_identity, - const std::string &track_name, - VideoFrameEventCallback callback, - const VideoStream::Options &opts = {}); + void setOnVideoFrameEventCallback(const std::string& participant_identity, const std::string& track_name, + VideoFrameEventCallback callback, const VideoStream::Options& opts = {}); /** * Remove an audio callback registration and stop any active reader. @@ -192,8 +182,7 @@ class SubscriptionThreadDispatcher { * @param participant_identity Identity of the remote participant. * @param source Track source to clear. */ - void clearOnAudioFrameCallback(const std::string &participant_identity, - TrackSource source); + void clearOnAudioFrameCallback(const std::string& participant_identity, TrackSource source); /** * Remove an audio callback registration and stop any active reader. @@ -204,8 +193,7 @@ class SubscriptionThreadDispatcher { * @param participant_identity Identity of the remote participant. * @param track_name Track name to clear. */ - void clearOnAudioFrameCallback(const std::string &participant_identity, - const std::string &track_name); + void clearOnAudioFrameCallback(const std::string& participant_identity, const std::string& track_name); /** * Remove a video callback registration and stop any active reader. @@ -216,8 +204,7 @@ class SubscriptionThreadDispatcher { * @param participant_identity Identity of the remote participant. * @param source Track source to clear. */ - void clearOnVideoFrameCallback(const std::string &participant_identity, - TrackSource source); + void clearOnVideoFrameCallback(const std::string& participant_identity, TrackSource source); /** * Remove a video callback registration and stop any active reader. @@ -228,8 +215,7 @@ class SubscriptionThreadDispatcher { * @param participant_identity Identity of the remote participant. * @param track_name Track name to clear. */ - void clearOnVideoFrameCallback(const std::string &participant_identity, - const std::string &track_name); + void clearOnVideoFrameCallback(const std::string& participant_identity, const std::string& track_name); /** * Start or restart reader dispatch for a newly subscribed remote track. @@ -245,9 +231,8 @@ class SubscriptionThreadDispatcher { * @param source Track source associated with the subscription. * @param track Subscribed remote track to read from. */ - void handleTrackSubscribed(const std::string &participant_identity, - TrackSource source, const std::string &track_name, - const std::shared_ptr &track); + void handleTrackSubscribed(const std::string& participant_identity, TrackSource source, const std::string& track_name, + const std::shared_ptr& track); /** * Stop reader dispatch for an unsubscribed remote track. @@ -261,9 +246,8 @@ class SubscriptionThreadDispatcher { * @param source Track source associated with the subscription. * @param track_name Track name associated with the subscription. */ - void handleTrackUnsubscribed(const std::string &participant_identity, - TrackSource source, - const std::string &track_name); + void handleTrackUnsubscribed(const std::string& participant_identity, TrackSource source, + const std::string& track_name); // --------------------------------------------------------------- // Data track callbacks @@ -286,10 +270,8 @@ class SubscriptionThreadDispatcher { * @return An opaque ID that can later be passed to * removeOnDataFrameCallback() to tear down this subscription. */ - DataFrameCallbackId - addOnDataFrameCallback(const std::string &participant_identity, - const std::string &track_name, - DataFrameCallback callback); + DataFrameCallbackId addOnDataFrameCallback(const std::string& participant_identity, const std::string& track_name, + DataFrameCallback callback); /** * Remove a data frame callback previously registered via @@ -310,7 +292,7 @@ class SubscriptionThreadDispatcher { * * @param track The newly published remote data track. */ - void handleDataTrackPublished(const std::shared_ptr &track); + void handleDataTrackPublished(const std::shared_ptr& track); /** * Notify the dispatcher that a remote data track has been unpublished. @@ -320,7 +302,7 @@ class SubscriptionThreadDispatcher { * * @param sid The SID of the unpublished data track. */ - void handleDataTrackUnpublished(const std::string &sid); + void handleDataTrackUnpublished(const std::string& sid); /** * Stop all readers and clear all callback registrations. @@ -341,15 +323,14 @@ class SubscriptionThreadDispatcher { TrackSource source; std::string track_name; - bool operator==(const CallbackKey &o) const { - return participant_identity == o.participant_identity && - source == o.source && track_name == o.track_name; + bool operator==(const CallbackKey& o) const { + return participant_identity == o.participant_identity && source == o.source && track_name == o.track_name; } }; /// Hash function for \ref CallbackKey so it can be used in unordered maps. struct CallbackKeyHash { - std::size_t operator()(const CallbackKey &k) const { + std::size_t operator()(const CallbackKey& k) const { auto h1 = std::hash{}(k.participant_identity); auto h2 = std::hash{}(static_cast(k.source)); auto h3 = std::hash{}(k.track_name); @@ -369,15 +350,14 @@ class SubscriptionThreadDispatcher { std::string participant_identity; std::string track_name; - bool operator==(const DataCallbackKey &o) const { - return participant_identity == o.participant_identity && - track_name == o.track_name; + bool operator==(const DataCallbackKey& o) const { + return participant_identity == o.participant_identity && track_name == o.track_name; } }; /// Hash function for \ref DataCallbackKey. struct DataCallbackKeyHash { - std::size_t operator()(const DataCallbackKey &k) const { + std::size_t operator()(const DataCallbackKey& k) const { auto h1 = std::hash{}(k.participant_identity); auto h2 = std::hash{}(k.track_name); return h1 ^ (h2 << 1); @@ -415,30 +395,26 @@ class SubscriptionThreadDispatcher { /// /// Must be called with \ref lock_ held. The returned thread, if joinable, /// must be joined after releasing the lock. - std::thread extractReaderThreadLocked(const CallbackKey &key); + std::thread extractReaderThreadLocked(const CallbackKey& key); /// Select the appropriate reader startup path for \p track. /// /// Must be called with \ref lock_ held. - std::thread startReaderLocked(const CallbackKey &key, - const std::shared_ptr &track); + std::thread startReaderLocked(const CallbackKey& key, const std::shared_ptr& track); /// Start an audio reader thread for \p key using \p track. /// /// Must be called with \ref lock_ held. Any previous reader for the same key /// is extracted and returned to the caller for joining outside the lock. - std::thread startAudioReaderLocked(const CallbackKey &key, - const std::shared_ptr &track, - const AudioFrameCallback &cb, - const AudioStream::Options &opts); + std::thread startAudioReaderLocked(const CallbackKey& key, const std::shared_ptr& track, + const AudioFrameCallback& cb, const AudioStream::Options& opts); /// Start a video reader thread for \p key using \p track. /// /// Must be called with \ref lock_ held. Any previous reader for the same key /// is extracted and returned to the caller for joining outside the lock. - std::thread startVideoReaderLocked(const CallbackKey &key, - const std::shared_ptr &track, - const RegisteredVideoCallback &callback); + std::thread startVideoReaderLocked(const CallbackKey& key, const std::shared_ptr& track, + const RegisteredVideoCallback& callback); /// Extract and close the data reader for a given callback ID, returning its /// thread. Must be called with \ref lock_ held. @@ -446,45 +422,36 @@ class SubscriptionThreadDispatcher { /// Extract and close the data reader for a given (participant, track_name) /// key, returning its thread. Must be called with \ref lock_ held. - std::thread extractDataReaderThreadLocked(const DataCallbackKey &key); + std::thread extractDataReaderThreadLocked(const DataCallbackKey& key); /// Start a data reader thread for the given callback ID, key, and track. /// Must be called with \ref lock_ held. - std::thread - startDataReaderLocked(DataFrameCallbackId id, const DataCallbackKey &key, - const std::shared_ptr &track, - const DataFrameCallback &cb); + std::thread startDataReaderLocked(DataFrameCallbackId id, const DataCallbackKey& key, + const std::shared_ptr& track, const DataFrameCallback& cb); /// Protects callback registration maps and active reader state. mutable std::mutex lock_; /// Registered audio frame callbacks keyed by \ref CallbackKey. - std::unordered_map - audio_callbacks_; + std::unordered_map audio_callbacks_; /// Registered video frame callbacks keyed by \ref CallbackKey. - std::unordered_map - video_callbacks_; + std::unordered_map video_callbacks_; /// Active stream/thread state keyed by \ref CallbackKey. - std::unordered_map - active_readers_; + std::unordered_map active_readers_; /// Next auto-increment ID for data frame callbacks. DataFrameCallbackId next_data_callback_id_{0}; /// Registered data frame callbacks keyed by opaque callback ID. - std::unordered_map - data_callbacks_; + std::unordered_map data_callbacks_; /// Active data reader threads keyed by callback ID. - std::unordered_map> - active_data_readers_; + std::unordered_map> active_data_readers_; /// Currently published remote data tracks, keyed by (participant, name). - std::unordered_map, - DataCallbackKeyHash> - remote_data_tracks_; + std::unordered_map, DataCallbackKeyHash> remote_data_tracks_; /// Hard limit on concurrently active per-subscription reader threads. static constexpr int kMaxActiveReaders = 20; diff --git a/include/livekit/tracing.h b/include/livekit/tracing.h index 0434a4d9..1477bac6 100644 --- a/include/livekit/tracing.h +++ b/include/livekit/tracing.h @@ -35,8 +35,7 @@ namespace livekit { * categories. * @return true if tracing was started, false if already running or file error */ -bool startTracing(const std::string &trace_file_path, - const std::vector &categories = {}); +bool startTracing(const std::string& trace_file_path, const std::vector& categories = {}); /** * Stop tracing and flush remaining events to file. diff --git a/include/livekit/track.h b/include/livekit/track.h index 6d1e98c4..fd89656b 100644 --- a/include/livekit/track.h +++ b/include/livekit/track.h @@ -15,8 +15,6 @@ */ #pragma once -#include "livekit/ffi_handle.h" -#include "livekit/stats.h" #include #include #include @@ -25,6 +23,9 @@ #include #include +#include "livekit/ffi_handle.h" +#include "livekit/stats.h" + namespace livekit { class LocalTrackPublication; @@ -73,8 +74,8 @@ class Track { virtual ~Track() = default; // Read-only properties - const std::string &sid() const noexcept { return sid_; } - const std::string &name() const noexcept { return name_; } + const std::string& sid() const noexcept { return sid_; } + const std::string& name() const noexcept { return name_; } TrackKind kind() const noexcept { return kind_; } StreamState stream_state() const noexcept { return state_; } bool muted() const noexcept { return muted_; } @@ -85,7 +86,8 @@ class Track { std::optional simulcasted() const noexcept { return simulcasted_; } std::optional width() const noexcept { return width_; } std::optional height() const noexcept { return height_; } - // std::string can actually throw, suppressing for now to maintain API compatibility + // std::string can actually throw, suppressing for now to maintain API + // compatibility // NOLINTNEXTLINE(bugprone-exception-escape) std::optional mime_type() const noexcept { return mime_type_; } @@ -98,10 +100,7 @@ class Track { /// After publishing a local track, associates the \ref LocalTrackPublication /// with this track. Default implementation is a no-op (e.g. remote tracks). - virtual void setPublication( - const std::shared_ptr &publication) noexcept { - (void)publication; - } + virtual void setPublication(const std::shared_ptr& publication) noexcept { (void)publication; } // Internal updates (called by Room) void setStreamState(StreamState s) noexcept { state_ = s; } @@ -109,13 +108,11 @@ class Track { void setName(std::string n) noexcept { name_ = std::move(n); } protected: - Track(FfiHandle handle, std::string sid, std::string name, TrackKind kind, - StreamState state, bool muted, bool remote); + Track(FfiHandle handle, std::string sid, std::string name, TrackKind kind, StreamState state, bool muted, + bool remote); - void setPublicationFields(std::optional source, - std::optional simulcasted, - std::optional width, - std::optional height, + void setPublicationFields(std::optional source, std::optional simulcasted, + std::optional width, std::optional height, std::optional mime_type); private: diff --git a/include/livekit/track_publication.h b/include/livekit/track_publication.h index 5d0ff47f..abed3a27 100644 --- a/include/livekit/track_publication.h +++ b/include/livekit/track_publication.h @@ -41,44 +41,37 @@ class TrackPublication { public: virtual ~TrackPublication() = default; - TrackPublication(const TrackPublication &) = delete; - TrackPublication &operator=(const TrackPublication &) = delete; - TrackPublication(TrackPublication &&) noexcept = default; - TrackPublication &operator=(TrackPublication &&) noexcept = default; + TrackPublication(const TrackPublication&) = delete; + TrackPublication& operator=(const TrackPublication&) = delete; + TrackPublication(TrackPublication&&) noexcept = default; + TrackPublication& operator=(TrackPublication&&) noexcept = default; // Basic metadata - const std::string &sid() const noexcept { return sid_; } - const std::string &name() const noexcept { return name_; } + const std::string& sid() const noexcept { return sid_; } + const std::string& name() const noexcept { return name_; } TrackKind kind() const noexcept { return kind_; } TrackSource source() const noexcept { return source_; } bool simulcasted() const noexcept { return simulcasted_; } std::uint32_t width() const noexcept { return width_; } std::uint32_t height() const noexcept { return height_; } - const std::string &mimeType() const noexcept { return mime_type_; } + const std::string& mimeType() const noexcept { return mime_type_; } bool muted() const noexcept { return muted_; } void setMuted(bool muted) noexcept { muted_ = muted; } EncryptionType encryptionType() const noexcept { return encryption_type_; } - const std::vector &audioFeatures() const noexcept { - return audio_features_; - } + const std::vector& audioFeatures() const noexcept { return audio_features_; } /// Underlying FFI handle value. uintptr_t ffiHandleId() const noexcept { return handle_.get(); } /// Associated Track (if attached). std::shared_ptr track() const noexcept { return track_; } - void setTrack(const std::shared_ptr &track) noexcept { - track_ = track; - } + void setTrack(const std::shared_ptr& track) noexcept { track_ = track; } protected: - TrackPublication(FfiHandle handle, std::string sid, std::string name, - TrackKind kind, TrackSource source, bool simulcasted, - std::uint32_t width, std::uint32_t height, - std::string mime_type, bool muted, - EncryptionType encryption_type, - std::vector audio_features); + TrackPublication(FfiHandle handle, std::string sid, std::string name, TrackKind kind, TrackSource source, + bool simulcasted, std::uint32_t width, std::uint32_t height, std::string mime_type, bool muted, + EncryptionType encryption_type, std::vector audio_features); FfiHandle handle_; std::shared_ptr track_; diff --git a/include/livekit/video_frame.h b/include/livekit/video_frame.h index d9632f30..ecdae9a3 100644 --- a/include/livekit/video_frame.h +++ b/include/livekit/video_frame.h @@ -24,19 +24,7 @@ namespace livekit { // Mirror of WebRTC video buffer type -enum class VideoBufferType { - RGBA = 0, - ABGR, - ARGB, - BGRA, - RGB24, - I420, - I420A, - I422, - I444, - I010, - NV12 -}; +enum class VideoBufferType { RGBA = 0, ABGR, ARGB, BGRA, RGB24, I420, I420A, I422, I444, I010, NV12 }; struct VideoPlaneInfo { std::uintptr_t data_ptr; // pointer to plane data (for FFI) @@ -59,14 +47,13 @@ class OwnedVideoBuffer; class VideoFrame { public: VideoFrame(); - VideoFrame(int width, int height, VideoBufferType type, - std::vector data); + VideoFrame(int width, int height, VideoBufferType type, std::vector data); virtual ~VideoFrame() = default; - VideoFrame(const VideoFrame &) = delete; - VideoFrame &operator=(const VideoFrame &) = delete; - VideoFrame(VideoFrame &&) noexcept = default; - VideoFrame &operator=(VideoFrame &&) noexcept = default; + VideoFrame(const VideoFrame&) = delete; + VideoFrame& operator=(const VideoFrame&) = delete; + VideoFrame(VideoFrame&&) noexcept = default; + VideoFrame& operator=(VideoFrame&&) noexcept = default; /** * Allocate a new frame with the correct buffer size for the given format. @@ -79,8 +66,8 @@ class VideoFrame { int height() const noexcept { return height_; } VideoBufferType type() const noexcept { return type_; } - std::uint8_t *data() noexcept { return data_.data(); } - const std::uint8_t *data() const noexcept { return data_.data(); } + std::uint8_t* data() noexcept { return data_.data(); } + const std::uint8_t* data() const noexcept { return data_.data(); } std::size_t dataSize() const noexcept { return data_.size(); } /** @@ -122,7 +109,7 @@ class VideoFrame { friend class VideoStream; // Only internal classes (e.g., VideoStream) // should construct frames directly from FFI buffers. - static VideoFrame fromOwnedInfo(const proto::OwnedVideoBuffer &owned); + static VideoFrame fromOwnedInfo(const proto::OwnedVideoBuffer& owned); private: int width_; diff --git a/include/livekit/video_source.h b/include/livekit/video_source.h index c015a437..e0d8544f 100644 --- a/include/livekit/video_source.h +++ b/include/livekit/video_source.h @@ -76,10 +76,10 @@ class VideoSource { VideoSource(int width, int height); virtual ~VideoSource() = default; - VideoSource(const VideoSource &) = delete; - VideoSource &operator=(const VideoSource &) = delete; - VideoSource(VideoSource &&) noexcept = default; - VideoSource &operator=(VideoSource &&) noexcept = default; + VideoSource(const VideoSource&) = delete; + VideoSource& operator=(const VideoSource&) = delete; + VideoSource(VideoSource&&) noexcept = default; + VideoSource& operator=(VideoSource&&) noexcept = default; /// Source resolution as declared at construction. int width() const noexcept { return width_; } @@ -94,13 +94,12 @@ class VideoSource { * @param frame Video frame to send. * @param options Timestamp, rotation, and optional metadata for this frame. */ - void captureFrame(const VideoFrame &frame, - const VideoCaptureOptions &options); + void captureFrame(const VideoFrame& frame, const VideoCaptureOptions& options); /** * Backward-compatible convenience overload for timestamp + rotation only. */ - void captureFrame(const VideoFrame &frame, std::int64_t timestamp_us = 0, + void captureFrame(const VideoFrame& frame, std::int64_t timestamp_us = 0, VideoRotation rotation = VideoRotation::VIDEO_ROTATION_0); private: diff --git a/include/livekit/video_stream.h b/include/livekit/video_stream.h index 850b5038..2ac9aca6 100644 --- a/include/livekit/video_stream.h +++ b/include/livekit/video_stream.h @@ -79,20 +79,18 @@ class VideoStream { }; // Factory: create a VideoStream bound to a specific Track - static std::shared_ptr - fromTrack(const std::shared_ptr &track, const Options &options); + static std::shared_ptr fromTrack(const std::shared_ptr& track, const Options& options); // Factory: create a VideoStream from a Participant + TrackSource - static std::shared_ptr fromParticipant(Participant &participant, - TrackSource track_source, - const Options &options); + static std::shared_ptr fromParticipant(Participant& participant, TrackSource track_source, + const Options& options); virtual ~VideoStream(); - VideoStream(const VideoStream &) = delete; - VideoStream &operator=(const VideoStream &) = delete; - VideoStream(VideoStream &&) noexcept; - VideoStream &operator=(VideoStream &&) noexcept; + VideoStream(const VideoStream&) = delete; + VideoStream& operator=(const VideoStream&) = delete; + VideoStream(VideoStream&&) noexcept; + VideoStream& operator=(VideoStream&&) noexcept; /// Blocking read: waits until a VideoFrameEvent is available in the internal /// queue, or the stream reaches EOS / is closed. @@ -100,7 +98,7 @@ class VideoStream { /// \param out On success, filled with the next video frame event. /// \return true if a frame was delivered; false if the stream ended /// (end-of-stream or close()) and no more data is available. - bool read(VideoFrameEvent &out); + bool read(VideoFrameEvent& out); /// Signal that we are no longer interested in video frames. /// @@ -113,16 +111,14 @@ class VideoStream { VideoStream() = default; // Internal init helpers, used by the factories - void initFromTrack(const std::shared_ptr &track, - const Options &options); - void initFromParticipant(Participant &participant, TrackSource source, - const Options &options); + void initFromTrack(const std::shared_ptr& track, const Options& options); + void initFromParticipant(Participant& participant, TrackSource source, const Options& options); // FFI event handler (registered with FfiClient) - void onFfiEvent(const proto::FfiEvent &event); + void onFfiEvent(const proto::FfiEvent& event); // Queue helpers - void pushFrame(VideoFrameEvent &&ev); + void pushFrame(VideoFrameEvent&& ev); void pushEos(); mutable std::mutex mutex_; diff --git a/scripts/clang-format.sh b/scripts/clang-format.sh new file mode 100755 index 00000000..9210ecfd --- /dev/null +++ b/scripts/clang-format.sh @@ -0,0 +1,495 @@ +#!/usr/bin/env bash +# +# 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. + +# +# clang-format.sh -- Run clang-format locally and in CI using the same file +# set and config. Picks up style from the repo-root .clang-format +# automatically. +# +# Usage (from anywhere; the script self-anchors to the repo root): +# ./scripts/clang-format.sh # check the full src/ tree +# ./scripts/clang-format.sh --fix # apply formatting in place +# ./scripts/clang-format.sh src/room.cpp src/foo.h # check just these files +# ./scripts/clang-format.sh --fix src/room.cpp # fix just this file +# ./scripts/clang-format.sh --github-actions # force CI annotation mode +# +# Default mode is "check" (clang-format --dry-run --Werror): no files are +# modified and the script exits non-zero if any file diverges from +# .clang-format. Pass --fix / -i to apply formatting in place instead. +# +# Every run prints a concise stdout summary at the end with the number of +# files checked / fixed (and the list of offending paths when checking). +# +# In GitHub Actions (auto-detected via $GITHUB_ACTIONS=true, or forced with +# --github-actions), this script additionally: +# - Emits ::error workflow commands so violations appear as PR file +# annotations (red). +# - Writes a markdown summary to $GITHUB_STEP_SUMMARY listing every file +# that needs formatting, linkified to github.com when possible. +# +# Exit code: +# - 0 when every file is already formatted (check mode) or when --fix +# completed successfully. +# - 1 when at least one file needs formatting (check mode only). +# - The clang-format / xargs exit code on any other failure (missing tool, +# unreadable file, etc.). + +set -euo pipefail + +# Anchor every relative path (clang-format.log, git ls-files, etc.) to the +# repo root regardless of how/where this script is invoked. Without this, +# calling the script from a subdirectory or via an absolute path would break +# git ls-files scoping and the log path below. +script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +cd "${script_dir}/.." + +# Usage banner. Printed by --help and referenced by the EXIT trap below so +# users who hit a pre-flight error or a bad argument get pointed at this. +usage() { + cat <<'EOF' +Usage: ./scripts/clang-format.sh [OPTIONS] [FILE...] + +Run clang-format locally or in CI using the repo-root .clang-format. Defaults +to check-only (dry-run); pass --fix to rewrite files in place. + +Options: + -h, --help, -? + Show this help and exit. + --fix, -i + Apply formatting in place. Without this, the script runs in check + mode (--dry-run --Werror) and exits non-zero on any divergence. + --github-actions, --gh + Force GitHub Actions annotation + step-summary mode. + Auto-detected when GITHUB_ACTIONS=true. + +Positional arguments: + FILE... + Explicit list of files to format / check. When omitted, the script + walks the tracked first-party C/C++ trees (src/, include/, and + benchmarks/) and operates on every source / header it finds. Pass + paths to make the script work as a precommit hook on a curated set + of files, e.g.: + git diff --cached --name-only --diff-filter=ACMR \ + | grep -E '\.(c|cc|cpp|cxx|h|hpp|hxx)$' \ + | xargs ./scripts/clang-format.sh --fix + +Examples: + ./scripts/clang-format.sh # check src/, include/, benchmarks/ + ./scripts/clang-format.sh --fix # fix the same trees + ./scripts/clang-format.sh src/room.cpp include/livekit/room.h + # check just these files + ./scripts/clang-format.sh --fix src/room.cpp # fix just this file + ./scripts/clang-format.sh --github-actions # force CI annotations + +Pre-requisites: + clang-format must be installed and on PATH: + brew install clang-format # macOS + apt install clang-format-19 # Linux (or any version >= 11) +EOF +} + +# Print a one-line "see --help" hint on any non-zero exit during pre-flight +# (argument parsing, missing tool, etc.). Suppressed once we hand off to +# clang-format itself, so legitimate formatting violations don't trigger +# the hint. +__fmt_hint_active=1 +__fmt_print_hint() { + local rc=$? + if (( rc != 0 )) && (( __fmt_hint_active )); then + echo >&2 + echo "Run './scripts/clang-format.sh --help' for usage." >&2 + fi +} +trap __fmt_print_hint EXIT + +CI_MODE=0 +# Automatically detect CI mode if in GitHub Actions environment. +if [[ "${GITHUB_ACTIONS:-}" == "true" ]]; then + CI_MODE=1 +fi + +# Default to check (dry-run) mode; --fix flips to in-place rewriting. +FIX_MODE=0 + +explicit_files=() +while (($#)); do + case "$1" in + -h|--help|-\?) + usage + __fmt_hint_active=0 + exit 0 + ;; + --fix|-i) + FIX_MODE=1 + shift + ;; + --github-actions|--gh) + CI_MODE=1 + shift + ;; + --) + # Explicit positional separator: everything after `--` is a file path. + shift + explicit_files+=("$@") + break + ;; + --*|-*) + # Long / short options we don't recognize are user typos far more often + # than legitimate clang-format flags. Reject and surface usage. + echo "ERROR: unknown option: $1" >&2 + usage >&2 + exit 2 + ;; + *) + explicit_files+=("$1") + shift + ;; + esac +done + +if ! command -v clang-format >/dev/null 2>&1; then + echo "ERROR: clang-format not found in PATH." >&2 + echo "Install: brew install clang-format (macOS)" >&2 + echo " apt install clang-format-19 (Linux)" >&2 + exit 1 +fi + +# Surface the version in CI logs so future regressions tied to a specific +# clang-format release are easy to bisect. +clang-format --version + +# Build the list of files to operate on. When the user passes explicit paths +# we honor them verbatim so a precommit hook can supply its own staged-file +# list. Otherwise we walk the tracked first-party C/C++ trees: +# - src/ (implementation + internal headers, including src/tests/) +# - include/ (public API headers shipped in the installed SDK) +# - benchmarks/ (in-tree micro-benchmarks) +# Using `git ls-files` automatically skips the client-sdk-rust/ submodule, +# build-*/, _deps/, local-install/, vcpkg_installed/, etc., without having +# to maintain a lookahead exclusion regex. The deprecated bridge/ tree and +# the empty examples/ tree are intentionally left out -- bridge/ has its +# own conventions (it's frozen pending removal), and examples/ has no +# tracked C/C++ files today. +files=() +if (( ${#explicit_files[@]} > 0 )); then + files=("${explicit_files[@]}") +else + while IFS= read -r -d '' path; do + files+=("${path}") + done < <(git ls-files -z \ + 'src/*.c' 'src/*.cc' 'src/*.cpp' 'src/*.cxx' \ + 'src/*.h' 'src/*.hpp' 'src/*.hxx' \ + 'include/*.h' 'include/*.hpp' 'include/*.hxx' \ + 'benchmarks/*.c' 'benchmarks/*.cc' 'benchmarks/*.cpp' 'benchmarks/*.cxx' \ + 'benchmarks/*.h' 'benchmarks/*.hpp' 'benchmarks/*.hxx') +fi + +file_count=${#files[@]} +if (( file_count == 0 )); then + echo "clang-format: no files to process." + __fmt_hint_active=0 + exit 0 +fi + +# Capture clang-format's combined stdout+stderr to a stable, repo-local path +# so it can be re-parsed after the run (for annotations and the step summary) +# and re-read by the user afterwards. `*.log` is gitignored so this file +# never gets committed. +log="clang-format.log" +: > "${log}" + +# Past pre-flight: any non-zero exit from here on is a clang-format result +# (violations, internal error, etc.), not a user-facing argument/usage error, +# so suppress the "see --help" hint installed via the EXIT trap above. +__fmt_hint_active=0 + +# -------- Begin GitHub Actions annotations -------- + +# Emit GitHub Actions workflow commands for each clang-format diagnostic line +# in the given log. Source / caret lines are skipped; only the +# `path:line:col: error: ... [-Wclang-format-violations]` lines become +# annotations. Severity is always ::error because --Werror promotes every +# violation to an error and we treat any divergence as a hard CI failure. +emit_annotations() { + local log_file="$1" + local workspace="${GITHUB_WORKSPACE:-${PWD}}" + local line path lineno col message rel_path + + while IFS= read -r line; do + [[ "${line}" =~ ^(.+):([0-9]+):([0-9]+):[[:space:]]+(error|warning):[[:space:]]+(.+)[[:space:]]\[-Wclang-format-violations\][[:space:]]*$ ]] || continue + path="${BASH_REMATCH[1]}" + lineno="${BASH_REMATCH[2]}" + col="${BASH_REMATCH[3]}" + message="${BASH_REMATCH[5]}" + + rel_path="${path#${workspace}/}" + + message="${message//$'%'/%25}" + message="${message//$'\r'/%0D}" + message="${message//$'\n'/%0A}" + + printf '::error file=%s,line=%s,col=%s,title=clang-format::%s\n' \ + "${rel_path}" "${lineno}" "${col}" "${message}" + done < "${log_file}" +} + +# Append a markdown summary (count + per-file list) to $GITHUB_STEP_SUMMARY so +# the GitHub job page surfaces every offending file without scanning the raw +# log. Each file is linkified to github.com when we can resolve a blob URL. +write_step_summary() { + local log_file="$1" + local summary_file="${GITHUB_STEP_SUMMARY:-}" + [[ -n "${summary_file}" ]] || return 0 + + local workspace="${GITHUB_WORKSPACE:-${PWD}}" + local files_tsv + files_tsv="$(mktemp -t fmt-files.XXXXXX)" + + # Extract first violation line per file as path\tline. Multiple violations + # in one file collapse to a single row -- clicking through to the file is + # enough; the developer fixes them with `clang-format -i` regardless. + local sline spath slineno + declare -A seen=() + while IFS= read -r sline; do + [[ "${sline}" =~ ^(.+):([0-9]+):([0-9]+):[[:space:]]+(error|warning):[[:space:]]+(.+)[[:space:]]\[-Wclang-format-violations\][[:space:]]*$ ]] || continue + spath="${BASH_REMATCH[1]#${workspace}/}" + slineno="${BASH_REMATCH[2]}" + if [[ -z "${seen[${spath}]:-}" ]]; then + seen[${spath}]=1 + printf '%s\t%s\n' "${spath}" "${slineno}" >> "${files_tsv}" + fi + done < "${log_file}" + + local violation_files + violation_files=$(wc -l < "${files_tsv}" | tr -d ' ') + + # Resolve a blob URL prefix so files in the table become clickable links. + # Prefer FORMAT_BLOB_SHA (set by the workflow to the PR head SHA) over + # GITHUB_SHA -- on pull_request events GITHUB_SHA points at the ephemeral + # refs/pull/N/merge commit, whose blob URLs stop resolving once the PR + # closes. On push / workflow_dispatch / schedule runs FORMAT_BLOB_SHA is + # unset and we fall through to GITHUB_SHA, which is the pushed / selected + # commit respectively. + local repo_url="" + if [[ -n "${GITHUB_SERVER_URL:-}" && -n "${GITHUB_REPOSITORY:-}" ]]; then + local blob_sha="${FORMAT_BLOB_SHA:-${GITHUB_SHA:-}}" + if [[ -n "${blob_sha}" ]]; then + repo_url="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/blob/${blob_sha}" + fi + fi + + { + echo "## clang-format results" + echo + if (( violation_files == 0 )); then + echo ":white_check_mark: All files are properly formatted." + else + echo ":x: ${violation_files} file(s) need formatting." + echo + echo "
Files needing formatting" + echo + echo '| File |' + echo '|------|' + while IFS=$'\t' read -r path lineno; do + local file_cell + if [[ -n "${repo_url}" && "${path}" != /* ]]; then + file_cell="[\`${path}\`](${repo_url}/${path}#L${lineno})" + else + file_cell="\`${path}\`" + fi + printf '| %s |\n' "${file_cell}" + done < "${files_tsv}" + echo + echo "
" + echo + echo "Run \`./scripts/clang-format.sh --fix\` locally to apply formatting." + fi + } >> "${summary_file}" + + rm -f "${files_tsv}" +} + +# Print a one-line summary plus the offending file list to stdout. Always +# runs (regardless of CI_MODE) so local invocations get the same headline +# view the GitHub step summary provides. Sets a global consumed by the +# exit-code logic below: __FMT_VIOLATION_FILES. +print_stdout_summary() { + local log_file="$1" + local total="$2" + local files_tsv + files_tsv="$(mktemp -t fmt-stdout.XXXXXX)" + + local line spath + declare -A seen=() + while IFS= read -r line; do + [[ "${line}" =~ ^(.+):([0-9]+):([0-9]+):[[:space:]]+(error|warning):[[:space:]]+(.+)[[:space:]]\[-Wclang-format-violations\][[:space:]]*$ ]] || continue + spath="${BASH_REMATCH[1]}" + if [[ -z "${seen[${spath}]:-}" ]]; then + seen[${spath}]=1 + echo "${spath}" >> "${files_tsv}" + fi + done < "${log_file}" + + local violation_files + violation_files=$(wc -l < "${files_tsv}" | tr -d ' ') + + echo "------------------------------------------------------------" + if (( violation_files == 0 )); then + printf 'clang-format summary: clean (%d file(s) checked)\n' "${total}" + else + printf 'clang-format summary: %d of %d file(s) need formatting\n' \ + "${violation_files}" "${total}" + echo " files:" + while IFS= read -r f; do + printf ' %s\n' "${f}" + done < "${files_tsv}" + echo + echo " Run './scripts/clang-format.sh --fix' to apply formatting." + fi + echo "------------------------------------------------------------" + + rm -f "${files_tsv}" + __FMT_VIOLATION_FILES="${violation_files}" +} + +# --------- End GitHub Actions annotations --------- + +# Run clang-format. We run serially (no -P parallelism) so per-file +# diagnostics never interleave -- clang-format itself is fast enough +# (~1-2 ms per file) that a few hundred files still complete in well +# under a second, and CI workflow startup dwarfs the runtime regardless. +# `xargs` (without -P) still chunks the file list so we don't blow past +# ARG_MAX when the tree grows: each invocation gets as many files as fit +# in one command line, and successive invocations append to the log in +# discovery order. +# +# In fix mode we snapshot file content hashes before and after invoking +# clang-format so the summary can report only the files that *actually +# changed* (vs. the entire scanned set). `git hash-object --stdin-paths` +# hashes every input path in a single git invocation, sidestepping the +# portability headache of sha1sum (Linux) vs shasum (macOS) vs cksum +# (POSIX, but only 32-bit) and getting us per-file SHA-1s in one shot. +__hash_files() { + if (( ${#files[@]} == 0 )); then + return + fi + printf '%s\n' "${files[@]}" | git hash-object --stdin-paths +} + +pre_hashes=() +if (( FIX_MODE == 1 )); then + while IFS= read -r __h; do + pre_hashes+=("${__h}") + done < <(__hash_files) +fi + +set +e +if (( FIX_MODE == 1 )); then + printf '%s\0' "${files[@]}" \ + | xargs -0 clang-format -i \ + >"${log}" 2>&1 + rc=$? +else + printf '%s\0' "${files[@]}" \ + | xargs -0 clang-format --dry-run --Werror \ + >"${log}" 2>&1 + rc=$? +fi +set -e + +# Identify which files actually changed on disk during the fix pass by +# rehashing and comparing against the pre-run snapshot. Files where the +# hash matches were no-ops (already conformant) and are excluded from +# the summary count. Order is preserved (git hash-object emits hashes in +# input order) so the i-th post-hash corresponds to the i-th file. +changed_files=() +if (( FIX_MODE == 1 )); then + post_hashes=() + while IFS= read -r __h; do + post_hashes+=("${__h}") + done < <(__hash_files) + for i in "${!files[@]}"; do + if [[ "${pre_hashes[$i]:-}" != "${post_hashes[$i]:-}" ]]; then + changed_files+=("${files[$i]}") + fi + done +fi + +# Mirror the captured log to stdout so users see violations inline. In fix +# mode there's nothing useful in the log (clang-format -i writes silently), +# so skip the cat to keep the output focused on the summary below. +if (( FIX_MODE == 0 )); then + cat "${log}" +fi + +# CI annotations / step summary only make sense for the check-mode log, +# which contains the violation diagnostics. Fix mode has already resolved +# them, so emitting annotations would be misleading. +if [[ "${CI_MODE}" == "1" ]] && (( FIX_MODE == 0 )); then + emit_annotations "${log}" + write_step_summary "${log}" +fi + +# Always emit the concise headline summary to stdout. Sets +# __FMT_VIOLATION_FILES which the exit-code logic below consumes. +# print_stdout_summary reads the log, so this must happen before the +# conditional log cleanup below. +__FMT_VIOLATION_FILES=0 +if (( FIX_MODE == 1 )); then + echo "------------------------------------------------------------" + if (( ${#changed_files[@]} == 0 )); then + printf 'clang-format summary: clean (0 of %d file(s) needed formatting)\n' \ + "${file_count}" + else + printf 'clang-format summary: formatted %d of %d file(s)\n' \ + "${#changed_files[@]}" "${file_count}" + echo " files:" + for __cf in "${changed_files[@]}"; do + printf ' %s\n' "${__cf}" + done + fi + echo "------------------------------------------------------------" +else + print_stdout_summary "${log}" "${file_count}" +fi + +# Only advertise the log when it actually has content -- a clean check run +# and a no-op fix run both leave it empty, and the summary banner above +# already conveys "nothing to see". Drop the empty file so a stale log +# from a previous dirty run doesn't linger and confuse the next reader. +if [[ -s "${log}" ]]; then + echo "Results written to: $(pwd)/${log}" +else + rm -f "${log}" +fi + +# Exit-code policy: +# - In --fix mode, propagate clang-format's own exit code (non-zero means +# a real failure such as a missing file, not "needed reformatting"). +# - In check mode, treat xargs's "child exited 1-125" status (123) as a +# formatting violation -> exit 1. Any other non-zero status is a real +# failure (missing file, IO error, etc.) and we propagate it as-is. +if (( FIX_MODE == 1 )); then + exit "${rc}" +fi + +if (( rc == 0 )); then + exit 0 +elif (( rc == 123 )) || (( __FMT_VIOLATION_FILES > 0 )); then + exit 1 +else + exit "${rc}" +fi diff --git a/scripts/clang-tidy.sh b/scripts/clang-tidy.sh index edcca5b9..fdbc43b2 100755 --- a/scripts/clang-tidy.sh +++ b/scripts/clang-tidy.sh @@ -504,14 +504,25 @@ if [[ "${CI_MODE}" == "1" ]]; then write_step_summary "${log}" fi -echo "Results written to: $(cd "$(dirname "${log}")" && pwd)/$(basename "${log}")" - # Always emit the concise headline summary to stdout. Sets __TIDY_WARNINGS / -# __TIDY_ERRORS globals which the strict-mode escalation below consumes. +# __TIDY_ERRORS globals which the strict-mode escalation and the log +# advertisement below consume. __TIDY_WARNINGS=0 __TIDY_ERRORS=0 print_stdout_summary "${log}" +# Only advertise the log when the run actually produced findings. A clean +# run still leaves per-file progress lines (`[N/M][T.Ts] /path/...`) in the +# file, but that's transient noise -- delete it so a stale log from a +# previous dirty run doesn't linger and confuse the next reader. CI +# annotations and the step summary above have already been emitted from +# the in-flight log, so cleanup here is purely about post-run state. +if (( __TIDY_WARNINGS > 0 || __TIDY_ERRORS > 0 )); then + echo "Results written to: $(cd "$(dirname "${log}")" && pwd)/$(basename "${log}")" +else + rm -f "${log}" +fi + # Strict mode: any warning escalates to a non-zero exit. Errors already make # run-clang-tidy itself exit non-zero (rc != 0), so this only changes the # warnings-only case. Used by CI to gate merges on a clean clang-tidy result. diff --git a/src/audio_frame.cpp b/src/audio_frame.cpp index dc27136d..a9e4544c 100644 --- a/src/audio_frame.cpp +++ b/src/audio_frame.cpp @@ -28,19 +28,17 @@ namespace livekit { -AudioFrame::AudioFrame() - : sample_rate_(0), num_channels_(0), samples_per_channel_(0) {} +AudioFrame::AudioFrame() : sample_rate_(0), num_channels_(0), samples_per_channel_(0) {} -AudioFrame::AudioFrame(std::vector data, int sample_rate, - int num_channels, int samples_per_channel) - : data_(std::move(data)), sample_rate_(sample_rate), - num_channels_(num_channels), samples_per_channel_(samples_per_channel) { - const std::size_t expected = static_cast(num_channels_) * - static_cast(samples_per_channel_); +AudioFrame::AudioFrame(std::vector data, int sample_rate, int num_channels, int samples_per_channel) + : data_(std::move(data)), + sample_rate_(sample_rate), + num_channels_(num_channels), + samples_per_channel_(samples_per_channel) { + const std::size_t expected = static_cast(num_channels_) * static_cast(samples_per_channel_); if (data_.size() < expected) { - throw std::invalid_argument( - "AudioFrame: data size must be >= num_channels * samples_per_channel"); + throw std::invalid_argument("AudioFrame: data size must be >= num_channels * samples_per_channel"); } if (data_.size() % expected != 0) { throw std::invalid_argument( @@ -49,32 +47,27 @@ AudioFrame::AudioFrame(std::vector data, int sample_rate, } } -AudioFrame AudioFrame::create(int sample_rate, int num_channels, - int samples_per_channel) { - const std::size_t count = static_cast(num_channels) * - static_cast(samples_per_channel); +AudioFrame AudioFrame::create(int sample_rate, int num_channels, int samples_per_channel) { + const std::size_t count = static_cast(num_channels) * static_cast(samples_per_channel); std::vector data(count, 0); - return AudioFrame(std::move(data), sample_rate, num_channels, - samples_per_channel); + return AudioFrame(std::move(data), sample_rate, num_channels, samples_per_channel); } -AudioFrame -AudioFrame::fromOwnedInfo(const proto::OwnedAudioFrameBuffer &owned) { - const auto &info = owned.info(); +AudioFrame AudioFrame::fromOwnedInfo(const proto::OwnedAudioFrameBuffer& owned) { + const auto& info = owned.info(); const int num_channels = static_cast(info.num_channels()); const int samples_per_channel = static_cast(info.samples_per_channel()); const int sample_rate = static_cast(info.sample_rate()); - const std::size_t count = static_cast(num_channels) * - static_cast(samples_per_channel); + const std::size_t count = static_cast(num_channels) * static_cast(samples_per_channel); - const std::int16_t *ptr = - reinterpret_cast(info.data_ptr()); // NOLINT(performance-no-int-to-ptr) + const std::int16_t* ptr = + // NOLINTNEXTLINE(performance-no-int-to-ptr) + reinterpret_cast(info.data_ptr()); if (ptr == nullptr && count > 0) { - throw std::runtime_error( - "AudioFrame::fromOwnedInfo: null data_ptr with nonzero size"); + throw std::runtime_error("AudioFrame::fromOwnedInfo: null data_ptr with nonzero size"); } std::vector data; @@ -88,20 +81,17 @@ AudioFrame::fromOwnedInfo(const proto::OwnedAudioFrameBuffer &owned) { // drop the OwnedAudioFrameBuffer. } - return AudioFrame(std::move(data), sample_rate, num_channels, - samples_per_channel); + return AudioFrame(std::move(data), sample_rate, num_channels, samples_per_channel); } proto::AudioFrameBufferInfo AudioFrame::toProto() const { proto::AudioFrameBufferInfo info; - const std::uint64_t ptr = - data_.empty() ? 0 : reinterpret_cast(data_.data()); + const std::uint64_t ptr = data_.empty() ? 0 : reinterpret_cast(data_.data()); info.set_data_ptr(ptr); info.set_num_channels(static_cast(num_channels_)); info.set_sample_rate(static_cast(sample_rate_)); - info.set_samples_per_channel( - static_cast(samples_per_channel_)); + info.set_samples_per_channel(static_cast(samples_per_channel_)); return info; } @@ -109,17 +99,14 @@ double AudioFrame::duration() const noexcept { if (sample_rate_ <= 0) { return 0.0; } - return static_cast(samples_per_channel_) / - static_cast(sample_rate_); + return static_cast(samples_per_channel_) / static_cast(sample_rate_); } std::string AudioFrame::to_string() const { std::ostringstream oss; - oss << "rtc.AudioFrame(sample_rate=" << sample_rate_ - << ", num_channels=" << num_channels_ - << ", samples_per_channel=" << samples_per_channel_ - << ", duration=" << std::fixed << std::setprecision(3) << duration() - << ")"; + oss << "rtc.AudioFrame(sample_rate=" << sample_rate_ << ", num_channels=" << num_channels_ + << ", samples_per_channel=" << samples_per_channel_ << ", duration=" << std::fixed << std::setprecision(3) + << duration() << ")"; return oss.str(); } diff --git a/src/audio_processing_module.cpp b/src/audio_processing_module.cpp index ff802891..601043bc 100644 --- a/src/audio_processing_module.cpp +++ b/src/audio_processing_module.cpp @@ -24,12 +24,11 @@ namespace livekit { -AudioProcessingModule::AudioProcessingModule() - : AudioProcessingModule(Options{}) {} +AudioProcessingModule::AudioProcessingModule() : AudioProcessingModule(Options{}) {} -AudioProcessingModule::AudioProcessingModule(const Options &options) { +AudioProcessingModule::AudioProcessingModule(const Options& options) { proto::FfiRequest req; - auto *msg = req.mutable_new_apm(); + auto* msg = req.mutable_new_apm(); msg->set_echo_canceller_enabled(options.echo_cancellation); msg->set_noise_suppression_enabled(options.noise_suppression); msg->set_high_pass_filter_enabled(options.high_pass_filter); @@ -38,20 +37,18 @@ AudioProcessingModule::AudioProcessingModule(const Options &options) { const proto::FfiResponse resp = FfiClient::instance().sendRequest(req); if (!resp.has_new_apm()) { - throw std::runtime_error( - "AudioProcessingModule: failed to create APM - no response"); + throw std::runtime_error("AudioProcessingModule: failed to create APM - no response"); } - const auto &apm_info = resp.new_apm().apm(); + const auto& apm_info = resp.new_apm().apm(); handle_ = FfiHandle(static_cast(apm_info.handle().id())); if (!handle_.valid()) { - throw std::runtime_error( - "AudioProcessingModule: failed to create APM - invalid handle"); + throw std::runtime_error("AudioProcessingModule: failed to create APM - invalid handle"); } } -void AudioProcessingModule::processStream(AudioFrame &frame) { +void AudioProcessingModule::processStream(AudioFrame& frame) { if (!handle_.valid()) { throw std::runtime_error("AudioProcessingModule: invalid handle"); } @@ -61,29 +58,26 @@ void AudioProcessingModule::processStream(AudioFrame &frame) { } proto::FfiRequest req; - auto *msg = req.mutable_apm_process_stream(); + auto* msg = req.mutable_apm_process_stream(); msg->set_apm_handle(static_cast(handle_.get())); msg->set_data_ptr(reinterpret_cast(frame.data().data())); - msg->set_size( - static_cast(frame.data().size() * sizeof(std::int16_t))); + msg->set_size(static_cast(frame.data().size() * sizeof(std::int16_t))); msg->set_sample_rate(static_cast(frame.sample_rate())); msg->set_num_channels(static_cast(frame.num_channels())); const proto::FfiResponse resp = FfiClient::instance().sendRequest(req); if (!resp.has_apm_process_stream()) { - throw std::runtime_error( - "AudioProcessingModule::processStream: unexpected response"); + throw std::runtime_error("AudioProcessingModule::processStream: unexpected response"); } - const auto &result = resp.apm_process_stream(); + const auto& result = resp.apm_process_stream(); if (result.has_error()) { - throw std::runtime_error("AudioProcessingModule::processStream: " + - result.error()); + throw std::runtime_error("AudioProcessingModule::processStream: " + result.error()); } } -void AudioProcessingModule::processReverseStream(AudioFrame &frame) { +void AudioProcessingModule::processReverseStream(AudioFrame& frame) { if (!handle_.valid()) { throw std::runtime_error("AudioProcessingModule: invalid handle"); } @@ -93,25 +87,22 @@ void AudioProcessingModule::processReverseStream(AudioFrame &frame) { } proto::FfiRequest req; - auto *msg = req.mutable_apm_process_reverse_stream(); + auto* msg = req.mutable_apm_process_reverse_stream(); msg->set_apm_handle(static_cast(handle_.get())); msg->set_data_ptr(reinterpret_cast(frame.data().data())); - msg->set_size( - static_cast(frame.data().size() * sizeof(std::int16_t))); + msg->set_size(static_cast(frame.data().size() * sizeof(std::int16_t))); msg->set_sample_rate(static_cast(frame.sample_rate())); msg->set_num_channels(static_cast(frame.num_channels())); const proto::FfiResponse resp = FfiClient::instance().sendRequest(req); if (!resp.has_apm_process_reverse_stream()) { - throw std::runtime_error( - "AudioProcessingModule::processReverseStream: unexpected response"); + throw std::runtime_error("AudioProcessingModule::processReverseStream: unexpected response"); } - const auto &result = resp.apm_process_reverse_stream(); + const auto& result = resp.apm_process_reverse_stream(); if (result.has_error()) { - throw std::runtime_error("AudioProcessingModule::processReverseStream: " + - result.error()); + throw std::runtime_error("AudioProcessingModule::processReverseStream: " + result.error()); } } @@ -121,21 +112,19 @@ void AudioProcessingModule::setStreamDelayMs(int delay_ms) { } proto::FfiRequest req; - auto *msg = req.mutable_apm_set_stream_delay(); + auto* msg = req.mutable_apm_set_stream_delay(); msg->set_apm_handle(static_cast(handle_.get())); msg->set_delay_ms(delay_ms); const proto::FfiResponse resp = FfiClient::instance().sendRequest(req); if (!resp.has_apm_set_stream_delay()) { - throw std::runtime_error( - "AudioProcessingModule::setStreamDelayMs: unexpected response"); + throw std::runtime_error("AudioProcessingModule::setStreamDelayMs: unexpected response"); } - const auto &result = resp.apm_set_stream_delay(); + const auto& result = resp.apm_set_stream_delay(); if (result.has_error()) { - throw std::runtime_error("AudioProcessingModule::setStreamDelayMs: " + - result.error()); + throw std::runtime_error("AudioProcessingModule::setStreamDelayMs: " + result.error()); } } diff --git a/src/audio_source.cpp b/src/audio_source.cpp index 63fcd07d..2a05655e 100644 --- a/src/audio_source.cpp +++ b/src/audio_source.cpp @@ -41,10 +41,9 @@ static double now_seconds() { // ============================================================================ AudioSource::AudioSource(int sample_rate, int num_channels, int queue_size_ms) - : sample_rate_(sample_rate), num_channels_(num_channels), - queue_size_ms_(queue_size_ms) { + : sample_rate_(sample_rate), num_channels_(num_channels), queue_size_ms_(queue_size_ms) { proto::FfiRequest req; - auto *msg = req.mutable_new_audio_source(); + auto* msg = req.mutable_new_audio_source(); msg->set_type(proto::AudioSourceType::AUDIO_SOURCE_NATIVE); msg->set_sample_rate(static_cast(sample_rate_)); msg->set_num_channels(static_cast(num_channels_)); @@ -52,7 +51,7 @@ AudioSource::AudioSource(int sample_rate, int num_channels, int queue_size_ms) const proto::FfiResponse resp = FfiClient::instance().sendRequest(req); - const auto &source_info = resp.new_audio_source().source(); + const auto& source_info = resp.new_audio_source().source(); // Wrap FFI handle in RAII FfiHandle handle_ = FfiHandle(static_cast(source_info.handle().id())); } @@ -80,7 +79,7 @@ void AudioSource::clearQueue() { } proto::FfiRequest req; - auto *msg = req.mutable_clear_audio_buffer(); + auto* msg = req.mutable_clear_audio_buffer(); msg->set_source_handle(static_cast(handle_.get())); (void)FfiClient::instance().sendRequest(req); @@ -89,7 +88,7 @@ void AudioSource::clearQueue() { resetQueueTracking(); } -void AudioSource::captureFrame(const AudioFrame &frame, int timeout_ms) { +void AudioSource::captureFrame(const AudioFrame& frame, int timeout_ms) { using namespace std::chrono_literals; if (!handle_) { return; @@ -102,8 +101,7 @@ void AudioSource::captureFrame(const AudioFrame &frame, int timeout_ms) { // Queue tracking, same logic as before const double now = now_seconds(); const double elapsed = (last_capture_ == 0.0) ? 0.0 : (now - last_capture_); - const double frame_duration = static_cast(frame.samples_per_channel()) / - static_cast(sample_rate_); + const double frame_duration = static_cast(frame.samples_per_channel()) / static_cast(sample_rate_); q_size_ += frame_duration - elapsed; if (q_size_ < 0.0) { q_size_ = 0.0; // clamp @@ -120,8 +118,7 @@ void AudioSource::captureFrame(const AudioFrame &frame, int timeout_ms) { } // This will throw std::runtime_error if the callback reported an error auto status = fut.wait_for(std::chrono::milliseconds(timeout_ms)); - if (status == std::future_status::ready || - status == std::future_status::deferred) { + if (status == std::future_status::ready || status == std::future_status::deferred) { fut.get(); } else { // std::future_status::timeout LK_LOG_WARN("captureAudioFrameAsync timed out after {} ms", timeout_ms); diff --git a/src/audio_stream.cpp b/src/audio_stream.cpp index 07b1e693..ae27761d 100644 --- a/src/audio_stream.cpp +++ b/src/audio_stream.cpp @@ -32,17 +32,14 @@ using proto::FfiRequest; // Factory helpers // ------------------------ -std::shared_ptr -AudioStream::fromTrack(const std::shared_ptr &track, - const Options &options) { +std::shared_ptr AudioStream::fromTrack(const std::shared_ptr& track, const Options& options) { auto stream = std::shared_ptr(new AudioStream()); stream->initFromTrack(track, options); return stream; } -std::shared_ptr -AudioStream::fromParticipant(Participant &participant, TrackSource track_source, - const Options &options) { +std::shared_ptr AudioStream::fromParticipant(Participant& participant, TrackSource track_source, + const Options& options) { auto stream = std::shared_ptr(new AudioStream()); stream->initFromParticipant(participant, track_source, options); return stream; @@ -54,7 +51,7 @@ AudioStream::fromParticipant(Participant &participant, TrackSource track_source, AudioStream::~AudioStream() { close(); } -AudioStream::AudioStream(AudioStream &&other) noexcept { +AudioStream::AudioStream(AudioStream&& other) noexcept { const std::scoped_lock lock(other.mutex_); queue_ = std::move(other.queue_); capacity_ = other.capacity_; @@ -68,7 +65,7 @@ AudioStream::AudioStream(AudioStream &&other) noexcept { other.closed_ = true; } -AudioStream &AudioStream::operator=(AudioStream &&other) noexcept { +AudioStream& AudioStream::operator=(AudioStream&& other) noexcept { if (this == &other) { return *this; } @@ -94,7 +91,7 @@ AudioStream &AudioStream::operator=(AudioStream &&other) noexcept { return *this; } -bool AudioStream::read(AudioFrameEvent &out_event) { +bool AudioStream::read(AudioFrameEvent& out_event) { std::unique_lock lock(mutex_); cv_.wait(lock, [this] { return !queue_.empty() || eof_ || closed_; }); @@ -134,20 +131,17 @@ void AudioStream::close() { // Internal functions -void AudioStream::initFromTrack(const std::shared_ptr &track, - const Options &options) { +void AudioStream::initFromTrack(const std::shared_ptr& track, const Options& options) { capacity_ = options.capacity; options_ = options; // 1) Subscribe to FFI events - listener_id_ = FfiClient::instance().AddListener( - [this](const FfiEvent &e) { this->onFfiEvent(e); }); + listener_id_ = FfiClient::instance().AddListener([this](const FfiEvent& e) { this->onFfiEvent(e); }); // 2) Send FfiRequest to create a new audio stream bound to this track FfiRequest req; - auto *new_audio_stream = req.mutable_new_audio_stream(); - new_audio_stream->set_track_handle( - static_cast(track->ffi_handle_id())); + auto* new_audio_stream = req.mutable_new_audio_stream(); + new_audio_stream->set_track_handle(static_cast(track->ffi_handle_id())); // TODO, sample_rate and num_channels are not useful in AudioStream, remove it // from FFI. // new_audio_stream->set_sample_rate(options_.sample_rate); @@ -155,32 +149,27 @@ void AudioStream::initFromTrack(const std::shared_ptr &track, new_audio_stream->set_type(proto::AudioStreamType::AUDIO_STREAM_NATIVE); if (!options_.noise_cancellation_module.empty()) { - new_audio_stream->set_audio_filter_module_id( - options_.noise_cancellation_module); + new_audio_stream->set_audio_filter_module_id(options_.noise_cancellation_module); // Always set options JSON even if empty - backend will treat empty string // as "no options" - new_audio_stream->set_audio_filter_options( - options_.noise_cancellation_options_json); + new_audio_stream->set_audio_filter_options(options_.noise_cancellation_options_json); } auto resp = FfiClient::instance().sendRequest(req); - const auto &stream = resp.new_audio_stream().stream(); + const auto& stream = resp.new_audio_stream().stream(); stream_handle_ = FfiHandle(static_cast(stream.handle().id())); } -void AudioStream::initFromParticipant(Participant &participant, - TrackSource track_source, - const Options &options) { +void AudioStream::initFromParticipant(Participant& participant, TrackSource track_source, const Options& options) { capacity_ = options.capacity; options_ = options; // 1) Subscribe to FFI events - listener_id_ = FfiClient::instance().AddListener( - [this](const FfiEvent &e) { this->onFfiEvent(e); }); + listener_id_ = FfiClient::instance().AddListener([this](const FfiEvent& e) { this->onFfiEvent(e); }); // 2) Send FfiRequest to create audio stream from participant + track source FfiRequest req; - auto *as = req.mutable_audio_stream_from_participant(); + auto* as = req.mutable_audio_stream_from_participant(); as->set_participant_handle(participant.ffiHandleId()); // TODO, sample_rate and num_channels are not useful in AudioStream, remove it // from FFI. @@ -197,21 +186,21 @@ void AudioStream::initFromParticipant(Participant &participant, } auto resp = FfiClient::instance().sendRequest(req); - const auto &stream = resp.audio_stream_from_participant().stream(); + const auto& stream = resp.audio_stream_from_participant().stream(); stream_handle_ = FfiHandle(static_cast(stream.handle().id())); } -void AudioStream::onFfiEvent(const FfiEvent &event) { +void AudioStream::onFfiEvent(const FfiEvent& event) { if (event.message_case() != FfiEvent::kAudioStreamEvent) { return; } - const auto &ase = event.audio_stream_event(); + const auto& ase = event.audio_stream_event(); // Check if this event is for our stream handle. if (ase.stream_handle() != static_cast(stream_handle_.get())) { return; } if (ase.has_frame_received()) { - const auto &fr = ase.frame_received(); + const auto& fr = ase.frame_received(); AudioFrameEvent ev{AudioFrame::fromOwnedInfo(fr.frame())}; pushFrame(std::move(ev)); } else if (ase.has_eos()) { @@ -219,7 +208,7 @@ void AudioStream::onFfiEvent(const FfiEvent &event) { } } -void AudioStream::pushFrame(AudioFrameEvent &&ev) { +void AudioStream::pushFrame(AudioFrameEvent&& ev) { { const std::scoped_lock lock(mutex_); diff --git a/src/data_stream.cpp b/src/data_stream.cpp index 9f846a27..d75a6c37 100644 --- a/src/data_stream.cpp +++ b/src/data_stream.cpp @@ -36,7 +36,7 @@ std::string generateRandomId(std::size_t bytes = 16) { std::string out; out.reserve(bytes * 2); - const char *hex = "0123456789abcdef"; + const char* hex = "0123456789abcdef"; for (std::size_t i = 0; i < bytes; ++i) { const int v = dist(rng); out.push_back(hex[(v >> 4) & 0xF]); @@ -46,11 +46,11 @@ std::string generateRandomId(std::size_t bytes = 16) { } // Split UTF-8 string into chunks of at most max_bytes, not breaking codepoints. -std::vector splitUtf8(const std::string &s, - std::size_t max_bytes) { +std::vector splitUtf8(const std::string& s, std::size_t max_bytes) { std::vector result; - if (s.empty()) + if (s.empty()) { return result; + } std::size_t i = 0; const std::size_t n = s.size(); @@ -60,13 +60,11 @@ std::vector splitUtf8(const std::string &s, // If end points into a UTF-8 continuation byte, walk back to a lead byte. // NOTE: end is an index into the string; ensure end < n before indexing. - while (end > i && end < n && - (static_cast(s[end]) & 0xC0) == 0x80) { + while (end > i && end < n && (static_cast(s[end]) & 0xC0) == 0x80) { --end; } // If end==n, we may still have landed on a continuation byte due to min(). - while (end > i && end == n && - (static_cast(s[end - 1]) & 0xC0) == 0x80) { + while (end > i && end == n && (static_cast(s[end - 1]) & 0xC0) == 0x80) { // Walk back until we hit a lead byte boundary. // This isn't perfect but is consistent with your earlier intent. --end; @@ -85,11 +83,9 @@ std::vector splitUtf8(const std::string &s, return result; } -void fillBaseInfo(BaseStreamInfo &dst, const std::string &stream_id, - const std::string &mime_type, const std::string &topic, - std::int64_t timestamp_ms, - const std::optional &total_size, - const std::map &attrs) { +void fillBaseInfo(BaseStreamInfo& dst, const std::string& stream_id, const std::string& mime_type, + const std::string& topic, std::int64_t timestamp_ms, const std::optional& total_size, + const std::map& attrs) { dst.stream_id = stream_id; dst.mime_type = mime_type; dst.topic = topic; @@ -104,24 +100,23 @@ void fillBaseInfo(BaseStreamInfo &dst, const std::string &stream_id, // Reader implementation // ===================================================================== -TextStreamReader::TextStreamReader(TextStreamInfo info) - : info_(std::move(info)) {} +TextStreamReader::TextStreamReader(TextStreamInfo info) : info_(std::move(info)) {} -void TextStreamReader::onChunkUpdate(const std::string &text) { +void TextStreamReader::onChunkUpdate(const std::string& text) { { const std::scoped_lock lock(mutex_); - if (closed_) + if (closed_) { return; + } queue_.push_back(text); } cv_.notify_one(); } -void TextStreamReader::onStreamClose( - const std::map &trailer_attrs) { +void TextStreamReader::onStreamClose(const std::map& trailer_attrs) { { const std::scoped_lock lock(mutex_); - for (const auto &kv : trailer_attrs) { + for (const auto& kv : trailer_attrs) { info_.attributes[kv.first] = kv.second; } closed_ = true; @@ -129,7 +124,7 @@ void TextStreamReader::onStreamClose( cv_.notify_all(); } -bool TextStreamReader::readNext(std::string &out) { +bool TextStreamReader::readNext(std::string& out) { std::unique_lock lock(mutex_); cv_.wait(lock, [this] { return !queue_.empty() || closed_; }); @@ -144,29 +139,29 @@ bool TextStreamReader::readNext(std::string &out) { std::string TextStreamReader::readAll() { std::string result; std::string chunk; - while (readNext(chunk)) + while (readNext(chunk)) { result += chunk; + } return result; } -ByteStreamReader::ByteStreamReader(ByteStreamInfo info) - : info_(std::move(info)) {} +ByteStreamReader::ByteStreamReader(ByteStreamInfo info) : info_(std::move(info)) {} -void ByteStreamReader::onChunkUpdate(const std::vector &bytes) { +void ByteStreamReader::onChunkUpdate(const std::vector& bytes) { { const std::scoped_lock lock(mutex_); - if (closed_) + if (closed_) { return; + } queue_.push_back(bytes); } cv_.notify_one(); } -void ByteStreamReader::onStreamClose( - const std::map &trailer_attrs) { +void ByteStreamReader::onStreamClose(const std::map& trailer_attrs) { { const std::scoped_lock lock(mutex_); - for (const auto &kv : trailer_attrs) { + for (const auto& kv : trailer_attrs) { info_.attributes[kv.first] = kv.second; } closed_ = true; @@ -174,7 +169,7 @@ void ByteStreamReader::onStreamClose( cv_.notify_all(); } -bool ByteStreamReader::readNext(std::vector &out) { +bool ByteStreamReader::readNext(std::vector& out) { std::unique_lock lock(mutex_); cv_.wait(lock, [this] { return !queue_.empty() || closed_; }); @@ -190,21 +185,19 @@ bool ByteStreamReader::readNext(std::vector &out) { // Writer implementation (uses your future-based FfiClient) // ===================================================================== -BaseStreamWriter::BaseStreamWriter( - LocalParticipant &local_participant, std::string topic, - std::map attributes, - std::string stream_id, std::optional total_size, - std::string mime_type, - std::vector destination_identities, - std::string sender_identity) +BaseStreamWriter::BaseStreamWriter(LocalParticipant& local_participant, std::string topic, + std::map attributes, std::string stream_id, + std::optional total_size, std::string mime_type, + std::vector destination_identities, std::string sender_identity) : local_participant_(local_participant), - stream_id_(stream_id.empty() ? generateRandomId() - : std::move(stream_id)), - mime_type_(std::move(mime_type)), topic_(std::move(topic)), - timestamp_ms_(std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()) - .count()), - total_size_(total_size), attributes_(std::move(attributes)), + stream_id_(stream_id.empty() ? generateRandomId() : std::move(stream_id)), + mime_type_(std::move(mime_type)), + topic_(std::move(topic)), + timestamp_ms_( + std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()) + .count()), + total_size_(total_size), + attributes_(std::move(attributes)), destination_identities_(std::move(destination_identities)), sender_identity_(std::move(sender_identity)) { if (sender_identity_.empty()) { @@ -213,8 +206,9 @@ BaseStreamWriter::BaseStreamWriter( } void BaseStreamWriter::ensureHeaderSent() { - if (header_sent_) + if (header_sent_) { return; + } proto::DataStream::Header header; header.set_stream_id(stream_id_); header.set_timestamp(timestamp_ms_); @@ -224,13 +218,12 @@ void BaseStreamWriter::ensureHeaderSent() { if (total_size_.has_value()) { header.set_total_length(static_cast(*total_size_)); } - for (const auto &kv : attributes_) { + for (const auto& kv : attributes_) { (*header.mutable_attributes())[kv.first] = kv.second; } if (kind_ == StreamKind::kText) { - auto *th = header.mutable_text_header(); - th->set_operation_type( - proto::DataStream::OperationType::DataStream_OperationType_CREATE); + auto* th = header.mutable_text_header(); + th->set_operation_type(proto::DataStream::OperationType::DataStream_OperationType_CREATE); if (!reply_to_id_.empty()) { th->set_reply_to_stream_id(reply_to_id_); } @@ -239,15 +232,15 @@ void BaseStreamWriter::ensureHeaderSent() { } FfiClient::instance() - .sendStreamHeaderAsync(local_participant_.ffiHandleId(), header, - destination_identities_, sender_identity_) + .sendStreamHeaderAsync(local_participant_.ffiHandleId(), header, destination_identities_, sender_identity_) .get(); header_sent_ = true; } -void BaseStreamWriter::sendChunk(const std::vector &content) { - if (closed_) +void BaseStreamWriter::sendChunk(const std::vector& content) { + if (closed_) { throw std::runtime_error("Cannot send chunk after stream is closed"); + } ensureHeaderSent(); @@ -257,96 +250,81 @@ void BaseStreamWriter::sendChunk(const std::vector &content) { chunk.set_content(content.data(), content.size()); FfiClient::instance() - .sendStreamChunkAsync(local_participant_.ffiHandleId(), chunk, - destination_identities_, sender_identity_) + .sendStreamChunkAsync(local_participant_.ffiHandleId(), chunk, destination_identities_, sender_identity_) .get(); } -void BaseStreamWriter::sendTrailer( - const std::string &reason, - const std::map &attributes) { +void BaseStreamWriter::sendTrailer(const std::string& reason, const std::map& attributes) { ensureHeaderSent(); proto::DataStream::Trailer trailer; trailer.set_stream_id(stream_id_); trailer.set_reason(reason); - for (const auto &kv : attributes) { + for (const auto& kv : attributes) { (*trailer.mutable_attributes())[kv.first] = kv.second; } - FfiClient::instance() - .sendStreamTrailerAsync(local_participant_.ffiHandleId(), trailer, - sender_identity_) - .get(); + FfiClient::instance().sendStreamTrailerAsync(local_participant_.ffiHandleId(), trailer, sender_identity_).get(); } -void BaseStreamWriter::close( - const std::string &reason, - const std::map &attributes) { - if (closed_) +void BaseStreamWriter::close(const std::string& reason, const std::map& attributes) { + if (closed_) { throw std::runtime_error("Stream already closed"); + } closed_ = true; sendTrailer(reason, attributes); } -TextStreamWriter::TextStreamWriter( - LocalParticipant &local_participant, const std::string &topic, - const std::map &attributes, - const std::string &stream_id, std::optional total_size, - const std::string &reply_to_id, - const std::vector &destination_identities, - const std::string &sender_identity) - : BaseStreamWriter( - local_participant, topic, attributes, stream_id, total_size, - /*mime_type=*/"text/plain", destination_identities, sender_identity) { +TextStreamWriter::TextStreamWriter(LocalParticipant& local_participant, const std::string& topic, + const std::map& attributes, const std::string& stream_id, + std::optional total_size, const std::string& reply_to_id, + const std::vector& destination_identities, + const std::string& sender_identity) + : BaseStreamWriter(local_participant, topic, attributes, stream_id, total_size, + /*mime_type=*/"text/plain", destination_identities, sender_identity) { kind_ = StreamKind::kText; reply_to_id_ = reply_to_id; // ✅ Canonical user-facing metadata comes from BaseStreamWriter fields. - fillBaseInfo(info_, stream_id_, mime_type_, topic_, timestamp_ms_, - total_size_, attributes_); + fillBaseInfo(info_, stream_id_, mime_type_, topic_, timestamp_ms_, total_size_, attributes_); } -void TextStreamWriter::write(const std::string &text) { +void TextStreamWriter::write(const std::string& text) { const std::scoped_lock lock(write_mutex_); - if (closed_) + if (closed_) { throw std::runtime_error("Cannot write to closed TextStreamWriter"); + } - for (const auto &chunk_str : splitUtf8(text, kStreamChunkSize)) { - const auto *p = reinterpret_cast(chunk_str.data()); + for (const auto& chunk_str : splitUtf8(text, kStreamChunkSize)) { + const auto* p = reinterpret_cast(chunk_str.data()); const std::vector bytes(p, p + chunk_str.size()); LK_LOG_DEBUG("sending chunk"); sendChunk(bytes); } } -ByteStreamWriter::ByteStreamWriter( - LocalParticipant &local_participant, const std::string &name, - const std::string &topic, - const std::map &attributes, - const std::string &stream_id, std::optional total_size, - const std::string &mime_type, - const std::vector &destination_identities, - const std::string &sender_identity) - : BaseStreamWriter(local_participant, topic, attributes, stream_id, - total_size, mime_type, destination_identities, +ByteStreamWriter::ByteStreamWriter(LocalParticipant& local_participant, const std::string& name, + const std::string& topic, const std::map& attributes, + const std::string& stream_id, std::optional total_size, + const std::string& mime_type, const std::vector& destination_identities, + const std::string& sender_identity) + : BaseStreamWriter(local_participant, topic, attributes, stream_id, total_size, mime_type, destination_identities, sender_identity) { kind_ = StreamKind::kByte; byte_name_ = name; - fillBaseInfo(info_, stream_id_, mime_type_, topic_, timestamp_ms_, - total_size_, attributes_); + fillBaseInfo(info_, stream_id_, mime_type_, topic_, timestamp_ms_, total_size_, attributes_); info_.name = name; } -void ByteStreamWriter::write(const std::vector &data) { +void ByteStreamWriter::write(const std::vector& data) { const std::scoped_lock lock(write_mutex_); - if (closed_) + if (closed_) { throw std::runtime_error("Cannot write to closed ByteStreamWriter"); + } std::size_t offset = 0; while (offset < data.size()) { - const std::size_t n = - std::min(kStreamChunkSize, data.size() - offset); + const std::size_t n = std::min(kStreamChunkSize, data.size() - offset); auto it = data.begin() + static_cast(offset); const std::vector chunk(it, it + static_cast(n)); sendChunk(chunk); diff --git a/src/data_track_error.cpp b/src/data_track_error.cpp index 9926493c..3b107797 100644 --- a/src/data_track_error.cpp +++ b/src/data_track_error.cpp @@ -24,83 +24,77 @@ namespace { PublishDataTrackErrorCode fromProtoCode(proto::PublishDataTrackErrorCode code) { switch (code) { - case proto::PUBLISH_DATA_TRACK_ERROR_CODE_INVALID_HANDLE: - return PublishDataTrackErrorCode::INVALID_HANDLE; - case proto::PUBLISH_DATA_TRACK_ERROR_CODE_DUPLICATE_NAME: - return PublishDataTrackErrorCode::DUPLICATE_NAME; - case proto::PUBLISH_DATA_TRACK_ERROR_CODE_TIMEOUT: - return PublishDataTrackErrorCode::TIMEOUT; - case proto::PUBLISH_DATA_TRACK_ERROR_CODE_DISCONNECTED: - return PublishDataTrackErrorCode::DISCONNECTED; - case proto::PUBLISH_DATA_TRACK_ERROR_CODE_NOT_ALLOWED: - return PublishDataTrackErrorCode::NOT_ALLOWED; - case proto::PUBLISH_DATA_TRACK_ERROR_CODE_INVALID_NAME: - return PublishDataTrackErrorCode::INVALID_NAME; - case proto::PUBLISH_DATA_TRACK_ERROR_CODE_LIMIT_REACHED: - return PublishDataTrackErrorCode::LIMIT_REACHED; - case proto::PUBLISH_DATA_TRACK_ERROR_CODE_PROTOCOL_ERROR: - return PublishDataTrackErrorCode::PROTOCOL_ERROR; - case proto::PUBLISH_DATA_TRACK_ERROR_CODE_INTERNAL: - return PublishDataTrackErrorCode::INTERNAL; - case proto::PUBLISH_DATA_TRACK_ERROR_CODE_UNKNOWN: - default: - return PublishDataTrackErrorCode::UNKNOWN; + case proto::PUBLISH_DATA_TRACK_ERROR_CODE_INVALID_HANDLE: + return PublishDataTrackErrorCode::INVALID_HANDLE; + case proto::PUBLISH_DATA_TRACK_ERROR_CODE_DUPLICATE_NAME: + return PublishDataTrackErrorCode::DUPLICATE_NAME; + case proto::PUBLISH_DATA_TRACK_ERROR_CODE_TIMEOUT: + return PublishDataTrackErrorCode::TIMEOUT; + case proto::PUBLISH_DATA_TRACK_ERROR_CODE_DISCONNECTED: + return PublishDataTrackErrorCode::DISCONNECTED; + case proto::PUBLISH_DATA_TRACK_ERROR_CODE_NOT_ALLOWED: + return PublishDataTrackErrorCode::NOT_ALLOWED; + case proto::PUBLISH_DATA_TRACK_ERROR_CODE_INVALID_NAME: + return PublishDataTrackErrorCode::INVALID_NAME; + case proto::PUBLISH_DATA_TRACK_ERROR_CODE_LIMIT_REACHED: + return PublishDataTrackErrorCode::LIMIT_REACHED; + case proto::PUBLISH_DATA_TRACK_ERROR_CODE_PROTOCOL_ERROR: + return PublishDataTrackErrorCode::PROTOCOL_ERROR; + case proto::PUBLISH_DATA_TRACK_ERROR_CODE_INTERNAL: + return PublishDataTrackErrorCode::INTERNAL; + case proto::PUBLISH_DATA_TRACK_ERROR_CODE_UNKNOWN: + default: + return PublishDataTrackErrorCode::UNKNOWN; } } -LocalDataTrackTryPushErrorCode -fromProtoCode(proto::LocalDataTrackTryPushErrorCode code) { +LocalDataTrackTryPushErrorCode fromProtoCode(proto::LocalDataTrackTryPushErrorCode code) { switch (code) { - case proto::LOCAL_DATA_TRACK_TRY_PUSH_ERROR_CODE_INVALID_HANDLE: - return LocalDataTrackTryPushErrorCode::INVALID_HANDLE; - case proto::LOCAL_DATA_TRACK_TRY_PUSH_ERROR_CODE_TRACK_UNPUBLISHED: - return LocalDataTrackTryPushErrorCode::TRACK_UNPUBLISHED; - case proto::LOCAL_DATA_TRACK_TRY_PUSH_ERROR_CODE_QUEUE_FULL: - return LocalDataTrackTryPushErrorCode::QUEUE_FULL; - case proto::LOCAL_DATA_TRACK_TRY_PUSH_ERROR_CODE_INTERNAL: - return LocalDataTrackTryPushErrorCode::INTERNAL; - case proto::LOCAL_DATA_TRACK_TRY_PUSH_ERROR_CODE_UNKNOWN: - default: - return LocalDataTrackTryPushErrorCode::UNKNOWN; + case proto::LOCAL_DATA_TRACK_TRY_PUSH_ERROR_CODE_INVALID_HANDLE: + return LocalDataTrackTryPushErrorCode::INVALID_HANDLE; + case proto::LOCAL_DATA_TRACK_TRY_PUSH_ERROR_CODE_TRACK_UNPUBLISHED: + return LocalDataTrackTryPushErrorCode::TRACK_UNPUBLISHED; + case proto::LOCAL_DATA_TRACK_TRY_PUSH_ERROR_CODE_QUEUE_FULL: + return LocalDataTrackTryPushErrorCode::QUEUE_FULL; + case proto::LOCAL_DATA_TRACK_TRY_PUSH_ERROR_CODE_INTERNAL: + return LocalDataTrackTryPushErrorCode::INTERNAL; + case proto::LOCAL_DATA_TRACK_TRY_PUSH_ERROR_CODE_UNKNOWN: + default: + return LocalDataTrackTryPushErrorCode::UNKNOWN; } } -SubscribeDataTrackErrorCode -fromProtoCode(proto::SubscribeDataTrackErrorCode code) { +SubscribeDataTrackErrorCode fromProtoCode(proto::SubscribeDataTrackErrorCode code) { switch (code) { - case proto::SUBSCRIBE_DATA_TRACK_ERROR_CODE_INVALID_HANDLE: - return SubscribeDataTrackErrorCode::INVALID_HANDLE; - case proto::SUBSCRIBE_DATA_TRACK_ERROR_CODE_UNPUBLISHED: - return SubscribeDataTrackErrorCode::UNPUBLISHED; - case proto::SUBSCRIBE_DATA_TRACK_ERROR_CODE_TIMEOUT: - return SubscribeDataTrackErrorCode::TIMEOUT; - case proto::SUBSCRIBE_DATA_TRACK_ERROR_CODE_DISCONNECTED: - return SubscribeDataTrackErrorCode::DISCONNECTED; - case proto::SUBSCRIBE_DATA_TRACK_ERROR_CODE_PROTOCOL_ERROR: - return SubscribeDataTrackErrorCode::PROTOCOL_ERROR; - case proto::SUBSCRIBE_DATA_TRACK_ERROR_CODE_INTERNAL: - return SubscribeDataTrackErrorCode::INTERNAL; - case proto::SUBSCRIBE_DATA_TRACK_ERROR_CODE_UNKNOWN: - default: - return SubscribeDataTrackErrorCode::UNKNOWN; + case proto::SUBSCRIBE_DATA_TRACK_ERROR_CODE_INVALID_HANDLE: + return SubscribeDataTrackErrorCode::INVALID_HANDLE; + case proto::SUBSCRIBE_DATA_TRACK_ERROR_CODE_UNPUBLISHED: + return SubscribeDataTrackErrorCode::UNPUBLISHED; + case proto::SUBSCRIBE_DATA_TRACK_ERROR_CODE_TIMEOUT: + return SubscribeDataTrackErrorCode::TIMEOUT; + case proto::SUBSCRIBE_DATA_TRACK_ERROR_CODE_DISCONNECTED: + return SubscribeDataTrackErrorCode::DISCONNECTED; + case proto::SUBSCRIBE_DATA_TRACK_ERROR_CODE_PROTOCOL_ERROR: + return SubscribeDataTrackErrorCode::PROTOCOL_ERROR; + case proto::SUBSCRIBE_DATA_TRACK_ERROR_CODE_INTERNAL: + return SubscribeDataTrackErrorCode::INTERNAL; + case proto::SUBSCRIBE_DATA_TRACK_ERROR_CODE_UNKNOWN: + default: + return SubscribeDataTrackErrorCode::UNKNOWN; } } } // namespace -PublishDataTrackError -PublishDataTrackError::fromProto(const proto::PublishDataTrackError &error) { +PublishDataTrackError PublishDataTrackError::fromProto(const proto::PublishDataTrackError& error) { return PublishDataTrackError{fromProtoCode(error.code()), error.message()}; } -LocalDataTrackTryPushError LocalDataTrackTryPushError::fromProto( - const proto::LocalDataTrackTryPushError &error) { - return LocalDataTrackTryPushError{fromProtoCode(error.code()), - error.message()}; +LocalDataTrackTryPushError LocalDataTrackTryPushError::fromProto(const proto::LocalDataTrackTryPushError& error) { + return LocalDataTrackTryPushError{fromProtoCode(error.code()), error.message()}; } -SubscribeDataTrackError SubscribeDataTrackError::fromProto( - const proto::SubscribeDataTrackError &error) { +SubscribeDataTrackError SubscribeDataTrackError::fromProto(const proto::SubscribeDataTrackError& error) { return SubscribeDataTrackError{fromProtoCode(error.code()), error.message()}; } diff --git a/src/data_track_frame.cpp b/src/data_track_frame.cpp index 99dc1268..9728272f 100644 --- a/src/data_track_frame.cpp +++ b/src/data_track_frame.cpp @@ -20,14 +20,11 @@ namespace livekit { -DataTrackFrame -DataTrackFrame::fromOwnedInfo(const proto::DataTrackFrame &owned) { +DataTrackFrame DataTrackFrame::fromOwnedInfo(const proto::DataTrackFrame& owned) { DataTrackFrame frame; - const auto &payload_str = owned.payload(); - frame.payload.assign( - reinterpret_cast(payload_str.data()), - reinterpret_cast(payload_str.data()) + - payload_str.size()); + const auto& payload_str = owned.payload(); + frame.payload.assign(reinterpret_cast(payload_str.data()), + reinterpret_cast(payload_str.data()) + payload_str.size()); if (owned.has_user_timestamp()) { frame.user_timestamp = owned.user_timestamp(); } diff --git a/src/data_track_stream.cpp b/src/data_track_stream.cpp index 30a355c8..277120de 100644 --- a/src/data_track_stream.cpp +++ b/src/data_track_stream.cpp @@ -16,13 +16,13 @@ #include "livekit/data_track_stream.h" +#include + #include "data_track.pb.h" #include "ffi.pb.h" #include "ffi_client.h" #include "lk_log.h" -#include - namespace livekit { using proto::FfiEvent; @@ -32,25 +32,23 @@ DataTrackStream::~DataTrackStream() { close(); } void DataTrackStream::init(FfiHandle subscription_handle) { subscription_handle_ = std::move(subscription_handle); - listener_id_ = FfiClient::instance().AddListener( - [this](const FfiEvent &e) { this->onFfiEvent(e); }); + listener_id_ = FfiClient::instance().AddListener([this](const FfiEvent& e) { this->onFfiEvent(e); }); } -bool DataTrackStream::read(DataTrackFrame &out) { +bool DataTrackStream::read(DataTrackFrame& out) { { const std::scoped_lock lock(mutex_); if (closed_ || eof_) { return false; } - const auto subscription_handle = - static_cast(subscription_handle_.get()); + const auto subscription_handle = static_cast(subscription_handle_.get()); // Signal the Rust side that we're ready to receive the next frame. // The Rust SubscriptionTask uses a demand-driven protocol: it won't pull // from the underlying stream until notified via this request. proto::FfiRequest req; - auto *msg = req.mutable_data_track_stream_read(); + auto* msg = req.mutable_data_track_stream_read(); msg->set_stream_handle(subscription_handle); FfiClient::instance().sendRequest(req); } @@ -87,22 +85,21 @@ void DataTrackStream::close() { cv_.notify_all(); } -void DataTrackStream::onFfiEvent(const FfiEvent &event) { +void DataTrackStream::onFfiEvent(const FfiEvent& event) { if (event.message_case() != FfiEvent::kDataTrackStreamEvent) { return; } - const auto &dts = event.data_track_stream_event(); + const auto& dts = event.data_track_stream_event(); { const std::scoped_lock lock(mutex_); - if (closed_ || dts.stream_handle() != - static_cast(subscription_handle_.get())) { + if (closed_ || dts.stream_handle() != static_cast(subscription_handle_.get())) { return; } } if (dts.has_frame_received()) { - const auto &fr = dts.frame_received().frame(); + const auto& fr = dts.frame_received().frame(); DataTrackFrame frame = DataTrackFrame::fromOwnedInfo(fr); pushFrame(std::move(frame)); } else if (dts.has_eos()) { @@ -110,7 +107,7 @@ void DataTrackStream::onFfiEvent(const FfiEvent &event) { } } -void DataTrackStream::pushFrame(DataTrackFrame &&frame) { +void DataTrackStream::pushFrame(DataTrackFrame&& frame) { const std::scoped_lock lock(mutex_); if (closed_ || eof_) { diff --git a/src/e2ee.cpp b/src/e2ee.cpp index ae46bf79..c1f8db2b 100644 --- a/src/e2ee.cpp +++ b/src/e2ee.cpp @@ -28,13 +28,11 @@ namespace livekit { namespace { -std::string bytesToString(const std::vector &v) { - return std::string(reinterpret_cast(v.data()), v.size()); +std::string bytesToString(const std::vector& v) { + return std::string(reinterpret_cast(v.data()), v.size()); } -std::vector stringToBytes(const std::string &s) { - return std::vector(s.begin(), s.end()); -} +std::vector stringToBytes(const std::string& s) { return std::vector(s.begin(), s.end()); } } // namespace @@ -42,26 +40,20 @@ std::vector stringToBytes(const std::string &s) { // KeyProvider // ============================================================================ -E2EEManager::KeyProvider::KeyProvider(std::uint64_t room_handle, - KeyProviderOptions options) +E2EEManager::KeyProvider::KeyProvider(std::uint64_t room_handle, KeyProviderOptions options) : room_handle_(room_handle), options_(std::move(options)) {} -const KeyProviderOptions &E2EEManager::KeyProvider::options() const { - return options_; -} +const KeyProviderOptions& E2EEManager::KeyProvider::options() const { return options_; } -void E2EEManager::KeyProvider::setSharedKey( - const std::vector &key, int key_index) { +void E2EEManager::KeyProvider::setSharedKey(const std::vector& key, int key_index) { proto::FfiRequest req; req.mutable_e2ee()->set_room_handle(room_handle_); req.mutable_e2ee()->mutable_set_shared_key()->set_key_index(key_index); - req.mutable_e2ee()->mutable_set_shared_key()->set_shared_key( - bytesToString(key)); + req.mutable_e2ee()->mutable_set_shared_key()->set_shared_key(bytesToString(key)); FfiClient::instance().sendRequest(req); } -std::vector -E2EEManager::KeyProvider::exportSharedKey(int key_index) const { +std::vector E2EEManager::KeyProvider::exportSharedKey(int key_index) const { proto::FfiRequest req; req.mutable_e2ee()->set_room_handle(room_handle_); req.mutable_e2ee()->mutable_get_shared_key()->set_key_index(key_index); @@ -69,8 +61,7 @@ E2EEManager::KeyProvider::exportSharedKey(int key_index) const { return stringToBytes(resp.e2ee().get_shared_key().key()); } -std::vector -E2EEManager::KeyProvider::ratchetSharedKey(int key_index) { +std::vector E2EEManager::KeyProvider::ratchetSharedKey(int key_index) { proto::FfiRequest req; req.mutable_e2ee()->set_room_handle(room_handle_); req.mutable_e2ee()->mutable_ratchet_shared_key()->set_key_index(key_index); @@ -78,37 +69,30 @@ E2EEManager::KeyProvider::ratchetSharedKey(int key_index) { return stringToBytes(resp.e2ee().ratchet_shared_key().new_key()); } -void E2EEManager::KeyProvider::setKey(const std::string &participant_identity, - const std::vector &key, +void E2EEManager::KeyProvider::setKey(const std::string& participant_identity, const std::vector& key, int key_index) { proto::FfiRequest req; req.mutable_e2ee()->set_room_handle(room_handle_); - req.mutable_e2ee()->mutable_set_key()->set_participant_identity( - participant_identity); + req.mutable_e2ee()->mutable_set_key()->set_participant_identity(participant_identity); req.mutable_e2ee()->mutable_set_key()->set_key_index(key_index); req.mutable_e2ee()->mutable_set_key()->set_key(bytesToString(key)); FfiClient::instance().sendRequest(req); } -std::vector -E2EEManager::KeyProvider::exportKey(const std::string &participant_identity, - int key_index) const { +std::vector E2EEManager::KeyProvider::exportKey(const std::string& participant_identity, + int key_index) const { proto::FfiRequest req; req.mutable_e2ee()->set_room_handle(room_handle_); - req.mutable_e2ee()->mutable_get_key()->set_participant_identity( - participant_identity); + req.mutable_e2ee()->mutable_get_key()->set_participant_identity(participant_identity); req.mutable_e2ee()->mutable_get_key()->set_key_index(key_index); auto resp = FfiClient::instance().sendRequest(req); return stringToBytes(resp.e2ee().get_key().key()); } -std::vector -E2EEManager::KeyProvider::ratchetKey(const std::string &participant_identity, - int key_index) { +std::vector E2EEManager::KeyProvider::ratchetKey(const std::string& participant_identity, int key_index) { proto::FfiRequest req; req.mutable_e2ee()->set_room_handle(room_handle_); - req.mutable_e2ee()->mutable_ratchet_key()->set_participant_identity( - participant_identity); + req.mutable_e2ee()->mutable_ratchet_key()->set_participant_identity(participant_identity); req.mutable_e2ee()->mutable_ratchet_key()->set_key_index(key_index); auto resp = FfiClient::instance().sendRequest(req); return stringToBytes(resp.e2ee().ratchet_key().new_key()); @@ -118,24 +102,21 @@ E2EEManager::KeyProvider::ratchetKey(const std::string &participant_identity, // FrameCryptor // ============================================================================ -E2EEManager::FrameCryptor::FrameCryptor(std::uint64_t room_handle, - std::string participant_identity, - int key_index, bool enabled) - : room_handle_(room_handle), enabled_(enabled), +E2EEManager::FrameCryptor::FrameCryptor(std::uint64_t room_handle, std::string participant_identity, int key_index, + bool enabled) + : room_handle_(room_handle), + enabled_(enabled), participant_identity_(std::move(participant_identity)), key_index_(key_index) {} -const std::string &E2EEManager::FrameCryptor::participantIdentity() const { - return participant_identity_; -} +const std::string& E2EEManager::FrameCryptor::participantIdentity() const { return participant_identity_; } int E2EEManager::FrameCryptor::keyIndex() const { return key_index_; } bool E2EEManager::FrameCryptor::enabled() const { return enabled_; } void E2EEManager::FrameCryptor::setEnabled(bool enabled) { proto::FfiRequest req; req.mutable_e2ee()->set_room_handle(room_handle_); - req.mutable_e2ee()->mutable_cryptor_set_enabled()->set_participant_identity( - participant_identity_); + req.mutable_e2ee()->mutable_cryptor_set_enabled()->set_participant_identity(participant_identity_); req.mutable_e2ee()->mutable_cryptor_set_enabled()->set_enabled(enabled); FfiClient::instance().sendRequest(req); } @@ -143,8 +124,7 @@ void E2EEManager::FrameCryptor::setEnabled(bool enabled) { void E2EEManager::FrameCryptor::setKeyIndex(int key_index) { proto::FfiRequest req; req.mutable_e2ee()->set_room_handle(room_handle_); - req.mutable_e2ee()->mutable_cryptor_set_key_index()->set_participant_identity( - participant_identity_); + req.mutable_e2ee()->mutable_cryptor_set_key_index()->set_participant_identity(participant_identity_); req.mutable_e2ee()->mutable_cryptor_set_key_index()->set_key_index(key_index); FfiClient::instance().sendRequest(req); } @@ -153,7 +133,7 @@ void E2EEManager::FrameCryptor::setKeyIndex(int key_index) { // E2EEManager // ============================================================================ -E2EEManager::E2EEManager(std::uint64_t room_handle, const E2EEOptions &options) +E2EEManager::E2EEManager(std::uint64_t room_handle, const E2EEOptions& options) : room_handle_(room_handle), enabled_(true), // or false, depending on your desired default behavior options_(options), @@ -169,21 +149,18 @@ void E2EEManager::setEnabled(bool enabled) { enabled_ = enabled; } -E2EEManager::KeyProvider *E2EEManager::keyProvider() { return &key_provider_; } -const E2EEManager::KeyProvider *E2EEManager::keyProvider() const { - return &key_provider_; -} +E2EEManager::KeyProvider* E2EEManager::keyProvider() { return &key_provider_; } +const E2EEManager::KeyProvider* E2EEManager::keyProvider() const { return &key_provider_; } std::vector E2EEManager::frameCryptors() const { proto::FfiRequest req; req.mutable_e2ee()->set_room_handle(room_handle_); auto resp = FfiClient::instance().sendRequest(req); std::vector out; - const auto &list = resp.e2ee().manager_get_frame_cryptors().frame_cryptors(); + const auto& list = resp.e2ee().manager_get_frame_cryptors().frame_cryptors(); out.reserve(static_cast(list.size())); - for (const auto &fc : list) { - out.emplace_back(room_handle_, fc.participant_identity(), fc.key_index(), - fc.enabled()); + for (const auto& fc : list) { + out.emplace_back(room_handle_, fc.participant_identity(), fc.key_index(), fc.enabled()); } return out; } diff --git a/src/ffi_client.cpp b/src/ffi_client.cpp index 1f08d824..9d7bf76e 100644 --- a/src/ffi_client.cpp +++ b/src/ffi_client.cpp @@ -14,21 +14,22 @@ * limitations under the License. */ +#include "ffi_client.h" + #include #include "data_track.pb.h" #include "e2ee.pb.h" #include "ffi.pb.h" -#include "ffi_client.h" #include "livekit/build.h" #include "livekit/data_track_error.h" #include "livekit/e2ee.h" #include "livekit/ffi_handle.h" -#include "lk_log.h" #include "livekit/room.h" #include "livekit/rpc_error.h" #include "livekit/track.h" #include "livekit_ffi.h" +#include "lk_log.h" #include "room.pb.h" #include "room_proto_converter.h" @@ -36,104 +37,104 @@ namespace livekit { namespace { -std::string bytesToString(const std::vector &b) { - return std::string(reinterpret_cast(b.data()), b.size()); +std::string bytesToString(const std::vector& b) { + return std::string(reinterpret_cast(b.data()), b.size()); } -inline void logAndThrow(const std::string &error_msg) { +inline void logAndThrow(const std::string& error_msg) { LK_LOG_ERROR("LiveKit SDK Error: {}", error_msg); throw std::runtime_error(error_msg); } -std::optional ExtractAsyncId(const proto::FfiEvent &event) { +std::optional ExtractAsyncId(const proto::FfiEvent& event) { using E = proto::FfiEvent; switch (event.message_case()) { - case E::kConnect: - return event.connect().async_id(); - case E::kDisconnect: - return event.disconnect().async_id(); - case E::kDispose: - return event.dispose().async_id(); - case E::kPublishTrack: - return event.publish_track().async_id(); - case E::kUnpublishTrack: - return event.unpublish_track().async_id(); - case E::kPublishData: - return event.publish_data().async_id(); - case E::kPublishTranscription: - return event.publish_transcription().async_id(); - case E::kCaptureAudioFrame: - return event.capture_audio_frame().async_id(); - case E::kSetLocalMetadata: - return event.set_local_metadata().async_id(); - case E::kSetLocalName: - return event.set_local_name().async_id(); - case E::kSetLocalAttributes: - return event.set_local_attributes().async_id(); - case E::kGetStats: - return event.get_stats().async_id(); - case E::kGetSessionStats: - return event.get_session_stats().async_id(); - case E::kPublishSipDtmf: - return event.publish_sip_dtmf().async_id(); - case E::kChatMessage: - return event.chat_message().async_id(); - case E::kPerformRpc: - return event.perform_rpc().async_id(); - - // low-level data stream callbacks - case E::kSendStreamHeader: - return event.send_stream_header().async_id(); - case E::kSendStreamChunk: - return event.send_stream_chunk().async_id(); - case E::kSendStreamTrailer: - return event.send_stream_trailer().async_id(); - - // high-level - case E::kByteStreamReaderReadAll: - return event.byte_stream_reader_read_all().async_id(); - case E::kByteStreamReaderWriteToFile: - return event.byte_stream_reader_write_to_file().async_id(); - case E::kByteStreamOpen: - return event.byte_stream_open().async_id(); - case E::kByteStreamWriterWrite: - return event.byte_stream_writer_write().async_id(); - case E::kByteStreamWriterClose: - return event.byte_stream_writer_close().async_id(); - case E::kSendFile: - return event.send_file().async_id(); - - case E::kTextStreamReaderReadAll: - return event.text_stream_reader_read_all().async_id(); - case E::kTextStreamOpen: - return event.text_stream_open().async_id(); - case E::kTextStreamWriterWrite: - return event.text_stream_writer_write().async_id(); - case E::kTextStreamWriterClose: - return event.text_stream_writer_close().async_id(); - case E::kSendText: - return event.send_text().async_id(); - case E::kSendBytes: - return event.send_bytes().async_id(); - - // data track async completions - case E::kPublishDataTrack: - return event.publish_data_track().async_id(); - - // NOT async completion: - case E::kRoomEvent: - case E::kTrackEvent: - case E::kVideoStreamEvent: - case E::kAudioStreamEvent: - case E::kByteStreamReaderEvent: - case E::kTextStreamReaderEvent: - case E::kDataTrackStreamEvent: - case E::kRpcMethodInvocation: - case E::kLogs: - case E::kPanic: - case E::MESSAGE_NOT_SET: - default: - return std::nullopt; + case E::kConnect: + return event.connect().async_id(); + case E::kDisconnect: + return event.disconnect().async_id(); + case E::kDispose: + return event.dispose().async_id(); + case E::kPublishTrack: + return event.publish_track().async_id(); + case E::kUnpublishTrack: + return event.unpublish_track().async_id(); + case E::kPublishData: + return event.publish_data().async_id(); + case E::kPublishTranscription: + return event.publish_transcription().async_id(); + case E::kCaptureAudioFrame: + return event.capture_audio_frame().async_id(); + case E::kSetLocalMetadata: + return event.set_local_metadata().async_id(); + case E::kSetLocalName: + return event.set_local_name().async_id(); + case E::kSetLocalAttributes: + return event.set_local_attributes().async_id(); + case E::kGetStats: + return event.get_stats().async_id(); + case E::kGetSessionStats: + return event.get_session_stats().async_id(); + case E::kPublishSipDtmf: + return event.publish_sip_dtmf().async_id(); + case E::kChatMessage: + return event.chat_message().async_id(); + case E::kPerformRpc: + return event.perform_rpc().async_id(); + + // low-level data stream callbacks + case E::kSendStreamHeader: + return event.send_stream_header().async_id(); + case E::kSendStreamChunk: + return event.send_stream_chunk().async_id(); + case E::kSendStreamTrailer: + return event.send_stream_trailer().async_id(); + + // high-level + case E::kByteStreamReaderReadAll: + return event.byte_stream_reader_read_all().async_id(); + case E::kByteStreamReaderWriteToFile: + return event.byte_stream_reader_write_to_file().async_id(); + case E::kByteStreamOpen: + return event.byte_stream_open().async_id(); + case E::kByteStreamWriterWrite: + return event.byte_stream_writer_write().async_id(); + case E::kByteStreamWriterClose: + return event.byte_stream_writer_close().async_id(); + case E::kSendFile: + return event.send_file().async_id(); + + case E::kTextStreamReaderReadAll: + return event.text_stream_reader_read_all().async_id(); + case E::kTextStreamOpen: + return event.text_stream_open().async_id(); + case E::kTextStreamWriterWrite: + return event.text_stream_writer_write().async_id(); + case E::kTextStreamWriterClose: + return event.text_stream_writer_close().async_id(); + case E::kSendText: + return event.send_text().async_id(); + case E::kSendBytes: + return event.send_bytes().async_id(); + + // data track async completions + case E::kPublishDataTrack: + return event.publish_data_track().async_id(); + + // NOT async completion: + case E::kRoomEvent: + case E::kTrackEvent: + case E::kVideoStreamEvent: + case E::kAudioStreamEvent: + case E::kByteStreamReaderEvent: + case E::kTextStreamReaderEvent: + case E::kDataTrackStreamEvent: + case E::kRpcMethodInvocation: + case E::kLogs: + case E::kPanic: + case E::MESSAGE_NOT_SET: + default: + return std::nullopt; } } @@ -161,17 +162,13 @@ bool FfiClient::initialize(bool capture_logs) { return false; } initialized_.store(true, std::memory_order_release); - livekit_ffi_initialize(&LivekitFfiCallback, capture_logs, - LIVEKIT_BUILD_FLAVOR, LIVEKIT_BUILD_VERSION_FULL); + livekit_ffi_initialize(&LivekitFfiCallback, capture_logs, LIVEKIT_BUILD_FLAVOR, LIVEKIT_BUILD_VERSION_FULL); return true; } -bool FfiClient::isInitialized() const noexcept { - return initialized_.load(std::memory_order_acquire); -} +bool FfiClient::isInitialized() const noexcept { return initialized_.load(std::memory_order_acquire); } -FfiClient::ListenerId -FfiClient::AddListener(const FfiClient::Listener &listener) { +FfiClient::ListenerId FfiClient::AddListener(const FfiClient::Listener& listener) { const std::scoped_lock guard(lock_); const FfiClient::ListenerId id = next_listener_id++; listeners_[id] = listener; @@ -183,20 +180,17 @@ void FfiClient::RemoveListener(ListenerId id) { listeners_.erase(id); } -proto::FfiResponse -FfiClient::sendRequest(const proto::FfiRequest &request) const { +proto::FfiResponse FfiClient::sendRequest(const proto::FfiRequest& request) const { std::string bytes; if (!request.SerializeToString(&bytes) || bytes.empty()) { throw std::runtime_error("failed to serialize FfiRequest"); } - const uint8_t *resp_ptr = nullptr; + const uint8_t* resp_ptr = nullptr; size_t resp_len = 0; const FfiHandleId handle = - livekit_ffi_request(reinterpret_cast(bytes.data()), - bytes.size(), &resp_ptr, &resp_len); + livekit_ffi_request(reinterpret_cast(bytes.data()), bytes.size(), &resp_ptr, &resp_len); if (handle == INVALID_HANDLE) { - throw std::runtime_error( - "failed to send request, received an invalid handle"); + throw std::runtime_error("failed to send request, received an invalid handle"); } // Ensure we drop the handle exactly once on all paths @@ -212,7 +206,7 @@ FfiClient::sendRequest(const proto::FfiRequest &request) const { return response; } -void FfiClient::PushEvent(const proto::FfiEvent &event) const { +void FfiClient::PushEvent(const proto::FfiEvent& event) const { std::unique_ptr to_complete; std::vector listeners_copy; { @@ -221,8 +215,7 @@ void FfiClient::PushEvent(const proto::FfiEvent &event) const { // Complete pending future if this event is a callback with async_id if (auto async_id = ExtractAsyncId(event)) { auto it = pending_by_id_.find(*async_id); - if (it != pending_by_id_.end() && it->second && - it->second->matches(event)) { + if (it != pending_by_id_.end() && it->second && it->second->matches(event)) { to_complete = std::move(it->second); pending_by_id_.erase(it); } @@ -230,7 +223,7 @@ void FfiClient::PushEvent(const proto::FfiEvent &event) const { // Snapshot listeners listeners_copy.reserve(listeners_.size()); - for (const auto &kv : listeners_) { + for (const auto& kv : listeners_) { listeners_copy.push_back(kv.second); } } @@ -240,21 +233,20 @@ void FfiClient::PushEvent(const proto::FfiEvent &event) const { } // Notify listeners outside lock - for (auto &listener : listeners_copy) { + for (auto& listener : listeners_copy) { listener(event); } } -void LivekitFfiCallback(const uint8_t *buf, size_t len) { +void LivekitFfiCallback(const uint8_t* buf, size_t len) { proto::FfiEvent event; - event.ParseFromArray(buf, static_cast(len)); // TODO: this fixes for now, what if len exceeds int? + event.ParseFromArray(buf, + static_cast(len)); // TODO: this fixes for now, what if len exceeds int? FfiClient::instance().PushEvent(event); } -FfiClient::AsyncId FfiClient::generateAsyncId() { - return next_async_id_.fetch_add(1, std::memory_order_relaxed); -} +FfiClient::AsyncId FfiClient::generateAsyncId() { return next_async_id_.fetch_add(1, std::memory_order_relaxed); } bool FfiClient::cancelPendingByAsyncId(AsyncId async_id) { std::unique_ptr to_cancel; @@ -274,9 +266,8 @@ bool FfiClient::cancelPendingByAsyncId(AsyncId async_id) { } template -std::future FfiClient::registerAsync( - AsyncId async_id, std::function match, - std::function &)> handler) { +std::future FfiClient::registerAsync(AsyncId async_id, std::function match, + std::function&)> handler) { auto pending = std::make_unique>(); pending->async_id = async_id; auto fut = pending->promise.get_future(); @@ -290,10 +281,8 @@ std::future FfiClient::registerAsync( } // Room APIs Implementation -std::future -FfiClient::connectAsync(const std::string &url, const std::string &token, - const RoomOptions &options) { - +std::future FfiClient::connectAsync(const std::string& url, const std::string& token, + const RoomOptions& options) { // Generate client-side async_id first const AsyncId async_id = generateAsyncId(); @@ -301,16 +290,14 @@ FfiClient::connectAsync(const std::string &url, const std::string &token, auto fut = registerAsync( async_id, // match lambda: is this the connect event with our async_id? - [async_id](const proto::FfiEvent &event) { + [async_id](const proto::FfiEvent& event) { return event.has_connect() && event.connect().async_id() == async_id; }, // handler lambda: fill the promise with RoomInfo or an exception - [](const proto::FfiEvent &event, - std::promise &pr) { - const auto &connectCb = event.connect(); + [](const proto::FfiEvent& event, std::promise& pr) { + const auto& connectCb = event.connect(); if (!connectCb.error().empty()) { - pr.set_exception( - std::make_exception_ptr(std::runtime_error(connectCb.error()))); + pr.set_exception(std::make_exception_ptr(std::runtime_error(connectCb.error()))); return; } @@ -319,29 +306,28 @@ FfiClient::connectAsync(const std::string &url, const std::string &token, // Build and send the request proto::FfiRequest req; - auto *connect = req.mutable_connect(); + auto* connect = req.mutable_connect(); connect->set_url(url); connect->set_token(token); connect->set_request_async_id(async_id); - auto *opts = connect->mutable_options(); + auto* opts = connect->mutable_options(); opts->set_auto_subscribe(options.auto_subscribe); opts->set_dynacast(options.dynacast); opts->set_single_peer_connection(options.single_peer_connection); - LK_LOG_DEBUG("[FfiClient] connectAsync: auto_subscribe={}, dynacast={}, " - "single_peer_connection={}", - options.auto_subscribe, options.dynacast, - options.single_peer_connection); + LK_LOG_DEBUG( + "[FfiClient] connectAsync: auto_subscribe={}, dynacast={}, " + "single_peer_connection={}", + options.auto_subscribe, options.dynacast, options.single_peer_connection); // --- E2EE / encryption (optional) --- if (options.encryption.has_value()) { - const E2EEOptions &e2ee = *options.encryption; - const auto &kpo = e2ee.key_provider_options; + const E2EEOptions& e2ee = *options.encryption; + const auto& kpo = e2ee.key_provider_options; - auto *enc = opts->mutable_encryption(); - enc->set_encryption_type( - static_cast(e2ee.encryption_type)); - auto *kp = enc->mutable_key_provider_options(); + auto* enc = opts->mutable_encryption(); + enc->set_encryption_type(static_cast(e2ee.encryption_type)); + auto* kp = enc->mutable_key_provider_options(); // shared_key is optional. If not set, leave the field unset/cleared. if (kpo.shared_key && !kpo.shared_key->empty()) { kp->set_shared_key(bytesToString(*kpo.shared_key)); @@ -352,10 +338,8 @@ FfiClient::connectAsync(const std::string &url, const std::string &token, // uses default. if (!kpo.ratchet_salt.empty() && kpo.ratchet_salt != - std::vector( - kDefaultRatchetSalt, - kDefaultRatchetSalt + - std::char_traits::length(kDefaultRatchetSalt))) { + std::vector(kDefaultRatchetSalt, + kDefaultRatchetSalt + std::char_traits::length(kDefaultRatchetSalt))) { kp->set_ratchet_salt(bytesToString(kpo.ratchet_salt)); } else { kp->clear_ratchet_salt(); @@ -376,17 +360,14 @@ FfiClient::connectAsync(const std::string &url, const std::string &token, // --- RTC configuration (optional) --- if (options.rtc_config.has_value()) { - const RtcConfig &rc = *options.rtc_config; - auto *rtc = opts->mutable_rtc_config(); + const RtcConfig& rc = *options.rtc_config; + auto* rtc = opts->mutable_rtc_config(); - rtc->set_ice_transport_type( - static_cast(rc.ice_transport_type)); - rtc->set_continual_gathering_policy( - static_cast( - rc.continual_gathering_policy)); + rtc->set_ice_transport_type(static_cast(rc.ice_transport_type)); + rtc->set_continual_gathering_policy(static_cast(rc.continual_gathering_policy)); - for (const IceServer &ice : rc.ice_servers) { - auto *s = rtc->add_ice_servers(); + for (const IceServer& ice : rc.ice_servers) { + auto* s = rtc->add_ice_servers(); // proto: repeated string urls = 1 if (!ice.url.empty()) { @@ -416,8 +397,7 @@ FfiClient::connectAsync(const std::string &url, const std::string &token, } // Track APIs Implementation -std::future> -FfiClient::getTrackStatsAsync(uintptr_t track_handle) { +std::future> FfiClient::getTrackStatsAsync(uintptr_t track_handle) { // Generate client-side async_id first const AsyncId async_id = generateAsyncId(); @@ -425,24 +405,21 @@ FfiClient::getTrackStatsAsync(uintptr_t track_handle) { auto fut = registerAsync>( async_id, // match - [async_id](const proto::FfiEvent &event) { - return event.has_get_stats() && - event.get_stats().async_id() == async_id; + [async_id](const proto::FfiEvent& event) { + return event.has_get_stats() && event.get_stats().async_id() == async_id; }, // handler - [](const proto::FfiEvent &event, - std::promise> &pr) { - const auto &gs = event.get_stats(); + [](const proto::FfiEvent& event, std::promise>& pr) { + const auto& gs = event.get_stats(); if (!gs.error().empty()) { - pr.set_exception( - std::make_exception_ptr(std::runtime_error(gs.error()))); + pr.set_exception(std::make_exception_ptr(std::runtime_error(gs.error()))); return; } std::vector stats_vec; stats_vec.reserve(gs.stats_size()); - for (const auto &ps : gs.stats()) { + for (const auto& ps : gs.stats()) { stats_vec.push_back(fromProto(ps)); } pr.set_value(std::move(stats_vec)); @@ -450,7 +427,7 @@ FfiClient::getTrackStatsAsync(uintptr_t track_handle) { // Build and send the request proto::FfiRequest req; - auto *get_stats_req = req.mutable_get_stats(); + auto* get_stats_req = req.mutable_get_stats(); get_stats_req->set_track_handle(track_handle); get_stats_req->set_request_async_id(async_id); @@ -468,10 +445,9 @@ FfiClient::getTrackStatsAsync(uintptr_t track_handle) { } // Participant APIs Implementation -std::future -FfiClient::publishTrackAsync(std::uint64_t local_participant_handle, - std::uint64_t track_handle, - const TrackPublishOptions &options) { +std::future FfiClient::publishTrackAsync(std::uint64_t local_participant_handle, + std::uint64_t track_handle, + const TrackPublishOptions& options) { // Generate client-side async_id first const AsyncId async_id = generateAsyncId(); @@ -479,35 +455,31 @@ FfiClient::publishTrackAsync(std::uint64_t local_participant_handle, auto fut = registerAsync( async_id, // Match: is this our PublishTrackCallback? - [async_id](const proto::FfiEvent &event) { - return event.has_publish_track() && - event.publish_track().async_id() == async_id; + [async_id](const proto::FfiEvent& event) { + 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 &pr) { - const auto &cb = event.publish_track(); + [](const proto::FfiEvent& event, std::promise& pr) { + const auto& cb = event.publish_track(); // Oneof message { string error = 2; OwnedTrackPublication publication = // 3; } if (cb.has_error() && !cb.error().empty()) { - pr.set_exception( - std::make_exception_ptr(std::runtime_error(cb.error()))); + pr.set_exception(std::make_exception_ptr(std::runtime_error(cb.error()))); return; } if (!cb.has_publication()) { - pr.set_exception(std::make_exception_ptr( - std::runtime_error("PublishTrackCallback missing publication"))); + pr.set_exception(std::make_exception_ptr(std::runtime_error("PublishTrackCallback missing publication"))); return; } - const proto::OwnedTrackPublication &pub = cb.publication(); + const proto::OwnedTrackPublication& pub = cb.publication(); pr.set_value(pub); }); // Build and send the request proto::FfiRequest req; - auto *msg = req.mutable_publish_track(); + auto* msg = req.mutable_publish_track(); msg->set_local_participant_handle(local_participant_handle); msg->set_track_handle(track_handle); msg->set_request_async_id(async_id); @@ -527,25 +499,21 @@ FfiClient::publishTrackAsync(std::uint64_t local_participant_handle, return fut; } -std::future -FfiClient::unpublishTrackAsync(std::uint64_t local_participant_handle, - const std::string &track_sid, - bool stop_on_unpublish) { +std::future FfiClient::unpublishTrackAsync(std::uint64_t local_participant_handle, const std::string& track_sid, + bool stop_on_unpublish) { // Generate client-side async_id first const AsyncId async_id = generateAsyncId(); // Register the async handler BEFORE sending the request auto fut = registerAsync( async_id, - [async_id](const proto::FfiEvent &event) { - return event.has_unpublish_track() && - event.unpublish_track().async_id() == async_id; + [async_id](const proto::FfiEvent& event) { + return event.has_unpublish_track() && event.unpublish_track().async_id() == async_id; }, - [](const proto::FfiEvent &event, std::promise &pr) { - const auto &cb = event.unpublish_track(); + [](const proto::FfiEvent& event, std::promise& pr) { + const auto& cb = event.unpublish_track(); if (cb.has_error() && !cb.error().empty()) { - pr.set_exception( - std::make_exception_ptr(std::runtime_error(cb.error()))); + pr.set_exception(std::make_exception_ptr(std::runtime_error(cb.error()))); return; } pr.set_value(); @@ -553,7 +521,7 @@ FfiClient::unpublishTrackAsync(std::uint64_t local_participant_handle, // Build and send the request proto::FfiRequest req; - auto *msg = req.mutable_unpublish_track(); + auto* msg = req.mutable_unpublish_track(); msg->set_local_participant_handle(local_participant_handle); msg->set_track_sid(track_sid); msg->set_stop_on_unpublish(stop_on_unpublish); @@ -572,26 +540,23 @@ FfiClient::unpublishTrackAsync(std::uint64_t local_participant_handle, return fut; } -std::future FfiClient::publishDataAsync( - std::uint64_t local_participant_handle, const std::uint8_t *data_ptr, - std::uint64_t data_len, bool reliable, - const std::vector &destination_identities, - const std::string &topic) { +std::future FfiClient::publishDataAsync(std::uint64_t local_participant_handle, const std::uint8_t* data_ptr, + std::uint64_t data_len, bool reliable, + const std::vector& destination_identities, + const std::string& topic) { // Generate client-side async_id first const AsyncId async_id = generateAsyncId(); // Register the async handler BEFORE sending the request auto fut = registerAsync( async_id, - [async_id](const proto::FfiEvent &event) { - return event.has_publish_data() && - event.publish_data().async_id() == async_id; + [async_id](const proto::FfiEvent& event) { + return event.has_publish_data() && event.publish_data().async_id() == async_id; }, - [](const proto::FfiEvent &event, std::promise &pr) { - const auto &cb = event.publish_data(); + [](const proto::FfiEvent& event, std::promise& pr) { + const auto& cb = event.publish_data(); if (cb.has_error() && !cb.error().empty()) { - pr.set_exception( - std::make_exception_ptr(std::runtime_error(cb.error()))); + pr.set_exception(std::make_exception_ptr(std::runtime_error(cb.error()))); return; } pr.set_value(); @@ -599,14 +564,14 @@ std::future FfiClient::publishDataAsync( // Build and send the request proto::FfiRequest req; - auto *msg = req.mutable_publish_data(); + auto* msg = req.mutable_publish_data(); msg->set_local_participant_handle(local_participant_handle); msg->set_data_ptr(reinterpret_cast(data_ptr)); msg->set_data_len(data_len); msg->set_reliable(reliable); msg->set_topic(topic); msg->set_request_async_id(async_id); - for (const auto &id : destination_identities) { + for (const auto& id : destination_identities) { msg->add_destination_identities(id); } @@ -623,43 +588,32 @@ std::future FfiClient::publishDataAsync( return fut; } -std::future> -FfiClient::publishDataTrackAsync(std::uint64_t local_participant_handle, - const std::string &track_name) { +std::future> FfiClient::publishDataTrackAsync( + std::uint64_t local_participant_handle, const std::string& track_name) { const AsyncId async_id = generateAsyncId(); - auto fut = registerAsync< - Result>( + auto fut = registerAsync>( async_id, - [async_id](const proto::FfiEvent &event) { - return event.has_publish_data_track() && - event.publish_data_track().async_id() == async_id; + [async_id](const proto::FfiEvent& event) { + return event.has_publish_data_track() && event.publish_data_track().async_id() == async_id; }, - [](const proto::FfiEvent &event, - std::promise> - &pr) { - const auto &cb = event.publish_data_track(); + [](const proto::FfiEvent& event, std::promise>& pr) { + const auto& cb = event.publish_data_track(); if (cb.has_error()) { - pr.set_value( - Result::failure( - PublishDataTrackError::fromProto(cb.error()))); + pr.set_value(Result::failure( + PublishDataTrackError::fromProto(cb.error()))); return; } if (!cb.has_track()) { - pr.set_value( - Result::failure(PublishDataTrackError{ - PublishDataTrackErrorCode::PROTOCOL_ERROR, - "PublishDataTrackCallback missing track"})); + pr.set_value(Result::failure(PublishDataTrackError{ + PublishDataTrackErrorCode::PROTOCOL_ERROR, "PublishDataTrackCallback missing track"})); return; } - pr.set_value( - Result::success(cb.track())); + pr.set_value(Result::success(cb.track())); }); proto::FfiRequest req; - auto *msg = req.mutable_publish_data_track(); + auto* msg = req.mutable_publish_data_track(); msg->set_local_participant_handle(local_participant_handle); msg->mutable_options()->set_name(track_name); msg->set_request_async_id(async_id); @@ -668,34 +622,28 @@ FfiClient::publishDataTrackAsync(std::uint64_t local_participant_handle, const proto::FfiResponse resp = sendRequest(req); if (!resp.has_publish_data_track()) { cancelPendingByAsyncId(async_id); - std::promise> - pr; - pr.set_value( - Result::failure( - PublishDataTrackError{PublishDataTrackErrorCode::PROTOCOL_ERROR, - "FfiResponse missing publish_data_track"})); + std::promise> pr; + pr.set_value(Result::failure( + PublishDataTrackError{PublishDataTrackErrorCode::PROTOCOL_ERROR, "FfiResponse missing publish_data_track"})); return pr.get_future(); } - } catch (const std::exception &e) { + } catch (const std::exception& e) { cancelPendingByAsyncId(async_id); std::promise> pr; - pr.set_value( - Result::failure( - PublishDataTrackError{PublishDataTrackErrorCode::INTERNAL, - e.what()})); + pr.set_value(Result::failure( + PublishDataTrackError{PublishDataTrackErrorCode::INTERNAL, e.what()})); return pr.get_future(); } return fut; } -Result -FfiClient::subscribeDataTrack(std::uint64_t track_handle, - std::optional buffer_size) { +Result FfiClient::subscribeDataTrack( + std::uint64_t track_handle, std::optional buffer_size) { proto::FfiRequest req; - auto *msg = req.mutable_subscribe_data_track(); + auto* msg = req.mutable_subscribe_data_track(); msg->set_track_handle(track_handle); - auto *opts = msg->mutable_options(); + auto* opts = msg->mutable_options(); if (buffer_size.has_value()) { opts->set_buffer_size(buffer_size.value()); } @@ -703,46 +651,37 @@ FfiClient::subscribeDataTrack(std::uint64_t track_handle, try { const proto::FfiResponse resp = sendRequest(req); if (!resp.has_subscribe_data_track()) { - return Result::failure(SubscribeDataTrackError{ - SubscribeDataTrackErrorCode::PROTOCOL_ERROR, - "FfiResponse missing subscribe_data_track"}); + return Result::failure(SubscribeDataTrackError{ + SubscribeDataTrackErrorCode::PROTOCOL_ERROR, "FfiResponse missing subscribe_data_track"}); } if (!resp.subscribe_data_track().has_stream()) { - return Result::failure(SubscribeDataTrackError{ - SubscribeDataTrackErrorCode::PROTOCOL_ERROR, - "FfiResponse subscribe_data_track missing stream"}); + return Result::failure(SubscribeDataTrackError{ + SubscribeDataTrackErrorCode::PROTOCOL_ERROR, "FfiResponse subscribe_data_track missing stream"}); } proto::OwnedDataTrackStream sub = resp.subscribe_data_track().stream(); - return Result::success(std::move(sub)); - } catch (const std::exception &e) { // NOLINT(bugprone-empty-catch) - return Result::failure(SubscribeDataTrackError{ - SubscribeDataTrackErrorCode::INTERNAL, e.what()}); + return Result::success(std::move(sub)); + } catch (const std::exception& e) { // NOLINT(bugprone-empty-catch) + return Result::failure( + SubscribeDataTrackError{SubscribeDataTrackErrorCode::INTERNAL, e.what()}); } } -std::future FfiClient::publishSipDtmfAsync( - std::uint64_t local_participant_handle, std::uint32_t code, - const std::string &digit, - const std::vector &destination_identities) { +std::future FfiClient::publishSipDtmfAsync(std::uint64_t local_participant_handle, std::uint32_t code, + const std::string& digit, + const std::vector& destination_identities) { // Generate client-side async_id first const AsyncId async_id = generateAsyncId(); // Register the async handler BEFORE sending the request auto fut = registerAsync( async_id, - [async_id](const proto::FfiEvent &event) { - return event.has_publish_sip_dtmf() && - event.publish_sip_dtmf().async_id() == async_id; + [async_id](const proto::FfiEvent& event) { + return event.has_publish_sip_dtmf() && event.publish_sip_dtmf().async_id() == async_id; }, - [](const proto::FfiEvent &event, std::promise &pr) { - const auto &cb = event.publish_sip_dtmf(); + [](const proto::FfiEvent& event, std::promise& pr) { + const auto& cb = event.publish_sip_dtmf(); if (cb.has_error() && !cb.error().empty()) { - pr.set_exception( - std::make_exception_ptr(std::runtime_error(cb.error()))); + pr.set_exception(std::make_exception_ptr(std::runtime_error(cb.error()))); return; } pr.set_value(); @@ -750,12 +689,12 @@ std::future FfiClient::publishSipDtmfAsync( // Build and send the request proto::FfiRequest req; - auto *msg = req.mutable_publish_sip_dtmf(); + auto* msg = req.mutable_publish_sip_dtmf(); msg->set_local_participant_handle(local_participant_handle); msg->set_code(code); msg->set_digit(digit); msg->set_request_async_id(async_id); - for (const auto &id : destination_identities) { + for (const auto& id : destination_identities) { msg->add_destination_identities(id); } @@ -772,24 +711,21 @@ std::future FfiClient::publishSipDtmfAsync( return fut; } -std::future -FfiClient::setLocalMetadataAsync(std::uint64_t local_participant_handle, - const std::string &metadata) { +std::future FfiClient::setLocalMetadataAsync(std::uint64_t local_participant_handle, + const std::string& metadata) { // Generate client-side async_id first const AsyncId async_id = generateAsyncId(); // Register the async handler BEFORE sending the request auto fut = registerAsync( async_id, - [async_id](const proto::FfiEvent &event) { - return event.has_set_local_metadata() && - event.set_local_metadata().async_id() == async_id; + [async_id](const proto::FfiEvent& event) { + return event.has_set_local_metadata() && event.set_local_metadata().async_id() == async_id; }, - [](const proto::FfiEvent &event, std::promise &pr) { - const auto &cb = event.set_local_metadata(); + [](const proto::FfiEvent& event, std::promise& pr) { + const auto& cb = event.set_local_metadata(); if (cb.has_error() && !cb.error().empty()) { - pr.set_exception( - std::make_exception_ptr(std::runtime_error(cb.error()))); + pr.set_exception(std::make_exception_ptr(std::runtime_error(cb.error()))); return; } pr.set_value(); @@ -797,7 +733,7 @@ FfiClient::setLocalMetadataAsync(std::uint64_t local_participant_handle, // Build and send the request proto::FfiRequest req; - auto *msg = req.mutable_set_local_metadata(); + auto* msg = req.mutable_set_local_metadata(); msg->set_local_participant_handle(local_participant_handle); msg->set_metadata(metadata); msg->set_request_async_id(async_id); @@ -815,9 +751,8 @@ FfiClient::setLocalMetadataAsync(std::uint64_t local_participant_handle, return fut; } -std::future -FfiClient::captureAudioFrameAsync(std::uint64_t source_handle, - const proto::AudioFrameBufferInfo &buffer) { +std::future FfiClient::captureAudioFrameAsync(std::uint64_t source_handle, + const proto::AudioFrameBufferInfo& buffer) { // Generate client-side async_id first const AsyncId async_id = generateAsyncId(); @@ -825,16 +760,14 @@ FfiClient::captureAudioFrameAsync(std::uint64_t source_handle, auto fut = registerAsync( async_id, // match predicate - [async_id](const proto::FfiEvent &event) { - return event.has_capture_audio_frame() && - event.capture_audio_frame().async_id() == async_id; + [async_id](const proto::FfiEvent& event) { + return event.has_capture_audio_frame() && event.capture_audio_frame().async_id() == async_id; }, // completion handler - [](const proto::FfiEvent &event, std::promise &pr) { - const auto &cb = event.capture_audio_frame(); + [](const proto::FfiEvent& event, std::promise& pr) { + const auto& cb = event.capture_audio_frame(); if (cb.has_error() && !cb.error().empty()) { - pr.set_exception( - std::make_exception_ptr(std::runtime_error(cb.error()))); + pr.set_exception(std::make_exception_ptr(std::runtime_error(cb.error()))); return; } pr.set_value(); @@ -842,7 +775,7 @@ FfiClient::captureAudioFrameAsync(std::uint64_t source_handle, // Build and send the request proto::FfiRequest req; - auto *msg = req.mutable_capture_audio_frame(); + auto* msg = req.mutable_capture_audio_frame(); msg->set_source_handle(source_handle); msg->set_request_async_id(async_id); msg->mutable_buffer()->CopyFrom(buffer); @@ -860,12 +793,10 @@ FfiClient::captureAudioFrameAsync(std::uint64_t source_handle, return fut; } -std::future -FfiClient::performRpcAsync(std::uint64_t local_participant_handle, - const std::string &destination_identity, - const std::string &method, - const std::string &payload, - std::optional response_timeout_ms) { +std::future FfiClient::performRpcAsync(std::uint64_t local_participant_handle, + const std::string& destination_identity, const std::string& method, + const std::string& payload, + std::optional response_timeout_ms) { // Generate client-side async_id first const AsyncId async_id = generateAsyncId(); @@ -873,17 +804,15 @@ FfiClient::performRpcAsync(std::uint64_t local_participant_handle, auto fut = registerAsync( async_id, // match predicate - [async_id](const proto::FfiEvent &event) { - return event.has_perform_rpc() && - event.perform_rpc().async_id() == async_id; + [async_id](const proto::FfiEvent& event) { + return event.has_perform_rpc() && event.perform_rpc().async_id() == async_id; }, - [](const proto::FfiEvent &event, std::promise &pr) { - const auto &cb = event.perform_rpc(); + [](const proto::FfiEvent& event, std::promise& pr) { + const auto& cb = event.perform_rpc(); if (cb.has_error()) { // RpcError is a proto message; convert to C++ RpcError and throw - pr.set_exception( - std::make_exception_ptr(RpcError::fromProto(cb.error()))); + pr.set_exception(std::make_exception_ptr(RpcError::fromProto(cb.error()))); return; } pr.set_value(cb.payload()); @@ -891,7 +820,7 @@ FfiClient::performRpcAsync(std::uint64_t local_participant_handle, // Build and send the request proto::FfiRequest req; - auto *msg = req.mutable_perform_rpc(); + auto* msg = req.mutable_perform_rpc(); msg->set_local_participant_handle(local_participant_handle); msg->set_destination_identity(destination_identity); msg->set_method(method); @@ -914,26 +843,23 @@ FfiClient::performRpcAsync(std::uint64_t local_participant_handle, return fut; } -std::future FfiClient::sendStreamHeaderAsync( - std::uint64_t local_participant_handle, - const proto::DataStream::Header &header, - const std::vector &destination_identities, - const std::string &sender_identity) { +std::future FfiClient::sendStreamHeaderAsync(std::uint64_t local_participant_handle, + const proto::DataStream::Header& header, + const std::vector& destination_identities, + const std::string& sender_identity) { // Generate client-side async_id first const AsyncId async_id = generateAsyncId(); // Register the async handler BEFORE sending the request auto fut = registerAsync( async_id, - [async_id](const proto::FfiEvent &e) { - return e.has_send_stream_header() && - e.send_stream_header().async_id() == async_id; + [async_id](const proto::FfiEvent& e) { + return e.has_send_stream_header() && e.send_stream_header().async_id() == async_id; }, - [](const proto::FfiEvent &e, std::promise &pr) { - const auto &cb = e.send_stream_header(); + [](const proto::FfiEvent& e, std::promise& pr) { + const auto& cb = e.send_stream_header(); if (!cb.error().empty()) { - pr.set_exception( - std::make_exception_ptr(std::runtime_error(cb.error()))); + pr.set_exception(std::make_exception_ptr(std::runtime_error(cb.error()))); return; } pr.set_value(); @@ -941,12 +867,12 @@ std::future FfiClient::sendStreamHeaderAsync( // Build and send the request proto::FfiRequest req; - auto *msg = req.mutable_send_stream_header(); + auto* msg = req.mutable_send_stream_header(); msg->set_local_participant_handle(local_participant_handle); *msg->mutable_header() = header; msg->set_sender_identity(sender_identity); msg->set_request_async_id(async_id); - for (const auto &id : destination_identities) { + for (const auto& id : destination_identities) { msg->add_destination_identities(id); } @@ -963,26 +889,23 @@ std::future FfiClient::sendStreamHeaderAsync( return fut; } -std::future FfiClient::sendStreamChunkAsync( - std::uint64_t local_participant_handle, - const proto::DataStream::Chunk &chunk, - const std::vector &destination_identities, - const std::string &sender_identity) { +std::future FfiClient::sendStreamChunkAsync(std::uint64_t local_participant_handle, + const proto::DataStream::Chunk& chunk, + const std::vector& destination_identities, + const std::string& sender_identity) { // Generate client-side async_id first const AsyncId async_id = generateAsyncId(); // Register the async handler BEFORE sending the request auto fut = registerAsync( async_id, - [async_id](const proto::FfiEvent &e) { - return e.has_send_stream_chunk() && - e.send_stream_chunk().async_id() == async_id; + [async_id](const proto::FfiEvent& e) { + return e.has_send_stream_chunk() && e.send_stream_chunk().async_id() == async_id; }, - [](const proto::FfiEvent &e, std::promise &pr) { - const auto &cb = e.send_stream_chunk(); + [](const proto::FfiEvent& e, std::promise& pr) { + const auto& cb = e.send_stream_chunk(); if (!cb.error().empty()) { - pr.set_exception( - std::make_exception_ptr(std::runtime_error(cb.error()))); + pr.set_exception(std::make_exception_ptr(std::runtime_error(cb.error()))); return; } pr.set_value(); @@ -990,12 +913,12 @@ std::future FfiClient::sendStreamChunkAsync( // Build and send the request proto::FfiRequest req; - auto *msg = req.mutable_send_stream_chunk(); + auto* msg = req.mutable_send_stream_chunk(); msg->set_local_participant_handle(local_participant_handle); *msg->mutable_chunk() = chunk; msg->set_sender_identity(sender_identity); msg->set_request_async_id(async_id); - for (const auto &id : destination_identities) { + for (const auto& id : destination_identities) { msg->add_destination_identities(id); } @@ -1012,25 +935,22 @@ std::future FfiClient::sendStreamChunkAsync( return fut; } -std::future -FfiClient::sendStreamTrailerAsync(std::uint64_t local_participant_handle, - const proto::DataStream::Trailer &trailer, - const std::string &sender_identity) { +std::future FfiClient::sendStreamTrailerAsync(std::uint64_t local_participant_handle, + const proto::DataStream::Trailer& trailer, + const std::string& sender_identity) { // Generate client-side async_id first const AsyncId async_id = generateAsyncId(); // Register the async handler BEFORE sending the request auto fut = registerAsync( async_id, - [async_id](const proto::FfiEvent &e) { - return e.has_send_stream_trailer() && - e.send_stream_trailer().async_id() == async_id; + [async_id](const proto::FfiEvent& e) { + return e.has_send_stream_trailer() && e.send_stream_trailer().async_id() == async_id; }, - [](const proto::FfiEvent &e, std::promise &pr) { - const auto &cb = e.send_stream_trailer(); + [](const proto::FfiEvent& e, std::promise& pr) { + const auto& cb = e.send_stream_trailer(); if (!cb.error().empty()) { - pr.set_exception( - std::make_exception_ptr(std::runtime_error(cb.error()))); + pr.set_exception(std::make_exception_ptr(std::runtime_error(cb.error()))); return; } pr.set_value(); @@ -1038,7 +958,7 @@ FfiClient::sendStreamTrailerAsync(std::uint64_t local_participant_handle, // Build and send the request proto::FfiRequest req; - auto *msg = req.mutable_send_stream_trailer(); + auto* msg = req.mutable_send_stream_trailer(); msg->set_local_participant_handle(local_participant_handle); *msg->mutable_trailer() = trailer; msg->set_sender_identity(sender_identity); diff --git a/src/ffi_client.h b/src/ffi_client.h index 182dc143..2879f2d2 100644 --- a/src/ffi_client.h +++ b/src/ffi_client.h @@ -53,30 +53,28 @@ class DataStream; struct RoomOptions; struct TrackPublishOptions; -using FfiCallbackFn = void (*)(const uint8_t *, size_t); -extern "C" void livekit_ffi_initialize(FfiCallbackFn cb, bool capture_logs, - const char *sdk, - const char *sdk_version); +using FfiCallbackFn = void (*)(const uint8_t*, size_t); +extern "C" void livekit_ffi_initialize(FfiCallbackFn cb, bool capture_logs, const char* sdk, const char* sdk_version); extern "C" void livekit_ffi_dispose(); -extern "C" void LivekitFfiCallback(const uint8_t *buf, size_t len); +extern "C" void LivekitFfiCallback(const uint8_t* buf, size_t len); // The FfiClient is used to communicate with the FFI interface of the Rust SDK // We use the generated protocol messages to facilitate the communication class FfiClient { public: using ListenerId = int; - using Listener = std::function; + using Listener = std::function; using AsyncId = std::uint64_t; ~FfiClient(); - FfiClient(const FfiClient &) = delete; - FfiClient &operator=(const FfiClient &) = delete; - FfiClient(FfiClient &&) = delete; - FfiClient &operator=(FfiClient &&) = delete; + FfiClient(const FfiClient&) = delete; + FfiClient& operator=(const FfiClient&) = delete; + FfiClient(FfiClient&&) = delete; + FfiClient& operator=(FfiClient&&) = delete; - static FfiClient &instance() noexcept { + static FfiClient& instance() noexcept { static FfiClient instance; return instance; } @@ -90,76 +88,58 @@ class FfiClient { bool isInitialized() const noexcept; - ListenerId AddListener(const Listener &listener); + ListenerId AddListener(const Listener& listener); void RemoveListener(ListenerId id); // Room APIs - std::future connectAsync(const std::string &url, - const std::string &token, - const RoomOptions &options); + std::future connectAsync(const std::string& url, const std::string& token, + const RoomOptions& options); // Track APIs std::future> getTrackStatsAsync(uintptr_t track_handle); // Participant APIs - std::future - publishTrackAsync(std::uint64_t local_participant_handle, - std::uint64_t track_handle, - const TrackPublishOptions &options); - std::future unpublishTrackAsync(std::uint64_t local_participant_handle, - const std::string &track_sid, + std::future publishTrackAsync(std::uint64_t local_participant_handle, + std::uint64_t track_handle, + const TrackPublishOptions& options); + std::future unpublishTrackAsync(std::uint64_t local_participant_handle, const std::string& track_sid, bool stop_on_unpublish); - std::future - publishDataAsync(std::uint64_t local_participant_handle, - const std::uint8_t *data_ptr, std::uint64_t data_len, - bool reliable, - const std::vector &destination_identities, - const std::string &topic); - std::future - publishSipDtmfAsync(std::uint64_t local_participant_handle, - std::uint32_t code, const std::string &digit, - const std::vector &destination_identities); - std::future - setLocalMetadataAsync(std::uint64_t local_participant_handle, - const std::string &metadata); - std::future - captureAudioFrameAsync(std::uint64_t source_handle, - const proto::AudioFrameBufferInfo &buffer); - std::future performRpcAsync( - std::uint64_t local_participant_handle, - const std::string &destination_identity, const std::string &method, - const std::string &payload, - std::optional response_timeout_ms = std::nullopt); + std::future publishDataAsync(std::uint64_t local_participant_handle, const std::uint8_t* data_ptr, + std::uint64_t data_len, bool reliable, + const std::vector& destination_identities, const std::string& topic); + std::future publishSipDtmfAsync(std::uint64_t local_participant_handle, std::uint32_t code, + const std::string& digit, + const std::vector& destination_identities); + std::future setLocalMetadataAsync(std::uint64_t local_participant_handle, const std::string& metadata); + std::future captureAudioFrameAsync(std::uint64_t source_handle, const proto::AudioFrameBufferInfo& buffer); + std::future performRpcAsync(std::uint64_t local_participant_handle, + const std::string& destination_identity, const std::string& method, + const std::string& payload, + std::optional response_timeout_ms = std::nullopt); // Data Track APIs - std::future> - publishDataTrackAsync(std::uint64_t local_participant_handle, - const std::string &track_name); + std::future> publishDataTrackAsync( + std::uint64_t local_participant_handle, const std::string& track_name); - Result - subscribeDataTrack(std::uint64_t track_handle, - std::optional buffer_size = std::nullopt); + Result subscribeDataTrack( + std::uint64_t track_handle, std::optional buffer_size = std::nullopt); // Data stream functionalities - std::future - sendStreamHeaderAsync(std::uint64_t local_participant_handle, - const proto::DataStream::Header &header, - const std::vector &destination_identities, - const std::string &sender_identity); - std::future - sendStreamChunkAsync(std::uint64_t local_participant_handle, - const proto::DataStream::Chunk &chunk, - const std::vector &destination_identities, - const std::string &sender_identity); - std::future - sendStreamTrailerAsync(std::uint64_t local_participant_handle, - const proto::DataStream::Trailer &trailer, - const std::string &sender_identity); + std::future sendStreamHeaderAsync(std::uint64_t local_participant_handle, + const proto::DataStream::Header& header, + const std::vector& destination_identities, + const std::string& sender_identity); + std::future sendStreamChunkAsync(std::uint64_t local_participant_handle, const proto::DataStream::Chunk& chunk, + const std::vector& destination_identities, + const std::string& sender_identity); + std::future sendStreamTrailerAsync(std::uint64_t local_participant_handle, + const proto::DataStream::Trailer& trailer, + const std::string& sender_identity); // Generic function for sending a request to the Rust FFI. // Note: For asynchronous requests, use the dedicated async functions instead // of sendRequest. - proto::FfiResponse sendRequest(const proto::FfiRequest &request) const; + proto::FfiResponse sendRequest(const proto::FfiRequest& request) const; private: FfiClient() = default; @@ -168,40 +148,34 @@ class FfiClient { struct PendingBase { AsyncId async_id = 0; // Client-generated async ID for cancellation virtual ~PendingBase() = default; - virtual bool matches(const proto::FfiEvent &event) const = 0; - virtual void complete(const proto::FfiEvent &event) = 0; + virtual bool matches(const proto::FfiEvent& event) const = 0; + virtual void complete(const proto::FfiEvent& event) = 0; virtual void cancel() = 0; // Cancel the pending operation }; - template struct Pending : PendingBase { + template + struct Pending : PendingBase { std::promise promise; - std::function match; - std::function &)> handler; + std::function match; + std::function&)> handler; - bool matches(const proto::FfiEvent &event) const override { - return match && match(event); - } + bool matches(const proto::FfiEvent& event) const override { return match && match(event); } - void complete(const proto::FfiEvent &event) override { - handler(event, promise); - } + void complete(const proto::FfiEvent& event) override { handler(event, promise); } void cancel() override { try { - promise.set_exception(std::make_exception_ptr( - std::runtime_error("Async operation cancelled"))); - } catch (const std::future_error &e) { - // Unlikely to throw here as the promise should be satisfied before cancel() - // Logging a debug message to avoid clang empty catch warning - LK_LOG_DEBUG("FfiClient::cancel: promise already satisfied: {}", - e.what()); + promise.set_exception(std::make_exception_ptr(std::runtime_error("Async operation cancelled"))); + } catch (const std::future_error& e) { + // Unlikely to throw here as the promise should be satisfied before + // cancel() Logging a debug message to avoid clang empty catch warning + LK_LOG_DEBUG("FfiClient::cancel: promise already satisfied: {}", e.what()); } } }; template - std::future registerAsync( - AsyncId async_id, std::function match, - std::function &)> handler); + std::future registerAsync(AsyncId async_id, std::function match, + std::function&)> handler); // Generate a unique client-side async ID for request correlation AsyncId generateAsyncId(); @@ -213,12 +187,11 @@ class FfiClient { std::unordered_map listeners_; std::atomic next_listener_id{1}; mutable std::mutex lock_; - mutable std::unordered_map> - pending_by_id_; + mutable std::unordered_map> pending_by_id_; std::atomic next_async_id_{1}; - void PushEvent(const proto::FfiEvent &event) const; - friend void LivekitFfiCallback(const uint8_t *buf, size_t len); + void PushEvent(const proto::FfiEvent& event) const; + friend void LivekitFfiCallback(const uint8_t* buf, size_t len); std::atomic initialized_{false}; }; } // namespace livekit diff --git a/src/ffi_handle.cpp b/src/ffi_handle.cpp index 4c28effb..b19ebe94 100644 --- a/src/ffi_handle.cpp +++ b/src/ffi_handle.cpp @@ -15,6 +15,7 @@ */ #include "livekit/ffi_handle.h" + #include "livekit_ffi.h" namespace livekit { @@ -23,9 +24,9 @@ FfiHandle::FfiHandle(uintptr_t h) noexcept : handle_(h) {} FfiHandle::~FfiHandle() { reset(); } -FfiHandle::FfiHandle(FfiHandle &&other) noexcept : handle_(other.release()) {} +FfiHandle::FfiHandle(FfiHandle&& other) noexcept : handle_(other.release()) {} -FfiHandle &FfiHandle::operator=(FfiHandle &&other) noexcept { +FfiHandle& FfiHandle::operator=(FfiHandle&& other) noexcept { if (this != &other) { reset(other.release()); } diff --git a/src/livekit.cpp b/src/livekit.cpp index b9132efc..1bcd9df9 100644 --- a/src/livekit.cpp +++ b/src/livekit.cpp @@ -15,19 +15,20 @@ */ #include "livekit/livekit.h" + #include "ffi_client.h" #include "lk_log.h" namespace livekit { -bool initialize(const LogLevel &level, const LogSink &log_sink) { +bool initialize(const LogLevel& level, const LogSink& log_sink) { setLogLevel(level); - auto &ffi_client = FfiClient::instance(); + auto& ffi_client = FfiClient::instance(); return ffi_client.initialize(log_sink == LogSink::kCallback); } void shutdown() { - auto &ffi_client = FfiClient::instance(); + auto& ffi_client = FfiClient::instance(); ffi_client.shutdown(); detail::shutdownLogger(); } diff --git a/src/lk_log.h b/src/lk_log.h index 21a064ba..a42a441f 100644 --- a/src/lk_log.h +++ b/src/lk_log.h @@ -17,9 +17,10 @@ #ifndef LIVEKIT_LK_LOG_H #define LIVEKIT_LK_LOG_H -#include #include +#include + namespace livekit::detail { /// Returns the shared "livekit" logger instance. @@ -42,17 +43,11 @@ void shutdownLogger(); // // Default LIVEKIT_LOG_LEVEL is TRACE (nothing stripped). For release builds // consider -DLIVEKIT_LOG_LEVEL=INFO or WARN to eliminate verbose calls. -#define LK_LOG_TRACE(...) \ - SPDLOG_LOGGER_TRACE(livekit::detail::getLogger(), __VA_ARGS__) -#define LK_LOG_DEBUG(...) \ - SPDLOG_LOGGER_DEBUG(livekit::detail::getLogger(), __VA_ARGS__) -#define LK_LOG_INFO(...) \ - SPDLOG_LOGGER_INFO(livekit::detail::getLogger(), __VA_ARGS__) -#define LK_LOG_WARN(...) \ - SPDLOG_LOGGER_WARN(livekit::detail::getLogger(), __VA_ARGS__) -#define LK_LOG_ERROR(...) \ - SPDLOG_LOGGER_ERROR(livekit::detail::getLogger(), __VA_ARGS__) -#define LK_LOG_CRITICAL(...) \ - SPDLOG_LOGGER_CRITICAL(livekit::detail::getLogger(), __VA_ARGS__) +#define LK_LOG_TRACE(...) SPDLOG_LOGGER_TRACE(livekit::detail::getLogger(), __VA_ARGS__) +#define LK_LOG_DEBUG(...) SPDLOG_LOGGER_DEBUG(livekit::detail::getLogger(), __VA_ARGS__) +#define LK_LOG_INFO(...) SPDLOG_LOGGER_INFO(livekit::detail::getLogger(), __VA_ARGS__) +#define LK_LOG_WARN(...) SPDLOG_LOGGER_WARN(livekit::detail::getLogger(), __VA_ARGS__) +#define LK_LOG_ERROR(...) SPDLOG_LOGGER_ERROR(livekit::detail::getLogger(), __VA_ARGS__) +#define LK_LOG_CRITICAL(...) SPDLOG_LOGGER_CRITICAL(livekit::detail::getLogger(), __VA_ARGS__) #endif /* LIVEKIT_LK_LOG_H */ diff --git a/src/local_audio_track.cpp b/src/local_audio_track.cpp index 93808654..720b8740 100644 --- a/src/local_audio_track.cpp +++ b/src/local_audio_track.cpp @@ -24,25 +24,21 @@ namespace livekit { -LocalAudioTrack::LocalAudioTrack(FfiHandle handle, - const proto::OwnedTrack &track) - : Track(std::move(handle), track.info().sid(), track.info().name(), - fromProto(track.info().kind()), - fromProto(track.info().stream_state()), track.info().muted(), - false) {} +LocalAudioTrack::LocalAudioTrack(FfiHandle handle, const proto::OwnedTrack& track) + : Track(std::move(handle), track.info().sid(), track.info().name(), fromProto(track.info().kind()), + fromProto(track.info().stream_state()), track.info().muted(), false) {} -std::shared_ptr LocalAudioTrack::createLocalAudioTrack( - const std::string &name, const std::shared_ptr &source) { +std::shared_ptr LocalAudioTrack::createLocalAudioTrack(const std::string& name, + const std::shared_ptr& source) { proto::FfiRequest req; - auto *msg = req.mutable_create_audio_track(); + auto* msg = req.mutable_create_audio_track(); msg->set_name(name); msg->set_source_handle(static_cast(source->ffi_handle_id())); const proto::FfiResponse resp = FfiClient::instance().sendRequest(req); - const proto::OwnedTrack &owned = resp.create_audio_track().track(); + const proto::OwnedTrack& owned = resp.create_audio_track().track(); FfiHandle handle(static_cast(owned.handle().id())); - return std::shared_ptr( - new LocalAudioTrack(std::move(handle), owned)); + return std::shared_ptr(new LocalAudioTrack(std::move(handle), owned)); } void LocalAudioTrack::mute() { @@ -52,7 +48,7 @@ void LocalAudioTrack::mute() { } proto::FfiRequest req; - auto *msg = req.mutable_local_track_mute(); + auto* msg = req.mutable_local_track_mute(); msg->set_track_handle(static_cast(ffi_handle_id())); msg->set_mute(true); @@ -67,7 +63,7 @@ void LocalAudioTrack::unmute() { } proto::FfiRequest req; - auto *msg = req.mutable_local_track_mute(); + auto* msg = req.mutable_local_track_mute(); msg->set_track_handle(static_cast(ffi_handle_id())); msg->set_mute(false); @@ -75,8 +71,6 @@ void LocalAudioTrack::unmute() { setMuted(false); } -std::string LocalAudioTrack::to_string() const { - return "rtc.LocalAudioTrack(sid=" + sid() + ", name=" + name() + ")"; -} +std::string LocalAudioTrack::to_string() const { return "rtc.LocalAudioTrack(sid=" + sid() + ", name=" + name() + ")"; } } // namespace livekit \ No newline at end of file diff --git a/src/local_data_track.cpp b/src/local_data_track.cpp index 3c24a9cf..6173ab28 100644 --- a/src/local_data_track.cpp +++ b/src/local_data_track.cpp @@ -16,59 +16,52 @@ #include "livekit/local_data_track.h" -#include "livekit/data_track_error.h" -#include "lk_log.h" - #include "data_track.pb.h" #include "ffi.pb.h" #include "ffi_client.h" +#include "livekit/data_track_error.h" +#include "lk_log.h" namespace livekit { -LocalDataTrack::LocalDataTrack(const proto::OwnedLocalDataTrack &owned) +LocalDataTrack::LocalDataTrack(const proto::OwnedLocalDataTrack& owned) : handle_(static_cast(owned.handle().id())) { - const auto &pi = owned.info(); + const auto& pi = owned.info(); info_.name = pi.name(); info_.sid = pi.sid(); info_.uses_e2ee = pi.uses_e2ee(); } -Result -LocalDataTrack::tryPush(const DataTrackFrame &frame) { +Result LocalDataTrack::tryPush(const DataTrackFrame& frame) { if (!handle_.valid()) { - return Result::failure( - LocalDataTrackTryPushError{ - LocalDataTrackTryPushErrorCode::INVALID_HANDLE, - "LocalDataTrack::tryPush: invalid FFI handle"}); + return Result::failure(LocalDataTrackTryPushError{ + LocalDataTrackTryPushErrorCode::INVALID_HANDLE, "LocalDataTrack::tryPush: invalid FFI handle"}); } try { proto::FfiRequest req; - auto *msg = req.mutable_local_data_track_try_push(); + auto* msg = req.mutable_local_data_track_try_push(); msg->set_track_handle(static_cast(handle_.get())); - auto *pf = msg->mutable_frame(); + auto* pf = msg->mutable_frame(); pf->set_payload(frame.payload.data(), frame.payload.size()); if (frame.user_timestamp.has_value()) { pf->set_user_timestamp(frame.user_timestamp.value()); } const proto::FfiResponse resp = FfiClient::instance().sendRequest(req); - const auto &r = resp.local_data_track_try_push(); + const auto& r = resp.local_data_track_try_push(); if (r.has_error()) { - return Result::failure( - LocalDataTrackTryPushError::fromProto(r.error())); + return Result::failure(LocalDataTrackTryPushError::fromProto(r.error())); } return Result::success(); - } catch (const std::exception &e) { + } catch (const std::exception& e) { return Result::failure( - LocalDataTrackTryPushError{LocalDataTrackTryPushErrorCode::INTERNAL, - e.what()}); + LocalDataTrackTryPushError{LocalDataTrackTryPushErrorCode::INTERNAL, e.what()}); } } -Result -LocalDataTrack::tryPush(std::vector &&payload, - std::optional user_timestamp) { +Result LocalDataTrack::tryPush(std::vector&& payload, + std::optional user_timestamp) { DataTrackFrame frame; frame.payload = std::move(payload); frame.user_timestamp = user_timestamp; @@ -81,7 +74,7 @@ bool LocalDataTrack::isPublished() const { } proto::FfiRequest req; - auto *msg = req.mutable_local_data_track_is_published(); + auto* msg = req.mutable_local_data_track_is_published(); msg->set_track_handle(static_cast(handle_.get())); const proto::FfiResponse resp = FfiClient::instance().sendRequest(req); @@ -94,7 +87,7 @@ void LocalDataTrack::unpublishDataTrack() { } proto::FfiRequest req; - auto *msg = req.mutable_local_data_track_unpublish(); + auto* msg = req.mutable_local_data_track_unpublish(); msg->set_track_handle(static_cast(handle_.get())); (void)FfiClient::instance().sendRequest(req); diff --git a/src/local_participant.cpp b/src/local_participant.cpp index d33b7f2a..bbeda2fb 100644 --- a/src/local_participant.cpp +++ b/src/local_participant.cpp @@ -16,6 +16,12 @@ #include "livekit/local_participant.h" +#include +#include + +#include "data_track.pb.h" +#include "ffi.pb.h" +#include "ffi_client.h" #include "livekit/ffi_handle.h" #include "livekit/local_audio_track.h" #include "livekit/local_data_track.h" @@ -23,23 +29,15 @@ #include "livekit/local_video_track.h" #include "livekit/room_delegate.h" #include "livekit/track.h" - -#include "data_track.pb.h" -#include "ffi.pb.h" -#include "ffi_client.h" #include "participant.pb.h" #include "room.pb.h" #include "room_proto_converter.h" #include "track.pb.h" #include "track_proto_converter.h" -#include -#include - namespace { -std::shared_ptr -localTrackPublication(const std::shared_ptr &t) { +std::shared_ptr localTrackPublication(const std::shared_ptr& t) { if (!t) { return nullptr; } @@ -59,67 +57,56 @@ namespace livekit { using proto::FfiRequest; using proto::FfiResponse; -LocalParticipant::LocalParticipant( - FfiHandle handle, std::string sid, std::string name, std::string identity, - std::string metadata, - std::unordered_map attributes, - ParticipantKind kind, DisconnectReason reason) - : Participant(std::move(handle), std::move(sid), std::move(name), - std::move(identity), std::move(metadata), +LocalParticipant::LocalParticipant(FfiHandle handle, std::string sid, std::string name, std::string identity, + std::string metadata, std::unordered_map attributes, + ParticipantKind kind, DisconnectReason reason) + : Participant(std::move(handle), std::move(sid), std::move(name), std::move(identity), std::move(metadata), std::move(attributes), kind, reason) {} -void LocalParticipant::publishData( - const std::vector &payload, bool reliable, - const std::vector &destination_identities, - const std::string &topic) { +void LocalParticipant::publishData(const std::vector& payload, bool reliable, + const std::vector& destination_identities, const std::string& topic) { if (payload.empty()) { return; } auto handle_id = ffiHandleId(); if (handle_id == 0) { - throw std::runtime_error( - "LocalParticipant::publishData: invalid FFI handle"); + throw std::runtime_error("LocalParticipant::publishData: invalid FFI handle"); } // Use async FFI API and block until completion. - auto fut = FfiClient::instance().publishDataAsync( - static_cast(handle_id), payload.data(), - static_cast(payload.size()), reliable, - destination_identities, topic); + auto fut = FfiClient::instance().publishDataAsync(static_cast(handle_id), payload.data(), + static_cast(payload.size()), reliable, + destination_identities, topic); fut.get(); } -void LocalParticipant::publishDtmf(int code, const std::string &digit) { +void LocalParticipant::publishDtmf(int code, const std::string& digit) { auto handle_id = ffiHandleId(); if (handle_id == 0) { - throw std::runtime_error( - "LocalParticipant::publishDtmf: invalid FFI handle"); + throw std::runtime_error("LocalParticipant::publishDtmf: invalid FFI handle"); } // TODO, should we take destination as inputs? const std::vector destination_identities; - auto fut = FfiClient::instance().publishSipDtmfAsync( - static_cast(handle_id), static_cast(code), - digit, destination_identities); + auto fut = FfiClient::instance().publishSipDtmfAsync(static_cast(handle_id), + static_cast(code), digit, destination_identities); fut.get(); } -void LocalParticipant::setMetadata(const std::string &metadata) { +void LocalParticipant::setMetadata(const std::string& metadata) { auto handle_id = ffiHandleId(); if (handle_id == 0) { - throw std::runtime_error( - "LocalParticipant::setMetadata: invalid FFI handle"); + throw std::runtime_error("LocalParticipant::setMetadata: invalid FFI handle"); } - auto fut = FfiClient::instance().setLocalMetadataAsync( - static_cast(handle_id), metadata); + auto fut = FfiClient::instance().setLocalMetadataAsync(static_cast(handle_id), metadata); fut.get(); } -void LocalParticipant::setName(const std::string &name) { +void LocalParticipant::setName(const std::string& name) { auto handle_id = ffiHandleId(); if (handle_id == 0) { throw std::runtime_error("LocalParticipant::setName: invalid FFI handle"); @@ -128,28 +115,26 @@ void LocalParticipant::setName(const std::string &name) { // No async helper defined for SetLocalName in FfiClient yet, so keep using // the direct request. FfiRequest req; - auto *msg = req.mutable_set_local_name(); + auto* msg = req.mutable_set_local_name(); msg->set_local_participant_handle(static_cast(handle_id)); msg->set_name(name); (void)FfiClient::instance().sendRequest(req); } -void LocalParticipant::setAttributes( - const std::unordered_map &attributes) { +void LocalParticipant::setAttributes(const std::unordered_map& attributes) { auto handle_id = ffiHandleId(); if (handle_id == 0) { - throw std::runtime_error( - "LocalParticipant::setAttributes: invalid FFI handle"); + throw std::runtime_error("LocalParticipant::setAttributes: invalid FFI handle"); } // No async helper defined for SetLocalAttributes in FfiClient yet. FfiRequest req; - auto *msg = req.mutable_set_local_attributes(); + auto* msg = req.mutable_set_local_attributes(); msg->set_local_participant_handle(static_cast(handle_id)); - for (const auto &kv : attributes) { - auto *entry = msg->add_attributes(); + for (const auto& kv : attributes) { + auto* entry = msg->add_attributes(); entry->set_key(kv.first); entry->set_value(kv.second); } @@ -162,8 +147,7 @@ void LocalParticipant::setAttributes( // ---------------------------------------------------------------------------- void LocalParticipant::setTrackSubscriptionPermissions( - bool allow_all_participants, - const std::vector &participant_permissions) { + bool allow_all_participants, const std::vector& participant_permissions) { auto handle_id = ffiHandleId(); if (handle_id == 0) { throw std::runtime_error( @@ -173,12 +157,12 @@ void LocalParticipant::setTrackSubscriptionPermissions( // No dedicated async helper; do it directly. FfiRequest req; - auto *msg = req.mutable_set_track_subscription_permissions(); + auto* msg = req.mutable_set_track_subscription_permissions(); msg->set_local_participant_handle(static_cast(handle_id)); msg->set_all_participants_allowed(allow_all_participants); - for (const auto &perm : participant_permissions) { - auto *p = msg->add_permissions(); + for (const auto& perm : participant_permissions) { + auto* p = msg->add_permissions(); p->CopyFrom(toProto(perm)); } @@ -189,27 +173,22 @@ void LocalParticipant::setTrackSubscriptionPermissions( // Track publish / unpublish // ---------------------------------------------------------------------------- -void LocalParticipant::publishTrack(const std::shared_ptr &track, - const TrackPublishOptions &options) { +void LocalParticipant::publishTrack(const std::shared_ptr& track, const TrackPublishOptions& options) { if (!track) { - throw std::invalid_argument( - "LocalParticipant::publishTrack: track is null"); + throw std::invalid_argument("LocalParticipant::publishTrack: track is null"); } auto participant_handle = ffiHandleId(); if (participant_handle == 0) { - throw std::runtime_error( - "LocalParticipant::publishTrack: invalid participant FFI handle"); + throw std::runtime_error("LocalParticipant::publishTrack: invalid participant FFI handle"); } auto track_handle = track->ffi_handle_id(); if (track_handle == 0) { - throw std::runtime_error( - "LocalParticipant::publishTrack: invalid track FFI handle"); + throw std::runtime_error("LocalParticipant::publishTrack: invalid track FFI handle"); } - auto fut = FfiClient::instance().publishTrackAsync( - static_cast(participant_handle), - static_cast(track_handle), options); + auto fut = FfiClient::instance().publishTrackAsync(static_cast(participant_handle), + static_cast(track_handle), options); // Will throw if the async op fails (error in callback). const proto::OwnedTrackPublication owned_pub = fut.get(); @@ -223,10 +202,9 @@ void LocalParticipant::publishTrack(const std::shared_ptr &track, track->setPublication(publication); } -std::shared_ptr -LocalParticipant::publishVideoTrack(const std::string &name, - const std::shared_ptr &source, - TrackSource track_source) { +std::shared_ptr LocalParticipant::publishVideoTrack(const std::string& name, + const std::shared_ptr& source, + TrackSource track_source) { auto track = LocalVideoTrack::createLocalVideoTrack(name, source); TrackPublishOptions opts; opts.source = track_source; @@ -234,10 +212,9 @@ LocalParticipant::publishVideoTrack(const std::string &name, return track; } -std::shared_ptr -LocalParticipant::publishAudioTrack(const std::string &name, - const std::shared_ptr &source, - TrackSource track_source) { +std::shared_ptr LocalParticipant::publishAudioTrack(const std::string& name, + const std::shared_ptr& source, + TrackSource track_source) { auto track = LocalAudioTrack::createLocalAudioTrack(name, source); TrackPublishOptions opts; opts.source = track_source; @@ -245,25 +222,22 @@ LocalParticipant::publishAudioTrack(const std::string &name, return track; } -void LocalParticipant::unpublishTrack(const std::string &track_sid) { +void LocalParticipant::unpublishTrack(const std::string& track_sid) { if (track_sid.empty()) { return; } auto handle_id = ffiHandleId(); if (handle_id == 0) { - throw std::runtime_error( - "LocalParticipant::unpublishTrack: invalid FFI handle"); + throw std::runtime_error("LocalParticipant::unpublishTrack: invalid FFI handle"); } - auto fut = FfiClient::instance().unpublishTrackAsync( - static_cast(handle_id), track_sid, - /*stop_on_unpublish=*/true); + auto fut = FfiClient::instance().unpublishTrackAsync(static_cast(handle_id), track_sid, + /*stop_on_unpublish=*/true); fut.get(); - if (auto it = published_tracks_by_sid_.find(track_sid); - it != published_tracks_by_sid_.end()) { + if (auto it = published_tracks_by_sid_.find(track_sid); it != published_tracks_by_sid_.end()) { if (auto t = it->second.lock()) { t->setPublication(nullptr); } @@ -273,8 +247,7 @@ void LocalParticipant::unpublishTrack(const std::string &track_sid) { LocalParticipant::PublicationMap LocalParticipant::trackPublications() const { PublicationMap out; - for (auto it = published_tracks_by_sid_.begin(); - it != published_tracks_by_sid_.end();) { + for (auto it = published_tracks_by_sid_.begin(); it != published_tracks_by_sid_.end();) { auto t = it->second.lock(); if (!t) { it = published_tracks_by_sid_.erase(it); @@ -288,33 +261,28 @@ LocalParticipant::PublicationMap LocalParticipant::trackPublications() const { return out; } -Result, PublishDataTrackError> -LocalParticipant::publishDataTrack(const std::string &name) { +Result, PublishDataTrackError> LocalParticipant::publishDataTrack( + const std::string& name) { auto handle_id = ffiHandleId(); if (handle_id == 0) { - return Result, - PublishDataTrackError>::failure(PublishDataTrackError{ - PublishDataTrackErrorCode::INVALID_HANDLE, - "LocalParticipant::publishDataTrack: invalid FFI " - "handle"}); + return Result, PublishDataTrackError>::failure( + PublishDataTrackError{PublishDataTrackErrorCode::INVALID_HANDLE, + "LocalParticipant::publishDataTrack: invalid FFI " + "handle"}); } - auto fut = FfiClient::instance().publishDataTrackAsync( - static_cast(handle_id), name); + auto fut = FfiClient::instance().publishDataTrackAsync(static_cast(handle_id), name); auto result = fut.get(); if (!result) { - return Result, - PublishDataTrackError>::failure(std::move(result).error()); + return Result, PublishDataTrackError>::failure(std::move(result).error()); } - return Result, PublishDataTrackError>:: - success( - std::shared_ptr(new LocalDataTrack(result.value()))); + return Result, PublishDataTrackError>::success( + std::shared_ptr(new LocalDataTrack(result.value()))); } -void LocalParticipant::unpublishDataTrack( - const std::shared_ptr &track) { +void LocalParticipant::unpublishDataTrack(const std::shared_ptr& track) { if (!track) { return; } @@ -322,13 +290,11 @@ void LocalParticipant::unpublishDataTrack( track->unpublishDataTrack(); } -std::string LocalParticipant::performRpc( - const std::string &destination_identity, const std::string &method, - const std::string &payload, const std::optional &response_timeout) { +std::string LocalParticipant::performRpc(const std::string& destination_identity, const std::string& method, + const std::string& payload, const std::optional& response_timeout) { auto handle_id = ffiHandleId(); if (handle_id == 0) { - throw std::runtime_error( - "LocalParticipant::performRpc: invalid FFI handle"); + throw std::runtime_error("LocalParticipant::performRpc: invalid FFI handle"); } std::uint32_t timeout_ms = 0; @@ -339,37 +305,33 @@ std::string LocalParticipant::performRpc( } auto fut = FfiClient::instance().performRpcAsync( - static_cast(handle_id), destination_identity, method, - payload, + static_cast(handle_id), destination_identity, method, payload, has_timeout ? std::optional(timeout_ms) : std::nullopt); return fut.get(); } -void LocalParticipant::registerRpcMethod(const std::string &method_name, - RpcHandler handler) { +void LocalParticipant::registerRpcMethod(const std::string& method_name, RpcHandler handler) { auto handle_id = ffiHandleId(); if (handle_id == 0) { - throw std::runtime_error( - "LocalParticipant::registerRpcMethod: invalid FFI handle"); + throw std::runtime_error("LocalParticipant::registerRpcMethod: invalid FFI handle"); } rpc_handlers_[method_name] = std::move(handler); FfiRequest req; - auto *msg = req.mutable_register_rpc_method(); + auto* msg = req.mutable_register_rpc_method(); msg->set_local_participant_handle(static_cast(handle_id)); msg->set_method(method_name); (void)FfiClient::instance().sendRequest(req); } -void LocalParticipant::unregisterRpcMethod(const std::string &method_name) { +void LocalParticipant::unregisterRpcMethod(const std::string& method_name) { auto handle_id = ffiHandleId(); if (handle_id == 0) { - throw std::runtime_error( - "LocalParticipant::unregisterRpcMethod: invalid FFI handle"); + throw std::runtime_error("LocalParticipant::unregisterRpcMethod: invalid FFI handle"); } rpc_handlers_.erase(method_name); FfiRequest req; - auto *msg = req.mutable_unregister_rpc_method(); + auto* msg = req.mutable_unregister_rpc_method(); msg->set_local_participant_handle(static_cast(handle_id)); msg->set_method(method_name); @@ -384,9 +346,7 @@ void LocalParticipant::shutdown() { // Wait up to 5 seconds for active RPC invocations to complete. // If timeout expires, proceed anyway - late responses will fail but // at least we won't block shutdown indefinitely. - rpc_state_->cv.wait_for(lock, std::chrono::seconds(5), [this] { - return rpc_state_->active_invocations == 0; - }); + rpc_state_->cv.wait_for(lock, std::chrono::seconds(5), [this] { return rpc_state_->active_invocations == 0; }); } auto handle_id = ffiHandleId(); @@ -397,9 +357,9 @@ void LocalParticipant::shutdown() { } // Unregister all RPC methods with FFI and clear local handlers - for (const auto &pair : rpc_handlers_) { + for (const auto& pair : rpc_handlers_) { FfiRequest req; - auto *msg = req.mutable_unregister_rpc_method(); + auto* msg = req.mutable_unregister_rpc_method(); msg->set_local_participant_handle(static_cast(handle_id)); msg->set_method(pair.first); (void)FfiClient::instance().sendRequest(req); @@ -407,10 +367,9 @@ void LocalParticipant::shutdown() { rpc_handlers_.clear(); } -void LocalParticipant::handleRpcMethodInvocation( - uint64_t invocation_id, const std::string &method, - const std::string &request_id, const std::string &caller_identity, - const std::string &payload, double response_timeout_sec) { +void LocalParticipant::handleRpcMethodInvocation(uint64_t invocation_id, const std::string& method, + const std::string& request_id, const std::string& caller_identity, + const std::string& payload, double response_timeout_sec) { // Capture shared state so it outlives LocalParticipant if needed auto state = rpc_state_; @@ -440,8 +399,7 @@ void LocalParticipant::handleRpcMethodInvocation( std::optional response_error; std::optional response_payload; - const RpcInvocationData params{request_id, caller_identity, payload, - response_timeout_sec}; + const RpcInvocationData params{request_id, caller_identity, payload, response_timeout_sec}; auto it = rpc_handlers_.find(method); if (it == rpc_handlers_.end()) { // No handler registered → built-in UNSUPPORTED_METHOD @@ -450,16 +408,14 @@ void LocalParticipant::handleRpcMethodInvocation( try { // Invoke user handler: may return payload or throw RpcError response_payload = it->second(params); - } catch (const RpcError &err) { + } catch (const RpcError& err) { // Handler explicitly signalled an RPC error: forward as-is response_error = err; - } catch (const std::exception &ex) { + } catch (const std::exception& ex) { // Any other exception: wrap as built-in APPLICATION_ERROR - response_error = - RpcError::builtIn(RpcError::ErrorCode::APPLICATION_ERROR, ex.what()); + response_error = RpcError::builtIn(RpcError::ErrorCode::APPLICATION_ERROR, ex.what()); } catch (...) { - response_error = RpcError::builtIn(RpcError::ErrorCode::APPLICATION_ERROR, - "unknown error"); + response_error = RpcError::builtIn(RpcError::ErrorCode::APPLICATION_ERROR, "unknown error"); } } @@ -473,11 +429,11 @@ void LocalParticipant::handleRpcMethodInvocation( } FfiRequest req; - auto *msg = req.mutable_rpc_method_invocation_response(); + auto* msg = req.mutable_rpc_method_invocation_response(); msg->set_local_participant_handle(ffiHandleId()); msg->set_invocation_id(invocation_id); if (response_error.has_value()) { - auto *err_proto = msg->mutable_error(); + auto* err_proto = msg->mutable_error(); err_proto->CopyFrom(response_error->toProto()); } if (response_payload.has_value()) { @@ -486,8 +442,7 @@ void LocalParticipant::handleRpcMethodInvocation( FfiClient::instance().sendRequest(req); } -std::shared_ptr -LocalParticipant::findTrackPublication(const std::string &sid) const { +std::shared_ptr LocalParticipant::findTrackPublication(const std::string& sid) const { auto it = published_tracks_by_sid_.find(sid); if (it == published_tracks_by_sid_.end()) { return nullptr; diff --git a/src/local_track_publication.cpp b/src/local_track_publication.cpp index b98ead82..efe6fea9 100644 --- a/src/local_track_publication.cpp +++ b/src/local_track_publication.cpp @@ -20,15 +20,11 @@ namespace livekit { -LocalTrackPublication::LocalTrackPublication( - const proto::OwnedTrackPublication &owned) - : TrackPublication( - FfiHandle(owned.handle().id()), owned.info().sid(), - owned.info().name(), fromProto(owned.info().kind()), - fromProto(owned.info().source()), owned.info().simulcasted(), - owned.info().width(), owned.info().height(), owned.info().mime_type(), - owned.info().muted(), - static_cast(owned.info().encryption_type()), - convertAudioFeatures(owned.info().audio_features())) {} +LocalTrackPublication::LocalTrackPublication(const proto::OwnedTrackPublication& owned) + : TrackPublication(FfiHandle(owned.handle().id()), owned.info().sid(), owned.info().name(), + fromProto(owned.info().kind()), fromProto(owned.info().source()), owned.info().simulcasted(), + owned.info().width(), owned.info().height(), owned.info().mime_type(), owned.info().muted(), + static_cast(owned.info().encryption_type()), + convertAudioFeatures(owned.info().audio_features())) {} } // namespace livekit diff --git a/src/local_video_track.cpp b/src/local_video_track.cpp index cc9ca6fa..3f002c7d 100644 --- a/src/local_video_track.cpp +++ b/src/local_video_track.cpp @@ -24,25 +24,21 @@ namespace livekit { -LocalVideoTrack::LocalVideoTrack(FfiHandle handle, - const proto::OwnedTrack &track) - : Track(std::move(handle), track.info().sid(), track.info().name(), - fromProto(track.info().kind()), - fromProto(track.info().stream_state()), track.info().muted(), - false) {} +LocalVideoTrack::LocalVideoTrack(FfiHandle handle, const proto::OwnedTrack& track) + : Track(std::move(handle), track.info().sid(), track.info().name(), fromProto(track.info().kind()), + fromProto(track.info().stream_state()), track.info().muted(), false) {} -std::shared_ptr LocalVideoTrack::createLocalVideoTrack( - const std::string &name, const std::shared_ptr &source) { +std::shared_ptr LocalVideoTrack::createLocalVideoTrack(const std::string& name, + const std::shared_ptr& source) { proto::FfiRequest req; - auto *msg = req.mutable_create_video_track(); + auto* msg = req.mutable_create_video_track(); msg->set_name(name); msg->set_source_handle(static_cast(source->ffi_handle_id())); const proto::FfiResponse resp = FfiClient::instance().sendRequest(req); - const proto::OwnedTrack &owned = resp.create_video_track().track(); + const proto::OwnedTrack& owned = resp.create_video_track().track(); FfiHandle handle(static_cast(owned.handle().id())); - return std::shared_ptr( - new LocalVideoTrack(std::move(handle), owned)); + return std::shared_ptr(new LocalVideoTrack(std::move(handle), owned)); } void LocalVideoTrack::mute() { @@ -52,7 +48,7 @@ void LocalVideoTrack::mute() { } proto::FfiRequest req; - auto *msg = req.mutable_local_track_mute(); + auto* msg = req.mutable_local_track_mute(); msg->set_track_handle(static_cast(ffi_handle_id())); msg->set_mute(true); @@ -67,7 +63,7 @@ void LocalVideoTrack::unmute() { } proto::FfiRequest req; - auto *msg = req.mutable_local_track_mute(); + auto* msg = req.mutable_local_track_mute(); msg->set_track_handle(static_cast(ffi_handle_id())); msg->set_mute(false); @@ -75,8 +71,6 @@ void LocalVideoTrack::unmute() { setMuted(false); } -std::string LocalVideoTrack::to_string() const { - return "rtc.LocalVideoTrack(sid=" + sid() + ", name=" + name() + ")"; -} +std::string LocalVideoTrack::to_string() const { return "rtc.LocalVideoTrack(sid=" + sid() + ", name=" + name() + ")"; } } // namespace livekit \ No newline at end of file diff --git a/src/logging.cpp b/src/logging.cpp index f8001e7b..59c51f0e 100644 --- a/src/logging.cpp +++ b/src/logging.cpp @@ -16,64 +16,64 @@ #include "livekit/logging.h" -#include - #include #include #include +#include + namespace livekit { namespace { -const char *kLoggerName = "livekit"; +const char* kLoggerName = "livekit"; spdlog::level::level_enum toSpdlogLevel(LogLevel level) { switch (level) { - case LogLevel::Trace: - return spdlog::level::trace; - case LogLevel::Debug: - return spdlog::level::debug; - case LogLevel::Info: - return spdlog::level::info; - case LogLevel::Warn: - return spdlog::level::warn; - case LogLevel::Error: - return spdlog::level::err; - case LogLevel::Critical: - return spdlog::level::critical; - case LogLevel::Off: - return spdlog::level::off; + case LogLevel::Trace: + return spdlog::level::trace; + case LogLevel::Debug: + return spdlog::level::debug; + case LogLevel::Info: + return spdlog::level::info; + case LogLevel::Warn: + return spdlog::level::warn; + case LogLevel::Error: + return spdlog::level::err; + case LogLevel::Critical: + return spdlog::level::critical; + case LogLevel::Off: + return spdlog::level::off; } return spdlog::level::info; } LogLevel fromSpdlogLevel(spdlog::level::level_enum level) { switch (level) { - case spdlog::level::trace: - return LogLevel::Trace; - case spdlog::level::debug: - return LogLevel::Debug; - case spdlog::level::info: - return LogLevel::Info; - case spdlog::level::warn: - return LogLevel::Warn; - case spdlog::level::err: - return LogLevel::Error; - case spdlog::level::critical: - return LogLevel::Critical; - case spdlog::level::off: // NOLINT(bugprone-branch-clone) - return LogLevel::Off; - default: - return LogLevel::Info; + case spdlog::level::trace: + return LogLevel::Trace; + case spdlog::level::debug: + return LogLevel::Debug; + case spdlog::level::info: + return LogLevel::Info; + case spdlog::level::warn: + return LogLevel::Warn; + case spdlog::level::err: + return LogLevel::Error; + case spdlog::level::critical: + return LogLevel::Critical; + case spdlog::level::off: // NOLINT(bugprone-branch-clone) + return LogLevel::Off; + default: + return LogLevel::Info; } } -std::mutex &loggerMutex() { +std::mutex& loggerMutex() { static std::mutex mtx; return mtx; } -std::shared_ptr &loggerStorage() { +std::shared_ptr& loggerStorage() { static std::shared_ptr logger; return logger; } @@ -92,7 +92,7 @@ namespace detail { std::shared_ptr getLogger() { const std::scoped_lock lock(loggerMutex()); - auto &logger = loggerStorage(); + auto& logger = loggerStorage(); if (!logger) { logger = createDefaultLogger(); spdlog::register_logger(logger); @@ -102,7 +102,7 @@ std::shared_ptr getLogger() { void shutdownLogger() { const std::scoped_lock lock(loggerMutex()); - auto &logger = loggerStorage(); + auto& logger = loggerStorage(); if (logger) { spdlog::drop(kLoggerName); logger.reset(); @@ -111,15 +111,13 @@ void shutdownLogger() { } // namespace detail -void setLogLevel(LogLevel level) { - detail::getLogger()->set_level(toSpdlogLevel(level)); -} +void setLogLevel(LogLevel level) { detail::getLogger()->set_level(toSpdlogLevel(level)); } LogLevel getLogLevel() { return fromSpdlogLevel(detail::getLogger()->level()); } void setLogCallback(LogCallback callback) { const std::scoped_lock lock(loggerMutex()); - auto &logger = loggerStorage(); + auto& logger = loggerStorage(); auto current_level = logger ? logger->level() : spdlog::level::info; if (logger) { @@ -128,9 +126,8 @@ void setLogCallback(LogCallback callback) { if (callback) { auto sink = std::make_shared( - [cb = std::move(callback)](const spdlog::details::log_msg &msg) { - cb(fromSpdlogLevel(msg.level), - std::string(msg.logger_name.data(), msg.logger_name.size()), + [cb = std::move(callback)](const spdlog::details::log_msg& msg) { + cb(fromSpdlogLevel(msg.level), std::string(msg.logger_name.data(), msg.logger_name.size()), std::string(msg.payload.data(), msg.payload.size())); }); logger = std::make_shared(kLoggerName, sink); diff --git a/src/remote_audio_track.cpp b/src/remote_audio_track.cpp index a5e2b2dc..cfb7b4d4 100644 --- a/src/remote_audio_track.cpp +++ b/src/remote_audio_track.cpp @@ -23,12 +23,9 @@ namespace livekit { -RemoteAudioTrack::RemoteAudioTrack(const proto::OwnedTrack &track) - : Track(FfiHandle{static_cast(track.handle().id())}, - track.info().sid(), track.info().name(), - fromProto(track.info().kind()), - fromProto(track.info().stream_state()), track.info().muted(), - true) {} +RemoteAudioTrack::RemoteAudioTrack(const proto::OwnedTrack& track) + : Track(FfiHandle{static_cast(track.handle().id())}, track.info().sid(), track.info().name(), + fromProto(track.info().kind()), fromProto(track.info().stream_state()), track.info().muted(), true) {} std::string RemoteAudioTrack::to_string() const { return "rtc.RemoteAudioTrack(sid=" + sid() + ", name=" + name() + ")"; diff --git a/src/remote_data_track.cpp b/src/remote_data_track.cpp index b00ca06e..cd2300d9 100644 --- a/src/remote_data_track.cpp +++ b/src/remote_data_track.cpp @@ -16,18 +16,17 @@ #include "livekit/remote_data_track.h" +#include + #include "data_track.pb.h" #include "ffi.pb.h" #include "ffi_client.h" -#include - namespace livekit { -RemoteDataTrack::RemoteDataTrack(const proto::OwnedRemoteDataTrack &owned) - : handle_(static_cast(owned.handle().id())), - publisher_identity_(owned.publisher_identity()) { - const auto &pi = owned.info(); +RemoteDataTrack::RemoteDataTrack(const proto::OwnedRemoteDataTrack& owned) + : handle_(static_cast(owned.handle().id())), publisher_identity_(owned.publisher_identity()) { + const auto& pi = owned.info(); info_.name = pi.name(); info_.sid = pi.sid(); info_.uses_e2ee = pi.uses_e2ee(); @@ -39,28 +38,26 @@ bool RemoteDataTrack::isPublished() const { } proto::FfiRequest req; - auto *msg = req.mutable_remote_data_track_is_published(); + auto* msg = req.mutable_remote_data_track_is_published(); msg->set_track_handle(static_cast(handle_.get())); const proto::FfiResponse resp = FfiClient::instance().sendRequest(req); return resp.remote_data_track_is_published().is_published(); } -Result, SubscribeDataTrackError> -RemoteDataTrack::subscribe(const DataTrackStream::Options &options) { +Result, SubscribeDataTrackError> RemoteDataTrack::subscribe( + const DataTrackStream::Options& options) { if (!handle_.valid()) { - return Result, - SubscribeDataTrackError>::failure(SubscribeDataTrackError{ - SubscribeDataTrackErrorCode::INVALID_HANDLE, - "RemoteDataTrack::subscribe: invalid FFI " - "handle"}); + return Result, SubscribeDataTrackError>::failure( + SubscribeDataTrackError{SubscribeDataTrackErrorCode::INVALID_HANDLE, + "RemoteDataTrack::subscribe: invalid FFI " + "handle"}); } - auto result = FfiClient::instance().subscribeDataTrack( - static_cast(handle_.get()), options.buffer_size); + auto result = + FfiClient::instance().subscribeDataTrack(static_cast(handle_.get()), options.buffer_size); if (!result) { - return Result, - SubscribeDataTrackError>::failure(std::move(result).error()); + return Result, SubscribeDataTrackError>::failure(std::move(result).error()); } const proto::OwnedDataTrackStream owned_sub = result.value(); @@ -69,8 +66,7 @@ RemoteDataTrack::subscribe(const DataTrackStream::Options &options) { auto stream = std::shared_ptr(new DataTrackStream()); stream->init(std::move(sub_handle)); - return Result, - SubscribeDataTrackError>::success(std::move(stream)); + return Result, SubscribeDataTrackError>::success(std::move(stream)); } } // namespace livekit diff --git a/src/remote_participant.cpp b/src/remote_participant.cpp index a7848874..ee44367c 100644 --- a/src/remote_participant.cpp +++ b/src/remote_participant.cpp @@ -24,31 +24,25 @@ namespace livekit { -RemoteParticipant::RemoteParticipant( - FfiHandle handle, std::string sid, std::string name, std::string identity, - std::string metadata, - std::unordered_map attributes, - ParticipantKind kind, DisconnectReason reason) - : Participant(std::move(handle), std::move(sid), std::move(name), - std::move(identity), std::move(metadata), +RemoteParticipant::RemoteParticipant(FfiHandle handle, std::string sid, std::string name, std::string identity, + std::string metadata, std::unordered_map attributes, + ParticipantKind kind, DisconnectReason reason) + : Participant(std::move(handle), std::move(sid), std::move(name), std::move(identity), std::move(metadata), std::move(attributes), kind, reason), track_publications_() {} std::string RemoteParticipant::to_string() const { std::ostringstream oss; - oss << "rtc.RemoteParticipant(sid=" << sid() << ", identity=" << identity() - << ", name=" << name() << ")"; + oss << "rtc.RemoteParticipant(sid=" << sid() << ", identity=" << identity() << ", name=" << name() << ")"; return oss.str(); } -std::ostream &operator<<(std::ostream &os, - const RemoteParticipant &participant) { +std::ostream& operator<<(std::ostream& os, const RemoteParticipant& participant) { os << participant.to_string(); return os; } -std::shared_ptr -RemoteParticipant::findTrackPublication(const std::string &sid) const { +std::shared_ptr RemoteParticipant::findTrackPublication(const std::string& sid) const { auto it = track_publications_.find(sid); if (it == track_publications_.end()) { return nullptr; diff --git a/src/remote_track_publication.cpp b/src/remote_track_publication.cpp index c57b5586..03e9f94f 100644 --- a/src/remote_track_publication.cpp +++ b/src/remote_track_publication.cpp @@ -22,25 +22,20 @@ namespace livekit { -RemoteTrackPublication::RemoteTrackPublication( - const proto::OwnedTrackPublication &owned) - : TrackPublication( - FfiHandle(owned.handle().id()), owned.info().sid(), - owned.info().name(), fromProto(owned.info().kind()), - fromProto(owned.info().source()), owned.info().simulcasted(), - owned.info().width(), owned.info().height(), owned.info().mime_type(), - owned.info().muted(), - static_cast(owned.info().encryption_type()), - convertAudioFeatures(owned.info().audio_features())) {} +RemoteTrackPublication::RemoteTrackPublication(const proto::OwnedTrackPublication& owned) + : TrackPublication(FfiHandle(owned.handle().id()), owned.info().sid(), owned.info().name(), + fromProto(owned.info().kind()), fromProto(owned.info().source()), owned.info().simulcasted(), + owned.info().width(), owned.info().height(), owned.info().mime_type(), owned.info().muted(), + static_cast(owned.info().encryption_type()), + convertAudioFeatures(owned.info().audio_features())) {} void RemoteTrackPublication::setSubscribed(bool subscribed) { if (ffiHandleId() == 0) { - throw std::runtime_error( - "RemoteTrackPublication::setSubscribed: invalid FFI handle"); + throw std::runtime_error("RemoteTrackPublication::setSubscribed: invalid FFI handle"); } proto::FfiRequest req; - auto *msg = req.mutable_set_subscribed(); + auto* msg = req.mutable_set_subscribed(); msg->set_subscribe(subscribed); msg->set_publication_handle(static_cast(ffiHandleId())); diff --git a/src/remote_video_track.cpp b/src/remote_video_track.cpp index c957c4df..9e43e47e 100644 --- a/src/remote_video_track.cpp +++ b/src/remote_video_track.cpp @@ -23,12 +23,9 @@ namespace livekit { -RemoteVideoTrack::RemoteVideoTrack(const proto::OwnedTrack &track) - : Track(FfiHandle{static_cast(track.handle().id())}, - track.info().sid(), track.info().name(), - fromProto(track.info().kind()), - fromProto(track.info().stream_state()), track.info().muted(), - true) {} +RemoteVideoTrack::RemoteVideoTrack(const proto::OwnedTrack& track) + : Track(FfiHandle{static_cast(track.handle().id())}, track.info().sid(), track.info().name(), + fromProto(track.info().kind()), fromProto(track.info().stream_state()), track.info().muted(), true) {} std::string RemoteVideoTrack::to_string() const { return "rtc.RemoteVideoTrack(sid=" + sid() + ", name=" + name() + ")"; diff --git a/src/room.cpp b/src/room.cpp index 9eae941d..d647b0fc 100644 --- a/src/room.cpp +++ b/src/room.cpp @@ -16,6 +16,11 @@ #include "livekit/room.h" +#include + +#include "data_track.pb.h" +#include "ffi.pb.h" +#include "ffi_client.h" #include "livekit/audio_stream.h" #include "livekit/e2ee.h" #include "livekit/local_data_track.h" @@ -28,10 +33,6 @@ #include "livekit/remote_video_track.h" #include "livekit/room_delegate.h" #include "livekit/room_event_types.h" - -#include "data_track.pb.h" -#include "ffi.pb.h" -#include "ffi_client.h" #include "livekit_ffi.h" #include "lk_log.h" #include "room.pb.h" @@ -39,7 +40,6 @@ #include "trace/trace_event.h" #include "track.pb.h" #include "track_proto_converter.h" -#include namespace livekit { @@ -51,26 +51,22 @@ using proto::FfiResponse; namespace { -std::shared_ptr -createRemoteParticipant(const proto::OwnedParticipant &owned) { - const auto &pinfo = owned.info(); +std::shared_ptr createRemoteParticipant(const proto::OwnedParticipant& owned) { + const auto& pinfo = owned.info(); std::unordered_map attrs; attrs.reserve(pinfo.attributes_size()); - for (const auto &kv : pinfo.attributes()) { + for (const auto& kv : pinfo.attributes()) { attrs.emplace(kv.first, kv.second); } auto kind = livekit::fromProto(pinfo.kind()); auto reason = livekit::toDisconnectReason(pinfo.disconnect_reason()); livekit::FfiHandle handle(static_cast(owned.handle().id())); - return std::make_shared( - std::move(handle), pinfo.sid(), pinfo.name(), pinfo.identity(), - pinfo.metadata(), std::move(attrs), kind, reason); + return std::make_shared(std::move(handle), pinfo.sid(), pinfo.name(), pinfo.identity(), + pinfo.metadata(), std::move(attrs), kind, reason); } } // namespace -Room::Room() - : subscription_thread_dispatcher_( - std::make_unique()) {} +Room::Room() : subscription_thread_dispatcher_(std::make_unique()) {} Room::~Room() { if (subscription_thread_dispatcher_) { @@ -101,13 +97,12 @@ Room::~Room() { // local_participant_to_cleanup is destroyed here after listener is removed } -void Room::setDelegate(RoomDelegate *delegate) { +void Room::setDelegate(RoomDelegate* delegate) { const std::scoped_lock g(lock_); delegate_ = delegate; } -bool Room::Connect(const std::string &url, const std::string &token, - const RoomOptions &options) { +bool Room::Connect(const std::string& url, const std::string& token, const RoomOptions& options) { TRACE_EVENT0("livekit", "Room::Connect"); { @@ -119,23 +114,21 @@ bool Room::Connect(const std::string &url, const std::string &token, } auto fut = FfiClient::instance().connectAsync(url, token, options); try { - auto connectCb = - fut.get(); // fut will throw if it fails to connect to the room + auto connectCb = fut.get(); // fut will throw if it fails to connect to the room - const auto &owned_room = connectCb.result().room(); - auto new_room_handle = - std::make_shared(owned_room.handle().id()); + const auto& owned_room = connectCb.result().room(); + auto new_room_handle = std::make_shared(owned_room.handle().id()); auto new_room_info = fromProto(owned_room.info()); // Setup local particpant std::unique_ptr new_local_participant; { - const auto &owned_local = connectCb.result().local_participant(); - const auto &pinfo = owned_local.info(); + const auto& owned_local = connectCb.result().local_participant(); + const auto& pinfo = owned_local.info(); // Build attributes map std::unordered_map attrs; - for (const auto &kv : pinfo.attributes()) { + for (const auto& kv : pinfo.attributes()) { attrs.emplace(kv.first, kv.second); } @@ -143,27 +136,23 @@ bool Room::Connect(const std::string &url, const std::string &token, auto reason = toDisconnectReason(pinfo.disconnect_reason()); // Participant base stores a weak_ptr, so share the room handle - FfiHandle participant_handle( - static_cast(owned_local.handle().id())); - new_local_participant = std::make_unique( - std::move(participant_handle), pinfo.sid(), pinfo.name(), - pinfo.identity(), pinfo.metadata(), std::move(attrs), kind, reason); + FfiHandle participant_handle(static_cast(owned_local.handle().id())); + new_local_participant = + std::make_unique(std::move(participant_handle), pinfo.sid(), pinfo.name(), pinfo.identity(), + pinfo.metadata(), std::move(attrs), kind, reason); } // Setup remote participants - std::unordered_map> - new_remote_participants; + std::unordered_map> new_remote_participants; { - const auto &participants = connectCb.result().participants(); + const auto& participants = connectCb.result().participants(); const std::scoped_lock g(lock_); - for (const auto &pt : participants) { - const auto &owned = pt.participant(); + for (const auto& pt : participants) { + const auto& owned = pt.participant(); auto rp = createRemoteParticipant(owned); // Add the initial remote participant tracks - for (const auto &owned_publication_info : pt.publications()) { - auto publication = - std::make_shared(owned_publication_info); - rp->mutableTrackPublications().emplace(publication->sid(), - std::move(publication)); + for (const auto& owned_publication_info : pt.publications()) { + auto publication = std::make_shared(owned_publication_info); + rp->mutableTrackPublications().emplace(publication->sid(), std::move(publication)); } new_remote_participants.emplace(rp->identity(), std::move(rp)); @@ -174,8 +163,8 @@ bool Room::Connect(const std::string &url, const std::string &token, std::unique_ptr new_e2ee_manager; if (options.encryption) { LK_LOG_INFO("creating E2eeManager"); - new_e2ee_manager = std::unique_ptr( - new E2EEManager(new_room_handle->get(), options.encryption.value())); + new_e2ee_manager = + std::unique_ptr(new E2EEManager(new_room_handle->get(), options.encryption.value())); } // Publish all state atomically under lock @@ -190,15 +179,14 @@ bool Room::Connect(const std::string &url, const std::string &token, } // Install listener (Room is fully initialized) - auto listenerId = FfiClient::instance().AddListener( - [this](const proto::FfiEvent &e) { OnEvent(e); }); + auto listenerId = FfiClient::instance().AddListener([this](const proto::FfiEvent& e) { OnEvent(e); }); { const std::scoped_lock g(lock_); listener_id_ = listenerId; } return true; - } catch (const std::exception &e) { + } catch (const std::exception& e) { // On error, set the connection_state_ to Disconnected connection_state_ = ConnectionState::Disconnected; LK_LOG_ERROR("Room::Connect failed: {}", e.what()); @@ -211,61 +199,54 @@ RoomInfoData Room::room_info() const { return room_info_; } -LocalParticipant *Room::localParticipant() const { +LocalParticipant* Room::localParticipant() const { const std::scoped_lock g(lock_); return local_participant_.get(); } -RemoteParticipant *Room::remoteParticipant(const std::string &identity) const { +RemoteParticipant* Room::remoteParticipant(const std::string& identity) const { const std::scoped_lock g(lock_); auto it = remote_participants_.find(identity); return it == remote_participants_.end() ? nullptr : it->second.get(); } -std::vector> -Room::remoteParticipants() const { +std::vector> Room::remoteParticipants() const { const std::scoped_lock guard(lock_); std::vector> out; out.reserve(remote_participants_.size()); - for (const auto &kv : remote_participants_) { + for (const auto& kv : remote_participants_) { out.push_back(kv.second); } return out; } -E2EEManager *Room::e2eeManager() const { +E2EEManager* Room::e2eeManager() const { const std::scoped_lock g(lock_); return e2ee_manager_.get(); } -void Room::registerTextStreamHandler(const std::string &topic, - TextStreamHandler handler) { +void Room::registerTextStreamHandler(const std::string& topic, TextStreamHandler handler) { const std::scoped_lock g(lock_); - auto [it, inserted] = - text_stream_handlers_.emplace(topic, std::move(handler)); + auto [it, inserted] = text_stream_handlers_.emplace(topic, std::move(handler)); if (!inserted) { - throw std::runtime_error("text stream handler for topic '" + topic + - "' already set"); + throw std::runtime_error("text stream handler for topic '" + topic + "' already set"); } } -void Room::unregisterTextStreamHandler(const std::string &topic) { +void Room::unregisterTextStreamHandler(const std::string& topic) { const std::scoped_lock g(lock_); text_stream_handlers_.erase(topic); } -void Room::registerByteStreamHandler(const std::string &topic, - ByteStreamHandler handler) { +void Room::registerByteStreamHandler(const std::string& topic, ByteStreamHandler handler) { const std::scoped_lock g(lock_); - auto [it, inserted] = - byte_stream_handlers_.emplace(topic, std::move(handler)); + auto [it, inserted] = byte_stream_handlers_.emplace(topic, std::move(handler)); if (!inserted) { - throw std::runtime_error("byte stream handler for topic '" + topic + - "' already set"); + throw std::runtime_error("byte stream handler for topic '" + topic + "' already set"); } } -void Room::unregisterByteStreamHandler(const std::string &topic) { +void Room::unregisterByteStreamHandler(const std::string& topic) { const std::scoped_lock g(lock_); byte_stream_handlers_.erase(topic); } @@ -274,95 +255,73 @@ void Room::unregisterByteStreamHandler(const std::string &topic) { // Frame callback registration // ------------------------------------------------------------------- -void Room::setOnAudioFrameCallback(const std::string &participant_identity, - TrackSource source, - AudioFrameCallback callback, - const AudioStream::Options &opts) { +void Room::setOnAudioFrameCallback(const std::string& participant_identity, TrackSource source, + AudioFrameCallback callback, const AudioStream::Options& opts) { if (subscription_thread_dispatcher_) { - subscription_thread_dispatcher_->setOnAudioFrameCallback( - participant_identity, source, std::move(callback), opts); + subscription_thread_dispatcher_->setOnAudioFrameCallback(participant_identity, source, std::move(callback), opts); } } -void Room::setOnAudioFrameCallback(const std::string &participant_identity, - const std::string &track_name, - AudioFrameCallback callback, - const AudioStream::Options &opts) { +void Room::setOnAudioFrameCallback(const std::string& participant_identity, const std::string& track_name, + AudioFrameCallback callback, const AudioStream::Options& opts) { if (subscription_thread_dispatcher_) { - subscription_thread_dispatcher_->setOnAudioFrameCallback( - participant_identity, track_name, std::move(callback), opts); + subscription_thread_dispatcher_->setOnAudioFrameCallback(participant_identity, track_name, std::move(callback), + opts); } } -void Room::setOnVideoFrameCallback(const std::string &participant_identity, - TrackSource source, - VideoFrameCallback callback, - const VideoStream::Options &opts) { +void Room::setOnVideoFrameCallback(const std::string& participant_identity, TrackSource source, + VideoFrameCallback callback, const VideoStream::Options& opts) { if (subscription_thread_dispatcher_) { - subscription_thread_dispatcher_->setOnVideoFrameCallback( - participant_identity, source, std::move(callback), opts); + subscription_thread_dispatcher_->setOnVideoFrameCallback(participant_identity, source, std::move(callback), opts); } } -void Room::setOnVideoFrameCallback(const std::string &participant_identity, - const std::string &track_name, - VideoFrameCallback callback, - const VideoStream::Options &opts) { +void Room::setOnVideoFrameCallback(const std::string& participant_identity, const std::string& track_name, + VideoFrameCallback callback, const VideoStream::Options& opts) { if (subscription_thread_dispatcher_) { - subscription_thread_dispatcher_->setOnVideoFrameCallback( - participant_identity, track_name, std::move(callback), opts); + subscription_thread_dispatcher_->setOnVideoFrameCallback(participant_identity, track_name, std::move(callback), + opts); } } -void Room::setOnVideoFrameEventCallback(const std::string &participant_identity, - const std::string &track_name, - VideoFrameEventCallback callback, - const VideoStream::Options &opts) { +void Room::setOnVideoFrameEventCallback(const std::string& participant_identity, const std::string& track_name, + VideoFrameEventCallback callback, const VideoStream::Options& opts) { if (subscription_thread_dispatcher_) { - subscription_thread_dispatcher_->setOnVideoFrameEventCallback( - participant_identity, track_name, std::move(callback), opts); + subscription_thread_dispatcher_->setOnVideoFrameEventCallback(participant_identity, track_name, std::move(callback), + opts); } } -void Room::clearOnAudioFrameCallback(const std::string &participant_identity, - TrackSource source) { +void Room::clearOnAudioFrameCallback(const std::string& participant_identity, TrackSource source) { if (subscription_thread_dispatcher_) { - subscription_thread_dispatcher_->clearOnAudioFrameCallback( - participant_identity, source); + subscription_thread_dispatcher_->clearOnAudioFrameCallback(participant_identity, source); } } -void Room::clearOnAudioFrameCallback(const std::string &participant_identity, - const std::string &track_name) { +void Room::clearOnAudioFrameCallback(const std::string& participant_identity, const std::string& track_name) { if (subscription_thread_dispatcher_) { - subscription_thread_dispatcher_->clearOnAudioFrameCallback( - participant_identity, track_name); + subscription_thread_dispatcher_->clearOnAudioFrameCallback(participant_identity, track_name); } } -void Room::clearOnVideoFrameCallback(const std::string &participant_identity, - TrackSource source) { +void Room::clearOnVideoFrameCallback(const std::string& participant_identity, TrackSource source) { if (subscription_thread_dispatcher_) { - subscription_thread_dispatcher_->clearOnVideoFrameCallback( - participant_identity, source); + subscription_thread_dispatcher_->clearOnVideoFrameCallback(participant_identity, source); } } -void Room::clearOnVideoFrameCallback(const std::string &participant_identity, - const std::string &track_name) { +void Room::clearOnVideoFrameCallback(const std::string& participant_identity, const std::string& track_name) { if (subscription_thread_dispatcher_) { - subscription_thread_dispatcher_->clearOnVideoFrameCallback( - participant_identity, track_name); + subscription_thread_dispatcher_->clearOnVideoFrameCallback(participant_identity, track_name); } } -DataFrameCallbackId -Room::addOnDataFrameCallback(const std::string &participant_identity, - const std::string &track_name, - DataFrameCallback callback) { +DataFrameCallbackId Room::addOnDataFrameCallback(const std::string& participant_identity, const std::string& track_name, + DataFrameCallback callback) { if (subscription_thread_dispatcher_) { - return subscription_thread_dispatcher_->addOnDataFrameCallback( - participant_identity, track_name, std::move(callback)); + return subscription_thread_dispatcher_->addOnDataFrameCallback(participant_identity, track_name, + std::move(callback)); } return std::numeric_limits::max(); } @@ -373,10 +332,10 @@ void Room::removeOnDataFrameCallback(DataFrameCallbackId id) { } } -void Room::OnEvent(const FfiEvent &event) { +void Room::OnEvent(const FfiEvent& event) { // Take a snapshot of the delegate under lock, but do NOT call it under the // lock. - RoomDelegate *delegate_snapshot = nullptr; + RoomDelegate* delegate_snapshot = nullptr; { const std::scoped_lock guard(lock_); delegate_snapshot = delegate_; @@ -384,9 +343,9 @@ void Room::OnEvent(const FfiEvent &event) { // First, handle RPC method invocations (not part of RoomEvent). if (event.message_case() == FfiEvent::kRpcMethodInvocation) { - const auto &rpc = event.rpc_method_invocation(); + const auto& rpc = event.rpc_method_invocation(); - LocalParticipant *lp = nullptr; + LocalParticipant* lp = nullptr; { const std::scoped_lock guard(lock_); if (!local_participant_) { @@ -394,8 +353,7 @@ void Room::OnEvent(const FfiEvent &event) { } auto local_handle = local_participant_->ffiHandleId(); if (local_handle == INVALID_HANDLE || - rpc.local_participant_handle() != - static_cast(local_handle)) { + rpc.local_participant_handle() != static_cast(local_handle)) { // RPC is not targeted at this room's local participant; ignore. return; } @@ -403,1024 +361,995 @@ void Room::OnEvent(const FfiEvent &event) { } // Call outside the lock to avoid deadlocks / re-entrancy issues. - lp->handleRpcMethodInvocation( - rpc.invocation_id(), rpc.method(), rpc.request_id(), - rpc.caller_identity(), rpc.payload(), - static_cast(rpc.response_timeout_ms()) / 1000.0); + lp->handleRpcMethodInvocation(rpc.invocation_id(), rpc.method(), rpc.request_id(), rpc.caller_identity(), + rpc.payload(), static_cast(rpc.response_timeout_ms()) / 1000.0); return; } switch (event.message_case()) { - case FfiEvent::kRoomEvent: { - const proto::RoomEvent &re = event.room_event(); + case FfiEvent::kRoomEvent: { + const proto::RoomEvent& re = event.room_event(); - // Check if this event is for our room handle - { - const std::scoped_lock guard(lock_); - if (!room_handle_ || - re.room_handle() != static_cast(room_handle_->get())) { - return; - } - } - - switch (re.message_case()) { - case proto::RoomEvent::kParticipantConnected: { - std::shared_ptr new_participant; - { - const std::scoped_lock guard(lock_); - const auto &owned = re.participant_connected().info(); - // createRemoteParticipant takes proto::OwnedParticipant - new_participant = createRemoteParticipant(owned); - remote_participants_.emplace(new_participant->identity(), - new_participant); - } - ParticipantConnectedEvent ev; - ev.participant = new_participant.get(); - if (delegate_snapshot) { - delegate_snapshot->onParticipantConnected(*this, ev); - } - break; - } - case proto::RoomEvent::kParticipantDisconnected: { - std::shared_ptr removed; - DisconnectReason reason = DisconnectReason::Unknown; + // Check if this event is for our room handle { const std::scoped_lock guard(lock_); - const auto &pd = re.participant_disconnected(); - const std::string &identity = pd.participant_identity(); - reason = toDisconnectReason(pd.disconnect_reason()); - - auto it = remote_participants_.find(identity); - if (it != remote_participants_.end()) { - removed = it->second; - remote_participants_.erase(it); - } else { - // We saw a disconnect event for a participant we don't track - // internally. This can happen on races or if we never created a - // RemoteParticipant - LK_LOG_WARN("participant_disconnected for unknown identity: {}", - identity); + if (!room_handle_ || re.room_handle() != static_cast(room_handle_->get())) { + return; } } - if (removed) { - ParticipantDisconnectedEvent ev; - ev.participant = removed.get(); - ev.reason = reason; - if (delegate_snapshot) { - delegate_snapshot->onParticipantDisconnected(*this, ev); + + switch (re.message_case()) { + case proto::RoomEvent::kParticipantConnected: { + std::shared_ptr new_participant; + { + const std::scoped_lock guard(lock_); + const auto& owned = re.participant_connected().info(); + // createRemoteParticipant takes proto::OwnedParticipant + new_participant = createRemoteParticipant(owned); + remote_participants_.emplace(new_participant->identity(), new_participant); + } + ParticipantConnectedEvent ev; + ev.participant = new_participant.get(); + if (delegate_snapshot) { + delegate_snapshot->onParticipantConnected(*this, ev); + } + break; } - } - break; - } - case proto::RoomEvent::kLocalTrackPublished: { - LocalTrackPublishedEvent ev; - { - const std::scoped_lock guard(lock_); - if (!local_participant_) { - LK_LOG_ERROR("kLocalTrackPublished: local_participant_ is nullptr"); + case proto::RoomEvent::kParticipantDisconnected: { + std::shared_ptr removed; + DisconnectReason reason = DisconnectReason::Unknown; + { + const std::scoped_lock guard(lock_); + const auto& pd = re.participant_disconnected(); + const std::string& identity = pd.participant_identity(); + reason = toDisconnectReason(pd.disconnect_reason()); + + auto it = remote_participants_.find(identity); + if (it != remote_participants_.end()) { + removed = it->second; + remote_participants_.erase(it); + } else { + // We saw a disconnect event for a participant we don't track + // internally. This can happen on races or if we never created a + // RemoteParticipant + LK_LOG_WARN("participant_disconnected for unknown identity: {}", identity); + } + } + if (removed) { + ParticipantDisconnectedEvent ev; + ev.participant = removed.get(); + ev.reason = reason; + if (delegate_snapshot) { + delegate_snapshot->onParticipantDisconnected(*this, ev); + } + } break; } - const auto <p = re.local_track_published(); - const std::string &sid = ltp.track_sid(); - const auto pubs = local_participant_->trackPublications(); - auto it = pubs.find(sid); - if (it == pubs.end()) { - LK_LOG_WARN("local_track_published for unknown sid: {}", sid); + case proto::RoomEvent::kLocalTrackPublished: { + LocalTrackPublishedEvent ev; + { + const std::scoped_lock guard(lock_); + if (!local_participant_) { + LK_LOG_ERROR("kLocalTrackPublished: local_participant_ is nullptr"); + break; + } + const auto& ltp = re.local_track_published(); + const std::string& sid = ltp.track_sid(); + const auto pubs = local_participant_->trackPublications(); + auto it = pubs.find(sid); + if (it == pubs.end()) { + LK_LOG_WARN("local_track_published for unknown sid: {}", sid); + break; + } + ev.publication = it->second; + ev.track = ev.publication ? ev.publication->track() : nullptr; + } + if (delegate_snapshot) { + delegate_snapshot->onLocalTrackPublished(*this, ev); + } break; } - ev.publication = it->second; - ev.track = ev.publication ? ev.publication->track() : nullptr; - } - if (delegate_snapshot) { - delegate_snapshot->onLocalTrackPublished(*this, ev); - } - break; - } - case proto::RoomEvent::kLocalTrackUnpublished: { - LocalTrackUnpublishedEvent ev; - { - const std::scoped_lock guard(lock_); - if (!local_participant_) { - LK_LOG_ERROR("kLocalTrackUnpublished: local_participant_ is nullptr"); + case proto::RoomEvent::kLocalTrackUnpublished: { + LocalTrackUnpublishedEvent ev; + { + const std::scoped_lock guard(lock_); + if (!local_participant_) { + LK_LOG_ERROR("kLocalTrackUnpublished: local_participant_ is nullptr"); + break; + } + const auto& ltu = re.local_track_unpublished(); + const std::string& pub_sid = ltu.publication_sid(); + const auto pubs = local_participant_->trackPublications(); + auto it = pubs.find(pub_sid); + if (it == pubs.end()) { + LK_LOG_WARN("local_track_unpublished for unknown publication sid: {}", pub_sid); + break; + } + ev.publication = it->second; + } + if (delegate_snapshot) { + delegate_snapshot->onLocalTrackUnpublished(*this, ev); + } break; } - const auto <u = re.local_track_unpublished(); - const std::string &pub_sid = ltu.publication_sid(); - const auto pubs = local_participant_->trackPublications(); - auto it = pubs.find(pub_sid); - if (it == pubs.end()) { - LK_LOG_WARN("local_track_unpublished for unknown publication sid: {}", - pub_sid); + case proto::RoomEvent::kLocalTrackSubscribed: { + LocalTrackSubscribedEvent ev; + { + const std::scoped_lock guard(lock_); + if (!local_participant_) { + break; + } + const auto& lts = re.local_track_subscribed(); + const std::string& sid = lts.track_sid(); + const auto pubs = local_participant_->trackPublications(); + auto it = pubs.find(sid); + if (it == pubs.end()) { + LK_LOG_WARN("local_track_subscribed for unknown sid: {}", sid); + break; + } + auto publication = it->second; + ev.track = publication ? publication->track() : nullptr; + } + + if (delegate_snapshot) { + delegate_snapshot->onLocalTrackSubscribed(*this, ev); + } break; } - ev.publication = it->second; - } - if (delegate_snapshot) { - delegate_snapshot->onLocalTrackUnpublished(*this, ev); - } - break; - } - case proto::RoomEvent::kLocalTrackSubscribed: { - LocalTrackSubscribedEvent ev; - { - const std::scoped_lock guard(lock_); - if (!local_participant_) { + case proto::RoomEvent::kTrackPublished: { + TrackPublishedEvent ev; + { + const std::scoped_lock guard(lock_); + const auto& tp = re.track_published(); + const std::string& identity = tp.participant_identity(); + auto it = remote_participants_.find(identity); + if (it != remote_participants_.end()) { + RemoteParticipant* rparticipant = it->second.get(); + const auto& owned_publication = tp.publication(); + auto rpublication = std::make_shared(owned_publication); + // Store it on the participant, keyed by SID + rparticipant->mutableTrackPublications().emplace(rpublication->sid(), std::move(rpublication)); + ev.participant = rparticipant; + ev.publication = rpublication; + } else { + // Optional: log if we get a track for an unknown participant + LK_LOG_WARN("track_published for unknown participant: {}", identity); + // Don't emit the + break; + } + } + if (delegate_snapshot) { + delegate_snapshot->onTrackPublished(*this, ev); + } break; } - const auto <s = re.local_track_subscribed(); - const std::string &sid = lts.track_sid(); - const auto pubs = local_participant_->trackPublications(); - auto it = pubs.find(sid); - if (it == pubs.end()) { - LK_LOG_WARN("local_track_subscribed for unknown sid: {}", sid); + case proto::RoomEvent::kTrackUnpublished: { + TrackUnpublishedEvent ev; + { + const std::scoped_lock guard(lock_); + const auto& tu = re.track_unpublished(); + const std::string& identity = tu.participant_identity(); + const std::string& pub_sid = tu.publication_sid(); + auto pit = remote_participants_.find(identity); + if (pit == remote_participants_.end()) { + LK_LOG_WARN("track_unpublished for unknown participant: {}", identity); + break; + } + RemoteParticipant* rparticipant = pit->second.get(); + auto& pubs = rparticipant->mutableTrackPublications(); + auto it = pubs.find(pub_sid); + if (it == pubs.end()) { + LK_LOG_WARN( + "track_unpublished for unknown publication sid {} " + "(participant {})", + pub_sid, identity); + break; + } + ev.participant = rparticipant; + ev.publication = it->second; + pubs.erase(it); + } + + if (delegate_snapshot) { + delegate_snapshot->onTrackUnpublished(*this, ev); + } break; } - auto publication = it->second; - ev.track = publication ? publication->track() : nullptr; - } + case proto::RoomEvent::kTrackSubscribed: { + const auto& ts = re.track_subscribed(); + const std::string& identity = ts.participant_identity(); + const auto& owned_track = ts.track(); + const auto& track_info = owned_track.info(); + std::shared_ptr rpublication; + RemoteParticipant* rparticipant = nullptr; + std::shared_ptr remote_track; + { + const std::scoped_lock guard(lock_); + // Find participant + auto pit = remote_participants_.find(identity); + if (pit == remote_participants_.end()) { + LK_LOG_WARN("track_subscribed for unknown participant: {}", identity); + break; + } + rparticipant = pit->second.get(); + // Find existing publication by track SID (from track_published) + auto& pubs = rparticipant->mutableTrackPublications(); + auto pubIt = pubs.find(track_info.sid()); + if (pubIt == pubs.end()) { + LK_LOG_WARN( + "track_subscribed for unknown publication sid {} " + "(participant {})", + track_info.sid(), identity); + break; + } + rpublication = pubIt->second; + + // Create RemoteVideoTrack / RemoteAudioTrack + if (track_info.kind() == proto::TrackKind::KIND_VIDEO) { + remote_track = std::make_shared(owned_track); + } else if (track_info.kind() == proto::TrackKind::KIND_AUDIO) { + remote_track = std::make_shared(owned_track); + } else { + LK_LOG_WARN("track_subscribed with unsupported kind: {}", static_cast(track_info.kind())); + break; + } + // Attach to publication, mark subscribed + rpublication->setTrack(remote_track); + rpublication->setSubscribed(true); + } - if (delegate_snapshot) { - delegate_snapshot->onLocalTrackSubscribed(*this, ev); - } - break; - } - case proto::RoomEvent::kTrackPublished: { - TrackPublishedEvent ev; - { - const std::scoped_lock guard(lock_); - const auto &tp = re.track_published(); - const std::string &identity = tp.participant_identity(); - auto it = remote_participants_.find(identity); - if (it != remote_participants_.end()) { - RemoteParticipant *rparticipant = it->second.get(); - const auto &owned_publication = tp.publication(); - auto rpublication = - std::make_shared(owned_publication); - // Store it on the participant, keyed by SID - rparticipant->mutableTrackPublications().emplace( - rpublication->sid(), std::move(rpublication)); - ev.participant = rparticipant; + // Emit remote track_subscribed-style callback + TrackSubscribedEvent ev; + ev.track = remote_track; ev.publication = rpublication; - } else { - // Optional: log if we get a track for an unknown participant - LK_LOG_WARN("track_published for unknown participant: {}", identity); - // Don't emit the + ev.participant = rparticipant; + if (delegate_snapshot) { + delegate_snapshot->onTrackSubscribed(*this, ev); + } + + if (subscription_thread_dispatcher_ && remote_track && rpublication) { + subscription_thread_dispatcher_->handleTrackSubscribed(identity, rpublication->source(), + rpublication->name(), remote_track); + } break; } - } - if (delegate_snapshot) { - delegate_snapshot->onTrackPublished(*this, ev); - } - break; - } - case proto::RoomEvent::kTrackUnpublished: { - TrackUnpublishedEvent ev; - { - const std::scoped_lock guard(lock_); - const auto &tu = re.track_unpublished(); - const std::string &identity = tu.participant_identity(); - const std::string &pub_sid = tu.publication_sid(); - auto pit = remote_participants_.find(identity); - if (pit == remote_participants_.end()) { - LK_LOG_WARN("track_unpublished for unknown participant: {}", - identity); + case proto::RoomEvent::kTrackUnsubscribed: { + TrackUnsubscribedEvent ev; + TrackSource unsub_source = TrackSource::SOURCE_UNKNOWN; + std::string unsub_identity; + { + const std::scoped_lock guard(lock_); + const auto& tu = re.track_unsubscribed(); + unsub_identity = tu.participant_identity(); + const std::string& track_sid = tu.track_sid(); + auto pit = remote_participants_.find(unsub_identity); + if (pit == remote_participants_.end()) { + LK_LOG_WARN("track_unsubscribed for unknown participant: {}", unsub_identity); + break; + } + RemoteParticipant* rparticipant = pit->second.get(); + auto& pubs = rparticipant->mutableTrackPublications(); + auto pubIt = pubs.find(track_sid); + if (pubIt == pubs.end()) { + LK_LOG_WARN( + "track_unsubscribed for unknown publication sid {} " + "(participant {})", + track_sid, unsub_identity); + break; + } + auto publication = pubIt->second; + unsub_source = publication->source(); + auto track = publication->track(); + publication->setTrack(nullptr); + publication->setSubscribed(false); + ev.participant = rparticipant; + ev.publication = publication; + ev.track = track; + } + + if (delegate_snapshot) { + delegate_snapshot->onTrackUnsubscribed(*this, ev); + } + + if (subscription_thread_dispatcher_ && unsub_source != TrackSource::SOURCE_UNKNOWN) { + subscription_thread_dispatcher_->handleTrackUnsubscribed(unsub_identity, unsub_source, + ev.publication ? ev.publication->name() : ""); + } break; } - RemoteParticipant *rparticipant = pit->second.get(); - auto &pubs = rparticipant->mutableTrackPublications(); - auto it = pubs.find(pub_sid); - if (it == pubs.end()) { - LK_LOG_WARN("track_unpublished for unknown publication sid {} " - "(participant {})", - pub_sid, identity); + case proto::RoomEvent::kTrackSubscriptionFailed: { + TrackSubscriptionFailedEvent ev; + { + const std::scoped_lock guard(lock_); + const auto& tsf = re.track_subscription_failed(); + const std::string& identity = tsf.participant_identity(); + auto pit = remote_participants_.find(identity); + if (pit == remote_participants_.end()) { + LK_LOG_WARN("track_subscription_failed for unknown participant: {}", identity); + break; + } + ev.participant = pit->second.get(); + ev.track_sid = tsf.track_sid(); + ev.error = tsf.error(); + } + if (delegate_snapshot) { + delegate_snapshot->onTrackSubscriptionFailed(*this, ev); + } break; } - ev.participant = rparticipant; - ev.publication = it->second; - pubs.erase(it); - } + case proto::RoomEvent::kDataTrackPublished: { + const auto& rdtp = re.data_track_published(); + auto remote_track = std::shared_ptr(new RemoteDataTrack(rdtp.track())); - if (delegate_snapshot) { - delegate_snapshot->onTrackUnpublished(*this, ev); - } - break; - } - case proto::RoomEvent::kTrackSubscribed: { - const auto &ts = re.track_subscribed(); - const std::string &identity = ts.participant_identity(); - const auto &owned_track = ts.track(); - const auto &track_info = owned_track.info(); - std::shared_ptr rpublication; - RemoteParticipant *rparticipant = nullptr; - std::shared_ptr remote_track; - { - const std::scoped_lock guard(lock_); - // Find participant - auto pit = remote_participants_.find(identity); - if (pit == remote_participants_.end()) { - LK_LOG_WARN("track_subscribed for unknown participant: {}", identity); + if (subscription_thread_dispatcher_) { + subscription_thread_dispatcher_->handleDataTrackPublished(remote_track); + } + + DataTrackPublishedEvent ev; + ev.track = remote_track; + if (delegate_snapshot) { + delegate_snapshot->onDataTrackPublished(*this, ev); + } break; } - rparticipant = pit->second.get(); - // Find existing publication by track SID (from track_published) - auto &pubs = rparticipant->mutableTrackPublications(); - auto pubIt = pubs.find(track_info.sid()); - if (pubIt == pubs.end()) { - LK_LOG_WARN("track_subscribed for unknown publication sid {} " - "(participant {})", - track_info.sid(), identity); + case proto::RoomEvent::kDataTrackUnpublished: { + const auto& dtu = re.data_track_unpublished(); + + if (subscription_thread_dispatcher_) { + subscription_thread_dispatcher_->handleDataTrackUnpublished(dtu.sid()); + } + + DataTrackUnpublishedEvent ev; + ev.sid = dtu.sid(); + if (delegate_snapshot) { + delegate_snapshot->onDataTrackUnpublished(*this, ev); + } break; } - rpublication = pubIt->second; - - // Create RemoteVideoTrack / RemoteAudioTrack - if (track_info.kind() == proto::TrackKind::KIND_VIDEO) { - remote_track = std::make_shared(owned_track); - } else if (track_info.kind() == proto::TrackKind::KIND_AUDIO) { - remote_track = std::make_shared(owned_track); - } else { - LK_LOG_WARN("track_subscribed with unsupported kind: {}", - static_cast(track_info.kind())); + case proto::RoomEvent::kTrackMuted: { + TrackMutedEvent ev; + bool success = false; + { + const std::scoped_lock guard(lock_); + const auto& tm = re.track_muted(); + const std::string& identity = tm.participant_identity(); + const std::string& sid = tm.track_sid(); + Participant* participant = nullptr; + if (local_participant_ && local_participant_->identity() == identity) { + participant = local_participant_.get(); + } else { + auto pit = remote_participants_.find(identity); + if (pit != remote_participants_.end()) { + participant = pit->second.get(); + } + } + if (!participant) { + LK_LOG_WARN("track_muted for unknown participant: {}", identity); + break; + } + auto pub = participant->findTrackPublication(sid); + if (!pub) { + LK_LOG_WARN("track_muted for unknown track sid: {}", sid); + } else { + pub->setMuted(true); + if (auto t = pub->track()) { + t->setMuted(true); + } + ev.participant = participant; + ev.publication = pub; + success = true; + } + } + if (success && delegate_snapshot) { + delegate_snapshot->onTrackMuted(*this, ev); + } break; } - // Attach to publication, mark subscribed - rpublication->setTrack(remote_track); - rpublication->setSubscribed(true); - } + case proto::RoomEvent::kTrackUnmuted: { + TrackUnmutedEvent ev; + bool success = false; + { + const std::scoped_lock guard(lock_); + const auto& tu = re.track_unmuted(); + const std::string& identity = tu.participant_identity(); + const std::string& sid = tu.track_sid(); + Participant* participant = nullptr; + if (local_participant_ && local_participant_->identity() == identity) { + participant = local_participant_.get(); + } else { + auto pit = remote_participants_.find(identity); + if (pit != remote_participants_.end()) { + participant = pit->second.get(); + } + } + if (!participant) { + LK_LOG_WARN("track_unmuted for unknown participant: {}", identity); + break; + } - // Emit remote track_subscribed-style callback - TrackSubscribedEvent ev; - ev.track = remote_track; - ev.publication = rpublication; - ev.participant = rparticipant; - if (delegate_snapshot) { - delegate_snapshot->onTrackSubscribed(*this, ev); - } + auto pub = participant->findTrackPublication(sid); + if (!pub) { + LK_LOG_WARN("track_unmuted for unknown track sid: {}", sid); + } else { + pub->setMuted(false); + if (auto t = pub->track()) { + t->setMuted(false); + } + ev.participant = participant; + ev.publication = pub; + success = true; + } - if (subscription_thread_dispatcher_ && remote_track && rpublication) { - subscription_thread_dispatcher_->handleTrackSubscribed( - identity, rpublication->source(), rpublication->name(), - remote_track); - } - break; - } - case proto::RoomEvent::kTrackUnsubscribed: { - TrackUnsubscribedEvent ev; - TrackSource unsub_source = TrackSource::SOURCE_UNKNOWN; - std::string unsub_identity; - { - const std::scoped_lock guard(lock_); - const auto &tu = re.track_unsubscribed(); - unsub_identity = tu.participant_identity(); - const std::string &track_sid = tu.track_sid(); - auto pit = remote_participants_.find(unsub_identity); - if (pit == remote_participants_.end()) { - LK_LOG_WARN("track_unsubscribed for unknown participant: {}", - unsub_identity); + ev.participant = participant; + ev.publication = pub; + } + + if (success && delegate_snapshot) { + delegate_snapshot->onTrackUnmuted(*this, ev); + } break; } - RemoteParticipant *rparticipant = pit->second.get(); - auto &pubs = rparticipant->mutableTrackPublications(); - auto pubIt = pubs.find(track_sid); - if (pubIt == pubs.end()) { - LK_LOG_WARN("track_unsubscribed for unknown publication sid {} " - "(participant {})", - track_sid, unsub_identity); + case proto::RoomEvent::kActiveSpeakersChanged: { + ActiveSpeakersChangedEvent ev; + { + const std::scoped_lock guard(lock_); + const auto& asc = re.active_speakers_changed(); + for (const auto& identity : asc.participant_identities()) { + // Appears to be clang-tidy false positive + // NOLINTNEXTLINE(misc-const-correctness) + Participant* participant = nullptr; + if (local_participant_ && local_participant_->identity() == identity) { + participant = local_participant_.get(); + } else { + auto pit = remote_participants_.find(identity); + if (pit != remote_participants_.end()) { + participant = pit->second.get(); + } + } + if (participant) { + ev.speakers.push_back(participant); + } + } + } + if (delegate_snapshot) { + delegate_snapshot->onActiveSpeakersChanged(*this, ev); + } break; } - auto publication = pubIt->second; - unsub_source = publication->source(); - auto track = publication->track(); - publication->setTrack(nullptr); - publication->setSubscribed(false); - ev.participant = rparticipant; - ev.publication = publication; - ev.track = track; - } - - if (delegate_snapshot) { - delegate_snapshot->onTrackUnsubscribed(*this, ev); - } - - if (subscription_thread_dispatcher_ && - unsub_source != TrackSource::SOURCE_UNKNOWN) { - subscription_thread_dispatcher_->handleTrackUnsubscribed( - unsub_identity, unsub_source, - ev.publication ? ev.publication->name() : ""); - } - break; - } - case proto::RoomEvent::kTrackSubscriptionFailed: { - TrackSubscriptionFailedEvent ev; - { - const std::scoped_lock guard(lock_); - const auto &tsf = re.track_subscription_failed(); - const std::string &identity = tsf.participant_identity(); - auto pit = remote_participants_.find(identity); - if (pit == remote_participants_.end()) { - LK_LOG_WARN("track_subscription_failed for unknown participant: {}", - identity); + case proto::RoomEvent::kRoomMetadataChanged: { + RoomMetadataChangedEvent ev; + { + const std::scoped_lock guard(lock_); + const auto old_metadata = room_info_.metadata; + room_info_.metadata = re.room_metadata_changed().metadata(); + ev.old_metadata = old_metadata; + ev.new_metadata = room_info_.metadata; + } + if (delegate_snapshot) { + delegate_snapshot->onRoomMetadataChanged(*this, ev); + } break; } - ev.participant = pit->second.get(); - ev.track_sid = tsf.track_sid(); - ev.error = tsf.error(); - } - if (delegate_snapshot) { - delegate_snapshot->onTrackSubscriptionFailed(*this, ev); - } - break; - } - case proto::RoomEvent::kDataTrackPublished: { - const auto &rdtp = re.data_track_published(); - auto remote_track = - std::shared_ptr(new RemoteDataTrack(rdtp.track())); - - if (subscription_thread_dispatcher_) { - subscription_thread_dispatcher_->handleDataTrackPublished(remote_track); - } - - DataTrackPublishedEvent ev; - ev.track = remote_track; - if (delegate_snapshot) { - delegate_snapshot->onDataTrackPublished(*this, ev); - } - break; - } - case proto::RoomEvent::kDataTrackUnpublished: { - const auto &dtu = re.data_track_unpublished(); - - if (subscription_thread_dispatcher_) { - subscription_thread_dispatcher_->handleDataTrackUnpublished(dtu.sid()); - } - - DataTrackUnpublishedEvent ev; - ev.sid = dtu.sid(); - if (delegate_snapshot) { - delegate_snapshot->onDataTrackUnpublished(*this, ev); - } - break; - } - case proto::RoomEvent::kTrackMuted: { - TrackMutedEvent ev; - bool success = false; - { - const std::scoped_lock guard(lock_); - const auto &tm = re.track_muted(); - const std::string &identity = tm.participant_identity(); - const std::string &sid = tm.track_sid(); - Participant *participant = nullptr; - if (local_participant_ && local_participant_->identity() == identity) { - participant = local_participant_.get(); - } else { - auto pit = remote_participants_.find(identity); - if (pit != remote_participants_.end()) { - participant = pit->second.get(); + case proto::RoomEvent::kRoomSidChanged: { + RoomSidChangedEvent ev; + { + const std::scoped_lock guard(lock_); + room_info_.sid = re.room_sid_changed().sid(); + ev.sid = room_info_.sid.value_or(std::string{}); + } + if (delegate_snapshot) { + delegate_snapshot->onRoomSidChanged(*this, ev); } - } - if (!participant) { - LK_LOG_WARN("track_muted for unknown participant: {}", identity); break; } - auto pub = participant->findTrackPublication(sid); - if (!pub) { - LK_LOG_WARN("track_muted for unknown track sid: {}", sid); - } else { - pub->setMuted(true); - if (auto t = pub->track()) { - t->setMuted(true); - } - ev.participant = participant; - ev.publication = pub; - success = true; + case proto::RoomEvent::kParticipantMetadataChanged: { + ParticipantMetadataChangedEvent ev; + { + const std::scoped_lock guard(lock_); + const auto& pm = re.participant_metadata_changed(); + const std::string& identity = pm.participant_identity(); + Participant* participant = nullptr; + if (local_participant_ && local_participant_->identity() == identity) { + participant = local_participant_.get(); + } else { + auto it = remote_participants_.find(identity); + if (it != remote_participants_.end()) { + participant = it->second.get(); + } + } + if (!participant) { + LK_LOG_WARN("participant_metadata_changed for unknown participant: {}", identity); + break; + } + const std::string old_metadata = participant->metadata(); + participant->set_metadata(pm.metadata()); + ev.participant = participant; + ev.old_metadata = old_metadata; + ev.new_metadata = participant->metadata(); + } + + if (delegate_snapshot) { + delegate_snapshot->onParticipantMetadataChanged(*this, ev); + } + break; } - } - if (success && delegate_snapshot) { - delegate_snapshot->onTrackMuted(*this, ev); - } - break; - } - case proto::RoomEvent::kTrackUnmuted: { - TrackUnmutedEvent ev; - bool success = false; - { - const std::scoped_lock guard(lock_); - const auto &tu = re.track_unmuted(); - const std::string &identity = tu.participant_identity(); - const std::string &sid = tu.track_sid(); - Participant *participant = nullptr; - if (local_participant_ && local_participant_->identity() == identity) { - participant = local_participant_.get(); - } else { - auto pit = remote_participants_.find(identity); - if (pit != remote_participants_.end()) { - participant = pit->second.get(); + case proto::RoomEvent::kParticipantNameChanged: { + ParticipantNameChangedEvent ev; + { + const std::scoped_lock guard(lock_); + const auto& pn = re.participant_name_changed(); + const std::string& identity = pn.participant_identity(); + Participant* participant = nullptr; + if (local_participant_ && local_participant_->identity() == identity) { + participant = local_participant_.get(); + } else { + auto it = remote_participants_.find(identity); + if (it != remote_participants_.end()) { + participant = it->second.get(); + } + } + if (!participant) { + LK_LOG_WARN("participant_name_changed for unknown participant: {}", identity); + break; + } + const std::string old_name = participant->name(); + participant->set_name(pn.name()); + ev.participant = participant; + ev.old_name = old_name; + ev.new_name = participant->name(); + } + if (delegate_snapshot) { + delegate_snapshot->onParticipantNameChanged(*this, ev); } + break; } - if (!participant) { - LK_LOG_WARN("track_unmuted for unknown participant: {}", identity); + case proto::RoomEvent::kParticipantAttributesChanged: { + ParticipantAttributesChangedEvent ev; + { + const std::scoped_lock guard(lock_); + const auto& pa = re.participant_attributes_changed(); + const std::string& identity = pa.participant_identity(); + Participant* participant = nullptr; + if (local_participant_ && local_participant_->identity() == identity) { + participant = local_participant_.get(); + } else { + auto it = remote_participants_.find(identity); + if (it != remote_participants_.end()) { + participant = it->second.get(); + } + } + if (!participant) { + LK_LOG_WARN("participant_attributes_changed for unknown participant: {}", identity); + break; + } + // Build full attributes map + std::unordered_map attrs; + for (const auto& entry : pa.attributes()) { + attrs.emplace(entry.key(), entry.value()); + } + participant->set_attributes(attrs); + + // Build changed_attributes map + for (const auto& entry : pa.changed_attributes()) { + ev.changed_attributes.emplace_back(entry.key(), entry.value()); + } + ev.participant = participant; + } + if (delegate_snapshot) { + delegate_snapshot->onParticipantAttributesChanged(*this, ev); + } break; } + case proto::RoomEvent::kParticipantEncryptionStatusChanged: { + ParticipantEncryptionStatusChangedEvent ev; + { + const std::scoped_lock guard(lock_); + const auto& pe = re.participant_encryption_status_changed(); + const std::string& identity = pe.participant_identity(); + Participant* participant = nullptr; + if (local_participant_ && local_participant_->identity() == identity) { + participant = local_participant_.get(); + } else { + auto it = remote_participants_.find(identity); + if (it != remote_participants_.end()) { + participant = it->second.get(); + } + } + if (!participant) { + LK_LOG_WARN( + "participant_encryption_status_changed for unknown " + "participant: {}", + identity); + break; + } + ev.participant = participant; + ev.is_encrypted = pe.is_encrypted(); + } - auto pub = participant->findTrackPublication(sid); - if (!pub) { - LK_LOG_WARN("track_unmuted for unknown track sid: {}", sid); - } else { - pub->setMuted(false); - if (auto t = pub->track()) { - t->setMuted(false); - } - ev.participant = participant; - ev.publication = pub; - success = true; + if (delegate_snapshot) { + delegate_snapshot->onParticipantEncryptionStatusChanged(*this, ev); + } + break; } + case proto::RoomEvent::kConnectionQualityChanged: { + ConnectionQualityChangedEvent ev; + { + const std::scoped_lock guard(lock_); + const auto& cq = re.connection_quality_changed(); + const std::string& identity = cq.participant_identity(); + Participant* participant = nullptr; + if (local_participant_ && local_participant_->identity() == identity) { + participant = local_participant_.get(); + } else { + auto it = remote_participants_.find(identity); + if (it != remote_participants_.end()) { + participant = it->second.get(); + } + } + if (!participant) { + LK_LOG_WARN("connection_quality_changed for unknown participant: {}", identity); + break; + } + ev.participant = participant; + ev.quality = static_cast(cq.quality()); + } - ev.participant = participant; - ev.publication = pub; - } + if (delegate_snapshot) { + delegate_snapshot->onConnectionQualityChanged(*this, ev); + } + break; + } - if (success && delegate_snapshot) { - delegate_snapshot->onTrackUnmuted(*this, ev); - } - break; - } - case proto::RoomEvent::kActiveSpeakersChanged: { - ActiveSpeakersChangedEvent ev; - { - const std::scoped_lock guard(lock_); - const auto &asc = re.active_speakers_changed(); - for (const auto &identity : asc.participant_identities()) { - // Appears to be clang-tidy false positive - // NOLINTNEXTLINE(misc-const-correctness) - Participant *participant = nullptr; - if (local_participant_ && - local_participant_->identity() == identity) { - participant = local_participant_.get(); - } else { - auto pit = remote_participants_.find(identity); - if (pit != remote_participants_.end()) { - participant = pit->second.get(); + // ------------------------------------------------------------------------ + // Data packets: user vs SIP DTMF + // ------------------------------------------------------------------------ + case proto::RoomEvent::kDataPacketReceived: { + const auto& dp = re.data_packet_received(); + RemoteParticipant* rp = nullptr; + { + const std::scoped_lock guard(lock_); + auto it = remote_participants_.find(dp.participant_identity()); + if (it != remote_participants_.end()) { + rp = it->second.get(); } } - if (participant) { - ev.speakers.push_back(participant); + const auto which_val = dp.value_case(); + if (which_val == proto::DataPacketReceived::kUser && delegate_snapshot) { + const UserDataPacketEvent ev = userDataPacketFromProto(dp, rp); + delegate_snapshot->onUserPacketReceived(*this, ev); + } else if (which_val == proto::DataPacketReceived::kSipDtmf && delegate_snapshot) { + const SipDtmfReceivedEvent ev = sipDtmfFromProto(dp, rp); + delegate_snapshot->onSipDtmfReceived(*this, ev); } + break; } - } - if (delegate_snapshot) { - delegate_snapshot->onActiveSpeakersChanged(*this, ev); - } - break; - } - case proto::RoomEvent::kRoomMetadataChanged: { - RoomMetadataChangedEvent ev; - { - const std::scoped_lock guard(lock_); - const auto old_metadata = room_info_.metadata; - room_info_.metadata = re.room_metadata_changed().metadata(); - ev.old_metadata = old_metadata; - ev.new_metadata = room_info_.metadata; - } - if (delegate_snapshot) { - delegate_snapshot->onRoomMetadataChanged(*this, ev); - } - break; - } - case proto::RoomEvent::kRoomSidChanged: { - RoomSidChangedEvent ev; - { - const std::scoped_lock guard(lock_); - room_info_.sid = re.room_sid_changed().sid(); - ev.sid = room_info_.sid.value_or(std::string{}); - } - if (delegate_snapshot) { - delegate_snapshot->onRoomSidChanged(*this, ev); - } - break; - } - case proto::RoomEvent::kParticipantMetadataChanged: { - ParticipantMetadataChangedEvent ev; - { - const std::scoped_lock guard(lock_); - const auto &pm = re.participant_metadata_changed(); - const std::string &identity = pm.participant_identity(); - Participant *participant = nullptr; - if (local_participant_ && local_participant_->identity() == identity) { - participant = local_participant_.get(); - } else { - auto it = remote_participants_.find(identity); - if (it != remote_participants_.end()) { - participant = it->second.get(); + + // ------------------------------------------------------------------------ + // E2EE state + // ------------------------------------------------------------------------ + case proto::RoomEvent::kE2EeStateChanged: { + E2eeStateChangedEvent ev; + { + LK_LOG_DEBUG("e2ee_state_changed for participant"); + const std::scoped_lock guard(lock_); + const auto& es = re.e2ee_state_changed(); + const std::string& identity = es.participant_identity(); + Participant* participant = nullptr; + if (local_participant_ && local_participant_->identity() == identity) { + participant = local_participant_.get(); + } else { + auto it = remote_participants_.find(identity); + if (it != remote_participants_.end()) { + participant = it->second.get(); + } + } + if (!participant) { + LK_LOG_WARN("e2ee_state_changed for unknown participant: {}", identity); + break; + } + + ev.participant = participant; + ev.state = static_cast(es.state()); + } + if (delegate_snapshot) { + delegate_snapshot->onE2eeStateChanged(*this, ev); } - } - if (!participant) { - LK_LOG_WARN( - "participant_metadata_changed for unknown participant: {}", - identity); break; } - const std::string old_metadata = participant->metadata(); - participant->set_metadata(pm.metadata()); - ev.participant = participant; - ev.old_metadata = old_metadata; - ev.new_metadata = participant->metadata(); - } - if (delegate_snapshot) { - delegate_snapshot->onParticipantMetadataChanged(*this, ev); - } - break; - } - case proto::RoomEvent::kParticipantNameChanged: { - ParticipantNameChangedEvent ev; - { - const std::scoped_lock guard(lock_); - const auto &pn = re.participant_name_changed(); - const std::string &identity = pn.participant_identity(); - Participant *participant = nullptr; - if (local_participant_ && local_participant_->identity() == identity) { - participant = local_participant_.get(); - } else { - auto it = remote_participants_.find(identity); - if (it != remote_participants_.end()) { - participant = it->second.get(); + // ------------------------------------------------------------------------ + // Connection state / lifecycle + // ------------------------------------------------------------------------ + + case proto::RoomEvent::kConnectionStateChanged: { + ConnectionStateChangedEvent ev; + { + const std::scoped_lock guard(lock_); + const auto& cs = re.connection_state_changed(); + // TODO, maybe we should update our |connection_state_| + // correspoindingly, but the this kConnectionStateChanged event is never + // triggered in my local test. + LK_LOG_DEBUG("cs.state() is {} connection_state_ is {}", static_cast(cs.state()), + static_cast(connection_state_)); + ev.state = static_cast(cs.state()); + } + if (delegate_snapshot) { + delegate_snapshot->onConnectionStateChanged(*this, ev); } - } - if (!participant) { - LK_LOG_WARN("participant_name_changed for unknown participant: {}", - identity); break; } - const std::string old_name = participant->name(); - participant->set_name(pn.name()); - ev.participant = participant; - ev.old_name = old_name; - ev.new_name = participant->name(); - } - if (delegate_snapshot) { - delegate_snapshot->onParticipantNameChanged(*this, ev); - } - break; - } - case proto::RoomEvent::kParticipantAttributesChanged: { - ParticipantAttributesChangedEvent ev; - { - const std::scoped_lock guard(lock_); - const auto &pa = re.participant_attributes_changed(); - const std::string &identity = pa.participant_identity(); - Participant *participant = nullptr; - if (local_participant_ && local_participant_->identity() == identity) { - participant = local_participant_.get(); - } else { - auto it = remote_participants_.find(identity); - if (it != remote_participants_.end()) { - participant = it->second.get(); + case proto::RoomEvent::kDisconnected: { + DisconnectedEvent ev; + ev.reason = toDisconnectReason(re.disconnected().reason()); + if (delegate_snapshot) { + delegate_snapshot->onDisconnected(*this, ev); } + break; } - if (!participant) { - LK_LOG_WARN( - "participant_attributes_changed for unknown participant: {}", - identity); + case proto::RoomEvent::kReconnecting: { + const ReconnectingEvent ev; + if (delegate_snapshot) { + delegate_snapshot->onReconnecting(*this, ev); + } break; } - // Build full attributes map - std::unordered_map attrs; - for (const auto &entry : pa.attributes()) { - attrs.emplace(entry.key(), entry.value()); + case proto::RoomEvent::kReconnected: { + const ReconnectedEvent ev; + if (delegate_snapshot) { + delegate_snapshot->onReconnected(*this, ev); + } + break; } - participant->set_attributes(attrs); + case proto::RoomEvent::kEos: { + if (subscription_thread_dispatcher_) { + subscription_thread_dispatcher_->stopAll(); + } - // Build changed_attributes map - for (const auto &entry : pa.changed_attributes()) { - ev.changed_attributes.emplace_back(entry.key(), entry.value()); - } - ev.participant = participant; - } - if (delegate_snapshot) { - delegate_snapshot->onParticipantAttributesChanged(*this, ev); - } - break; - } - case proto::RoomEvent::kParticipantEncryptionStatusChanged: { - ParticipantEncryptionStatusChangedEvent ev; - { - const std::scoped_lock guard(lock_); - const auto &pe = re.participant_encryption_status_changed(); - const std::string &identity = pe.participant_identity(); - Participant *participant = nullptr; - if (local_participant_ && local_participant_->identity() == identity) { - participant = local_participant_.get(); - } else { - auto it = remote_participants_.find(identity); - if (it != remote_participants_.end()) { - participant = it->second.get(); + int listener_to_remove = 0; + + // Move state out of lock scope before destroying to avoid holding lock + // during potentially long destructors + std::unique_ptr old_local_participant; + std::unordered_map> old_remote_participants; + std::shared_ptr old_room_handle; + std::unique_ptr old_e2ee_manager; + std::unordered_map> old_text_readers; + std::unordered_map> old_byte_readers; + + { + const std::scoped_lock guard(lock_); + listener_to_remove = listener_id_; + listener_id_ = 0; + + // Reset connection state + connection_state_ = ConnectionState::Disconnected; + + // Move state out for cleanup outside lock + old_local_participant = std::move(local_participant_); + old_remote_participants = std::move(remote_participants_); + old_room_handle = std::move(room_handle_); + old_e2ee_manager = std::move(e2ee_manager_); + old_text_readers = std::move(text_stream_readers_); + old_byte_readers = std::move(byte_stream_readers_); + } + + // Remove listener outside lock + if (listener_to_remove != 0) { + FfiClient::instance().RemoveListener(listener_to_remove); + } + + // Old state will be destroyed here when going out of scope + + const RoomEosEvent ev; + if (delegate_snapshot) { + delegate_snapshot->onRoomEos(*this, ev); } + break; } - if (!participant) { - LK_LOG_WARN("participant_encryption_status_changed for unknown " - "participant: {}", - identity); + case proto::RoomEvent::kChatMessage: { + // Deprecated event, do nothing. break; } - ev.participant = participant; - ev.is_encrypted = pe.is_encrypted(); - } + case proto::RoomEvent::kStreamHeaderReceived: { + const auto& sh = re.stream_header_received(); + const auto& header = sh.header(); + const std::string& participant_identity = sh.participant_identity(); + + // Snapshot handler + create reader without holding lock during user + // callback + TextStreamHandler text_cb; + ByteStreamHandler byte_cb; + std::shared_ptr text_reader; + std::shared_ptr byte_reader; + { + const std::scoped_lock guard(lock_); + + // Determine stream type from oneof in protobuf + // Adjust these names if your generated C++ uses different ones + const auto stream_type = header.content_header_case(); + if (stream_type == proto::DataStream::Header::kTextHeader) { + auto it = text_stream_handlers_.find(header.topic()); + if (it == text_stream_handlers_.end()) { + // Ignore if no callback attached + break; + } + text_cb = it->second; + + const TextStreamInfo info = makeTextInfo(header); + text_reader = std::make_shared(info); + text_stream_readers_[header.stream_id()] = text_reader; + + } else if (stream_type == proto::DataStream::Header::kByteHeader) { + auto it = byte_stream_handlers_.find(header.topic()); + if (it == byte_stream_handlers_.end()) { + break; + } + byte_cb = it->second; + const ByteStreamInfo info = makeByteInfo(header); + byte_reader = std::make_shared(info); + byte_stream_readers_[header.stream_id()] = byte_reader; + + } else { + // unknown header type: ignore + break; + } + } - if (delegate_snapshot) { - delegate_snapshot->onParticipantEncryptionStatusChanged(*this, ev); - } - break; - } - case proto::RoomEvent::kConnectionQualityChanged: { - ConnectionQualityChangedEvent ev; - { - const std::scoped_lock guard(lock_); - const auto &cq = re.connection_quality_changed(); - const std::string &identity = cq.participant_identity(); - Participant *participant = nullptr; - if (local_participant_ && local_participant_->identity() == identity) { - participant = local_participant_.get(); - } else { - auto it = remote_participants_.find(identity); - if (it != remote_participants_.end()) { - participant = it->second.get(); + // Invoke user callback outside lock (very important) + if (text_reader) { + text_cb(text_reader, participant_identity); + } else if (byte_reader) { + byte_cb(byte_reader, participant_identity); } + break; } - if (!participant) { - LK_LOG_WARN("connection_quality_changed for unknown participant: {}", - identity); + case proto::RoomEvent::kStreamChunkReceived: { + const auto& sc = re.stream_chunk_received(); + const auto& chunk = sc.chunk(); + std::shared_ptr text_reader; + std::shared_ptr byte_reader; + { + const std::scoped_lock guard(lock_); + auto itT = text_stream_readers_.find(chunk.stream_id()); + if (itT != text_stream_readers_.end()) { + text_reader = itT->second; + } else { + auto itB = byte_stream_readers_.find(chunk.stream_id()); + if (itB != byte_stream_readers_.end()) { + byte_reader = itB->second; + } + } + } + if (text_reader) { + // chunk.content() is bytes; treat as UTF-8 string. + text_reader->onChunkUpdate(chunk.content()); + } else if (byte_reader) { + // Convert string bytes -> vector + const std::string& s = chunk.content(); + const std::vector bytes(s.begin(), s.end()); + byte_reader->onChunkUpdate(bytes); + } break; } - ev.participant = participant; - ev.quality = static_cast(cq.quality()); - } - - if (delegate_snapshot) { - delegate_snapshot->onConnectionQualityChanged(*this, ev); - } - break; - } - - // ------------------------------------------------------------------------ - // Data packets: user vs SIP DTMF - // ------------------------------------------------------------------------ - case proto::RoomEvent::kDataPacketReceived: { - const auto &dp = re.data_packet_received(); - RemoteParticipant *rp = nullptr; - { - const std::scoped_lock guard(lock_); - auto it = remote_participants_.find(dp.participant_identity()); - if (it != remote_participants_.end()) { - rp = it->second.get(); + case proto::RoomEvent::kStreamTrailerReceived: { + const auto& st = re.stream_trailer_received(); + const auto& trailer = st.trailer(); + std::shared_ptr text_reader; + std::shared_ptr byte_reader; + std::map trailer_attrs; + for (const auto& kv : trailer.attributes()) { + trailer_attrs.emplace(kv.first, kv.second); + } + { + const std::scoped_lock guard(lock_); + auto itT = text_stream_readers_.find(trailer.stream_id()); + if (itT != text_stream_readers_.end()) { + text_reader = itT->second; + text_stream_readers_.erase(itT); + } else { + auto itB = byte_stream_readers_.find(trailer.stream_id()); + if (itB != byte_stream_readers_.end()) { + byte_reader = itB->second; + byte_stream_readers_.erase(itB); + } + } + } + if (text_reader) { + text_reader->onStreamClose(trailer_attrs); + } else if (byte_reader) { + byte_reader->onStreamClose(trailer_attrs); + } + break; } - } - const auto which_val = dp.value_case(); - if (which_val == proto::DataPacketReceived::kUser && delegate_snapshot) { - const UserDataPacketEvent ev = userDataPacketFromProto(dp, rp); - delegate_snapshot->onUserPacketReceived(*this, ev); - } else if (which_val == proto::DataPacketReceived::kSipDtmf && - delegate_snapshot) { - const SipDtmfReceivedEvent ev = sipDtmfFromProto(dp, rp); - delegate_snapshot->onSipDtmfReceived(*this, ev); - } - break; - } - - // ------------------------------------------------------------------------ - // E2EE state - // ------------------------------------------------------------------------ - case proto::RoomEvent::kE2EeStateChanged: { - E2eeStateChangedEvent ev; - { - LK_LOG_DEBUG("e2ee_state_changed for participant"); - const std::scoped_lock guard(lock_); - const auto &es = re.e2ee_state_changed(); - const std::string &identity = es.participant_identity(); - Participant *participant = nullptr; - if (local_participant_ && local_participant_->identity() == identity) { - participant = local_participant_.get(); - } else { - auto it = remote_participants_.find(identity); - if (it != remote_participants_.end()) { - participant = it->second.get(); + case proto::RoomEvent::kDataChannelLowThresholdChanged: { + auto ev = fromProto(re.data_channel_low_threshold_changed()); + if (delegate_snapshot) { + delegate_snapshot->onDataChannelBufferedAmountLowThresholdChanged(*this, ev); } + break; } - if (!participant) { - LK_LOG_WARN("e2ee_state_changed for unknown participant: {}", - identity); + case proto::RoomEvent::kByteStreamOpened: { + auto ev = fromProto(re.byte_stream_opened()); + if (delegate_snapshot) { + delegate_snapshot->onByteStreamOpened(*this, ev); + } break; } - - ev.participant = participant; - ev.state = static_cast(es.state()); - } - if (delegate_snapshot) { - delegate_snapshot->onE2eeStateChanged(*this, ev); - } - break; - } - - // ------------------------------------------------------------------------ - // Connection state / lifecycle - // ------------------------------------------------------------------------ - - case proto::RoomEvent::kConnectionStateChanged: { - ConnectionStateChangedEvent ev; - { - const std::scoped_lock guard(lock_); - const auto &cs = re.connection_state_changed(); - // TODO, maybe we should update our |connection_state_| - // correspoindingly, but the this kConnectionStateChanged event is never - // triggered in my local test. - LK_LOG_DEBUG("cs.state() is {} connection_state_ is {}", - static_cast(cs.state()), - static_cast(connection_state_)); - ev.state = static_cast(cs.state()); - } - if (delegate_snapshot) { - delegate_snapshot->onConnectionStateChanged(*this, ev); - } - break; - } - case proto::RoomEvent::kDisconnected: { - DisconnectedEvent ev; - ev.reason = toDisconnectReason(re.disconnected().reason()); - if (delegate_snapshot) { - delegate_snapshot->onDisconnected(*this, ev); - } - break; - } - case proto::RoomEvent::kReconnecting: { - const ReconnectingEvent ev; - if (delegate_snapshot) { - delegate_snapshot->onReconnecting(*this, ev); - } - break; - } - case proto::RoomEvent::kReconnected: { - const ReconnectedEvent ev; - if (delegate_snapshot) { - delegate_snapshot->onReconnected(*this, ev); - } - break; - } - case proto::RoomEvent::kEos: { - if (subscription_thread_dispatcher_) { - subscription_thread_dispatcher_->stopAll(); - } - - int listener_to_remove = 0; - - // Move state out of lock scope before destroying to avoid holding lock - // during potentially long destructors - std::unique_ptr old_local_participant; - std::unordered_map> - old_remote_participants; - std::shared_ptr old_room_handle; - std::unique_ptr old_e2ee_manager; - std::unordered_map> - old_text_readers; - std::unordered_map> - old_byte_readers; - - { - const std::scoped_lock guard(lock_); - listener_to_remove = listener_id_; - listener_id_ = 0; - - // Reset connection state - connection_state_ = ConnectionState::Disconnected; - - // Move state out for cleanup outside lock - old_local_participant = std::move(local_participant_); - old_remote_participants = std::move(remote_participants_); - old_room_handle = std::move(room_handle_); - old_e2ee_manager = std::move(e2ee_manager_); - old_text_readers = std::move(text_stream_readers_); - old_byte_readers = std::move(byte_stream_readers_); - } - - // Remove listener outside lock - if (listener_to_remove != 0) { - FfiClient::instance().RemoveListener(listener_to_remove); - } - - // Old state will be destroyed here when going out of scope - - const RoomEosEvent ev; - if (delegate_snapshot) { - delegate_snapshot->onRoomEos(*this, ev); - } - break; - } - case proto::RoomEvent::kChatMessage: { - // Deprecated event, do nothing. - break; - } - case proto::RoomEvent::kStreamHeaderReceived: { - const auto &sh = re.stream_header_received(); - const auto &header = sh.header(); - const std::string &participant_identity = sh.participant_identity(); - - // Snapshot handler + create reader without holding lock during user - // callback - TextStreamHandler text_cb; - ByteStreamHandler byte_cb; - std::shared_ptr text_reader; - std::shared_ptr byte_reader; - { - const std::scoped_lock guard(lock_); - - // Determine stream type from oneof in protobuf - // Adjust these names if your generated C++ uses different ones - const auto stream_type = header.content_header_case(); - if (stream_type == proto::DataStream::Header::kTextHeader) { - auto it = text_stream_handlers_.find(header.topic()); - if (it == text_stream_handlers_.end()) { - // Ignore if no callback attached - break; - } - text_cb = it->second; - - const TextStreamInfo info = makeTextInfo(header); - text_reader = std::make_shared(info); - text_stream_readers_[header.stream_id()] = text_reader; - - } else if (stream_type == proto::DataStream::Header::kByteHeader) { - auto it = byte_stream_handlers_.find(header.topic()); - if (it == byte_stream_handlers_.end()) { - break; - } - byte_cb = it->second; - const ByteStreamInfo info = makeByteInfo(header); - byte_reader = std::make_shared(info); - byte_stream_readers_[header.stream_id()] = byte_reader; - - } else { - // unknown header type: ignore + case proto::RoomEvent::kTextStreamOpened: { + auto ev = fromProto(re.text_stream_opened()); + if (delegate_snapshot) { + delegate_snapshot->onTextStreamOpened(*this, ev); + } break; } - } - - // Invoke user callback outside lock (very important) - if (text_reader) { - text_cb(text_reader, participant_identity); - } else if (byte_reader) { - byte_cb(byte_reader, participant_identity); - } - break; - } - case proto::RoomEvent::kStreamChunkReceived: { - const auto &sc = re.stream_chunk_received(); - const auto &chunk = sc.chunk(); - std::shared_ptr text_reader; - std::shared_ptr byte_reader; - { - const std::scoped_lock guard(lock_); - auto itT = text_stream_readers_.find(chunk.stream_id()); - if (itT != text_stream_readers_.end()) { - text_reader = itT->second; - } else { - auto itB = byte_stream_readers_.find(chunk.stream_id()); - if (itB != byte_stream_readers_.end()) { - byte_reader = itB->second; + case proto::RoomEvent::kRoomUpdated: { + auto ev = roomUpdatedFromProto(re.room_updated()); + if (delegate_snapshot) { + delegate_snapshot->onRoomUpdated(*this, ev); } + break; } - } - if (text_reader) { - // chunk.content() is bytes; treat as UTF-8 string. - text_reader->onChunkUpdate(chunk.content()); - } else if (byte_reader) { - // Convert string bytes -> vector - const std::string &s = chunk.content(); - const std::vector bytes(s.begin(), s.end()); - byte_reader->onChunkUpdate(bytes); - } - break; - } - case proto::RoomEvent::kStreamTrailerReceived: { - const auto &st = re.stream_trailer_received(); - const auto &trailer = st.trailer(); - std::shared_ptr text_reader; - std::shared_ptr byte_reader; - std::map trailer_attrs; - for (const auto &kv : trailer.attributes()) { - trailer_attrs.emplace(kv.first, kv.second); - } - { - const std::scoped_lock guard(lock_); - auto itT = text_stream_readers_.find(trailer.stream_id()); - if (itT != text_stream_readers_.end()) { - text_reader = itT->second; - text_stream_readers_.erase(itT); - } else { - auto itB = byte_stream_readers_.find(trailer.stream_id()); - if (itB != byte_stream_readers_.end()) { - byte_reader = itB->second; - byte_stream_readers_.erase(itB); + case proto::RoomEvent::kMoved: { + auto ev = roomMovedFromProto(re.moved()); + if (delegate_snapshot) { + delegate_snapshot->onRoomMoved(*this, ev); } + break; } - } - if (text_reader) { - text_reader->onStreamClose(trailer_attrs); - } else if (byte_reader) { - byte_reader->onStreamClose(trailer_attrs); - } - break; - } - case proto::RoomEvent::kDataChannelLowThresholdChanged: { - auto ev = fromProto(re.data_channel_low_threshold_changed()); - if (delegate_snapshot) { - delegate_snapshot->onDataChannelBufferedAmountLowThresholdChanged(*this, - ev); - } - break; - } - case proto::RoomEvent::kByteStreamOpened: { - auto ev = fromProto(re.byte_stream_opened()); - if (delegate_snapshot) { - delegate_snapshot->onByteStreamOpened(*this, ev); - } - break; - } - case proto::RoomEvent::kTextStreamOpened: { - auto ev = fromProto(re.text_stream_opened()); - if (delegate_snapshot) { - delegate_snapshot->onTextStreamOpened(*this, ev); - } - break; - } - case proto::RoomEvent::kRoomUpdated: { - auto ev = roomUpdatedFromProto(re.room_updated()); - if (delegate_snapshot) { - delegate_snapshot->onRoomUpdated(*this, ev); - } - break; - } - case proto::RoomEvent::kMoved: { - auto ev = roomMovedFromProto(re.moved()); - if (delegate_snapshot) { - delegate_snapshot->onRoomMoved(*this, ev); - } - break; - } - case proto::RoomEvent::kParticipantsUpdated: { - ParticipantsUpdatedEvent ev; - { - const std::scoped_lock guard(lock_); - const auto &pu = re.participants_updated(); - for (const auto &info : pu.participants()) { - const std::string &identity = info.identity(); - Participant *participant = nullptr; - - if (local_participant_ && - identity == local_participant_->identity()) { - participant = local_participant_.get(); - } else { - auto it = remote_participants_.find(identity); - if (it != remote_participants_.end()) { - participant = it->second.get(); + case proto::RoomEvent::kParticipantsUpdated: { + ParticipantsUpdatedEvent ev; + { + const std::scoped_lock guard(lock_); + const auto& pu = re.participants_updated(); + for (const auto& info : pu.participants()) { + const std::string& identity = info.identity(); + Participant* participant = nullptr; + + if (local_participant_ && identity == local_participant_->identity()) { + participant = local_participant_.get(); + } else { + auto it = remote_participants_.find(identity); + if (it != remote_participants_.end()) { + participant = it->second.get(); + } + } + if (!participant) { + LK_LOG_WARN("kParticipantsUpdated: participant does not exist: {}", identity); + continue; + } + + participant->set_name(info.name()); + participant->set_metadata(info.metadata()); + + std::unordered_map attrs; + attrs.reserve(info.attributes_size()); + for (const auto& kv : info.attributes()) { + attrs.emplace(kv.first, kv.second); + } + participant->set_attributes(std::move(attrs)); + participant->set_kind(fromProto(info.kind())); + participant->set_disconnect_reason(toDisconnectReason(info.disconnect_reason())); + + ev.participants.push_back(participant); } } - if (!participant) { - LK_LOG_WARN("kParticipantsUpdated: participant does not exist: {}", - identity); - continue; - } - - participant->set_name(info.name()); - participant->set_metadata(info.metadata()); - - std::unordered_map attrs; - attrs.reserve(info.attributes_size()); - for (const auto &kv : info.attributes()) { - attrs.emplace(kv.first, kv.second); + if (delegate_snapshot) { + delegate_snapshot->onParticipantsUpdated(*this, ev); } - participant->set_attributes(std::move(attrs)); - participant->set_kind(fromProto(info.kind())); - participant->set_disconnect_reason( - toDisconnectReason(info.disconnect_reason())); - - ev.participants.push_back(participant); + break; } + + case proto::RoomEvent::MESSAGE_NOT_SET: + default: + break; } - if (delegate_snapshot) { - delegate_snapshot->onParticipantsUpdated(*this, ev); - } + break; } - case proto::RoomEvent::MESSAGE_NOT_SET: default: break; - } - - break; - } - - default: - break; } } diff --git a/src/room_event_converter.cpp b/src/room_event_converter.cpp index 075adad5..305fb964 100644 --- a/src/room_event_converter.cpp +++ b/src/room_event_converter.cpp @@ -24,40 +24,40 @@ namespace livekit { ConnectionQuality toConnectionQuality(proto::ConnectionQuality src) { switch (src) { - case proto::QUALITY_POOR: - return ConnectionQuality::Poor; - case proto::QUALITY_GOOD: - return ConnectionQuality::Good; - case proto::QUALITY_EXCELLENT: - return ConnectionQuality::Excellent; - case proto::QUALITY_LOST: - return ConnectionQuality::Lost; - default: - return ConnectionQuality::Good; + case proto::QUALITY_POOR: + return ConnectionQuality::Poor; + case proto::QUALITY_GOOD: + return ConnectionQuality::Good; + case proto::QUALITY_EXCELLENT: + return ConnectionQuality::Excellent; + case proto::QUALITY_LOST: + return ConnectionQuality::Lost; + default: + return ConnectionQuality::Good; } } ConnectionState toConnectionState(proto::ConnectionState src) { switch (src) { - case proto::CONN_DISCONNECTED: - return ConnectionState::Disconnected; - case proto::CONN_CONNECTED: - return ConnectionState::Connected; - case proto::CONN_RECONNECTING: - return ConnectionState::Reconnecting; - default: - return ConnectionState::Disconnected; + case proto::CONN_DISCONNECTED: + return ConnectionState::Disconnected; + case proto::CONN_CONNECTED: + return ConnectionState::Connected; + case proto::CONN_RECONNECTING: + return ConnectionState::Reconnecting; + default: + return ConnectionState::Disconnected; } } DataPacketKind toDataPacketKind(proto::DataPacketKind src) { switch (src) { - case proto::KIND_LOSSY: - return DataPacketKind::Lossy; - case proto::KIND_RELIABLE: // NOLINT(bugprone-branch-clone) - return DataPacketKind::Reliable; - default: - return DataPacketKind::Reliable; + case proto::KIND_LOSSY: + return DataPacketKind::Lossy; + case proto::KIND_RELIABLE: // NOLINT(bugprone-branch-clone) + return DataPacketKind::Reliable; + default: + return DataPacketKind::Reliable; } } @@ -73,7 +73,7 @@ DisconnectReason toDisconnectReason(proto::DisconnectReason /*src*/) { // --------- basic helper conversions --------- -TranscriptionSegmentData fromProto(const proto::TranscriptionSegment &src) { +TranscriptionSegmentData fromProto(const proto::TranscriptionSegment& src) { TranscriptionSegmentData out; out.id = src.id(); out.text = src.text(); @@ -84,7 +84,7 @@ TranscriptionSegmentData fromProto(const proto::TranscriptionSegment &src) { return out; } -ChatMessageData fromProto(const proto::ChatMessage &src) { +ChatMessageData fromProto(const proto::ChatMessage& src) { ChatMessageData out; out.id = src.id(); out.timestamp = src.timestamp(); @@ -101,11 +101,11 @@ ChatMessageData fromProto(const proto::ChatMessage &src) { return out; } -UserPacketData fromProto(const proto::UserPacket &src) { +UserPacketData fromProto(const proto::UserPacket& src) { UserPacketData out; // TODO, double check following code is safe - const auto &buf = src.data().data(); - auto ptr = reinterpret_cast(buf.data_ptr()); + const auto& buf = src.data().data(); + auto ptr = reinterpret_cast(buf.data_ptr()); auto len = static_cast(buf.data_len()); out.data.assign(ptr, ptr + len); if (src.has_topic()) { @@ -114,7 +114,7 @@ UserPacketData fromProto(const proto::UserPacket &src) { return out; } -SipDtmfData fromProto(const proto::SipDTMF &src) { +SipDtmfData fromProto(const proto::SipDTMF& src) { SipDtmfData out; out.code = src.code(); if (src.has_digit()) { @@ -123,17 +123,15 @@ SipDtmfData fromProto(const proto::SipDTMF &src) { return out; } -RoomInfoData fromProto(const proto::RoomInfo &src) { +RoomInfoData fromProto(const proto::RoomInfo& src) { RoomInfoData out; if (src.has_sid()) { out.sid = src.sid(); } out.name = src.name(); out.metadata = src.metadata(); - out.lossy_dc_buffered_amount_low_threshold = - src.lossy_dc_buffered_amount_low_threshold(); - out.reliable_dc_buffered_amount_low_threshold = - src.reliable_dc_buffered_amount_low_threshold(); + out.lossy_dc_buffered_amount_low_threshold = src.lossy_dc_buffered_amount_low_threshold(); + out.reliable_dc_buffered_amount_low_threshold = src.reliable_dc_buffered_amount_low_threshold(); out.empty_timeout = src.empty_timeout(); out.departure_timeout = src.departure_timeout(); out.max_participants = src.max_participants(); @@ -144,14 +142,14 @@ RoomInfoData fromProto(const proto::RoomInfo &src) { return out; } -AttributeEntry fromProto(const proto::AttributesEntry &src) { +AttributeEntry fromProto(const proto::AttributesEntry& src) { AttributeEntry a; a.key = src.key(); a.value = src.value(); return a; } -DataStreamHeaderData fromProto(const proto::DataStream_Header &src) { +DataStreamHeaderData fromProto(const proto::DataStream_Header& src) { DataStreamHeaderData out; out.stream_id = src.stream_id(); out.timestamp = src.timestamp(); @@ -160,47 +158,46 @@ DataStreamHeaderData fromProto(const proto::DataStream_Header &src) { if (src.has_total_length()) { out.total_length = src.total_length(); } - for (const auto &kv : src.attributes()) { + for (const auto& kv : src.attributes()) { out.attributes.emplace(kv.first, kv.second); } // content_header oneof switch (src.content_header_case()) { - case proto::DataStream_Header::kTextHeader: { - out.content_type = DataStreamHeaderData::ContentType::Text; - const auto &t = src.text_header(); - out.operation_type = - static_cast(t.operation_type()); - if (t.has_version()) { - out.version = t.version(); + case proto::DataStream_Header::kTextHeader: { + out.content_type = DataStreamHeaderData::ContentType::Text; + const auto& t = src.text_header(); + out.operation_type = static_cast(t.operation_type()); + if (t.has_version()) { + out.version = t.version(); + } + if (t.has_reply_to_stream_id()) { + out.reply_to_stream_id = t.reply_to_stream_id(); + } + for (const auto& id : t.attached_stream_ids()) { + out.attached_stream_ids.push_back(id); + } + if (t.has_generated()) { + out.generated = t.generated(); + } + break; } - if (t.has_reply_to_stream_id()) { - out.reply_to_stream_id = t.reply_to_stream_id(); + case proto::DataStream_Header::kByteHeader: { + out.content_type = DataStreamHeaderData::ContentType::Byte; + const auto& b = src.byte_header(); + out.name = b.name(); + break; } - for (const auto &id : t.attached_stream_ids()) { - out.attached_stream_ids.push_back(id); - } - if (t.has_generated()) { - out.generated = t.generated(); - } - break; - } - case proto::DataStream_Header::kByteHeader: { - out.content_type = DataStreamHeaderData::ContentType::Byte; - const auto &b = src.byte_header(); - out.name = b.name(); - break; - } - case proto::DataStream_Header::CONTENT_HEADER_NOT_SET: - default: - out.content_type = DataStreamHeaderData::ContentType::None; - break; + case proto::DataStream_Header::CONTENT_HEADER_NOT_SET: + default: + out.content_type = DataStreamHeaderData::ContentType::None; + break; } return out; } -DataStreamChunkData fromProto(const proto::DataStream_Chunk &src) { +DataStreamChunkData fromProto(const proto::DataStream_Chunk& src) { DataStreamChunkData out; out.stream_id = src.stream_id(); out.chunk_index = src.chunk_index(); @@ -214,11 +211,11 @@ DataStreamChunkData fromProto(const proto::DataStream_Chunk &src) { return out; } -DataStreamTrailerData fromProto(const proto::DataStream_Trailer &src) { +DataStreamTrailerData fromProto(const proto::DataStream_Trailer& src) { DataStreamTrailerData out; out.stream_id = src.stream_id(); out.reason = src.reason(); - for (const auto &kv : src.attributes()) { + for (const auto& kv : src.attributes()) { out.attributes.emplace(kv.first, kv.second); } return out; @@ -226,7 +223,7 @@ DataStreamTrailerData fromProto(const proto::DataStream_Trailer &src) { // --------- event conversions --------- -ParticipantConnectedEvent fromProto(const proto::ParticipantConnected &src) { +ParticipantConnectedEvent fromProto(const proto::ParticipantConnected& src) { ParticipantConnectedEvent ev; // src.info() is OwnedParticipant; you can fill more fields once you inspect // it. For now, leave metadata/name/identity as TODO. @@ -234,48 +231,46 @@ ParticipantConnectedEvent fromProto(const proto::ParticipantConnected &src) { return ev; } -ParticipantDisconnectedEvent -fromProto(const proto::ParticipantDisconnected &src) { +ParticipantDisconnectedEvent fromProto(const proto::ParticipantDisconnected& src) { ParticipantDisconnectedEvent ev; ev.participant_identity = src.participant_identity(); ev.reason = toDisconnectReason(src.disconnect_reason()); return ev; } -LocalTrackPublishedEvent fromProto(const proto::LocalTrackPublished &src) { +LocalTrackPublishedEvent fromProto(const proto::LocalTrackPublished& src) { LocalTrackPublishedEvent ev; ev.track_sid = src.track_sid(); return ev; } -LocalTrackUnpublishedEvent fromProto(const proto::LocalTrackUnpublished &src) { +LocalTrackUnpublishedEvent fromProto(const proto::LocalTrackUnpublished& src) { LocalTrackUnpublishedEvent ev; ev.publication_sid = src.publication_sid(); return ev; } -LocalTrackSubscribedEvent fromProto(const proto::LocalTrackSubscribed &src) { +LocalTrackSubscribedEvent fromProto(const proto::LocalTrackSubscribed& src) { LocalTrackSubscribedEvent ev; ev.track_sid = src.track_sid(); return ev; } -TrackUnpublishedEvent fromProto(const proto::TrackUnpublished &src) { +TrackUnpublishedEvent fromProto(const proto::TrackUnpublished& src) { TrackUnpublishedEvent ev; ev.participant_identity = src.participant_identity(); ev.publication_sid = src.publication_sid(); return ev; } -TrackUnsubscribedEvent fromProto(const proto::TrackUnsubscribed &src) { +TrackUnsubscribedEvent fromProto(const proto::TrackUnsubscribed& src) { TrackUnsubscribedEvent ev; ev.participant_identity = src.participant_identity(); ev.track_sid = src.track_sid(); return ev; } -TrackSubscriptionFailedEvent -fromProto(const proto::TrackSubscriptionFailed &src) { +TrackSubscriptionFailedEvent fromProto(const proto::TrackSubscriptionFailed& src) { TrackSubscriptionFailedEvent ev; ev.participant_identity = src.participant_identity(); ev.track_sid = src.track_sid(); @@ -283,106 +278,101 @@ fromProto(const proto::TrackSubscriptionFailed &src) { return ev; } -TrackMutedEvent fromProto(const proto::TrackMuted &src) { +TrackMutedEvent fromProto(const proto::TrackMuted& src) { TrackMutedEvent ev; ev.participant_identity = src.participant_identity(); ev.track_sid = src.track_sid(); return ev; } -TrackUnmutedEvent fromProto(const proto::TrackUnmuted &src) { +TrackUnmutedEvent fromProto(const proto::TrackUnmuted& src) { TrackUnmutedEvent ev; ev.participant_identity = src.participant_identity(); ev.track_sid = src.track_sid(); return ev; } -ActiveSpeakersChangedEvent fromProto(const proto::ActiveSpeakersChanged &src) { +ActiveSpeakersChangedEvent fromProto(const proto::ActiveSpeakersChanged& src) { ActiveSpeakersChangedEvent ev; - for (const auto &id : src.participant_identities()) { + for (const auto& id : src.participant_identities()) { ev.participant_identities.push_back(id); } return ev; } -RoomMetadataChangedEvent fromProto(const proto::RoomMetadataChanged &src) { +RoomMetadataChangedEvent fromProto(const proto::RoomMetadataChanged& src) { RoomMetadataChangedEvent ev; ev.metadata = src.metadata(); return ev; } -RoomSidChangedEvent fromProto(const proto::RoomSidChanged &src) { +RoomSidChangedEvent fromProto(const proto::RoomSidChanged& src) { RoomSidChangedEvent ev; ev.sid = src.sid(); return ev; } -ParticipantMetadataChangedEvent -fromProto(const proto::ParticipantMetadataChanged &src) { +ParticipantMetadataChangedEvent fromProto(const proto::ParticipantMetadataChanged& src) { ParticipantMetadataChangedEvent ev; ev.participant_identity = src.participant_identity(); ev.metadata = src.metadata(); return ev; } -ParticipantNameChangedEvent -fromProto(const proto::ParticipantNameChanged &src) { +ParticipantNameChangedEvent fromProto(const proto::ParticipantNameChanged& src) { ParticipantNameChangedEvent ev; ev.participant_identity = src.participant_identity(); ev.name = src.name(); return ev; } -ParticipantAttributesChangedEvent -fromProto(const proto::ParticipantAttributesChanged &src) { +ParticipantAttributesChangedEvent fromProto(const proto::ParticipantAttributesChanged& src) { ParticipantAttributesChangedEvent ev; ev.participant_identity = src.participant_identity(); - for (const auto &a : src.attributes()) { + for (const auto& a : src.attributes()) { ev.attributes.push_back(fromProto(a)); } - for (const auto &a : src.changed_attributes()) { + for (const auto& a : src.changed_attributes()) { ev.changed_attributes.push_back(fromProto(a)); } return ev; } -ParticipantEncryptionStatusChangedEvent -fromProto(const proto::ParticipantEncryptionStatusChanged &src) { +ParticipantEncryptionStatusChangedEvent fromProto(const proto::ParticipantEncryptionStatusChanged& src) { ParticipantEncryptionStatusChangedEvent ev; ev.participant_identity = src.participant_identity(); ev.is_encrypted = src.is_encrypted(); return ev; } -ConnectionQualityChangedEvent -fromProto(const proto::ConnectionQualityChanged &src) { +ConnectionQualityChangedEvent fromProto(const proto::ConnectionQualityChanged& src) { ConnectionQualityChangedEvent ev; ev.participant_identity = src.participant_identity(); ev.quality = toConnectionQuality(src.quality()); return ev; } -DataPacketReceivedEvent fromProto(const proto::DataPacketReceived &src) { +DataPacketReceivedEvent fromProto(const proto::DataPacketReceived& src) { DataPacketReceivedEvent ev; ev.kind = toDataPacketKind(src.kind()); ev.participant_identity = src.participant_identity(); switch (src.value_case()) { - case proto::DataPacketReceived::kUser: - ev.user = fromProto(src.user()); - break; - case proto::DataPacketReceived::kSipDtmf: - ev.sip_dtmf = fromProto(src.sip_dtmf()); - break; - case proto::DataPacketReceived::VALUE_NOT_SET: - default: - break; + case proto::DataPacketReceived::kUser: + ev.user = fromProto(src.user()); + break; + case proto::DataPacketReceived::kSipDtmf: + ev.sip_dtmf = fromProto(src.sip_dtmf()); + break; + case proto::DataPacketReceived::VALUE_NOT_SET: + default: + break; } return ev; } -TranscriptionReceivedEvent fromProto(const proto::TranscriptionReceived &src) { +TranscriptionReceivedEvent fromProto(const proto::TranscriptionReceived& src) { TranscriptionReceivedEvent ev; if (src.has_participant_identity()) { ev.participant_identity = src.participant_identity(); @@ -390,70 +380,60 @@ TranscriptionReceivedEvent fromProto(const proto::TranscriptionReceived &src) { if (src.has_track_sid()) { ev.track_sid = src.track_sid(); } - for (const auto &seg : src.segments()) { + for (const auto& seg : src.segments()) { ev.segments.push_back(fromProto(seg)); } return ev; } -ConnectionStateChangedEvent -fromProto(const proto::ConnectionStateChanged &src) { +ConnectionStateChangedEvent fromProto(const proto::ConnectionStateChanged& src) { ConnectionStateChangedEvent ev; ev.state = toConnectionState(src.state()); return ev; } -DisconnectedEvent fromProto(const proto::Disconnected &src) { +DisconnectedEvent fromProto(const proto::Disconnected& src) { DisconnectedEvent ev; ev.reason = toDisconnectReason(src.reason()); return ev; } -ReconnectingEvent fromProto(const proto::Reconnecting & /*src*/) { - return ReconnectingEvent{}; -} +ReconnectingEvent fromProto(const proto::Reconnecting& /*src*/) { return ReconnectingEvent{}; } -ReconnectedEvent fromProto(const proto::Reconnected & /*src*/) { - return ReconnectedEvent{}; -} +ReconnectedEvent fromProto(const proto::Reconnected& /*src*/) { return ReconnectedEvent{}; } -RoomEosEvent fromProto(const proto::RoomEOS & /*src*/) { - return RoomEosEvent{}; -} +RoomEosEvent fromProto(const proto::RoomEOS& /*src*/) { return RoomEosEvent{}; } -DataStreamHeaderReceivedEvent -fromProto(const proto::DataStreamHeaderReceived &src) { +DataStreamHeaderReceivedEvent fromProto(const proto::DataStreamHeaderReceived& src) { DataStreamHeaderReceivedEvent ev; ev.participant_identity = src.participant_identity(); ev.header = fromProto(src.header()); return ev; } -DataStreamChunkReceivedEvent -fromProto(const proto::DataStreamChunkReceived &src) { +DataStreamChunkReceivedEvent fromProto(const proto::DataStreamChunkReceived& src) { DataStreamChunkReceivedEvent ev; ev.participant_identity = src.participant_identity(); ev.chunk = fromProto(src.chunk()); return ev; } -DataStreamTrailerReceivedEvent -fromProto(const proto::DataStreamTrailerReceived &src) { +DataStreamTrailerReceivedEvent fromProto(const proto::DataStreamTrailerReceived& src) { DataStreamTrailerReceivedEvent ev; ev.participant_identity = src.participant_identity(); ev.trailer = fromProto(src.trailer()); return ev; } -DataChannelBufferedAmountLowThresholdChangedEvent -fromProto(const proto::DataChannelBufferedAmountLowThresholdChanged &src) { +DataChannelBufferedAmountLowThresholdChangedEvent fromProto( + const proto::DataChannelBufferedAmountLowThresholdChanged& src) { DataChannelBufferedAmountLowThresholdChangedEvent ev; ev.kind = toDataPacketKind(src.kind()); ev.threshold = src.threshold(); return ev; } -ByteStreamOpenedEvent fromProto(const proto::ByteStreamOpened &src) { +ByteStreamOpenedEvent fromProto(const proto::ByteStreamOpened& src) { ByteStreamOpenedEvent ev; // TODO: map reader handle once OwnedByteStreamReader is known // ev.reader_handle = src.reader().handle().id(); @@ -461,7 +441,7 @@ ByteStreamOpenedEvent fromProto(const proto::ByteStreamOpened &src) { return ev; } -TextStreamOpenedEvent fromProto(const proto::TextStreamOpened &src) { +TextStreamOpenedEvent fromProto(const proto::TextStreamOpened& src) { TextStreamOpenedEvent ev; // TODO: map reader handle once OwnedTextStreamReader is known // ev.reader_handle = src.reader().handle().id(); @@ -469,36 +449,36 @@ TextStreamOpenedEvent fromProto(const proto::TextStreamOpened &src) { return ev; } -RoomUpdatedEvent roomUpdatedFromProto(const proto::RoomInfo &src) { +RoomUpdatedEvent roomUpdatedFromProto(const proto::RoomInfo& src) { RoomUpdatedEvent ev; ev.info = fromProto(src); return ev; } -RoomMovedEvent roomMovedFromProto(const proto::RoomInfo &src) { +RoomMovedEvent roomMovedFromProto(const proto::RoomInfo& src) { RoomMovedEvent ev; ev.info = fromProto(src); return ev; } -ParticipantsUpdatedEvent fromProto(const proto::ParticipantsUpdated &src) { +ParticipantsUpdatedEvent fromProto(const proto::ParticipantsUpdated& src) { ParticipantsUpdatedEvent ev; // We only know that it has ParticipantInfo participants = 1; // TODO: fill real identities once you inspect proto::ParticipantInfo - for (const auto &p : src.participants()) { + for (const auto& p : src.participants()) { ev.participant_identities.push_back(p.identity()); } return ev; } -E2eeStateChangedEvent fromProto(const proto::E2eeStateChanged &src) { +E2eeStateChangedEvent fromProto(const proto::E2eeStateChanged& src) { E2eeStateChangedEvent ev; ev.participant_identity = src.participant_identity(); ev.state = toEncryptionState(src.state()); return ev; } -ChatMessageReceivedEvent fromProto(const proto::ChatMessageReceived &src) { +ChatMessageReceivedEvent fromProto(const proto::ChatMessageReceived& src) { ChatMessageReceivedEvent ev; ev.message = fromProto(src.message()); ev.participant_identity = src.participant_identity(); diff --git a/src/room_proto_converter.cpp b/src/room_proto_converter.cpp index 4de4c822..048b855c 100644 --- a/src/room_proto_converter.cpp +++ b/src/room_proto_converter.cpp @@ -24,8 +24,7 @@ namespace livekit { namespace { -std::vector -toProto(const PacketTrailerFeatures &features) { +std::vector toProto(const PacketTrailerFeatures& features) { std::vector out; out.reserve(2); if (features.user_timestamp) { @@ -37,19 +36,18 @@ toProto(const PacketTrailerFeatures &features) { return out; } -PacketTrailerFeatures -fromProto(const google::protobuf::RepeatedField &features) { +PacketTrailerFeatures fromProto(const google::protobuf::RepeatedField& features) { PacketTrailerFeatures out{}; for (const int feature : features) { switch (feature) { - case proto::PacketTrailerFeature::PTF_USER_TIMESTAMP: - out.user_timestamp = true; - break; - case proto::PacketTrailerFeature::PTF_FRAME_ID: - out.frame_id = true; - break; - default: - break; + case proto::PacketTrailerFeature::PTF_USER_TIMESTAMP: + out.user_timestamp = true; + break; + case proto::PacketTrailerFeature::PTF_FRAME_ID: + out.frame_id = true; + break; + default: + break; } } return out; @@ -61,40 +59,40 @@ fromProto(const google::protobuf::RepeatedField &features) { ConnectionQuality toConnectionQuality(proto::ConnectionQuality in) { switch (in) { - case proto::QUALITY_POOR: - return ConnectionQuality::Poor; - case proto::QUALITY_GOOD: - return ConnectionQuality::Good; - case proto::QUALITY_EXCELLENT: - return ConnectionQuality::Excellent; - case proto::QUALITY_LOST: - return ConnectionQuality::Lost; - default: - return ConnectionQuality::Good; + case proto::QUALITY_POOR: + return ConnectionQuality::Poor; + case proto::QUALITY_GOOD: + return ConnectionQuality::Good; + case proto::QUALITY_EXCELLENT: + return ConnectionQuality::Excellent; + case proto::QUALITY_LOST: + return ConnectionQuality::Lost; + default: + return ConnectionQuality::Good; } } ConnectionState toConnectionState(proto::ConnectionState in) { switch (in) { - case proto::CONN_DISCONNECTED: - return ConnectionState::Disconnected; - case proto::CONN_CONNECTED: - return ConnectionState::Connected; - case proto::CONN_RECONNECTING: - return ConnectionState::Reconnecting; - default: - return ConnectionState::Disconnected; + case proto::CONN_DISCONNECTED: + return ConnectionState::Disconnected; + case proto::CONN_CONNECTED: + return ConnectionState::Connected; + case proto::CONN_RECONNECTING: + return ConnectionState::Reconnecting; + default: + return ConnectionState::Disconnected; } } DataPacketKind toDataPacketKind(proto::DataPacketKind in) { switch (in) { - case proto::KIND_LOSSY: - return DataPacketKind::Lossy; - case proto::KIND_RELIABLE: // NOLINT(bugprone-branch-clone) - return DataPacketKind::Reliable; - default: - return DataPacketKind::Reliable; + case proto::KIND_LOSSY: + return DataPacketKind::Lossy; + case proto::KIND_RELIABLE: // NOLINT(bugprone-branch-clone) + return DataPacketKind::Reliable; + default: + return DataPacketKind::Reliable; } } @@ -105,11 +103,12 @@ DisconnectReason toDisconnectReason(proto::DisconnectReason /*in*/) { // --------- basic helper conversions --------- -UserPacketData fromProto(const proto::UserPacket &in) { +UserPacketData fromProto(const proto::UserPacket& in) { UserPacketData out; // TODO, double check following code is safe - const auto &buf = in.data().data(); - const auto *ptr = reinterpret_cast(buf.data_ptr()); // NOLINT(performance-no-int-to-ptr) + const auto& buf = in.data().data(); + // NOLINTNEXTLINE(performance-no-int-to-ptr) + const auto* ptr = reinterpret_cast(buf.data_ptr()); auto len = static_cast(buf.data_len()); out.data.assign(ptr, ptr + len); if (in.has_topic()) { @@ -118,7 +117,7 @@ UserPacketData fromProto(const proto::UserPacket &in) { return out; } -SipDtmfData fromProto(const proto::SipDTMF &in) { +SipDtmfData fromProto(const proto::SipDTMF& in) { SipDtmfData out; out.code = in.code(); if (in.has_digit()) { @@ -127,17 +126,15 @@ SipDtmfData fromProto(const proto::SipDTMF &in) { return out; } -RoomInfoData fromProto(const proto::RoomInfo &in) { +RoomInfoData fromProto(const proto::RoomInfo& in) { RoomInfoData out; if (in.has_sid()) { out.sid = in.sid(); } out.name = in.name(); out.metadata = in.metadata(); - out.lossy_dc_buffered_amount_low_threshold = - in.lossy_dc_buffered_amount_low_threshold(); - out.reliable_dc_buffered_amount_low_threshold = - in.reliable_dc_buffered_amount_low_threshold(); + out.lossy_dc_buffered_amount_low_threshold = in.lossy_dc_buffered_amount_low_threshold(); + out.reliable_dc_buffered_amount_low_threshold = in.reliable_dc_buffered_amount_low_threshold(); out.empty_timeout = in.empty_timeout(); out.departure_timeout = in.departure_timeout(); out.max_participants = in.max_participants(); @@ -148,14 +145,14 @@ RoomInfoData fromProto(const proto::RoomInfo &in) { return out; } -AttributeEntry fromProto(const proto::AttributesEntry &in) { +AttributeEntry fromProto(const proto::AttributesEntry& in) { AttributeEntry a; a.key = in.key(); a.value = in.value(); return a; } -DataStreamHeaderData fromProto(const proto::DataStream_Header &in) { +DataStreamHeaderData fromProto(const proto::DataStream_Header& in) { DataStreamHeaderData out; out.stream_id = in.stream_id(); out.timestamp = in.timestamp(); @@ -164,47 +161,46 @@ DataStreamHeaderData fromProto(const proto::DataStream_Header &in) { if (in.has_total_length()) { out.total_length = in.total_length(); } - for (const auto &kv : in.attributes()) { + for (const auto& kv : in.attributes()) { out.attributes.emplace(kv.first, kv.second); } // content_header oneof switch (in.content_header_case()) { - case proto::DataStream_Header::kTextHeader: { - out.content_type = DataStreamHeaderData::ContentType::Text; - const auto &t = in.text_header(); - out.operation_type = - static_cast(t.operation_type()); - if (t.has_version()) { - out.version = t.version(); - } - if (t.has_reply_to_stream_id()) { - out.reply_to_stream_id = t.reply_to_stream_id(); - } - for (const auto &id : t.attached_stream_ids()) { - out.attached_stream_ids.push_back(id); + case proto::DataStream_Header::kTextHeader: { + out.content_type = DataStreamHeaderData::ContentType::Text; + const auto& t = in.text_header(); + out.operation_type = static_cast(t.operation_type()); + if (t.has_version()) { + out.version = t.version(); + } + if (t.has_reply_to_stream_id()) { + out.reply_to_stream_id = t.reply_to_stream_id(); + } + for (const auto& id : t.attached_stream_ids()) { + out.attached_stream_ids.push_back(id); + } + if (t.has_generated()) { + out.generated = t.generated(); + } + break; } - if (t.has_generated()) { - out.generated = t.generated(); + case proto::DataStream_Header::kByteHeader: { + out.content_type = DataStreamHeaderData::ContentType::Byte; + const auto& b = in.byte_header(); + out.name = b.name(); + break; } - break; - } - case proto::DataStream_Header::kByteHeader: { - out.content_type = DataStreamHeaderData::ContentType::Byte; - const auto &b = in.byte_header(); - out.name = b.name(); - break; - } - case proto::DataStream_Header::CONTENT_HEADER_NOT_SET: - default: - out.content_type = DataStreamHeaderData::ContentType::None; - break; + case proto::DataStream_Header::CONTENT_HEADER_NOT_SET: + default: + out.content_type = DataStreamHeaderData::ContentType::None; + break; } return out; } -DataStreamChunkData fromProto(const proto::DataStream_Chunk &in) { +DataStreamChunkData fromProto(const proto::DataStream_Chunk& in) { DataStreamChunkData out; out.stream_id = in.stream_id(); out.chunk_index = in.chunk_index(); @@ -218,11 +214,11 @@ DataStreamChunkData fromProto(const proto::DataStream_Chunk &in) { return out; } -DataStreamTrailerData fromProto(const proto::DataStream_Trailer &in) { +DataStreamTrailerData fromProto(const proto::DataStream_Trailer& in) { DataStreamTrailerData out; out.stream_id = in.stream_id(); out.reason = in.reason(); - for (const auto &kv : in.attributes()) { + for (const auto& kv : in.attributes()) { out.attributes.emplace(kv.first, kv.second); } return out; @@ -230,67 +226,60 @@ DataStreamTrailerData fromProto(const proto::DataStream_Trailer &in) { // --------- event conversions --------- -RoomSidChangedEvent fromProto(const proto::RoomSidChanged &in) { +RoomSidChangedEvent fromProto(const proto::RoomSidChanged& in) { RoomSidChangedEvent ev; ev.sid = in.sid(); return ev; } -ConnectionStateChangedEvent fromProto(const proto::ConnectionStateChanged &in) { +ConnectionStateChangedEvent fromProto(const proto::ConnectionStateChanged& in) { ConnectionStateChangedEvent ev; ev.state = toConnectionState(in.state()); return ev; } -DisconnectedEvent fromProto(const proto::Disconnected &in) { +DisconnectedEvent fromProto(const proto::Disconnected& in) { DisconnectedEvent ev; ev.reason = toDisconnectReason(in.reason()); return ev; } -ReconnectingEvent fromProto(const proto::Reconnecting & /*in*/) { - return ReconnectingEvent{}; -} +ReconnectingEvent fromProto(const proto::Reconnecting& /*in*/) { return ReconnectingEvent{}; } -ReconnectedEvent fromProto(const proto::Reconnected & /*in*/) { - return ReconnectedEvent{}; -} +ReconnectedEvent fromProto(const proto::Reconnected& /*in*/) { return ReconnectedEvent{}; } -RoomEosEvent fromProto(const proto::RoomEOS & /*in*/) { return RoomEosEvent{}; } +RoomEosEvent fromProto(const proto::RoomEOS& /*in*/) { return RoomEosEvent{}; } -DataStreamHeaderReceivedEvent -fromProto(const proto::DataStreamHeaderReceived &in) { +DataStreamHeaderReceivedEvent fromProto(const proto::DataStreamHeaderReceived& in) { DataStreamHeaderReceivedEvent ev; ev.participant_identity = in.participant_identity(); ev.header = fromProto(in.header()); return ev; } -DataStreamChunkReceivedEvent -fromProto(const proto::DataStreamChunkReceived &in) { +DataStreamChunkReceivedEvent fromProto(const proto::DataStreamChunkReceived& in) { DataStreamChunkReceivedEvent ev; ev.participant_identity = in.participant_identity(); ev.chunk = fromProto(in.chunk()); return ev; } -DataStreamTrailerReceivedEvent -fromProto(const proto::DataStreamTrailerReceived &in) { +DataStreamTrailerReceivedEvent fromProto(const proto::DataStreamTrailerReceived& in) { DataStreamTrailerReceivedEvent ev; ev.participant_identity = in.participant_identity(); ev.trailer = fromProto(in.trailer()); return ev; } -DataChannelBufferedAmountLowThresholdChangedEvent -fromProto(const proto::DataChannelBufferedAmountLowThresholdChanged &in) { +DataChannelBufferedAmountLowThresholdChangedEvent fromProto( + const proto::DataChannelBufferedAmountLowThresholdChanged& in) { DataChannelBufferedAmountLowThresholdChangedEvent ev; ev.kind = toDataPacketKind(in.kind()); ev.threshold = in.threshold(); return ev; } -ByteStreamOpenedEvent fromProto(const proto::ByteStreamOpened &in) { +ByteStreamOpenedEvent fromProto(const proto::ByteStreamOpened& in) { ByteStreamOpenedEvent ev; // TODO: map reader handle once OwnedByteStreamReader is known // ev.reader_handle = in.reader().handle().id(); @@ -298,7 +287,7 @@ ByteStreamOpenedEvent fromProto(const proto::ByteStreamOpened &in) { return ev; } -TextStreamOpenedEvent fromProto(const proto::TextStreamOpened &in) { +TextStreamOpenedEvent fromProto(const proto::TextStreamOpened& in) { TextStreamOpenedEvent ev; // TODO: map reader handle once OwnedTextStreamReader is known // ev.reader_handle = in.reader().handle().id(); @@ -306,13 +295,13 @@ TextStreamOpenedEvent fromProto(const proto::TextStreamOpened &in) { return ev; } -RoomUpdatedEvent roomUpdatedFromProto(const proto::RoomInfo &in) { +RoomUpdatedEvent roomUpdatedFromProto(const proto::RoomInfo& in) { RoomUpdatedEvent ev; ev.info = fromProto(in); return ev; } -RoomMovedEvent roomMovedFromProto(const proto::RoomInfo &in) { +RoomMovedEvent roomMovedFromProto(const proto::RoomInfo& in) { RoomMovedEvent ev; ev.info = fromProto(in); return ev; @@ -320,33 +309,33 @@ RoomMovedEvent roomMovedFromProto(const proto::RoomInfo &in) { // ---------------- Room Options ---------------- -proto::AudioEncoding toProto(const AudioEncodingOptions &in) { +proto::AudioEncoding toProto(const AudioEncodingOptions& in) { proto::AudioEncoding msg; msg.set_max_bitrate(in.max_bitrate); return msg; } -AudioEncodingOptions fromProto(const proto::AudioEncoding &in) { +AudioEncodingOptions fromProto(const proto::AudioEncoding& in) { AudioEncodingOptions out; out.max_bitrate = in.max_bitrate(); return out; } -proto::VideoEncoding toProto(const VideoEncodingOptions &in) { +proto::VideoEncoding toProto(const VideoEncodingOptions& in) { proto::VideoEncoding msg; msg.set_max_bitrate(in.max_bitrate); msg.set_max_framerate(in.max_framerate); return msg; } -VideoEncodingOptions fromProto(const proto::VideoEncoding &in) { +VideoEncodingOptions fromProto(const proto::VideoEncoding& in) { VideoEncodingOptions out; out.max_bitrate = in.max_bitrate(); out.max_framerate = in.max_framerate(); return out; } -proto::TrackPublishOptions toProto(const TrackPublishOptions &in) { +proto::TrackPublishOptions toProto(const TrackPublishOptions& in) { proto::TrackPublishOptions msg; if (in.video_encoding) { msg.mutable_video_encoding()->CopyFrom(toProto(*in.video_encoding)); @@ -375,14 +364,13 @@ proto::TrackPublishOptions toProto(const TrackPublishOptions &in) { if (in.preconnect_buffer) { msg.set_preconnect_buffer(*in.preconnect_buffer); } - for (const proto::PacketTrailerFeature feature : - toProto(in.packet_trailer_features)) { + for (const proto::PacketTrailerFeature feature : toProto(in.packet_trailer_features)) { msg.add_packet_trailer_features(feature); } return msg; } -TrackPublishOptions fromProto(const proto::TrackPublishOptions &in) { +TrackPublishOptions fromProto(const proto::TrackPublishOptions& in) { TrackPublishOptions out; if (in.has_video_encoding()) { out.video_encoding = fromProto(in.video_encoding()); @@ -415,18 +403,18 @@ TrackPublishOptions fromProto(const proto::TrackPublishOptions &in) { return out; } -UserDataPacketEvent userDataPacketFromProto(const proto::DataPacketReceived &in, - RemoteParticipant *participant) { +UserDataPacketEvent userDataPacketFromProto(const proto::DataPacketReceived& in, RemoteParticipant* participant) { UserDataPacketEvent ev; ev.kind = static_cast(in.kind()); ev.participant = participant; ev.topic = in.user().topic(); // Copy bytes - const auto &owned = in.user().data(); - const auto &info = owned.data(); + const auto& owned = in.user().data(); + const auto& info = owned.data(); if (info.data_ptr() != 0 && info.data_len() > 0) { - const auto *ptr = reinterpret_cast(info.data_ptr()); // NOLINT(performance-no-int-to-ptr) + // NOLINTNEXTLINE(performance-no-int-to-ptr) + const auto* ptr = reinterpret_cast(info.data_ptr()); auto len = static_cast(info.data_len()); ev.data.assign(ptr, ptr + len); } else { @@ -436,8 +424,7 @@ UserDataPacketEvent userDataPacketFromProto(const proto::DataPacketReceived &in, return ev; } -SipDtmfReceivedEvent sipDtmfFromProto(const proto::DataPacketReceived &in, - RemoteParticipant *participant) { +SipDtmfReceivedEvent sipDtmfFromProto(const proto::DataPacketReceived& in, RemoteParticipant* participant) { SipDtmfReceivedEvent ev; ev.participant = participant; ev.code = static_cast(in.sip_dtmf().code()); @@ -445,16 +432,15 @@ SipDtmfReceivedEvent sipDtmfFromProto(const proto::DataPacketReceived &in, return ev; } -std::map -toAttrMap(const proto::DataStream::Trailer &trailer) { +std::map toAttrMap(const proto::DataStream::Trailer& trailer) { std::map out; - for (const auto &kv : trailer.attributes()) { + for (const auto& kv : trailer.attributes()) { out.emplace(kv.first, kv.second); } return out; } -TextStreamInfo makeTextInfo(const proto::DataStream::Header &header) { +TextStreamInfo makeTextInfo(const proto::DataStream::Header& header) { TextStreamInfo info; info.stream_id = header.stream_id(); info.mime_type = header.mime_type(); @@ -465,18 +451,18 @@ TextStreamInfo makeTextInfo(const proto::DataStream::Header &header) { info.size = static_cast(header.total_length()); } - for (const auto &kv : header.attributes()) { + for (const auto& kv : header.attributes()) { info.attributes.emplace(kv.first, kv.second); } - for (const auto &id : header.text_header().attached_stream_ids()) { + for (const auto& id : header.text_header().attached_stream_ids()) { info.attachments.push_back(id); } return info; } -ByteStreamInfo makeByteInfo(const proto::DataStream::Header &header) { +ByteStreamInfo makeByteInfo(const proto::DataStream::Header& header) { ByteStreamInfo info; info.stream_id = header.stream_id(); info.mime_type = header.mime_type(); @@ -487,7 +473,7 @@ ByteStreamInfo makeByteInfo(const proto::DataStream::Header &header) { info.size = static_cast(header.total_length()); } - for (const auto &kv : header.attributes()) { + for (const auto& kv : header.attributes()) { info.attributes.emplace(kv.first, kv.second); } diff --git a/src/room_proto_converter.h b/src/room_proto_converter.h index 23f8b0e5..8df7c795 100644 --- a/src/room_proto_converter.h +++ b/src/room_proto_converter.h @@ -16,11 +16,11 @@ #pragma once +#include + #include "livekit/room_event_types.h" #include "room.pb.h" -#include - namespace livekit { enum class RpcErrorCode; @@ -35,64 +35,57 @@ ConnectionState toConnectionState(proto::ConnectionState in); DataPacketKind toDataPacketKind(proto::DataPacketKind in); DisconnectReason toDisconnectReason(proto::DisconnectReason in); -UserPacketData fromProto(const proto::UserPacket &in); -SipDtmfData fromProto(const proto::SipDTMF &in); -RoomInfoData fromProto(const proto::RoomInfo &in); +UserPacketData fromProto(const proto::UserPacket& in); +SipDtmfData fromProto(const proto::SipDTMF& in); +RoomInfoData fromProto(const proto::RoomInfo& in); -DataStreamHeaderData fromProto(const proto::DataStream_Header &in); -DataStreamChunkData fromProto(const proto::DataStream_Chunk &in); -DataStreamTrailerData fromProto(const proto::DataStream_Trailer &in); +DataStreamHeaderData fromProto(const proto::DataStream_Header& in); +DataStreamChunkData fromProto(const proto::DataStream_Chunk& in); +DataStreamTrailerData fromProto(const proto::DataStream_Trailer& in); // --------- event conversions (RoomEvent.oneof message) --------- -RoomSidChangedEvent fromProto(const proto::RoomSidChanged &in); +RoomSidChangedEvent fromProto(const proto::RoomSidChanged& in); -ConnectionStateChangedEvent fromProto(const proto::ConnectionStateChanged &in); -DisconnectedEvent fromProto(const proto::Disconnected &in); -ReconnectingEvent fromProto(const proto::Reconnecting &in); -ReconnectedEvent fromProto(const proto::Reconnected &in); -RoomEosEvent fromProto(const proto::RoomEOS &in); +ConnectionStateChangedEvent fromProto(const proto::ConnectionStateChanged& in); +DisconnectedEvent fromProto(const proto::Disconnected& in); +ReconnectingEvent fromProto(const proto::Reconnecting& in); +ReconnectedEvent fromProto(const proto::Reconnected& in); +RoomEosEvent fromProto(const proto::RoomEOS& in); -DataStreamHeaderReceivedEvent -fromProto(const proto::DataStreamHeaderReceived &in); -DataStreamChunkReceivedEvent -fromProto(const proto::DataStreamChunkReceived &in); -DataStreamTrailerReceivedEvent -fromProto(const proto::DataStreamTrailerReceived &in); +DataStreamHeaderReceivedEvent fromProto(const proto::DataStreamHeaderReceived& in); +DataStreamChunkReceivedEvent fromProto(const proto::DataStreamChunkReceived& in); +DataStreamTrailerReceivedEvent fromProto(const proto::DataStreamTrailerReceived& in); -DataChannelBufferedAmountLowThresholdChangedEvent -fromProto(const proto::DataChannelBufferedAmountLowThresholdChanged &in); +DataChannelBufferedAmountLowThresholdChangedEvent fromProto( + const proto::DataChannelBufferedAmountLowThresholdChanged& in); -ByteStreamOpenedEvent fromProto(const proto::ByteStreamOpened &in); -TextStreamOpenedEvent fromProto(const proto::TextStreamOpened &in); +ByteStreamOpenedEvent fromProto(const proto::ByteStreamOpened& in); +TextStreamOpenedEvent fromProto(const proto::TextStreamOpened& in); -RoomUpdatedEvent -roomUpdatedFromProto(const proto::RoomInfo &in); // room_updated -RoomMovedEvent roomMovedFromProto(const proto::RoomInfo &in); // moved +RoomUpdatedEvent roomUpdatedFromProto(const proto::RoomInfo& in); // room_updated +RoomMovedEvent roomMovedFromProto(const proto::RoomInfo& in); // moved // --------- room options conversions --------- -proto::AudioEncoding toProto(const AudioEncodingOptions &in); -AudioEncodingOptions fromProto(const proto::AudioEncoding &in); +proto::AudioEncoding toProto(const AudioEncodingOptions& in); +AudioEncodingOptions fromProto(const proto::AudioEncoding& in); -proto::VideoEncoding toProto(const VideoEncodingOptions &in); -VideoEncodingOptions fromProto(const proto::VideoEncoding &in); +proto::VideoEncoding toProto(const VideoEncodingOptions& in); +VideoEncodingOptions fromProto(const proto::VideoEncoding& in); -proto::TrackPublishOptions toProto(const TrackPublishOptions &in); -TrackPublishOptions fromProto(const proto::TrackPublishOptions &in); +proto::TrackPublishOptions toProto(const TrackPublishOptions& in); +TrackPublishOptions fromProto(const proto::TrackPublishOptions& in); // --------- room Data Packet conversions --------- -UserDataPacketEvent userDataPacketFromProto(const proto::DataPacketReceived &in, - RemoteParticipant *participant); +UserDataPacketEvent userDataPacketFromProto(const proto::DataPacketReceived& in, RemoteParticipant* participant); -SipDtmfReceivedEvent sipDtmfFromProto(const proto::DataPacketReceived &in, - RemoteParticipant *participant); +SipDtmfReceivedEvent sipDtmfFromProto(const proto::DataPacketReceived& in, RemoteParticipant* participant); // --------- room Data Stream conversions --------- -std::map -toAttrMap(const proto::DataStream::Trailer &trailer); -ByteStreamInfo makeByteInfo(const proto::DataStream::Header &header); -TextStreamInfo makeTextInfo(const proto::DataStream::Header &header); +std::map toAttrMap(const proto::DataStream::Trailer& trailer); +ByteStreamInfo makeByteInfo(const proto::DataStream::Header& header); +TextStreamInfo makeTextInfo(const proto::DataStream::Header& header); } // namespace livekit diff --git a/src/rpc_error.cpp b/src/rpc_error.cpp index 1e521cd5..82e7abdf 100644 --- a/src/rpc_error.cpp +++ b/src/rpc_error.cpp @@ -21,18 +21,16 @@ namespace livekit { RpcError::RpcError(std::uint32_t code, std::string message, std::string data) - : std::runtime_error(message), code_(code), message_(std::move(message)), - data_(std::move(data)) {} + : std::runtime_error(message), code_(code), message_(std::move(message)), data_(std::move(data)) {} RpcError::RpcError(ErrorCode code, std::string message, std::string data) - : RpcError(static_cast(code), std::move(message), - std::move(data)) {} + : RpcError(static_cast(code), std::move(message), std::move(data)) {} std::uint32_t RpcError::code() const noexcept { return code_; } -const std::string &RpcError::message() const noexcept { return message_; } +const std::string& RpcError::message() const noexcept { return message_; } -const std::string &RpcError::data() const noexcept { return data_; } +const std::string& RpcError::data() const noexcept { return data_; } proto::RpcError RpcError::toProto() const { proto::RpcError err; @@ -47,40 +45,40 @@ proto::RpcError RpcError::toProto() const { return err; } -RpcError RpcError::fromProto(const proto::RpcError &err) { +RpcError RpcError::fromProto(const proto::RpcError& err) { // proto::RpcError.data() will return empty string if unset, which is fine. return RpcError(err.code(), err.message(), err.data()); } -RpcError RpcError::builtIn(ErrorCode code, const std::string &data) { - const char *msg = defaultMessageFor(code); +RpcError RpcError::builtIn(ErrorCode code, const std::string& data) { + const char* msg = defaultMessageFor(code); return RpcError(code, msg ? std::string(msg) : std::string{}, data); } -const char *RpcError::defaultMessageFor(ErrorCode code) { +const char* RpcError::defaultMessageFor(ErrorCode code) { switch (code) { - case ErrorCode::APPLICATION_ERROR: - return "Application error in method handler"; - case ErrorCode::CONNECTION_TIMEOUT: - return "Connection timeout"; - case ErrorCode::RESPONSE_TIMEOUT: - return "Response timeout"; - case ErrorCode::RECIPIENT_DISCONNECTED: - return "Recipient disconnected"; - case ErrorCode::RESPONSE_PAYLOAD_TOO_LARGE: - return "Response payload too large"; - case ErrorCode::SEND_FAILED: - return "Failed to send"; - case ErrorCode::UNSUPPORTED_METHOD: - return "Method not supported at destination"; - case ErrorCode::RECIPIENT_NOT_FOUND: - return "Recipient not found"; - case ErrorCode::REQUEST_PAYLOAD_TOO_LARGE: - return "Request payload too large"; - case ErrorCode::UNSUPPORTED_SERVER: - return "RPC not supported by server"; - case ErrorCode::UNSUPPORTED_VERSION: - return "Unsupported RPC version"; + case ErrorCode::APPLICATION_ERROR: + return "Application error in method handler"; + case ErrorCode::CONNECTION_TIMEOUT: + return "Connection timeout"; + case ErrorCode::RESPONSE_TIMEOUT: + return "Response timeout"; + case ErrorCode::RECIPIENT_DISCONNECTED: + return "Recipient disconnected"; + case ErrorCode::RESPONSE_PAYLOAD_TOO_LARGE: + return "Response payload too large"; + case ErrorCode::SEND_FAILED: + return "Failed to send"; + case ErrorCode::UNSUPPORTED_METHOD: + return "Method not supported at destination"; + case ErrorCode::RECIPIENT_NOT_FOUND: + return "Recipient not found"; + case ErrorCode::REQUEST_PAYLOAD_TOO_LARGE: + return "Request payload too large"; + case ErrorCode::UNSUPPORTED_SERVER: + return "RPC not supported by server"; + case ErrorCode::UNSUPPORTED_VERSION: + return "Unsupported RPC version"; } // Should be unreachable if all enum values are covered. diff --git a/src/stats.cpp b/src/stats.cpp index 4d3b762a..fa31f113 100644 --- a/src/stats.cpp +++ b/src/stats.cpp @@ -27,161 +27,160 @@ namespace { DataChannelState fromProto(livekit::proto::DataChannelState s) { using P = livekit::proto::DataChannelState; switch (s) { - case P::DC_CONNECTING: - return DataChannelState::Connecting; - case P::DC_OPEN: - return DataChannelState::Open; - case P::DC_CLOSING: - return DataChannelState::Closing; - case P::DC_CLOSED: - return DataChannelState::Closed; - default: - return DataChannelState::Unknown; + case P::DC_CONNECTING: + return DataChannelState::Connecting; + case P::DC_OPEN: + return DataChannelState::Open; + case P::DC_CLOSING: + return DataChannelState::Closing; + case P::DC_CLOSED: + return DataChannelState::Closed; + default: + return DataChannelState::Unknown; } } QualityLimitationReason fromProto(livekit::proto::QualityLimitationReason r) { using P = livekit::proto::QualityLimitationReason; switch (r) { - case P::LIMITATION_NONE: - return QualityLimitationReason::None; - case P::LIMITATION_CPU: - return QualityLimitationReason::Cpu; - case P::LIMITATION_BANDWIDTH: - return QualityLimitationReason::Bandwidth; - case P::LIMITATION_OTHER: // NOLINT(bugprone-branch-clone) - return QualityLimitationReason::Other; - default: - return QualityLimitationReason::Other; + case P::LIMITATION_NONE: + return QualityLimitationReason::None; + case P::LIMITATION_CPU: + return QualityLimitationReason::Cpu; + case P::LIMITATION_BANDWIDTH: + return QualityLimitationReason::Bandwidth; + case P::LIMITATION_OTHER: // NOLINT(bugprone-branch-clone) + return QualityLimitationReason::Other; + default: + return QualityLimitationReason::Other; } } IceRole fromProto(livekit::proto::IceRole r) { using P = livekit::proto::IceRole; switch (r) { - case P::ICE_CONTROLLING: - return IceRole::Controlling; - case P::ICE_CONTROLLED: - return IceRole::Controlled; - case P::ICE_UNKNOWN: - default: - return IceRole::Unknown; + case P::ICE_CONTROLLING: + return IceRole::Controlling; + case P::ICE_CONTROLLED: + return IceRole::Controlled; + case P::ICE_UNKNOWN: + default: + return IceRole::Unknown; } } DtlsTransportState fromProto(livekit::proto::DtlsTransportState s) { using P = livekit::proto::DtlsTransportState; switch (s) { - case P::DTLS_TRANSPORT_NEW: - return DtlsTransportState::New; - case P::DTLS_TRANSPORT_CONNECTING: - return DtlsTransportState::Connecting; - case P::DTLS_TRANSPORT_CONNECTED: - return DtlsTransportState::Connected; - case P::DTLS_TRANSPORT_CLOSED: - return DtlsTransportState::Closed; - case P::DTLS_TRANSPORT_FAILED: - return DtlsTransportState::Failed; - default: - return DtlsTransportState::Unknown; + case P::DTLS_TRANSPORT_NEW: + return DtlsTransportState::New; + case P::DTLS_TRANSPORT_CONNECTING: + return DtlsTransportState::Connecting; + case P::DTLS_TRANSPORT_CONNECTED: + return DtlsTransportState::Connected; + case P::DTLS_TRANSPORT_CLOSED: + return DtlsTransportState::Closed; + case P::DTLS_TRANSPORT_FAILED: + return DtlsTransportState::Failed; + default: + return DtlsTransportState::Unknown; } } IceTransportState fromProto(livekit::proto::IceTransportState s) { using P = livekit::proto::IceTransportState; switch (s) { - case P::ICE_TRANSPORT_NEW: - return IceTransportState::New; - case P::ICE_TRANSPORT_CHECKING: - return IceTransportState::Checking; - case P::ICE_TRANSPORT_CONNECTED: - return IceTransportState::Connected; - case P::ICE_TRANSPORT_COMPLETED: - return IceTransportState::Completed; - case P::ICE_TRANSPORT_DISCONNECTED: - return IceTransportState::Disconnected; - case P::ICE_TRANSPORT_FAILED: - return IceTransportState::Failed; - case P::ICE_TRANSPORT_CLOSED: - return IceTransportState::Closed; - default: - return IceTransportState::Unknown; + case P::ICE_TRANSPORT_NEW: + return IceTransportState::New; + case P::ICE_TRANSPORT_CHECKING: + return IceTransportState::Checking; + case P::ICE_TRANSPORT_CONNECTED: + return IceTransportState::Connected; + case P::ICE_TRANSPORT_COMPLETED: + return IceTransportState::Completed; + case P::ICE_TRANSPORT_DISCONNECTED: + return IceTransportState::Disconnected; + case P::ICE_TRANSPORT_FAILED: + return IceTransportState::Failed; + case P::ICE_TRANSPORT_CLOSED: + return IceTransportState::Closed; + default: + return IceTransportState::Unknown; } } DtlsRole fromProto(livekit::proto::DtlsRole r) { using P = livekit::proto::DtlsRole; switch (r) { - case P::DTLS_CLIENT: - return DtlsRole::Client; - case P::DTLS_SERVER: - return DtlsRole::Server; - case P::DTLS_UNKNOWN: - default: - return DtlsRole::Unknown; + case P::DTLS_CLIENT: + return DtlsRole::Client; + case P::DTLS_SERVER: + return DtlsRole::Server; + case P::DTLS_UNKNOWN: + default: + return DtlsRole::Unknown; } } IceCandidatePairState fromProto(livekit::proto::IceCandidatePairState s) { using P = livekit::proto::IceCandidatePairState; switch (s) { - case P::PAIR_FROZEN: - return IceCandidatePairState::Frozen; - case P::PAIR_WAITING: - return IceCandidatePairState::Waiting; - case P::PAIR_IN_PROGRESS: - return IceCandidatePairState::InProgress; - case P::PAIR_FAILED: - return IceCandidatePairState::Failed; - case P::PAIR_SUCCEEDED: - return IceCandidatePairState::Succeeded; - default: - return IceCandidatePairState::Unknown; + case P::PAIR_FROZEN: + return IceCandidatePairState::Frozen; + case P::PAIR_WAITING: + return IceCandidatePairState::Waiting; + case P::PAIR_IN_PROGRESS: + return IceCandidatePairState::InProgress; + case P::PAIR_FAILED: + return IceCandidatePairState::Failed; + case P::PAIR_SUCCEEDED: + return IceCandidatePairState::Succeeded; + default: + return IceCandidatePairState::Unknown; } } IceCandidateType fromProto(livekit::proto::IceCandidateType t) { using P = livekit::proto::IceCandidateType; switch (t) { - case P::HOST: - return IceCandidateType::Host; - case P::SRFLX: - return IceCandidateType::Srflx; - case P::PRFLX: - return IceCandidateType::Prflx; - case P::RELAY: - return IceCandidateType::Relay; - default: - return IceCandidateType::Unknown; + case P::HOST: + return IceCandidateType::Host; + case P::SRFLX: + return IceCandidateType::Srflx; + case P::PRFLX: + return IceCandidateType::Prflx; + case P::RELAY: + return IceCandidateType::Relay; + default: + return IceCandidateType::Unknown; } } -IceServerTransportProtocol -fromProto(livekit::proto::IceServerTransportProtocol p) { +IceServerTransportProtocol fromProto(livekit::proto::IceServerTransportProtocol p) { using P = livekit::proto::IceServerTransportProtocol; switch (p) { - case P::TRANSPORT_UDP: - return IceServerTransportProtocol::Udp; - case P::TRANSPORT_TCP: - return IceServerTransportProtocol::Tcp; - case P::TRANSPORT_TLS: - return IceServerTransportProtocol::Tls; - default: - return IceServerTransportProtocol::Unknown; + case P::TRANSPORT_UDP: + return IceServerTransportProtocol::Udp; + case P::TRANSPORT_TCP: + return IceServerTransportProtocol::Tcp; + case P::TRANSPORT_TLS: + return IceServerTransportProtocol::Tls; + default: + return IceServerTransportProtocol::Unknown; } } IceTcpCandidateType fromProto(livekit::proto::IceTcpCandidateType t) { using P = livekit::proto::IceTcpCandidateType; switch (t) { - case P::CANDIDATE_ACTIVE: - return IceTcpCandidateType::Active; - case P::CANDIDATE_PASSIVE: - return IceTcpCandidateType::Passive; - case P::CANDIDATE_SO: - return IceTcpCandidateType::So; - default: - return IceTcpCandidateType::Unknown; + case P::CANDIDATE_ACTIVE: + return IceTcpCandidateType::Active; + case P::CANDIDATE_PASSIVE: + return IceTcpCandidateType::Passive; + case P::CANDIDATE_SO: + return IceTcpCandidateType::So; + default: + return IceTcpCandidateType::Unknown; } } @@ -191,14 +190,14 @@ IceTcpCandidateType fromProto(livekit::proto::IceTcpCandidateType t) { // Leaf conversions // ---------------------- -RtcStatsData fromProto(const proto::RtcStatsData &s) { +RtcStatsData fromProto(const proto::RtcStatsData& s) { RtcStatsData out; out.id = s.id(); out.timestamp_ms = s.timestamp(); return out; } -CodecStats fromProto(const proto::CodecStats &s) { +CodecStats fromProto(const proto::CodecStats& s) { CodecStats out; out.payload_type = s.payload_type(); out.transport_id = s.transport_id(); @@ -209,7 +208,7 @@ CodecStats fromProto(const proto::CodecStats &s) { return out; } -RtpStreamStats fromProto(const proto::RtpStreamStats &s) { +RtpStreamStats fromProto(const proto::RtpStreamStats& s) { RtpStreamStats out; out.ssrc = s.ssrc(); out.kind = s.kind(); @@ -218,7 +217,7 @@ RtpStreamStats fromProto(const proto::RtpStreamStats &s) { return out; } -ReceivedRtpStreamStats fromProto(const proto::ReceivedRtpStreamStats &s) { +ReceivedRtpStreamStats fromProto(const proto::ReceivedRtpStreamStats& s) { ReceivedRtpStreamStats out; out.packets_received = s.packets_received(); out.packets_lost = s.packets_lost(); @@ -226,7 +225,7 @@ ReceivedRtpStreamStats fromProto(const proto::ReceivedRtpStreamStats &s) { return out; } -InboundRtpStreamStats fromProto(const proto::InboundRtpStreamStats &s) { +InboundRtpStreamStats fromProto(const proto::InboundRtpStreamStats& s) { InboundRtpStreamStats out; out.track_identifier = s.track_identifier(); out.mid = s.mid(); @@ -275,8 +274,7 @@ InboundRtpStreamStats fromProto(const proto::InboundRtpStreamStats &s) { out.decoder_implementation = s.decoder_implementation(); out.playout_id = s.playout_id(); out.power_efficient_decoder = s.power_efficient_decoder(); - out.frames_assembled_from_multiple_packets = - s.frames_assembled_from_multiple_packets(); + out.frames_assembled_from_multiple_packets = s.frames_assembled_from_multiple_packets(); out.total_assembly_time = s.total_assembly_time(); out.retransmitted_packets_received = s.retransmitted_packets_received(); out.retransmitted_bytes_received = s.retransmitted_bytes_received(); @@ -285,14 +283,14 @@ InboundRtpStreamStats fromProto(const proto::InboundRtpStreamStats &s) { return out; } -SentRtpStreamStats fromProto(const proto::SentRtpStreamStats &s) { +SentRtpStreamStats fromProto(const proto::SentRtpStreamStats& s) { SentRtpStreamStats out; out.packets_sent = s.packets_sent(); out.bytes_sent = s.bytes_sent(); return out; } -OutboundRtpStreamStats fromProto(const proto::OutboundRtpStreamStats &s) { +OutboundRtpStreamStats fromProto(const proto::OutboundRtpStreamStats& s) { OutboundRtpStreamStats out; out.mid = s.mid(); out.media_source_id = s.media_source_id(); @@ -316,11 +314,10 @@ OutboundRtpStreamStats fromProto(const proto::OutboundRtpStreamStats &s) { out.total_packet_send_delay = s.total_packet_send_delay(); out.quality_limitation_reason = fromProto(s.quality_limitation_reason()); out.quality_limitation_durations.clear(); - for (const auto &kv : s.quality_limitation_durations()) { + for (const auto& kv : s.quality_limitation_durations()) { out.quality_limitation_durations.emplace(kv.first, kv.second); } - out.quality_limitation_resolution_changes = - s.quality_limitation_resolution_changes(); + out.quality_limitation_resolution_changes = s.quality_limitation_resolution_changes(); out.nack_count = s.nack_count(); out.fir_count = s.fir_count(); out.pli_count = s.pli_count(); @@ -331,8 +328,7 @@ OutboundRtpStreamStats fromProto(const proto::OutboundRtpStreamStats &s) { return out; } -RemoteInboundRtpStreamStats -fromProto(const proto::RemoteInboundRtpStreamStats &s) { +RemoteInboundRtpStreamStats fromProto(const proto::RemoteInboundRtpStreamStats& s) { RemoteInboundRtpStreamStats out; out.local_id = s.local_id(); out.round_trip_time = s.round_trip_time(); @@ -342,8 +338,7 @@ fromProto(const proto::RemoteInboundRtpStreamStats &s) { return out; } -RemoteOutboundRtpStreamStats -fromProto(const proto::RemoteOutboundRtpStreamStats &s) { +RemoteOutboundRtpStreamStats fromProto(const proto::RemoteOutboundRtpStreamStats& s) { RemoteOutboundRtpStreamStats out; out.local_id = s.local_id(); out.remote_timestamp = s.remote_timestamp(); @@ -354,14 +349,14 @@ fromProto(const proto::RemoteOutboundRtpStreamStats &s) { return out; } -MediaSourceStats fromProto(const proto::MediaSourceStats &s) { +MediaSourceStats fromProto(const proto::MediaSourceStats& s) { MediaSourceStats out; out.track_identifier = s.track_identifier(); out.kind = s.kind(); return out; } -AudioSourceStats fromProto(const proto::AudioSourceStats &s) { +AudioSourceStats fromProto(const proto::AudioSourceStats& s) { AudioSourceStats out; out.audio_level = s.audio_level(); out.total_audio_energy = s.total_audio_energy(); @@ -375,7 +370,7 @@ AudioSourceStats fromProto(const proto::AudioSourceStats &s) { return out; } -VideoSourceStats fromProto(const proto::VideoSourceStats &s) { +VideoSourceStats fromProto(const proto::VideoSourceStats& s) { VideoSourceStats out; out.width = s.width(); out.height = s.height(); @@ -384,7 +379,7 @@ VideoSourceStats fromProto(const proto::VideoSourceStats &s) { return out; } -AudioPlayoutStats fromProto(const proto::AudioPlayoutStats &s) { +AudioPlayoutStats fromProto(const proto::AudioPlayoutStats& s) { AudioPlayoutStats out; out.kind = s.kind(); out.synthesized_samples_duration = s.synthesized_samples_duration(); @@ -395,14 +390,14 @@ AudioPlayoutStats fromProto(const proto::AudioPlayoutStats &s) { return out; } -PeerConnectionStats fromProto(const proto::PeerConnectionStats &s) { +PeerConnectionStats fromProto(const proto::PeerConnectionStats& s) { PeerConnectionStats out; out.data_channels_opened = s.data_channels_opened(); out.data_channels_closed = s.data_channels_closed(); return out; } -DataChannelStats fromProto(const proto::DataChannelStats &s) { +DataChannelStats fromProto(const proto::DataChannelStats& s) { DataChannelStats out; out.label = s.label(); out.protocol = s.protocol(); @@ -419,7 +414,7 @@ DataChannelStats fromProto(const proto::DataChannelStats &s) { return out; } -TransportStats fromProto(const proto::TransportStats &s) { +TransportStats fromProto(const proto::TransportStats& s) { TransportStats out; out.packets_sent = s.packets_sent(); out.packets_received = s.packets_received(); @@ -448,7 +443,7 @@ TransportStats fromProto(const proto::TransportStats &s) { return out; } -CandidatePairStats fromProto(const proto::CandidatePairStats &s) { +CandidatePairStats fromProto(const proto::CandidatePairStats& s) { CandidatePairStats out; out.transport_id = s.transport_id(); out.local_candidate_id = s.local_candidate_id(); @@ -479,7 +474,7 @@ CandidatePairStats fromProto(const proto::CandidatePairStats &s) { return out; } -IceCandidateStats fromProto(const proto::IceCandidateStats &s) { +IceCandidateStats fromProto(const proto::IceCandidateStats& s) { IceCandidateStats out; out.transport_id = s.transport_id(); out.address = s.address(); @@ -509,7 +504,7 @@ IceCandidateStats fromProto(const proto::IceCandidateStats &s) { return out; } -CertificateStats fromProto(const proto::CertificateStats &s) { +CertificateStats fromProto(const proto::CertificateStats& s) { CertificateStats out; out.fingerprint = s.fingerprint(); out.fingerprint_algorithm = s.fingerprint_algorithm(); @@ -518,7 +513,7 @@ CertificateStats fromProto(const proto::CertificateStats &s) { return out; } -StreamStats fromProto(const proto::StreamStats &s) { +StreamStats fromProto(const proto::StreamStats& s) { StreamStats out; out.id = s.id(); out.stream_identifier = s.stream_identifier(); @@ -529,127 +524,127 @@ StreamStats fromProto(const proto::StreamStats &s) { // High-level RtcStats fromProto // ---------------------- -RtcStats fromProto(const proto::RtcStats &s) { +RtcStats fromProto(const proto::RtcStats& s) { using P = proto::RtcStats; switch (s.stats_case()) { - case P::kCodec: { - RtcCodecStats out; - out.rtc = fromProto(s.codec().rtc()); - out.codec = fromProto(s.codec().codec()); - return RtcStats{std::move(out)}; - } - case P::kInboundRtp: { - RtcInboundRtpStats out; - out.rtc = fromProto(s.inbound_rtp().rtc()); - out.stream = fromProto(s.inbound_rtp().stream()); - out.received = fromProto(s.inbound_rtp().received()); - out.inbound = fromProto(s.inbound_rtp().inbound()); - return RtcStats{std::move(out)}; - } - case P::kOutboundRtp: { - RtcOutboundRtpStats out; - out.rtc = fromProto(s.outbound_rtp().rtc()); - out.stream = fromProto(s.outbound_rtp().stream()); - out.sent = fromProto(s.outbound_rtp().sent()); - out.outbound = fromProto(s.outbound_rtp().outbound()); - return RtcStats{std::move(out)}; - } - case P::kRemoteInboundRtp: { - RtcRemoteInboundRtpStats out; - out.rtc = fromProto(s.remote_inbound_rtp().rtc()); - out.stream = fromProto(s.remote_inbound_rtp().stream()); - out.received = fromProto(s.remote_inbound_rtp().received()); - out.remote_inbound = fromProto(s.remote_inbound_rtp().remote_inbound()); - return RtcStats{std::move(out)}; - } - case P::kRemoteOutboundRtp: { - RtcRemoteOutboundRtpStats out; - out.rtc = fromProto(s.remote_outbound_rtp().rtc()); - out.stream = fromProto(s.remote_outbound_rtp().stream()); - out.sent = fromProto(s.remote_outbound_rtp().sent()); - out.remote_outbound = fromProto(s.remote_outbound_rtp().remote_outbound()); - return RtcStats{std::move(out)}; - } - case P::kMediaSource: { - RtcMediaSourceStats out; - out.rtc = fromProto(s.media_source().rtc()); - out.source = fromProto(s.media_source().source()); - out.audio = fromProto(s.media_source().audio()); - out.video = fromProto(s.media_source().video()); - return RtcStats{std::move(out)}; - } - case P::kMediaPlayout: { - RtcMediaPlayoutStats out; - out.rtc = fromProto(s.media_playout().rtc()); - out.audio_playout = fromProto(s.media_playout().audio_playout()); - return RtcStats{std::move(out)}; - } - case P::kPeerConnection: { - RtcPeerConnectionStats out; - out.rtc = fromProto(s.peer_connection().rtc()); - out.pc = fromProto(s.peer_connection().pc()); - return RtcStats{std::move(out)}; - } - case P::kDataChannel: { - RtcDataChannelStats out; - out.rtc = fromProto(s.data_channel().rtc()); - out.dc = fromProto(s.data_channel().dc()); - return RtcStats{std::move(out)}; - } - case P::kTransport: { - RtcTransportStats out; - out.rtc = fromProto(s.transport().rtc()); - out.transport = fromProto(s.transport().transport()); - return RtcStats{std::move(out)}; - } - case P::kCandidatePair: { - RtcCandidatePairStats out; - out.rtc = fromProto(s.candidate_pair().rtc()); - out.candidate_pair = fromProto(s.candidate_pair().candidate_pair()); - return RtcStats{std::move(out)}; - } - case P::kLocalCandidate: { - RtcLocalCandidateStats out; - out.rtc = fromProto(s.local_candidate().rtc()); - out.candidate = fromProto(s.local_candidate().candidate()); - return RtcStats{std::move(out)}; - } - case P::kRemoteCandidate: { - RtcRemoteCandidateStats out; - out.rtc = fromProto(s.remote_candidate().rtc()); - out.candidate = fromProto(s.remote_candidate().candidate()); - return RtcStats{std::move(out)}; - } - case P::kCertificate: { - RtcCertificateStats out; - out.rtc = fromProto(s.certificate().rtc()); - out.certificate = fromProto(s.certificate().certificate()); - return RtcStats{std::move(out)}; - } - case P::kStream: { - RtcStreamStats out; - out.rtc = fromProto(s.stream().rtc()); - out.stream = fromProto(s.stream().stream()); - return RtcStats{std::move(out)}; - } - case P::kTrack: - // Deprecated; fall through to default - case P::STATS_NOT_SET: - default: { - // You might want to handle this differently (throw, assert, etc.) - RtcCodecStats dummy{}; - dummy.rtc = RtcStatsData{}; - dummy.codec = CodecStats{}; - return RtcStats{std::move(dummy)}; - } - } -} - -std::vector fromProto(const std::vector &src) { + case P::kCodec: { + RtcCodecStats out; + out.rtc = fromProto(s.codec().rtc()); + out.codec = fromProto(s.codec().codec()); + return RtcStats{std::move(out)}; + } + case P::kInboundRtp: { + RtcInboundRtpStats out; + out.rtc = fromProto(s.inbound_rtp().rtc()); + out.stream = fromProto(s.inbound_rtp().stream()); + out.received = fromProto(s.inbound_rtp().received()); + out.inbound = fromProto(s.inbound_rtp().inbound()); + return RtcStats{std::move(out)}; + } + case P::kOutboundRtp: { + RtcOutboundRtpStats out; + out.rtc = fromProto(s.outbound_rtp().rtc()); + out.stream = fromProto(s.outbound_rtp().stream()); + out.sent = fromProto(s.outbound_rtp().sent()); + out.outbound = fromProto(s.outbound_rtp().outbound()); + return RtcStats{std::move(out)}; + } + case P::kRemoteInboundRtp: { + RtcRemoteInboundRtpStats out; + out.rtc = fromProto(s.remote_inbound_rtp().rtc()); + out.stream = fromProto(s.remote_inbound_rtp().stream()); + out.received = fromProto(s.remote_inbound_rtp().received()); + out.remote_inbound = fromProto(s.remote_inbound_rtp().remote_inbound()); + return RtcStats{std::move(out)}; + } + case P::kRemoteOutboundRtp: { + RtcRemoteOutboundRtpStats out; + out.rtc = fromProto(s.remote_outbound_rtp().rtc()); + out.stream = fromProto(s.remote_outbound_rtp().stream()); + out.sent = fromProto(s.remote_outbound_rtp().sent()); + out.remote_outbound = fromProto(s.remote_outbound_rtp().remote_outbound()); + return RtcStats{std::move(out)}; + } + case P::kMediaSource: { + RtcMediaSourceStats out; + out.rtc = fromProto(s.media_source().rtc()); + out.source = fromProto(s.media_source().source()); + out.audio = fromProto(s.media_source().audio()); + out.video = fromProto(s.media_source().video()); + return RtcStats{std::move(out)}; + } + case P::kMediaPlayout: { + RtcMediaPlayoutStats out; + out.rtc = fromProto(s.media_playout().rtc()); + out.audio_playout = fromProto(s.media_playout().audio_playout()); + return RtcStats{std::move(out)}; + } + case P::kPeerConnection: { + RtcPeerConnectionStats out; + out.rtc = fromProto(s.peer_connection().rtc()); + out.pc = fromProto(s.peer_connection().pc()); + return RtcStats{std::move(out)}; + } + case P::kDataChannel: { + RtcDataChannelStats out; + out.rtc = fromProto(s.data_channel().rtc()); + out.dc = fromProto(s.data_channel().dc()); + return RtcStats{std::move(out)}; + } + case P::kTransport: { + RtcTransportStats out; + out.rtc = fromProto(s.transport().rtc()); + out.transport = fromProto(s.transport().transport()); + return RtcStats{std::move(out)}; + } + case P::kCandidatePair: { + RtcCandidatePairStats out; + out.rtc = fromProto(s.candidate_pair().rtc()); + out.candidate_pair = fromProto(s.candidate_pair().candidate_pair()); + return RtcStats{std::move(out)}; + } + case P::kLocalCandidate: { + RtcLocalCandidateStats out; + out.rtc = fromProto(s.local_candidate().rtc()); + out.candidate = fromProto(s.local_candidate().candidate()); + return RtcStats{std::move(out)}; + } + case P::kRemoteCandidate: { + RtcRemoteCandidateStats out; + out.rtc = fromProto(s.remote_candidate().rtc()); + out.candidate = fromProto(s.remote_candidate().candidate()); + return RtcStats{std::move(out)}; + } + case P::kCertificate: { + RtcCertificateStats out; + out.rtc = fromProto(s.certificate().rtc()); + out.certificate = fromProto(s.certificate().certificate()); + return RtcStats{std::move(out)}; + } + case P::kStream: { + RtcStreamStats out; + out.rtc = fromProto(s.stream().rtc()); + out.stream = fromProto(s.stream().stream()); + return RtcStats{std::move(out)}; + } + case P::kTrack: + // Deprecated; fall through to default + case P::STATS_NOT_SET: + default: { + // You might want to handle this differently (throw, assert, etc.) + RtcCodecStats dummy{}; + dummy.rtc = RtcStatsData{}; + dummy.codec = CodecStats{}; + return RtcStats{std::move(dummy)}; + } + } +} + +std::vector fromProto(const std::vector& src) { std::vector out; out.reserve(src.size()); - for (const auto &s : src) { + for (const auto& s : src) { out.push_back(fromProto(s)); } return out; diff --git a/src/subscription_thread_dispatcher.cpp b/src/subscription_thread_dispatcher.cpp index 91877877..aa7703a3 100644 --- a/src/subscription_thread_dispatcher.cpp +++ b/src/subscription_thread_dispatcher.cpp @@ -16,27 +16,30 @@ #include "livekit/subscription_thread_dispatcher.h" +#include +#include +#include + #include "livekit/data_track_frame.h" #include "livekit/data_track_stream.h" #include "livekit/remote_data_track.h" #include "livekit/track.h" #include "lk_log.h" -#include -#include -#include - namespace livekit { namespace { -const char *trackKindName(TrackKind kind) { - if (kind == TrackKind::KIND_AUDIO) +const char* trackKindName(TrackKind kind) { + if (kind == TrackKind::KIND_AUDIO) { return "audio"; - if (kind == TrackKind::KIND_VIDEO) + } + if (kind == TrackKind::KIND_VIDEO) { return "video"; - if (kind == TrackKind::KIND_UNKNOWN) + } + if (kind == TrackKind::KIND_UNKNOWN) { return "unknown"; + } return "unsupported"; } @@ -54,24 +57,23 @@ SubscriptionThreadDispatcher::~SubscriptionThreadDispatcher() { } // NOLINTEND(bugprone-exception-escape) -void SubscriptionThreadDispatcher::setOnAudioFrameCallback( - const std::string &participant_identity, TrackSource source, - AudioFrameCallback callback, const AudioStream::Options &opts) { +void SubscriptionThreadDispatcher::setOnAudioFrameCallback(const std::string& participant_identity, TrackSource source, + AudioFrameCallback callback, + const AudioStream::Options& opts) { const CallbackKey key{participant_identity, source, ""}; const std::scoped_lock lock(lock_); const bool replacing = audio_callbacks_.find(key) != audio_callbacks_.end(); audio_callbacks_[key] = RegisteredAudioCallback{std::move(callback), opts}; - LK_LOG_DEBUG("Registered audio frame callback for participant={} source={} " - "replacing_existing={} total_audio_callbacks={}", - participant_identity, static_cast(source), replacing, - audio_callbacks_.size()); + LK_LOG_DEBUG( + "Registered audio frame callback for participant={} source={} " + "replacing_existing={} total_audio_callbacks={}", + participant_identity, static_cast(source), replacing, audio_callbacks_.size()); } -void SubscriptionThreadDispatcher::setOnAudioFrameCallback( - const std::string &participant_identity, const std::string &track_name, - AudioFrameCallback callback, const AudioStream::Options &opts) { - const CallbackKey key{participant_identity, TrackSource::SOURCE_UNKNOWN, - track_name}; +void SubscriptionThreadDispatcher::setOnAudioFrameCallback(const std::string& participant_identity, + const std::string& track_name, AudioFrameCallback callback, + const AudioStream::Options& opts) { + const CallbackKey key{participant_identity, TrackSource::SOURCE_UNKNOWN, track_name}; const std::scoped_lock lock(lock_); const bool replacing = audio_callbacks_.find(key) != audio_callbacks_.end(); audio_callbacks_[key] = RegisteredAudioCallback{std::move(callback), opts}; @@ -81,9 +83,9 @@ void SubscriptionThreadDispatcher::setOnAudioFrameCallback( participant_identity, track_name, replacing, audio_callbacks_.size()); } -void SubscriptionThreadDispatcher::setOnVideoFrameCallback( - const std::string &participant_identity, TrackSource source, - VideoFrameCallback callback, const VideoStream::Options &opts) { +void SubscriptionThreadDispatcher::setOnVideoFrameCallback(const std::string& participant_identity, TrackSource source, + VideoFrameCallback callback, + const VideoStream::Options& opts) { const CallbackKey key{participant_identity, source, ""}; const std::scoped_lock lock(lock_); const bool replacing = video_callbacks_.find(key) != video_callbacks_.end(); @@ -92,17 +94,17 @@ void SubscriptionThreadDispatcher::setOnVideoFrameCallback( VideoFrameEventCallback{}, opts, }; - LK_LOG_DEBUG("Registered legacy video frame callback for participant={} " - "source={} replacing_existing={} total_video_callbacks={}", - participant_identity, static_cast(source), replacing, - video_callbacks_.size()); + LK_LOG_DEBUG( + "Registered legacy video frame callback for participant={} " + "source={} replacing_existing={} total_video_callbacks={}", + participant_identity, static_cast(source), replacing, video_callbacks_.size()); } -void SubscriptionThreadDispatcher::setOnVideoFrameEventCallback( - const std::string &participant_identity, const std::string &track_name, - VideoFrameEventCallback callback, const VideoStream::Options &opts) { - const CallbackKey key{participant_identity, TrackSource::SOURCE_UNKNOWN, - track_name}; +void SubscriptionThreadDispatcher::setOnVideoFrameEventCallback(const std::string& participant_identity, + const std::string& track_name, + VideoFrameEventCallback callback, + const VideoStream::Options& opts) { + const CallbackKey key{participant_identity, TrackSource::SOURCE_UNKNOWN, track_name}; const std::scoped_lock lock(lock_); const bool replacing = video_callbacks_.find(key) != video_callbacks_.end(); video_callbacks_[key] = RegisteredVideoCallback{ @@ -116,11 +118,10 @@ void SubscriptionThreadDispatcher::setOnVideoFrameEventCallback( participant_identity, track_name, replacing, video_callbacks_.size()); } -void SubscriptionThreadDispatcher::setOnVideoFrameCallback( - const std::string &participant_identity, const std::string &track_name, - VideoFrameCallback callback, const VideoStream::Options &opts) { - const CallbackKey key{participant_identity, TrackSource::SOURCE_UNKNOWN, - track_name}; +void SubscriptionThreadDispatcher::setOnVideoFrameCallback(const std::string& participant_identity, + const std::string& track_name, VideoFrameCallback callback, + const VideoStream::Options& opts) { + const CallbackKey key{participant_identity, TrackSource::SOURCE_UNKNOWN, track_name}; const std::scoped_lock lock(lock_); const bool replacing = video_callbacks_.find(key) != video_callbacks_.end(); video_callbacks_[key] = RegisteredVideoCallback{ @@ -134,8 +135,8 @@ void SubscriptionThreadDispatcher::setOnVideoFrameCallback( participant_identity, track_name, replacing, video_callbacks_.size()); } -void SubscriptionThreadDispatcher::clearOnAudioFrameCallback( - const std::string &participant_identity, TrackSource source) { +void SubscriptionThreadDispatcher::clearOnAudioFrameCallback(const std::string& participant_identity, + TrackSource source) { const CallbackKey key{participant_identity, source, ""}; std::thread old_thread; bool removed_callback = false; @@ -146,18 +147,17 @@ void SubscriptionThreadDispatcher::clearOnAudioFrameCallback( LK_LOG_DEBUG( "Clearing audio frame callback for participant={} source={} " "removed_callback={} stopped_reader={} remaining_audio_callbacks={}", - participant_identity, static_cast(source), removed_callback, - old_thread.joinable(), audio_callbacks_.size()); + participant_identity, static_cast(source), removed_callback, old_thread.joinable(), + audio_callbacks_.size()); } if (old_thread.joinable()) { old_thread.join(); } } -void SubscriptionThreadDispatcher::clearOnAudioFrameCallback( - const std::string &participant_identity, const std::string &track_name) { - const CallbackKey key{participant_identity, TrackSource::SOURCE_UNKNOWN, - track_name}; +void SubscriptionThreadDispatcher::clearOnAudioFrameCallback(const std::string& participant_identity, + const std::string& track_name) { + const CallbackKey key{participant_identity, TrackSource::SOURCE_UNKNOWN, track_name}; std::thread old_thread; bool removed_callback = false; { @@ -167,16 +167,15 @@ void SubscriptionThreadDispatcher::clearOnAudioFrameCallback( LK_LOG_DEBUG( "Clearing audio frame callback for participant={} track_name={} " "removed_callback={} stopped_reader={} remaining_audio_callbacks={}", - participant_identity, track_name, removed_callback, - old_thread.joinable(), audio_callbacks_.size()); + participant_identity, track_name, removed_callback, old_thread.joinable(), audio_callbacks_.size()); } if (old_thread.joinable()) { old_thread.join(); } } -void SubscriptionThreadDispatcher::clearOnVideoFrameCallback( - const std::string &participant_identity, TrackSource source) { +void SubscriptionThreadDispatcher::clearOnVideoFrameCallback(const std::string& participant_identity, + TrackSource source) { const CallbackKey key{participant_identity, source, ""}; std::thread old_thread; bool removed_callback = false; @@ -187,18 +186,17 @@ void SubscriptionThreadDispatcher::clearOnVideoFrameCallback( LK_LOG_DEBUG( "Clearing video frame callback for participant={} source={} " "removed_callback={} stopped_reader={} remaining_video_callbacks={}", - participant_identity, static_cast(source), removed_callback, - old_thread.joinable(), video_callbacks_.size()); + participant_identity, static_cast(source), removed_callback, old_thread.joinable(), + video_callbacks_.size()); } if (old_thread.joinable()) { old_thread.join(); } } -void SubscriptionThreadDispatcher::clearOnVideoFrameCallback( - const std::string &participant_identity, const std::string &track_name) { - const CallbackKey key{participant_identity, TrackSource::SOURCE_UNKNOWN, - track_name}; +void SubscriptionThreadDispatcher::clearOnVideoFrameCallback(const std::string& participant_identity, + const std::string& track_name) { + const CallbackKey key{participant_identity, TrackSource::SOURCE_UNKNOWN, track_name}; std::thread old_thread; bool removed_callback = false; { @@ -208,17 +206,16 @@ void SubscriptionThreadDispatcher::clearOnVideoFrameCallback( LK_LOG_DEBUG( "Clearing video frame callback for participant={} track_name={} " "removed_callback={} stopped_reader={} remaining_video_callbacks={}", - participant_identity, track_name, removed_callback, - old_thread.joinable(), video_callbacks_.size()); + participant_identity, track_name, removed_callback, old_thread.joinable(), video_callbacks_.size()); } if (old_thread.joinable()) { old_thread.join(); } } -void SubscriptionThreadDispatcher::handleTrackSubscribed( - const std::string &participant_identity, TrackSource source, - const std::string &track_name, const std::shared_ptr &track) { +void SubscriptionThreadDispatcher::handleTrackSubscribed(const std::string& participant_identity, TrackSource source, + const std::string& track_name, + const std::shared_ptr& track) { if (!track) { LK_LOG_WARN( "Ignoring subscribed track dispatch for participant={} source={} " @@ -227,20 +224,16 @@ void SubscriptionThreadDispatcher::handleTrackSubscribed( return; } - LK_LOG_DEBUG("Handling subscribed track for participant={} source={} kind={}", - participant_identity, static_cast(source), - trackKindName(track->kind())); + LK_LOG_DEBUG("Handling subscribed track for participant={} source={} kind={}", participant_identity, + static_cast(source), trackKindName(track->kind())); - CallbackKey key{participant_identity, TrackSource::SOURCE_UNKNOWN, - track_name}; + CallbackKey key{participant_identity, TrackSource::SOURCE_UNKNOWN, track_name}; const CallbackKey fallback_key{participant_identity, source, ""}; std::thread old_thread; { const std::scoped_lock lock(lock_); - if ((track->kind() == TrackKind::KIND_AUDIO && - audio_callbacks_.find(key) == audio_callbacks_.end()) || - (track->kind() == TrackKind::KIND_VIDEO && - video_callbacks_.find(key) == video_callbacks_.end())) { + if ((track->kind() == TrackKind::KIND_AUDIO && audio_callbacks_.find(key) == audio_callbacks_.end()) || + (track->kind() == TrackKind::KIND_VIDEO && video_callbacks_.find(key) == video_callbacks_.end())) { key = fallback_key; } old_thread = startReaderLocked(key, track); @@ -250,11 +243,9 @@ void SubscriptionThreadDispatcher::handleTrackSubscribed( } } -void SubscriptionThreadDispatcher::handleTrackUnsubscribed( - const std::string &participant_identity, TrackSource source, - const std::string &track_name) { - const CallbackKey key{participant_identity, TrackSource::SOURCE_UNKNOWN, - track_name}; +void SubscriptionThreadDispatcher::handleTrackUnsubscribed(const std::string& participant_identity, TrackSource source, + const std::string& track_name) { + const CallbackKey key{participant_identity, TrackSource::SOURCE_UNKNOWN, track_name}; const CallbackKey fallback_key{participant_identity, source, ""}; std::thread old_thread; std::thread fallback_old_thread; @@ -262,10 +253,11 @@ void SubscriptionThreadDispatcher::handleTrackUnsubscribed( const std::scoped_lock lock(lock_); old_thread = extractReaderThreadLocked(key); fallback_old_thread = extractReaderThreadLocked(fallback_key); - LK_LOG_DEBUG("Handling unsubscribed track for participant={} source={} " - "track_name={} stopped_reader={} fallback_stopped_reader={}", - participant_identity, static_cast(source), track_name, - old_thread.joinable(), fallback_old_thread.joinable()); + LK_LOG_DEBUG( + "Handling unsubscribed track for participant={} source={} " + "track_name={} stopped_reader={} fallback_stopped_reader={}", + participant_identity, static_cast(source), track_name, old_thread.joinable(), + fallback_old_thread.joinable()); } if (old_thread.joinable()) { old_thread.join(); @@ -279,9 +271,9 @@ void SubscriptionThreadDispatcher::handleTrackUnsubscribed( // Data track callback registration // ------------------------------------------------------------------- -DataFrameCallbackId SubscriptionThreadDispatcher::addOnDataFrameCallback( - const std::string &participant_identity, const std::string &track_name, - DataFrameCallback callback) { +DataFrameCallbackId SubscriptionThreadDispatcher::addOnDataFrameCallback(const std::string& participant_identity, + const std::string& track_name, + DataFrameCallback callback) { std::thread old_thread; DataFrameCallbackId id; { @@ -292,8 +284,7 @@ DataFrameCallbackId SubscriptionThreadDispatcher::addOnDataFrameCallback( auto track_it = remote_data_tracks_.find(key); if (track_it != remote_data_tracks_.end()) { - old_thread = startDataReaderLocked(id, key, track_it->second, - data_callbacks_[id].callback); + old_thread = startDataReaderLocked(id, key, track_it->second, data_callbacks_[id].callback); } } if (old_thread.joinable()) { @@ -302,8 +293,7 @@ DataFrameCallbackId SubscriptionThreadDispatcher::addOnDataFrameCallback( return id; } -void SubscriptionThreadDispatcher::removeOnDataFrameCallback( - DataFrameCallbackId id) { +void SubscriptionThreadDispatcher::removeOnDataFrameCallback(DataFrameCallbackId id) { std::thread old_thread; { const std::scoped_lock lock(lock_); @@ -315,16 +305,14 @@ void SubscriptionThreadDispatcher::removeOnDataFrameCallback( } } -void SubscriptionThreadDispatcher::handleDataTrackPublished( - const std::shared_ptr &track) { +void SubscriptionThreadDispatcher::handleDataTrackPublished(const std::shared_ptr& track) { if (!track) { LK_LOG_WARN("handleDataTrackPublished called with null track"); return; } - LK_LOG_INFO("Handling data track published: \"{}\" from \"{}\" (sid={})", - track->info().name, track->publisherIdentity(), - track->info().sid); + LK_LOG_INFO("Handling data track published: \"{}\" from \"{}\" (sid={})", track->info().name, + track->publisherIdentity(), track->info().sid); std::vector old_threads; { @@ -332,7 +320,7 @@ void SubscriptionThreadDispatcher::handleDataTrackPublished( const DataCallbackKey key{track->publisherIdentity(), track->info().name}; remote_data_tracks_[key] = track; - for (auto &[id, reg] : data_callbacks_) { + for (auto& [id, reg] : data_callbacks_) { if (reg.key == key) { auto t = startDataReaderLocked(id, key, track, reg.callback); if (t.joinable()) { @@ -341,21 +329,19 @@ void SubscriptionThreadDispatcher::handleDataTrackPublished( } } } - for (auto &t : old_threads) { + for (auto& t : old_threads) { t.join(); } } -void SubscriptionThreadDispatcher::handleDataTrackUnpublished( - const std::string &sid) { +void SubscriptionThreadDispatcher::handleDataTrackUnpublished(const std::string& sid) { LK_LOG_INFO("Handling data track unpublished: sid={}", sid); std::vector old_threads; { const std::scoped_lock lock(lock_); - for (auto it = active_data_readers_.begin(); - it != active_data_readers_.end();) { - auto &reader = it->second; + for (auto it = active_data_readers_.begin(); it != active_data_readers_.end();) { + auto& reader = it->second; if (reader->remote_track && reader->remote_track->info().sid == sid) { { const std::scoped_lock sub_guard(reader->sub_mutex); @@ -371,15 +357,14 @@ void SubscriptionThreadDispatcher::handleDataTrackUnpublished( ++it; } } - for (auto it = remote_data_tracks_.begin(); it != remote_data_tracks_.end(); - ++it) { + for (auto it = remote_data_tracks_.begin(); it != remote_data_tracks_.end(); ++it) { if (it->second && it->second->info().sid == sid) { remote_data_tracks_.erase(it); break; } } } - for (auto &t : old_threads) { + for (auto& t : old_threads) { t.join(); } } @@ -388,14 +373,14 @@ void SubscriptionThreadDispatcher::stopAll() { std::vector threads; { const std::scoped_lock lock(lock_); - LK_LOG_DEBUG("Stopping all subscription readers active_readers={} " - "active_data_readers={} audio_callbacks={} " - "video_callbacks={} data_callbacks={}", - active_readers_.size(), active_data_readers_.size(), - audio_callbacks_.size(), video_callbacks_.size(), - data_callbacks_.size()); - - for (auto &[key, reader] : active_readers_) { + LK_LOG_DEBUG( + "Stopping all subscription readers active_readers={} " + "active_data_readers={} audio_callbacks={} " + "video_callbacks={} data_callbacks={}", + active_readers_.size(), active_data_readers_.size(), audio_callbacks_.size(), video_callbacks_.size(), + data_callbacks_.size()); + + for (auto& [key, reader] : active_readers_) { if (reader.audio_stream) { reader.audio_stream->close(); } @@ -410,7 +395,7 @@ void SubscriptionThreadDispatcher::stopAll() { audio_callbacks_.clear(); video_callbacks_.clear(); - for (auto &[id, reader] : active_data_readers_) { + for (auto& [id, reader] : active_data_readers_) { { const std::scoped_lock sub_guard(reader->sub_mutex); if (reader->stream) { @@ -425,27 +410,26 @@ void SubscriptionThreadDispatcher::stopAll() { data_callbacks_.clear(); remote_data_tracks_.clear(); } - for (auto &thread : threads) { + for (auto& thread : threads) { thread.join(); } LK_LOG_DEBUG("Stopped {} subscription reader threads", threads.size()); } -std::thread SubscriptionThreadDispatcher::extractReaderThreadLocked( - const CallbackKey &key) { +std::thread SubscriptionThreadDispatcher::extractReaderThreadLocked(const CallbackKey& key) { auto it = active_readers_.find(key); if (it == active_readers_.end()) { - LK_LOG_TRACE("No active reader to extract for participant={} source={} " - "track_name={}", - key.participant_identity, static_cast(key.source), - key.track_name); + LK_LOG_TRACE( + "No active reader to extract for participant={} source={} " + "track_name={}", + key.participant_identity, static_cast(key.source), key.track_name); return {}; } - LK_LOG_DEBUG("Extracting active reader for participant={} source={} " - "track_name={}", - key.participant_identity, static_cast(key.source), - key.track_name); + LK_LOG_DEBUG( + "Extracting active reader for participant={} source={} " + "track_name={}", + key.participant_identity, static_cast(key.source), key.track_name); ActiveReader reader = std::move(it->second); active_readers_.erase(it); @@ -458,25 +442,26 @@ std::thread SubscriptionThreadDispatcher::extractReaderThreadLocked( return std::move(reader.thread); } -std::thread SubscriptionThreadDispatcher::startReaderLocked( - const CallbackKey &key, const std::shared_ptr &track) { +std::thread SubscriptionThreadDispatcher::startReaderLocked(const CallbackKey& key, + const std::shared_ptr& track) { if (track->kind() == TrackKind::KIND_AUDIO) { auto it = audio_callbacks_.find(key); if (it == audio_callbacks_.end()) { - LK_LOG_TRACE("Skipping audio reader start for participant={} source={} " - "because no audio callback is registered", - key.participant_identity, static_cast(key.source)); + LK_LOG_TRACE( + "Skipping audio reader start for participant={} source={} " + "because no audio callback is registered", + key.participant_identity, static_cast(key.source)); return {}; } - return startAudioReaderLocked(key, track, it->second.callback, - it->second.options); + return startAudioReaderLocked(key, track, it->second.callback, it->second.options); } if (track->kind() == TrackKind::KIND_VIDEO) { auto it = video_callbacks_.find(key); if (it == video_callbacks_.end()) { - LK_LOG_TRACE("Skipping video reader start for participant={} source={} " - "because no video callback is registered", - key.participant_identity, static_cast(key.source)); + LK_LOG_TRACE( + "Skipping video reader start for participant={} source={} " + "because no video callback is registered", + key.participant_identity, static_cast(key.source)); return {}; } return startVideoReaderLocked(key, track, it->second); @@ -496,26 +481,26 @@ std::thread SubscriptionThreadDispatcher::startReaderLocked( return {}; } -std::thread SubscriptionThreadDispatcher::startAudioReaderLocked( - const CallbackKey &key, const std::shared_ptr &track, - const AudioFrameCallback &cb, const AudioStream::Options &opts) { - LK_LOG_DEBUG("Starting audio reader for participant={} source={}", - key.participant_identity, static_cast(key.source)); +std::thread SubscriptionThreadDispatcher::startAudioReaderLocked(const CallbackKey& key, + const std::shared_ptr& track, + const AudioFrameCallback& cb, + const AudioStream::Options& opts) { + LK_LOG_DEBUG("Starting audio reader for participant={} source={}", key.participant_identity, + static_cast(key.source)); auto old_thread = extractReaderThreadLocked(key); if (static_cast(active_readers_.size()) >= kMaxActiveReaders) { LK_LOG_ERROR( "Cannot start audio reader for {} source={}: active reader limit ({}) " "reached", - key.participant_identity, static_cast(key.source), - kMaxActiveReaders); + key.participant_identity, static_cast(key.source), kMaxActiveReaders); return old_thread; } const auto stream = AudioStream::fromTrack(track, opts); if (!stream) { - LK_LOG_ERROR("Failed to create AudioStream for {} source={}", - key.participant_identity, static_cast(key.source)); + LK_LOG_ERROR("Failed to create AudioStream for {} source={}", key.participant_identity, + static_cast(key.source)); return old_thread; } @@ -531,54 +516,52 @@ std::thread SubscriptionThreadDispatcher::startAudioReaderLocked( // fault, not application logic -- suppressed at the lambda level. reader.thread = std::thread([stream, cb, participant_identity, source]() { try { - LK_LOG_DEBUG("Audio reader thread started for participant={} source={}", - participant_identity, static_cast(source)); + LK_LOG_DEBUG("Audio reader thread started for participant={} source={}", participant_identity, + static_cast(source)); AudioFrameEvent ev; while (stream->read(ev)) { try { cb(ev.frame); - } catch (const std::exception &e) { + } catch (const std::exception& e) { LK_LOG_ERROR("Audio frame callback exception: {}", e.what()); } } - LK_LOG_DEBUG("Audio reader thread exiting for participant={} source={}", - participant_identity, static_cast(source)); - } catch (const std::exception &e) { - LK_LOG_ERROR("Audio reader thread terminating due to exception: {}", - e.what()); + LK_LOG_DEBUG("Audio reader thread exiting for participant={} source={}", participant_identity, + static_cast(source)); + } catch (const std::exception& e) { + LK_LOG_ERROR("Audio reader thread terminating due to exception: {}", e.what()); } catch (...) { LK_LOG_ERROR("Audio reader thread terminating due to unknown exception"); } }); // NOLINTEND(bugprone-lambda-function-name,bugprone-exception-escape) active_readers_[key] = std::move(reader); - LK_LOG_DEBUG("Started audio reader for participant={} source={} " - "active_readers={}", - key.participant_identity, static_cast(key.source), - active_readers_.size()); + LK_LOG_DEBUG( + "Started audio reader for participant={} source={} " + "active_readers={}", + key.participant_identity, static_cast(key.source), active_readers_.size()); return old_thread; } -std::thread SubscriptionThreadDispatcher::startVideoReaderLocked( - const CallbackKey &key, const std::shared_ptr &track, - const RegisteredVideoCallback &callback) { - LK_LOG_DEBUG("Starting video reader for participant={} source={}", - key.participant_identity, static_cast(key.source)); +std::thread SubscriptionThreadDispatcher::startVideoReaderLocked(const CallbackKey& key, + const std::shared_ptr& track, + const RegisteredVideoCallback& callback) { + LK_LOG_DEBUG("Starting video reader for participant={} source={}", key.participant_identity, + static_cast(key.source)); auto old_thread = extractReaderThreadLocked(key); if (static_cast(active_readers_.size()) >= kMaxActiveReaders) { LK_LOG_ERROR( "Cannot start video reader for {} source={}: active reader limit ({}) " "reached", - key.participant_identity, static_cast(key.source), - kMaxActiveReaders); + key.participant_identity, static_cast(key.source), kMaxActiveReaders); return old_thread; } auto stream = VideoStream::fromTrack(track, callback.options); if (!stream) { - LK_LOG_ERROR("Failed to create VideoStream for {} source={}", - key.participant_identity, static_cast(key.source)); + LK_LOG_ERROR("Failed to create VideoStream for {} source={}", key.participant_identity, + static_cast(key.source)); return old_thread; } @@ -592,11 +575,10 @@ std::thread SubscriptionThreadDispatcher::startVideoReaderLocked( // Mirrors the audio reader: outer try/catch contains escapes from // stream->read, LK_LOG, etc. Residual diagnostic from spdlog's own // formatter is an unrelated logger-fault path and is suppressed. - reader.thread = std::thread([stream = std::move(stream), legacy_cb, event_cb, - participant_identity, source]() { + reader.thread = std::thread([stream = std::move(stream), legacy_cb, event_cb, participant_identity, source]() { try { - LK_LOG_DEBUG("Video reader thread started for participant={} source={}", - participant_identity, static_cast(source)); + LK_LOG_DEBUG("Video reader thread started for participant={} source={}", participant_identity, + static_cast(source)); VideoFrameEvent ev; while (stream->read(ev)) { try { @@ -605,25 +587,24 @@ std::thread SubscriptionThreadDispatcher::startVideoReaderLocked( } else if (legacy_cb) { legacy_cb(ev.frame, ev.timestamp_us); } - } catch (const std::exception &e) { + } catch (const std::exception& e) { LK_LOG_ERROR("Video frame callback exception: {}", e.what()); } } - LK_LOG_DEBUG("Video reader thread exiting for participant={} source={}", - participant_identity, static_cast(source)); - } catch (const std::exception &e) { - LK_LOG_ERROR("Video reader thread terminating due to exception: {}", - e.what()); + LK_LOG_DEBUG("Video reader thread exiting for participant={} source={}", participant_identity, + static_cast(source)); + } catch (const std::exception& e) { + LK_LOG_ERROR("Video reader thread terminating due to exception: {}", e.what()); } catch (...) { LK_LOG_ERROR("Video reader thread terminating due to unknown exception"); } }); // NOLINTEND(bugprone-lambda-function-name,bugprone-exception-escape) active_readers_[key] = std::move(reader); - LK_LOG_DEBUG("Started video reader for participant={} source={} " - "active_readers={}", - key.participant_identity, static_cast(key.source), - active_readers_.size()); + LK_LOG_DEBUG( + "Started video reader for participant={} source={} " + "active_readers={}", + key.participant_identity, static_cast(key.source), active_readers_.size()); return old_thread; } @@ -631,8 +612,7 @@ std::thread SubscriptionThreadDispatcher::startVideoReaderLocked( // Data track reader helpers // ------------------------------------------------------------------- -std::thread SubscriptionThreadDispatcher::extractDataReaderThreadLocked( - DataFrameCallbackId id) { +std::thread SubscriptionThreadDispatcher::extractDataReaderThreadLocked(DataFrameCallbackId id) { auto it = active_data_readers_.find(id); if (it == active_data_readers_.end()) { return {}; @@ -648,13 +628,10 @@ std::thread SubscriptionThreadDispatcher::extractDataReaderThreadLocked( return std::move(reader->thread); } -std::thread SubscriptionThreadDispatcher::extractDataReaderThreadLocked( - const DataCallbackKey &key) { - for (auto it = active_data_readers_.begin(); it != active_data_readers_.end(); - ++it) { +std::thread SubscriptionThreadDispatcher::extractDataReaderThreadLocked(const DataCallbackKey& key) { + for (auto it = active_data_readers_.begin(); it != active_data_readers_.end(); ++it) { if (it->second && it->second->remote_track && - it->second->remote_track->publisherIdentity() == - key.participant_identity && + it->second->remote_track->publisherIdentity() == key.participant_identity && it->second->remote_track->info().name == key.track_name) { auto reader = std::move(it->second); active_data_readers_.erase(it); @@ -670,23 +647,21 @@ std::thread SubscriptionThreadDispatcher::extractDataReaderThreadLocked( return {}; } -std::thread SubscriptionThreadDispatcher::startDataReaderLocked( - DataFrameCallbackId id, const DataCallbackKey &key, - const std::shared_ptr &track, - const DataFrameCallback &cb) { +std::thread SubscriptionThreadDispatcher::startDataReaderLocked(DataFrameCallbackId id, const DataCallbackKey& key, + const std::shared_ptr& track, + const DataFrameCallback& cb) { auto old_thread = extractDataReaderThreadLocked(id); - const int total_active = static_cast(active_readers_.size()) + - static_cast(active_data_readers_.size()); + const int total_active = static_cast(active_readers_.size()) + static_cast(active_data_readers_.size()); if (total_active >= kMaxActiveReaders) { - LK_LOG_ERROR("Cannot start data reader for {} track={}: active reader " - "limit ({}) reached", - key.participant_identity, key.track_name, kMaxActiveReaders); + LK_LOG_ERROR( + "Cannot start data reader for {} track={}: active reader " + "limit ({}) reached", + key.participant_identity, key.track_name, kMaxActiveReaders); return old_thread; } - LK_LOG_INFO("Starting data reader for \"{}\" track=\"{}\"", - key.participant_identity, key.track_name); + LK_LOG_INFO("Starting data reader for \"{}\" track=\"{}\"", key.participant_identity, key.track_name); auto reader = std::make_shared(); reader->remote_track = track; @@ -694,22 +669,19 @@ std::thread SubscriptionThreadDispatcher::startDataReaderLocked( auto track_name = key.track_name; // NOLINTBEGIN(bugprone-lambda-function-name) reader->thread = std::thread([reader, track, cb, identity, track_name]() { - LK_LOG_INFO("Data reader thread: subscribing to \"{}\" track=\"{}\"", - identity, track_name); + LK_LOG_INFO("Data reader thread: subscribing to \"{}\" track=\"{}\"", identity, track_name); std::shared_ptr stream; auto subscribe_result = track->subscribe(); if (!subscribe_result) { - const auto &error = subscribe_result.error(); + const auto& error = subscribe_result.error(); LK_LOG_ERROR( "Failed to subscribe to data track \"{}\" from \"{}\": code={} " "message={}", - track_name, identity, static_cast(error.code), - error.message); + track_name, identity, static_cast(error.code), error.message); return; } stream = subscribe_result.value(); - LK_LOG_INFO("Data reader thread: subscribed to \"{}\" track=\"{}\"", - identity, track_name); + LK_LOG_INFO("Data reader thread: subscribed to \"{}\" track=\"{}\"", identity, track_name); { const std::scoped_lock guard(reader->sub_mutex); @@ -720,12 +692,11 @@ std::thread SubscriptionThreadDispatcher::startDataReaderLocked( while (stream->read(frame)) { try { cb(frame.payload, frame.user_timestamp); - } catch (const std::exception &e) { + } catch (const std::exception& e) { LK_LOG_ERROR("Data frame callback exception: {}", e.what()); } } - LK_LOG_INFO("Data reader thread exiting for \"{}\" track=\"{}\"", identity, - track_name); + LK_LOG_INFO("Data reader thread exiting for \"{}\" track=\"{}\"", identity, track_name); }); // NOLINTEND(bugprone-lambda-function-name) active_data_readers_[id] = reader; diff --git a/src/tests/benchmark/benchmark_utils.cpp b/src/tests/benchmark/benchmark_utils.cpp index b5340ef8..febfcf4d 100644 --- a/src/tests/benchmark/benchmark_utils.cpp +++ b/src/tests/benchmark/benchmark_utils.cpp @@ -35,7 +35,7 @@ namespace { // Note: This is a minimal parser for Chrome trace format, not a general JSON // parser -std::string readFile(const std::string &path) { +std::string readFile(const std::string& path) { std::ifstream file(path); if (!file.is_open()) { return ""; @@ -46,14 +46,14 @@ std::string readFile(const std::string &path) { } // Skip whitespace -void skipWhitespace(const std::string &json, size_t &pos) { +void skipWhitespace(const std::string& json, size_t& pos) { while (pos < json.size() && std::isspace(json[pos])) { pos++; } } // Parse a JSON string (assumes pos is at opening quote) -std::string parseString(const std::string &json, size_t &pos) { +std::string parseString(const std::string& json, size_t& pos) { if (pos >= json.size() || json[pos] != '"') { return ""; } @@ -64,24 +64,24 @@ std::string parseString(const std::string &json, size_t &pos) { if (json[pos] == '\\' && pos + 1 < json.size()) { pos++; switch (json[pos]) { - case '"': - result += '"'; - break; - case '\\': - result += '\\'; - break; - case 'n': - result += '\n'; - break; - case 't': - result += '\t'; - break; - case 'r': - result += '\r'; - break; - default: - result += json[pos]; - break; + case '"': + result += '"'; + break; + case '\\': + result += '\\'; + break; + case 'n': + result += '\n'; + break; + case 't': + result += '\t'; + break; + case 'r': + result += '\r'; + break; + default: + result += json[pos]; + break; } } else { result += json[pos]; @@ -95,14 +95,13 @@ std::string parseString(const std::string &json, size_t &pos) { } // Parse a JSON number -double parseNumber(const std::string &json, size_t &pos) { +double parseNumber(const std::string& json, size_t& pos) { size_t start = pos; if (pos < json.size() && (json[pos] == '-' || json[pos] == '+')) { pos++; } - while (pos < json.size() && - (std::isdigit(json[pos]) || json[pos] == '.' || json[pos] == 'e' || - json[pos] == 'E' || json[pos] == '-' || json[pos] == '+')) { + while (pos < json.size() && (std::isdigit(json[pos]) || json[pos] == '.' || json[pos] == 'e' || json[pos] == 'E' || + json[pos] == '-' || json[pos] == '+')) { pos++; } if (pos == start) { @@ -116,7 +115,7 @@ double parseNumber(const std::string &json, size_t &pos) { } // Parse a trace event object -TraceEvent parseTraceEvent(const std::string &json, size_t &pos) { +TraceEvent parseTraceEvent(const std::string& json, size_t& pos) { TraceEvent event; skipWhitespace(json, pos); @@ -202,8 +201,7 @@ TraceEvent parseTraceEvent(const std::string &json, size_t &pos) { // Determine value type if (json[pos] == '"') { event.string_args[arg_key] = parseString(json, pos); - } else if (json[pos] == '-' || json[pos] == '+' || - std::isdigit(json[pos])) { + } else if (json[pos] == '-' || json[pos] == '+' || std::isdigit(json[pos])) { event.double_args[arg_key] = parseNumber(json, pos); } else { // Skip unknown value types (booleans, nulls, nested objects) @@ -212,8 +210,7 @@ TraceEvent parseTraceEvent(const std::string &json, size_t &pos) { if (json[pos] == '{' || json[pos] == '[') depth++; else if (json[pos] == '}' || json[pos] == ']') { - if (depth == 0) - break; + if (depth == 0) break; depth--; } else if (json[pos] == ',' && depth == 0) break; @@ -236,8 +233,7 @@ TraceEvent parseTraceEvent(const std::string &json, size_t &pos) { depth--; pos++; } - } else if (pos < json.size() && (json[pos] == '-' || json[pos] == '+' || - std::isdigit(json[pos]))) { + } else if (pos < json.size() && (json[pos] == '-' || json[pos] == '+' || std::isdigit(json[pos]))) { parseNumber(json, pos); } else { // Skip boolean/null @@ -252,18 +248,16 @@ TraceEvent parseTraceEvent(const std::string &json, size_t &pos) { } // Calculate percentile from sorted values -double percentile(const std::vector &sorted, double p) { - if (sorted.empty()) - return 0.0; +double percentile(const std::vector& sorted, double p) { + if (sorted.empty()) return 0.0; size_t idx = static_cast(sorted.size() * p / 100.0); - if (idx >= sorted.size()) - idx = sorted.size() - 1; + if (idx >= sorted.size()) idx = sorted.size() - 1; return sorted[idx]; } } // namespace -std::vector loadTraceFile(const std::string &trace_file_path) { +std::vector loadTraceFile(const std::string& trace_file_path) { std::vector events; std::string json = readFile(trace_file_path); @@ -304,8 +298,7 @@ std::vector loadTraceFile(const std::string &trace_file_path) { return events; } -std::vector calculateDurations(const std::vector &events, - const std::string &name) { +std::vector calculateDurations(const std::vector& events, const std::string& name) { std::vector durations; // For scoped events (B/E): track by thread ID @@ -314,7 +307,7 @@ std::vector calculateDurations(const std::vector &events, // For async events (S/F): track by event ID std::unordered_map async_begin_times; - for (const auto &event : events) { + for (const auto& event : events) { if (event.name != name) { continue; } @@ -326,8 +319,7 @@ std::vector calculateDurations(const std::vector &events, // Scoped end - match by thread ID auto it = scoped_begin_times.find(event.tid); if (it != scoped_begin_times.end()) { - double duration_ms = - static_cast(event.timestamp_us - it->second) / 1000.0; + double duration_ms = static_cast(event.timestamp_us - it->second) / 1000.0; durations.push_back(duration_ms); scoped_begin_times.erase(it); } @@ -338,8 +330,7 @@ std::vector calculateDurations(const std::vector &events, // Async finish - match by event ID auto it = async_begin_times.find(event.id); if (it != async_begin_times.end()) { - double duration_ms = - static_cast(event.timestamp_us - it->second) / 1000.0; + double duration_ms = static_cast(event.timestamp_us - it->second) / 1000.0; durations.push_back(duration_ms); async_begin_times.erase(it); } @@ -349,8 +340,7 @@ std::vector calculateDurations(const std::vector &events, return durations; } -BenchmarkStats calculateStats(const std::string &name, - const std::vector &durations) { +BenchmarkStats calculateStats(const std::string& name, const std::vector& durations) { BenchmarkStats stats; stats.name = name; stats.count = durations.size(); @@ -375,21 +365,19 @@ BenchmarkStats calculateStats(const std::string &name, return stats; } -BenchmarkStats analyzeTraceFile(const std::string &trace_file_path, - const std::string &event_name) { +BenchmarkStats analyzeTraceFile(const std::string& trace_file_path, const std::string& event_name) { auto events = loadTraceFile(trace_file_path); auto durations = calculateDurations(events, event_name); return calculateStats(event_name, durations); } -std::map -analyzeTraceFile(const std::string &trace_file_path, - const std::vector &event_names) { +std::map analyzeTraceFile(const std::string& trace_file_path, + const std::vector& event_names) { std::map results; auto events = loadTraceFile(trace_file_path); - for (const auto &name : event_names) { + for (const auto& name : event_names) { auto durations = calculateDurations(events, name); results[name] = calculateStats(name, durations); } @@ -397,7 +385,7 @@ analyzeTraceFile(const std::string &trace_file_path, return results; } -void printStats(const BenchmarkStats &stats) { +void printStats(const BenchmarkStats& stats) { std::cout << "\n========================================" << std::endl; std::cout << " " << stats.name << std::endl; std::cout << "========================================" << std::endl; @@ -418,40 +406,37 @@ void printStats(const BenchmarkStats &stats) { std::cout << "========================================\n" << std::endl; } -void printComparisonTable(const std::vector &results) { +void printComparisonTable(const std::vector& results) { if (results.empty()) { return; } // Find max name length for formatting size_t max_name_len = 10; - for (const auto &stats : results) { + for (const auto& stats : results) { max_name_len = std::max(max_name_len, stats.name.size()); } // Print header std::cout << "\n" - << std::left << std::setw(max_name_len + 2) << "Metric" - << std::right << std::setw(8) << "Count" << std::setw(10) << "Min" - << std::setw(10) << "Avg" << std::setw(10) << "P50" << std::setw(10) - << "P95" << std::setw(10) << "P99" << std::setw(10) << "Max" - << std::endl; + << std::left << std::setw(max_name_len + 2) << "Metric" << std::right << std::setw(8) << "Count" + << std::setw(10) << "Min" << std::setw(10) << "Avg" << std::setw(10) << "P50" << std::setw(10) << "P95" + << std::setw(10) << "P99" << std::setw(10) << "Max" << std::endl; std::cout << std::string(max_name_len + 2 + 68, '-') << std::endl; // Print rows std::cout << std::fixed << std::setprecision(1); - for (const auto &stats : results) { - std::cout << std::left << std::setw(max_name_len + 2) << stats.name - << std::right << std::setw(8) << stats.count << std::setw(10) - << stats.min_ms << std::setw(10) << stats.avg_ms << std::setw(10) - << stats.p50_ms << std::setw(10) << stats.p95_ms << std::setw(10) - << stats.p99_ms << std::setw(10) << stats.max_ms << std::endl; + for (const auto& stats : results) { + std::cout << std::left << std::setw(max_name_len + 2) << stats.name << std::right << std::setw(8) << stats.count + << std::setw(10) << stats.min_ms << std::setw(10) << stats.avg_ms << std::setw(10) << stats.p50_ms + << std::setw(10) << stats.p95_ms << std::setw(10) << stats.p99_ms << std::setw(10) << stats.max_ms + << std::endl; } std::cout << std::endl; } -std::string exportToCsv(const std::vector &results) { +std::string exportToCsv(const std::vector& results) { std::ostringstream csv; // Header @@ -459,10 +444,9 @@ std::string exportToCsv(const std::vector &results) { // Data rows csv << std::fixed << std::setprecision(3); - for (const auto &stats : results) { - csv << stats.name << "," << stats.count << "," << stats.min_ms << "," - << stats.avg_ms << "," << stats.p50_ms << "," << stats.p95_ms << "," - << stats.p99_ms << "," << stats.max_ms << "\n"; + for (const auto& stats : results) { + csv << stats.name << "," << stats.count << "," << stats.min_ms << "," << stats.avg_ms << "," << stats.p50_ms << "," + << stats.p95_ms << "," << stats.p99_ms << "," << stats.max_ms << "\n"; } return csv.str(); diff --git a/src/tests/benchmark/benchmark_utils.h b/src/tests/benchmark/benchmark_utils.h index f12328bb..c2912160 100644 --- a/src/tests/benchmark/benchmark_utils.h +++ b/src/tests/benchmark/benchmark_utils.h @@ -36,10 +36,10 @@ namespace benchmark { // Trace Categories (for documentation/organization) // ============================================================================= -constexpr const char *kCategoryConnect = "livekit.benchmark.connect"; -constexpr const char *kCategoryRpc = "livekit.benchmark.rpc"; -constexpr const char *kCategoryAudio = "livekit.benchmark.audio"; -constexpr const char *kCategoryDataChannel = "livekit.benchmark.datachannel"; +constexpr const char* kCategoryConnect = "livekit.benchmark.connect"; +constexpr const char* kCategoryRpc = "livekit.benchmark.rpc"; +constexpr const char* kCategoryAudio = "livekit.benchmark.audio"; +constexpr const char* kCategoryDataChannel = "livekit.benchmark.datachannel"; // ============================================================================= // Trace Event Structure (for parsing trace files) @@ -49,8 +49,8 @@ constexpr const char *kCategoryDataChannel = "livekit.benchmark.datachannel"; * Represents a single trace event parsed from a trace file. */ struct TraceEvent { - char phase = 0; // 'B' = begin, 'E' = end, 'S' = async start, 'F' = async - // finish, 'I' = instant + char phase = 0; // 'B' = begin, 'E' = end, 'S' = async start, 'F' = async + // finish, 'I' = instant std::string category; // Event category std::string name; // Event name uint64_t id = 0; // Event ID (for async event pairing) @@ -91,7 +91,7 @@ struct BenchmarkStats { * @param trace_file_path Path to the trace JSON file * @return Vector of parsed trace events, empty if file cannot be read */ -std::vector loadTraceFile(const std::string &trace_file_path); +std::vector loadTraceFile(const std::string& trace_file_path); /** * Calculate durations for paired begin/end or async start/finish events. @@ -104,8 +104,7 @@ std::vector loadTraceFile(const std::string &trace_file_path); * @param name Event name to match (category is ignored, matches by name only) * @return Vector of durations in milliseconds */ -std::vector calculateDurations(const std::vector &events, - const std::string &name); +std::vector calculateDurations(const std::vector& events, const std::string& name); /** * Calculate statistics from duration measurements. @@ -114,8 +113,7 @@ std::vector calculateDurations(const std::vector &events, * @param durations Vector of duration measurements in milliseconds * @return Calculated statistics */ -BenchmarkStats calculateStats(const std::string &name, - const std::vector &durations); +BenchmarkStats calculateStats(const std::string& name, const std::vector& durations); /** * Analyze a trace file and calculate statistics for a specific event. @@ -127,8 +125,7 @@ BenchmarkStats calculateStats(const std::string &name, * @param event_name Event name to analyze * @return Statistics for the event, or empty stats if not found */ -BenchmarkStats analyzeTraceFile(const std::string &trace_file_path, - const std::string &event_name); +BenchmarkStats analyzeTraceFile(const std::string& trace_file_path, const std::string& event_name); /** * Analyze a trace file and calculate statistics for multiple events. @@ -137,9 +134,8 @@ BenchmarkStats analyzeTraceFile(const std::string &trace_file_path, * @param event_names List of event names to analyze * @return Map of event name to statistics */ -std::map -analyzeTraceFile(const std::string &trace_file_path, - const std::vector &event_names); +std::map analyzeTraceFile(const std::string& trace_file_path, + const std::vector& event_names); // ============================================================================= // Output Functions @@ -150,14 +146,14 @@ analyzeTraceFile(const std::string &trace_file_path, * * @param stats Statistics to print */ -void printStats(const BenchmarkStats &stats); +void printStats(const BenchmarkStats& stats); /** * Print a comparison table of multiple benchmark results. * * @param results Vector of benchmark statistics to compare */ -void printComparisonTable(const std::vector &results); +void printComparisonTable(const std::vector& results); /** * Export benchmark results to CSV format. @@ -165,7 +161,7 @@ void printComparisonTable(const std::vector &results); * @param results Vector of benchmark statistics * @return CSV formatted string */ -std::string exportToCsv(const std::vector &results); +std::string exportToCsv(const std::vector& results); // ============================================================================= // Benchmark Session (wraps tracing lifecycle) @@ -182,8 +178,7 @@ std::string exportToCsv(const std::vector &results); */ class BenchmarkSession { public: - BenchmarkSession(const std::string &name, - const std::vector &categories = {}) + BenchmarkSession(const std::string& name, const std::vector& categories = {}) : name_(name), categories_(categories) { if (categories_.empty()) { categories_ = {"livekit", "livekit.*"}; @@ -196,32 +191,26 @@ class BenchmarkSession { } } - void start(const std::string &trace_filename = "") { - if (running_) - return; - trace_filename_ = - trace_filename.empty() ? (name_ + "_trace.json") : trace_filename; + void start(const std::string& trace_filename = "") { + if (running_) return; + trace_filename_ = trace_filename.empty() ? (name_ + "_trace.json") : trace_filename; livekit::startTracing(trace_filename_, categories_); running_ = true; - std::cout << "\n=== Benchmark Session Started: " << name_ - << " ===" << std::endl; + std::cout << "\n=== Benchmark Session Started: " << name_ << " ===" << std::endl; } void stop() { - if (!running_) - return; + if (!running_) return; livekit::stopTracing(); running_ = false; - std::cout << "=== Benchmark Session Stopped: " << name_ - << " ===" << std::endl; + std::cout << "=== Benchmark Session Stopped: " << name_ << " ===" << std::endl; std::cout << "Trace saved to: " << trace_filename_ << std::endl; - std::cout << "View in Chrome: chrome://tracing or https://ui.perfetto.dev" - << std::endl; + std::cout << "View in Chrome: chrome://tracing or https://ui.perfetto.dev" << std::endl; } bool isRunning() const { return running_; } - const std::string &name() const { return name_; } - const std::string &traceFilename() const { return trace_filename_; } + const std::string& name() const { return name_; } + const std::string& traceFilename() const { return trace_filename_; } private: std::string name_; @@ -240,9 +229,7 @@ class BenchmarkSession { */ class TracedStressStats { public: - TracedStressStats(const std::string &name, - const char *category = kCategoryRpc) - : name_(name), category_(category) {} + TracedStressStats(const std::string& name, const char* category = kCategoryRpc) : name_(name), category_(category) {} void recordCall(bool success, double latency_ms, size_t payload_size = 0) { std::lock_guard lock(mutex_); @@ -256,7 +243,7 @@ class TracedStressStats { } } - void recordError(const std::string &error_type) { + void recordError(const std::string& error_type) { std::lock_guard lock(mutex_); error_counts_[error_type]++; } @@ -306,11 +293,9 @@ class TracedStressStats { std::cout << "Successful: " << successful_calls_ << std::endl; std::cout << "Failed: " << failed_calls_ << std::endl; std::cout << "Success rate: " << std::fixed << std::setprecision(2) - << (total_calls_ > 0 ? (100.0 * successful_calls_ / total_calls_) - : 0.0) - << "%" << std::endl; - std::cout << "Total bytes: " << total_bytes_ << " (" - << (total_bytes_ / (1024.0 * 1024.0)) << " MB)" << std::endl; + << (total_calls_ > 0 ? (100.0 * successful_calls_ / total_calls_) : 0.0) << "%" << std::endl; + std::cout << "Total bytes: " << total_bytes_ << " (" << (total_bytes_ / (1024.0 * 1024.0)) << " MB)" + << std::endl; if (!latencies_.empty()) { std::vector sorted = latencies_; @@ -321,8 +306,7 @@ class TracedStressStats { auto percentile = [&sorted](double p) { size_t idx = static_cast(sorted.size() * p / 100.0); - if (idx >= sorted.size()) - idx = sorted.size() - 1; + if (idx >= sorted.size()) idx = sorted.size() - 1; return sorted[idx]; }; @@ -337,7 +321,7 @@ class TracedStressStats { if (!error_counts_.empty()) { std::cout << "\nError breakdown:" << std::endl; - for (const auto &pair : error_counts_) { + for (const auto& pair : error_counts_) { std::cout << " " << pair.first << ": " << pair.second << std::endl; } } @@ -357,7 +341,7 @@ class TracedStressStats { private: std::string name_; - const char *category_; + const char* category_; mutable std::mutex mutex_; int total_calls_ = 0; diff --git a/src/tests/common/audio_utils.h b/src/tests/common/audio_utils.h index 0f85547b..f1a51a4b 100644 --- a/src/tests/common/audio_utils.h +++ b/src/tests/common/audio_utils.h @@ -16,11 +16,12 @@ #pragma once +#include + #include #include #include #include -#include #include #include @@ -30,8 +31,7 @@ namespace livekit::test { constexpr int kDefaultAudioSampleRate = 48000; constexpr int kDefaultAudioChannels = 1; constexpr int kDefaultAudioFrameMs = 10; -constexpr int kDefaultSamplesPerChannel = - kDefaultAudioSampleRate * kDefaultAudioFrameMs / 1000; +constexpr int kDefaultSamplesPerChannel = kDefaultAudioSampleRate * kDefaultAudioFrameMs / 1000; /// Create an AudioFrame with the given parameters for a 10ms duration. /// @param sample_rate Sample rate in Hz. @@ -50,34 +50,26 @@ inline AudioFrame create10msFrame(int sample_rate, int num_channels) { /// @param sample_rate Audio sample rate (default: 48000). /// @param num_channels Number of audio channels (default: 1). /// @param frame_ms Duration of each frame in milliseconds (default: 10). -inline void runToneLoop(const std::shared_ptr &source, - std::atomic &running, double base_freq_hz, - bool siren_mode, - int sample_rate = kDefaultAudioSampleRate, - int num_channels = kDefaultAudioChannels, - int frame_ms = kDefaultAudioFrameMs) { +inline void runToneLoop(const std::shared_ptr& source, std::atomic& running, double base_freq_hz, + bool siren_mode, int sample_rate = kDefaultAudioSampleRate, + int num_channels = kDefaultAudioChannels, int frame_ms = kDefaultAudioFrameMs) { double phase = 0.0; constexpr double kTwoPi = 6.283185307179586; const int samples_per_channel = sample_rate * frame_ms / 1000; while (running.load(std::memory_order_relaxed)) { - AudioFrame frame = - AudioFrame::create(sample_rate, num_channels, samples_per_channel); - auto &samples = frame.data(); + AudioFrame frame = AudioFrame::create(sample_rate, num_channels, samples_per_channel); + auto& samples = frame.data(); - const double time_sec = - static_cast( - std::chrono::duration_cast( - std::chrono::steady_clock::now().time_since_epoch()) - .count()) / - 1000.0; - const double freq = - siren_mode ? (700.0 + 250.0 * std::sin(time_sec * 2.0)) : base_freq_hz; + const double time_sec = static_cast(std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()) + .count()) / + 1000.0; + const double freq = siren_mode ? (700.0 + 250.0 * std::sin(time_sec * 2.0)) : base_freq_hz; const double phase_inc = kTwoPi * freq / static_cast(sample_rate); for (int i = 0; i < samples_per_channel; ++i) { - samples[static_cast(i)] = - static_cast(std::sin(phase) * 12000.0); + samples[static_cast(i)] = static_cast(std::sin(phase) * 12000.0); phase += phase_inc; if (phase > kTwoPi) { phase -= kTwoPi; @@ -100,10 +92,10 @@ inline void runToneLoop(const std::shared_ptr &source, /// @param sample_rate Sample rate in Hz. /// @param phase Reference to phase accumulator (updated after call). /// @param amplitude Amplitude of the tone (default: 12000). -inline void fillToneFrame(AudioFrame &frame, double freq_hz, int sample_rate, - double &phase, double amplitude = 12000.0) { +inline void fillToneFrame(AudioFrame& frame, double freq_hz, int sample_rate, double& phase, + double amplitude = 12000.0) { constexpr double kTwoPi = 6.283185307179586; - auto &samples = frame.data(); + auto& samples = frame.data(); const double phase_inc = kTwoPi * freq_hz / static_cast(sample_rate); for (std::size_t i = 0; i < samples.size(); ++i) { diff --git a/src/tests/common/test_common.h b/src/tests/common/test_common.h index 4dcd88d6..ec5ac7c2 100644 --- a/src/tests/common/test_common.h +++ b/src/tests/common/test_common.h @@ -16,15 +16,16 @@ #pragma once +#include +#include + #include #include #include #include #include -#include #include #include -#include #include #include #include @@ -88,12 +89,12 @@ struct TestConfig { static TestConfig fromEnv() { TestConfig config; - const char *url = std::getenv("LIVEKIT_URL"); - const char *token_a = std::getenv("LIVEKIT_TOKEN_A"); - const char *token_b = std::getenv("LIVEKIT_TOKEN_B"); - const char *iterations_env = std::getenv("TEST_ITERATIONS"); - const char *duration_env = std::getenv("STRESS_DURATION_SECONDS"); - const char *threads_env = std::getenv("STRESS_CALLER_THREADS"); + const char* url = std::getenv("LIVEKIT_URL"); + const char* token_a = std::getenv("LIVEKIT_TOKEN_A"); + const char* token_b = std::getenv("LIVEKIT_TOKEN_B"); + const char* iterations_env = std::getenv("TEST_ITERATIONS"); + const char* duration_env = std::getenv("STRESS_DURATION_SECONDS"); + const char* threads_env = std::getenv("STRESS_CALLER_THREADS"); if (url && token_a && token_b) { config.url = url; @@ -102,10 +103,8 @@ struct TestConfig { config.available = true; } - config.test_iterations = - iterations_env ? std::atoi(iterations_env) : kDefaultTestIterations; - config.stress_duration_seconds = - duration_env ? std::atoi(duration_env) : kDefaultStressDurationSeconds; + config.test_iterations = iterations_env ? std::atoi(iterations_env) : kDefaultTestIterations; + config.stress_duration_seconds = duration_env ? std::atoi(duration_env) : kDefaultStressDurationSeconds; config.num_caller_threads = threads_env ? std::atoi(threads_env) : 4; return config; @@ -114,7 +113,7 @@ struct TestConfig { struct TestRoomConnectionOptions { RoomOptions room_options; - RoomDelegate *delegate = nullptr; + RoomDelegate* delegate = nullptr; }; // ============================================================================= @@ -123,14 +122,12 @@ struct TestRoomConnectionOptions { /// Get current timestamp in microseconds inline uint64_t getTimestampUs() { - return std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()) + return std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()) .count(); } /// Wait for a remote participant to appear in the room -inline bool waitForParticipant(Room *room, const std::string &identity, - std::chrono::milliseconds timeout) { +inline bool waitForParticipant(Room* room, const std::string& identity, std::chrono::milliseconds timeout) { auto start = std::chrono::steady_clock::now(); while (std::chrono::steady_clock::now() - start < timeout) { if (room->remoteParticipant(identity) != nullptr) { @@ -142,14 +139,14 @@ inline bool waitForParticipant(Room *room, const std::string &identity, } inline std::array getDataTrackTestTokens() { - const char *token_a = std::getenv("LIVEKIT_TOKEN_A"); + const char* token_a = std::getenv("LIVEKIT_TOKEN_A"); if (token_a == nullptr || std::string(token_a).empty()) { throw std::runtime_error( "LIVEKIT_TOKEN_A must be present and non-empty for data track E2E " "tests"); } - const char *token_b = std::getenv("LIVEKIT_TOKEN_B"); + const char* token_b = std::getenv("LIVEKIT_TOKEN_B"); if (token_b == nullptr || std::string(token_b).empty()) { throw std::runtime_error( "LIVEKIT_TOKEN_B must be present and non-empty for data track E2E " @@ -159,15 +156,13 @@ inline std::array getDataTrackTestTokens() { return {token_a, token_b}; } -inline void -waitForParticipantVisibility(const std::vector> &rooms, - std::chrono::milliseconds timeout = 5s) { +inline void waitForParticipantVisibility(const std::vector>& rooms, + std::chrono::milliseconds timeout = 5s) { std::vector participant_identities; participant_identities.reserve(rooms.size()); - for (const auto &room : rooms) { + for (const auto& room : rooms) { if (!room || room->localParticipant() == nullptr) { - throw std::runtime_error( - "Test room is missing a local participant after connect"); + throw std::runtime_error("Test room is missing a local participant after connect"); } participant_identities.push_back(room->localParticipant()->identity()); } @@ -176,10 +171,9 @@ waitForParticipantVisibility(const std::vector> &rooms, while (std::chrono::steady_clock::now() - start < timeout) { bool all_visible = true; for (size_t i = 0; i < rooms.size(); ++i) { - const auto &room = rooms[i]; + const auto& room = rooms[i]; if (!room || room->localParticipant() == nullptr) { - throw std::runtime_error( - "Test room is missing a local participant after connect"); + throw std::runtime_error("Test room is missing a local participant after connect"); } for (size_t j = 0; j < participant_identities.size(); ++j) { @@ -208,15 +202,15 @@ waitForParticipantVisibility(const std::vector> &rooms, throw std::runtime_error("Not all test participants became visible"); } -inline std::vector> -testRooms(const std::vector &room_configs) { +inline std::vector> testRooms(const std::vector& room_configs) { if (room_configs.empty()) { throw std::invalid_argument("testRooms requires at least one room"); } if (room_configs.size() > 2) { - throw std::invalid_argument("testRooms supports at most two rooms with " - "LIVEKIT_TOKEN_A/LIVEKIT_TOKEN_B"); + throw std::invalid_argument( + "testRooms supports at most two rooms with " + "LIVEKIT_TOKEN_A/LIVEKIT_TOKEN_B"); } auto tokens = getDataTrackTestTokens(); @@ -230,10 +224,8 @@ testRooms(const std::vector &room_configs) { room->setDelegate(room_configs[i].delegate); } - if (!room->Connect(kLocalTestLiveKitUrl, tokens[i], - room_configs[i].room_options)) { - throw std::runtime_error("Failed to connect test room " + - std::to_string(i)); + if (!room->Connect(kLocalTestLiveKitUrl, tokens[i], room_configs[i].room_options)) { + throw std::runtime_error("Failed to connect test room " + std::to_string(i)); } rooms.push_back(std::move(room)); @@ -263,7 +255,7 @@ class LatencyStats { measurements_.push_back(latency_ms); } - void printStats(const std::string &title) const { + void printStats(const std::string& title) const { std::lock_guard lock(mutex_); if (measurements_.empty()) { @@ -307,13 +299,10 @@ class LatencyStats { } private: - static double getPercentile(const std::vector &sorted, - int percentile) { - if (sorted.empty()) - return 0.0; + static double getPercentile(const std::vector& sorted, int percentile) { + if (sorted.empty()) return 0.0; size_t index = (sorted.size() * percentile) / 100; - if (index >= sorted.size()) - index = sorted.size() - 1; + if (index >= sorted.size()) index = sorted.size() - 1; return sorted[index]; } @@ -339,12 +328,12 @@ class StressTestStats { } } - void recordError(const std::string &error_type) { + void recordError(const std::string& error_type) { std::lock_guard lock(mutex_); error_counts_[error_type]++; } - void printStats(const std::string &title = "Stress Test Statistics") const { + void printStats(const std::string& title = "Stress Test Statistics") const { std::lock_guard lock(mutex_); std::cout << "\n========================================" << std::endl; @@ -354,18 +343,15 @@ class StressTestStats { std::cout << "Successful: " << successful_calls_ << std::endl; std::cout << "Failed: " << failed_calls_ << std::endl; std::cout << "Success rate: " << std::fixed << std::setprecision(2) - << (total_calls_ > 0 ? (100.0 * successful_calls_ / total_calls_) - : 0.0) - << "%" << std::endl; - std::cout << "Total bytes: " << total_bytes_ << " (" - << (total_bytes_ / (1024.0 * 1024.0)) << " MB)" << std::endl; + << (total_calls_ > 0 ? (100.0 * successful_calls_ / total_calls_) : 0.0) << "%" << std::endl; + std::cout << "Total bytes: " << total_bytes_ << " (" << (total_bytes_ / (1024.0 * 1024.0)) << " MB)" + << std::endl; if (!latencies_.empty()) { std::vector sorted_latencies = latencies_; std::sort(sorted_latencies.begin(), sorted_latencies.end()); - double sum = std::accumulate(sorted_latencies.begin(), - sorted_latencies.end(), 0.0); + double sum = std::accumulate(sorted_latencies.begin(), sorted_latencies.end(), 0.0); double avg = sum / sorted_latencies.size(); double min = sorted_latencies.front(); double max = sorted_latencies.back(); @@ -384,7 +370,7 @@ class StressTestStats { if (!error_counts_.empty()) { std::cout << "\nError breakdown:" << std::endl; - for (const auto &pair : error_counts_) { + for (const auto& pair : error_counts_) { std::cout << " " << pair.first << ": " << pair.second << std::endl; } } @@ -478,10 +464,9 @@ class LiveKitTestBase : public ::testing::Test { if (tracing_enabled_) { // Generate trace filename from test name: TestSuite_TestName_trace.json - const ::testing::TestInfo *test_info = - ::testing::UnitTest::GetInstance()->current_test_info(); - trace_filename_ = std::string(test_info->test_suite_name()) + "_" + - std::string(test_info->name()) + "_trace.json"; + const ::testing::TestInfo* test_info = ::testing::UnitTest::GetInstance()->current_test_info(); + trace_filename_ = + std::string(test_info->test_suite_name()) + "_" + std::string(test_info->name()) + "_trace.json"; // Start tracing with background file writer // Events are written to file asynchronously, so memory usage is bounded @@ -495,8 +480,7 @@ class LiveKitTestBase : public ::testing::Test { livekit::stopTracing(); std::cout << "\nTrace saved to: " << trace_filename_ << std::endl; - std::cout << "View in Chrome: chrome://tracing or https://ui.perfetto.dev" - << std::endl; + std::cout << "View in Chrome: chrome://tracing or https://ui.perfetto.dev" << std::endl; // Analyze the trace file and print statistics analyzeTraceFile(); @@ -508,8 +492,7 @@ class LiveKitTestBase : public ::testing::Test { /// Skip the test if the required environment variables are not set void skipIfNotConfigured() { if (!config_.available) { - GTEST_SKIP() - << "LIVEKIT_URL, LIVEKIT_TOKEN_A, and LIVEKIT_TOKEN_B not set"; + GTEST_SKIP() << "LIVEKIT_URL, LIVEKIT_TOKEN_A, and LIVEKIT_TOKEN_B not set"; } } @@ -524,9 +507,7 @@ class LiveKitTestBase : public ::testing::Test { * * @param name The event name to analyze (e.g., "audio_latency") */ - void addTraceEventToAnalyze(const std::string &name) { - custom_trace_events_.push_back(name); - } + void addTraceEventToAnalyze(const std::string& name) { custom_trace_events_.push_back(name); } TestConfig config_; bool tracing_enabled_ = false; @@ -541,21 +522,19 @@ class LiveKitTestBase : public ::testing::Test { */ void analyzeTraceFile() { // Build list of events to analyze - std::vector events_to_analyze = {"Room::Connect", - "FfiClient::initialize"}; + std::vector events_to_analyze = {"Room::Connect", "FfiClient::initialize"}; // Add custom events - for (const auto &name : custom_trace_events_) { + for (const auto& name : custom_trace_events_) { events_to_analyze.push_back(name); } // Analyze the trace file - auto results = - benchmark::analyzeTraceFile(trace_filename_, events_to_analyze); + auto results = benchmark::analyzeTraceFile(trace_filename_, events_to_analyze); // Print statistics for events that have data std::cout << "\n=== Trace Statistics ===" << std::endl; - for (const auto &[name, stats] : results) { + for (const auto& [name, stats] : results) { if (stats.count > 0) { benchmark::printStats(stats); } diff --git a/src/tests/common/video_utils.h b/src/tests/common/video_utils.h index 829168e9..9c9e6132 100644 --- a/src/tests/common/video_utils.h +++ b/src/tests/common/video_utils.h @@ -16,10 +16,11 @@ #pragma once +#include + #include #include #include -#include #include #include @@ -32,9 +33,9 @@ constexpr int kDefaultVideoHeight = 360; /// Fill a VideoFrame with webcam-like colors (green tint with varying blue). /// @param frame The VideoFrame to fill (must be ARGB format). /// @param frame_index Frame counter used to vary the blue channel. -inline void fillWebcamLikeFrame(VideoFrame &frame, std::uint64_t frame_index) { +inline void fillWebcamLikeFrame(VideoFrame& frame, std::uint64_t frame_index) { // ARGB layout: [A, R, G, B] - std::uint8_t *data = frame.data(); + std::uint8_t* data = frame.data(); const std::size_t size = frame.dataSize(); const std::uint8_t blue = static_cast((frame_index * 3) % 255); for (std::size_t i = 0; i < size; i += 4) { @@ -49,11 +50,9 @@ inline void fillWebcamLikeFrame(VideoFrame &frame, std::uint64_t frame_index) { /// @param frame The VideoFrame to fill (must be ARGB format). /// @param frame_index Frame counter encoded into metadata. /// @param timestamp_us Timestamp in microseconds encoded into metadata. -inline void fillRedFrameWithMetadata(VideoFrame &frame, - std::uint64_t frame_index, - std::uint64_t timestamp_us) { +inline void fillRedFrameWithMetadata(VideoFrame& frame, std::uint64_t frame_index, std::uint64_t timestamp_us) { // ARGB layout: [A, R, G, B] - std::uint8_t *data = frame.data(); + std::uint8_t* data = frame.data(); const std::size_t size = frame.dataSize(); for (std::size_t i = 0; i < size; i += 4) { data[i + 0] = 255; // A @@ -80,19 +79,17 @@ inline void fillRedFrameWithMetadata(VideoFrame &frame, } /// Wrapper for fillWebcamLikeFrame with timestamp parameter (ignored). -inline void fillWebcamWrapper(VideoFrame &frame, std::uint64_t frame_index, - std::uint64_t /*timestamp_us*/) { +inline void fillWebcamWrapper(VideoFrame& frame, std::uint64_t frame_index, std::uint64_t /*timestamp_us*/) { fillWebcamLikeFrame(frame, frame_index); } /// Wrapper for fillRedFrameWithMetadata. -inline void fillRedWrapper(VideoFrame &frame, std::uint64_t frame_index, - std::uint64_t timestamp_us) { +inline void fillRedWrapper(VideoFrame& frame, std::uint64_t frame_index, std::uint64_t timestamp_us) { fillRedFrameWithMetadata(frame, frame_index, timestamp_us); } /// Type alias for video frame fill functions. -using VideoFrameFillFn = void (*)(VideoFrame &, std::uint64_t, std::uint64_t); +using VideoFrameFillFn = void (*)(VideoFrame&, std::uint64_t, std::uint64_t); /// Run a video capture loop, calling the fill function for each frame. /// @param source The VideoSource to capture frames to. @@ -101,21 +98,17 @@ using VideoFrameFillFn = void (*)(VideoFrame &, std::uint64_t, std::uint64_t); /// @param width Video frame width (default: kDefaultVideoWidth). /// @param height Video frame height (default: kDefaultVideoHeight). /// @param frame_interval Frame interval (default: 33ms for ~30fps). -inline void runVideoLoop( - const std::shared_ptr &source, std::atomic &running, - VideoFrameFillFn fill_fn, int width = kDefaultVideoWidth, - int height = kDefaultVideoHeight, - std::chrono::milliseconds frame_interval = std::chrono::milliseconds(33)) { +inline void runVideoLoop(const std::shared_ptr& source, std::atomic& running, + VideoFrameFillFn fill_fn, int width = kDefaultVideoWidth, int height = kDefaultVideoHeight, + std::chrono::milliseconds frame_interval = std::chrono::milliseconds(33)) { VideoFrame frame = VideoFrame::create(width, height, VideoBufferType::ARGB); std::uint64_t frame_index = 0; while (running.load(std::memory_order_relaxed)) { const auto now = std::chrono::steady_clock::now().time_since_epoch(); - const auto ts_us = static_cast( - std::chrono::duration_cast(now).count()); + const auto ts_us = static_cast(std::chrono::duration_cast(now).count()); fill_fn(frame, frame_index, ts_us); try { - source->captureFrame(frame, static_cast(ts_us), - VideoRotation::VIDEO_ROTATION_0); + source->captureFrame(frame, static_cast(ts_us), VideoRotation::VIDEO_ROTATION_0); } catch (...) { break; } diff --git a/src/tests/integration/test_data_track.cpp b/src/tests/integration/test_data_track.cpp index db319c0f..2da8a1c3 100644 --- a/src/tests/integration/test_data_track.cpp +++ b/src/tests/integration/test_data_track.cpp @@ -21,20 +21,20 @@ // and run: // ./build-debug/bin/livekit_integration_tests -#include "../common/test_common.h" - -#include "ffi_client.h" -#include "lk_log.h" +#include +#include +#include #include #include #include #include -#include -#include -#include #include +#include "../common/test_common.h" +#include "ffi_client.h" +#include "lk_log.h" + namespace livekit::test { using namespace std::chrono_literals; @@ -52,23 +52,20 @@ constexpr char kE2EESharedSecret[] = "password"; constexpr int kE2EEFrameCount = 5; constexpr int kTimestampFrameAttempts = 200; -std::string makeTrackName(const std::string &suffix) { - return std::string(kTrackNamePrefix) + "_" + suffix + "_" + - std::to_string(getTimestampUs()); +std::string makeTrackName(const std::string& suffix) { + return std::string(kTrackNamePrefix) + "_" + suffix + "_" + std::to_string(getTimestampUs()); } std::vector e2eeSharedKey() { - return std::vector( - kE2EESharedSecret, kE2EESharedSecret + sizeof(kE2EESharedSecret) - 1); + return std::vector(kE2EESharedSecret, kE2EESharedSecret + sizeof(kE2EESharedSecret) - 1); } -std::size_t parseTestTrackIndex(const std::string &track_name) { +std::size_t parseTestTrackIndex(const std::string& track_name) { constexpr char kPrefix[] = "test_"; if (track_name.rfind(kPrefix, 0) != 0) { throw std::runtime_error("Unexpected test track name: " + track_name); } - return static_cast( - std::stoul(track_name.substr(sizeof(kPrefix) - 1))); + return static_cast(std::stoul(track_name.substr(sizeof(kPrefix) - 1))); } E2EEOptions makeE2EEOptions() { @@ -77,8 +74,7 @@ E2EEOptions makeE2EEOptions() { return options; } -std::vector -encryptedRoomConfigs(RoomDelegate *subscriber_delegate) { +std::vector encryptedRoomConfigs(RoomDelegate* subscriber_delegate) { std::vector room_configs(2); room_configs[0].room_options.encryption = makeE2EEOptions(); room_configs[1].room_options.encryption = makeE2EEOptions(); @@ -87,7 +83,7 @@ encryptedRoomConfigs(RoomDelegate *subscriber_delegate) { } template -bool waitForCondition(Predicate &&predicate, std::chrono::milliseconds timeout, +bool waitForCondition(Predicate&& predicate, std::chrono::milliseconds timeout, std::chrono::milliseconds interval = kPollingInterval) { auto start = std::chrono::steady_clock::now(); while (std::chrono::steady_clock::now() - start < timeout) { @@ -100,43 +96,35 @@ bool waitForCondition(Predicate &&predicate, std::chrono::milliseconds timeout, } template -std::string describeDataTrackError(const Error &error) { - return "code=" + std::to_string(static_cast(error.code)) + - " message=" + error.message; +std::string describeDataTrackError(const Error& error) { + return "code=" + std::to_string(static_cast(error.code)) + " message=" + error.message; } -std::shared_ptr -requirePublishedTrack(LocalParticipant *participant, const std::string &name) { +std::shared_ptr requirePublishedTrack(LocalParticipant* participant, const std::string& name) { auto result = participant->publishDataTrack(name); if (!result) { - throw std::runtime_error("Failed to publish data track: " + - describeDataTrackError(result.error())); + throw std::runtime_error("Failed to publish data track: " + describeDataTrackError(result.error())); } return result.value(); } -std::shared_ptr -requireSubscription(const std::shared_ptr &track) { +std::shared_ptr requireSubscription(const std::shared_ptr& track) { auto result = track->subscribe(); if (!result) { - throw std::runtime_error("Failed to subscribe to data track: " + - describeDataTrackError(result.error())); + throw std::runtime_error("Failed to subscribe to data track: " + describeDataTrackError(result.error())); } return result.value(); } -void requirePushSuccess(const Result &result, - const std::string &context) { +void requirePushSuccess(const Result& result, const std::string& context) { if (!result) { - throw std::runtime_error(context + ": " + - describeDataTrackError(result.error())); + throw std::runtime_error(context + ": " + describeDataTrackError(result.error())); } } class DataTrackPublishedDelegate : public RoomDelegate { public: - void onDataTrackPublished(Room &, - const DataTrackPublishedEvent &event) override { + void onDataTrackPublished(Room&, const DataTrackPublishedEvent& event) override { if (!event.track) { return; } @@ -146,8 +134,7 @@ class DataTrackPublishedDelegate : public RoomDelegate { cv_.notify_all(); } - std::shared_ptr - waitForTrack(std::chrono::milliseconds timeout) { + std::shared_ptr waitForTrack(std::chrono::milliseconds timeout) { std::unique_lock lock(mutex_); if (!cv_.wait_for(lock, timeout, [this] { return !tracks_.empty(); })) { return nullptr; @@ -155,15 +142,12 @@ class DataTrackPublishedDelegate : public RoomDelegate { return tracks_.front(); } - std::vector> - waitForTracks(std::size_t count, std::chrono::milliseconds timeout) { + std::vector> waitForTracks(std::size_t count, std::chrono::milliseconds timeout) { std::unique_lock lock(mutex_); - if (!cv_.wait_for(lock, timeout, - [this, count] { return tracks_.size() >= count; })) { + if (!cv_.wait_for(lock, timeout, [this, count] { return tracks_.size() >= count; })) { return {}; } - return {tracks_.begin(), - tracks_.begin() + static_cast(count)}; + return {tracks_.begin(), tracks_.begin() + static_cast(count)}; } private: @@ -172,14 +156,12 @@ class DataTrackPublishedDelegate : public RoomDelegate { std::vector> tracks_; }; -DataTrackFrame -readFrameWithTimeout(const std::shared_ptr &subscription, - std::chrono::milliseconds timeout) { +DataTrackFrame readFrameWithTimeout(const std::shared_ptr& subscription, + std::chrono::milliseconds timeout) { std::promise frame_promise; auto future = frame_promise.get_future(); - std::thread reader([subscription, - promise = std::move(frame_promise)]() mutable { + std::thread reader([subscription, promise = std::move(frame_promise)]() mutable { try { DataTrackFrame frame; if (!subscription->read(frame)) { @@ -203,9 +185,8 @@ readFrameWithTimeout(const std::shared_ptr &subscription, class DataTrackE2ETest : public LiveKitTestBase {}; -class DataTrackTransportTest - : public DataTrackE2ETest, - public ::testing::WithParamInterface> {}; +class DataTrackTransportTest : public DataTrackE2ETest, + public ::testing::WithParamInterface> {}; TEST_P(DataTrackTransportTest, PublishesAndReceivesFramesEndToEnd) { const auto publish_fps = std::get<0>(GetParam()); @@ -227,12 +208,10 @@ TEST_P(DataTrackTransportTest, PublishesAndReceivesFramesEndToEnd) { room_configs[1].delegate = &subscriber_delegate; auto rooms = testRooms(room_configs); - auto &publisher_room = rooms[0]; - const auto publisher_identity = - publisher_room->localParticipant()->identity(); + auto& publisher_room = rooms[0]; + const auto publisher_identity = publisher_room->localParticipant()->identity(); - auto track = - requirePublishedTrack(publisher_room->localParticipant(), track_name); + auto track = requirePublishedTrack(publisher_room->localParticipant(), track_name); std::cerr << "Track published\n"; auto remote_track = subscriber_delegate.waitForTrack(kTrackWaitTimeout); @@ -244,8 +223,8 @@ TEST_P(DataTrackTransportTest, PublishesAndReceivesFramesEndToEnd) { EXPECT_EQ(remote_track->info().name, track_name); EXPECT_EQ(remote_track->publisherIdentity(), publisher_identity); - const auto frame_count = static_cast(std::llround( - std::chrono::duration(PUBLISH_DURATION).count() * publish_fps)); + const auto frame_count = + static_cast(std::llround(std::chrono::duration(PUBLISH_DURATION).count() * publish_fps)); auto publish = [&]() { if (!track->isPublished()) { @@ -258,18 +237,14 @@ TEST_P(DataTrackTransportTest, PublishesAndReceivesFramesEndToEnd) { throw std::runtime_error("Published track name mismatch"); } - const auto frame_interval = - std::chrono::duration_cast( - std::chrono::duration(1.0 / publish_fps)); + const auto frame_interval = std::chrono::duration_cast( + std::chrono::duration(1.0 / publish_fps)); auto next_send = std::chrono::steady_clock::now(); - std::cout << "Publishing " << frame_count - << " frames with payload length " << payload_len << '\n'; + std::cout << "Publishing " << frame_count << " frames with payload length " << payload_len << '\n'; for (size_t index = 0; index < frame_count; ++index) { - std::vector payload(payload_len, - static_cast(index)); - requirePushSuccess(track->tryPush(std::move(payload)), - "Failed to push data frame"); + std::vector payload(payload_len, static_cast(index)); + requirePushSuccess(track->tryPush(std::move(payload)), "Failed to push data frame"); next_send += frame_interval; std::this_thread::sleep_until(next_send); @@ -297,14 +272,11 @@ TEST_P(DataTrackTransportTest, PublishesAndReceivesFramesEndToEnd) { const auto first_byte = frame.payload.front(); if (!std::all_of(frame.payload.begin(), frame.payload.end(), - [first_byte](std::uint8_t byte) { - return byte == first_byte; - })) { + [first_byte](std::uint8_t byte) { return byte == first_byte; })) { throw std::runtime_error("Received frame with inconsistent payload"); } if (frame.user_timestamp.has_value()) { - throw std::runtime_error( - "Received unexpected user timestamp in transport test"); + throw std::runtime_error("Received unexpected user timestamp in transport test"); } ++received_count; @@ -332,13 +304,11 @@ TEST_P(DataTrackTransportTest, PublishesAndReceivesFramesEndToEnd) { sub_fut.get(); const auto received_count = receive_count_future.get(); - const auto received_percent = - static_cast(received_count) / static_cast(frame_count); - std::cout << "Received " << received_count << "/" << frame_count - << " frames (" << received_percent * 100.0f << "%)" << '\n'; + const auto received_percent = static_cast(received_count) / static_cast(frame_count); + std::cout << "Received " << received_count << "/" << frame_count << " frames (" << received_percent * 100.0f << "%)" + << '\n'; - EXPECT_GE(received_percent, MIN_PERCENTAGE) - << "Received " << received_count << "/" << frame_count << " frames"; + EXPECT_GE(received_percent, MIN_PERCENTAGE) << "Received " << received_count << "/" << frame_count << " frames"; } TEST_F(DataTrackE2ETest, UnpublishUpdatesPublishedStateEndToEnd) { @@ -349,10 +319,9 @@ TEST_F(DataTrackE2ETest, UnpublishUpdatesPublishedStateEndToEnd) { room_configs[1].delegate = &subscriber_delegate; auto rooms = testRooms(room_configs); - auto &publisher_room = rooms[0]; + auto& publisher_room = rooms[0]; - auto publish_result = - publisher_room->localParticipant()->publishDataTrack(track_name); + auto publish_result = publisher_room->localParticipant()->publishDataTrack(track_name); if (!publish_result) { FAIL() << describeDataTrackError(publish_result.error()); } @@ -367,14 +336,13 @@ TEST_F(DataTrackE2ETest, UnpublishUpdatesPublishedStateEndToEnd) { local_track->unpublishDataTrack(); EXPECT_FALSE(local_track->isPublished()); - EXPECT_TRUE( - waitForCondition([&]() { return !remote_track->isPublished(); }, 2s)) + EXPECT_TRUE(waitForCondition([&]() { return !remote_track->isPublished(); }, 2s)) << "Remote track did not report unpublished state"; } TEST_F(DataTrackE2ETest, PublishManyTracks) { auto rooms = testRooms(1); - auto &room = rooms[0]; + auto& room = rooms[0]; std::vector> tracks; tracks.reserve(kPublishManyTrackCount); @@ -382,25 +350,20 @@ TEST_F(DataTrackE2ETest, PublishManyTracks) { const auto start = std::chrono::steady_clock::now(); for (int index = 0; index < kPublishManyTrackCount; ++index) { const auto track_name = "track_" + std::to_string(index); - auto publish_result = - room->localParticipant()->publishDataTrack(track_name); + auto publish_result = room->localParticipant()->publishDataTrack(track_name); if (!publish_result) { - FAIL() << "Failed to publish track " << track_name << ": " - << describeDataTrackError(publish_result.error()); + FAIL() << "Failed to publish track " << track_name << ": " << describeDataTrackError(publish_result.error()); } auto track = publish_result.value(); - EXPECT_TRUE(track->isPublished()) - << "Track was not published: " << track_name; + EXPECT_TRUE(track->isPublished()) << "Track was not published: " << track_name; EXPECT_EQ(track->info().name, track_name); tracks.push_back(std::move(track)); } const auto elapsed = std::chrono::steady_clock::now() - start; - std::cout - << "Publishing " << kPublishManyTrackCount << " tracks took " - << std::chrono::duration_cast(elapsed).count() - << " ms" << std::endl; + std::cout << "Publishing " << kPublishManyTrackCount << " tracks took " + << std::chrono::duration_cast(elapsed).count() << " ms" << std::endl; EXPECT_LT(elapsed, kPublishManyTimeout); // This test intentionally creates bursty data-track traffic by pushing a @@ -409,18 +372,16 @@ TEST_F(DataTrackE2ETest, PublishManyTracks) { // to make it onto the transport and "Failed to enqueue data track packet" // logs are expected. The purpose of this test is to verify publish/push // behavior and local track state, not end-to-end delivery of every packet. - for (const auto &track : tracks) { - auto push_result = track->tryPush( - std::vector(kLargeFramePayloadBytes, 0xFA)); + for (const auto& track : tracks) { + auto push_result = track->tryPush(std::vector(kLargeFramePayloadBytes, 0xFA)); if (!push_result) { - ADD_FAILURE() << "Failed to push large frame on track " - << track->info().name << ": " + ADD_FAILURE() << "Failed to push large frame on track " << track->info().name << ": " << describeDataTrackError(push_result.error()); } std::this_thread::sleep_for(50ms); } - for (const auto &track : tracks) { + for (const auto& track : tracks) { track->unpublishDataTrack(); EXPECT_FALSE(track->isPublished()); } @@ -428,7 +389,7 @@ TEST_F(DataTrackE2ETest, PublishManyTracks) { TEST_F(DataTrackE2ETest, PublishDuplicateName) { auto rooms = testRooms(1); - auto &room = rooms[0]; + auto& room = rooms[0]; auto first_track_result = room->localParticipant()->publishDataTrack("first"); if (!first_track_result) { @@ -438,10 +399,8 @@ TEST_F(DataTrackE2ETest, PublishDuplicateName) { ASSERT_TRUE(first_track->isPublished()); auto duplicate_result = room->localParticipant()->publishDataTrack("first"); - ASSERT_FALSE(duplicate_result) - << "Expected duplicate data-track name to be rejected"; - EXPECT_EQ(duplicate_result.error().code, - PublishDataTrackErrorCode::DUPLICATE_NAME); + ASSERT_FALSE(duplicate_result) << "Expected duplicate data-track name to be rejected"; + EXPECT_EQ(duplicate_result.error().code, PublishDataTrackErrorCode::DUPLICATE_NAME); EXPECT_FALSE(duplicate_result.error().message.empty()); first_track->unpublishDataTrack(); @@ -455,14 +414,13 @@ TEST_F(DataTrackE2ETest, CanResubscribeToRemoteDataTrack) { room_configs[1].delegate = &subscriber_delegate; auto rooms = testRooms(room_configs); - auto &publisher_room = rooms[0]; + auto& publisher_room = rooms[0]; std::atomic keep_publishing{true}; std::exception_ptr publish_error; std::thread publisher([&]() { try { - auto track = - requirePublishedTrack(publisher_room->localParticipant(), track_name); + auto track = requirePublishedTrack(publisher_room->localParticipant(), track_name); if (!track->isPublished()) { throw std::runtime_error("Publisher failed to publish data track"); } @@ -512,72 +470,60 @@ TEST_F(DataTrackE2ETest, FfiClientSubscribeDataTrackReturnsSyncResult) { room_configs[1].delegate = &subscriber_delegate; auto rooms = testRooms(room_configs); - auto &publisher_room = rooms[0]; + auto& publisher_room = rooms[0]; std::vector> local_tracks; local_tracks.reserve(kTopicCount); for (std::size_t idx = 0; idx < kTopicCount; ++idx) { const auto track_name = "test_" + std::to_string(idx); - auto publish_result = - publisher_room->localParticipant()->publishDataTrack(track_name); + auto publish_result = publisher_room->localParticipant()->publishDataTrack(track_name); if (!publish_result) { - FAIL() << "Failed to publish " << track_name << ": " - << describeDataTrackError(publish_result.error()); + FAIL() << "Failed to publish " << track_name << ": " << describeDataTrackError(publish_result.error()); } auto local_track = publish_result.value(); ASSERT_TRUE(local_track->isPublished()) << track_name; local_tracks.push_back(std::move(local_track)); } - auto remote_tracks = - subscriber_delegate.waitForTracks(kTopicCount, kTrackWaitTimeout); - ASSERT_EQ(remote_tracks.size(), kTopicCount) - << "Timed out waiting for all remote data tracks"; + auto remote_tracks = subscriber_delegate.waitForTracks(kTopicCount, kTrackWaitTimeout); + ASSERT_EQ(remote_tracks.size(), kTopicCount) << "Timed out waiting for all remote data tracks"; std::sort(remote_tracks.begin(), remote_tracks.end(), - [](const std::shared_ptr &lhs, - const std::shared_ptr &rhs) { - return parseTestTrackIndex(lhs->info().name) < - parseTestTrackIndex(rhs->info().name); + [](const std::shared_ptr& lhs, const std::shared_ptr& rhs) { + return parseTestTrackIndex(lhs->info().name) < parseTestTrackIndex(rhs->info().name); }); std::vector subscription_handles; subscription_handles.reserve(kTopicCount); for (std::size_t idx = 0; idx < remote_tracks.size(); ++idx) { - const auto &remote_track = remote_tracks[idx]; + const auto& remote_track = remote_tracks[idx]; const auto expected_name = "test_" + std::to_string(idx); ASSERT_NE(remote_track, nullptr); EXPECT_TRUE(remote_track->isPublished()) << expected_name; EXPECT_EQ(remote_track->info().name, expected_name); const auto subscribe_start = std::chrono::steady_clock::now(); - auto subscribe_result = FfiClient::instance().subscribeDataTrack( - static_cast(remote_track->testFfiHandleId())); - const auto subscribe_elapsed = - std::chrono::steady_clock::now() - subscribe_start; - const auto subscribe_elapsed_ns = - std::chrono::duration_cast(subscribe_elapsed) - .count(); - - std::cout << "FfiClient::subscribeDataTrack(" << expected_name - << ") completed in " << subscribe_elapsed_ns << " ns" + auto subscribe_result = + FfiClient::instance().subscribeDataTrack(static_cast(remote_track->testFfiHandleId())); + const auto subscribe_elapsed = std::chrono::steady_clock::now() - subscribe_start; + const auto subscribe_elapsed_ns = std::chrono::duration_cast(subscribe_elapsed).count(); + + std::cout << "FfiClient::subscribeDataTrack(" << expected_name << ") completed in " << subscribe_elapsed_ns << " ns" << std::endl; if (!subscribe_result) { - FAIL() << "Failed to subscribe to " << expected_name << ": " - << describeDataTrackError(subscribe_result.error()); + FAIL() << "Failed to subscribe to " << expected_name << ": " << describeDataTrackError(subscribe_result.error()); } - const auto subscription_handle_id = - static_cast(subscribe_result.value().handle().id()); + const auto subscription_handle_id = static_cast(subscribe_result.value().handle().id()); EXPECT_NE(subscription_handle_id, 0u) << expected_name; subscription_handles.emplace_back(subscription_handle_id); EXPECT_TRUE(subscription_handles.back().valid()) << expected_name; } - for (auto &local_track : local_tracks) { + for (auto& local_track : local_tracks) { local_track->unpublishDataTrack(); } } @@ -591,10 +537,9 @@ TEST_F(DataTrackE2ETest, PreservesUserTimestampEndToEnd) { room_configs[1].delegate = &subscriber_delegate; auto rooms = testRooms(room_configs); - auto &publisher_room = rooms[0]; + auto& publisher_room = rooms[0]; - auto publish_result = - publisher_room->localParticipant()->publishDataTrack(track_name); + auto publish_result = publisher_room->localParticipant()->publishDataTrack(track_name); if (!publish_result) { FAIL() << describeDataTrackError(publish_result.error()); } @@ -616,8 +561,7 @@ TEST_F(DataTrackE2ETest, PreservesUserTimestampEndToEnd) { try { DataTrackFrame frame; if (!subscription->read(frame)) { - throw std::runtime_error( - "Subscription ended before timestamped frame arrived"); + throw std::runtime_error("Subscription ended before timestamped frame arrived"); } frame_promise.set_value(std::move(frame)); } catch (...) { @@ -631,8 +575,7 @@ TEST_F(DataTrackE2ETest, PreservesUserTimestampEndToEnd) { // every frame and asserts every received frame carries that value. bool pushed = false; for (int attempt = 0; attempt < kTimestampFrameAttempts; ++attempt) { - auto push_result = local_track->tryPush( - std::vector(64, 0xFA), sent_timestamp); + auto push_result = local_track->tryPush(std::vector(64, 0xFA), sent_timestamp); pushed = static_cast(push_result) || pushed; if (frame_future.wait_for(25ms) == std::future_status::ready) { break; @@ -649,13 +592,12 @@ TEST_F(DataTrackE2ETest, PreservesUserTimestampEndToEnd) { local_track->unpublishDataTrack(); ASSERT_TRUE(pushed) << "Failed to push timestamped data frame"; - ASSERT_EQ(frame_status, std::future_status::ready) - << "Timed out waiting for timestamped frame"; + ASSERT_EQ(frame_status, std::future_status::ready) << "Timed out waiting for timestamped frame"; DataTrackFrame frame; try { frame = frame_future.get(); - } catch (const std::exception &e) { + } catch (const std::exception& e) { FAIL() << e.what(); } @@ -670,8 +612,8 @@ TEST_F(DataTrackE2ETest, PublishesAndReceivesEncryptedFramesEndToEnd) { DataTrackPublishedDelegate subscriber_delegate; auto room_configs = encryptedRoomConfigs(&subscriber_delegate); auto rooms = testRooms(room_configs); - auto &publisher_room = rooms[0]; - auto &subscriber_room = rooms[1]; + auto& publisher_room = rooms[0]; + auto& subscriber_room = rooms[1]; ASSERT_NE(publisher_room->e2eeManager(), nullptr); ASSERT_NE(subscriber_room->e2eeManager(), nullptr); @@ -679,13 +621,10 @@ TEST_F(DataTrackE2ETest, PublishesAndReceivesEncryptedFramesEndToEnd) { ASSERT_NE(subscriber_room->e2eeManager()->keyProvider(), nullptr); publisher_room->e2eeManager()->setEnabled(true); subscriber_room->e2eeManager()->setEnabled(true); - EXPECT_EQ(publisher_room->e2eeManager()->keyProvider()->exportSharedKey(), - e2eeSharedKey()); - EXPECT_EQ(subscriber_room->e2eeManager()->keyProvider()->exportSharedKey(), - e2eeSharedKey()); + EXPECT_EQ(publisher_room->e2eeManager()->keyProvider()->exportSharedKey(), e2eeSharedKey()); + EXPECT_EQ(subscriber_room->e2eeManager()->keyProvider()->exportSharedKey(), e2eeSharedKey()); - auto publish_result = - publisher_room->localParticipant()->publishDataTrack(track_name); + auto publish_result = publisher_room->localParticipant()->publishDataTrack(track_name); if (!publish_result) { FAIL() << describeDataTrackError(publish_result.error()); } @@ -711,8 +650,7 @@ TEST_F(DataTrackE2ETest, PublishesAndReceivesEncryptedFramesEndToEnd) { try { DataTrackFrame frame; if (!subscription->read(frame)) { - throw std::runtime_error( - "Subscription ended before an encrypted frame arrived"); + throw std::runtime_error("Subscription ended before an encrypted frame arrived"); } frame_promise.set_value(std::move(frame)); } catch (...) { @@ -722,8 +660,7 @@ TEST_F(DataTrackE2ETest, PublishesAndReceivesEncryptedFramesEndToEnd) { bool pushed = false; for (int index = 0; index < 200; ++index) { - std::vector payload(kLargeFramePayloadBytes, - static_cast(index + 1)); + std::vector payload(kLargeFramePayloadBytes, static_cast(index + 1)); auto push_result = local_track->tryPush(std::move(payload)); pushed = static_cast(push_result) || pushed; if (frame_future.wait_for(25ms) == std::future_status::ready) { @@ -737,23 +674,20 @@ TEST_F(DataTrackE2ETest, PublishesAndReceivesEncryptedFramesEndToEnd) { } reader.join(); ASSERT_TRUE(pushed) << "Failed to push encrypted data frames"; - ASSERT_EQ(frame_status, std::future_status::ready) - << "Timed out waiting for encrypted frame delivery"; + ASSERT_EQ(frame_status, std::future_status::ready) << "Timed out waiting for encrypted frame delivery"; DataTrackFrame frame; try { frame = frame_future.get(); - } catch (const std::exception &e) { + } catch (const std::exception& e) { FAIL() << e.what(); } ASSERT_FALSE(frame.payload.empty()); const auto first_byte = frame.payload.front(); - EXPECT_TRUE(std::all_of( - frame.payload.begin(), frame.payload.end(), - [first_byte](std::uint8_t byte) { return byte == first_byte; })) - << "Encrypted payload is not byte-consistent"; - EXPECT_FALSE(frame.user_timestamp.has_value()) - << "Unexpected user timestamp on encrypted frame"; + EXPECT_TRUE(std::all_of(frame.payload.begin(), frame.payload.end(), [first_byte](std::uint8_t byte) { + return byte == first_byte; + })) << "Encrypted payload is not byte-consistent"; + EXPECT_FALSE(frame.user_timestamp.has_value()) << "Unexpected user timestamp on encrypted frame"; subscription->close(); local_track->unpublishDataTrack(); @@ -767,16 +701,15 @@ TEST_F(DataTrackE2ETest, PreservesUserTimestampOnEncryptedDataTrack) { DataTrackPublishedDelegate subscriber_delegate; auto room_configs = encryptedRoomConfigs(&subscriber_delegate); auto rooms = testRooms(room_configs); - auto &publisher_room = rooms[0]; - auto &subscriber_room = rooms[1]; + auto& publisher_room = rooms[0]; + auto& subscriber_room = rooms[1]; ASSERT_NE(publisher_room->e2eeManager(), nullptr); ASSERT_NE(subscriber_room->e2eeManager(), nullptr); publisher_room->e2eeManager()->setEnabled(true); subscriber_room->e2eeManager()->setEnabled(true); - auto publish_result = - publisher_room->localParticipant()->publishDataTrack(track_name); + auto publish_result = publisher_room->localParticipant()->publishDataTrack(track_name); if (!publish_result) { FAIL() << describeDataTrackError(publish_result.error()); } @@ -800,8 +733,7 @@ TEST_F(DataTrackE2ETest, PreservesUserTimestampOnEncryptedDataTrack) { try { DataTrackFrame incoming_frame; if (!subscription->read(incoming_frame)) { - throw std::runtime_error( - "Subscription ended before timestamped encrypted frame arrived"); + throw std::runtime_error("Subscription ended before timestamped encrypted frame arrived"); } frame_promise.set_value(std::move(incoming_frame)); } catch (...) { @@ -812,8 +744,7 @@ TEST_F(DataTrackE2ETest, PreservesUserTimestampOnEncryptedDataTrack) { bool pushed = false; for (int attempt = 0; attempt < kTimestampFrameAttempts; ++attempt) { auto payload_copy = payload; - auto push_result = - local_track->tryPush(std::move(payload_copy), sent_timestamp); + auto push_result = local_track->tryPush(std::move(payload_copy), sent_timestamp); pushed = static_cast(push_result) || pushed; if (frame_future.wait_for(25ms) == std::future_status::ready) { break; @@ -826,13 +757,12 @@ TEST_F(DataTrackE2ETest, PreservesUserTimestampOnEncryptedDataTrack) { reader.join(); ASSERT_TRUE(pushed) << "Failed to push timestamped encrypted frame"; - ASSERT_EQ(frame_status, std::future_status::ready) - << "Timed out waiting for timestamped encrypted frame"; + ASSERT_EQ(frame_status, std::future_status::ready) << "Timed out waiting for timestamped encrypted frame"; DataTrackFrame frame; try { frame = frame_future.get(); - } catch (const std::exception &e) { + } catch (const std::exception& e) { FAIL() << e.what(); } EXPECT_EQ(frame.payload, payload); @@ -843,8 +773,7 @@ TEST_F(DataTrackE2ETest, PreservesUserTimestampOnEncryptedDataTrack) { local_track->unpublishDataTrack(); } -std::string dataTrackParamName( - const ::testing::TestParamInfo> &info) { +std::string dataTrackParamName(const ::testing::TestParamInfo>& info) { if (std::get<0>(info.param) > 100.0) { return "HighFpsSinglePacket"; } @@ -852,9 +781,7 @@ std::string dataTrackParamName( } INSTANTIATE_TEST_SUITE_P(DataTrackScenarios, DataTrackTransportTest, - ::testing::Values(std::make_tuple(120.0, size_t{8192}), - std::make_tuple(10.0, - size_t{196608})), + ::testing::Values(std::make_tuple(120.0, size_t{8192}), std::make_tuple(10.0, size_t{196608})), dataTrackParamName); } // namespace livekit::test diff --git a/src/tests/integration/test_media_multistream.cpp b/src/tests/integration/test_media_multistream.cpp index 9dcc03da..ccb6dcae 100644 --- a/src/tests/integration/test_media_multistream.cpp +++ b/src/tests/integration/test_media_multistream.cpp @@ -14,9 +14,6 @@ * limitations under the License. */ -#include "../common/audio_utils.h" -#include "../common/test_common.h" -#include "../common/video_utils.h" #include #include #include @@ -26,6 +23,10 @@ #include #include +#include "../common/audio_utils.h" +#include "../common/test_common.h" +#include "../common/video_utils.h" + namespace livekit::test { using namespace std::chrono_literals; @@ -47,10 +48,9 @@ struct MediaSubscriptionState { class MediaTrackCollectorDelegate : public RoomDelegate { public: - explicit MediaTrackCollectorDelegate(MediaSubscriptionState &state) - : state_(state) {} + explicit MediaTrackCollectorDelegate(MediaSubscriptionState& state) : state_(state) {} - void onTrackSubscribed(Room &, const TrackSubscribedEvent &event) override { + void onTrackSubscribed(Room&, const TrackSubscribedEvent& event) override { std::lock_guard lock(state_.mutex); std::string name = ""; std::string sid = ""; @@ -66,14 +66,13 @@ class MediaTrackCollectorDelegate : public RoomDelegate { name = event.publication->name(); state_.subscribed_track_names.insert(name); } - std::cerr << "[MediaMultiStream] onTrackSubscribed name=" << name - << " sid=" << sid << " audio_count=" << state_.audio_tracks - << " video_count=" << state_.video_tracks << std::endl; + std::cerr << "[MediaMultiStream] onTrackSubscribed name=" << name << " sid=" << sid + << " audio_count=" << state_.audio_tracks << " video_count=" << state_.video_tracks << std::endl; state_.cv.notify_all(); } private: - MediaSubscriptionState &state_; + MediaSubscriptionState& state_; }; } // namespace @@ -83,11 +82,9 @@ class MediaMultiStreamIntegrationTest : public LiveKitTestBase { void runPublishTwoVideoAndTwoAudioTracks(bool single_peer_connection); }; -void MediaMultiStreamIntegrationTest::runPublishTwoVideoAndTwoAudioTracks( - bool single_peer_connection) { +void MediaMultiStreamIntegrationTest::runPublishTwoVideoAndTwoAudioTracks(bool single_peer_connection) { if (!config_.available) { - GTEST_SKIP() - << "LIVEKIT_URL, LIVEKIT_TOKEN_A, and LIVEKIT_TOKEN_B not set"; + GTEST_SKIP() << "LIVEKIT_URL, LIVEKIT_TOKEN_A, and LIVEKIT_TOKEN_B not set"; } RoomOptions options; @@ -99,18 +96,13 @@ void MediaMultiStreamIntegrationTest::runPublishTwoVideoAndTwoAudioTracks( auto receiver_room = std::make_unique(); receiver_room->setDelegate(&receiver_delegate); - ASSERT_TRUE( - receiver_room->Connect(config_.url, config_.token_b, options)) - << "Receiver failed to connect"; + ASSERT_TRUE(receiver_room->Connect(config_.url, config_.token_b, options)) << "Receiver failed to connect"; auto sender_room = std::make_unique(); - ASSERT_TRUE(sender_room->Connect(config_.url, config_.token_a, options)) - << "Sender failed to connect"; + ASSERT_TRUE(sender_room->Connect(config_.url, config_.token_a, options)) << "Sender failed to connect"; - const std::string receiver_identity = - receiver_room->localParticipant()->identity(); - const std::string sender_identity = - sender_room->localParticipant()->identity(); + const std::string receiver_identity = receiver_room->localParticipant()->identity(); + const std::string sender_identity = sender_room->localParticipant()->identity(); constexpr int kVideoTrackCount = 10; constexpr int kAudioTrackCount = 10; @@ -133,27 +125,22 @@ void MediaMultiStreamIntegrationTest::runPublishTwoVideoAndTwoAudioTracks( const std::string name = "video-track-" + std::to_string(i); auto track = LocalVideoTrack::createLocalVideoTrack(name, source); TrackPublishOptions opts; - opts.source = (i % 2 == 0) ? TrackSource::SOURCE_CAMERA - : TrackSource::SOURCE_SCREENSHARE; + opts.source = (i % 2 == 0) ? TrackSource::SOURCE_CAMERA : TrackSource::SOURCE_SCREENSHARE; sender_room->localParticipant()->publishTrack(track, opts); - std::cerr << "[MediaMultiStream] published video " << name - << " sid=" << track->sid() << std::endl; + std::cerr << "[MediaMultiStream] published video " << name << " sid=" << track->sid() << std::endl; video_sources.push_back(source); video_tracks.push_back(track); expected_names.insert(name); } for (int i = 0; i < kAudioTrackCount; ++i) { - auto source = - std::make_shared(kAudioSampleRate, kAudioChannels, 0); + auto source = std::make_shared(kAudioSampleRate, kAudioChannels, 0); const std::string name = "audio-track-" + std::to_string(i); auto track = LocalAudioTrack::createLocalAudioTrack(name, source); TrackPublishOptions opts; - opts.source = (i % 2 == 0) ? TrackSource::SOURCE_MICROPHONE - : TrackSource::SOURCE_SCREENSHARE_AUDIO; + opts.source = (i % 2 == 0) ? TrackSource::SOURCE_MICROPHONE : TrackSource::SOURCE_SCREENSHARE_AUDIO; sender_room->localParticipant()->publishTrack(track, opts); - std::cerr << "[MediaMultiStream] published audio " << name - << " sid=" << track->sid() << std::endl; + std::cerr << "[MediaMultiStream] published audio " << name << " sid=" << track->sid() << std::endl; audio_sources.push_back(source); audio_tracks.push_back(track); expected_names.insert(name); @@ -164,92 +151,80 @@ void MediaMultiStreamIntegrationTest::runPublishTwoVideoAndTwoAudioTracks( auto source = video_sources[static_cast(i)]; const bool red_mode = (i % 2 == 1); threads.emplace_back([source, &running, red_mode]() { - runVideoLoop(source, running, - red_mode ? fillRedWrapper : fillWebcamWrapper); + runVideoLoop(source, running, red_mode ? fillRedWrapper : fillWebcamWrapper); }); } for (int i = 0; i < kAudioTrackCount; ++i) { auto source = audio_sources[static_cast(i)]; const bool siren_mode = (i % 2 == 1); const double base_freq = 320.0 + static_cast(i) * 40.0; - threads.emplace_back([source, &running, base_freq, siren_mode]() { - runToneLoop(source, running, base_freq, siren_mode); - }); + threads.emplace_back( + [source, &running, base_freq, siren_mode]() { runToneLoop(source, running, base_freq, siren_mode); }); } { std::unique_lock lock(receiver_state.mutex); - const bool all_received = receiver_state.cv.wait_for(lock, 20s, [&]() { - return receiver_state.subscribed_track_names.size() >= - expected_names.size(); - }); + const bool all_received = receiver_state.cv.wait_for( + lock, 20s, [&]() { return receiver_state.subscribed_track_names.size() >= expected_names.size(); }); EXPECT_TRUE(all_received) << "Timed out waiting for all subscribed tracks"; if (!all_received) { std::cerr << "[MediaMultiStream] timeout waiting subscriptions; received " "names:"; - for (const auto &n : receiver_state.subscribed_track_names) { + for (const auto& n : receiver_state.subscribed_track_names) { std::cerr << " " << n; } - std::cerr << " (audio=" << receiver_state.audio_tracks - << " video=" << receiver_state.video_tracks << ")" << std::endl; + std::cerr << " (audio=" << receiver_state.audio_tracks << " video=" << receiver_state.video_tracks << ")" + << std::endl; } } { std::lock_guard lock(receiver_state.mutex); - for (const auto &expected_name : expected_names) { - EXPECT_TRUE(receiver_state.subscribed_track_names.count(expected_name) > - 0) + for (const auto& expected_name : expected_names) { + EXPECT_TRUE(receiver_state.subscribed_track_names.count(expected_name) > 0) << "Missing subscribed track: " << expected_name; } EXPECT_GE(receiver_state.video_tracks, kVideoTrackCount); EXPECT_GE(receiver_state.audio_tracks, kAudioTrackCount); } - auto *sender_on_receiver = receiver_room->remoteParticipant(sender_identity); + auto* sender_on_receiver = receiver_room->remoteParticipant(sender_identity); ASSERT_NE(sender_on_receiver, nullptr); - std::cerr << "[MediaMultiStream] receiver sees sender publications=" - << sender_on_receiver->trackPublications().size() << std::endl; - for (const auto &kv : sender_on_receiver->trackPublications()) { - const auto &pub = kv.second; - std::cerr << "[MediaMultiStream] remote publication sid=" << kv.first - << " name=" << (pub ? pub->name() : "") << " kind=" - << (pub && pub->kind() == TrackKind::KIND_AUDIO ? "audio" - : "video") - << " source=" << (pub ? static_cast(pub->source()) : -1) - << std::endl; + std::cerr << "[MediaMultiStream] receiver sees sender publications=" << sender_on_receiver->trackPublications().size() + << std::endl; + for (const auto& kv : sender_on_receiver->trackPublications()) { + const auto& pub = kv.second; + std::cerr << "[MediaMultiStream] remote publication sid=" << kv.first << " name=" << (pub ? pub->name() : "") + << " kind=" << (pub && pub->kind() == TrackKind::KIND_AUDIO ? "audio" : "video") + << " source=" << (pub ? static_cast(pub->source()) : -1) << std::endl; } EXPECT_GE(sender_on_receiver->trackPublications().size(), static_cast(kVideoTrackCount + kAudioTrackCount)); running.store(false, std::memory_order_relaxed); - for (auto &t : threads) { + for (auto& t : threads) { if (t.joinable()) { t.join(); } } - for (const auto &track : video_tracks) { + for (const auto& track : video_tracks) { if (track->publication()) { - sender_room->localParticipant()->unpublishTrack( - track->publication()->sid()); + sender_room->localParticipant()->unpublishTrack(track->publication()->sid()); } } - for (const auto &track : audio_tracks) { + for (const auto& track : audio_tracks) { if (track->publication()) { - sender_room->localParticipant()->unpublishTrack( - track->publication()->sid()); + sender_room->localParticipant()->unpublishTrack(track->publication()->sid()); } } } -TEST_F(MediaMultiStreamIntegrationTest, - PublishTwoVideoAndTwoAudioTracks_DualPeerConnection) { +TEST_F(MediaMultiStreamIntegrationTest, PublishTwoVideoAndTwoAudioTracks_DualPeerConnection) { runPublishTwoVideoAndTwoAudioTracks(false); } -TEST_F(MediaMultiStreamIntegrationTest, - PublishTwoVideoAndTwoAudioTracks_SinglePeerConnection) { +TEST_F(MediaMultiStreamIntegrationTest, PublishTwoVideoAndTwoAudioTracks_SinglePeerConnection) { runPublishTwoVideoAndTwoAudioTracks(true); } diff --git a/src/tests/integration/test_room.cpp b/src/tests/integration/test_room.cpp index 1a1339b1..011bdf23 100644 --- a/src/tests/integration/test_room.cpp +++ b/src/tests/integration/test_room.cpp @@ -21,9 +21,7 @@ namespace livekit::test { class RoomTest : public ::testing::Test { protected: - void SetUp() override { - livekit::initialize(livekit::LogLevel::Info, livekit::LogSink::kConsole); - } + void SetUp() override { livekit::initialize(livekit::LogLevel::Info, livekit::LogSink::kConsole); } void TearDown() override { livekit::shutdown(); } }; @@ -31,20 +29,16 @@ class RoomTest : public ::testing::Test { TEST_F(RoomTest, CreateRoom) { Room room; // Room should be created without issues - EXPECT_EQ(room.localParticipant(), nullptr) - << "Local participant should be null before connect"; + EXPECT_EQ(room.localParticipant(), nullptr) << "Local participant should be null before connect"; } TEST_F(RoomTest, RoomOptionsDefaults) { RoomOptions options; - EXPECT_TRUE(options.auto_subscribe) - << "auto_subscribe should default to true"; + EXPECT_TRUE(options.auto_subscribe) << "auto_subscribe should default to true"; EXPECT_FALSE(options.dynacast) << "dynacast should default to false"; - EXPECT_FALSE(options.rtc_config.has_value()) - << "rtc_config should not have a value by default"; - EXPECT_FALSE(options.encryption.has_value()) - << "encryption should not have a value by default"; + EXPECT_FALSE(options.rtc_config.has_value()) << "rtc_config should not have a value by default"; + EXPECT_FALSE(options.encryption.has_value()) << "encryption should not have a value by default"; } TEST_F(RoomTest, RtcConfigDefaults) { @@ -73,8 +67,7 @@ TEST_F(RoomTest, RoomWithCustomRtcConfig) { RtcConfig rtc_config; rtc_config.ice_servers.push_back({"stun:stun.l.google.com:19302", "", ""}); - rtc_config.ice_servers.push_back( - {"turn:turn.example.com:3478", "user", "pass"}); + rtc_config.ice_servers.push_back({"turn:turn.example.com:3478", "user", "pass"}); options.rtc_config = rtc_config; @@ -87,15 +80,13 @@ TEST_F(RoomTest, RoomWithCustomRtcConfig) { TEST_F(RoomTest, RemoteParticipantsEmptyBeforeConnect) { Room room; auto participants = room.remoteParticipants(); - EXPECT_TRUE(participants.empty()) - << "Remote participants should be empty before connect"; + EXPECT_TRUE(participants.empty()) << "Remote participants should be empty before connect"; } TEST_F(RoomTest, RemoteParticipantLookupBeforeConnect) { Room room; auto participant = room.remoteParticipant("nonexistent"); - EXPECT_EQ(participant, nullptr) - << "Looking up participant before connect should return nullptr"; + EXPECT_EQ(participant, nullptr) << "Looking up participant before connect should return nullptr"; } // Server-dependent tests - require LIVEKIT_URL and LIVEKIT_TOKEN_A env vars @@ -104,8 +95,8 @@ class RoomServerTest : public ::testing::Test { void SetUp() override { livekit::initialize(livekit::LogLevel::Info, livekit::LogSink::kConsole); - const char *url_env = std::getenv("LIVEKIT_URL"); - const char *token_env = std::getenv("LIVEKIT_TOKEN_A"); + const char* url_env = std::getenv("LIVEKIT_URL"); + const char* token_env = std::getenv("LIVEKIT_TOKEN_A"); if (url_env && token_env) { server_url_ = url_env; @@ -134,8 +125,7 @@ TEST_F(RoomServerTest, ConnectToServer) { EXPECT_TRUE(connected) << "Should connect to server successfully"; if (connected) { - EXPECT_NE(room.localParticipant(), nullptr) - << "Local participant should exist after connect"; + EXPECT_NE(room.localParticipant(), nullptr) << "Local participant should exist after connect"; } } diff --git a/src/tests/integration/test_rpc.cpp b/src/tests/integration/test_rpc.cpp index ab03d656..20e857ec 100644 --- a/src/tests/integration/test_rpc.cpp +++ b/src/tests/integration/test_rpc.cpp @@ -14,12 +14,13 @@ * limitations under the License. */ +#include +#include + #include #include #include #include -#include -#include #include #include #include @@ -42,9 +43,9 @@ struct RpcTestConfig { static RpcTestConfig fromEnv() { RpcTestConfig config; - const char *url = std::getenv("LIVEKIT_URL"); - const char *token_a = std::getenv("LIVEKIT_TOKEN_A"); - const char *token_b = std::getenv("LIVEKIT_TOKEN_B"); + const char* url = std::getenv("LIVEKIT_URL"); + const char* token_a = std::getenv("LIVEKIT_TOKEN_A"); + const char* token_b = std::getenv("LIVEKIT_TOKEN_B"); if (url && token_a && token_b) { config.url = url; @@ -71,8 +72,7 @@ static const std::vector kSampleSentences = { std::string generateRandomPayload(size_t size) { static std::random_device rd; static std::mt19937 gen(static_cast(rd())); - static std::uniform_int_distribution dis(0, - kSampleSentences.size() - 1); + static std::uniform_int_distribution dis(0, kSampleSentences.size() - 1); std::string result; result.reserve(size); @@ -81,8 +81,7 @@ std::string generateRandomPayload(size_t size) { size_t start_idx = dis(gen); while (result.size() < size) { - const std::string &sentence = - kSampleSentences[(start_idx + result.size()) % kSampleSentences.size()]; + const std::string& sentence = kSampleSentences[(start_idx + result.size()) % kSampleSentences.size()]; result += sentence; } @@ -90,8 +89,7 @@ std::string generateRandomPayload(size_t size) { } // Wait for a remote participant to appear -bool waitForParticipant(Room *room, const std::string &identity, - std::chrono::milliseconds timeout) { +bool waitForParticipant(Room* room, const std::string& identity, std::chrono::milliseconds timeout) { auto start = std::chrono::steady_clock::now(); while (std::chrono::steady_clock::now() - start < timeout) { if (room->remoteParticipant(identity) != nullptr) { @@ -117,8 +115,7 @@ class RpcIntegrationTest : public ::testing::Test { // Test basic RPC round-trip TEST_F(RpcIntegrationTest, BasicRpcRoundTrip) { if (!config_.available) { - GTEST_SKIP() - << "LIVEKIT_URL, LIVEKIT_TOKEN_A, and LIVEKIT_TOKEN_B not set"; + GTEST_SKIP() << "LIVEKIT_URL, LIVEKIT_TOKEN_A, and LIVEKIT_TOKEN_B not set"; } // Create receiver room @@ -126,8 +123,7 @@ TEST_F(RpcIntegrationTest, BasicRpcRoundTrip) { RoomOptions receiver_options; receiver_options.auto_subscribe = true; - bool receiver_connected = receiver_room->Connect( - config_.url, config_.token_b, receiver_options); + bool receiver_connected = receiver_room->Connect(config_.url, config_.token_b, receiver_options); ASSERT_TRUE(receiver_connected) << "Receiver failed to connect"; std::string receiver_identity = receiver_room->localParticipant()->identity(); @@ -136,16 +132,13 @@ TEST_F(RpcIntegrationTest, BasicRpcRoundTrip) { // full payload std::atomic rpc_calls_received{0}; receiver_room->localParticipant()->registerRpcMethod( - "echo", - [&rpc_calls_received]( - const RpcInvocationData &data) -> std::optional { + "echo", [&rpc_calls_received](const RpcInvocationData& data) -> std::optional { rpc_calls_received++; size_t checksum = 0; for (char c : data.payload) { checksum += static_cast(c); } - return "echo:" + std::to_string(data.payload.size()) + ":" + - std::to_string(checksum); + return "echo:" + std::to_string(data.payload.size()) + ":" + std::to_string(checksum); }); // Create caller room @@ -153,19 +146,16 @@ TEST_F(RpcIntegrationTest, BasicRpcRoundTrip) { RoomOptions caller_options; caller_options.auto_subscribe = true; - bool caller_connected = - caller_room->Connect(config_.url, config_.token_a, caller_options); + bool caller_connected = caller_room->Connect(config_.url, config_.token_a, caller_options); ASSERT_TRUE(caller_connected) << "Caller failed to connect"; // Wait for receiver to be visible to caller - bool receiver_visible = - waitForParticipant(caller_room.get(), receiver_identity, 10s); + bool receiver_visible = waitForParticipant(caller_room.get(), receiver_identity, 10s); ASSERT_TRUE(receiver_visible) << "Receiver not visible to caller"; // Perform RPC call std::string test_payload = "hello world"; - std::string response = caller_room->localParticipant()->performRpc( - receiver_identity, "echo", test_payload, 10.0); + std::string response = caller_room->localParticipant()->performRpc(receiver_identity, "echo", test_payload, 10.0); // Verify response contains correct size and checksum size_t expected_checksum = 0; @@ -173,8 +163,7 @@ TEST_F(RpcIntegrationTest, BasicRpcRoundTrip) { expected_checksum += static_cast(c); } std::string expected_response = - "echo:" + std::to_string(test_payload.size()) + ":" + - std::to_string(expected_checksum); + "echo:" + std::to_string(test_payload.size()) + ":" + std::to_string(expected_checksum); EXPECT_EQ(response, expected_response); EXPECT_EQ(rpc_calls_received.load(), 1); @@ -187,16 +176,14 @@ TEST_F(RpcIntegrationTest, BasicRpcRoundTrip) { // Test maximum payload size (15KB) TEST_F(RpcIntegrationTest, MaxPayloadSize) { if (!config_.available) { - GTEST_SKIP() - << "LIVEKIT_URL, LIVEKIT_TOKEN_A, and LIVEKIT_TOKEN_B not set"; + GTEST_SKIP() << "LIVEKIT_URL, LIVEKIT_TOKEN_A, and LIVEKIT_TOKEN_B not set"; } auto receiver_room = std::make_unique(); RoomOptions options; options.auto_subscribe = true; - bool receiver_connected = - receiver_room->Connect(config_.url, config_.token_b, options); + bool receiver_connected = receiver_room->Connect(config_.url, config_.token_b, options); ASSERT_TRUE(receiver_connected) << "Receiver failed to connect"; std::string receiver_identity = receiver_room->localParticipant()->identity(); @@ -204,23 +191,19 @@ TEST_F(RpcIntegrationTest, MaxPayloadSize) { // Register handler that echoes payload size receiver_room->localParticipant()->registerRpcMethod( "payload-size", - [](const RpcInvocationData &data) -> std::optional { - return std::to_string(data.payload.size()); - }); + [](const RpcInvocationData& data) -> std::optional { return std::to_string(data.payload.size()); }); auto caller_room = std::make_unique(); - bool caller_connected = - caller_room->Connect(config_.url, config_.token_a, options); + bool caller_connected = caller_room->Connect(config_.url, config_.token_a, options); ASSERT_TRUE(caller_connected) << "Caller failed to connect"; - bool receiver_visible = - waitForParticipant(caller_room.get(), receiver_identity, 10s); + bool receiver_visible = waitForParticipant(caller_room.get(), receiver_identity, 10s); ASSERT_TRUE(receiver_visible) << "Receiver not visible to caller"; // Test with max payload size (15KB) std::string max_payload = generateRandomPayload(kMaxRpcPayloadSize); - std::string response = caller_room->localParticipant()->performRpc( - receiver_identity, "payload-size", max_payload, 30.0); + std::string response = + caller_room->localParticipant()->performRpc(receiver_identity, "payload-size", max_payload, 30.0); EXPECT_EQ(response, std::to_string(kMaxRpcPayloadSize)); @@ -232,44 +215,34 @@ TEST_F(RpcIntegrationTest, MaxPayloadSize) { // Test RPC timeout TEST_F(RpcIntegrationTest, RpcTimeout) { if (!config_.available) { - GTEST_SKIP() - << "LIVEKIT_URL, LIVEKIT_TOKEN_A, and LIVEKIT_TOKEN_B not set"; + GTEST_SKIP() << "LIVEKIT_URL, LIVEKIT_TOKEN_A, and LIVEKIT_TOKEN_B not set"; } auto receiver_room = std::make_unique(); RoomOptions options; options.auto_subscribe = true; - bool receiver_connected = - receiver_room->Connect(config_.url, config_.token_b, options); + bool receiver_connected = receiver_room->Connect(config_.url, config_.token_b, options); ASSERT_TRUE(receiver_connected) << "Receiver failed to connect"; std::string receiver_identity = receiver_room->localParticipant()->identity(); // Register handler that takes too long - receiver_room->localParticipant()->registerRpcMethod( - "slow-method", - [](const RpcInvocationData &) -> std::optional { - std::this_thread::sleep_for(10s); - return "done"; - }); + receiver_room->localParticipant()->registerRpcMethod("slow-method", + [](const RpcInvocationData&) -> std::optional { + std::this_thread::sleep_for(10s); + return "done"; + }); auto caller_room = std::make_unique(); - bool caller_connected = - caller_room->Connect(config_.url, config_.token_a, options); + bool caller_connected = caller_room->Connect(config_.url, config_.token_a, options); ASSERT_TRUE(caller_connected) << "Caller failed to connect"; - bool receiver_visible = - waitForParticipant(caller_room.get(), receiver_identity, 10s); + bool receiver_visible = waitForParticipant(caller_room.get(), receiver_identity, 10s); ASSERT_TRUE(receiver_visible) << "Receiver not visible to caller"; // Call with short timeout - should fail - EXPECT_THROW( - { - caller_room->localParticipant()->performRpc(receiver_identity, - "slow-method", "", 2.0); - }, - RpcError); + EXPECT_THROW({ caller_room->localParticipant()->performRpc(receiver_identity, "slow-method", "", 2.0); }, RpcError); receiver_room->localParticipant()->unregisterRpcMethod("slow-method"); caller_room.reset(); @@ -279,37 +252,31 @@ TEST_F(RpcIntegrationTest, RpcTimeout) { // Test RPC with unsupported method TEST_F(RpcIntegrationTest, UnsupportedMethod) { if (!config_.available) { - GTEST_SKIP() - << "LIVEKIT_URL, LIVEKIT_TOKEN_A, and LIVEKIT_TOKEN_B not set"; + GTEST_SKIP() << "LIVEKIT_URL, LIVEKIT_TOKEN_A, and LIVEKIT_TOKEN_B not set"; } auto receiver_room = std::make_unique(); RoomOptions options; options.auto_subscribe = true; - bool receiver_connected = - receiver_room->Connect(config_.url, config_.token_b, options); + bool receiver_connected = receiver_room->Connect(config_.url, config_.token_b, options); ASSERT_TRUE(receiver_connected) << "Receiver failed to connect"; std::string receiver_identity = receiver_room->localParticipant()->identity(); auto caller_room = std::make_unique(); - bool caller_connected = - caller_room->Connect(config_.url, config_.token_a, options); + bool caller_connected = caller_room->Connect(config_.url, config_.token_a, options); ASSERT_TRUE(caller_connected) << "Caller failed to connect"; - bool receiver_visible = - waitForParticipant(caller_room.get(), receiver_identity, 10s); + bool receiver_visible = waitForParticipant(caller_room.get(), receiver_identity, 10s); ASSERT_TRUE(receiver_visible) << "Receiver not visible to caller"; // Call unregistered method try { - caller_room->localParticipant()->performRpc(receiver_identity, - "nonexistent-method", "", 5.0); + caller_room->localParticipant()->performRpc(receiver_identity, "nonexistent-method", "", 5.0); FAIL() << "Expected RpcError for unsupported method"; - } catch (const RpcError &e) { - EXPECT_EQ(static_cast(e.code()), - RpcError::ErrorCode::UNSUPPORTED_METHOD); + } catch (const RpcError& e) { + EXPECT_EQ(static_cast(e.code()), RpcError::ErrorCode::UNSUPPORTED_METHOD); } caller_room.reset(); @@ -319,16 +286,14 @@ TEST_F(RpcIntegrationTest, UnsupportedMethod) { // Test RPC with application error TEST_F(RpcIntegrationTest, ApplicationError) { if (!config_.available) { - GTEST_SKIP() - << "LIVEKIT_URL, LIVEKIT_TOKEN_A, and LIVEKIT_TOKEN_B not set"; + GTEST_SKIP() << "LIVEKIT_URL, LIVEKIT_TOKEN_A, and LIVEKIT_TOKEN_B not set"; } auto receiver_room = std::make_unique(); RoomOptions options; options.auto_subscribe = true; - bool receiver_connected = - receiver_room->Connect(config_.url, config_.token_b, options); + bool receiver_connected = receiver_room->Connect(config_.url, config_.token_b, options); ASSERT_TRUE(receiver_connected) << "Receiver failed to connect"; std::string receiver_identity = receiver_room->localParticipant()->identity(); @@ -336,26 +301,20 @@ TEST_F(RpcIntegrationTest, ApplicationError) { // Register handler that throws an error receiver_room->localParticipant()->registerRpcMethod( "error-method", - [](const RpcInvocationData &) -> std::optional { - throw std::runtime_error("intentional error"); - }); + [](const RpcInvocationData&) -> std::optional { throw std::runtime_error("intentional error"); }); auto caller_room = std::make_unique(); - bool caller_connected = - caller_room->Connect(config_.url, config_.token_a, options); + bool caller_connected = caller_room->Connect(config_.url, config_.token_a, options); ASSERT_TRUE(caller_connected) << "Caller failed to connect"; - bool receiver_visible = - waitForParticipant(caller_room.get(), receiver_identity, 10s); + bool receiver_visible = waitForParticipant(caller_room.get(), receiver_identity, 10s); ASSERT_TRUE(receiver_visible) << "Receiver not visible to caller"; try { - caller_room->localParticipant()->performRpc(receiver_identity, - "error-method", "", 5.0); + caller_room->localParticipant()->performRpc(receiver_identity, "error-method", "", 5.0); FAIL() << "Expected RpcError for application error"; - } catch (const RpcError &e) { - EXPECT_EQ(static_cast(e.code()), - RpcError::ErrorCode::APPLICATION_ERROR); + } catch (const RpcError& e) { + EXPECT_EQ(static_cast(e.code()), RpcError::ErrorCode::APPLICATION_ERROR); } receiver_room->localParticipant()->unregisterRpcMethod("error-method"); @@ -366,25 +325,21 @@ TEST_F(RpcIntegrationTest, ApplicationError) { // Test multiple concurrent RPC calls TEST_F(RpcIntegrationTest, ConcurrentRpcCalls) { if (!config_.available) { - GTEST_SKIP() - << "LIVEKIT_URL, LIVEKIT_TOKEN_A, and LIVEKIT_TOKEN_B not set"; + GTEST_SKIP() << "LIVEKIT_URL, LIVEKIT_TOKEN_A, and LIVEKIT_TOKEN_B not set"; } auto receiver_room = std::make_unique(); RoomOptions options; options.auto_subscribe = true; - bool receiver_connected = - receiver_room->Connect(config_.url, config_.token_b, options); + bool receiver_connected = receiver_room->Connect(config_.url, config_.token_b, options); ASSERT_TRUE(receiver_connected) << "Receiver failed to connect"; std::string receiver_identity = receiver_room->localParticipant()->identity(); std::atomic calls_processed{0}; receiver_room->localParticipant()->registerRpcMethod( - "counter", - [&calls_processed]( - const RpcInvocationData &data) -> std::optional { + "counter", [&calls_processed](const RpcInvocationData& data) -> std::optional { int id = std::stoi(data.payload); calls_processed++; std::this_thread::sleep_for(100ms); // Simulate some work @@ -392,12 +347,10 @@ TEST_F(RpcIntegrationTest, ConcurrentRpcCalls) { }); auto caller_room = std::make_unique(); - bool caller_connected = - caller_room->Connect(config_.url, config_.token_a, options); + bool caller_connected = caller_room->Connect(config_.url, config_.token_a, options); ASSERT_TRUE(caller_connected) << "Caller failed to connect"; - bool receiver_visible = - waitForParticipant(caller_room.get(), receiver_identity, 10s); + bool receiver_visible = waitForParticipant(caller_room.get(), receiver_identity, 10s); ASSERT_TRUE(receiver_visible) << "Receiver not visible to caller"; const int num_concurrent_calls = 10; @@ -407,19 +360,19 @@ TEST_F(RpcIntegrationTest, ConcurrentRpcCalls) { for (int i = 0; i < num_concurrent_calls; ++i) { threads.emplace_back([&, i]() { try { - std::string response = caller_room->localParticipant()->performRpc( - receiver_identity, "counter", std::to_string(i), 30.0); + std::string response = + caller_room->localParticipant()->performRpc(receiver_identity, "counter", std::to_string(i), 30.0); int expected = i * 2; if (std::stoi(response) == expected) { successful_calls++; } - } catch (const std::exception &e) { + } catch (const std::exception& e) { std::cerr << "RPC call " << i << " failed: " << e.what() << std::endl; } }); } - for (auto &t : threads) { + for (auto& t : threads) { t.join(); } @@ -434,16 +387,14 @@ TEST_F(RpcIntegrationTest, ConcurrentRpcCalls) { // Integration test: Run for approximately 1 minute TEST_F(RpcIntegrationTest, OneMinuteIntegration) { if (!config_.available) { - GTEST_SKIP() - << "LIVEKIT_URL, LIVEKIT_TOKEN_A, and LIVEKIT_TOKEN_B not set"; + GTEST_SKIP() << "LIVEKIT_URL, LIVEKIT_TOKEN_A, and LIVEKIT_TOKEN_B not set"; } auto receiver_room = std::make_unique(); RoomOptions options; options.auto_subscribe = true; - bool receiver_connected = - receiver_room->Connect(config_.url, config_.token_b, options); + bool receiver_connected = receiver_room->Connect(config_.url, config_.token_b, options); ASSERT_TRUE(receiver_connected) << "Receiver failed to connect"; std::string receiver_identity = receiver_room->localParticipant()->identity(); @@ -452,20 +403,17 @@ TEST_F(RpcIntegrationTest, OneMinuteIntegration) { std::atomic total_bytes_received{0}; receiver_room->localParticipant()->registerRpcMethod( - "integration-test", - [&](const RpcInvocationData &data) -> std::optional { + "integration-test", [&](const RpcInvocationData& data) -> std::optional { total_received++; total_bytes_received += data.payload.size(); return "ack:" + std::to_string(data.payload.size()); }); auto caller_room = std::make_unique(); - bool caller_connected = - caller_room->Connect(config_.url, config_.token_a, options); + bool caller_connected = caller_room->Connect(config_.url, config_.token_a, options); ASSERT_TRUE(caller_connected) << "Caller failed to connect"; - bool receiver_visible = - waitForParticipant(caller_room.get(), receiver_identity, 10s); + bool receiver_visible = waitForParticipant(caller_room.get(), receiver_identity, 10s); ASSERT_TRUE(receiver_visible) << "Receiver not visible to caller"; // Run for 1 minute @@ -479,8 +427,7 @@ TEST_F(RpcIntegrationTest, OneMinuteIntegration) { // Sender thread std::thread sender([&]() { - std::vector payload_sizes = {100, 1024, 5 * 1024, 10 * 1024, - kMaxRpcPayloadSize}; + std::vector payload_sizes = {100, 1024, 5 * 1024, 10 * 1024, kMaxRpcPayloadSize}; int size_index = 0; while (running.load()) { @@ -488,12 +435,12 @@ TEST_F(RpcIntegrationTest, OneMinuteIntegration) { std::string payload = generateRandomPayload(payload_size); try { - std::string response = caller_room->localParticipant()->performRpc( - receiver_identity, "integration-test", payload, 30.0); + std::string response = + caller_room->localParticipant()->performRpc(receiver_identity, "integration-test", payload, 30.0); if (response == "ack:" + std::to_string(payload_size)) { successful_calls++; } - } catch (const std::exception &e) { + } catch (const std::exception& e) { failed_calls++; } @@ -506,10 +453,8 @@ TEST_F(RpcIntegrationTest, OneMinuteIntegration) { // Wait for test duration while (std::chrono::steady_clock::now() - start_time < test_duration) { std::this_thread::sleep_for(1s); - std::cout << "Progress: sent=" << total_sent.load() - << " successful=" << successful_calls.load() - << " failed=" << failed_calls.load() - << " received=" << total_received.load() << std::endl; + std::cout << "Progress: sent=" << total_sent.load() << " successful=" << successful_calls.load() + << " failed=" << failed_calls.load() << " received=" << total_received.load() << std::endl; } running.store(false); @@ -520,8 +465,7 @@ TEST_F(RpcIntegrationTest, OneMinuteIntegration) { std::cout << "Successful: " << successful_calls.load() << std::endl; std::cout << "Failed: " << failed_calls.load() << std::endl; std::cout << "Total received: " << total_received.load() << std::endl; - std::cout << "Total bytes received: " << total_bytes_received.load() - << std::endl; + std::cout << "Total bytes received: " << total_bytes_received.load() << std::endl; EXPECT_GT(successful_calls.load(), 0); EXPECT_EQ(total_sent.load(), total_received.load()); diff --git a/src/tests/integration/test_video_frame_metadata.cpp b/src/tests/integration/test_video_frame_metadata.cpp index faf8dcb8..c25be702 100644 --- a/src/tests/integration/test_video_frame_metadata.cpp +++ b/src/tests/integration/test_video_frame_metadata.cpp @@ -14,8 +14,6 @@ * limitations under the License. */ -#include "tests/common/test_common.h" - #include #include #include @@ -25,26 +23,25 @@ #include #include +#include "tests/common/test_common.h" + namespace livekit::test { class VideoFrameMetadataServerTest : public LiveKitTestBase {}; -TEST_F(VideoFrameMetadataServerTest, - UserTimestampRoundTripsToReceiverEventCallback) { +TEST_F(VideoFrameMetadataServerTest, UserTimestampRoundTripsToReceiverEventCallback) { skipIfNotConfigured(); Room sender_room; Room receiver_room; RoomOptions options; - ASSERT_TRUE( - receiver_room.Connect(config_.url, config_.token_b, options)); + ASSERT_TRUE(receiver_room.Connect(config_.url, config_.token_b, options)); ASSERT_TRUE(sender_room.Connect(config_.url, config_.token_a, options)); ASSERT_NE(sender_room.localParticipant(), nullptr); ASSERT_NE(receiver_room.localParticipant(), nullptr); - const std::string sender_identity = - sender_room.localParticipant()->identity(); + const std::string sender_identity = sender_room.localParticipant()->identity(); ASSERT_FALSE(sender_identity.empty()); ASSERT_TRUE(waitForParticipant(&receiver_room, sender_identity, 10s)); @@ -53,19 +50,18 @@ TEST_F(VideoFrameMetadataServerTest, std::optional received_user_timestamp_us; const std::string track_name = "metadata-track"; - receiver_room.setOnVideoFrameEventCallback( - sender_identity, track_name, - [&mutex, &cv, &received_user_timestamp_us](const VideoFrameEvent &event) { - std::lock_guard lock(mutex); - if (!event.metadata) { - return; - } - const auto &user_timestamp_us = event.metadata->user_timestamp_us; - if (user_timestamp_us.has_value() && *user_timestamp_us != 0) { - received_user_timestamp_us = user_timestamp_us; - cv.notify_all(); - } - }); + receiver_room.setOnVideoFrameEventCallback(sender_identity, track_name, + [&mutex, &cv, &received_user_timestamp_us](const VideoFrameEvent& event) { + std::lock_guard lock(mutex); + if (!event.metadata) { + return; + } + const auto& user_timestamp_us = event.metadata->user_timestamp_us; + if (user_timestamp_us.has_value() && *user_timestamp_us != 0) { + received_user_timestamp_us = user_timestamp_us; + cv.notify_all(); + } + }); auto source = std::make_shared(16, 16); auto track = LocalVideoTrack::createLocalVideoTrack(track_name, source); @@ -75,23 +71,20 @@ TEST_F(VideoFrameMetadataServerTest, publish_options.simulcast = false; publish_options.packet_trailer_features.user_timestamp = true; - ASSERT_NO_THROW( - sender_room.localParticipant()->publishTrack(track, publish_options)); + ASSERT_NO_THROW(sender_room.localParticipant()->publishTrack(track, publish_options)); const auto track_ready_deadline = std::chrono::steady_clock::now() + 10s; bool receiver_track_ready = false; while (std::chrono::steady_clock::now() < track_ready_deadline) { - auto *sender_on_receiver = receiver_room.remoteParticipant(sender_identity); + auto* sender_on_receiver = receiver_room.remoteParticipant(sender_identity); if (sender_on_receiver != nullptr) { - for (const auto &[sid, publication] : - sender_on_receiver->trackPublications()) { + for (const auto& [sid, publication] : sender_on_receiver->trackPublications()) { (void)sid; if (publication == nullptr) { continue; } - if (publication->name() != track_name || - publication->kind() != TrackKind::KIND_VIDEO) { + if (publication->name() != track_name || publication->kind() != TrackKind::KIND_VIDEO) { continue; } @@ -109,8 +102,7 @@ TEST_F(VideoFrameMetadataServerTest, std::this_thread::sleep_for(10ms); } - ASSERT_TRUE(receiver_track_ready) - << "Timed out waiting for receiver video track subscription"; + ASSERT_TRUE(receiver_track_ready) << "Timed out waiting for receiver video track subscription"; std::atomic publishing{true}; std::thread publisher([&]() { @@ -120,8 +112,7 @@ TEST_F(VideoFrameMetadataServerTest, while (publishing.load(std::memory_order_relaxed)) { const std::uint64_t user_timestamp_us = getTimestampUs(); VideoCaptureOptions capture_options; - capture_options.timestamp_us = - static_cast(user_timestamp_us); + capture_options.timestamp_us = static_cast(user_timestamp_us); capture_options.metadata = VideoFrameMetadata{}; capture_options.metadata->user_timestamp_us = user_timestamp_us; @@ -138,9 +129,8 @@ TEST_F(VideoFrameMetadataServerTest, bool got_metadata = false; { std::unique_lock lock(mutex); - got_metadata = cv.wait_for(lock, 10s, [&received_user_timestamp_us] { - return received_user_timestamp_us.has_value(); - }); + got_metadata = + cv.wait_for(lock, 10s, [&received_user_timestamp_us] { return received_user_timestamp_us.has_value(); }); } publishing.store(false, std::memory_order_relaxed); diff --git a/src/tests/stress/test_audio_frame_stress.cpp b/src/tests/stress/test_audio_frame_stress.cpp index efecfe0c..4a11e66f 100644 --- a/src/tests/stress/test_audio_frame_stress.cpp +++ b/src/tests/stress/test_audio_frame_stress.cpp @@ -14,11 +14,12 @@ * limitations under the License. */ -#include -#include #include #include #include + +#include +#include #include #include @@ -26,9 +27,7 @@ namespace livekit::test { class AudioFrameStressTest : public ::testing::Test { protected: - void SetUp() override { - livekit::initialize(livekit::LogLevel::Info, livekit::LogSink::kConsole); - } + void SetUp() override { livekit::initialize(livekit::LogLevel::Info, livekit::LogSink::kConsole); } void TearDown() override { livekit::shutdown(); } }; @@ -43,21 +42,17 @@ TEST_F(AudioFrameStressTest, RapidFrameCreation) { auto start = std::chrono::high_resolution_clock::now(); for (int i = 0; i < num_iterations; ++i) { - AudioFrame frame = - AudioFrame::create(sample_rate, num_channels, samples_per_channel); + AudioFrame frame = AudioFrame::create(sample_rate, num_channels, samples_per_channel); ASSERT_EQ(frame.sample_rate(), sample_rate); ASSERT_EQ(frame.num_channels(), num_channels); ASSERT_EQ(frame.samples_per_channel(), samples_per_channel); } auto end = std::chrono::high_resolution_clock::now(); - auto duration = - std::chrono::duration_cast(end - start); + auto duration = std::chrono::duration_cast(end - start); - std::cout << "Created " << num_iterations << " AudioFrames in " - << duration.count() << "ms" - << " (" << (num_iterations * 1000.0 / duration.count()) - << " frames/sec)" << std::endl; + std::cout << "Created " << num_iterations << " AudioFrames in " << duration.count() << "ms" + << " (" << (num_iterations * 1000.0 / duration.count()) << " frames/sec)" << std::endl; } // Stress test: Large buffer allocation @@ -69,18 +64,14 @@ TEST_F(AudioFrameStressTest, LargeBufferAllocation) { auto start = std::chrono::high_resolution_clock::now(); for (int i = 0; i < 100; ++i) { - AudioFrame frame = - AudioFrame::create(sample_rate, num_channels, samples_per_channel); - ASSERT_EQ(frame.total_samples(), - static_cast(num_channels * samples_per_channel)); + AudioFrame frame = AudioFrame::create(sample_rate, num_channels, samples_per_channel); + ASSERT_EQ(frame.total_samples(), static_cast(num_channels * samples_per_channel)); } auto end = std::chrono::high_resolution_clock::now(); - auto duration = - std::chrono::duration_cast(end - start); + auto duration = std::chrono::duration_cast(end - start); - std::cout << "Created 100 large (1 second, 8-channel) AudioFrames in " - << duration.count() << "ms" << std::endl; + std::cout << "Created 100 large (1 second, 8-channel) AudioFrames in " << duration.count() << "ms" << std::endl; } // Stress test: Concurrent frame creation from multiple threads @@ -103,19 +94,17 @@ TEST_F(AudioFrameStressTest, ConcurrentFrameCreation) { }); } - for (auto &thread : threads) { + for (auto& thread : threads) { thread.join(); } auto end = std::chrono::high_resolution_clock::now(); - auto duration = - std::chrono::duration_cast(end - start); + auto duration = std::chrono::duration_cast(end - start); EXPECT_EQ(total_frames.load(), num_threads * frames_per_thread); - std::cout << "Created " << total_frames.load() << " AudioFrames across " - << num_threads << " threads in " << duration.count() << "ms" - << std::endl; + std::cout << "Created " << total_frames.load() << " AudioFrames across " << num_threads << " threads in " + << duration.count() << "ms" << std::endl; } // Stress test: Memory pressure with many simultaneous frames @@ -132,18 +121,16 @@ TEST_F(AudioFrameStressTest, MemoryPressure) { } // Verify all frames are valid - for (const auto &frame : frames) { + for (const auto& frame : frames) { ASSERT_EQ(frame.sample_rate(), 48000); ASSERT_EQ(frame.num_channels(), 2); ASSERT_EQ(frame.samples_per_channel(), 960); } auto end = std::chrono::high_resolution_clock::now(); - auto duration = - std::chrono::duration_cast(end - start); + auto duration = std::chrono::duration_cast(end - start); - std::cout << "Held " << num_frames << " AudioFrames simultaneously in " - << duration.count() << "ms" << std::endl; + std::cout << "Held " << num_frames << " AudioFrames simultaneously in " << duration.count() << "ms" << std::endl; // Frames are destroyed when vector goes out of scope } @@ -157,7 +144,7 @@ TEST_F(AudioFrameStressTest, DataModificationUnderLoad) { for (int f = 0; f < num_frames; ++f) { AudioFrame frame = AudioFrame::create(48000, 2, 960); - auto &data = frame.data(); + auto& data = frame.data(); for (int m = 0; m < modifications_per_frame; ++m) { // Simulate audio processing @@ -168,12 +155,10 @@ TEST_F(AudioFrameStressTest, DataModificationUnderLoad) { } auto end = std::chrono::high_resolution_clock::now(); - auto duration = - std::chrono::duration_cast(end - start); + auto duration = std::chrono::duration_cast(end - start); - std::cout << "Modified " << num_frames << " frames " - << modifications_per_frame << " times each in " << duration.count() - << "ms" << std::endl; + std::cout << "Modified " << num_frames << " frames " << modifications_per_frame << " times each in " + << duration.count() << "ms" << std::endl; } // Stress test: Copy operations @@ -192,7 +177,7 @@ TEST_F(AudioFrameStressTest, CopyOperationsStress) { } // Verify all copies are independent - for (auto © : copies) { + for (auto& copy : copies) { ASSERT_EQ(copy.data()[0], 12345); copy.data()[0] = 0; // Modify copy } @@ -201,11 +186,9 @@ TEST_F(AudioFrameStressTest, CopyOperationsStress) { ASSERT_EQ(original.data()[0], 12345); auto end = std::chrono::high_resolution_clock::now(); - auto duration = - std::chrono::duration_cast(end - start); + auto duration = std::chrono::duration_cast(end - start); - std::cout << "Performed " << num_copies << " copy operations in " - << duration.count() << "ms" << std::endl; + std::cout << "Performed " << num_copies << " copy operations in " << duration.count() << "ms" << std::endl; } // Stress test: Move operations @@ -224,11 +207,9 @@ TEST_F(AudioFrameStressTest, MoveOperationsStress) { ASSERT_EQ(frame.sample_rate(), 48000); auto end = std::chrono::high_resolution_clock::now(); - auto duration = - std::chrono::duration_cast(end - start); + auto duration = std::chrono::duration_cast(end - start); - std::cout << "Performed " << num_moves << " move operations in " - << duration.count() << "ms" << std::endl; + std::cout << "Performed " << num_moves << " move operations in " << duration.count() << "ms" << std::endl; } // Stress test: Simulated real-time audio processing @@ -250,7 +231,7 @@ TEST_F(AudioFrameStressTest, SimulatedRealtimeProcessing) { AudioFrame frame = AudioFrame::create(sample_rate, 2, samples_per_frame); // Simulate processing (apply simple gain) - auto &data = frame.data(); + auto& data = frame.data(); for (size_t j = 0; j < data.size(); ++j) { data[j] = static_cast(data[j] * 0.8); } @@ -259,23 +240,16 @@ TEST_F(AudioFrameStressTest, SimulatedRealtimeProcessing) { } auto end = std::chrono::high_resolution_clock::now(); - auto duration = - std::chrono::duration_cast(end - start); + auto duration = std::chrono::duration_cast(end - start); - double processing_time_per_frame_us = - static_cast(duration.count()) / total_frames; + double processing_time_per_frame_us = static_cast(duration.count()) / total_frames; double available_time_per_frame_us = frame_size_ms * 1000.0; - std::cout << "Processed " << total_frames << " frames (" << duration_seconds - << "s of audio)" << std::endl; - std::cout << "Average processing time per frame: " - << processing_time_per_frame_us << "us" << std::endl; - std::cout << "Available time per frame: " << available_time_per_frame_us - << "us" << std::endl; - std::cout << "Processing overhead: " - << (processing_time_per_frame_us / available_time_per_frame_us * - 100) - << "%" << std::endl; + std::cout << "Processed " << total_frames << " frames (" << duration_seconds << "s of audio)" << std::endl; + std::cout << "Average processing time per frame: " << processing_time_per_frame_us << "us" << std::endl; + std::cout << "Available time per frame: " << available_time_per_frame_us << "us" << std::endl; + std::cout << "Processing overhead: " << (processing_time_per_frame_us / available_time_per_frame_us * 100) << "%" + << std::endl; // Processing should be fast enough for real-time EXPECT_LT(processing_time_per_frame_us, available_time_per_frame_us) diff --git a/src/tests/stress/test_latency_measurement.cpp b/src/tests/stress/test_latency_measurement.cpp index 9b6b30f8..4ba95deb 100644 --- a/src/tests/stress/test_latency_measurement.cpp +++ b/src/tests/stress/test_latency_measurement.cpp @@ -14,14 +14,15 @@ * limitations under the License. */ -#include "../benchmark/benchmark_utils.h" -#include "../common/test_common.h" -#include "trace/trace_event.h" #include #include #include #include +#include "../benchmark/benchmark_utils.h" +#include "../common/test_common.h" +#include "trace/trace_event.h" + using namespace livekit::test::benchmark; namespace livekit::test { @@ -30,8 +31,7 @@ namespace livekit::test { constexpr int kAudioSampleRate = 48000; constexpr int kAudioChannels = 1; constexpr int kAudioFrameDurationMs = 10; -constexpr int kSamplesPerFrame = - kAudioSampleRate * kAudioFrameDurationMs / 1000; +constexpr int kSamplesPerFrame = kAudioSampleRate * kAudioFrameDurationMs / 1000; // Energy threshold for detecting high-energy frames constexpr double kHighEnergyThreshold = 0.3; @@ -45,9 +45,8 @@ constexpr int kHighEnergyFramesPerPulse = 5; // ============================================================================= /// Calculate RMS energy of audio samples (normalized to [-1, 1] range) -static double calculateEnergy(const std::vector &samples) { - if (samples.empty()) - return 0.0; +static double calculateEnergy(const std::vector& samples) { + if (samples.empty()) return 0.0; double sum_squared = 0.0; for (int16_t sample : samples) { double normalized = static_cast(sample) / 32768.0; @@ -63,8 +62,7 @@ static std::vector generateHighEnergyFrame(int samples_per_channel) { const double amplitude = 30000.0; // Near max for int16 for (int i = 0; i < samples_per_channel; ++i) { double t = static_cast(i) / kAudioSampleRate; - int16_t sample = - static_cast(amplitude * std::sin(2.0 * M_PI * frequency * t)); + int16_t sample = static_cast(amplitude * std::sin(2.0 * M_PI * frequency * t)); for (int ch = 0; ch < kAudioChannels; ++ch) { data[i * kAudioChannels + ch] = sample; } @@ -77,9 +75,9 @@ static std::vector generateSilentFrame(int samples_per_channel) { return std::vector(samples_per_channel * kAudioChannels, 0); } -static const char *rtcStatsTypeName(const RtcStats &stats) { +static const char* rtcStatsTypeName(const RtcStats& stats) { return std::visit( - [](const auto &s) -> const char * { + [](const auto& s) -> const char* { using T = std::decay_t; if constexpr (std::is_same_v) { return "Codec"; @@ -118,55 +116,39 @@ static const char *rtcStatsTypeName(const RtcStats &stats) { stats.stats); } -static void printSessionStats(const std::vector &stats, - const std::string &label) { +static void printSessionStats(const std::vector& stats, const std::string& label) { std::cout << " " << label << " stats: " << stats.size() << std::endl; - for (const auto &stat : stats) { + for (const auto& stat : stats) { std::visit( - [&](const auto &s) { + [&](const auto& s) { using T = std::decay_t; if constexpr (std::is_same_v) { - std::cout << " [CandidatePair] id=" << s.rtc.id - << " rtt=" << std::fixed << std::setprecision(4) + std::cout << " [CandidatePair] id=" << s.rtc.id << " rtt=" << std::fixed << std::setprecision(4) << s.candidate_pair.current_round_trip_time << "s" - << " total_rtt=" << s.candidate_pair.total_round_trip_time - << "s" - << " in_bitrate=" - << s.candidate_pair.available_incoming_bitrate - << " out_bitrate=" - << s.candidate_pair.available_outgoing_bitrate + << " total_rtt=" << s.candidate_pair.total_round_trip_time << "s" + << " in_bitrate=" << s.candidate_pair.available_incoming_bitrate + << " out_bitrate=" << s.candidate_pair.available_outgoing_bitrate << " bytes_sent=" << s.candidate_pair.bytes_sent - << " bytes_received=" << s.candidate_pair.bytes_received - << std::endl; + << " bytes_received=" << s.candidate_pair.bytes_received << std::endl; } else if constexpr (std::is_same_v) { std::cout << " [Transport] id=" << s.rtc.id - << " selected_pair=" - << s.transport.selected_candidate_pair_id + << " selected_pair=" << s.transport.selected_candidate_pair_id << " packets_sent=" << s.transport.packets_sent << " packets_received=" << s.transport.packets_received - << " bytes_sent=" << s.transport.bytes_sent - << " bytes_received=" << s.transport.bytes_received + << " bytes_sent=" << s.transport.bytes_sent << " bytes_received=" << s.transport.bytes_received << std::endl; } else if constexpr (std::is_same_v) { std::cout << " [PeerConnection] id=" << s.rtc.id << " data_channels_opened=" << s.pc.data_channels_opened - << " data_channels_closed=" << s.pc.data_channels_closed - << std::endl; + << " data_channels_closed=" << s.pc.data_channels_closed << std::endl; } else if constexpr (std::is_same_v) { - std::cout << " [InboundRtp] id=" << s.rtc.id - << " kind=" << s.stream.kind - << " packets_lost=" << s.received.packets_lost - << " jitter=" << std::fixed << std::setprecision(6) - << s.received.jitter - << " bytes_received=" << s.inbound.bytes_received - << std::endl; + std::cout << " [InboundRtp] id=" << s.rtc.id << " kind=" << s.stream.kind + << " packets_lost=" << s.received.packets_lost << " jitter=" << std::fixed << std::setprecision(6) + << s.received.jitter << " bytes_received=" << s.inbound.bytes_received << std::endl; } else if constexpr (std::is_same_v) { - std::cout << " [OutboundRtp] id=" << s.rtc.id - << " kind=" << s.stream.kind - << " packets_sent=" << s.sent.packets_sent - << " bytes_sent=" << s.sent.bytes_sent - << " target_bitrate=" << std::fixed - << std::setprecision(2) << s.outbound.target_bitrate + std::cout << " [OutboundRtp] id=" << s.rtc.id << " kind=" << s.stream.kind + << " packets_sent=" << s.sent.packets_sent << " bytes_sent=" << s.sent.bytes_sent + << " target_bitrate=" << std::fixed << std::setprecision(2) << s.outbound.target_bitrate << std::endl; } }, @@ -174,88 +156,62 @@ static void printSessionStats(const std::vector &stats, } std::map type_counts; - for (const auto &stat : stats) { + for (const auto& stat : stats) { type_counts[rtcStatsTypeName(stat)]++; } if (!type_counts.empty()) { std::cout << " " << label << " type counts:"; - for (const auto &kv : type_counts) { + for (const auto& kv : type_counts) { std::cout << " " << kv.first << "=" << kv.second; } std::cout << std::endl; } } -static void -printAudioLatencyAndNetworkSummary(const std::vector &stats, - const std::string &label) { +static void printAudioLatencyAndNetworkSummary(const std::vector& stats, const std::string& label) { std::cout << " " << label << " audio/network summary:" << std::endl; bool printed = false; - for (const auto &stat : stats) { + for (const auto& stat : stats) { std::visit( - [&](const auto &s) { + [&](const auto& s) { using T = std::decay_t; if constexpr (std::is_same_v) { if (s.stream.kind == "audio") { printed = true; - double emitted = - static_cast(s.inbound.jitter_buffer_emitted_count); - double avg_jb_delay_s = - emitted > 0.0 ? (s.inbound.jitter_buffer_delay / emitted) - : 0.0; - double avg_jb_target_s = - emitted > 0.0 - ? (s.inbound.jitter_buffer_target_delay / emitted) - : 0.0; - double avg_processing_s = - emitted > 0.0 ? (s.inbound.total_processing_delay / emitted) - : 0.0; - - std::cout << " [InboundAudio] id=" << s.rtc.id - << " packets_received=" << s.received.packets_received - << " packets_lost=" << s.received.packets_lost - << " jitter=" << std::fixed << std::setprecision(6) - << s.received.jitter + double emitted = static_cast(s.inbound.jitter_buffer_emitted_count); + double avg_jb_delay_s = emitted > 0.0 ? (s.inbound.jitter_buffer_delay / emitted) : 0.0; + double avg_jb_target_s = emitted > 0.0 ? (s.inbound.jitter_buffer_target_delay / emitted) : 0.0; + double avg_processing_s = emitted > 0.0 ? (s.inbound.total_processing_delay / emitted) : 0.0; + + std::cout << " [InboundAudio] id=" << s.rtc.id << " packets_received=" << s.received.packets_received + << " packets_lost=" << s.received.packets_lost << " jitter=" << std::fixed + << std::setprecision(6) << s.received.jitter << " jb_delay_total_s=" << s.inbound.jitter_buffer_delay - << " jb_target_total_s=" - << s.inbound.jitter_buffer_target_delay - << " jb_emitted=" - << s.inbound.jitter_buffer_emitted_count - << " jb_delay_avg_ms=" << std::setprecision(2) - << (avg_jb_delay_s * 1000.0) + << " jb_target_total_s=" << s.inbound.jitter_buffer_target_delay + << " jb_emitted=" << s.inbound.jitter_buffer_emitted_count + << " jb_delay_avg_ms=" << std::setprecision(2) << (avg_jb_delay_s * 1000.0) << " jb_target_avg_ms=" << (avg_jb_target_s * 1000.0) << " processing_avg_ms=" << (avg_processing_s * 1000.0) << " concealed_samples=" << s.inbound.concealed_samples - << " inserted_for_decel=" - << s.inbound.inserted_samples_for_deceleration - << " removed_for_accel=" - << s.inbound.removed_samples_for_acceleration - << std::endl; + << " inserted_for_decel=" << s.inbound.inserted_samples_for_deceleration + << " removed_for_accel=" << s.inbound.removed_samples_for_acceleration << std::endl; } } else if constexpr (std::is_same_v) { printed = true; - std::cout << " [CandidatePair] id=" << s.rtc.id - << " rtt_ms=" << std::fixed << std::setprecision(2) - << (s.candidate_pair.current_round_trip_time * 1000.0) - << " total_rtt_s=" << std::setprecision(4) - << s.candidate_pair.total_round_trip_time - << " bytes_sent=" << s.candidate_pair.bytes_sent + std::cout << " [CandidatePair] id=" << s.rtc.id << " rtt_ms=" << std::fixed << std::setprecision(2) + << (s.candidate_pair.current_round_trip_time * 1000.0) << " total_rtt_s=" << std::setprecision(4) + << s.candidate_pair.total_round_trip_time << " bytes_sent=" << s.candidate_pair.bytes_sent << " bytes_received=" << s.candidate_pair.bytes_received - << " in_bitrate=" - << s.candidate_pair.available_incoming_bitrate - << " out_bitrate=" - << s.candidate_pair.available_outgoing_bitrate - << std::endl; + << " in_bitrate=" << s.candidate_pair.available_incoming_bitrate + << " out_bitrate=" << s.candidate_pair.available_outgoing_bitrate << std::endl; } else if constexpr (std::is_same_v) { printed = true; std::cout << " [Transport] id=" << s.rtc.id - << " selected_pair=" - << s.transport.selected_candidate_pair_id + << " selected_pair=" << s.transport.selected_candidate_pair_id << " packets_sent=" << s.transport.packets_sent << " packets_received=" << s.transport.packets_received - << " bytes_sent=" << s.transport.bytes_sent - << " bytes_received=" << s.transport.bytes_received + << " bytes_sent=" << s.transport.bytes_sent << " bytes_received=" << s.transport.bytes_received << std::endl; } }, @@ -297,26 +253,19 @@ TEST_F(LatencyMeasurementTest, ConnectionTime) { if (connected) { successful_connections++; - double latency_ms = - std::chrono::duration(end - start).count(); + double latency_ms = std::chrono::duration(end - start).count(); stats.addMeasurement(latency_ms); // Get room and participant session IDs for debugging auto room_info = room->room_info(); - std::string room_sid = - room_info.sid.has_value() ? room_info.sid.value() : "unknown"; - std::string participant_sid = room->localParticipant() - ? room->localParticipant()->sid() - : "unknown"; + std::string room_sid = room_info.sid.has_value() ? room_info.sid.value() : "unknown"; + std::string participant_sid = room->localParticipant() ? room->localParticipant()->sid() : "unknown"; - std::cout << " Iteration " << (i + 1) << ": " << std::fixed - << std::setprecision(2) << latency_ms << " ms" - << " | participant_sid=" << participant_sid - << " | room_sid=" << room_sid << std::endl; + std::cout << " Iteration " << (i + 1) << ": " << std::fixed << std::setprecision(2) << latency_ms << " ms" + << " | participant_sid=" << participant_sid << " | room_sid=" << room_sid << std::endl; } else { - std::cout << " Iteration " << (i + 1) << ": FAILED to connect" - << std::endl; + std::cout << " Iteration " << (i + 1) << ": FAILED to connect" << std::endl; } // Small delay between iterations to allow cleanup @@ -326,8 +275,7 @@ TEST_F(LatencyMeasurementTest, ConnectionTime) { // Tracing is automatically handled by LiveKitTestBase // Stats for Room::Connect will be printed in TearDown() - EXPECT_GT(successful_connections, 0) - << "At least one connection should succeed"; + EXPECT_GT(successful_connections, 0) << "At least one connection should succeed"; } // ============================================================================= @@ -335,34 +283,28 @@ TEST_F(LatencyMeasurementTest, ConnectionTime) { // ============================================================================= class AudioLatencyDelegate : public RoomDelegate { public: - void onTrackSubscribed(Room &, const TrackSubscribedEvent &event) override { + void onTrackSubscribed(Room&, const TrackSubscribedEvent& event) override { std::lock_guard lock(mutex_); - if (event.track && event.track->kind() == TrackKind::KIND_AUDIO && - event.participant) { + if (event.track && event.track->kind() == TrackKind::KIND_AUDIO && event.participant) { subscribed_audio_track_ = event.track; - subscribed_audio_tracks_by_participant_[event.participant->identity()] = - event.track; + subscribed_audio_tracks_by_participant_[event.participant->identity()] = event.track; track_cv_.notify_all(); } } std::shared_ptr waitForAudioTrack(std::chrono::milliseconds timeout) { std::unique_lock lock(mutex_); - if (track_cv_.wait_for(lock, timeout, [this] { - return subscribed_audio_track_ != nullptr; - })) { + if (track_cv_.wait_for(lock, timeout, [this] { return subscribed_audio_track_ != nullptr; })) { return subscribed_audio_track_; } return nullptr; } - std::shared_ptr - waitForAudioTrackFromParticipant(const std::string &identity, - std::chrono::milliseconds timeout) { + std::shared_ptr waitForAudioTrackFromParticipant(const std::string& identity, + std::chrono::milliseconds timeout) { std::unique_lock lock(mutex_); - if (track_cv_.wait_for(lock, timeout, [this, &identity] { - return subscribed_audio_tracks_by_participant_.count(identity) > 0; - })) { + if (track_cv_.wait_for(lock, timeout, + [this, &identity] { return subscribed_audio_tracks_by_participant_.count(identity) > 0; })) { return subscribed_audio_tracks_by_participant_[identity]; } return nullptr; @@ -372,16 +314,14 @@ class AudioLatencyDelegate : public RoomDelegate { std::mutex mutex_; std::condition_variable track_cv_; std::shared_ptr subscribed_audio_track_; - std::map> - subscribed_audio_tracks_by_participant_; + std::map> subscribed_audio_tracks_by_participant_; }; TEST_F(LatencyMeasurementTest, AudioLatency) { skipIfNotConfigured(); std::cout << "\n=== Audio Latency Measurement Test ===" << std::endl; - std::cout << "Using energy detection to measure audio round-trip latency" - << std::endl; + std::cout << "Using energy detection to measure audio round-trip latency" << std::endl; // Register custom trace events to analyze at test end addTraceEventToAnalyze("audio_latency"); @@ -394,8 +334,7 @@ TEST_F(LatencyMeasurementTest, AudioLatency) { RoomOptions options; options.auto_subscribe = true; - bool receiver_connected = - receiver_room->Connect(config_.url, config_.token_b, options); + bool receiver_connected = receiver_room->Connect(config_.url, config_.token_b, options); ASSERT_TRUE(receiver_connected) << "Receiver failed to connect"; std::string receiver_identity = receiver_room->localParticipant()->identity(); @@ -403,35 +342,28 @@ TEST_F(LatencyMeasurementTest, AudioLatency) { // Create sender room (using token_a) auto sender_room = std::make_unique(); - bool sender_connected = - sender_room->Connect(config_.url, config_.token_a, options); + bool sender_connected = sender_room->Connect(config_.url, config_.token_a, options); ASSERT_TRUE(sender_connected) << "Sender failed to connect"; std::string sender_identity = sender_room->localParticipant()->identity(); std::cout << "Sender connected as: " << sender_identity << std::endl; // Wait for sender to be visible to receiver - ASSERT_TRUE(waitForParticipant(receiver_room.get(), sender_identity, 10s)) - << "Sender not visible to receiver"; + ASSERT_TRUE(waitForParticipant(receiver_room.get(), sender_identity, 10s)) << "Sender not visible to receiver"; // Create audio source in real-time mode (queue_size_ms = 0) - auto audio_source = - std::make_shared(kAudioSampleRate, kAudioChannels, 0); - auto audio_track = - LocalAudioTrack::createLocalAudioTrack("latency-test", audio_source); + auto audio_source = std::make_shared(kAudioSampleRate, kAudioChannels, 0); + auto audio_track = LocalAudioTrack::createLocalAudioTrack("latency-test", audio_source); TrackPublishOptions publish_options; sender_room->localParticipant()->publishTrack(audio_track, publish_options); - ASSERT_NE(audio_track->publication(), nullptr) - << "Failed to publish audio track"; + ASSERT_NE(audio_track->publication(), nullptr) << "Failed to publish audio track"; - std::cout << "Audio track published, waiting for subscription..." - << std::endl; + std::cout << "Audio track published, waiting for subscription..." << std::endl; // Wait for receiver to subscribe to the audio track auto subscribed_track = receiver_delegate.waitForAudioTrack(10s); - ASSERT_NE(subscribed_track, nullptr) - << "Receiver did not subscribe to audio track"; + ASSERT_NE(subscribed_track, nullptr) << "Receiver did not subscribe to audio track"; std::cout << "Audio track subscribed, creating audio stream..." << std::endl; @@ -459,24 +391,18 @@ TEST_F(LatencyMeasurementTest, AudioLatency) { if (waiting_for_echo.load() && energy > kHighEnergyThreshold) { uint64_t receive_time_us = - std::chrono::duration_cast( - std::chrono::steady_clock::now().time_since_epoch()) + std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()) .count(); uint64_t send_time_us = last_high_energy_send_time_us.load(); if (send_time_us > 0) { - double latency_ms = - static_cast(receive_time_us - send_time_us) / 1000.0; + double latency_ms = static_cast(receive_time_us - send_time_us) / 1000.0; if (latency_ms > 0 && latency_ms < 5000) { // Sanity check // End the async trace span - TRACE_EVENT_ASYNC_END1(kCategoryAudio, "audio_latency", - current_pulse_id.load(), "latency_ms", - latency_ms); + TRACE_EVENT_ASYNC_END1(kCategoryAudio, "audio_latency", current_pulse_id.load(), "latency_ms", latency_ms); successful_measurements++; - std::cout << " Audio latency: " << std::fixed - << std::setprecision(2) << latency_ms << " ms" - << " (energy: " << std::setprecision(3) << energy << ")" - << std::endl; + std::cout << " Audio latency: " << std::fixed << std::setprecision(2) << latency_ms << " ms" + << " (energy: " << std::setprecision(3) << energy << ")" << std::endl; } waiting_for_echo.store(false); } @@ -494,8 +420,7 @@ TEST_F(LatencyMeasurementTest, AudioLatency) { int high_energy_frames_remaining = 0; auto next_frame_time = std::chrono::steady_clock::now(); - const auto frame_duration = - std::chrono::milliseconds(kAudioFrameDurationMs); + const auto frame_duration = std::chrono::milliseconds(kAudioFrameDurationMs); while (running.load() && pulses_sent < total_pulses) { std::this_thread::sleep_until(next_frame_time); @@ -506,12 +431,10 @@ TEST_F(LatencyMeasurementTest, AudioLatency) { // Check for echo timeout if (waiting_for_echo.load() && pulse_send_time > 0) { uint64_t now_us = - std::chrono::duration_cast( - std::chrono::steady_clock::now().time_since_epoch()) + std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()) .count(); if (now_us - pulse_send_time > kEchoTimeoutUs) { - std::cout << " Echo timeout for pulse " << pulses_sent - << ", moving on..." << std::endl; + std::cout << " Echo timeout for pulse " << pulses_sent << ", moving on..." << std::endl; waiting_for_echo.store(false); missed_pulses++; pulse_send_time = 0; @@ -522,15 +445,13 @@ TEST_F(LatencyMeasurementTest, AudioLatency) { if (high_energy_frames_remaining > 0) { frame_data = generateHighEnergyFrame(kSamplesPerFrame); high_energy_frames_remaining--; - } else if (frame_count % frames_between_pulses == 0 && - !waiting_for_echo.load()) { + } else if (frame_count % frames_between_pulses == 0 && !waiting_for_echo.load()) { // Start a new pulse frame_data = generateHighEnergyFrame(kSamplesPerFrame); high_energy_frames_remaining = kHighEnergyFramesPerPulse - 1; pulse_send_time = - std::chrono::duration_cast( - std::chrono::steady_clock::now().time_since_epoch()) + std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()) .count(); last_high_energy_send_time_us.store(pulse_send_time); waiting_for_echo.store(true); @@ -538,21 +459,19 @@ TEST_F(LatencyMeasurementTest, AudioLatency) { // Begin async trace span current_pulse_id.store(static_cast(pulses_sent)); - TRACE_EVENT_ASYNC_BEGIN1(kCategoryAudio, "audio_latency", - current_pulse_id.load(), "pulse", pulses_sent); + TRACE_EVENT_ASYNC_BEGIN1(kCategoryAudio, "audio_latency", current_pulse_id.load(), "pulse", pulses_sent); - std::cout << "Sent pulse " << pulses_sent << "/" << total_pulses << " (" - << kHighEnergyFramesPerPulse << " frames)" << std::endl; + std::cout << "Sent pulse " << pulses_sent << "/" << total_pulses << " (" << kHighEnergyFramesPerPulse + << " frames)" << std::endl; } else { frame_data = generateSilentFrame(kSamplesPerFrame); } - AudioFrame frame(std::move(frame_data), kAudioSampleRate, kAudioChannels, - kSamplesPerFrame); + AudioFrame frame(std::move(frame_data), kAudioSampleRate, kAudioChannels, kSamplesPerFrame); try { audio_source->captureFrame(frame); - } catch (const std::exception &e) { + } catch (const std::exception& e) { std::cerr << "Error capturing frame: " << e.what() << std::endl; } @@ -573,22 +492,19 @@ TEST_F(LatencyMeasurementTest, AudioLatency) { } // Clean up - sender_room->localParticipant()->unpublishTrack( - audio_track->publication()->sid()); + sender_room->localParticipant()->unpublishTrack(audio_track->publication()->sid()); // Tracing is automatically handled by LiveKitTestBase // Stats for audio_latency will be printed in TearDown() - EXPECT_GT(successful_measurements.load(), 0) - << "At least one audio latency measurement should be recorded"; + EXPECT_GT(successful_measurements.load(), 0) << "At least one audio latency measurement should be recorded"; } TEST_F(LatencyMeasurementTest, FullDeplexAudioLatency) { skipIfNotConfigured(); std::cout << "\n=== FullDeplexAudioLatency Test ===" << std::endl; - std::cout << "Measuring A->B, B->A, and A->B->A using audio ping-pong" - << std::endl; + std::cout << "Measuring A->B, B->A, and A->B->A using audio ping-pong" << std::endl; // Register custom trace events to analyze at test end addTraceEventToAnalyze("A_to_B"); @@ -605,10 +521,8 @@ TEST_F(LatencyMeasurementTest, FullDeplexAudioLatency) { RoomOptions options; options.auto_subscribe = true; - ASSERT_TRUE(room_a->Connect(config_.url, config_.token_a, options)) - << "Participant A failed to connect"; - ASSERT_TRUE(room_b->Connect(config_.url, config_.token_b, options)) - << "Participant B failed to connect"; + ASSERT_TRUE(room_a->Connect(config_.url, config_.token_a, options)) << "Participant A failed to connect"; + ASSERT_TRUE(room_b->Connect(config_.url, config_.token_b, options)) << "Participant B failed to connect"; std::string id_a = room_a->localParticipant()->identity(); std::string id_b = room_b->localParticipant()->identity(); @@ -618,14 +532,10 @@ TEST_F(LatencyMeasurementTest, FullDeplexAudioLatency) { ASSERT_TRUE(waitForParticipant(room_a.get(), id_b, 10s)) << "A cannot see B"; ASSERT_TRUE(waitForParticipant(room_b.get(), id_a, 10s)) << "B cannot see A"; - auto source_a = - std::make_shared(kAudioSampleRate, kAudioChannels, 0); - auto source_b = - std::make_shared(kAudioSampleRate, kAudioChannels, 1000); - auto track_a = - LocalAudioTrack::createLocalAudioTrack("full-duplex-a", source_a); - auto track_b = - LocalAudioTrack::createLocalAudioTrack("full-duplex-b", source_b); + auto source_a = std::make_shared(kAudioSampleRate, kAudioChannels, 0); + auto source_b = std::make_shared(kAudioSampleRate, kAudioChannels, 1000); + auto track_a = LocalAudioTrack::createLocalAudioTrack("full-duplex-a", source_a); + auto track_b = LocalAudioTrack::createLocalAudioTrack("full-duplex-b", source_b); ASSERT_NE(track_a, nullptr); ASSERT_NE(track_b, nullptr); @@ -633,19 +543,15 @@ TEST_F(LatencyMeasurementTest, FullDeplexAudioLatency) { room_a->localParticipant()->publishTrack(track_a, publish_options); room_b->localParticipant()->publishTrack(track_b, publish_options); - auto track_from_a_on_b = - delegate_b.waitForAudioTrackFromParticipant(id_a, 10s); - auto track_from_b_on_a = - delegate_a.waitForAudioTrackFromParticipant(id_b, 10s); + auto track_from_a_on_b = delegate_b.waitForAudioTrackFromParticipant(id_a, 10s); + auto track_from_b_on_a = delegate_a.waitForAudioTrackFromParticipant(id_b, 10s); ASSERT_NE(track_from_a_on_b, nullptr) << "B did not subscribe to A audio"; ASSERT_NE(track_from_b_on_a, nullptr) << "A did not subscribe to B audio"; AudioStream::Options stream_options; stream_options.capacity = 100; - auto stream_b_recv_a = - AudioStream::fromTrack(track_from_a_on_b, stream_options); - auto stream_a_recv_b = - AudioStream::fromTrack(track_from_b_on_a, stream_options); + auto stream_b_recv_a = AudioStream::fromTrack(track_from_a_on_b, stream_options); + auto stream_a_recv_b = AudioStream::fromTrack(track_from_b_on_a, stream_options); ASSERT_NE(stream_b_recv_a, nullptr); ASSERT_NE(stream_a_recv_b, nullptr); @@ -667,9 +573,8 @@ TEST_F(LatencyMeasurementTest, FullDeplexAudioLatency) { constexpr int kPrePulseSilenceFrames = 50; // 500ms at 10ms/frame constexpr uint64_t kPulseTimeoutUs = 8000000; // 8 seconds constexpr int kBMaxResponseFrames = 50; // 500ms at 10ms/frame - constexpr double kMinValidOneWayMs = - 10.0; // Filter impossible matches (< 10ms) - constexpr double kMinValidBToAMs = 10.0; // Filter impossible matches (< 10ms) + constexpr double kMinValidOneWayMs = 10.0; // Filter impossible matches (< 10ms) + constexpr double kMinValidBToAMs = 10.0; // Filter impossible matches (< 10ms) // B receives A pulses and sends response frames std::thread b_receiver_thread([&]() { @@ -685,15 +590,13 @@ TEST_F(LatencyMeasurementTest, FullDeplexAudioLatency) { } uint64_t detect_us = - std::chrono::duration_cast( - std::chrono::steady_clock::now().time_since_epoch()) + std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()) .count(); uint64_t send_from_a_us = a_send_us.load(); if (send_from_a_us == 0 || detect_us <= send_from_a_us) { continue; } - double a_to_b_ms = - static_cast(detect_us - send_from_a_us) / 1000.0; + double a_to_b_ms = static_cast(detect_us - send_from_a_us) / 1000.0; if (a_to_b_ms < kMinValidOneWayMs || a_to_b_ms > 5000) { continue; } @@ -702,33 +605,25 @@ TEST_F(LatencyMeasurementTest, FullDeplexAudioLatency) { b_responded_pulse_id.store(pulse_id); // End the A->B trace span - TRACE_EVENT_ASYNC_END1(kCategoryAudio, "A_to_B", - static_cast(pulse_id), "latency_ms", - a_to_b_ms); + TRACE_EVENT_ASYNC_END1(kCategoryAudio, "A_to_B", static_cast(pulse_id), "latency_ms", a_to_b_ms); a_to_b_count++; - std::cout << " A->B latency: " << std::fixed << std::setprecision(2) - << a_to_b_ms << " ms" << std::endl; + std::cout << " A->B latency: " << std::fixed << std::setprecision(2) << a_to_b_ms << " ms" << std::endl; // Begin B->A trace span - TRACE_EVENT_ASYNC_BEGIN1(kCategoryAudio, "B_to_A", - static_cast(pulse_id), "pulse", - pulse_id); + TRACE_EVENT_ASYNC_BEGIN1(kCategoryAudio, "B_to_A", static_cast(pulse_id), "pulse", pulse_id); for (int i = 0; i < kBMaxResponseFrames; ++i) { std::vector pulse = generateHighEnergyFrame(kSamplesPerFrame); - AudioFrame response_frame(std::move(pulse), kAudioSampleRate, - kAudioChannels, kSamplesPerFrame); + AudioFrame response_frame(std::move(pulse), kAudioSampleRate, kAudioChannels, kSamplesPerFrame); try { if (i == 0) { - b_send_us.store( - std::chrono::duration_cast( - std::chrono::steady_clock::now().time_since_epoch()) - .count()); + b_send_us.store(std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()) + .count()); } source_b->captureFrame(response_frame); - } catch (const std::exception &e) { - std::cerr << "Error sending B response frame: " << e.what() - << std::endl; + } catch (const std::exception& e) { + std::cerr << "Error sending B response frame: " << e.what() << std::endl; break; } if (!waiting_for_response.load()) { @@ -743,8 +638,7 @@ TEST_F(LatencyMeasurementTest, FullDeplexAudioLatency) { std::thread a_receiver_thread([&]() { AudioFrameEvent event; while (running.load() && stream_a_recv_b->read(event)) { - if (!waiting_for_response.load() || - calculateEnergy(event.frame.data()) <= kHighEnergyThreshold) { + if (!waiting_for_response.load() || calculateEnergy(event.frame.data()) <= kHighEnergyThreshold) { continue; } @@ -754,40 +648,30 @@ TEST_F(LatencyMeasurementTest, FullDeplexAudioLatency) { } uint64_t receive_us = - std::chrono::duration_cast( - std::chrono::steady_clock::now().time_since_epoch()) + std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()) .count(); uint64_t send_from_a_us = a_send_us.load(); uint64_t send_from_b_us = b_send_us.load(); int responded_pulse_id = b_responded_pulse_id.load(); - if (responded_pulse_id != pulse_id || send_from_b_us == 0 || - receive_us < send_from_b_us) { + if (responded_pulse_id != pulse_id || send_from_b_us == 0 || receive_us < send_from_b_us) { continue; } a_received_pulse_id.store(pulse_id); - double b_to_a_ms = - static_cast(receive_us - send_from_b_us) / 1000.0; + double b_to_a_ms = static_cast(receive_us - send_from_b_us) / 1000.0; if (b_to_a_ms >= kMinValidBToAMs && b_to_a_ms < 5000) { - TRACE_EVENT_ASYNC_END1(kCategoryAudio, "B_to_A", - static_cast(pulse_id), "latency_ms", - b_to_a_ms); + TRACE_EVENT_ASYNC_END1(kCategoryAudio, "B_to_A", static_cast(pulse_id), "latency_ms", b_to_a_ms); b_to_a_count++; - std::cout << " B->A latency: " << std::fixed << std::setprecision(2) - << b_to_a_ms << " ms" << std::endl; + std::cout << " B->A latency: " << std::fixed << std::setprecision(2) << b_to_a_ms << " ms" << std::endl; } if (send_from_a_us > 0) { - double rtt_ms = - static_cast(receive_us - send_from_a_us) / 1000.0; + double rtt_ms = static_cast(receive_us - send_from_a_us) / 1000.0; if (rtt_ms > 0 && rtt_ms < 10000) { - TRACE_EVENT_ASYNC_END1(kCategoryAudio, "round_trip", - static_cast(pulse_id), "latency_ms", - rtt_ms); + TRACE_EVENT_ASYNC_END1(kCategoryAudio, "round_trip", static_cast(pulse_id), "latency_ms", rtt_ms); round_trip_count++; - std::cout << " A->B->A latency: " << std::fixed - << std::setprecision(2) << rtt_ms << " ms" << std::endl; + std::cout << " A->B->A latency: " << std::fixed << std::setprecision(2) << rtt_ms << " ms" << std::endl; } } @@ -800,24 +684,20 @@ TEST_F(LatencyMeasurementTest, FullDeplexAudioLatency) { // A sends ping pulses std::thread a_sender_thread([&]() { auto next_frame_time = std::chrono::steady_clock::now(); - const auto frame_duration = - std::chrono::milliseconds(kAudioFrameDurationMs); + const auto frame_duration = std::chrono::milliseconds(kAudioFrameDurationMs); int pulses_sent = 0; uint64_t pulse_start_us = 0; - while (running.load() && - (pulses_sent < kTotalPulses || waiting_for_response.load())) { + while (running.load() && (pulses_sent < kTotalPulses || waiting_for_response.load())) { std::this_thread::sleep_until(next_frame_time); next_frame_time += frame_duration; if (waiting_for_response.load()) { uint64_t now_us = - std::chrono::duration_cast( - std::chrono::steady_clock::now().time_since_epoch()) + std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()) .count(); if (pulse_start_us > 0 && now_us - pulse_start_us > kPulseTimeoutUs) { - std::cout << " Timeout waiting for B response to pulse " - << active_pulse_id.load() << std::endl; + std::cout << " Timeout waiting for B response to pulse " << active_pulse_id.load() << std::endl; waiting_for_response.store(false); active_pulse_id.store(0); pre_pulse_silence_frames_remaining.store(kPrePulseSilenceFrames); @@ -842,34 +722,27 @@ TEST_F(LatencyMeasurementTest, FullDeplexAudioLatency) { b_send_us.store(0); pulse_start_us = - std::chrono::duration_cast( - std::chrono::steady_clock::now().time_since_epoch()) + std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()) .count(); a_send_us.store(pulse_start_us); waiting_for_response.store(true); // Begin trace spans for A->B and round-trip - TRACE_EVENT_ASYNC_BEGIN1(kCategoryAudio, "A_to_B", - static_cast(pulse_id), "pulse", - pulse_id); - TRACE_EVENT_ASYNC_BEGIN1(kCategoryAudio, "round_trip", - static_cast(pulse_id), "pulse", - pulse_id); + TRACE_EVENT_ASYNC_BEGIN1(kCategoryAudio, "A_to_B", static_cast(pulse_id), "pulse", pulse_id); + TRACE_EVENT_ASYNC_BEGIN1(kCategoryAudio, "round_trip", static_cast(pulse_id), "pulse", pulse_id); frame_data = generateHighEnergyFrame(kSamplesPerFrame); - std::cout << "Sent ping pulse " << pulse_id << "/" << kTotalPulses - << std::endl; + std::cout << "Sent ping pulse " << pulse_id << "/" << kTotalPulses << std::endl; } } else { frame_data = generateSilentFrame(kSamplesPerFrame); } - AudioFrame frame(std::move(frame_data), kAudioSampleRate, kAudioChannels, - kSamplesPerFrame); + AudioFrame frame(std::move(frame_data), kAudioSampleRate, kAudioChannels, kSamplesPerFrame); try { source_a->captureFrame(frame); - } catch (const std::exception &e) { + } catch (const std::exception& e) { std::cerr << "Error sending A frame: " << e.what() << std::endl; } } @@ -897,12 +770,9 @@ TEST_F(LatencyMeasurementTest, FullDeplexAudioLatency) { room_b->localParticipant()->unpublishTrack(track_b->publication()->sid()); } - EXPECT_GT(round_trip_count.load(), 0) - << "At least one round-trip latency measurement should be recorded"; - EXPECT_GT(a_to_b_count.load(), 0) - << "At least one A->B latency measurement should be recorded"; - EXPECT_GT(b_to_a_count.load(), 0) - << "At least one B->A latency measurement should be recorded"; + EXPECT_GT(round_trip_count.load(), 0) << "At least one round-trip latency measurement should be recorded"; + EXPECT_GT(a_to_b_count.load(), 0) << "At least one A->B latency measurement should be recorded"; + EXPECT_GT(b_to_a_count.load(), 0) << "At least one B->A latency measurement should be recorded"; } } // namespace livekit::test diff --git a/src/tests/stress/test_room_stress.cpp b/src/tests/stress/test_room_stress.cpp index e98b32d2..f20c3fd6 100644 --- a/src/tests/stress/test_room_stress.cpp +++ b/src/tests/stress/test_room_stress.cpp @@ -14,10 +14,11 @@ * limitations under the License. */ -#include -#include #include #include + +#include +#include #include #include #include @@ -26,9 +27,7 @@ namespace livekit::test { class RoomStressTest : public ::testing::Test { protected: - void SetUp() override { - livekit::initialize(livekit::LogLevel::Info, livekit::LogSink::kConsole); - } + void SetUp() override { livekit::initialize(livekit::LogLevel::Info, livekit::LogSink::kConsole); } void TearDown() override { livekit::shutdown(); } }; @@ -45,13 +44,10 @@ TEST_F(RoomStressTest, RapidRoomCreation) { } auto end = std::chrono::high_resolution_clock::now(); - auto duration = - std::chrono::duration_cast(end - start); + auto duration = std::chrono::duration_cast(end - start); - std::cout << "Created and destroyed " << num_iterations << " Room objects in " - << duration.count() << "ms" - << " (" << (num_iterations * 1000.0 / duration.count()) - << " rooms/sec)" << std::endl; + std::cout << "Created and destroyed " << num_iterations << " Room objects in " << duration.count() << "ms" + << " (" << (num_iterations * 1000.0 / duration.count()) << " rooms/sec)" << std::endl; } // Stress test: Multiple simultaneous Room objects @@ -67,17 +63,15 @@ TEST_F(RoomStressTest, MultipleSimultaneousRooms) { } // Verify all rooms are valid - for (const auto &room : rooms) { + for (const auto& room : rooms) { ASSERT_NE(room, nullptr); ASSERT_EQ(room->localParticipant(), nullptr); } auto end = std::chrono::high_resolution_clock::now(); - auto duration = - std::chrono::duration_cast(end - start); + auto duration = std::chrono::duration_cast(end - start); - std::cout << "Held " << num_rooms << " Room objects simultaneously in " - << duration.count() << "ms" << std::endl; + std::cout << "Held " << num_rooms << " Room objects simultaneously in " << duration.count() << "ms" << std::endl; // Rooms are destroyed when vector goes out of scope } @@ -102,19 +96,17 @@ TEST_F(RoomStressTest, ConcurrentRoomCreation) { }); } - for (auto &thread : threads) { + for (auto& thread : threads) { thread.join(); } auto end = std::chrono::high_resolution_clock::now(); - auto duration = - std::chrono::duration_cast(end - start); + auto duration = std::chrono::duration_cast(end - start); EXPECT_EQ(total_rooms.load(), num_threads * rooms_per_thread); - std::cout << "Created " << total_rooms.load() << " Room objects across " - << num_threads << " threads in " << duration.count() << "ms" - << std::endl; + std::cout << "Created " << total_rooms.load() << " Room objects across " << num_threads << " threads in " + << duration.count() << "ms" << std::endl; } // Stress test: RoomOptions creation and copying @@ -147,11 +139,9 @@ TEST_F(RoomStressTest, RoomOptionsStress) { } auto end = std::chrono::high_resolution_clock::now(); - auto duration = - std::chrono::duration_cast(end - start); + auto duration = std::chrono::duration_cast(end - start); - std::cout << "Created and copied " << num_iterations << " RoomOptions in " - << duration.count() << "ms" << std::endl; + std::cout << "Created and copied " << num_iterations << " RoomOptions in " << duration.count() << "ms" << std::endl; } // Stress test: Stream handler registration and unregistration @@ -164,10 +154,8 @@ TEST_F(RoomStressTest, StreamHandlerRegistrationStress) { // Register many handlers for (int i = 0; i < num_topics; ++i) { std::string topic = "topic_" + std::to_string(i); - room.registerTextStreamHandler( - topic, [](std::shared_ptr, const std::string &) {}); - room.registerByteStreamHandler( - topic, [](std::shared_ptr, const std::string &) {}); + room.registerTextStreamHandler(topic, [](std::shared_ptr, const std::string&) {}); + room.registerByteStreamHandler(topic, [](std::shared_ptr, const std::string&) {}); } // Unregister all handlers @@ -178,11 +166,10 @@ TEST_F(RoomStressTest, StreamHandlerRegistrationStress) { } auto end = std::chrono::high_resolution_clock::now(); - auto duration = - std::chrono::duration_cast(end - start); + auto duration = std::chrono::duration_cast(end - start); - std::cout << "Registered and unregistered " << (num_topics * 2) - << " stream handlers in " << duration.count() << "ms" << std::endl; + std::cout << "Registered and unregistered " << (num_topics * 2) << " stream handlers in " << duration.count() << "ms" + << std::endl; } // Server-dependent stress tests @@ -191,8 +178,8 @@ class RoomServerStressTest : public ::testing::Test { void SetUp() override { livekit::initialize(livekit::LogLevel::Info, livekit::LogSink::kConsole); - const char *url_env = std::getenv("LIVEKIT_URL"); - const char *token_env = std::getenv("LIVEKIT_TOKEN_A"); + const char* url_env = std::getenv("LIVEKIT_URL"); + const char* token_env = std::getenv("LIVEKIT_TOKEN_A"); if (url_env && token_env) { server_url_ = url_env; @@ -232,8 +219,7 @@ TEST_F(RoomServerStressTest, RepeatedConnectDisconnect) { auto end = std::chrono::high_resolution_clock::now(); auto duration = std::chrono::duration_cast(end - start); - std::cout << "Completed " << num_iterations - << " connect/disconnect cycles in " << duration.count() << "s" + std::cout << "Completed " << num_iterations << " connect/disconnect cycles in " << duration.count() << "s" << std::endl; } diff --git a/src/tests/stress/test_rpc_stress.cpp b/src/tests/stress/test_rpc_stress.cpp index 89e249fd..03534c10 100644 --- a/src/tests/stress/test_rpc_stress.cpp +++ b/src/tests/stress/test_rpc_stress.cpp @@ -14,12 +14,13 @@ * limitations under the License. */ -#include "../benchmark/benchmark_utils.h" -#include "../common/test_common.h" #include #include #include +#include "../benchmark/benchmark_utils.h" +#include "../common/test_common.h" + using namespace livekit::test::benchmark; namespace livekit::test { @@ -28,7 +29,7 @@ namespace livekit::test { constexpr size_t kMaxRpcPayloadSize = 15 * 1024; // Path to test data file (relative to repo root) -static const char *kTestDataFile = "data/rpc_test_data.txt"; +static const char* kTestDataFile = "data/rpc_test_data.txt"; // Loaded test data lines static std::vector gTestDataLines; @@ -47,7 +48,7 @@ void loadTestData() { }; std::ifstream file; - for (const auto &path : search_paths) { + for (const auto& path : search_paths) { file.open(path); if (file.is_open()) { std::cout << "Loaded test data from: " << path << std::endl; @@ -56,8 +57,7 @@ void loadTestData() { } if (!file.is_open()) { - std::cerr << "Warning: Could not find " << kTestDataFile - << ", using fallback test data" << std::endl; + std::cerr << "Warning: Could not find " << kTestDataFile << ", using fallback test data" << std::endl; // Fallback to some default lines if file not found gTestDataLines = { "This is a fallback test line for RPC stress testing.", @@ -75,14 +75,13 @@ void loadTestData() { } file.close(); - std::cout << "Loaded " << gTestDataLines.size() << " lines of test data" - << std::endl; + std::cout << "Loaded " << gTestDataLines.size() << " lines of test data" << std::endl; }); } // Truncate a string at a valid UTF-8 boundary, then pad with spaces to exact // size -std::string truncateUtf8AndPad(const std::string &str, size_t target_size) { +std::string truncateUtf8AndPad(const std::string& str, size_t target_size) { if (str.size() <= target_size) { // Pad with spaces to reach target size std::string result = str; @@ -118,8 +117,7 @@ std::string generateRandomPayload(size_t size) { loadTestData(); static thread_local std::random_device rd; - static thread_local std::mt19937 gen( - static_cast(rd())); + static thread_local std::mt19937 gen(static_cast(rd())); if (gTestDataLines.empty()) { // Should not happen, but return empty string if no data @@ -134,7 +132,7 @@ std::string generateRandomPayload(size_t size) { // Build payload from random lines while (result.size() < size) { size_t line_idx = dis(gen); - const std::string &line = gTestDataLines[line_idx]; + const std::string& line = gTestDataLines[line_idx]; if (!result.empty()) { result += "\n"; } @@ -152,25 +150,21 @@ TEST_F(RpcStressTest, MaxPayloadStress) { skipIfNotConfigured(); std::cout << "\n=== RPC Max Payload Stress Test ===" << std::endl; - std::cout << "Duration: " << config_.stress_duration_seconds << " seconds" - << std::endl; + std::cout << "Duration: " << config_.stress_duration_seconds << " seconds" << std::endl; std::cout << "Caller threads: " << config_.num_caller_threads << std::endl; - std::cout << "Max payload size: " << kMaxRpcPayloadSize << " bytes (15KB)" - << std::endl; + std::cout << "Max payload size: " << kMaxRpcPayloadSize << " bytes (15KB)" << std::endl; // Create receiver room auto receiver_room = std::make_unique(); RoomOptions options; options.auto_subscribe = true; - bool receiver_connected = - receiver_room->Connect(config_.url, config_.token_b, options); + bool receiver_connected = receiver_room->Connect(config_.url, config_.token_b, options); ASSERT_TRUE(receiver_connected) << "Receiver failed to connect"; auto receiver_info = receiver_room->room_info(); - std::cout << "Receiver connected - Room: " << receiver_info.name - << " (SID: " << receiver_info.sid.value_or("unknown") << ")" - << std::endl; + std::cout << "Receiver connected - Room: " << receiver_info.name << " (SID: " << receiver_info.sid.value_or("unknown") + << ")" << std::endl; std::string receiver_identity = receiver_room->localParticipant()->identity(); @@ -178,9 +172,7 @@ TEST_F(RpcStressTest, MaxPayloadStress) { // Register RPC handler that processes max payloads receiver_room->localParticipant()->registerRpcMethod( - "max-payload-stress", - [&total_received]( - const RpcInvocationData &data) -> std::optional { + "max-payload-stress", [&total_received](const RpcInvocationData& data) -> std::optional { total_received++; // Echo the payload back for round-trip verification return data.payload; @@ -188,17 +180,14 @@ TEST_F(RpcStressTest, MaxPayloadStress) { // Create caller room auto caller_room = std::make_unique(); - bool caller_connected = - caller_room->Connect(config_.url, config_.token_a, options); + bool caller_connected = caller_room->Connect(config_.url, config_.token_a, options); ASSERT_TRUE(caller_connected) << "Caller failed to connect"; auto caller_info = caller_room->room_info(); - std::cout << "Caller connected - Room: " << caller_info.name - << " (SID: " << caller_info.sid.value_or("unknown") << ")" - << std::endl; + std::cout << "Caller connected - Room: " << caller_info.name << " (SID: " << caller_info.sid.value_or("unknown") + << ")" << std::endl; - bool receiver_visible = - waitForParticipant(caller_room.get(), receiver_identity, 10s); + bool receiver_visible = waitForParticipant(caller_room.get(), receiver_identity, 10s); ASSERT_TRUE(receiver_visible) << "Receiver not visible to caller"; std::cout << "Both rooms connected. Starting stress test..." << std::endl; @@ -230,13 +219,11 @@ TEST_F(RpcStressTest, MaxPayloadStress) { auto call_start = std::chrono::high_resolution_clock::now(); try { - std::string response = caller_room->localParticipant()->performRpc( - receiver_identity, "max-payload-stress", payload, 60.0); + std::string response = + caller_room->localParticipant()->performRpc(receiver_identity, "max-payload-stress", payload, 60.0); auto call_end = std::chrono::high_resolution_clock::now(); - double latency_ms = - std::chrono::duration(call_end - call_start) - .count(); + double latency_ms = std::chrono::duration(call_end - call_start).count(); // Verify response by comparing checksum (more efficient than full // comparison) @@ -245,27 +232,21 @@ TEST_F(RpcStressTest, MaxPayloadStress) { response_checksum += static_cast(c); } - if (response.size() == payload.size() && - response_checksum == expected_checksum) { + if (response.size() == payload.size() && response_checksum == expected_checksum) { stats.recordCall(true, latency_ms, kMaxRpcPayloadSize); } else { stats.recordCall(false, latency_ms, kMaxRpcPayloadSize); stats.recordError("checksum_mismatch"); - std::cerr << "[CHECKSUM MISMATCH] sent size=" << payload.size() - << " checksum=" << expected_checksum - << " | received size=" << response.size() - << " checksum=" << response_checksum << std::endl; + std::cerr << "[CHECKSUM MISMATCH] sent size=" << payload.size() << " checksum=" << expected_checksum + << " | received size=" << response.size() << " checksum=" << response_checksum << std::endl; } - } catch (const RpcError &e) { + } catch (const RpcError& e) { auto call_end = std::chrono::high_resolution_clock::now(); - double latency_ms = - std::chrono::duration(call_end - call_start) - .count(); + double latency_ms = std::chrono::duration(call_end - call_start).count(); stats.recordCall(false, latency_ms, kMaxRpcPayloadSize); auto code = static_cast(e.code()); - std::cerr << "[RPC ERROR] code=" << e.code() << " message=\"" - << e.message() << "\"" + std::cerr << "[RPC ERROR] code=" << e.code() << " message=\"" << e.message() << "\"" << " data=\"" << e.data() << "\"" << " latency=" << latency_ms << "ms" << std::endl; @@ -278,7 +259,7 @@ TEST_F(RpcStressTest, MaxPayloadStress) { } else { stats.recordError("rpc_error_" + std::to_string(e.code())); } - } catch (const std::exception &e) { + } catch (const std::exception& e) { stats.recordCall(false, 0, kMaxRpcPayloadSize); stats.recordError("exception"); std::cerr << "[EXCEPTION] " << e.what() << std::endl; @@ -295,20 +276,16 @@ TEST_F(RpcStressTest, MaxPayloadStress) { int last_total = 0; while (running.load()) { std::this_thread::sleep_for(30s); - if (!running.load()) - break; + if (!running.load()) break; auto elapsed = std::chrono::steady_clock::now() - start_time; - auto elapsed_seconds = - std::chrono::duration_cast(elapsed).count(); + auto elapsed_seconds = std::chrono::duration_cast(elapsed).count(); int current_total = stats.totalCalls(); int calls_per_30s = current_total - last_total; last_total = current_total; - std::cout << "[" << elapsed_seconds << "s] Total: " << current_total - << " | Success: " << stats.successfulCalls() - << " | Failed: " << stats.failedCalls() - << " | Rate: " << (calls_per_30s / 30.0) << " calls/sec" + std::cout << "[" << elapsed_seconds << "s] Total: " << current_total << " | Success: " << stats.successfulCalls() + << " | Failed: " << stats.failedCalls() << " | Rate: " << (calls_per_30s / 30.0) << " calls/sec" << " | Received: " << total_received.load() << std::endl; } }); @@ -322,7 +299,7 @@ TEST_F(RpcStressTest, MaxPayloadStress) { running.store(false); // Wait for all threads - for (auto &t : caller_threads) { + for (auto& t : caller_threads) { t.join(); } progress_thread.join(); @@ -334,10 +311,7 @@ TEST_F(RpcStressTest, MaxPayloadStress) { // Verify results EXPECT_GT(stats.successfulCalls(), 0) << "No successful calls"; - double success_rate = - (stats.totalCalls() > 0) - ? (100.0 * stats.successfulCalls() / stats.totalCalls()) - : 0.0; + double success_rate = (stats.totalCalls() > 0) ? (100.0 * stats.successfulCalls() / stats.totalCalls()) : 0.0; EXPECT_GT(success_rate, 95.0) << "Success rate below 95%"; receiver_room->localParticipant()->unregisterRpcMethod("max-payload-stress"); @@ -354,25 +328,21 @@ TEST_F(RpcStressTest, SmallPayloadStress) { constexpr size_t kSmallPayloadSize = 1000; std::cout << "\n=== RPC Small Payload Stress Test ===" << std::endl; - std::cout << "Duration: " << config_.stress_duration_seconds << " seconds" - << std::endl; + std::cout << "Duration: " << config_.stress_duration_seconds << " seconds" << std::endl; std::cout << "Caller threads: " << config_.num_caller_threads << std::endl; - std::cout << "Payload size: " << kSmallPayloadSize - << " bytes (single SCTP chunk)" << std::endl; + std::cout << "Payload size: " << kSmallPayloadSize << " bytes (single SCTP chunk)" << std::endl; // Create receiver room auto receiver_room = std::make_unique(); RoomOptions options; options.auto_subscribe = true; - bool receiver_connected = - receiver_room->Connect(config_.url, config_.token_b, options); + bool receiver_connected = receiver_room->Connect(config_.url, config_.token_b, options); ASSERT_TRUE(receiver_connected) << "Receiver failed to connect"; auto receiver_info = receiver_room->room_info(); - std::cout << "Receiver connected - Room: " << receiver_info.name - << " (SID: " << receiver_info.sid.value_or("unknown") << ")" - << std::endl; + std::cout << "Receiver connected - Room: " << receiver_info.name << " (SID: " << receiver_info.sid.value_or("unknown") + << ")" << std::endl; std::string receiver_identity = receiver_room->localParticipant()->identity(); @@ -380,26 +350,21 @@ TEST_F(RpcStressTest, SmallPayloadStress) { // Register RPC handler receiver_room->localParticipant()->registerRpcMethod( - "small-payload-stress", - [&total_received]( - const RpcInvocationData &data) -> std::optional { + "small-payload-stress", [&total_received](const RpcInvocationData& data) -> std::optional { total_received++; return data.payload; }); // Create caller room auto caller_room = std::make_unique(); - bool caller_connected = - caller_room->Connect(config_.url, config_.token_a, options); + bool caller_connected = caller_room->Connect(config_.url, config_.token_a, options); ASSERT_TRUE(caller_connected) << "Caller failed to connect"; auto caller_info = caller_room->room_info(); - std::cout << "Caller connected - Room: " << caller_info.name - << " (SID: " << caller_info.sid.value_or("unknown") << ")" - << std::endl; + std::cout << "Caller connected - Room: " << caller_info.name << " (SID: " << caller_info.sid.value_or("unknown") + << ")" << std::endl; - bool receiver_visible = - waitForParticipant(caller_room.get(), receiver_identity, 10s); + bool receiver_visible = waitForParticipant(caller_room.get(), receiver_identity, 10s); ASSERT_TRUE(receiver_visible) << "Receiver not visible to caller"; std::cout << "Both rooms connected. Starting stress test..." << std::endl; @@ -430,13 +395,11 @@ TEST_F(RpcStressTest, SmallPayloadStress) { auto call_start = std::chrono::high_resolution_clock::now(); try { - std::string response = caller_room->localParticipant()->performRpc( - receiver_identity, "small-payload-stress", payload, 60.0); + std::string response = + caller_room->localParticipant()->performRpc(receiver_identity, "small-payload-stress", payload, 60.0); auto call_end = std::chrono::high_resolution_clock::now(); - double latency_ms = - std::chrono::duration(call_end - call_start) - .count(); + double latency_ms = std::chrono::duration(call_end - call_start).count(); // Verify response by comparing checksum size_t response_checksum = 0; @@ -444,23 +407,19 @@ TEST_F(RpcStressTest, SmallPayloadStress) { response_checksum += static_cast(c); } - if (response.size() == payload.size() && - response_checksum == expected_checksum) { + if (response.size() == payload.size() && response_checksum == expected_checksum) { stats.recordCall(true, latency_ms, kSmallPayloadSize); } else { stats.recordCall(false, latency_ms, kSmallPayloadSize); stats.recordError("checksum_mismatch"); } - } catch (const RpcError &e) { + } catch (const RpcError& e) { auto call_end = std::chrono::high_resolution_clock::now(); - double latency_ms = - std::chrono::duration(call_end - call_start) - .count(); + double latency_ms = std::chrono::duration(call_end - call_start).count(); stats.recordCall(false, latency_ms, kSmallPayloadSize); auto code = static_cast(e.code()); - std::cerr << "[RPC ERROR] code=" << e.code() << " message=\"" - << e.message() << "\"" + std::cerr << "[RPC ERROR] code=" << e.code() << " message=\"" << e.message() << "\"" << " data=\"" << e.data() << "\"" << std::endl; if (code == RpcError::ErrorCode::RESPONSE_TIMEOUT) { stats.recordError("timeout"); @@ -471,7 +430,7 @@ TEST_F(RpcStressTest, SmallPayloadStress) { } else { stats.recordError("rpc_error_" + std::to_string(e.code())); } - } catch (const std::exception &e) { + } catch (const std::exception& e) { stats.recordCall(false, 0, kSmallPayloadSize); stats.recordError("exception"); std::cerr << "[EXCEPTION] " << e.what() << std::endl; @@ -488,20 +447,16 @@ TEST_F(RpcStressTest, SmallPayloadStress) { int last_total = 0; while (running.load()) { std::this_thread::sleep_for(30s); - if (!running.load()) - break; + if (!running.load()) break; auto elapsed = std::chrono::steady_clock::now() - start_time; - auto elapsed_seconds = - std::chrono::duration_cast(elapsed).count(); + auto elapsed_seconds = std::chrono::duration_cast(elapsed).count(); int current_total = stats.totalCalls(); int calls_per_30s = current_total - last_total; last_total = current_total; - std::cout << "[" << elapsed_seconds << "s] Total: " << current_total - << " | Success: " << stats.successfulCalls() - << " | Failed: " << stats.failedCalls() - << " | Rate: " << (calls_per_30s / 30.0) << " calls/sec" + std::cout << "[" << elapsed_seconds << "s] Total: " << current_total << " | Success: " << stats.successfulCalls() + << " | Failed: " << stats.failedCalls() << " | Rate: " << (calls_per_30s / 30.0) << " calls/sec" << " | Received: " << total_received.load() << std::endl; } }); @@ -515,7 +470,7 @@ TEST_F(RpcStressTest, SmallPayloadStress) { running.store(false); // Wait for all threads - for (auto &t : caller_threads) { + for (auto& t : caller_threads) { t.join(); } progress_thread.join(); @@ -527,14 +482,10 @@ TEST_F(RpcStressTest, SmallPayloadStress) { // Verify results EXPECT_GT(stats.successfulCalls(), 0) << "No successful calls"; - double success_rate = - (stats.totalCalls() > 0) - ? (100.0 * stats.successfulCalls() / stats.totalCalls()) - : 0.0; + double success_rate = (stats.totalCalls() > 0) ? (100.0 * stats.successfulCalls() / stats.totalCalls()) : 0.0; EXPECT_GT(success_rate, 95.0) << "Success rate below 95%"; - receiver_room->localParticipant()->unregisterRpcMethod( - "small-payload-stress"); + receiver_room->localParticipant()->unregisterRpcMethod("small-payload-stress"); caller_room.reset(); receiver_room.reset(); } @@ -544,47 +495,38 @@ TEST_F(RpcStressTest, BidirectionalRpcStress) { skipIfNotConfigured(); std::cout << "\n=== Bidirectional RPC Stress Test ===" << std::endl; - std::cout << "Duration: " << config_.stress_duration_seconds << " seconds" - << std::endl; + std::cout << "Duration: " << config_.stress_duration_seconds << " seconds" << std::endl; auto room_a = std::make_unique(); auto room_b = std::make_unique(); RoomOptions options; options.auto_subscribe = true; - bool a_connected = - room_a->Connect(config_.url, config_.token_a, options); + bool a_connected = room_a->Connect(config_.url, config_.token_a, options); ASSERT_TRUE(a_connected) << "Room A failed to connect"; - bool b_connected = - room_b->Connect(config_.url, config_.token_b, options); + bool b_connected = room_b->Connect(config_.url, config_.token_b, options); ASSERT_TRUE(b_connected) << "Room B failed to connect"; std::string identity_a = room_a->localParticipant()->identity(); std::string identity_b = room_b->localParticipant()->identity(); - ASSERT_TRUE(waitForParticipant(room_a.get(), identity_b, 10s)) - << "Room B not visible to Room A"; - ASSERT_TRUE(waitForParticipant(room_b.get(), identity_a, 10s)) - << "Room A not visible to Room B"; + ASSERT_TRUE(waitForParticipant(room_a.get(), identity_b, 10s)) << "Room B not visible to Room A"; + ASSERT_TRUE(waitForParticipant(room_b.get(), identity_a, 10s)) << "Room A not visible to Room B"; std::atomic a_received{0}; std::atomic b_received{0}; // Register handlers on both sides - echo payload back for verification room_a->localParticipant()->registerRpcMethod( - "ping", - [&a_received]( - const RpcInvocationData &data) -> std::optional { + "ping", [&a_received](const RpcInvocationData& data) -> std::optional { a_received++; // Echo the payload back for round-trip verification return data.payload; }); room_b->localParticipant()->registerRpcMethod( - "ping", - [&b_received]( - const RpcInvocationData &data) -> std::optional { + "ping", [&b_received](const RpcInvocationData& data) -> std::optional { b_received++; // Echo the payload back for round-trip verification return data.payload; @@ -615,13 +557,10 @@ TEST_F(RpcStressTest, BidirectionalRpcStress) { auto call_start = std::chrono::high_resolution_clock::now(); try { - std::string response = room_a->localParticipant()->performRpc( - identity_b, "ping", payload, 60.0); + std::string response = room_a->localParticipant()->performRpc(identity_b, "ping", payload, 60.0); auto call_end = std::chrono::high_resolution_clock::now(); - double latency_ms = - std::chrono::duration(call_end - call_start) - .count(); + double latency_ms = std::chrono::duration(call_end - call_start).count(); // Verify response by comparing checksum size_t response_checksum = 0; @@ -629,22 +568,18 @@ TEST_F(RpcStressTest, BidirectionalRpcStress) { response_checksum += static_cast(c); } - if (response.size() == payload.size() && - response_checksum == expected_checksum) { + if (response.size() == payload.size() && response_checksum == expected_checksum) { stats_a_to_b.recordCall(true, latency_ms, kMaxRpcPayloadSize); } else { stats_a_to_b.recordCall(false, latency_ms, kMaxRpcPayloadSize); - std::cerr << "[A->B MISMATCH] sent size=" << payload.size() - << " checksum=" << expected_checksum - << " | received size=" << response.size() - << " checksum=" << response_checksum << std::endl; + std::cerr << "[A->B MISMATCH] sent size=" << payload.size() << " checksum=" << expected_checksum + << " | received size=" << response.size() << " checksum=" << response_checksum << std::endl; } - } catch (const RpcError &e) { + } catch (const RpcError& e) { stats_a_to_b.recordCall(false, 0, kMaxRpcPayloadSize); - std::cerr << "[A->B RPC ERROR] code=" << e.code() << " message=\"" - << e.message() << "\"" + std::cerr << "[A->B RPC ERROR] code=" << e.code() << " message=\"" << e.message() << "\"" << " data=\"" << e.data() << "\"" << std::endl; - } catch (const std::exception &ex) { + } catch (const std::exception& ex) { stats_a_to_b.recordCall(false, 0, kMaxRpcPayloadSize); std::cerr << "[A->B EXCEPTION] " << ex.what() << std::endl; } @@ -667,13 +602,10 @@ TEST_F(RpcStressTest, BidirectionalRpcStress) { auto call_start = std::chrono::high_resolution_clock::now(); try { - std::string response = room_b->localParticipant()->performRpc( - identity_a, "ping", payload, 60.0); + std::string response = room_b->localParticipant()->performRpc(identity_a, "ping", payload, 60.0); auto call_end = std::chrono::high_resolution_clock::now(); - double latency_ms = - std::chrono::duration(call_end - call_start) - .count(); + double latency_ms = std::chrono::duration(call_end - call_start).count(); // Verify response by comparing checksum size_t response_checksum = 0; @@ -681,22 +613,18 @@ TEST_F(RpcStressTest, BidirectionalRpcStress) { response_checksum += static_cast(c); } - if (response.size() == payload.size() && - response_checksum == expected_checksum) { + if (response.size() == payload.size() && response_checksum == expected_checksum) { stats_b_to_a.recordCall(true, latency_ms, kMaxRpcPayloadSize); } else { stats_b_to_a.recordCall(false, latency_ms, kMaxRpcPayloadSize); - std::cerr << "[B->A MISMATCH] sent size=" << payload.size() - << " checksum=" << expected_checksum - << " | received size=" << response.size() - << " checksum=" << response_checksum << std::endl; + std::cerr << "[B->A MISMATCH] sent size=" << payload.size() << " checksum=" << expected_checksum + << " | received size=" << response.size() << " checksum=" << response_checksum << std::endl; } - } catch (const RpcError &e) { + } catch (const RpcError& e) { stats_b_to_a.recordCall(false, 0, kMaxRpcPayloadSize); - std::cerr << "[B->A RPC ERROR] code=" << e.code() << " message=\"" - << e.message() << "\"" + std::cerr << "[B->A RPC ERROR] code=" << e.code() << " message=\"" << e.message() << "\"" << " data=\"" << e.data() << "\"" << std::endl; - } catch (const std::exception &ex) { + } catch (const std::exception& ex) { stats_b_to_a.recordCall(false, 0, kMaxRpcPayloadSize); std::cerr << "[B->A EXCEPTION] " << ex.what() << std::endl; } @@ -709,20 +637,15 @@ TEST_F(RpcStressTest, BidirectionalRpcStress) { std::thread progress_thread([&]() { while (running.load()) { std::this_thread::sleep_for(30s); - if (!running.load()) - break; + if (!running.load()) break; auto elapsed = std::chrono::steady_clock::now() - start_time; - auto elapsed_seconds = - std::chrono::duration_cast(elapsed).count(); + auto elapsed_seconds = std::chrono::duration_cast(elapsed).count(); std::cout << "[" << elapsed_seconds << "s] " - << "A->B: " << stats_a_to_b.successfulCalls() << "/" - << stats_a_to_b.totalCalls() << " | " - << "B->A: " << stats_b_to_a.successfulCalls() << "/" - << stats_b_to_a.totalCalls() << " | " - << "A rcvd: " << a_received.load() - << " | B rcvd: " << b_received.load() << std::endl; + << "A->B: " << stats_a_to_b.successfulCalls() << "/" << stats_a_to_b.totalCalls() << " | " + << "B->A: " << stats_b_to_a.successfulCalls() << "/" << stats_b_to_a.totalCalls() << " | " + << "A rcvd: " << a_received.load() << " | B rcvd: " << b_received.load() << std::endl; } }); @@ -746,8 +669,7 @@ TEST_F(RpcStressTest, BidirectionalRpcStress) { stats_b_to_a.printStats(); // Print comparison table - std::vector all_stats = {stats_a_to_b.getLatencyStats(), - stats_b_to_a.getLatencyStats()}; + std::vector all_stats = {stats_a_to_b.getLatencyStats(), stats_b_to_a.getLatencyStats()}; std::cout << "\n=== Comparison Table ===" << std::endl; printComparisonTable(all_stats); @@ -765,16 +687,14 @@ TEST_F(RpcStressTest, HighThroughputBurst) { skipIfNotConfigured(); std::cout << "\n=== High Throughput Burst Test ===" << std::endl; - std::cout << "Duration: " << config_.stress_duration_seconds << " seconds" - << std::endl; + std::cout << "Duration: " << config_.stress_duration_seconds << " seconds" << std::endl; std::cout << "Testing rapid-fire RPC with max payload (15KB)..." << std::endl; auto receiver_room = std::make_unique(); RoomOptions options; options.auto_subscribe = true; - bool receiver_connected = - receiver_room->Connect(config_.url, config_.token_b, options); + bool receiver_connected = receiver_room->Connect(config_.url, config_.token_b, options); ASSERT_TRUE(receiver_connected) << "Receiver failed to connect"; std::string receiver_identity = receiver_room->localParticipant()->identity(); @@ -782,21 +702,17 @@ TEST_F(RpcStressTest, HighThroughputBurst) { std::atomic total_received{0}; receiver_room->localParticipant()->registerRpcMethod( - "burst-test", - [&total_received]( - const RpcInvocationData &data) -> std::optional { + "burst-test", [&total_received](const RpcInvocationData& data) -> std::optional { total_received++; // Echo the payload back for round-trip verification return data.payload; }); auto caller_room = std::make_unique(); - bool caller_connected = - caller_room->Connect(config_.url, config_.token_a, options); + bool caller_connected = caller_room->Connect(config_.url, config_.token_a, options); ASSERT_TRUE(caller_connected) << "Caller failed to connect"; - bool receiver_visible = - waitForParticipant(caller_room.get(), receiver_identity, 10s); + bool receiver_visible = waitForParticipant(caller_room.get(), receiver_identity, 10s); ASSERT_TRUE(receiver_visible) << "Receiver not visible to caller"; // Start benchmark session with tracing @@ -825,13 +741,11 @@ TEST_F(RpcStressTest, HighThroughputBurst) { auto call_start = std::chrono::high_resolution_clock::now(); try { - std::string response = caller_room->localParticipant()->performRpc( - receiver_identity, "burst-test", payload, 60.0); + std::string response = + caller_room->localParticipant()->performRpc(receiver_identity, "burst-test", payload, 60.0); auto call_end = std::chrono::high_resolution_clock::now(); - double latency_ms = - std::chrono::duration(call_end - call_start) - .count(); + double latency_ms = std::chrono::duration(call_end - call_start).count(); // Verify response by comparing checksum size_t response_checksum = 0; @@ -839,22 +753,18 @@ TEST_F(RpcStressTest, HighThroughputBurst) { response_checksum += static_cast(c); } - if (response.size() == payload.size() && - response_checksum == expected_checksum) { + if (response.size() == payload.size() && response_checksum == expected_checksum) { stats.recordCall(true, latency_ms, kMaxRpcPayloadSize); } else { stats.recordCall(false, latency_ms, kMaxRpcPayloadSize); - std::cerr << "[BURST MISMATCH] sent size=" << payload.size() - << " checksum=" << expected_checksum - << " | received size=" << response.size() - << " checksum=" << response_checksum << std::endl; + std::cerr << "[BURST MISMATCH] sent size=" << payload.size() << " checksum=" << expected_checksum + << " | received size=" << response.size() << " checksum=" << response_checksum << std::endl; } - } catch (const RpcError &e) { + } catch (const RpcError& e) { stats.recordCall(false, 0, kMaxRpcPayloadSize); - std::cerr << "[BURST RPC ERROR] code=" << e.code() << " message=\"" - << e.message() << "\"" + std::cerr << "[BURST RPC ERROR] code=" << e.code() << " message=\"" << e.message() << "\"" << " data=\"" << e.data() << "\"" << std::endl; - } catch (const std::exception &ex) { + } catch (const std::exception& ex) { stats.recordCall(false, 0, kMaxRpcPayloadSize); std::cerr << "[BURST EXCEPTION] " << ex.what() << std::endl; } @@ -869,23 +779,19 @@ TEST_F(RpcStressTest, HighThroughputBurst) { int last_total = 0; while (running.load()) { std::this_thread::sleep_for(10s); - if (!running.load()) - break; + if (!running.load()) break; int current = stats.totalCalls(); double rate = (current - last_total) / 10.0; last_total = current; auto elapsed = std::chrono::steady_clock::now() - start_time; - auto elapsed_seconds = - std::chrono::duration_cast(elapsed).count(); + auto elapsed_seconds = std::chrono::duration_cast(elapsed).count(); std::cout << "[" << elapsed_seconds << "s] " - << "Total: " << current - << " | Success: " << stats.successfulCalls() - << " | Rate: " << rate << " calls/sec" - << " | Throughput: " << (rate * kMaxRpcPayloadSize / 1024.0) - << " KB/sec" << std::endl; + << "Total: " << current << " | Success: " << stats.successfulCalls() << " | Rate: " << rate + << " calls/sec" + << " | Throughput: " << (rate * kMaxRpcPayloadSize / 1024.0) << " KB/sec" << std::endl; } }); @@ -895,7 +801,7 @@ TEST_F(RpcStressTest, HighThroughputBurst) { running.store(false); - for (auto &t : burst_threads) { + for (auto& t : burst_threads) { t.join(); } progress_thread.join(); @@ -905,17 +811,13 @@ TEST_F(RpcStressTest, HighThroughputBurst) { // Print trace-based statistics stats.printStats(); - auto total_time = std::chrono::duration_cast( - std::chrono::steady_clock::now() - start_time) - .count(); + auto total_time = + std::chrono::duration_cast(std::chrono::steady_clock::now() - start_time).count(); double avg_rate = static_cast(stats.totalCalls()) / total_time; - double throughput_kbps = - (static_cast(stats.successfulCalls()) * kMaxRpcPayloadSize) / - (total_time * 1024.0); + double throughput_kbps = (static_cast(stats.successfulCalls()) * kMaxRpcPayloadSize) / (total_time * 1024.0); std::cout << "Average rate: " << avg_rate << " calls/sec" << std::endl; - std::cout << "Average throughput: " << throughput_kbps << " KB/sec" - << std::endl; + std::cout << "Average throughput: " << throughput_kbps << " KB/sec" << std::endl; EXPECT_GT(stats.successfulCalls(), 0); diff --git a/src/tests/unit/test_audio_frame.cpp b/src/tests/unit/test_audio_frame.cpp index 25d1fda4..9dd9d1dc 100644 --- a/src/tests/unit/test_audio_frame.cpp +++ b/src/tests/unit/test_audio_frame.cpp @@ -22,9 +22,7 @@ namespace livekit::test { class AudioFrameTest : public ::testing::Test { protected: - void SetUp() override { - livekit::initialize(livekit::LogLevel::Info, livekit::LogSink::kConsole); - } + void SetUp() override { livekit::initialize(livekit::LogLevel::Info, livekit::LogSink::kConsole); } void TearDown() override { livekit::shutdown(); } }; @@ -58,8 +56,8 @@ TEST_F(AudioFrameTest, CreateUsingStaticMethod) { EXPECT_EQ(frame.total_samples(), 1920); // Created frame should be zero-initialized - const auto &samples = frame.data(); - for (const auto &sample : samples) { + const auto& samples = frame.data(); + for (const auto& sample : samples) { EXPECT_EQ(sample, 0); } } @@ -88,7 +86,7 @@ TEST_F(AudioFrameTest, DataAccessMutable) { AudioFrame frame = AudioFrame::create(48000, 1, 480); // Modify data - auto &data = frame.data(); + auto& data = frame.data(); data[0] = 1000; data[1] = -1000; @@ -101,8 +99,8 @@ TEST_F(AudioFrameTest, DataAccessConst) { std::vector original_data = {100, 200, 300, 400}; AudioFrame frame(original_data, 48000, 1, 4); - const AudioFrame &const_frame = frame; - const auto &data = const_frame.data(); + const AudioFrame& const_frame = frame; + const auto& data = const_frame.data(); EXPECT_EQ(data[0], 100); EXPECT_EQ(data[1], 200); diff --git a/src/tests/unit/test_audio_processing_module.cpp b/src/tests/unit/test_audio_processing_module.cpp index 048aa1c8..d70d4149 100644 --- a/src/tests/unit/test_audio_processing_module.cpp +++ b/src/tests/unit/test_audio_processing_module.cpp @@ -32,9 +32,7 @@ namespace livekit::test { class AudioProcessingModuleTest : public ::testing::Test { protected: - void SetUp() override { - livekit::initialize(livekit::LogLevel::Info, livekit::LogSink::kConsole); - } + void SetUp() override { livekit::initialize(livekit::LogLevel::Info, livekit::LogSink::kConsole); } void TearDown() override { livekit::shutdown(); } @@ -45,17 +43,15 @@ class AudioProcessingModuleTest : public ::testing::Test { } // Helper to fill frame with sine wave - static void fillWithSineWave(AudioFrame &frame, double frequency, - double amplitude = 10000.0) { - auto &data = frame.data(); + static void fillWithSineWave(AudioFrame& frame, double frequency, double amplitude = 10000.0) { + auto& data = frame.data(); int sample_rate = frame.sample_rate(); int num_channels = frame.num_channels(); int samples_per_channel = frame.samples_per_channel(); for (int i = 0; i < samples_per_channel; ++i) { double t = static_cast(i) / sample_rate; - auto sample = static_cast( - amplitude * std::sin(2.0 * M_PI * frequency * t)); + auto sample = static_cast(amplitude * std::sin(2.0 * M_PI * frequency * t)); for (int ch = 0; ch < num_channels; ++ch) { data[i * num_channels + ch] = sample; } @@ -63,27 +59,25 @@ class AudioProcessingModuleTest : public ::testing::Test { } // Helper to fill frame with random noise - static void fillWithNoise(AudioFrame &frame, double amplitude = 5000.0, - unsigned int seed = 0) { - std::mt19937 gen(static_cast( - seed == 0 ? std::random_device{}() : seed)); + static void fillWithNoise(AudioFrame& frame, double amplitude = 5000.0, unsigned int seed = 0) { + std::mt19937 gen(static_cast(seed == 0 ? std::random_device{}() : seed)); std::uniform_real_distribution<> dis(-amplitude, amplitude); - auto &data = frame.data(); - for (auto &sample : data) { + auto& data = frame.data(); + for (auto& sample : data) { sample = static_cast(dis(gen)); } } // Calculate RMS (Root Mean Square) energy of audio frame - static double calculateRMS(const AudioFrame &frame) { - const auto &data = frame.data(); + static double calculateRMS(const AudioFrame& frame) { + const auto& data = frame.data(); if (data.empty()) { return 0.0; } double sum_squares = 0.0; - for (const auto &sample : data) { + for (const auto& sample : data) { sum_squares += static_cast(sample) * static_cast(sample); } return std::sqrt(sum_squares / static_cast(data.size())); @@ -91,10 +85,8 @@ class AudioProcessingModuleTest : public ::testing::Test { // Calculate energy in a specific frequency band using a simple DFT approach // This is a simplified calculation for testing purposes - static double calculateFrequencyBandEnergy(const AudioFrame &frame, - double low_freq, - double high_freq) { - const auto &data = frame.data(); + static double calculateFrequencyBandEnergy(const AudioFrame& frame, double low_freq, double high_freq) { + const auto& data = frame.data(); int sample_rate = frame.sample_rate(); int num_channels = frame.num_channels(); int samples_per_channel = frame.samples_per_channel(); @@ -111,13 +103,10 @@ class AudioProcessingModuleTest : public ::testing::Test { // Simple DFT for the frequency range of interest double energy = 0.0; - int low_bin = - static_cast(low_freq * samples_per_channel / sample_rate); - int high_bin = - static_cast(high_freq * samples_per_channel / sample_rate); + int low_bin = static_cast(low_freq * samples_per_channel / sample_rate); + int high_bin = static_cast(high_freq * samples_per_channel / sample_rate); - for (int k = std::max(1, low_bin); - k <= std::min(high_bin, samples_per_channel / 2); ++k) { + for (int k = std::max(1, low_bin); k <= std::min(high_bin, samples_per_channel / 2); ++k) { double real = 0.0; double imag = 0.0; double freq_rad = 2.0 * M_PI * k / samples_per_channel; @@ -134,15 +123,12 @@ class AudioProcessingModuleTest : public ::testing::Test { } // Copy audio frame data - static std::vector copyFrameData(const AudioFrame &frame) { - return frame.data(); - } + static std::vector copyFrameData(const AudioFrame& frame) { return frame.data(); } // Read a WAV file and return the raw PCM samples // Assumes 16-bit PCM format - static bool readWavFile(const std::string &path, - std::vector &samples, int &sample_rate, - int &num_channels) { + static bool readWavFile(const std::string& path, std::vector& samples, int& sample_rate, + int& num_channels) { std::ifstream file(path, std::ios::binary); if (!file.is_open()) { std::cerr << "Failed to open WAV file: " << path << std::endl; @@ -171,22 +157,22 @@ class AudioProcessingModuleTest : public ::testing::Test { char chunk_id[4]; file.read(chunk_id, 4); std::uint32_t chunk_size; - file.read(reinterpret_cast(&chunk_size), 4); + file.read(reinterpret_cast(&chunk_size), 4); if (std::string(chunk_id, 4) == "fmt ") { std::uint16_t audio_format; - file.read(reinterpret_cast(&audio_format), 2); + file.read(reinterpret_cast(&audio_format), 2); if (audio_format != 1) { // PCM std::cerr << "Only PCM format supported" << std::endl; return false; } std::uint16_t channels; - file.read(reinterpret_cast(&channels), 2); + file.read(reinterpret_cast(&channels), 2); num_channels = channels; std::uint32_t rate; - file.read(reinterpret_cast(&rate), 4); + file.read(reinterpret_cast(&rate), 4); sample_rate = static_cast(rate); file.seekg(chunk_size - 8, std::ios::cur); // Skip rest of fmt chunk @@ -194,7 +180,7 @@ class AudioProcessingModuleTest : public ::testing::Test { // Read audio data size_t num_samples = chunk_size / sizeof(std::int16_t); samples.resize(num_samples); - file.read(reinterpret_cast(samples.data()), chunk_size); + file.read(reinterpret_cast(samples.data()), chunk_size); return true; } else { // Skip unknown chunk @@ -207,8 +193,8 @@ class AudioProcessingModuleTest : public ::testing::Test { } // Scale audio samples by a factor (for simulating quiet/loud audio) - static void scaleAudio(std::vector &samples, double scale) { - for (auto &sample : samples) { + static void scaleAudio(std::vector& samples, double scale) { + for (auto& sample : samples) { double scaled = static_cast(sample) * scale; sample = static_cast(std::clamp(scaled, -32768.0, 32767.0)); } @@ -506,12 +492,10 @@ TEST_F(AudioProcessingModuleTest, NoiseSuppressionReducesNoiseEnergy) { // Noise suppression should reduce energy significantly // We expect at least 50% reduction for pure noise input std::cout << "[NoiseSuppression] Avg input energy: " << avg_input_energy - << ", Avg output energy: " << avg_output_energy << ", Reduction: " - << (1.0 - avg_output_energy / avg_input_energy) * 100.0 << "%" - << std::endl; + << ", Avg output energy: " << avg_output_energy + << ", Reduction: " << (1.0 - avg_output_energy / avg_input_energy) * 100.0 << "%" << std::endl; - EXPECT_LT(avg_output_energy, avg_input_energy) - << "Noise suppression should reduce energy"; + EXPECT_LT(avg_output_energy, avg_input_energy) << "Noise suppression should reduce energy"; } TEST_F(AudioProcessingModuleTest, NoiseSuppressionPreservesSpeechLikeSignal) { @@ -530,26 +514,23 @@ TEST_F(AudioProcessingModuleTest, NoiseSuppressionPreservesSpeechLikeSignal) { for (int i = 0; i < kFrames; ++i) { AudioFrame frame = create10msFrame(48000, 1); - auto &data = frame.data(); + auto& data = frame.data(); int sample_rate = frame.sample_rate(); int samples_per_channel = frame.samples_per_channel(); // Create speech-like signal with fundamental + harmonics (like voice) // Vary amplitude slightly to simulate natural speech variation - double amplitude_variation = - 8000.0 + 2000.0 * std::sin(2.0 * M_PI * i / 20.0); + double amplitude_variation = 8000.0 + 2000.0 * std::sin(2.0 * M_PI * i / 20.0); for (int s = 0; s < samples_per_channel; ++s) { double t = static_cast(s) / sample_rate; // Fundamental (250 Hz) + harmonics (typical of voiced speech) - double sample = amplitude_variation * - (0.5 * std::sin(2.0 * M_PI * 250.0 * t) + // Fundamental - 0.3 * std::sin(2.0 * M_PI * 500.0 * t) + // 2nd harmonic - 0.15 * std::sin(2.0 * M_PI * 750.0 * t) + // 3rd harmonic - 0.05 * std::sin(2.0 * M_PI * 1000.0 * t) // 4th harmonic - ); - data[s] = - static_cast(std::clamp(sample, -32768.0, 32767.0)); + double sample = amplitude_variation * (0.5 * std::sin(2.0 * M_PI * 250.0 * t) + // Fundamental + 0.3 * std::sin(2.0 * M_PI * 500.0 * t) + // 2nd harmonic + 0.15 * std::sin(2.0 * M_PI * 750.0 * t) + // 3rd harmonic + 0.05 * std::sin(2.0 * M_PI * 1000.0 * t) // 4th harmonic + ); + data[s] = static_cast(std::clamp(sample, -32768.0, 32767.0)); } double input_energy = calculateRMS(frame); @@ -565,16 +546,14 @@ TEST_F(AudioProcessingModuleTest, NoiseSuppressionPreservesSpeechLikeSignal) { double avg_output_energy = total_output_energy / kFrames; double preservation_ratio = avg_output_energy / avg_input_energy; - std::cout << "[NoiseSuppression-Speech] Avg input energy: " - << avg_input_energy << ", Avg output energy: " << avg_output_energy - << ", Preservation: " << preservation_ratio * 100.0 << "%" + std::cout << "[NoiseSuppression-Speech] Avg input energy: " << avg_input_energy + << ", Avg output energy: " << avg_output_energy << ", Preservation: " << preservation_ratio * 100.0 << "%" << std::endl; // Note: Even speech-like signals may be partially attenuated by NS // We just verify that the output has some significant energy // (i.e., NS doesn't completely silence the signal) - EXPECT_GT(avg_output_energy, avg_input_energy * 0.1) - << "Speech-like signals should not be completely suppressed"; + EXPECT_GT(avg_output_energy, avg_input_energy * 0.1) << "Speech-like signals should not be completely suppressed"; } // ============================================================================ @@ -611,13 +590,11 @@ TEST_F(AudioProcessingModuleTest, HighPassFilterAttenuatesLowFrequencies) { double avg_output_energy = total_output_energy / kFrames; std::cout << "[HighPassFilter-LowFreq] Avg input energy: " << avg_input_energy - << ", Avg output energy: " << avg_output_energy << ", Attenuation: " - << (1.0 - avg_output_energy / avg_input_energy) * 100.0 << "%" - << std::endl; + << ", Avg output energy: " << avg_output_energy + << ", Attenuation: " << (1.0 - avg_output_energy / avg_input_energy) * 100.0 << "%" << std::endl; // Low frequencies should be significantly attenuated - EXPECT_LT(avg_output_energy, avg_input_energy * 0.8) - << "High pass filter should attenuate low frequencies"; + EXPECT_LT(avg_output_energy, avg_input_energy * 0.8) << "High pass filter should attenuate low frequencies"; } TEST_F(AudioProcessingModuleTest, HighPassFilterPassesHighFrequencies) { @@ -650,9 +627,9 @@ TEST_F(AudioProcessingModuleTest, HighPassFilterPassesHighFrequencies) { double avg_output_energy = total_output_energy / kFrames; double pass_ratio = avg_output_energy / avg_input_energy; - std::cout << "[HighPassFilter-HighFreq] Avg input energy: " - << avg_input_energy << ", Avg output energy: " << avg_output_energy - << ", Pass ratio: " << pass_ratio * 100.0 << "%" << std::endl; + std::cout << "[HighPassFilter-HighFreq] Avg input energy: " << avg_input_energy + << ", Avg output energy: " << avg_output_energy << ", Pass ratio: " << pass_ratio * 100.0 << "%" + << std::endl; // High frequencies should pass through with minimal attenuation // Allow up to 20% loss due to processing artifacts @@ -721,7 +698,7 @@ TEST_F(AudioProcessingModuleTest, AGCProcessesAudioWithoutError) { for (int i = 0; i < kFrames; ++i) { AudioFrame frame = create10msFrame(48000, 1); - auto &data = frame.data(); + auto& data = frame.data(); int sample_rate = frame.sample_rate(); int samples_per_channel = frame.samples_per_channel(); @@ -730,10 +707,8 @@ TEST_F(AudioProcessingModuleTest, AGCProcessesAudioWithoutError) { for (int s = 0; s < samples_per_channel; ++s) { double t = static_cast(s) / sample_rate; - double sample = amplitude * (0.5 * std::sin(2.0 * M_PI * 250.0 * t) + - 0.3 * std::sin(2.0 * M_PI * 500.0 * t)); - data[s] = - static_cast(std::clamp(sample, -32768.0, 32767.0)); + double sample = amplitude * (0.5 * std::sin(2.0 * M_PI * 250.0 * t) + 0.3 * std::sin(2.0 * M_PI * 500.0 * t)); + data[s] = static_cast(std::clamp(sample, -32768.0, 32767.0)); } total_input_energy += calculateRMS(frame); @@ -745,13 +720,11 @@ TEST_F(AudioProcessingModuleTest, AGCProcessesAudioWithoutError) { double avg_output = total_output_energy / kFrames; std::cout << "[AGC] Processed " << kFrames << " frames. " - << "Avg input=" << avg_input << ", Avg output=" << avg_output - << std::endl; + << "Avg input=" << avg_input << ", Avg output=" << avg_output << std::endl; // Verify output is valid (not zero, not clipped) EXPECT_GT(avg_output, 0.0) << "AGC output should not be zero"; - EXPECT_LT(avg_output, 30000.0) - << "AGC output should not be excessively clipped"; + EXPECT_LT(avg_output, 30000.0) << "AGC output should not be excessively clipped"; } TEST_F(AudioProcessingModuleTest, AGCHandlesVaryingInputLevels) { @@ -784,8 +757,7 @@ TEST_F(AudioProcessingModuleTest, AGCHandlesVaryingInputLevels) { double quiet_avg = quiet_output_sum / kFramesPerPhase; double loud_avg = loud_output_sum / kFramesPerPhase; - std::cout << "[AGC-VaryingLevels] Quiet output=" << quiet_avg - << ", Loud output=" << loud_avg << std::endl; + std::cout << "[AGC-VaryingLevels] Quiet output=" << quiet_avg << ", Loud output=" << loud_avg << std::endl; // Verify outputs are valid and different levels produce different outputs EXPECT_GT(quiet_avg, 0.0) << "Quiet output should not be zero"; @@ -802,23 +774,22 @@ TEST_F(AudioProcessingModuleTest, AGCAttenuatesLoudSpeech) { int num_channels = 0; std::string wav_path = std::string(LIVEKIT_ROOT_DIR) + "/data/welcome.wav"; - ASSERT_TRUE(readWavFile(wav_path, original_samples, sample_rate, num_channels)) << "Could not read " << wav_path << " (is Git LFS pulled?)"; + ASSERT_TRUE(readWavFile(wav_path, original_samples, sample_rate, num_channels)) + << "Could not read " << wav_path << " (is Git LFS pulled?)"; - std::cout << "[AGC-LoudSpeech] Loaded " << original_samples.size() - << " samples, " << sample_rate << " Hz, " << num_channels - << " channels" << std::endl; + std::cout << "[AGC-LoudSpeech] Loaded " << original_samples.size() << " samples, " << sample_rate << " Hz, " + << num_channels << " channels" << std::endl; // Scale up to simulate loud input (3x original volume) std::vector loud_samples = original_samples; scaleAudio(loud_samples, 3.0); double loud_input_rms = 0.0; - for (const auto &s : loud_samples) { + for (const auto& s : loud_samples) { loud_input_rms += static_cast(s) * static_cast(s); } loud_input_rms = std::sqrt(loud_input_rms / loud_samples.size()); - std::cout << "[AGC-LoudSpeech] Loud input RMS (3x): " << loud_input_rms - << std::endl; + std::cout << "[AGC-LoudSpeech] Loud input RMS (3x): " << loud_input_rms << std::endl; // Create APM with AGC enabled AudioProcessingModule::Options opts; @@ -833,9 +804,8 @@ TEST_F(AudioProcessingModuleTest, AGCAttenuatesLoudSpeech) { int frame_count = 0; for (int f = 0; f < total_frames; ++f) { - std::vector frame_data( - loud_samples.begin() + f * samples_per_frame, - loud_samples.begin() + (f + 1) * samples_per_frame); + std::vector frame_data(loud_samples.begin() + f * samples_per_frame, + loud_samples.begin() + (f + 1) * samples_per_frame); AudioFrame frame(frame_data, sample_rate, num_channels, samples_per_frame); apm.processStream(frame); @@ -845,20 +815,17 @@ TEST_F(AudioProcessingModuleTest, AGCAttenuatesLoudSpeech) { } double avg_output_rms = total_output_rms / frame_count; - double gain_applied = - (loud_input_rms > 0) ? (avg_output_rms / loud_input_rms) : 0.0; + double gain_applied = (loud_input_rms > 0) ? (avg_output_rms / loud_input_rms) : 0.0; - std::cout << "[AGC-LoudSpeech] Input RMS=" << loud_input_rms - << ", Output RMS=" << avg_output_rms + std::cout << "[AGC-LoudSpeech] Input RMS=" << loud_input_rms << ", Output RMS=" << avg_output_rms << ", Effective gain=" << gain_applied << "x" << std::endl; // Verify AGC attenuated the loud signal (gain < 1.0) EXPECT_GT(avg_output_rms, 0.0) << "Output should not be zero"; - EXPECT_LT(gain_applied, 1.0) - << "AGC should attenuate loud audio (gain < 1.0)"; + EXPECT_LT(gain_applied, 1.0) << "AGC should attenuate loud audio (gain < 1.0)"; - std::cout << "[AGC-LoudSpeech] SUCCESS: AGC attenuated loud speech by " - << (1.0 - gain_applied) * 100.0 << "%" << std::endl; + std::cout << "[AGC-LoudSpeech] SUCCESS: AGC attenuated loud speech by " << (1.0 - gain_applied) * 100.0 << "%" + << std::endl; } TEST_F(AudioProcessingModuleTest, AGCWithNoiseSuppressionCombined) { @@ -893,12 +860,11 @@ TEST_F(AudioProcessingModuleTest, AGCWithNoiseSuppressionCombined) { fillWithSineWave(frame, kSignalFrequency, kSignalAmplitude); // Add noise on top - auto &data = frame.data(); + auto& data = frame.data(); std::mt19937 gen(static_cast(kSeed + 50 + i)); std::uniform_real_distribution<> dis(-kNoiseAmplitude, kNoiseAmplitude); - for (auto &sample : data) { - sample = static_cast(std::clamp( - static_cast(sample) + dis(gen), -32768.0, 32767.0)); + for (auto& sample : data) { + sample = static_cast(std::clamp(static_cast(sample) + dis(gen), -32768.0, 32767.0)); } apm.processStream(frame); @@ -912,12 +878,11 @@ TEST_F(AudioProcessingModuleTest, AGCWithNoiseSuppressionCombined) { double avg_output_energy = signal_energy_sum / signal_frames; std::cout << "[AGC+NS Combined] Avg output energy: " << avg_output_energy - << " (input signal amplitude: " << kSignalAmplitude - << ", noise amplitude: " << kNoiseAmplitude << ")" << std::endl; + << " (input signal amplitude: " << kSignalAmplitude << ", noise amplitude: " << kNoiseAmplitude << ")" + << std::endl; // Should have reasonable output energy (AGC boosted, NS cleaned) - EXPECT_GT(avg_output_energy, 100.0) - << "Combined AGC+NS should produce reasonable output"; + EXPECT_GT(avg_output_energy, 100.0) << "Combined AGC+NS should produce reasonable output"; } } // namespace livekit::test diff --git a/src/tests/unit/test_logging.cpp b/src/tests/unit/test_logging.cpp index 3e45fb3d..a58dbff5 100644 --- a/src/tests/unit/test_logging.cpp +++ b/src/tests/unit/test_logging.cpp @@ -17,14 +17,14 @@ #include #include -#include "lk_log.h" - #include #include #include #include #include +#include "lk_log.h" + namespace livekit::test { class LoggingTest : public ::testing::Test { @@ -53,9 +53,7 @@ TEST_F(LoggingTest, SetAndGetLogLevel) { } } -TEST_F(LoggingTest, DefaultLogLevelIsInfo) { - EXPECT_EQ(livekit::getLogLevel(), LogLevel::Info); -} +TEST_F(LoggingTest, DefaultLogLevelIsInfo) { EXPECT_EQ(livekit::getLogLevel(), LogLevel::Info); } // --------------------------------------------------------------------------- // setLogCallback captures messages @@ -71,8 +69,7 @@ TEST_F(LoggingTest, CallbackReceivesLogMessages) { std::mutex mtx; std::vector captured; - livekit::setLogCallback([&](LogLevel level, const std::string &logger_name, - const std::string &message) { + livekit::setLogCallback([&](LogLevel level, const std::string& logger_name, const std::string& message) { std::lock_guard lock(mtx); captured.push_back({level, logger_name, message}); }); @@ -85,7 +82,7 @@ TEST_F(LoggingTest, CallbackReceivesLogMessages) { ASSERT_GE(captured.size(), 1u); bool found = false; - for (const auto &entry : captured) { + for (const auto& entry : captured) { if (entry.message.find("hello from test") != std::string::npos) { EXPECT_EQ(entry.level, LogLevel::Info); EXPECT_EQ(entry.logger_name, "livekit"); @@ -100,11 +97,10 @@ TEST_F(LoggingTest, CallbackReceivesCorrectLevel) { std::mutex mtx; std::vector levels_seen; - livekit::setLogCallback( - [&](LogLevel level, const std::string &, const std::string &) { - std::lock_guard lock(mtx); - levels_seen.push_back(level); - }); + livekit::setLogCallback([&](LogLevel level, const std::string&, const std::string&) { + std::lock_guard lock(mtx); + levels_seen.push_back(level); + }); livekit::setLogLevel(LogLevel::Trace); @@ -126,10 +122,7 @@ TEST_F(LoggingTest, CallbackReceivesCorrectLevel) { TEST_F(LoggingTest, MessagesFilteredBelowLevel) { std::atomic call_count{0}; - livekit::setLogCallback( - [&](LogLevel, const std::string &, const std::string &) { - call_count.fetch_add(1); - }); + livekit::setLogCallback([&](LogLevel, const std::string&, const std::string&) { call_count.fetch_add(1); }); livekit::setLogLevel(LogLevel::Warn); @@ -137,24 +130,19 @@ TEST_F(LoggingTest, MessagesFilteredBelowLevel) { LK_LOG_DEBUG("should be filtered"); LK_LOG_INFO("should be filtered"); - EXPECT_EQ(call_count.load(), 0) - << "Messages below Warn should not reach callback"; + EXPECT_EQ(call_count.load(), 0) << "Messages below Warn should not reach callback"; LK_LOG_WARN("should pass"); LK_LOG_ERROR("should pass"); LK_LOG_CRITICAL("should pass"); - EXPECT_EQ(call_count.load(), 3) - << "Messages at or above Warn should reach callback"; + EXPECT_EQ(call_count.load(), 3) << "Messages at or above Warn should reach callback"; } TEST_F(LoggingTest, OffLevelSuppressesEverything) { std::atomic call_count{0}; - livekit::setLogCallback( - [&](LogLevel, const std::string &, const std::string &) { - call_count.fetch_add(1); - }); + livekit::setLogCallback([&](LogLevel, const std::string&, const std::string&) { call_count.fetch_add(1); }); livekit::setLogLevel(LogLevel::Off); @@ -175,10 +163,7 @@ TEST_F(LoggingTest, OffLevelSuppressesEverything) { TEST_F(LoggingTest, NullCallbackRestoresDefault) { std::atomic call_count{0}; - livekit::setLogCallback( - [&](LogLevel, const std::string &, const std::string &) { - call_count.fetch_add(1); - }); + livekit::setLogCallback([&](LogLevel, const std::string&, const std::string&) { call_count.fetch_add(1); }); livekit::setLogLevel(LogLevel::Trace); LK_LOG_INFO("goes to callback"); @@ -188,8 +173,7 @@ TEST_F(LoggingTest, NullCallbackRestoresDefault) { livekit::setLogCallback(nullptr); LK_LOG_INFO("goes to stderr, not callback"); - EXPECT_EQ(call_count.load(), before) - << "After setLogCallback(nullptr), old callback should not fire"; + EXPECT_EQ(call_count.load(), before) << "After setLogCallback(nullptr), old callback should not fire"; } // --------------------------------------------------------------------------- @@ -200,8 +184,7 @@ TEST_F(LoggingTest, ReplacingCallbackStopsOldOne) { std::atomic old_count{0}; std::atomic new_count{0}; - livekit::setLogCallback([&](LogLevel, const std::string &, - const std::string &) { old_count.fetch_add(1); }); + livekit::setLogCallback([&](LogLevel, const std::string&, const std::string&) { old_count.fetch_add(1); }); livekit::setLogLevel(LogLevel::Trace); LK_LOG_INFO("to old"); @@ -209,13 +192,11 @@ TEST_F(LoggingTest, ReplacingCallbackStopsOldOne) { int old_before = old_count.load(); - livekit::setLogCallback([&](LogLevel, const std::string &, - const std::string &) { new_count.fetch_add(1); }); + livekit::setLogCallback([&](LogLevel, const std::string&, const std::string&) { new_count.fetch_add(1); }); LK_LOG_INFO("to new"); - EXPECT_EQ(old_count.load(), old_before) - << "Old callback should not receive messages after replacement"; + EXPECT_EQ(old_count.load(), old_before) << "Old callback should not receive messages after replacement"; EXPECT_GE(new_count.load(), 1) << "New callback should receive messages"; } @@ -226,11 +207,9 @@ TEST_F(LoggingTest, ReplacingCallbackStopsOldOne) { TEST_F(LoggingTest, LogLevelPreservedAcrossCallbackChange) { livekit::setLogLevel(LogLevel::Error); - livekit::setLogCallback( - [](LogLevel, const std::string &, const std::string &) {}); + livekit::setLogCallback([](LogLevel, const std::string&, const std::string&) {}); - EXPECT_EQ(livekit::getLogLevel(), LogLevel::Error) - << "setLogCallback should preserve the current log level"; + EXPECT_EQ(livekit::getLogLevel(), LogLevel::Error) << "setLogCallback should preserve the current log level"; } // --------------------------------------------------------------------------- @@ -256,7 +235,7 @@ TEST_F(LoggingTest, ConcurrentSetLogLevelDoesNotCrash) { }); } - for (auto &th : threads) { + for (auto& th : threads) { th.join(); } } @@ -264,10 +243,7 @@ TEST_F(LoggingTest, ConcurrentSetLogLevelDoesNotCrash) { TEST_F(LoggingTest, ConcurrentLogEmissionDoesNotCrash) { std::atomic call_count{0}; - livekit::setLogCallback( - [&](LogLevel, const std::string &, const std::string &) { - call_count.fetch_add(1); - }); + livekit::setLogCallback([&](LogLevel, const std::string&, const std::string&) { call_count.fetch_add(1); }); livekit::setLogLevel(LogLevel::Trace); @@ -285,7 +261,7 @@ TEST_F(LoggingTest, ConcurrentLogEmissionDoesNotCrash) { }); } - for (auto &th : threads) { + for (auto& th : threads) { th.join(); } diff --git a/src/tests/unit/test_room_callbacks.cpp b/src/tests/unit/test_room_callbacks.cpp index 90ac35b4..d1f1739e 100644 --- a/src/tests/unit/test_room_callbacks.cpp +++ b/src/tests/unit/test_room_callbacks.cpp @@ -29,9 +29,7 @@ namespace livekit { class RoomCallbackTest : public ::testing::Test { protected: - void SetUp() override { - livekit::initialize(livekit::LogLevel::Info, livekit::LogSink::kConsole); - } + void SetUp() override { livekit::initialize(livekit::LogLevel::Info, livekit::LogSink::kConsole); } void TearDown() override { livekit::shutdown(); } }; @@ -39,39 +37,33 @@ class RoomCallbackTest : public ::testing::Test { TEST_F(RoomCallbackTest, AudioCallbackRegistrationIsAccepted) { Room room; - EXPECT_NO_THROW(room.setOnAudioFrameCallback( - "alice", TrackSource::SOURCE_MICROPHONE, [](const AudioFrame &) {})); + EXPECT_NO_THROW(room.setOnAudioFrameCallback("alice", TrackSource::SOURCE_MICROPHONE, [](const AudioFrame&) {})); } TEST_F(RoomCallbackTest, VideoCallbackRegistrationIsAccepted) { Room room; EXPECT_NO_THROW( - room.setOnVideoFrameCallback("alice", TrackSource::SOURCE_CAMERA, - [](const VideoFrame &, std::int64_t) {})); + room.setOnVideoFrameCallback("alice", TrackSource::SOURCE_CAMERA, [](const VideoFrame&, std::int64_t) {})); } TEST_F(RoomCallbackTest, AudioCallbackRegistrationByTrackNameIsAccepted) { Room room; - EXPECT_NO_THROW(room.setOnAudioFrameCallback("alice", "mic-main", - [](const AudioFrame &) {})); + EXPECT_NO_THROW(room.setOnAudioFrameCallback("alice", "mic-main", [](const AudioFrame&) {})); } TEST_F(RoomCallbackTest, VideoCallbackRegistrationByTrackNameIsAccepted) { Room room; - EXPECT_NO_THROW(room.setOnVideoFrameCallback( - "alice", "cam-main", [](const VideoFrame &, std::int64_t) {})); + EXPECT_NO_THROW(room.setOnVideoFrameCallback("alice", "cam-main", [](const VideoFrame&, std::int64_t) {})); } TEST_F(RoomCallbackTest, ClearingMissingCallbacksIsNoOp) { Room room; - EXPECT_NO_THROW( - room.clearOnAudioFrameCallback("nobody", TrackSource::SOURCE_MICROPHONE)); - EXPECT_NO_THROW( - room.clearOnVideoFrameCallback("nobody", TrackSource::SOURCE_CAMERA)); + EXPECT_NO_THROW(room.clearOnAudioFrameCallback("nobody", TrackSource::SOURCE_MICROPHONE)); + EXPECT_NO_THROW(room.clearOnVideoFrameCallback("nobody", TrackSource::SOURCE_CAMERA)); EXPECT_NO_THROW(room.clearOnAudioFrameCallback("nobody", "missing-audio")); EXPECT_NO_THROW(room.clearOnVideoFrameCallback("nobody", "missing-video")); } @@ -81,75 +73,60 @@ TEST_F(RoomCallbackTest, ReRegisteringSameAudioKeyDoesNotThrow) { std::atomic counter1{0}; std::atomic counter2{0}; - EXPECT_NO_THROW(room.setOnAudioFrameCallback( - "alice", TrackSource::SOURCE_MICROPHONE, - [&counter1](const AudioFrame &) { counter1++; })); - EXPECT_NO_THROW(room.setOnAudioFrameCallback( - "alice", TrackSource::SOURCE_MICROPHONE, - [&counter2](const AudioFrame &) { counter2++; })); + EXPECT_NO_THROW(room.setOnAudioFrameCallback("alice", TrackSource::SOURCE_MICROPHONE, + [&counter1](const AudioFrame&) { counter1++; })); + EXPECT_NO_THROW(room.setOnAudioFrameCallback("alice", TrackSource::SOURCE_MICROPHONE, + [&counter2](const AudioFrame&) { counter2++; })); } TEST_F(RoomCallbackTest, ReRegisteringSameVideoKeyDoesNotThrow) { Room room; EXPECT_NO_THROW( - room.setOnVideoFrameCallback("alice", TrackSource::SOURCE_CAMERA, - [](const VideoFrame &, std::int64_t) {})); + room.setOnVideoFrameCallback("alice", TrackSource::SOURCE_CAMERA, [](const VideoFrame&, std::int64_t) {})); EXPECT_NO_THROW( - room.setOnVideoFrameCallback("alice", TrackSource::SOURCE_CAMERA, - [](const VideoFrame &, std::int64_t) {})); + room.setOnVideoFrameCallback("alice", TrackSource::SOURCE_CAMERA, [](const VideoFrame&, std::int64_t) {})); } TEST_F(RoomCallbackTest, DistinctAudioAndVideoCallbacksCanCoexist) { Room room; - EXPECT_NO_THROW(room.setOnAudioFrameCallback( - "alice", TrackSource::SOURCE_MICROPHONE, [](const AudioFrame &) {})); + EXPECT_NO_THROW(room.setOnAudioFrameCallback("alice", TrackSource::SOURCE_MICROPHONE, [](const AudioFrame&) {})); EXPECT_NO_THROW( - room.setOnVideoFrameCallback("alice", TrackSource::SOURCE_CAMERA, - [](const VideoFrame &, std::int64_t) {})); - EXPECT_NO_THROW(room.setOnAudioFrameCallback( - "bob", TrackSource::SOURCE_MICROPHONE, [](const AudioFrame &) {})); + room.setOnVideoFrameCallback("alice", TrackSource::SOURCE_CAMERA, [](const VideoFrame&, std::int64_t) {})); + EXPECT_NO_THROW(room.setOnAudioFrameCallback("bob", TrackSource::SOURCE_MICROPHONE, [](const AudioFrame&) {})); EXPECT_NO_THROW( - room.setOnVideoFrameCallback("bob", TrackSource::SOURCE_CAMERA, - [](const VideoFrame &, std::int64_t) {})); + room.setOnVideoFrameCallback("bob", TrackSource::SOURCE_CAMERA, [](const VideoFrame&, std::int64_t) {})); } TEST_F(RoomCallbackTest, SameSourceDifferentTrackNamesAreAccepted) { Room room; - EXPECT_NO_THROW(room.setOnVideoFrameCallback( - "alice", "cam-main", [](const VideoFrame &, std::int64_t) {})); - EXPECT_NO_THROW(room.setOnVideoFrameCallback( - "alice", "cam-backup", [](const VideoFrame &, std::int64_t) {})); + EXPECT_NO_THROW(room.setOnVideoFrameCallback("alice", "cam-main", [](const VideoFrame&, std::int64_t) {})); + EXPECT_NO_THROW(room.setOnVideoFrameCallback("alice", "cam-backup", [](const VideoFrame&, std::int64_t) {})); } TEST_F(RoomCallbackTest, ClearingTrackNameCallbackIsAccepted) { Room room; - EXPECT_NO_THROW(room.setOnAudioFrameCallback("alice", "mic-main", - [](const AudioFrame &) {})); + EXPECT_NO_THROW(room.setOnAudioFrameCallback("alice", "mic-main", [](const AudioFrame&) {})); EXPECT_NO_THROW(room.clearOnAudioFrameCallback("alice", "mic-main")); } TEST_F(RoomCallbackTest, SourceAndTrackNameCallbacksCanCoexist) { Room room; - EXPECT_NO_THROW(room.setOnAudioFrameCallback( - "alice", TrackSource::SOURCE_MICROPHONE, [](const AudioFrame &) {})); - EXPECT_NO_THROW(room.setOnAudioFrameCallback("alice", "mic-main", - [](const AudioFrame &) {})); + EXPECT_NO_THROW(room.setOnAudioFrameCallback("alice", TrackSource::SOURCE_MICROPHONE, [](const AudioFrame&) {})); + EXPECT_NO_THROW(room.setOnAudioFrameCallback("alice", "mic-main", [](const AudioFrame&) {})); } TEST_F(RoomCallbackTest, DataCallbackRegistrationReturnsUsableIds) { Room room; - const auto id1 = room.addOnDataFrameCallback( - "alice", "track-a", - [](const std::vector &, std::optional) {}); - const auto id2 = room.addOnDataFrameCallback( - "alice", "track-a", - [](const std::vector &, std::optional) {}); + const auto id1 = room.addOnDataFrameCallback("alice", "track-a", + [](const std::vector&, std::optional) {}); + const auto id2 = room.addOnDataFrameCallback("alice", "track-a", + [](const std::vector&, std::optional) {}); EXPECT_NE(id1, std::numeric_limits::max()); EXPECT_NE(id2, std::numeric_limits::max()); @@ -162,33 +139,27 @@ TEST_F(RoomCallbackTest, DataCallbackRegistrationReturnsUsableIds) { TEST_F(RoomCallbackTest, RemovingUnknownDataCallbackIsNoOp) { Room room; - EXPECT_NO_THROW(room.removeOnDataFrameCallback( - std::numeric_limits::max())); + EXPECT_NO_THROW(room.removeOnDataFrameCallback(std::numeric_limits::max())); } TEST_F(RoomCallbackTest, DestroyRoomWithRegisteredCallbacksIsSafe) { EXPECT_NO_THROW({ Room room; - room.setOnAudioFrameCallback("alice", TrackSource::SOURCE_MICROPHONE, - [](const AudioFrame &) {}); - room.setOnVideoFrameCallback("bob", TrackSource::SOURCE_CAMERA, - [](const VideoFrame &, std::int64_t) {}); - room.addOnDataFrameCallback( - "carol", "track", - [](const std::vector &, std::optional) {}); + room.setOnAudioFrameCallback("alice", TrackSource::SOURCE_MICROPHONE, [](const AudioFrame&) {}); + room.setOnVideoFrameCallback("bob", TrackSource::SOURCE_CAMERA, [](const VideoFrame&, std::int64_t) {}); + room.addOnDataFrameCallback("carol", "track", + [](const std::vector&, std::optional) {}); }); } TEST_F(RoomCallbackTest, DestroyRoomAfterClearingCallbacksIsSafe) { EXPECT_NO_THROW({ Room room; - room.setOnAudioFrameCallback("alice", TrackSource::SOURCE_MICROPHONE, - [](const AudioFrame &) {}); + room.setOnAudioFrameCallback("alice", TrackSource::SOURCE_MICROPHONE, [](const AudioFrame&) {}); room.clearOnAudioFrameCallback("alice", TrackSource::SOURCE_MICROPHONE); - const auto id = room.addOnDataFrameCallback( - "alice", "track", - [](const std::vector &, std::optional) {}); + const auto id = room.addOnDataFrameCallback("alice", "track", + [](const std::vector&, std::optional) {}); room.removeOnDataFrameCallback(id); }); } @@ -205,14 +176,13 @@ TEST_F(RoomCallbackTest, ConcurrentRegistrationDoesNotCrash) { threads.emplace_back([&room, t, kIterations]() { for (int i = 0; i < kIterations; ++i) { const std::string id = "participant-" + std::to_string(t); - room.setOnAudioFrameCallback(id, TrackSource::SOURCE_MICROPHONE, - [](const AudioFrame &) {}); + room.setOnAudioFrameCallback(id, TrackSource::SOURCE_MICROPHONE, [](const AudioFrame&) {}); room.clearOnAudioFrameCallback(id, TrackSource::SOURCE_MICROPHONE); } }); } - for (auto &thread : threads) { + for (auto& thread : threads) { thread.join(); } @@ -231,20 +201,16 @@ TEST_F(RoomCallbackTest, ConcurrentMixedRegistrationDoesNotCrash) { threads.emplace_back([&room, t, kIterations]() { const std::string id = "p-" + std::to_string(t); for (int i = 0; i < kIterations; ++i) { - room.setOnAudioFrameCallback(id, TrackSource::SOURCE_MICROPHONE, - [](const AudioFrame &) {}); - room.setOnVideoFrameCallback(id, TrackSource::SOURCE_CAMERA, - [](const VideoFrame &, std::int64_t) {}); - const auto data_id = - room.addOnDataFrameCallback(id, "track", - [](const std::vector &, - std::optional) {}); + room.setOnAudioFrameCallback(id, TrackSource::SOURCE_MICROPHONE, [](const AudioFrame&) {}); + room.setOnVideoFrameCallback(id, TrackSource::SOURCE_CAMERA, [](const VideoFrame&, std::int64_t) {}); + const auto data_id = room.addOnDataFrameCallback( + id, "track", [](const std::vector&, std::optional) {}); room.removeOnDataFrameCallback(data_id); } }); } - for (auto &thread : threads) { + for (auto& thread : threads) { thread.join(); } @@ -256,14 +222,12 @@ TEST_F(RoomCallbackTest, ManyDistinctAudioCallbacksCanBeRegisteredAndCleared) { constexpr int kCount = 50; for (int i = 0; i < kCount; ++i) { - EXPECT_NO_THROW(room.setOnAudioFrameCallback( - "participant-" + std::to_string(i), TrackSource::SOURCE_MICROPHONE, - [](const AudioFrame &) {})); + EXPECT_NO_THROW(room.setOnAudioFrameCallback("participant-" + std::to_string(i), TrackSource::SOURCE_MICROPHONE, + [](const AudioFrame&) {})); } for (int i = 0; i < kCount; ++i) { - EXPECT_NO_THROW(room.clearOnAudioFrameCallback( - "participant-" + std::to_string(i), TrackSource::SOURCE_MICROPHONE)); + EXPECT_NO_THROW(room.clearOnAudioFrameCallback("participant-" + std::to_string(i), TrackSource::SOURCE_MICROPHONE)); } } diff --git a/src/tests/unit/test_sdk_initialization.cpp b/src/tests/unit/test_sdk_initialization.cpp index 9391ebcc..23d8e6b8 100644 --- a/src/tests/unit/test_sdk_initialization.cpp +++ b/src/tests/unit/test_sdk_initialization.cpp @@ -33,8 +33,7 @@ TEST_F(SDKInitializationTest, InitializeDefault) { TEST_F(SDKInitializationTest, InitializeWithLogLevel) { bool result = livekit::initialize(livekit::LogLevel::Debug); - EXPECT_TRUE(result) - << "Initialization with explicit log level should succeed"; + EXPECT_TRUE(result) << "Initialization with explicit log level should succeed"; EXPECT_EQ(livekit::getLogLevel(), livekit::LogLevel::Debug); } @@ -56,9 +55,7 @@ TEST_F(SDKInitializationTest, ReinitializeAfterShutdown) { EXPECT_TRUE(second) << "Re-initialization after shutdown should succeed"; } -TEST_F(SDKInitializationTest, ShutdownWithoutInitialize) { - EXPECT_NO_THROW(livekit::shutdown()); -} +TEST_F(SDKInitializationTest, ShutdownWithoutInitialize) { EXPECT_NO_THROW(livekit::shutdown()); } TEST_F(SDKInitializationTest, MultipleShutdowns) { livekit::initialize(); diff --git a/src/tests/unit/test_subscription_thread_dispatcher.cpp b/src/tests/unit/test_subscription_thread_dispatcher.cpp index a3864c64..7840c666 100644 --- a/src/tests/unit/test_subscription_thread_dispatcher.cpp +++ b/src/tests/unit/test_subscription_thread_dispatcher.cpp @@ -28,9 +28,7 @@ namespace livekit { class SubscriptionThreadDispatcherTest : public ::testing::Test { protected: - void SetUp() override { - livekit::initialize(livekit::LogLevel::Info, livekit::LogSink::kConsole); - } + void SetUp() override { livekit::initialize(livekit::LogLevel::Info, livekit::LogSink::kConsole); } void TearDown() override { livekit::shutdown(); } @@ -39,27 +37,13 @@ class SubscriptionThreadDispatcherTest : public ::testing::Test { using DataCallbackKey = SubscriptionThreadDispatcher::DataCallbackKey; using DataCallbackKeyHash = SubscriptionThreadDispatcher::DataCallbackKeyHash; - static auto &audioCallbacks(SubscriptionThreadDispatcher &dispatcher) { - return dispatcher.audio_callbacks_; - } - static auto &videoCallbacks(SubscriptionThreadDispatcher &dispatcher) { - return dispatcher.video_callbacks_; - } - static auto &activeReaders(SubscriptionThreadDispatcher &dispatcher) { - return dispatcher.active_readers_; - } - static auto &dataCallbacks(SubscriptionThreadDispatcher &dispatcher) { - return dispatcher.data_callbacks_; - } - static auto &activeDataReaders(SubscriptionThreadDispatcher &dispatcher) { - return dispatcher.active_data_readers_; - } - static auto &remoteDataTracks(SubscriptionThreadDispatcher &dispatcher) { - return dispatcher.remote_data_tracks_; - } - static int maxActiveReaders() { - return SubscriptionThreadDispatcher::kMaxActiveReaders; - } + static auto& audioCallbacks(SubscriptionThreadDispatcher& dispatcher) { return dispatcher.audio_callbacks_; } + static auto& videoCallbacks(SubscriptionThreadDispatcher& dispatcher) { return dispatcher.video_callbacks_; } + static auto& activeReaders(SubscriptionThreadDispatcher& dispatcher) { return dispatcher.active_readers_; } + static auto& dataCallbacks(SubscriptionThreadDispatcher& dispatcher) { return dispatcher.data_callbacks_; } + static auto& activeDataReaders(SubscriptionThreadDispatcher& dispatcher) { return dispatcher.active_data_readers_; } + static auto& remoteDataTracks(SubscriptionThreadDispatcher& dispatcher) { return dispatcher.remote_data_tracks_; } + static int maxActiveReaders() { return SubscriptionThreadDispatcher::kMaxActiveReaders; } }; // ============================================================================ @@ -84,8 +68,7 @@ TEST_F(SubscriptionThreadDispatcherTest, CallbackKeyDifferentSourceNotEqual) { EXPECT_FALSE(a == b); } -TEST_F(SubscriptionThreadDispatcherTest, - CallbackKeyDifferentTrackNameNotEqual) { +TEST_F(SubscriptionThreadDispatcherTest, CallbackKeyDifferentTrackNameNotEqual) { CallbackKey a{"alice", TrackSource::SOURCE_UNKNOWN, "cam-main"}; CallbackKey b{"alice", TrackSource::SOURCE_UNKNOWN, "cam-backup"}; EXPECT_FALSE(a == b); @@ -95,16 +78,14 @@ TEST_F(SubscriptionThreadDispatcherTest, // CallbackKeyHash // ============================================================================ -TEST_F(SubscriptionThreadDispatcherTest, - CallbackKeyHashEqualKeysProduceSameHash) { +TEST_F(SubscriptionThreadDispatcherTest, CallbackKeyHashEqualKeysProduceSameHash) { CallbackKey a{"alice", TrackSource::SOURCE_MICROPHONE, ""}; CallbackKey b{"alice", TrackSource::SOURCE_MICROPHONE, ""}; CallbackKeyHash hasher; EXPECT_EQ(hasher(a), hasher(b)); } -TEST_F(SubscriptionThreadDispatcherTest, - CallbackKeyHashDifferentKeysLikelyDifferentHash) { +TEST_F(SubscriptionThreadDispatcherTest, CallbackKeyHashDifferentKeysLikelyDifferentHash) { CallbackKeyHash hasher; CallbackKey mic{"alice", TrackSource::SOURCE_MICROPHONE, ""}; CallbackKey cam{"alice", TrackSource::SOURCE_CAMERA, ""}; @@ -153,9 +134,7 @@ TEST_F(SubscriptionThreadDispatcherTest, CallbackKeyEmptyIdentityWorks) { // kMaxActiveReaders // ============================================================================ -TEST_F(SubscriptionThreadDispatcherTest, MaxActiveReadersIs20) { - EXPECT_EQ(maxActiveReaders(), 20); -} +TEST_F(SubscriptionThreadDispatcherTest, MaxActiveReadersIs20) { EXPECT_EQ(maxActiveReaders(), 20); } // ============================================================================ // Registration and clearing @@ -163,84 +142,64 @@ TEST_F(SubscriptionThreadDispatcherTest, MaxActiveReadersIs20) { TEST_F(SubscriptionThreadDispatcherTest, SetAudioCallbackStoresRegistration) { SubscriptionThreadDispatcher dispatcher; - dispatcher.setOnAudioFrameCallback("alice", TrackSource::SOURCE_MICROPHONE, - [](const AudioFrame &) {}); + dispatcher.setOnAudioFrameCallback("alice", TrackSource::SOURCE_MICROPHONE, [](const AudioFrame&) {}); EXPECT_EQ(audioCallbacks(dispatcher).size(), 1u); } -TEST_F(SubscriptionThreadDispatcherTest, - SetAudioCallbackByTrackNameStoresRegistration) { +TEST_F(SubscriptionThreadDispatcherTest, SetAudioCallbackByTrackNameStoresRegistration) { SubscriptionThreadDispatcher dispatcher; - dispatcher.setOnAudioFrameCallback("alice", "mic-main", - [](const AudioFrame &) {}); + dispatcher.setOnAudioFrameCallback("alice", "mic-main", [](const AudioFrame&) {}); EXPECT_EQ(audioCallbacks(dispatcher).size(), 1u); - EXPECT_EQ( - audioCallbacks(dispatcher) - .count(CallbackKey{"alice", TrackSource::SOURCE_UNKNOWN, "mic-main"}), - 1u); + EXPECT_EQ(audioCallbacks(dispatcher).count(CallbackKey{"alice", TrackSource::SOURCE_UNKNOWN, "mic-main"}), 1u); } TEST_F(SubscriptionThreadDispatcherTest, SetVideoCallbackStoresRegistration) { SubscriptionThreadDispatcher dispatcher; - dispatcher.setOnVideoFrameCallback("alice", TrackSource::SOURCE_CAMERA, - [](const VideoFrame &, std::int64_t) {}); + dispatcher.setOnVideoFrameCallback("alice", TrackSource::SOURCE_CAMERA, [](const VideoFrame&, std::int64_t) {}); EXPECT_EQ(videoCallbacks(dispatcher).size(), 1u); } -TEST_F(SubscriptionThreadDispatcherTest, - SetVideoCallbackByTrackNameStoresRegistration) { +TEST_F(SubscriptionThreadDispatcherTest, SetVideoCallbackByTrackNameStoresRegistration) { SubscriptionThreadDispatcher dispatcher; - dispatcher.setOnVideoFrameCallback("alice", "cam-main", - [](const VideoFrame &, std::int64_t) {}); + dispatcher.setOnVideoFrameCallback("alice", "cam-main", [](const VideoFrame&, std::int64_t) {}); EXPECT_EQ(videoCallbacks(dispatcher).size(), 1u); - EXPECT_EQ( - videoCallbacks(dispatcher) - .count(CallbackKey{"alice", TrackSource::SOURCE_UNKNOWN, "cam-main"}), - 1u); + EXPECT_EQ(videoCallbacks(dispatcher).count(CallbackKey{"alice", TrackSource::SOURCE_UNKNOWN, "cam-main"}), 1u); } -TEST_F(SubscriptionThreadDispatcherTest, - ClearAudioCallbackRemovesRegistration) { +TEST_F(SubscriptionThreadDispatcherTest, ClearAudioCallbackRemovesRegistration) { SubscriptionThreadDispatcher dispatcher; - dispatcher.setOnAudioFrameCallback("alice", TrackSource::SOURCE_MICROPHONE, - [](const AudioFrame &) {}); + dispatcher.setOnAudioFrameCallback("alice", TrackSource::SOURCE_MICROPHONE, [](const AudioFrame&) {}); ASSERT_EQ(audioCallbacks(dispatcher).size(), 1u); dispatcher.clearOnAudioFrameCallback("alice", TrackSource::SOURCE_MICROPHONE); EXPECT_EQ(audioCallbacks(dispatcher).size(), 0u); } -TEST_F(SubscriptionThreadDispatcherTest, - ClearAudioCallbackByTrackNameRemovesRegistration) { +TEST_F(SubscriptionThreadDispatcherTest, ClearAudioCallbackByTrackNameRemovesRegistration) { SubscriptionThreadDispatcher dispatcher; - dispatcher.setOnAudioFrameCallback("alice", "mic-main", - [](const AudioFrame &) {}); + dispatcher.setOnAudioFrameCallback("alice", "mic-main", [](const AudioFrame&) {}); ASSERT_EQ(audioCallbacks(dispatcher).size(), 1u); dispatcher.clearOnAudioFrameCallback("alice", "mic-main"); EXPECT_EQ(audioCallbacks(dispatcher).size(), 0u); } -TEST_F(SubscriptionThreadDispatcherTest, - ClearVideoCallbackRemovesRegistration) { +TEST_F(SubscriptionThreadDispatcherTest, ClearVideoCallbackRemovesRegistration) { SubscriptionThreadDispatcher dispatcher; - dispatcher.setOnVideoFrameCallback("alice", TrackSource::SOURCE_CAMERA, - [](const VideoFrame &, std::int64_t) {}); + dispatcher.setOnVideoFrameCallback("alice", TrackSource::SOURCE_CAMERA, [](const VideoFrame&, std::int64_t) {}); ASSERT_EQ(videoCallbacks(dispatcher).size(), 1u); dispatcher.clearOnVideoFrameCallback("alice", TrackSource::SOURCE_CAMERA); EXPECT_EQ(videoCallbacks(dispatcher).size(), 0u); } -TEST_F(SubscriptionThreadDispatcherTest, - ClearVideoCallbackByTrackNameRemovesRegistration) { +TEST_F(SubscriptionThreadDispatcherTest, ClearVideoCallbackByTrackNameRemovesRegistration) { SubscriptionThreadDispatcher dispatcher; - dispatcher.setOnVideoFrameCallback("alice", "cam-main", - [](const VideoFrame &, std::int64_t) {}); + dispatcher.setOnVideoFrameCallback("alice", "cam-main", [](const VideoFrame&, std::int64_t) {}); ASSERT_EQ(videoCallbacks(dispatcher).size(), 1u); dispatcher.clearOnVideoFrameCallback("alice", "cam-main"); @@ -249,64 +208,47 @@ TEST_F(SubscriptionThreadDispatcherTest, TEST_F(SubscriptionThreadDispatcherTest, ClearNonExistentCallbackIsNoOp) { SubscriptionThreadDispatcher dispatcher; - EXPECT_NO_THROW(dispatcher.clearOnAudioFrameCallback( - "nobody", TrackSource::SOURCE_MICROPHONE)); - EXPECT_NO_THROW(dispatcher.clearOnVideoFrameCallback( - "nobody", TrackSource::SOURCE_CAMERA)); + EXPECT_NO_THROW(dispatcher.clearOnAudioFrameCallback("nobody", TrackSource::SOURCE_MICROPHONE)); + EXPECT_NO_THROW(dispatcher.clearOnVideoFrameCallback("nobody", TrackSource::SOURCE_CAMERA)); EXPECT_NO_THROW(dispatcher.clearOnAudioFrameCallback("nobody", "missing")); EXPECT_NO_THROW(dispatcher.clearOnVideoFrameCallback("nobody", "missing")); } -TEST_F(SubscriptionThreadDispatcherTest, - OverwriteAudioCallbackKeepsSingleEntry) { +TEST_F(SubscriptionThreadDispatcherTest, OverwriteAudioCallbackKeepsSingleEntry) { SubscriptionThreadDispatcher dispatcher; std::atomic counter1{0}; std::atomic counter2{0}; - dispatcher.setOnAudioFrameCallback( - "alice", TrackSource::SOURCE_MICROPHONE, - [&counter1](const AudioFrame &) { counter1++; }); - dispatcher.setOnAudioFrameCallback( - "alice", TrackSource::SOURCE_MICROPHONE, - [&counter2](const AudioFrame &) { counter2++; }); + dispatcher.setOnAudioFrameCallback("alice", TrackSource::SOURCE_MICROPHONE, + [&counter1](const AudioFrame&) { counter1++; }); + dispatcher.setOnAudioFrameCallback("alice", TrackSource::SOURCE_MICROPHONE, + [&counter2](const AudioFrame&) { counter2++; }); - EXPECT_EQ(audioCallbacks(dispatcher).size(), 1u) - << "Re-registering with the same key should overwrite, not add"; + EXPECT_EQ(audioCallbacks(dispatcher).size(), 1u) << "Re-registering with the same key should overwrite, not add"; } -TEST_F(SubscriptionThreadDispatcherTest, - OverwriteVideoCallbackKeepsSingleEntry) { +TEST_F(SubscriptionThreadDispatcherTest, OverwriteVideoCallbackKeepsSingleEntry) { SubscriptionThreadDispatcher dispatcher; - dispatcher.setOnVideoFrameCallback("alice", TrackSource::SOURCE_CAMERA, - [](const VideoFrame &, std::int64_t) {}); - dispatcher.setOnVideoFrameCallback("alice", TrackSource::SOURCE_CAMERA, - [](const VideoFrame &, std::int64_t) {}); + dispatcher.setOnVideoFrameCallback("alice", TrackSource::SOURCE_CAMERA, [](const VideoFrame&, std::int64_t) {}); + dispatcher.setOnVideoFrameCallback("alice", TrackSource::SOURCE_CAMERA, [](const VideoFrame&, std::int64_t) {}); EXPECT_EQ(videoCallbacks(dispatcher).size(), 1u); } -TEST_F(SubscriptionThreadDispatcherTest, - OverwriteTrackNameAudioCallbackKeepsSingleEntry) { +TEST_F(SubscriptionThreadDispatcherTest, OverwriteTrackNameAudioCallbackKeepsSingleEntry) { SubscriptionThreadDispatcher dispatcher; - dispatcher.setOnAudioFrameCallback("alice", "mic-main", - [](const AudioFrame &) {}); - dispatcher.setOnAudioFrameCallback("alice", "mic-main", - [](const AudioFrame &) {}); + dispatcher.setOnAudioFrameCallback("alice", "mic-main", [](const AudioFrame&) {}); + dispatcher.setOnAudioFrameCallback("alice", "mic-main", [](const AudioFrame&) {}); EXPECT_EQ(audioCallbacks(dispatcher).size(), 1u); } -TEST_F(SubscriptionThreadDispatcherTest, - MultipleDistinctCallbacksAreIndependent) { +TEST_F(SubscriptionThreadDispatcherTest, MultipleDistinctCallbacksAreIndependent) { SubscriptionThreadDispatcher dispatcher; - dispatcher.setOnAudioFrameCallback("alice", TrackSource::SOURCE_MICROPHONE, - [](const AudioFrame &) {}); - dispatcher.setOnVideoFrameCallback("alice", TrackSource::SOURCE_CAMERA, - [](const VideoFrame &, std::int64_t) {}); - dispatcher.setOnAudioFrameCallback("bob", TrackSource::SOURCE_MICROPHONE, - [](const AudioFrame &) {}); - dispatcher.setOnVideoFrameCallback("bob", TrackSource::SOURCE_CAMERA, - [](const VideoFrame &, std::int64_t) {}); + dispatcher.setOnAudioFrameCallback("alice", TrackSource::SOURCE_MICROPHONE, [](const AudioFrame&) {}); + dispatcher.setOnVideoFrameCallback("alice", TrackSource::SOURCE_CAMERA, [](const VideoFrame&, std::int64_t) {}); + dispatcher.setOnAudioFrameCallback("bob", TrackSource::SOURCE_MICROPHONE, [](const AudioFrame&) {}); + dispatcher.setOnVideoFrameCallback("bob", TrackSource::SOURCE_CAMERA, [](const VideoFrame&, std::int64_t) {}); EXPECT_EQ(audioCallbacks(dispatcher).size(), 2u); EXPECT_EQ(videoCallbacks(dispatcher).size(), 2u); @@ -318,11 +260,8 @@ TEST_F(SubscriptionThreadDispatcherTest, TEST_F(SubscriptionThreadDispatcherTest, ClearingOneSourceDoesNotAffectOther) { SubscriptionThreadDispatcher dispatcher; - dispatcher.setOnAudioFrameCallback("alice", TrackSource::SOURCE_MICROPHONE, - [](const AudioFrame &) {}); - dispatcher.setOnAudioFrameCallback("alice", - TrackSource::SOURCE_SCREENSHARE_AUDIO, - [](const AudioFrame &) {}); + dispatcher.setOnAudioFrameCallback("alice", TrackSource::SOURCE_MICROPHONE, [](const AudioFrame&) {}); + dispatcher.setOnAudioFrameCallback("alice", TrackSource::SOURCE_SCREENSHARE_AUDIO, [](const AudioFrame&) {}); ASSERT_EQ(audioCallbacks(dispatcher).size(), 2u); dispatcher.clearOnAudioFrameCallback("alice", TrackSource::SOURCE_MICROPHONE); @@ -332,21 +271,15 @@ TEST_F(SubscriptionThreadDispatcherTest, ClearingOneSourceDoesNotAffectOther) { EXPECT_EQ(audioCallbacks(dispatcher).count(remaining), 1u); } -TEST_F(SubscriptionThreadDispatcherTest, - SourceAndTrackNameAudioCallbacksAreIndependent) { +TEST_F(SubscriptionThreadDispatcherTest, SourceAndTrackNameAudioCallbacksAreIndependent) { SubscriptionThreadDispatcher dispatcher; - dispatcher.setOnAudioFrameCallback("alice", TrackSource::SOURCE_MICROPHONE, - [](const AudioFrame &) {}); - dispatcher.setOnAudioFrameCallback("alice", "mic-main", - [](const AudioFrame &) {}); + dispatcher.setOnAudioFrameCallback("alice", TrackSource::SOURCE_MICROPHONE, [](const AudioFrame&) {}); + dispatcher.setOnAudioFrameCallback("alice", "mic-main", [](const AudioFrame&) {}); ASSERT_EQ(audioCallbacks(dispatcher).size(), 2u); dispatcher.clearOnAudioFrameCallback("alice", "mic-main"); EXPECT_EQ(audioCallbacks(dispatcher).size(), 1u); - EXPECT_EQ( - audioCallbacks(dispatcher) - .count(CallbackKey{"alice", TrackSource::SOURCE_MICROPHONE, ""}), - 1u); + EXPECT_EQ(audioCallbacks(dispatcher).count(CallbackKey{"alice", TrackSource::SOURCE_MICROPHONE, ""}), 1u); } // ============================================================================ @@ -358,11 +291,9 @@ TEST_F(SubscriptionThreadDispatcherTest, NoActiveReadersInitially) { EXPECT_TRUE(activeReaders(dispatcher).empty()); } -TEST_F(SubscriptionThreadDispatcherTest, - ActiveReadersEmptyAfterCallbackRegistration) { +TEST_F(SubscriptionThreadDispatcherTest, ActiveReadersEmptyAfterCallbackRegistration) { SubscriptionThreadDispatcher dispatcher; - dispatcher.setOnAudioFrameCallback("alice", TrackSource::SOURCE_MICROPHONE, - [](const AudioFrame &) {}); + dispatcher.setOnAudioFrameCallback("alice", TrackSource::SOURCE_MICROPHONE, [](const AudioFrame&) {}); EXPECT_TRUE(activeReaders(dispatcher).empty()) << "Registering a callback without a subscribed track should not spawn " "readers"; @@ -372,25 +303,19 @@ TEST_F(SubscriptionThreadDispatcherTest, // Destruction safety // ============================================================================ -TEST_F(SubscriptionThreadDispatcherTest, - DestroyDispatcherWithRegisteredCallbacksIsSafe) { +TEST_F(SubscriptionThreadDispatcherTest, DestroyDispatcherWithRegisteredCallbacksIsSafe) { EXPECT_NO_THROW({ SubscriptionThreadDispatcher dispatcher; - dispatcher.setOnAudioFrameCallback("alice", TrackSource::SOURCE_MICROPHONE, - [](const AudioFrame &) {}); - dispatcher.setOnVideoFrameCallback("bob", TrackSource::SOURCE_CAMERA, - [](const VideoFrame &, std::int64_t) {}); + dispatcher.setOnAudioFrameCallback("alice", TrackSource::SOURCE_MICROPHONE, [](const AudioFrame&) {}); + dispatcher.setOnVideoFrameCallback("bob", TrackSource::SOURCE_CAMERA, [](const VideoFrame&, std::int64_t) {}); }); } -TEST_F(SubscriptionThreadDispatcherTest, - DestroyDispatcherAfterClearingCallbacksIsSafe) { +TEST_F(SubscriptionThreadDispatcherTest, DestroyDispatcherAfterClearingCallbacksIsSafe) { EXPECT_NO_THROW({ SubscriptionThreadDispatcher dispatcher; - dispatcher.setOnAudioFrameCallback("alice", TrackSource::SOURCE_MICROPHONE, - [](const AudioFrame &) {}); - dispatcher.clearOnAudioFrameCallback("alice", - TrackSource::SOURCE_MICROPHONE); + dispatcher.setOnAudioFrameCallback("alice", TrackSource::SOURCE_MICROPHONE, [](const AudioFrame&) {}); + dispatcher.clearOnAudioFrameCallback("alice", TrackSource::SOURCE_MICROPHONE); }); } @@ -410,24 +335,20 @@ TEST_F(SubscriptionThreadDispatcherTest, ConcurrentRegistrationDoesNotCrash) { threads.emplace_back([&dispatcher, t, kIterations]() { for (int i = 0; i < kIterations; ++i) { std::string id = "participant-" + std::to_string(t); - dispatcher.setOnAudioFrameCallback(id, TrackSource::SOURCE_MICROPHONE, - [](const AudioFrame &) {}); - dispatcher.clearOnAudioFrameCallback(id, - TrackSource::SOURCE_MICROPHONE); + dispatcher.setOnAudioFrameCallback(id, TrackSource::SOURCE_MICROPHONE, [](const AudioFrame&) {}); + dispatcher.clearOnAudioFrameCallback(id, TrackSource::SOURCE_MICROPHONE); } }); } - for (auto &thread : threads) { + for (auto& thread : threads) { thread.join(); } - EXPECT_TRUE(audioCallbacks(dispatcher).empty()) - << "All callbacks should be cleared after concurrent register/clear"; + EXPECT_TRUE(audioCallbacks(dispatcher).empty()) << "All callbacks should be cleared after concurrent register/clear"; } -TEST_F(SubscriptionThreadDispatcherTest, - ConcurrentMixedAudioVideoRegistration) { +TEST_F(SubscriptionThreadDispatcherTest, ConcurrentMixedAudioVideoRegistration) { SubscriptionThreadDispatcher dispatcher; constexpr int kThreads = 4; constexpr int kIterations = 50; @@ -438,16 +359,13 @@ TEST_F(SubscriptionThreadDispatcherTest, threads.emplace_back([&dispatcher, t, kIterations]() { std::string id = "p-" + std::to_string(t); for (int i = 0; i < kIterations; ++i) { - dispatcher.setOnAudioFrameCallback(id, TrackSource::SOURCE_MICROPHONE, - [](const AudioFrame &) {}); - dispatcher.setOnVideoFrameCallback( - id, TrackSource::SOURCE_CAMERA, - [](const VideoFrame &, std::int64_t) {}); + dispatcher.setOnAudioFrameCallback(id, TrackSource::SOURCE_MICROPHONE, [](const AudioFrame&) {}); + dispatcher.setOnVideoFrameCallback(id, TrackSource::SOURCE_CAMERA, [](const VideoFrame&, std::int64_t) {}); } }); } - for (auto &thread : threads) { + for (auto& thread : threads) { thread.join(); } @@ -464,16 +382,14 @@ TEST_F(SubscriptionThreadDispatcherTest, ManyDistinctCallbacksCanBeRegistered) { constexpr int kCount = 50; for (int i = 0; i < kCount; ++i) { - dispatcher.setOnAudioFrameCallback("participant-" + std::to_string(i), - TrackSource::SOURCE_MICROPHONE, - [](const AudioFrame &) {}); + dispatcher.setOnAudioFrameCallback("participant-" + std::to_string(i), TrackSource::SOURCE_MICROPHONE, + [](const AudioFrame&) {}); } EXPECT_EQ(audioCallbacks(dispatcher).size(), static_cast(kCount)); for (int i = 0; i < kCount; ++i) { - dispatcher.clearOnAudioFrameCallback("participant-" + std::to_string(i), - TrackSource::SOURCE_MICROPHONE); + dispatcher.clearOnAudioFrameCallback("participant-" + std::to_string(i), TrackSource::SOURCE_MICROPHONE); } EXPECT_EQ(audioCallbacks(dispatcher).size(), 0u); @@ -489,15 +405,13 @@ TEST_F(SubscriptionThreadDispatcherTest, DataCallbackKeyEqualKeysCompareEqual) { EXPECT_TRUE(a == b); } -TEST_F(SubscriptionThreadDispatcherTest, - DataCallbackKeyDifferentIdentityNotEqual) { +TEST_F(SubscriptionThreadDispatcherTest, DataCallbackKeyDifferentIdentityNotEqual) { DataCallbackKey a{"alice", "my-track"}; DataCallbackKey b{"bob", "my-track"}; EXPECT_FALSE(a == b); } -TEST_F(SubscriptionThreadDispatcherTest, - DataCallbackKeyDifferentTrackNameNotEqual) { +TEST_F(SubscriptionThreadDispatcherTest, DataCallbackKeyDifferentTrackNameNotEqual) { DataCallbackKey a{"alice", "track-a"}; DataCallbackKey b{"alice", "track-b"}; EXPECT_FALSE(a == b); @@ -507,16 +421,14 @@ TEST_F(SubscriptionThreadDispatcherTest, // DataCallbackKeyHash // ============================================================================ -TEST_F(SubscriptionThreadDispatcherTest, - DataCallbackKeyHashEqualKeysProduceSameHash) { +TEST_F(SubscriptionThreadDispatcherTest, DataCallbackKeyHashEqualKeysProduceSameHash) { DataCallbackKey a{"alice", "my-track"}; DataCallbackKey b{"alice", "my-track"}; DataCallbackKeyHash hasher; EXPECT_EQ(hasher(a), hasher(b)); } -TEST_F(SubscriptionThreadDispatcherTest, - DataCallbackKeyHashDifferentKeysLikelyDifferentHash) { +TEST_F(SubscriptionThreadDispatcherTest, DataCallbackKeyHashDifferentKeysLikelyDifferentHash) { DataCallbackKeyHash hasher; DataCallbackKey a{"alice", "track-a"}; DataCallbackKey b{"alice", "track-b"}; @@ -525,8 +437,7 @@ TEST_F(SubscriptionThreadDispatcherTest, EXPECT_NE(hasher(a), hasher(c)); } -TEST_F(SubscriptionThreadDispatcherTest, - DataCallbackKeyWorksAsUnorderedMapKey) { +TEST_F(SubscriptionThreadDispatcherTest, DataCallbackKeyWorksAsUnorderedMapKey) { std::unordered_map map; DataCallbackKey k1{"alice", "track-a"}; @@ -555,30 +466,25 @@ TEST_F(SubscriptionThreadDispatcherTest, // Data callback registration and clearing // ============================================================================ -TEST_F(SubscriptionThreadDispatcherTest, - AddDataFrameCallbackStoresRegistration) { +TEST_F(SubscriptionThreadDispatcherTest, AddDataFrameCallbackStoresRegistration) { SubscriptionThreadDispatcher dispatcher; - auto id = dispatcher.addOnDataFrameCallback( - "alice", "my-track", - [](const std::vector &, std::optional) {}); + auto id = dispatcher.addOnDataFrameCallback("alice", "my-track", + [](const std::vector&, std::optional) {}); EXPECT_EQ(id, 0u); EXPECT_EQ(dataCallbacks(dispatcher).size(), 1u); // Add a second one to confirm size and IDs are correct - auto id2 = dispatcher.addOnDataFrameCallback( - "alice", "my-track", - [](const std::vector &, std::optional) {}); + auto id2 = dispatcher.addOnDataFrameCallback("alice", "my-track", + [](const std::vector&, std::optional) {}); EXPECT_EQ(id2, 1u); EXPECT_EQ(dataCallbacks(dispatcher).size(), 2u); } -TEST_F(SubscriptionThreadDispatcherTest, - RemoveDataFrameCallbackRemovesRegistration) { +TEST_F(SubscriptionThreadDispatcherTest, RemoveDataFrameCallbackRemovesRegistration) { SubscriptionThreadDispatcher dispatcher; - auto id = dispatcher.addOnDataFrameCallback( - "alice", "my-track", - [](const std::vector &, std::optional) {}); + auto id = dispatcher.addOnDataFrameCallback("alice", "my-track", + [](const std::vector&, std::optional) {}); ASSERT_EQ(dataCallbacks(dispatcher).size(), 1u); dispatcher.removeOnDataFrameCallback(id); @@ -590,11 +496,9 @@ TEST_F(SubscriptionThreadDispatcherTest, RemoveNonExistentDataCallbackIsNoOp) { EXPECT_NO_THROW(dispatcher.removeOnDataFrameCallback(999)); } -TEST_F(SubscriptionThreadDispatcherTest, - MultipleDataCallbacksForSameKeyAreIndependent) { +TEST_F(SubscriptionThreadDispatcherTest, MultipleDataCallbacksForSameKeyAreIndependent) { SubscriptionThreadDispatcher dispatcher; - auto cb = [](const std::vector &, - std::optional) {}; + auto cb = [](const std::vector&, std::optional) {}; auto id1 = dispatcher.addOnDataFrameCallback("alice", "track", cb); auto id2 = dispatcher.addOnDataFrameCallback("alice", "track", cb); @@ -605,11 +509,9 @@ TEST_F(SubscriptionThreadDispatcherTest, EXPECT_EQ(dataCallbacks(dispatcher).size(), 1u); } -TEST_F(SubscriptionThreadDispatcherTest, - DataCallbackIdsAreMonotonicallyIncreasing) { +TEST_F(SubscriptionThreadDispatcherTest, DataCallbackIdsAreMonotonicallyIncreasing) { SubscriptionThreadDispatcher dispatcher; - auto cb = [](const std::vector &, - std::optional) {}; + auto cb = [](const std::vector&, std::optional) {}; auto id1 = dispatcher.addOnDataFrameCallback("alice", "t1", cb); auto id2 = dispatcher.addOnDataFrameCallback("bob", "t2", cb); auto id3 = dispatcher.addOnDataFrameCallback("carol", "t3", cb); @@ -627,12 +529,10 @@ TEST_F(SubscriptionThreadDispatcherTest, NoActiveDataReadersInitially) { EXPECT_TRUE(activeDataReaders(dispatcher).empty()); } -TEST_F(SubscriptionThreadDispatcherTest, - ActiveDataReadersEmptyAfterCallbackRegistration) { +TEST_F(SubscriptionThreadDispatcherTest, ActiveDataReadersEmptyAfterCallbackRegistration) { SubscriptionThreadDispatcher dispatcher; - dispatcher.addOnDataFrameCallback( - "alice", "my-track", - [](const std::vector &, std::optional) {}); + dispatcher.addOnDataFrameCallback("alice", "my-track", + [](const std::vector&, std::optional) {}); EXPECT_TRUE(activeDataReaders(dispatcher).empty()) << "Registering a callback without a published track should not spawn " "readers"; @@ -647,26 +547,21 @@ TEST_F(SubscriptionThreadDispatcherTest, NoRemoteDataTracksInitially) { // Data track destruction safety // ============================================================================ -TEST_F(SubscriptionThreadDispatcherTest, - DestroyDispatcherWithDataCallbacksIsSafe) { +TEST_F(SubscriptionThreadDispatcherTest, DestroyDispatcherWithDataCallbacksIsSafe) { EXPECT_NO_THROW({ SubscriptionThreadDispatcher dispatcher; - dispatcher.addOnDataFrameCallback( - "alice", "track-a", - [](const std::vector &, std::optional) {}); - dispatcher.addOnDataFrameCallback( - "bob", "track-b", - [](const std::vector &, std::optional) {}); + dispatcher.addOnDataFrameCallback("alice", "track-a", + [](const std::vector&, std::optional) {}); + dispatcher.addOnDataFrameCallback("bob", "track-b", + [](const std::vector&, std::optional) {}); }); } -TEST_F(SubscriptionThreadDispatcherTest, - DestroyDispatcherAfterRemovingDataCallbacksIsSafe) { +TEST_F(SubscriptionThreadDispatcherTest, DestroyDispatcherAfterRemovingDataCallbacksIsSafe) { EXPECT_NO_THROW({ SubscriptionThreadDispatcher dispatcher; - auto id = dispatcher.addOnDataFrameCallback( - "alice", "track-a", - [](const std::vector &, std::optional) {}); + auto id = dispatcher.addOnDataFrameCallback("alice", "track-a", + [](const std::vector&, std::optional) {}); dispatcher.removeOnDataFrameCallback(id); }); } @@ -675,16 +570,12 @@ TEST_F(SubscriptionThreadDispatcherTest, // Mixed audio/video/data registration // ============================================================================ -TEST_F(SubscriptionThreadDispatcherTest, - MixedAudioVideoDataCallbacksAreIndependent) { +TEST_F(SubscriptionThreadDispatcherTest, MixedAudioVideoDataCallbacksAreIndependent) { SubscriptionThreadDispatcher dispatcher; - dispatcher.setOnAudioFrameCallback("alice", TrackSource::SOURCE_MICROPHONE, - [](const AudioFrame &) {}); - dispatcher.setOnVideoFrameCallback("alice", TrackSource::SOURCE_CAMERA, - [](const VideoFrame &, std::int64_t) {}); - dispatcher.addOnDataFrameCallback( - "alice", "data-track", - [](const std::vector &, std::optional) {}); + dispatcher.setOnAudioFrameCallback("alice", TrackSource::SOURCE_MICROPHONE, [](const AudioFrame&) {}); + dispatcher.setOnVideoFrameCallback("alice", TrackSource::SOURCE_CAMERA, [](const VideoFrame&, std::int64_t) {}); + dispatcher.addOnDataFrameCallback("alice", "data-track", + [](const std::vector&, std::optional) {}); EXPECT_EQ(audioCallbacks(dispatcher).size(), 1u); EXPECT_EQ(videoCallbacks(dispatcher).size(), 1u); @@ -693,12 +584,10 @@ TEST_F(SubscriptionThreadDispatcherTest, TEST_F(SubscriptionThreadDispatcherTest, StopAllClearsDataCallbacksAndReaders) { SubscriptionThreadDispatcher dispatcher; - dispatcher.addOnDataFrameCallback( - "alice", "track-a", - [](const std::vector &, std::optional) {}); - dispatcher.addOnDataFrameCallback( - "bob", "track-b", - [](const std::vector &, std::optional) {}); + dispatcher.addOnDataFrameCallback("alice", "track-a", + [](const std::vector&, std::optional) {}); + dispatcher.addOnDataFrameCallback("bob", "track-b", + [](const std::vector&, std::optional) {}); dispatcher.stopAll(); @@ -711,8 +600,7 @@ TEST_F(SubscriptionThreadDispatcherTest, StopAllClearsDataCallbacksAndReaders) { // Concurrent data callback registration // ============================================================================ -TEST_F(SubscriptionThreadDispatcherTest, - ConcurrentDataCallbackRegistrationDoesNotCrash) { +TEST_F(SubscriptionThreadDispatcherTest, ConcurrentDataCallbackRegistrationDoesNotCrash) { SubscriptionThreadDispatcher dispatcher; constexpr int kThreads = 8; constexpr int kIterations = 100; @@ -723,22 +611,20 @@ TEST_F(SubscriptionThreadDispatcherTest, for (int t = 0; t < kThreads; ++t) { threads.emplace_back([&dispatcher, t, kIterations]() { for (int i = 0; i < kIterations; ++i) { - auto id = dispatcher.addOnDataFrameCallback( - "participant-" + std::to_string(t), "track", - [](const std::vector &, - std::optional) {}); + auto id = + dispatcher.addOnDataFrameCallback("participant-" + std::to_string(t), "track", + [](const std::vector&, std::optional) {}); dispatcher.removeOnDataFrameCallback(id); } }); } - for (auto &thread : threads) { + for (auto& thread : threads) { thread.join(); } - EXPECT_TRUE(dataCallbacks(dispatcher).empty()) - << "All data callbacks should be cleared after concurrent " - "register/remove"; + EXPECT_TRUE(dataCallbacks(dispatcher).empty()) << "All data callbacks should be cleared after concurrent " + "register/remove"; } } // namespace livekit diff --git a/src/tests/unit/test_tracing.cpp b/src/tests/unit/test_tracing.cpp index 50ab36c8..b4a7f84f 100644 --- a/src/tests/unit/test_tracing.cpp +++ b/src/tests/unit/test_tracing.cpp @@ -15,9 +15,6 @@ */ #include - -#include "benchmark_utils.h" -#include "event_tracer.h" #include #include @@ -28,6 +25,9 @@ #include #include +#include "benchmark_utils.h" +#include "event_tracer.h" + namespace livekit { namespace test { namespace { @@ -35,11 +35,9 @@ namespace { // Helper to get cross-platform temp directory std::string GetTempDir() { #ifdef _WIN32 - const char *temp = std::getenv("TEMP"); - if (!temp) - temp = std::getenv("TMP"); - if (!temp) - temp = "."; + const char* temp = std::getenv("TEMP"); + if (!temp) temp = std::getenv("TMP"); + if (!temp) temp = "."; return std::string(temp); #else return "/tmp"; @@ -47,23 +45,19 @@ std::string GetTempDir() { } // Helper to generate unique temp file paths -std::string GetTempTracePath(const std::string &test_name) { +std::string GetTempTracePath(const std::string& test_name) { auto now = std::chrono::steady_clock::now(); - auto ns = std::chrono::duration_cast( - now.time_since_epoch()) - .count(); + auto ns = std::chrono::duration_cast(now.time_since_epoch()).count(); std::string temp_dir = GetTempDir(); #ifdef _WIN32 - return temp_dir + "\\livekit_test_trace_" + test_name + "_" + - std::to_string(ns) + ".json"; + return temp_dir + "\\livekit_test_trace_" + test_name + "_" + std::to_string(ns) + ".json"; #else - return temp_dir + "/livekit_test_trace_" + test_name + "_" + - std::to_string(ns) + ".json"; + return temp_dir + "/livekit_test_trace_" + test_name + "_" + std::to_string(ns) + ".json"; #endif } // Helper to read file contents -std::string ReadFileContents(const std::string &path) { +std::string ReadFileContents(const std::string& path) { std::ifstream file(path); if (!file.is_open()) { return ""; @@ -74,7 +68,7 @@ std::string ReadFileContents(const std::string &path) { } // Helper to cleanup trace files -void CleanupTraceFile(const std::string &path) { std::remove(path.c_str()); } +void CleanupTraceFile(const std::string& path) { std::remove(path.c_str()); } // ============================================================================= // Basic Lifecycle Tests @@ -143,8 +137,7 @@ TEST(TracingTest, EventsAreWrittenToFile) { startTracing(trace_path); // Add some trace events using the internal API - const unsigned char *category = - trace::EventTracer::GetCategoryEnabled("test.category"); + const unsigned char* category = trace::EventTracer::GetCategoryEnabled("test.category"); EXPECT_TRUE(*category != 0); // Category should be enabled // Add a begin/end event pair @@ -189,8 +182,7 @@ TEST(TracingTest, AsyncEventsAreWrittenCorrectly) { startTracing(trace_path); - const unsigned char *category = - trace::EventTracer::GetCategoryEnabled("test.async"); + const unsigned char* category = trace::EventTracer::GetCategoryEnabled("test.async"); // Add async start event trace::EventTracer::AddTraceEvent('S', // phase: async start @@ -224,11 +216,10 @@ TEST(TracingTest, EventsWithArguments) { startTracing(trace_path); - const unsigned char *category = - trace::EventTracer::GetCategoryEnabled("test.args"); + const unsigned char* category = trace::EventTracer::GetCategoryEnabled("test.args"); // Add event with integer argument - const char *arg_names[] = {"count"}; + const char* arg_names[] = {"count"}; unsigned char arg_types[] = {2}; // TRACE_VALUE_TYPE_UINT = 2 unsigned long long arg_values[] = {42}; @@ -256,10 +247,8 @@ TEST(TracingTest, CategoryFilteringEnablesSpecificCategory) { // Start with specific category enabled startTracing(trace_path, {"enabled.category"}); - const unsigned char *enabled_cat = - trace::EventTracer::GetCategoryEnabled("enabled.category"); - const unsigned char *disabled_cat = - trace::EventTracer::GetCategoryEnabled("disabled.category"); + const unsigned char* enabled_cat = trace::EventTracer::GetCategoryEnabled("enabled.category"); + const unsigned char* disabled_cat = trace::EventTracer::GetCategoryEnabled("disabled.category"); EXPECT_TRUE(*enabled_cat != 0); EXPECT_TRUE(*disabled_cat == 0); @@ -274,12 +263,9 @@ TEST(TracingTest, CategoryWildcardMatching) { // Start with wildcard category startTracing(trace_path, {"livekit.*"}); - const unsigned char *match1 = - trace::EventTracer::GetCategoryEnabled("livekit.connect"); - const unsigned char *match2 = - trace::EventTracer::GetCategoryEnabled("livekit.rpc"); - const unsigned char *no_match = - trace::EventTracer::GetCategoryEnabled("other.category"); + const unsigned char* match1 = trace::EventTracer::GetCategoryEnabled("livekit.connect"); + const unsigned char* match2 = trace::EventTracer::GetCategoryEnabled("livekit.rpc"); + const unsigned char* no_match = trace::EventTracer::GetCategoryEnabled("other.category"); EXPECT_TRUE(*match1 != 0); EXPECT_TRUE(*match2 != 0); @@ -295,10 +281,8 @@ TEST(TracingTest, EmptyCategoriesEnablesAll) { // Start with empty categories (all enabled) startTracing(trace_path, {}); - const unsigned char *cat1 = - trace::EventTracer::GetCategoryEnabled("any.category"); - const unsigned char *cat2 = - trace::EventTracer::GetCategoryEnabled("another.category"); + const unsigned char* cat1 = trace::EventTracer::GetCategoryEnabled("any.category"); + const unsigned char* cat2 = trace::EventTracer::GetCategoryEnabled("another.category"); EXPECT_TRUE(*cat1 != 0); EXPECT_TRUE(*cat2 != 0); @@ -316,16 +300,13 @@ TEST(TracingTest, TraceFileCanBeParsed) { startTracing(trace_path); - const unsigned char *category = - trace::EventTracer::GetCategoryEnabled("test"); + const unsigned char* category = trace::EventTracer::GetCategoryEnabled("test"); // Add multiple events for (int i = 0; i < 5; ++i) { - trace::EventTracer::AddTraceEvent('B', category, "parse_event", 0, 0, - nullptr, nullptr, nullptr, 0); + trace::EventTracer::AddTraceEvent('B', category, "parse_event", 0, 0, nullptr, nullptr, nullptr, 0); std::this_thread::sleep_for(std::chrono::milliseconds(1)); - trace::EventTracer::AddTraceEvent('E', category, "parse_event", 0, 0, - nullptr, nullptr, nullptr, 0); + trace::EventTracer::AddTraceEvent('E', category, "parse_event", 0, 0, nullptr, nullptr, nullptr, 0); } stopTracing(); @@ -337,12 +318,10 @@ TEST(TracingTest, TraceFileCanBeParsed) { // Count begin/end events for our test event int begin_count = 0; int end_count = 0; - for (const auto &event : events) { + for (const auto& event : events) { if (event.name == "parse_event") { - if (event.phase == 'B') - begin_count++; - if (event.phase == 'E') - end_count++; + if (event.phase == 'B') begin_count++; + if (event.phase == 'E') end_count++; } } @@ -357,16 +336,13 @@ TEST(TracingTest, DurationCalculationWorks) { startTracing(trace_path); - const unsigned char *category = - trace::EventTracer::GetCategoryEnabled("test"); + const unsigned char* category = trace::EventTracer::GetCategoryEnabled("test"); // Add events with known delay for (int i = 0; i < 3; ++i) { - trace::EventTracer::AddTraceEvent('B', category, "timed_event", 0, 0, - nullptr, nullptr, nullptr, 0); + trace::EventTracer::AddTraceEvent('B', category, "timed_event", 0, 0, nullptr, nullptr, nullptr, 0); std::this_thread::sleep_for(std::chrono::milliseconds(10)); - trace::EventTracer::AddTraceEvent('E', category, "timed_event", 0, 0, - nullptr, nullptr, nullptr, 0); + trace::EventTracer::AddTraceEvent('E', category, "timed_event", 0, 0, nullptr, nullptr, nullptr, 0); } stopTracing(); @@ -390,24 +366,19 @@ TEST(TracingTest, AsyncDurationCalculationWorks) { startTracing(trace_path); - const unsigned char *category = - trace::EventTracer::GetCategoryEnabled("test"); + const unsigned char* category = trace::EventTracer::GetCategoryEnabled("test"); // Add async events with known IDs - trace::EventTracer::AddTraceEvent('S', category, "async_op", 100, 0, nullptr, - nullptr, nullptr, 0); - trace::EventTracer::AddTraceEvent('S', category, "async_op", 200, 0, nullptr, - nullptr, nullptr, 0); + trace::EventTracer::AddTraceEvent('S', category, "async_op", 100, 0, nullptr, nullptr, nullptr, 0); + trace::EventTracer::AddTraceEvent('S', category, "async_op", 200, 0, nullptr, nullptr, nullptr, 0); std::this_thread::sleep_for(std::chrono::milliseconds(15)); - trace::EventTracer::AddTraceEvent('F', category, "async_op", 100, 0, nullptr, - nullptr, nullptr, 0); + trace::EventTracer::AddTraceEvent('F', category, "async_op", 100, 0, nullptr, nullptr, nullptr, 0); std::this_thread::sleep_for(std::chrono::milliseconds(10)); - trace::EventTracer::AddTraceEvent('F', category, "async_op", 200, 0, nullptr, - nullptr, nullptr, 0); + trace::EventTracer::AddTraceEvent('F', category, "async_op", 200, 0, nullptr, nullptr, nullptr, 0); stopTracing(); @@ -424,16 +395,13 @@ TEST(TracingTest, StatisticsCalculation) { startTracing(trace_path); - const unsigned char *category = - trace::EventTracer::GetCategoryEnabled("test"); + const unsigned char* category = trace::EventTracer::GetCategoryEnabled("test"); // Add 10 events for (int i = 0; i < 10; ++i) { - trace::EventTracer::AddTraceEvent('B', category, "stats_event", 0, 0, - nullptr, nullptr, nullptr, 0); + trace::EventTracer::AddTraceEvent('B', category, "stats_event", 0, 0, nullptr, nullptr, nullptr, 0); std::this_thread::sleep_for(std::chrono::milliseconds(5)); - trace::EventTracer::AddTraceEvent('E', category, "stats_event", 0, 0, - nullptr, nullptr, nullptr, 0); + trace::EventTracer::AddTraceEvent('E', category, "stats_event", 0, 0, nullptr, nullptr, nullptr, 0); } stopTracing(); @@ -460,20 +428,16 @@ TEST(TracingTest, MultipleTracingSessions) { // First session startTracing(trace_path1); - const unsigned char *cat = trace::EventTracer::GetCategoryEnabled("test"); - trace::EventTracer::AddTraceEvent('B', cat, "session1_event", 0, 0, nullptr, - nullptr, nullptr, 0); - trace::EventTracer::AddTraceEvent('E', cat, "session1_event", 0, 0, nullptr, - nullptr, nullptr, 0); + const unsigned char* cat = trace::EventTracer::GetCategoryEnabled("test"); + trace::EventTracer::AddTraceEvent('B', cat, "session1_event", 0, 0, nullptr, nullptr, nullptr, 0); + trace::EventTracer::AddTraceEvent('E', cat, "session1_event", 0, 0, nullptr, nullptr, nullptr, 0); stopTracing(); // Second session startTracing(trace_path2); cat = trace::EventTracer::GetCategoryEnabled("test"); - trace::EventTracer::AddTraceEvent('B', cat, "session2_event", 0, 0, nullptr, - nullptr, nullptr, 0); - trace::EventTracer::AddTraceEvent('E', cat, "session2_event", 0, 0, nullptr, - nullptr, nullptr, 0); + trace::EventTracer::AddTraceEvent('B', cat, "session2_event", 0, 0, nullptr, nullptr, nullptr, 0); + trace::EventTracer::AddTraceEvent('E', cat, "session2_event", 0, 0, nullptr, nullptr, nullptr, 0); stopTracing(); // Verify first file contains session1 event only @@ -505,20 +469,17 @@ TEST(TracingTest, ConcurrentEventWriting) { for (int t = 0; t < num_threads; ++t) { threads.emplace_back([t, events_per_thread]() { - const unsigned char *cat = - trace::EventTracer::GetCategoryEnabled("test.concurrent"); + const unsigned char* cat = trace::EventTracer::GetCategoryEnabled("test.concurrent"); std::string event_name = "thread_" + std::to_string(t) + "_event"; for (int i = 0; i < events_per_thread; ++i) { - trace::EventTracer::AddTraceEvent('B', cat, event_name.c_str(), 0, 0, - nullptr, nullptr, nullptr, 0); - trace::EventTracer::AddTraceEvent('E', cat, event_name.c_str(), 0, 0, - nullptr, nullptr, nullptr, 0); + trace::EventTracer::AddTraceEvent('B', cat, event_name.c_str(), 0, 0, nullptr, nullptr, nullptr, 0); + trace::EventTracer::AddTraceEvent('E', cat, event_name.c_str(), 0, 0, nullptr, nullptr, nullptr, 0); } }); } - for (auto &t : threads) { + for (auto& t : threads) { t.join(); } @@ -530,11 +491,9 @@ TEST(TracingTest, ConcurrentEventWriting) { // Each thread should have written events_per_thread * 2 events (begin + end) int total_begin = 0; int total_end = 0; - for (const auto &event : events) { - if (event.phase == 'B') - total_begin++; - if (event.phase == 'E') - total_end++; + for (const auto& event : events) { + if (event.phase == 'B') total_begin++; + if (event.phase == 'E') total_end++; } // Should have all events written @@ -554,14 +513,12 @@ TEST(TracingTest, EventsIgnoredWhenTracingDisabled) { EXPECT_FALSE(isTracingEnabled()); // Get category - should return disabled - const unsigned char *cat = trace::EventTracer::GetCategoryEnabled("test"); + const unsigned char* cat = trace::EventTracer::GetCategoryEnabled("test"); EXPECT_EQ(*cat, 0); // Adding events should be safe (no crash) - trace::EventTracer::AddTraceEvent('B', cat, "ignored_event", 0, 0, nullptr, - nullptr, nullptr, 0); - trace::EventTracer::AddTraceEvent('E', cat, "ignored_event", 0, 0, nullptr, - nullptr, nullptr, 0); + trace::EventTracer::AddTraceEvent('B', cat, "ignored_event", 0, 0, nullptr, nullptr, nullptr, 0); + trace::EventTracer::AddTraceEvent('E', cat, "ignored_event", 0, 0, nullptr, nullptr, nullptr, 0); // Still disabled EXPECT_FALSE(isTracingEnabled()); diff --git a/src/tests/unit/test_track_publication.cpp b/src/tests/unit/test_track_publication.cpp index a0e74bf3..467f6ad2 100644 --- a/src/tests/unit/test_track_publication.cpp +++ b/src/tests/unit/test_track_publication.cpp @@ -14,10 +14,10 @@ * limitations under the License. */ -#include - #include +#include + #include "livekit/local_track_publication.h" #include "livekit/remote_track_publication.h" #include "livekit/track.h" @@ -33,23 +33,19 @@ namespace { class FakeTrack : public Track { public: FakeTrack() - : Track(FfiHandle(), "fake-sid", "fake-name", TrackKind::KIND_AUDIO, - StreamState::STATE_ACTIVE, + : Track(FfiHandle(), "fake-sid", "fake-name", TrackKind::KIND_AUDIO, StreamState::STATE_ACTIVE, /*muted=*/false, /*remote=*/false) {} }; // Default-construct the proto: the publication ctor only reads via getters, // which return well-defined defaults for unset fields. Using a zero handle // keeps the `FfiHandle` a no-op on destruction. -proto::OwnedTrackPublication makeOwnedPub() { - return proto::OwnedTrackPublication{}; -} +proto::OwnedTrackPublication makeOwnedPub() { return proto::OwnedTrackPublication{}; } template class TrackPublicationTest : public ::testing::Test {}; -using PublicationTypes = - ::testing::Types; +using PublicationTypes = ::testing::Types; TYPED_TEST_SUITE(TrackPublicationTest, PublicationTypes); } // namespace diff --git a/src/tests/unit/test_video_frame_metadata.cpp b/src/tests/unit/test_video_frame_metadata.cpp index 79afbc49..7f08ff6b 100644 --- a/src/tests/unit/test_video_frame_metadata.cpp +++ b/src/tests/unit/test_video_frame_metadata.cpp @@ -14,11 +14,11 @@ * limitations under the License. */ +#include + #include "room_proto_converter.h" #include "video_utils.h" -#include - namespace livekit::test { TEST(VideoFrameMetadataTest, EmptyMetadataIsOmittedFromProto) { @@ -86,10 +86,8 @@ TEST(TrackPublishOptionsTest, PacketTrailerFeaturesRoundTrip) { proto::TrackPublishOptions proto_options = toProto(options); ASSERT_EQ(proto_options.packet_trailer_features_size(), 2); - EXPECT_EQ(proto_options.packet_trailer_features(0), - proto::PacketTrailerFeature::PTF_USER_TIMESTAMP); - EXPECT_EQ(proto_options.packet_trailer_features(1), - proto::PacketTrailerFeature::PTF_FRAME_ID); + EXPECT_EQ(proto_options.packet_trailer_features(0), proto::PacketTrailerFeature::PTF_USER_TIMESTAMP); + EXPECT_EQ(proto_options.packet_trailer_features(1), proto::PacketTrailerFeature::PTF_FRAME_ID); TrackPublishOptions round_trip = fromProto(proto_options); EXPECT_TRUE(round_trip.packet_trailer_features.user_timestamp); diff --git a/src/trace/event_tracer.cpp b/src/trace/event_tracer.cpp index ec57de33..14522a24 100644 --- a/src/trace/event_tracer.cpp +++ b/src/trace/event_tracer.cpp @@ -15,7 +15,6 @@ */ #include "event_tracer.h" -#include "event_tracer_internal.h" #include #include @@ -31,6 +30,8 @@ #include #include +#include "event_tracer_internal.h" + #ifdef _WIN32 #include #else @@ -68,8 +69,7 @@ uint32_t GetCurrentThreadId() { uint64_t GetTimestampMicros() { auto now = std::chrono::steady_clock::now(); auto duration = now.time_since_epoch(); - return std::chrono::duration_cast(duration) - .count(); + return std::chrono::duration_cast(duration).count(); } // Internal trace event structure @@ -90,46 +90,44 @@ struct TraceEventData { }; // Escape a string for JSON -std::string JsonEscape(const std::string &s) { +std::string JsonEscape(const std::string& s) { std::ostringstream oss; for (const char c : s) { switch (c) { - case '"': - oss << "\\\""; - break; - case '\\': - oss << "\\\\"; - break; - case '\b': - oss << "\\b"; - break; - case '\f': - oss << "\\f"; - break; - case '\n': - oss << "\\n"; - break; - case '\r': - oss << "\\r"; - break; - case '\t': - oss << "\\t"; - break; - default: - if ('\x00' <= c && c <= '\x1f') { - oss << "\\u" << std::hex << std::setw(4) << std::setfill('0') - << static_cast(c); - } else { - oss << c; - } + case '"': + oss << "\\\""; + break; + case '\\': + oss << "\\\\"; + break; + case '\b': + oss << "\\b"; + break; + case '\f': + oss << "\\f"; + break; + case '\n': + oss << "\\n"; + break; + case '\r': + oss << "\\r"; + break; + case '\t': + oss << "\\t"; + break; + default: + if ('\x00' <= c && c <= '\x1f') { + oss << "\\u" << std::hex << std::setw(4) << std::setfill('0') << static_cast(c); + } else { + oss << c; + } } } return oss.str(); } // Convert argument value to JSON based on type -std::string ArgValueToJson(unsigned char type, uint64_t value, - const std::string &string_value) { +std::string ArgValueToJson(unsigned char type, uint64_t value, const std::string& string_value) { constexpr unsigned char TRACE_VALUE_TYPE_BOOL = 1; constexpr unsigned char TRACE_VALUE_TYPE_UINT = 2; constexpr unsigned char TRACE_VALUE_TYPE_INT = 3; @@ -140,40 +138,40 @@ std::string ArgValueToJson(unsigned char type, uint64_t value, std::ostringstream oss; switch (type) { - case TRACE_VALUE_TYPE_BOOL: - oss << (value ? "true" : "false"); - break; - case TRACE_VALUE_TYPE_UINT: - oss << value; - break; - case TRACE_VALUE_TYPE_INT: - oss << static_cast(value); - break; - case TRACE_VALUE_TYPE_DOUBLE: { - union { - uint64_t u; - double d; - } converter; - converter.u = value; - oss << std::setprecision(17) << converter.d; - break; - } - case TRACE_VALUE_TYPE_POINTER: - oss << "\"0x" << std::hex << value << "\""; - break; - case TRACE_VALUE_TYPE_STRING: - case TRACE_VALUE_TYPE_COPY_STRING: - oss << "\"" << JsonEscape(string_value) << "\""; - break; - default: - oss << value; - break; + case TRACE_VALUE_TYPE_BOOL: + oss << (value ? "true" : "false"); + break; + case TRACE_VALUE_TYPE_UINT: + oss << value; + break; + case TRACE_VALUE_TYPE_INT: + oss << static_cast(value); + break; + case TRACE_VALUE_TYPE_DOUBLE: { + union { + uint64_t u; + double d; + } converter; + converter.u = value; + oss << std::setprecision(17) << converter.d; + break; + } + case TRACE_VALUE_TYPE_POINTER: + oss << "\"0x" << std::hex << value << "\""; + break; + case TRACE_VALUE_TYPE_STRING: + case TRACE_VALUE_TYPE_COPY_STRING: + oss << "\"" << JsonEscape(string_value) << "\""; + break; + default: + oss << value; + break; } return oss.str(); } // Format a single event as JSON -std::string FormatEventJson(const TraceEventData &event, uint64_t start_time) { +std::string FormatEventJson(const TraceEventData& event, uint64_t start_time) { std::ostringstream oss; oss << "{"; oss << R"("ph":")" << event.phase << "\","; @@ -190,11 +188,11 @@ std::string FormatEventJson(const TraceEventData &event, uint64_t start_time) { if (event.num_args > 0) { oss << ",\"args\":{"; for (int i = 0; i < event.num_args && i < 2; ++i) { - if (i > 0) + if (i > 0) { oss << ","; + } oss << "\"" << JsonEscape(event.arg_names[i]) << "\":"; - oss << ArgValueToJson(event.arg_types[i], event.arg_values[i], - event.arg_string_values[i]); + oss << ArgValueToJson(event.arg_types[i], event.arg_values[i], event.arg_string_values[i]); } oss << "}"; } @@ -234,9 +232,7 @@ void WriterThreadFunc() { // Wait for events or shutdown { std::unique_lock lock(g_mutex); - g_cv.wait(lock, [] { - return !g_event_queue.empty() || g_shutdown_requested.load(); - }); + g_cv.wait(lock, [] { return !g_event_queue.empty() || g_shutdown_requested.load(); }); // Drain the queue into a local batch while (!g_event_queue.empty()) { @@ -246,7 +242,7 @@ void WriterThreadFunc() { } // Write batch to file (outside the lock) - for (const auto &event : batch) { + for (const auto& event : batch) { if (g_trace_file.is_open()) { if (!g_first_event) { g_trace_file << ","; @@ -269,14 +265,13 @@ void WriterThreadFunc() { } // namespace -void SetupEventTracer(GetCategoryEnabledPtr get_category_enabled_ptr, - AddTraceEventPtr add_trace_event_ptr) { +void SetupEventTracer(GetCategoryEnabledPtr get_category_enabled_ptr, AddTraceEventPtr add_trace_event_ptr) { const std::scoped_lock lock(g_mutex); g_custom_get_category_enabled = get_category_enabled_ptr; g_custom_add_trace_event = add_trace_event_ptr; } -const unsigned char *EventTracer::GetCategoryEnabled(const char *name) { +const unsigned char* EventTracer::GetCategoryEnabled(const char* name) { // If custom tracer is set, use it if (g_custom_get_category_enabled) { return g_custom_get_category_enabled(name); @@ -302,7 +297,7 @@ const unsigned char *EventTracer::GetCategoryEnabled(const char *name) { } // Check for wildcard matches (e.g., "livekit.*" matches "livekit.connect") - for (const auto &pattern : g_enabled_categories) { + for (const auto& pattern : g_enabled_categories) { if (pattern.back() == '*') { const std::string prefix = pattern.substr(0, pattern.size() - 1); if (category_name.compare(0, prefix.size(), prefix) == 0) { @@ -314,17 +309,13 @@ const unsigned char *EventTracer::GetCategoryEnabled(const char *name) { return &g_disabled_byte; } -void EventTracer::AddTraceEvent(char phase, - const unsigned char *category_enabled, - const char *name, unsigned long long id, - int num_args, const char **arg_names, - const unsigned char *arg_types, - const unsigned long long *arg_values, +void EventTracer::AddTraceEvent(char phase, const unsigned char* category_enabled, const char* name, + unsigned long long id, int num_args, const char** arg_names, + const unsigned char* arg_types, const unsigned long long* arg_values, unsigned char flags) { // If custom tracer is set, use it if (g_custom_add_trace_event) { - g_custom_add_trace_event(phase, category_enabled, name, id, num_args, - arg_names, arg_types, arg_values, flags); + g_custom_add_trace_event(phase, category_enabled, name, id, num_args, arg_names, arg_types, arg_values, flags); return; } @@ -357,8 +348,8 @@ void EventTracer::AddTraceEvent(char phase, // Handle string arguments if (arg_types && (arg_types[i] == 6 || arg_types[i] == 7)) { - const char *str_val = - reinterpret_cast(arg_values[i]); // NOLINT(performance-no-int-to-ptr) + // NOLINTNEXTLINE(performance-no-int-to-ptr) + const char* str_val = reinterpret_cast(arg_values[i]); if (str_val) { event.arg_string_values[i] = str_val; } @@ -376,8 +367,7 @@ void EventTracer::AddTraceEvent(char phase, namespace internal { -bool StartTracing(const std::string &file_path, - const std::vector &categories) { +bool StartTracing(const std::string& file_path, const std::vector& categories) { const std::scoped_lock lock(g_mutex); // Don't start if already running @@ -400,7 +390,7 @@ bool StartTracing(const std::string &file_path, // Set enabled categories g_enabled_categories.clear(); - for (const auto &cat : categories) { + for (const auto& cat : categories) { g_enabled_categories.insert(cat); } @@ -438,9 +428,7 @@ void StopTracing() { g_enabled_categories.clear(); } -bool IsTracingEnabled() { - return g_tracing_enabled.load(std::memory_order_acquire); -} +bool IsTracingEnabled() { return g_tracing_enabled.load(std::memory_order_acquire); } } // namespace internal diff --git a/src/trace/event_tracer.h b/src/trace/event_tracer.h index 181abf22..2bb93e82 100644 --- a/src/trace/event_tracer.h +++ b/src/trace/event_tracer.h @@ -53,35 +53,28 @@ namespace livekit { namespace trace { -typedef const unsigned char *(*GetCategoryEnabledPtr)(const char *name); -typedef void (*AddTraceEventPtr)(char phase, - const unsigned char *category_enabled, - const char *name, unsigned long long id, - int num_args, const char **arg_names, - const unsigned char *arg_types, - const unsigned long long *arg_values, +typedef const unsigned char* (*GetCategoryEnabledPtr)(const char* name); +typedef void (*AddTraceEventPtr)(char phase, const unsigned char* category_enabled, const char* name, + unsigned long long id, int num_args, const char** arg_names, + const unsigned char* arg_types, const unsigned long long* arg_values, unsigned char flags); // User of LiveKit SDK can call this method to setup custom event tracing. // // This method must be called before any tracing begins. Functions // provided should be thread-safe. -LIVEKIT_EXPORT void -SetupEventTracer(GetCategoryEnabledPtr get_category_enabled_ptr, - AddTraceEventPtr add_trace_event_ptr); +LIVEKIT_EXPORT void SetupEventTracer(GetCategoryEnabledPtr get_category_enabled_ptr, + AddTraceEventPtr add_trace_event_ptr); // This class defines interface for the event tracing system to call // internally. Do not call these methods directly. class EventTracer { public: - static const unsigned char *GetCategoryEnabled(const char *name); + static const unsigned char* GetCategoryEnabled(const char* name); - static void AddTraceEvent(char phase, const unsigned char *category_enabled, - const char *name, unsigned long long id, - int num_args, const char **arg_names, - const unsigned char *arg_types, - const unsigned long long *arg_values, - unsigned char flags); + static void AddTraceEvent(char phase, const unsigned char* category_enabled, const char* name, unsigned long long id, + int num_args, const char** arg_names, const unsigned char* arg_types, + const unsigned long long* arg_values, unsigned char flags); }; } // namespace trace diff --git a/src/trace/event_tracer_internal.h b/src/trace/event_tracer_internal.h index e01d76b3..1eff80b6 100644 --- a/src/trace/event_tracer_internal.h +++ b/src/trace/event_tracer_internal.h @@ -34,8 +34,7 @@ namespace livekit::trace::internal { * @return true if tracing started successfully, false if already running or * file error */ -bool StartTracing(const std::string &file_path, - const std::vector &categories); +bool StartTracing(const std::string& file_path, const std::vector& categories); /** * Stop tracing and flush all pending events. diff --git a/src/trace/trace_event.h b/src/trace/trace_event.h index 6944ba16..bc1bb57d 100644 --- a/src/trace/trace_event.h +++ b/src/trace/trace_event.h @@ -24,9 +24,10 @@ #ifndef LIVEKIT_TRACE_TRACE_EVENT_H_ #define LIVEKIT_TRACE_TRACE_EVENT_H_ -#include "event_tracer.h" #include #include + +#include "event_tracer.h" #if defined(TRACE_EVENT0) #error "Another copy of trace_event.h has already been included." #endif @@ -156,50 +157,37 @@ // for details. // By default, const char* argument values are assumed to have long-lived scope // and will not be copied. Use this macro to force a const char* to be copied. -#define TRACE_STR_COPY(str) \ - webrtc::trace_event_internal::TraceStringWithCopy(str) +#define TRACE_STR_COPY(str) webrtc::trace_event_internal::TraceStringWithCopy(str) // By default, uint64 ID argument values are not mangled with the Process ID in // TRACE_EVENT_ASYNC macros. Use this macro to force Process ID mangling. -#define TRACE_ID_MANGLE(id) \ - webrtc::trace_event_internal::TraceID::ForceMangle(id) +#define TRACE_ID_MANGLE(id) webrtc::trace_event_internal::TraceID::ForceMangle(id) // Records a pair of begin and end events called "name" for the current // scope, with 0, 1 or 2 associated arguments. If the category is not // enabled, then this does nothing. // - category and name strings must have application lifetime (statics or // literals). They may not include " chars. -#define TRACE_EVENT0(category, name) \ - INTERNAL_TRACE_EVENT_ADD_SCOPED(category, name) -#define TRACE_EVENT1(category, name, arg1_name, arg1_val) \ +#define TRACE_EVENT0(category, name) INTERNAL_TRACE_EVENT_ADD_SCOPED(category, name) +#define TRACE_EVENT1(category, name, arg1_name, arg1_val) \ INTERNAL_TRACE_EVENT_ADD_SCOPED(category, name, arg1_name, arg1_val) #define TRACE_EVENT2(category, name, arg1_name, arg1_val, arg2_name, arg2_val) \ - INTERNAL_TRACE_EVENT_ADD_SCOPED(category, name, arg1_name, arg1_val, \ - arg2_name, arg2_val) + INTERNAL_TRACE_EVENT_ADD_SCOPED(category, name, arg1_name, arg1_val, arg2_name, arg2_val) // Same as TRACE_EVENT except that they are not included in official builds. #ifdef OFFICIAL_BUILD #define UNSHIPPED_TRACE_EVENT0(category, name) (void)0 #define UNSHIPPED_TRACE_EVENT1(category, name, arg1_name, arg1_val) (void)0 -#define UNSHIPPED_TRACE_EVENT2(category, name, arg1_name, arg1_val, arg2_name, \ - arg2_val) \ - (void)0 +#define UNSHIPPED_TRACE_EVENT2(category, name, arg1_name, arg1_val, arg2_name, arg2_val) (void)0 #define UNSHIPPED_TRACE_EVENT_INSTANT0(category, name) (void)0 -#define UNSHIPPED_TRACE_EVENT_INSTANT1(category, name, arg1_name, arg1_val) \ - (void)0 -#define UNSHIPPED_TRACE_EVENT_INSTANT2(category, name, arg1_name, arg1_val, \ - arg2_name, arg2_val) \ - (void)0 +#define UNSHIPPED_TRACE_EVENT_INSTANT1(category, name, arg1_name, arg1_val) (void)0 +#define UNSHIPPED_TRACE_EVENT_INSTANT2(category, name, arg1_name, arg1_val, arg2_name, arg2_val) (void)0 #else #define UNSHIPPED_TRACE_EVENT0(category, name) TRACE_EVENT0(category, name) -#define UNSHIPPED_TRACE_EVENT1(category, name, arg1_name, arg1_val) \ - TRACE_EVENT1(category, name, arg1_name, arg1_val) -#define UNSHIPPED_TRACE_EVENT2(category, name, arg1_name, arg1_val, arg2_name, \ - arg2_val) \ +#define UNSHIPPED_TRACE_EVENT1(category, name, arg1_name, arg1_val) TRACE_EVENT1(category, name, arg1_name, arg1_val) +#define UNSHIPPED_TRACE_EVENT2(category, name, arg1_name, arg1_val, arg2_name, arg2_val) \ TRACE_EVENT2(category, name, arg1_name, arg1_val, arg2_name, arg2_val) -#define UNSHIPPED_TRACE_EVENT_INSTANT0(category, name) \ - TRACE_EVENT_INSTANT0(category, name) -#define UNSHIPPED_TRACE_EVENT_INSTANT1(category, name, arg1_name, arg1_val) \ +#define UNSHIPPED_TRACE_EVENT_INSTANT0(category, name) TRACE_EVENT_INSTANT0(category, name) +#define UNSHIPPED_TRACE_EVENT_INSTANT1(category, name, arg1_name, arg1_val) \ TRACE_EVENT_INSTANT1(category, name, arg1_name, arg1_val) -#define UNSHIPPED_TRACE_EVENT_INSTANT2(category, name, arg1_name, arg1_val, \ - arg2_name, arg2_val) \ +#define UNSHIPPED_TRACE_EVENT_INSTANT2(category, name, arg1_name, arg1_val, arg2_name, arg2_val) \ TRACE_EVENT_INSTANT2(category, name, arg1_name, arg1_val, arg2_name, arg2_val) #endif // Records a single event called "name" immediately, with 0, 1 or 2 @@ -207,110 +195,78 @@ // does nothing. // - category and name strings must have application lifetime (statics or // literals). They may not include " chars. -#define TRACE_EVENT_INSTANT0(category, name) \ - INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, category, name, \ - TRACE_EVENT_FLAG_NONE) -#define TRACE_EVENT_INSTANT1(category, name, arg1_name, arg1_val) \ - INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, category, name, \ - TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val) -#define TRACE_EVENT_INSTANT2(category, name, arg1_name, arg1_val, arg2_name, \ - arg2_val) \ - INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, category, name, \ - TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val, \ +#define TRACE_EVENT_INSTANT0(category, name) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, category, name, TRACE_EVENT_FLAG_NONE) +#define TRACE_EVENT_INSTANT1(category, name, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, category, name, TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val) +#define TRACE_EVENT_INSTANT2(category, name, arg1_name, arg1_val, arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, category, name, TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val, \ arg2_name, arg2_val) -#define TRACE_EVENT_COPY_INSTANT0(category, name) \ - INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, category, name, \ - TRACE_EVENT_FLAG_COPY) -#define TRACE_EVENT_COPY_INSTANT1(category, name, arg1_name, arg1_val) \ - INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, category, name, \ - TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val) -#define TRACE_EVENT_COPY_INSTANT2(category, name, arg1_name, arg1_val, \ - arg2_name, arg2_val) \ - INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, category, name, \ - TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val, \ +#define TRACE_EVENT_COPY_INSTANT0(category, name) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, category, name, TRACE_EVENT_FLAG_COPY) +#define TRACE_EVENT_COPY_INSTANT1(category, name, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, category, name, TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val) +#define TRACE_EVENT_COPY_INSTANT2(category, name, arg1_name, arg1_val, arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, category, name, TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val, \ arg2_name, arg2_val) // Records a single BEGIN event called "name" immediately, with 0, 1 or 2 // associated arguments. If the category is not enabled, then this // does nothing. // - category and name strings must have application lifetime (statics or // literals). They may not include " chars. -#define TRACE_EVENT_BEGIN0(category, name) \ - INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_BEGIN, category, name, \ - TRACE_EVENT_FLAG_NONE) -#define TRACE_EVENT_BEGIN1(category, name, arg1_name, arg1_val) \ - INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_BEGIN, category, name, \ - TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val) -#define TRACE_EVENT_BEGIN2(category, name, arg1_name, arg1_val, arg2_name, \ - arg2_val) \ - INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_BEGIN, category, name, \ - TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val, \ +#define TRACE_EVENT_BEGIN0(category, name) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_BEGIN, category, name, TRACE_EVENT_FLAG_NONE) +#define TRACE_EVENT_BEGIN1(category, name, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_BEGIN, category, name, TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val) +#define TRACE_EVENT_BEGIN2(category, name, arg1_name, arg1_val, arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_BEGIN, category, name, TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val, \ arg2_name, arg2_val) -#define TRACE_EVENT_COPY_BEGIN0(category, name) \ - INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_BEGIN, category, name, \ - TRACE_EVENT_FLAG_COPY) -#define TRACE_EVENT_COPY_BEGIN1(category, name, arg1_name, arg1_val) \ - INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_BEGIN, category, name, \ - TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val) -#define TRACE_EVENT_COPY_BEGIN2(category, name, arg1_name, arg1_val, \ - arg2_name, arg2_val) \ - INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_BEGIN, category, name, \ - TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val, \ +#define TRACE_EVENT_COPY_BEGIN0(category, name) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_BEGIN, category, name, TRACE_EVENT_FLAG_COPY) +#define TRACE_EVENT_COPY_BEGIN1(category, name, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_BEGIN, category, name, TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val) +#define TRACE_EVENT_COPY_BEGIN2(category, name, arg1_name, arg1_val, arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_BEGIN, category, name, TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val, \ arg2_name, arg2_val) // Records a single END event for "name" immediately. If the category // is not enabled, then this does nothing. // - category and name strings must have application lifetime (statics or // literals). They may not include " chars. -#define TRACE_EVENT_END0(category, name) \ - INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_END, category, name, \ - TRACE_EVENT_FLAG_NONE) -#define TRACE_EVENT_END1(category, name, arg1_name, arg1_val) \ - INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_END, category, name, \ - TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val) -#define TRACE_EVENT_END2(category, name, arg1_name, arg1_val, arg2_name, \ - arg2_val) \ - INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_END, category, name, \ - TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val, \ +#define TRACE_EVENT_END0(category, name) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_END, category, name, TRACE_EVENT_FLAG_NONE) +#define TRACE_EVENT_END1(category, name, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_END, category, name, TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val) +#define TRACE_EVENT_END2(category, name, arg1_name, arg1_val, arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_END, category, name, TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val, \ arg2_name, arg2_val) -#define TRACE_EVENT_COPY_END0(category, name) \ - INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_END, category, name, \ - TRACE_EVENT_FLAG_COPY) -#define TRACE_EVENT_COPY_END1(category, name, arg1_name, arg1_val) \ - INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_END, category, name, \ - TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val) -#define TRACE_EVENT_COPY_END2(category, name, arg1_name, arg1_val, arg2_name, \ - arg2_val) \ - INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_END, category, name, \ - TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val, \ +#define TRACE_EVENT_COPY_END0(category, name) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_END, category, name, TRACE_EVENT_FLAG_COPY) +#define TRACE_EVENT_COPY_END1(category, name, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_END, category, name, TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val) +#define TRACE_EVENT_COPY_END2(category, name, arg1_name, arg1_val, arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_END, category, name, TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val, \ arg2_name, arg2_val) // Records the value of a counter called "name" immediately. Value // must be representable as a 32 bit integer. // - category and name strings must have application lifetime (statics or // literals). They may not include " chars. -#define TRACE_COUNTER1(category, name, value) \ - INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_COUNTER, category, name, \ - TRACE_EVENT_FLAG_NONE, "value", \ +#define TRACE_COUNTER1(category, name, value) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_COUNTER, category, name, TRACE_EVENT_FLAG_NONE, "value", \ static_cast(value)) -#define TRACE_COPY_COUNTER1(category, name, value) \ - INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_COUNTER, category, name, \ - TRACE_EVENT_FLAG_COPY, "value", \ +#define TRACE_COPY_COUNTER1(category, name, value) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_COUNTER, category, name, TRACE_EVENT_FLAG_COPY, "value", \ static_cast(value)) // Records the values of a multi-parted counter called "name" immediately. // The UI will treat value1 and value2 as parts of a whole, displaying their // values as a stacked-bar chart. // - category and name strings must have application lifetime (statics or // literals). They may not include " chars. -#define TRACE_COUNTER2(category, name, value1_name, value1_val, value2_name, \ - value2_val) \ - INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_COUNTER, category, name, \ - TRACE_EVENT_FLAG_NONE, value1_name, \ - static_cast(value1_val), value2_name, \ - static_cast(value2_val)) -#define TRACE_COPY_COUNTER2(category, name, value1_name, value1_val, \ - value2_name, value2_val) \ - INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_COUNTER, category, name, \ - TRACE_EVENT_FLAG_COPY, value1_name, \ - static_cast(value1_val), value2_name, \ - static_cast(value2_val)) +#define TRACE_COUNTER2(category, name, value1_name, value1_val, value2_name, value2_val) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_COUNTER, category, name, TRACE_EVENT_FLAG_NONE, value1_name, \ + static_cast(value1_val), value2_name, static_cast(value2_val)) +#define TRACE_COPY_COUNTER2(category, name, value1_name, value1_val, value2_name, value2_val) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_COUNTER, category, name, TRACE_EVENT_FLAG_COPY, value1_name, \ + static_cast(value1_val), value2_name, static_cast(value2_val)) // Records the value of a counter called "name" immediately. Value // must be representable as a 32 bit integer. // - category and name strings must have application lifetime (statics or @@ -319,13 +275,11 @@ // be a pointer or an integer value up to 64 bits. If it's a pointer, the bits // will be xored with a hash of the process ID so that the same pointer on // two different processes will not collide. -#define TRACE_COUNTER_ID1(category, name, id, value) \ - INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_COUNTER, category, name, \ - id, TRACE_EVENT_FLAG_NONE, "value", \ +#define TRACE_COUNTER_ID1(category, name, id, value) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_COUNTER, category, name, id, TRACE_EVENT_FLAG_NONE, "value", \ static_cast(value)) -#define TRACE_COPY_COUNTER_ID1(category, name, id, value) \ - INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_COUNTER, category, name, \ - id, TRACE_EVENT_FLAG_COPY, "value", \ +#define TRACE_COPY_COUNTER_ID1(category, name, id, value) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_COUNTER, category, name, id, TRACE_EVENT_FLAG_COPY, "value", \ static_cast(value)) // Records the values of a multi-parted counter called "name" immediately. // The UI will treat value1 and value2 as parts of a whole, displaying their @@ -336,18 +290,12 @@ // be a pointer or an integer value up to 64 bits. If it's a pointer, the bits // will be xored with a hash of the process ID so that the same pointer on // two different processes will not collide. -#define TRACE_COUNTER_ID2(category, name, id, value1_name, value1_val, \ - value2_name, value2_val) \ - INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_COUNTER, category, name, \ - id, TRACE_EVENT_FLAG_NONE, value1_name, \ - static_cast(value1_val), value2_name, \ - static_cast(value2_val)) -#define TRACE_COPY_COUNTER_ID2(category, name, id, value1_name, value1_val, \ - value2_name, value2_val) \ - INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_COUNTER, category, name, \ - id, TRACE_EVENT_FLAG_COPY, value1_name, \ - static_cast(value1_val), value2_name, \ - static_cast(value2_val)) +#define TRACE_COUNTER_ID2(category, name, id, value1_name, value1_val, value2_name, value2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_COUNTER, category, name, id, TRACE_EVENT_FLAG_NONE, value1_name, \ + static_cast(value1_val), value2_name, static_cast(value2_val)) +#define TRACE_COPY_COUNTER_ID2(category, name, id, value1_name, value1_val, value2_name, value2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_COUNTER, category, name, id, TRACE_EVENT_FLAG_COPY, value1_name, \ + static_cast(value1_val), value2_name, static_cast(value2_val)) // Records a single ASYNC_BEGIN event called "name" immediately, with 0, 1 or 2 // associated arguments. If the category is not enabled, then this // does nothing. @@ -365,77 +313,56 @@ // drawn on the thread defined in the ASYNC_BEGIN event), but all events in that // operation must use the same |name| and |id|. Each event can have its own // args. -#define TRACE_EVENT_ASYNC_BEGIN0(category, name, id) \ - INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_BEGIN, category, \ - name, id, TRACE_EVENT_FLAG_NONE) -#define TRACE_EVENT_ASYNC_BEGIN1(category, name, id, arg1_name, arg1_val) \ - INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_BEGIN, category, \ - name, id, TRACE_EVENT_FLAG_NONE, arg1_name, \ - arg1_val) -#define TRACE_EVENT_ASYNC_BEGIN2(category, name, id, arg1_name, arg1_val, \ - arg2_name, arg2_val) \ - INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_BEGIN, category, \ - name, id, TRACE_EVENT_FLAG_NONE, arg1_name, \ - arg1_val, arg2_name, arg2_val) -#define TRACE_EVENT_COPY_ASYNC_BEGIN0(category, name, id) \ - INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_BEGIN, category, \ - name, id, TRACE_EVENT_FLAG_COPY) -#define TRACE_EVENT_COPY_ASYNC_BEGIN1(category, name, id, arg1_name, arg1_val) \ - INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_BEGIN, category, \ - name, id, TRACE_EVENT_FLAG_COPY, arg1_name, \ - arg1_val) -#define TRACE_EVENT_COPY_ASYNC_BEGIN2(category, name, id, arg1_name, arg1_val, \ - arg2_name, arg2_val) \ - INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_BEGIN, category, \ - name, id, TRACE_EVENT_FLAG_COPY, arg1_name, \ - arg1_val, arg2_name, arg2_val) +#define TRACE_EVENT_ASYNC_BEGIN0(category, name, id) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_BEGIN, category, name, id, TRACE_EVENT_FLAG_NONE) +#define TRACE_EVENT_ASYNC_BEGIN1(category, name, id, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_BEGIN, category, name, id, TRACE_EVENT_FLAG_NONE, \ + arg1_name, arg1_val) +#define TRACE_EVENT_ASYNC_BEGIN2(category, name, id, arg1_name, arg1_val, arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_BEGIN, category, name, id, TRACE_EVENT_FLAG_NONE, \ + arg1_name, arg1_val, arg2_name, arg2_val) +#define TRACE_EVENT_COPY_ASYNC_BEGIN0(category, name, id) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_BEGIN, category, name, id, TRACE_EVENT_FLAG_COPY) +#define TRACE_EVENT_COPY_ASYNC_BEGIN1(category, name, id, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_BEGIN, category, name, id, TRACE_EVENT_FLAG_COPY, \ + arg1_name, arg1_val) +#define TRACE_EVENT_COPY_ASYNC_BEGIN2(category, name, id, arg1_name, arg1_val, arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_BEGIN, category, name, id, TRACE_EVENT_FLAG_COPY, \ + arg1_name, arg1_val, arg2_name, arg2_val) // Records a single ASYNC_STEP event for |step| immediately. If the category // is not enabled, then this does nothing. The |name| and |id| must match the // ASYNC_BEGIN event above. The |step| param identifies this step within the // async event. This should be called at the beginning of the next phase of an // asynchronous operation. -#define TRACE_EVENT_ASYNC_STEP0(category, name, id, step) \ - INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_STEP, category, \ - name, id, TRACE_EVENT_FLAG_NONE, "step", \ +#define TRACE_EVENT_ASYNC_STEP0(category, name, id, step) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_STEP, category, name, id, TRACE_EVENT_FLAG_NONE, "step", \ step) -#define TRACE_EVENT_ASYNC_STEP1(category, name, id, step, arg1_name, arg1_val) \ - INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_STEP, category, \ - name, id, TRACE_EVENT_FLAG_NONE, "step", \ +#define TRACE_EVENT_ASYNC_STEP1(category, name, id, step, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_STEP, category, name, id, TRACE_EVENT_FLAG_NONE, "step", \ step, arg1_name, arg1_val) -#define TRACE_EVENT_COPY_ASYNC_STEP0(category, name, id, step) \ - INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_STEP, category, \ - name, id, TRACE_EVENT_FLAG_COPY, "step", \ +#define TRACE_EVENT_COPY_ASYNC_STEP0(category, name, id, step) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_STEP, category, name, id, TRACE_EVENT_FLAG_COPY, "step", \ step) -#define TRACE_EVENT_COPY_ASYNC_STEP1(category, name, id, step, arg1_name, \ - arg1_val) \ - INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_STEP, category, \ - name, id, TRACE_EVENT_FLAG_COPY, "step", \ +#define TRACE_EVENT_COPY_ASYNC_STEP1(category, name, id, step, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_STEP, category, name, id, TRACE_EVENT_FLAG_COPY, "step", \ step, arg1_name, arg1_val) // Records a single ASYNC_END event for "name" immediately. If the category // is not enabled, then this does nothing. -#define TRACE_EVENT_ASYNC_END0(category, name, id) \ - INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_END, category, \ - name, id, TRACE_EVENT_FLAG_NONE) -#define TRACE_EVENT_ASYNC_END1(category, name, id, arg1_name, arg1_val) \ - INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_END, category, \ - name, id, TRACE_EVENT_FLAG_NONE, arg1_name, \ +#define TRACE_EVENT_ASYNC_END0(category, name, id) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_END, category, name, id, TRACE_EVENT_FLAG_NONE) +#define TRACE_EVENT_ASYNC_END1(category, name, id, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_END, category, name, id, TRACE_EVENT_FLAG_NONE, arg1_name, \ arg1_val) -#define TRACE_EVENT_ASYNC_END2(category, name, id, arg1_name, arg1_val, \ - arg2_name, arg2_val) \ - INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_END, category, \ - name, id, TRACE_EVENT_FLAG_NONE, arg1_name, \ +#define TRACE_EVENT_ASYNC_END2(category, name, id, arg1_name, arg1_val, arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_END, category, name, id, TRACE_EVENT_FLAG_NONE, arg1_name, \ arg1_val, arg2_name, arg2_val) -#define TRACE_EVENT_COPY_ASYNC_END0(category, name, id) \ - INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_END, category, \ - name, id, TRACE_EVENT_FLAG_COPY) -#define TRACE_EVENT_COPY_ASYNC_END1(category, name, id, arg1_name, arg1_val) \ - INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_END, category, \ - name, id, TRACE_EVENT_FLAG_COPY, arg1_name, \ +#define TRACE_EVENT_COPY_ASYNC_END0(category, name, id) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_END, category, name, id, TRACE_EVENT_FLAG_COPY) +#define TRACE_EVENT_COPY_ASYNC_END1(category, name, id, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_END, category, name, id, TRACE_EVENT_FLAG_COPY, arg1_name, \ arg1_val) -#define TRACE_EVENT_COPY_ASYNC_END2(category, name, id, arg1_name, arg1_val, \ - arg2_name, arg2_val) \ - INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_END, category, \ - name, id, TRACE_EVENT_FLAG_COPY, arg1_name, \ +#define TRACE_EVENT_COPY_ASYNC_END2(category, name, id, arg1_name, arg1_val, arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_END, category, name, id, TRACE_EVENT_FLAG_COPY, arg1_name, \ arg1_val, arg2_name, arg2_val) // Records a single FLOW_BEGIN event called "name" immediately, with 0, 1 or 2 // associated arguments. If the category is not enabled, then this @@ -456,77 +383,54 @@ // macros. When the operation completes, call FLOW_END. An async operation can // span threads and processes, but all events in that operation must use the // same |name| and |id|. Each event can have its own args. -#define TRACE_EVENT_FLOW_BEGIN0(category, name, id) \ - INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_BEGIN, category, \ - name, id, TRACE_EVENT_FLAG_NONE) -#define TRACE_EVENT_FLOW_BEGIN1(category, name, id, arg1_name, arg1_val) \ - INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_BEGIN, category, \ - name, id, TRACE_EVENT_FLAG_NONE, arg1_name, \ +#define TRACE_EVENT_FLOW_BEGIN0(category, name, id) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_BEGIN, category, name, id, TRACE_EVENT_FLAG_NONE) +#define TRACE_EVENT_FLOW_BEGIN1(category, name, id, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_BEGIN, category, name, id, TRACE_EVENT_FLAG_NONE, arg1_name, \ arg1_val) -#define TRACE_EVENT_FLOW_BEGIN2(category, name, id, arg1_name, arg1_val, \ - arg2_name, arg2_val) \ - INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_BEGIN, category, \ - name, id, TRACE_EVENT_FLAG_NONE, arg1_name, \ +#define TRACE_EVENT_FLOW_BEGIN2(category, name, id, arg1_name, arg1_val, arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_BEGIN, category, name, id, TRACE_EVENT_FLAG_NONE, arg1_name, \ arg1_val, arg2_name, arg2_val) -#define TRACE_EVENT_COPY_FLOW_BEGIN0(category, name, id) \ - INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_BEGIN, category, \ - name, id, TRACE_EVENT_FLAG_COPY) -#define TRACE_EVENT_COPY_FLOW_BEGIN1(category, name, id, arg1_name, arg1_val) \ - INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_BEGIN, category, \ - name, id, TRACE_EVENT_FLAG_COPY, arg1_name, \ +#define TRACE_EVENT_COPY_FLOW_BEGIN0(category, name, id) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_BEGIN, category, name, id, TRACE_EVENT_FLAG_COPY) +#define TRACE_EVENT_COPY_FLOW_BEGIN1(category, name, id, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_BEGIN, category, name, id, TRACE_EVENT_FLAG_COPY, arg1_name, \ arg1_val) -#define TRACE_EVENT_COPY_FLOW_BEGIN2(category, name, id, arg1_name, arg1_val, \ - arg2_name, arg2_val) \ - INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_BEGIN, category, \ - name, id, TRACE_EVENT_FLAG_COPY, arg1_name, \ +#define TRACE_EVENT_COPY_FLOW_BEGIN2(category, name, id, arg1_name, arg1_val, arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_BEGIN, category, name, id, TRACE_EVENT_FLAG_COPY, arg1_name, \ arg1_val, arg2_name, arg2_val) // Records a single FLOW_STEP event for |step| immediately. If the category // is not enabled, then this does nothing. The |name| and |id| must match the // FLOW_BEGIN event above. The |step| param identifies this step within the // async event. This should be called at the beginning of the next phase of an // asynchronous operation. -#define TRACE_EVENT_FLOW_STEP0(category, name, id, step) \ - INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_STEP, category, \ - name, id, TRACE_EVENT_FLAG_NONE, "step", \ - step) -#define TRACE_EVENT_FLOW_STEP1(category, name, id, step, arg1_name, arg1_val) \ - INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_STEP, category, \ - name, id, TRACE_EVENT_FLAG_NONE, "step", \ +#define TRACE_EVENT_FLOW_STEP0(category, name, id, step) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_STEP, category, name, id, TRACE_EVENT_FLAG_NONE, "step", step) +#define TRACE_EVENT_FLOW_STEP1(category, name, id, step, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_STEP, category, name, id, TRACE_EVENT_FLAG_NONE, "step", \ step, arg1_name, arg1_val) -#define TRACE_EVENT_COPY_FLOW_STEP0(category, name, id, step) \ - INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_STEP, category, \ - name, id, TRACE_EVENT_FLAG_COPY, "step", \ - step) -#define TRACE_EVENT_COPY_FLOW_STEP1(category, name, id, step, arg1_name, \ - arg1_val) \ - INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_STEP, category, \ - name, id, TRACE_EVENT_FLAG_COPY, "step", \ +#define TRACE_EVENT_COPY_FLOW_STEP0(category, name, id, step) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_STEP, category, name, id, TRACE_EVENT_FLAG_COPY, "step", step) +#define TRACE_EVENT_COPY_FLOW_STEP1(category, name, id, step, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_STEP, category, name, id, TRACE_EVENT_FLAG_COPY, "step", \ step, arg1_name, arg1_val) // Records a single FLOW_END event for "name" immediately. If the category // is not enabled, then this does nothing. -#define TRACE_EVENT_FLOW_END0(category, name, id) \ - INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, category, name, \ - id, TRACE_EVENT_FLAG_NONE) -#define TRACE_EVENT_FLOW_END1(category, name, id, arg1_name, arg1_val) \ - INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, category, name, \ - id, TRACE_EVENT_FLAG_NONE, arg1_name, \ +#define TRACE_EVENT_FLOW_END0(category, name, id) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, category, name, id, TRACE_EVENT_FLAG_NONE) +#define TRACE_EVENT_FLOW_END1(category, name, id, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, category, name, id, TRACE_EVENT_FLAG_NONE, arg1_name, \ arg1_val) -#define TRACE_EVENT_FLOW_END2(category, name, id, arg1_name, arg1_val, \ - arg2_name, arg2_val) \ - INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, category, name, \ - id, TRACE_EVENT_FLAG_NONE, arg1_name, \ +#define TRACE_EVENT_FLOW_END2(category, name, id, arg1_name, arg1_val, arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, category, name, id, TRACE_EVENT_FLAG_NONE, arg1_name, \ arg1_val, arg2_name, arg2_val) -#define TRACE_EVENT_COPY_FLOW_END0(category, name, id) \ - INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, category, name, \ - id, TRACE_EVENT_FLAG_COPY) -#define TRACE_EVENT_COPY_FLOW_END1(category, name, id, arg1_name, arg1_val) \ - INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, category, name, \ - id, TRACE_EVENT_FLAG_COPY, arg1_name, \ +#define TRACE_EVENT_COPY_FLOW_END0(category, name, id) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, category, name, id, TRACE_EVENT_FLAG_COPY) +#define TRACE_EVENT_COPY_FLOW_END1(category, name, id, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, category, name, id, TRACE_EVENT_FLAG_COPY, arg1_name, \ arg1_val) -#define TRACE_EVENT_COPY_FLOW_END2(category, name, id, arg1_name, arg1_val, \ - arg2_name, arg2_val) \ - INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, category, name, \ - id, TRACE_EVENT_FLAG_COPY, arg1_name, \ +#define TRACE_EVENT_COPY_FLOW_END2(category, name, id, arg1_name, arg1_val, arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, category, name, id, TRACE_EVENT_FLAG_COPY, arg1_name, \ arg1_val, arg2_name, arg2_val) //////////////////////////////////////////////////////////////////////////////// // Implementation specific tracing API definitions. @@ -540,8 +444,7 @@ // for best performance when tracing is disabled. // const unsigned char* // TRACE_EVENT_API_GET_CATEGORY_ENABLED(const char* category_name) -#define TRACE_EVENT_API_GET_CATEGORY_ENABLED \ - webrtc::EventTracer::GetCategoryEnabled +#define TRACE_EVENT_API_GET_CATEGORY_ENABLED webrtc::EventTracer::GetCategoryEnabled // Add a trace event to the platform tracing system. // void TRACE_EVENT_API_ADD_TRACE_EVENT( // char phase, @@ -560,55 +463,46 @@ // variable a unique name based on the line number to prevent name collissions. #define INTERNAL_TRACE_EVENT_UID3(a, b) trace_event_unique_##a##b #define INTERNAL_TRACE_EVENT_UID2(a, b) INTERNAL_TRACE_EVENT_UID3(a, b) -#define INTERNAL_TRACE_EVENT_UID(name_prefix) \ - INTERNAL_TRACE_EVENT_UID2(name_prefix, __LINE__) +#define INTERNAL_TRACE_EVENT_UID(name_prefix) INTERNAL_TRACE_EVENT_UID2(name_prefix, __LINE__) // Implementation detail: internal macro to create static category. -#define INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category) \ - static const unsigned char *INTERNAL_TRACE_EVENT_UID(catstatic) = 0; \ - if (!INTERNAL_TRACE_EVENT_UID(catstatic)) { \ - INTERNAL_TRACE_EVENT_UID(catstatic) = \ - TRACE_EVENT_API_GET_CATEGORY_ENABLED(category); \ +#define INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category) \ + static const unsigned char* INTERNAL_TRACE_EVENT_UID(catstatic) = 0; \ + if (!INTERNAL_TRACE_EVENT_UID(catstatic)) { \ + INTERNAL_TRACE_EVENT_UID(catstatic) = TRACE_EVENT_API_GET_CATEGORY_ENABLED(category); \ } // Implementation detail: internal macro to create static category and add // event if the category is enabled. -#define INTERNAL_TRACE_EVENT_ADD(phase, category, name, flags, ...) \ - do { \ - INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category); \ - if (*INTERNAL_TRACE_EVENT_UID(catstatic)) { \ - webrtc::trace_event_internal::AddTraceEvent( \ - phase, INTERNAL_TRACE_EVENT_UID(catstatic), name, \ - webrtc::trace_event_internal::kNoEventId, flags, ##__VA_ARGS__); \ - } \ +#define INTERNAL_TRACE_EVENT_ADD(phase, category, name, flags, ...) \ + do { \ + INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category); \ + if (*INTERNAL_TRACE_EVENT_UID(catstatic)) { \ + webrtc::trace_event_internal::AddTraceEvent(phase, INTERNAL_TRACE_EVENT_UID(catstatic), name, \ + webrtc::trace_event_internal::kNoEventId, flags, ##__VA_ARGS__); \ + } \ } while (0) // Implementation detail: internal macro to create static category and add begin // event if the category is enabled. Also adds the end event when the scope // ends. -#define INTERNAL_TRACE_EVENT_ADD_SCOPED(category, name, ...) \ - INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category); \ - webrtc::trace_event_internal::TraceEndOnScopeClose INTERNAL_TRACE_EVENT_UID( \ - profileScope); \ - if (*INTERNAL_TRACE_EVENT_UID(catstatic)) { \ - webrtc::trace_event_internal::AddTraceEvent( \ - TRACE_EVENT_PHASE_BEGIN, INTERNAL_TRACE_EVENT_UID(catstatic), name, \ - webrtc::trace_event_internal::kNoEventId, TRACE_EVENT_FLAG_NONE, \ - ##__VA_ARGS__); \ - INTERNAL_TRACE_EVENT_UID(profileScope) \ - .Initialize(INTERNAL_TRACE_EVENT_UID(catstatic), name); \ +#define INTERNAL_TRACE_EVENT_ADD_SCOPED(category, name, ...) \ + INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category); \ + webrtc::trace_event_internal::TraceEndOnScopeClose INTERNAL_TRACE_EVENT_UID(profileScope); \ + if (*INTERNAL_TRACE_EVENT_UID(catstatic)) { \ + webrtc::trace_event_internal::AddTraceEvent(TRACE_EVENT_PHASE_BEGIN, INTERNAL_TRACE_EVENT_UID(catstatic), name, \ + webrtc::trace_event_internal::kNoEventId, TRACE_EVENT_FLAG_NONE, \ + ##__VA_ARGS__); \ + INTERNAL_TRACE_EVENT_UID(profileScope).Initialize(INTERNAL_TRACE_EVENT_UID(catstatic), name); \ } // Implementation detail: internal macro to create static category and add // event if the category is enabled. -#define INTERNAL_TRACE_EVENT_ADD_WITH_ID(phase, category, name, id, flags, \ - ...) \ - do { \ - INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category); \ - if (*INTERNAL_TRACE_EVENT_UID(catstatic)) { \ - unsigned char trace_event_flags = flags | TRACE_EVENT_FLAG_HAS_ID; \ - webrtc::trace_event_internal::TraceID trace_event_trace_id( \ - id, &trace_event_flags); \ - webrtc::trace_event_internal::AddTraceEvent( \ - phase, INTERNAL_TRACE_EVENT_UID(catstatic), name, \ - trace_event_trace_id.data(), trace_event_flags, ##__VA_ARGS__); \ - } \ +#define INTERNAL_TRACE_EVENT_ADD_WITH_ID(phase, category, name, id, flags, ...) \ + do { \ + INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category); \ + if (*INTERNAL_TRACE_EVENT_UID(catstatic)) { \ + unsigned char trace_event_flags = flags | TRACE_EVENT_FLAG_HAS_ID; \ + webrtc::trace_event_internal::TraceID trace_event_trace_id(id, &trace_event_flags); \ + webrtc::trace_event_internal::AddTraceEvent(phase, INTERNAL_TRACE_EVENT_UID(catstatic), name, \ + trace_event_trace_id.data(), trace_event_flags, ##__VA_ARGS__); \ + } \ } while (0) // Notes regarding the following definitions: // New values can be added and propagated to third party libraries, but existing @@ -657,63 +551,31 @@ class TraceID { explicit ForceMangle(unsigned int id) : data_(id) {} explicit ForceMangle(unsigned short id) : data_(id) {} explicit ForceMangle(unsigned char id) : data_(id) {} - explicit ForceMangle(long long id) - : data_(static_cast(id)) {} - explicit ForceMangle(long id) - : data_(static_cast(id)) {} + explicit ForceMangle(long long id) : data_(static_cast(id)) {} + explicit ForceMangle(long id) : data_(static_cast(id)) {} explicit ForceMangle(int id) : data_(static_cast(id)) {} - explicit ForceMangle(short id) - : data_(static_cast(id)) {} - explicit ForceMangle(signed char id) - : data_(static_cast(id)) {} + explicit ForceMangle(short id) : data_(static_cast(id)) {} + explicit ForceMangle(signed char id) : data_(static_cast(id)) {} unsigned long long data() const { return data_; } private: unsigned long long data_; }; - explicit TraceID(const void *id, unsigned char *flags) - : data_(static_cast( - reinterpret_cast(id))) { - *flags |= TRACE_EVENT_FLAG_MANGLE_ID; - } - explicit TraceID(ForceMangle id, unsigned char *flags) : data_(id.data()) { + explicit TraceID(const void* id, unsigned char* flags) + : data_(static_cast(reinterpret_cast(id))) { *flags |= TRACE_EVENT_FLAG_MANGLE_ID; } - explicit TraceID(unsigned long long id, unsigned char *flags) : data_(id) { - (void)flags; - } - explicit TraceID(unsigned long id, unsigned char *flags) : data_(id) { - (void)flags; - } - explicit TraceID(unsigned int id, unsigned char *flags) : data_(id) { - (void)flags; - } - explicit TraceID(unsigned short id, unsigned char *flags) : data_(id) { - (void)flags; - } - explicit TraceID(unsigned char id, unsigned char *flags) : data_(id) { - (void)flags; - } - explicit TraceID(long long id, unsigned char *flags) - : data_(static_cast(id)) { - (void)flags; - } - explicit TraceID(long id, unsigned char *flags) - : data_(static_cast(id)) { - (void)flags; - } - explicit TraceID(int id, unsigned char *flags) - : data_(static_cast(id)) { - (void)flags; - } - explicit TraceID(short id, unsigned char *flags) - : data_(static_cast(id)) { - (void)flags; - } - explicit TraceID(signed char id, unsigned char *flags) - : data_(static_cast(id)) { - (void)flags; - } + explicit TraceID(ForceMangle id, unsigned char* flags) : data_(id.data()) { *flags |= TRACE_EVENT_FLAG_MANGLE_ID; } + explicit TraceID(unsigned long long id, unsigned char* flags) : data_(id) { (void)flags; } + explicit TraceID(unsigned long id, unsigned char* flags) : data_(id) { (void)flags; } + explicit TraceID(unsigned int id, unsigned char* flags) : data_(id) { (void)flags; } + explicit TraceID(unsigned short id, unsigned char* flags) : data_(id) { (void)flags; } + explicit TraceID(unsigned char id, unsigned char* flags) : data_(id) { (void)flags; } + explicit TraceID(long long id, unsigned char* flags) : data_(static_cast(id)) { (void)flags; } + explicit TraceID(long id, unsigned char* flags) : data_(static_cast(id)) { (void)flags; } + explicit TraceID(int id, unsigned char* flags) : data_(static_cast(id)) { (void)flags; } + explicit TraceID(short id, unsigned char* flags) : data_(static_cast(id)) { (void)flags; } + explicit TraceID(signed char id, unsigned char* flags) : data_(static_cast(id)) { (void)flags; } unsigned long long data() const { return data_; } private: @@ -725,36 +587,33 @@ union TraceValueUnion { unsigned long long as_uint; long long as_int; double as_double; - const void *as_pointer; - const char *as_string; + const void* as_pointer; + const char* as_string; }; // Simple container for const char* that should be copied instead of retained. class TraceStringWithCopy { public: - explicit TraceStringWithCopy(const char *str) : str_(str) {} - operator const char *() const { return str_; } + explicit TraceStringWithCopy(const char* str) : str_(str) {} + operator const char*() const { return str_; } private: - const char *str_; + const char* str_; }; // Define SetTraceValue for each allowed type. It stores the type and // value in the return arguments. This allows this API to avoid declaring any // structures so that it is portable to third_party libraries. -#define INTERNAL_DECLARE_SET_TRACE_VALUE(actual_type, union_member, \ - value_type_id) \ - static inline void SetTraceValue(actual_type arg, unsigned char *type, \ - unsigned long long *value) { \ - TraceValueUnion type_value; \ - type_value.union_member = arg; \ - *type = value_type_id; \ - *value = type_value.as_uint; \ +#define INTERNAL_DECLARE_SET_TRACE_VALUE(actual_type, union_member, value_type_id) \ + static inline void SetTraceValue(actual_type arg, unsigned char* type, unsigned long long* value) { \ + TraceValueUnion type_value; \ + type_value.union_member = arg; \ + *type = value_type_id; \ + *value = type_value.as_uint; \ } // Simpler form for int types that can be safely casted. -#define INTERNAL_DECLARE_SET_TRACE_VALUE_INT(actual_type, value_type_id) \ - static inline void SetTraceValue(actual_type arg, unsigned char *type, \ - unsigned long long *value) { \ - *type = value_type_id; \ - *value = static_cast(arg); \ +#define INTERNAL_DECLARE_SET_TRACE_VALUE_INT(actual_type, value_type_id) \ + static inline void SetTraceValue(actual_type arg, unsigned char* type, unsigned long long* value) { \ + *type = value_type_id; \ + *value = static_cast(arg); \ } INTERNAL_DECLARE_SET_TRACE_VALUE_INT(unsigned long long, TRACE_VALUE_TYPE_UINT) INTERNAL_DECLARE_SET_TRACE_VALUE_INT(unsigned long, TRACE_VALUE_TYPE_UINT) @@ -768,17 +627,13 @@ INTERNAL_DECLARE_SET_TRACE_VALUE_INT(short, TRACE_VALUE_TYPE_INT) INTERNAL_DECLARE_SET_TRACE_VALUE_INT(signed char, TRACE_VALUE_TYPE_INT) INTERNAL_DECLARE_SET_TRACE_VALUE(bool, as_bool, TRACE_VALUE_TYPE_BOOL) INTERNAL_DECLARE_SET_TRACE_VALUE(double, as_double, TRACE_VALUE_TYPE_DOUBLE) -INTERNAL_DECLARE_SET_TRACE_VALUE(const void *, as_pointer, - TRACE_VALUE_TYPE_POINTER) -INTERNAL_DECLARE_SET_TRACE_VALUE(const char *, as_string, - TRACE_VALUE_TYPE_STRING) -INTERNAL_DECLARE_SET_TRACE_VALUE(const TraceStringWithCopy &, as_string, - TRACE_VALUE_TYPE_COPY_STRING) +INTERNAL_DECLARE_SET_TRACE_VALUE(const void*, as_pointer, TRACE_VALUE_TYPE_POINTER) +INTERNAL_DECLARE_SET_TRACE_VALUE(const char*, as_string, TRACE_VALUE_TYPE_STRING) +INTERNAL_DECLARE_SET_TRACE_VALUE(const TraceStringWithCopy&, as_string, TRACE_VALUE_TYPE_COPY_STRING) #undef INTERNAL_DECLARE_SET_TRACE_VALUE #undef INTERNAL_DECLARE_SET_TRACE_VALUE_INT // std::string version of SetTraceValue so that trace arguments can be strings. -static inline void SetTraceValue(const std::string &arg, unsigned char *type, - unsigned long long *value) { +static inline void SetTraceValue(const std::string& arg, unsigned char* type, unsigned long long* value) { TraceValueUnion type_value; type_value.as_string = arg.c_str(); *type = TRACE_VALUE_TYPE_COPY_STRING; @@ -789,39 +644,32 @@ static inline void SetTraceValue(const std::string &arg, unsigned char *type, // std::string. In order to store pointers to the internal c_str and pass // through to the tracing API, the arg_values must live throughout // these procedures. -static inline void AddTraceEvent(char phase, - const unsigned char *category_enabled, - const char *name, unsigned long long id, - unsigned char flags) { - TRACE_EVENT_API_ADD_TRACE_EVENT(phase, category_enabled, name, id, - kZeroNumArgs, NULL, NULL, NULL, flags); +static inline void AddTraceEvent(char phase, const unsigned char* category_enabled, const char* name, + unsigned long long id, unsigned char flags) { + TRACE_EVENT_API_ADD_TRACE_EVENT(phase, category_enabled, name, id, kZeroNumArgs, NULL, NULL, NULL, flags); } template -static inline void -AddTraceEvent(char phase, const unsigned char *category_enabled, - const char *name, unsigned long long id, unsigned char flags, - const char *arg1_name, const ARG1_TYPE &arg1_val) { +static inline void AddTraceEvent(char phase, const unsigned char* category_enabled, const char* name, + unsigned long long id, unsigned char flags, const char* arg1_name, + const ARG1_TYPE& arg1_val) { const int num_args = 1; unsigned char arg_types[1]; unsigned long long arg_values[1]; SetTraceValue(arg1_val, &arg_types[0], &arg_values[0]); - TRACE_EVENT_API_ADD_TRACE_EVENT(phase, category_enabled, name, id, num_args, - &arg1_name, arg_types, arg_values, flags); + TRACE_EVENT_API_ADD_TRACE_EVENT(phase, category_enabled, name, id, num_args, &arg1_name, arg_types, arg_values, + flags); } template -static inline void -AddTraceEvent(char phase, const unsigned char *category_enabled, - const char *name, unsigned long long id, unsigned char flags, - const char *arg1_name, const ARG1_TYPE &arg1_val, - const char *arg2_name, const ARG2_TYPE &arg2_val) { +static inline void AddTraceEvent(char phase, const unsigned char* category_enabled, const char* name, + unsigned long long id, unsigned char flags, const char* arg1_name, + const ARG1_TYPE& arg1_val, const char* arg2_name, const ARG2_TYPE& arg2_val) { const int num_args = 2; - const char *arg_names[2] = {arg1_name, arg2_name}; + const char* arg_names[2] = {arg1_name, arg2_name}; unsigned char arg_types[2]; unsigned long long arg_values[2]; SetTraceValue(arg1_val, &arg_types[0], &arg_values[0]); SetTraceValue(arg2_val, &arg_types[1], &arg_values[1]); - TRACE_EVENT_API_ADD_TRACE_EVENT(phase, category_enabled, name, id, num_args, - arg_names, arg_types, arg_values, flags); + TRACE_EVENT_API_ADD_TRACE_EVENT(phase, category_enabled, name, id, num_args, arg_names, arg_types, arg_values, flags); } // Used by TRACE_EVENTx macro. Do not use directly. class TraceEndOnScopeClose { @@ -829,10 +677,11 @@ class TraceEndOnScopeClose { // Note: members of data_ intentionally left uninitialized. See Initialize. TraceEndOnScopeClose() : p_data_(NULL) {} ~TraceEndOnScopeClose() { - if (p_data_) + if (p_data_) { AddEventIfEnabled(); + } } - void Initialize(const unsigned char *category_enabled, const char *name) { + void Initialize(const unsigned char* category_enabled, const char* name) { data_.category_enabled = category_enabled; data_.name = name; p_data_ = &data_; @@ -843,9 +692,8 @@ class TraceEndOnScopeClose { void AddEventIfEnabled() { // Only called when p_data_ is non-null. if (*p_data_->category_enabled) { - TRACE_EVENT_API_ADD_TRACE_EVENT( - TRACE_EVENT_PHASE_END, p_data_->category_enabled, p_data_->name, - kNoEventId, kZeroNumArgs, NULL, NULL, NULL, TRACE_EVENT_FLAG_NONE); + TRACE_EVENT_API_ADD_TRACE_EVENT(TRACE_EVENT_PHASE_END, p_data_->category_enabled, p_data_->name, kNoEventId, + kZeroNumArgs, NULL, NULL, NULL, TRACE_EVENT_FLAG_NONE); } } // This Data struct workaround is to avoid initializing all the members @@ -854,10 +702,10 @@ class TraceEndOnScopeClose { // members of this class instead, compiler warnings occur about potential // uninitialized accesses. struct Data { - const unsigned char *category_enabled; - const char *name; + const unsigned char* category_enabled; + const char* name; }; - Data *p_data_; + Data* p_data_; Data data_; }; } // namespace trace_event_internal diff --git a/src/trace/tracing.cpp b/src/trace/tracing.cpp index a753e2bf..4c2af2bc 100644 --- a/src/trace/tracing.cpp +++ b/src/trace/tracing.cpp @@ -15,12 +15,12 @@ */ #include "livekit/tracing.h" + #include "trace/event_tracer_internal.h" namespace livekit { -bool startTracing(const std::string &trace_file_path, - const std::vector &categories) { +bool startTracing(const std::string& trace_file_path, const std::vector& categories) { return trace::internal::StartTracing(trace_file_path, categories); } diff --git a/src/track.cpp b/src/track.cpp index 2dd403d9..d223ba76 100644 --- a/src/track.cpp +++ b/src/track.cpp @@ -16,21 +16,25 @@ #include "livekit/track.h" -#include "ffi_client.h" #include #include -namespace livekit { +#include "ffi_client.h" -Track::Track(FfiHandle handle, std::string sid, std::string name, - TrackKind kind, StreamState state, bool muted, bool remote) - : handle_(std::move(handle)), sid_(std::move(sid)), name_(std::move(name)), - kind_(kind), state_(state), muted_(muted), remote_(remote) {} +namespace livekit { -void Track::setPublicationFields(std::optional source, - std::optional simulcasted, - std::optional width, - std::optional height, +Track::Track(FfiHandle handle, std::string sid, std::string name, TrackKind kind, StreamState state, bool muted, + bool remote) + : handle_(std::move(handle)), + sid_(std::move(sid)), + name_(std::move(name)), + kind_(kind), + state_(state), + muted_(muted), + remote_(remote) {} + +void Track::setPublicationFields(std::optional source, std::optional simulcasted, + std::optional width, std::optional height, std::optional mime_type) { source_ = source; simulcasted_ = simulcasted; diff --git a/src/track_proto_converter.cpp b/src/track_proto_converter.cpp index d95b8568..567cd68b 100644 --- a/src/track_proto_converter.cpp +++ b/src/track_proto_converter.cpp @@ -20,21 +20,19 @@ namespace livekit { -proto::ParticipantTrackPermission -toProto(const ParticipantTrackPermission &in) { +proto::ParticipantTrackPermission toProto(const ParticipantTrackPermission& in) { proto::ParticipantTrackPermission out; out.set_participant_identity(in.participant_identity); if (in.allow_all.has_value()) { out.set_allow_all(*in.allow_all); } - for (const auto &sid : in.allowed_track_sids) { + for (const auto& sid : in.allowed_track_sids) { out.add_allowed_track_sids(sid); } return out; } -ParticipantTrackPermission -fromProto(const proto::ParticipantTrackPermission &in) { +ParticipantTrackPermission fromProto(const proto::ParticipantTrackPermission& in) { ParticipantTrackPermission out; out.participant_identity = in.participant_identity(); if (in.has_allow_all()) { @@ -43,7 +41,7 @@ fromProto(const proto::ParticipantTrackPermission &in) { out.allow_all = std::nullopt; } out.allowed_track_sids.reserve(in.allowed_track_sids_size()); - for (const auto &sid : in.allowed_track_sids()) { + for (const auto& sid : in.allowed_track_sids()) { out.allowed_track_sids.push_back(sid); } return out; @@ -51,71 +49,70 @@ fromProto(const proto::ParticipantTrackPermission &in) { TrackKind fromProto(proto::TrackKind in) { switch (in) { - case proto::TrackKind::KIND_AUDIO: - return TrackKind::KIND_AUDIO; - case proto::TrackKind::KIND_VIDEO: - return TrackKind::KIND_VIDEO; - case proto::TrackKind::KIND_UNKNOWN: // NOLINT(bugprone-branch-clone) - return TrackKind::KIND_UNKNOWN; - default: - return TrackKind::KIND_UNKNOWN; + case proto::TrackKind::KIND_AUDIO: + return TrackKind::KIND_AUDIO; + case proto::TrackKind::KIND_VIDEO: + return TrackKind::KIND_VIDEO; + case proto::TrackKind::KIND_UNKNOWN: // NOLINT(bugprone-branch-clone) + return TrackKind::KIND_UNKNOWN; + default: + return TrackKind::KIND_UNKNOWN; } } StreamState fromProto(proto::StreamState in) { switch (in) { - case proto::StreamState::STATE_ACTIVE: - return StreamState::STATE_ACTIVE; - case proto::StreamState::STATE_PAUSED: - return StreamState::STATE_PAUSED; - case proto::StreamState::STATE_UNKNOWN: // NOLINT(bugprone-branch-clone) - return StreamState::STATE_UNKNOWN; - default: - return StreamState::STATE_UNKNOWN; + case proto::StreamState::STATE_ACTIVE: + return StreamState::STATE_ACTIVE; + case proto::StreamState::STATE_PAUSED: + return StreamState::STATE_PAUSED; + case proto::StreamState::STATE_UNKNOWN: // NOLINT(bugprone-branch-clone) + return StreamState::STATE_UNKNOWN; + default: + return StreamState::STATE_UNKNOWN; } } TrackSource fromProto(proto::TrackSource in) { switch (in) { - case proto::TrackSource::SOURCE_CAMERA: - return TrackSource::SOURCE_CAMERA; - case proto::TrackSource::SOURCE_MICROPHONE: - return TrackSource::SOURCE_MICROPHONE; - case proto::TrackSource::SOURCE_SCREENSHARE: - return TrackSource::SOURCE_SCREENSHARE; - case proto::TrackSource::SOURCE_SCREENSHARE_AUDIO: - return TrackSource::SOURCE_SCREENSHARE_AUDIO; - case proto::TrackSource::SOURCE_UNKNOWN: // NOLINT(bugprone-branch-clone) - return TrackSource::SOURCE_UNKNOWN; - default: - return TrackSource::SOURCE_UNKNOWN; + case proto::TrackSource::SOURCE_CAMERA: + return TrackSource::SOURCE_CAMERA; + case proto::TrackSource::SOURCE_MICROPHONE: + return TrackSource::SOURCE_MICROPHONE; + case proto::TrackSource::SOURCE_SCREENSHARE: + return TrackSource::SOURCE_SCREENSHARE; + case proto::TrackSource::SOURCE_SCREENSHARE_AUDIO: + return TrackSource::SOURCE_SCREENSHARE_AUDIO; + case proto::TrackSource::SOURCE_UNKNOWN: // NOLINT(bugprone-branch-clone) + return TrackSource::SOURCE_UNKNOWN; + default: + return TrackSource::SOURCE_UNKNOWN; } } AudioTrackFeature fromProto(proto::AudioTrackFeature in) { switch (in) { - case proto::TF_STEREO: - return AudioTrackFeature::TF_STEREO; - case proto::TF_NO_DTX: - return AudioTrackFeature::TF_NO_DTX; - case proto::TF_AUTO_GAIN_CONTROL: - return AudioTrackFeature::TF_AUTO_GAIN_CONTROL; - case proto::TF_ECHO_CANCELLATION: - return AudioTrackFeature::TF_ECHO_CANCELLATION; - case proto::TF_NOISE_SUPPRESSION: - return AudioTrackFeature::TF_NOISE_SUPPRESSION; - case proto::TF_ENHANCED_NOISE_CANCELLATION: - return AudioTrackFeature::TF_ENHANCED_NOISE_CANCELLATION; - case proto::TF_PRECONNECT_BUFFER: - return AudioTrackFeature::TF_PRECONNECT_BUFFER; - default: - // Defensive fallback – pick something valid instead of UB. - return AudioTrackFeature::TF_STEREO; + case proto::TF_STEREO: + return AudioTrackFeature::TF_STEREO; + case proto::TF_NO_DTX: + return AudioTrackFeature::TF_NO_DTX; + case proto::TF_AUTO_GAIN_CONTROL: + return AudioTrackFeature::TF_AUTO_GAIN_CONTROL; + case proto::TF_ECHO_CANCELLATION: + return AudioTrackFeature::TF_ECHO_CANCELLATION; + case proto::TF_NOISE_SUPPRESSION: + return AudioTrackFeature::TF_NOISE_SUPPRESSION; + case proto::TF_ENHANCED_NOISE_CANCELLATION: + return AudioTrackFeature::TF_ENHANCED_NOISE_CANCELLATION; + case proto::TF_PRECONNECT_BUFFER: + return AudioTrackFeature::TF_PRECONNECT_BUFFER; + default: + // Defensive fallback – pick something valid instead of UB. + return AudioTrackFeature::TF_STEREO; } } -std::vector -convertAudioFeatures(const google::protobuf::RepeatedField &features) { +std::vector convertAudioFeatures(const google::protobuf::RepeatedField& features) { std::vector out; out.reserve(features.size()); for (const int v : features) { @@ -126,18 +123,18 @@ convertAudioFeatures(const google::protobuf::RepeatedField &features) { ParticipantKind fromProto(proto::ParticipantKind in) { switch (in) { - case proto::ParticipantKind::PARTICIPANT_KIND_STANDARD: - return ParticipantKind::Standard; - case proto::ParticipantKind::PARTICIPANT_KIND_INGRESS: - return ParticipantKind::Ingress; - case proto::ParticipantKind::PARTICIPANT_KIND_EGRESS: - return ParticipantKind::Egress; - case proto::ParticipantKind::PARTICIPANT_KIND_SIP: - return ParticipantKind::Sip; - case proto::ParticipantKind::PARTICIPANT_KIND_AGENT: - return ParticipantKind::Agent; - default: - return ParticipantKind::Standard; + case proto::ParticipantKind::PARTICIPANT_KIND_STANDARD: + return ParticipantKind::Standard; + case proto::ParticipantKind::PARTICIPANT_KIND_INGRESS: + return ParticipantKind::Ingress; + case proto::ParticipantKind::PARTICIPANT_KIND_EGRESS: + return ParticipantKind::Egress; + case proto::ParticipantKind::PARTICIPANT_KIND_SIP: + return ParticipantKind::Sip; + case proto::ParticipantKind::PARTICIPANT_KIND_AGENT: + return ParticipantKind::Agent; + default: + return ParticipantKind::Standard; } } diff --git a/src/track_proto_converter.h b/src/track_proto_converter.h index ef4180ca..6d72b556 100644 --- a/src/track_proto_converter.h +++ b/src/track_proto_converter.h @@ -26,13 +26,11 @@ TrackKind fromProto(proto::TrackKind in); StreamState fromProto(proto::StreamState in); TrackSource fromProto(proto::TrackSource in); AudioTrackFeature fromProto(proto::AudioTrackFeature in); -std::vector -convertAudioFeatures(const google::protobuf::RepeatedField &features); +std::vector convertAudioFeatures(const google::protobuf::RepeatedField& features); // Participant Utils ParticipantKind fromProto(proto::ParticipantKind kind); -proto::ParticipantTrackPermission toProto(const ParticipantTrackPermission &in); -ParticipantTrackPermission -fromProto(const proto::ParticipantTrackPermission &in); +proto::ParticipantTrackPermission toProto(const ParticipantTrackPermission& in); +ParticipantTrackPermission fromProto(const proto::ParticipantTrackPermission& in); } // namespace livekit \ No newline at end of file diff --git a/src/track_publication.cpp b/src/track_publication.cpp index 8722a5b1..0eddd1e2 100644 --- a/src/track_publication.cpp +++ b/src/track_publication.cpp @@ -18,15 +18,20 @@ namespace livekit { -TrackPublication::TrackPublication( - FfiHandle handle, std::string sid, std::string name, TrackKind kind, - TrackSource source, bool simulcasted, std::uint32_t width, - std::uint32_t height, std::string mime_type, bool muted, - EncryptionType encryption_type, - std::vector audio_features) - : handle_(std::move(handle)), sid_(std::move(sid)), name_(std::move(name)), - kind_(kind), source_(source), simulcasted_(simulcasted), width_(width), - height_(height), mime_type_(std::move(mime_type)), muted_(muted), +TrackPublication::TrackPublication(FfiHandle handle, std::string sid, std::string name, TrackKind kind, + TrackSource source, bool simulcasted, std::uint32_t width, std::uint32_t height, + std::string mime_type, bool muted, EncryptionType encryption_type, + std::vector audio_features) + : handle_(std::move(handle)), + sid_(std::move(sid)), + name_(std::move(name)), + kind_(kind), + source_(source), + simulcasted_(simulcasted), + width_(width), + height_(height), + mime_type_(std::move(mime_type)), + muted_(muted), encryption_type_(encryption_type), audio_features_(std::move(audio_features)) {} diff --git a/src/video_frame.cpp b/src/video_frame.cpp index 25505397..6f90c6b8 100644 --- a/src/video_frame.cpp +++ b/src/video_frame.cpp @@ -31,68 +31,65 @@ namespace { // Compute total buffer size in bytes for (width, height, type). std::size_t computeBufferSize(int width, int height, VideoBufferType type) { if (width <= 0 || height <= 0) { - throw std::invalid_argument( - "VideoFrame: width and height must be positive"); + throw std::invalid_argument("VideoFrame: width and height must be positive"); } const auto w = static_cast(width); const auto h = static_cast(height); switch (type) { - case VideoBufferType::ARGB: - case VideoBufferType::ABGR: - case VideoBufferType::RGBA: - case VideoBufferType::BGRA: - // 4 bytes per pixel - return w * h * 4; - - case VideoBufferType::RGB24: - case VideoBufferType::I444: - // 3 bytes per pixel (RGB24: packed; I444: Y+U+V all full resolution) - return w * h * 3; - - case VideoBufferType::I420: - // Y (1 byte) + U (1 byte) + V (1 byte) - case VideoBufferType::NV12: { - // Y (1 byte) + UV interleaved (2 bytes per chroma sample) - const std::size_t chroma_w = (w + 1) / 2; - const std::size_t chroma_h = (h + 1) / 2; - return w * h + chroma_w * chroma_h * 2; - } + case VideoBufferType::ARGB: + case VideoBufferType::ABGR: + case VideoBufferType::RGBA: + case VideoBufferType::BGRA: + // 4 bytes per pixel + return w * h * 4; + + case VideoBufferType::RGB24: + case VideoBufferType::I444: + // 3 bytes per pixel (RGB24: packed; I444: Y+U+V all full resolution) + return w * h * 3; + + case VideoBufferType::I420: + // Y (1 byte) + U (1 byte) + V (1 byte) + case VideoBufferType::NV12: { + // Y (1 byte) + UV interleaved (2 bytes per chroma sample) + const std::size_t chroma_w = (w + 1) / 2; + const std::size_t chroma_h = (h + 1) / 2; + return w * h + chroma_w * chroma_h * 2; + } - case VideoBufferType::I010: { - // 16 bits per sample in memory - // Y: 2 bytes per sample, U & V: 2 bytes per sample - const std::size_t chroma_w = (w + 1) / 2; - const std::size_t chroma_h = (h + 1) / 2; - return w * h * 2 + chroma_w * chroma_h * 4; - } + case VideoBufferType::I010: { + // 16 bits per sample in memory + // Y: 2 bytes per sample, U & V: 2 bytes per sample + const std::size_t chroma_w = (w + 1) / 2; + const std::size_t chroma_h = (h + 1) / 2; + return w * h * 2 + chroma_w * chroma_h * 4; + } - case VideoBufferType::I420A: { - // Y full, U & V 2x2, plus alpha full res - const std::size_t chroma_w = (w + 1) / 2; - const std::size_t chroma_h = (h + 1) / 2; - // Y + A are full resolution, U + V subsampled - return w * h * 2 + chroma_w * chroma_h * 2; - } + case VideoBufferType::I420A: { + // Y full, U & V 2x2, plus alpha full res + const std::size_t chroma_w = (w + 1) / 2; + const std::size_t chroma_h = (h + 1) / 2; + // Y + A are full resolution, U + V subsampled + return w * h * 2 + chroma_w * chroma_h * 2; + } - case VideoBufferType::I422: { - // Y full, U & V subsampled horizontally only - const std::size_t chroma_w = (w + 1) / 2; - return w * h + chroma_w * h * 2; - } + case VideoBufferType::I422: { + // Y full, U & V subsampled horizontally only + const std::size_t chroma_w = (w + 1) / 2; + return w * h + chroma_w * h * 2; + } - default: - throw std::runtime_error("VideoFrame: unsupported VideoBufferType"); + default: + throw std::runtime_error("VideoFrame: unsupported VideoBufferType"); } } // Compute plane layout for (base_ptr, width, height, type) -std::vector -computePlaneInfos(uintptr_t base, int width, int height, VideoBufferType type) { +std::vector computePlaneInfos(uintptr_t base, int width, int height, VideoBufferType type) { std::vector planes; if (!base || width <= 0 || height <= 0) { - LK_LOG_WARN("VideoFrame: invalid planeInfos input (ptr={}, w={}, h={})", - base, width, height); + LK_LOG_WARN("VideoFrame: invalid planeInfos input (ptr={}, w={}, h={})", base, width, height); return planes; } const auto w = static_cast(width); @@ -103,165 +100,165 @@ computePlaneInfos(uintptr_t base, int width, int height, VideoBufferType type) { }; switch (type) { - case VideoBufferType::ARGB: - case VideoBufferType::ABGR: - case VideoBufferType::RGBA: - case VideoBufferType::BGRA: { - const uint32_t stride = w * 4; - const uint32_t size = stride * h; - pushPlane(base, stride, size); - break; - } + case VideoBufferType::ARGB: + case VideoBufferType::ABGR: + case VideoBufferType::RGBA: + case VideoBufferType::BGRA: { + const uint32_t stride = w * 4; + const uint32_t size = stride * h; + pushPlane(base, stride, size); + break; + } - case VideoBufferType::RGB24: { - const uint32_t stride = w * 3; - const uint32_t size = stride * h; - pushPlane(base, stride, size); - break; - } + case VideoBufferType::RGB24: { + const uint32_t stride = w * 3; + const uint32_t size = stride * h; + pushPlane(base, stride, size); + break; + } - case VideoBufferType::I420: { - const uint32_t chroma_w = (w + 1) / 2; - const uint32_t chroma_h = (h + 1) / 2; - - // Y - const uint32_t y_stride = w; - const uint32_t y_size = w * h; - const uintptr_t y_ptr = base; - pushPlane(y_ptr, y_stride, y_size); - - // U - const uint32_t u_stride = chroma_w; - const uint32_t u_size = chroma_w * chroma_h; - const uintptr_t u_ptr = y_ptr + y_size; - pushPlane(u_ptr, u_stride, u_size); - - // V - const uint32_t v_stride = chroma_w; - const uint32_t v_size = chroma_w * chroma_h; - const uintptr_t v_ptr = u_ptr + u_size; - pushPlane(v_ptr, v_stride, v_size); - break; - } + case VideoBufferType::I420: { + const uint32_t chroma_w = (w + 1) / 2; + const uint32_t chroma_h = (h + 1) / 2; + + // Y + const uint32_t y_stride = w; + const uint32_t y_size = w * h; + const uintptr_t y_ptr = base; + pushPlane(y_ptr, y_stride, y_size); + + // U + const uint32_t u_stride = chroma_w; + const uint32_t u_size = chroma_w * chroma_h; + const uintptr_t u_ptr = y_ptr + y_size; + pushPlane(u_ptr, u_stride, u_size); + + // V + const uint32_t v_stride = chroma_w; + const uint32_t v_size = chroma_w * chroma_h; + const uintptr_t v_ptr = u_ptr + u_size; + pushPlane(v_ptr, v_stride, v_size); + break; + } - case VideoBufferType::I420A: { - const uint32_t chroma_w = (w + 1) / 2; - const uint32_t chroma_h = (h + 1) / 2; - - // Y - const uint32_t y_stride = w; - const uint32_t y_size = w * h; - const uintptr_t y_ptr = base; - pushPlane(y_ptr, y_stride, y_size); - - // U - const uint32_t u_stride = chroma_w; - const uint32_t u_size = chroma_w * chroma_h; - const uintptr_t u_ptr = y_ptr + y_size; - pushPlane(u_ptr, u_stride, u_size); - - // V - const uint32_t v_stride = chroma_w; - const uint32_t v_size = chroma_w * chroma_h; - const uintptr_t v_ptr = u_ptr + u_size; - pushPlane(v_ptr, v_stride, v_size); - - // A (full res) - const uint32_t a_stride = w; - const uint32_t a_size = w * h; - const uintptr_t a_ptr = v_ptr + v_size; - pushPlane(a_ptr, a_stride, a_size); - break; - } + case VideoBufferType::I420A: { + const uint32_t chroma_w = (w + 1) / 2; + const uint32_t chroma_h = (h + 1) / 2; + + // Y + const uint32_t y_stride = w; + const uint32_t y_size = w * h; + const uintptr_t y_ptr = base; + pushPlane(y_ptr, y_stride, y_size); + + // U + const uint32_t u_stride = chroma_w; + const uint32_t u_size = chroma_w * chroma_h; + const uintptr_t u_ptr = y_ptr + y_size; + pushPlane(u_ptr, u_stride, u_size); + + // V + const uint32_t v_stride = chroma_w; + const uint32_t v_size = chroma_w * chroma_h; + const uintptr_t v_ptr = u_ptr + u_size; + pushPlane(v_ptr, v_stride, v_size); + + // A (full res) + const uint32_t a_stride = w; + const uint32_t a_size = w * h; + const uintptr_t a_ptr = v_ptr + v_size; + pushPlane(a_ptr, a_stride, a_size); + break; + } - case VideoBufferType::I422: { - const uint32_t chroma_w = (w + 1) / 2; - - // Y - const uint32_t y_stride = w; - const uint32_t y_size = w * h; - const uintptr_t y_ptr = base; - pushPlane(y_ptr, y_stride, y_size); - - // U - const uint32_t u_stride = chroma_w; - const uint32_t u_size = chroma_w * h; - const uintptr_t u_ptr = y_ptr + y_size; - pushPlane(u_ptr, u_stride, u_size); - - // V - const uint32_t v_stride = chroma_w; - const uint32_t v_size = chroma_w * h; - const uintptr_t v_ptr = u_ptr + u_size; - pushPlane(v_ptr, v_stride, v_size); - break; - } + case VideoBufferType::I422: { + const uint32_t chroma_w = (w + 1) / 2; + + // Y + const uint32_t y_stride = w; + const uint32_t y_size = w * h; + const uintptr_t y_ptr = base; + pushPlane(y_ptr, y_stride, y_size); + + // U + const uint32_t u_stride = chroma_w; + const uint32_t u_size = chroma_w * h; + const uintptr_t u_ptr = y_ptr + y_size; + pushPlane(u_ptr, u_stride, u_size); + + // V + const uint32_t v_stride = chroma_w; + const uint32_t v_size = chroma_w * h; + const uintptr_t v_ptr = u_ptr + u_size; + pushPlane(v_ptr, v_stride, v_size); + break; + } - case VideoBufferType::I444: { - // All planes full-res - const uint32_t y_stride = w; - const uint32_t y_size = w * h; - const uintptr_t y_ptr = base; - pushPlane(y_ptr, y_stride, y_size); - - const uint32_t u_stride = w; - const uint32_t u_size = w * h; - const uintptr_t u_ptr = y_ptr + y_size; - pushPlane(u_ptr, u_stride, u_size); - - const uint32_t v_stride = w; - const uint32_t v_size = w * h; - const uintptr_t v_ptr = u_ptr + u_size; - pushPlane(v_ptr, v_stride, v_size); - break; - } + case VideoBufferType::I444: { + // All planes full-res + const uint32_t y_stride = w; + const uint32_t y_size = w * h; + const uintptr_t y_ptr = base; + pushPlane(y_ptr, y_stride, y_size); + + const uint32_t u_stride = w; + const uint32_t u_size = w * h; + const uintptr_t u_ptr = y_ptr + y_size; + pushPlane(u_ptr, u_stride, u_size); + + const uint32_t v_stride = w; + const uint32_t v_size = w * h; + const uintptr_t v_ptr = u_ptr + u_size; + pushPlane(v_ptr, v_stride, v_size); + break; + } - case VideoBufferType::I010: { - // 16-bit per sample - const uint32_t chroma_w = (w + 1) / 2; - const uint32_t chroma_h = (h + 1) / 2; - - // Y - const uint32_t y_stride = w * 2; - const uint32_t y_size = w * h * 2; - const uintptr_t y_ptr = base; - pushPlane(y_ptr, y_stride, y_size); - - // U - const uint32_t u_stride = chroma_w * 2; - const uint32_t u_size = chroma_w * chroma_h * 2; - const uintptr_t u_ptr = y_ptr + y_size; - pushPlane(u_ptr, u_stride, u_size); - - // V - const uint32_t v_stride = chroma_w * 2; - const uint32_t v_size = chroma_w * chroma_h * 2; - const uintptr_t v_ptr = u_ptr + u_size; - pushPlane(v_ptr, v_stride, v_size); - break; - } + case VideoBufferType::I010: { + // 16-bit per sample + const uint32_t chroma_w = (w + 1) / 2; + const uint32_t chroma_h = (h + 1) / 2; + + // Y + const uint32_t y_stride = w * 2; + const uint32_t y_size = w * h * 2; + const uintptr_t y_ptr = base; + pushPlane(y_ptr, y_stride, y_size); + + // U + const uint32_t u_stride = chroma_w * 2; + const uint32_t u_size = chroma_w * chroma_h * 2; + const uintptr_t u_ptr = y_ptr + y_size; + pushPlane(u_ptr, u_stride, u_size); + + // V + const uint32_t v_stride = chroma_w * 2; + const uint32_t v_size = chroma_w * chroma_h * 2; + const uintptr_t v_ptr = u_ptr + u_size; + pushPlane(v_ptr, v_stride, v_size); + break; + } - case VideoBufferType::NV12: { - const uint32_t chroma_w = (w + 1) / 2; - const uint32_t chroma_h = (h + 1) / 2; - - // Y - const uint32_t y_stride = w; - const uint32_t y_size = w * h; - const uintptr_t y_ptr = base; - pushPlane(y_ptr, y_stride, y_size); - - // UV interleaved - const uint32_t uv_stride = chroma_w * 2; - const uint32_t uv_size = chroma_w * chroma_h * 2; - const uintptr_t uv_ptr = y_ptr + y_size; - pushPlane(uv_ptr, uv_stride, uv_size); - break; - } + case VideoBufferType::NV12: { + const uint32_t chroma_w = (w + 1) / 2; + const uint32_t chroma_h = (h + 1) / 2; + + // Y + const uint32_t y_stride = w; + const uint32_t y_size = w * h; + const uintptr_t y_ptr = base; + pushPlane(y_ptr, y_stride, y_size); + + // UV interleaved + const uint32_t uv_stride = chroma_w * 2; + const uint32_t uv_size = chroma_w * chroma_h * 2; + const uintptr_t uv_ptr = y_ptr + y_size; + pushPlane(uv_ptr, uv_stride, uv_size); + break; + } - default: - // Unknown or unsupported -> no planes - break; + default: + // Unknown or unsupported -> no planes + break; } return planes; @@ -273,16 +270,15 @@ computePlaneInfos(uintptr_t base, int width, int height, VideoBufferType type) { // VideoFrame implementation // ---------------------------------------------------------------------------- -VideoFrame::VideoFrame() - : width_{0}, height_{0}, type_{VideoBufferType::BGRA}, data_{} {} +VideoFrame::VideoFrame() : width_{0}, height_{0}, type_{VideoBufferType::BGRA}, data_{} {} -VideoFrame::VideoFrame(int width, int height, VideoBufferType type, - std::vector data) +VideoFrame::VideoFrame(int width, int height, VideoBufferType type, std::vector data) : width_(width), height_(height), type_(type), data_(std::move(data)) { const std::size_t expected = computeBufferSize(width_, height_, type_); if (data_.size() < expected) { - throw std::invalid_argument("VideoFrame: provided data is too small for " - "the specified format and size"); + throw std::invalid_argument( + "VideoFrame: provided data is too small for " + "the specified format and size"); } } @@ -317,8 +313,8 @@ VideoFrame VideoFrame::convert(VideoBufferType dst, bool flip_y) const { return convertViaFfi(*this, dst, flip_y); } -VideoFrame VideoFrame::fromOwnedInfo(const proto::OwnedVideoBuffer &owned) { - const auto &info = owned.info(); +VideoFrame VideoFrame::fromOwnedInfo(const proto::OwnedVideoBuffer& owned) { + const auto& info = owned.info(); const int width = static_cast(info.width()); const int height = static_cast(info.height()); const VideoBufferType type = fromProto(info.type()); @@ -328,28 +324,31 @@ VideoFrame VideoFrame::fromOwnedInfo(const proto::OwnedVideoBuffer &owned) { if (info.components_size() > 0) { // Multi-plane (e.g. I420, NV12, etc.). We pack planes back-to-back. std::size_t total_size = 0; - for (const auto &comp : info.components()) { + for (const auto& comp : info.components()) { total_size += static_cast(comp.size()); } buffer.resize(total_size); std::size_t offset = 0; - for (const auto &comp : info.components()) { + for (const auto& comp : info.components()) { const auto sz = static_cast(comp.size()); - const auto *src_ptr = reinterpret_cast(comp.data_ptr()); // NOLINT(performance-no-int-to-ptr) + const auto* src_ptr = + // NOLINTNEXTLINE(performance-no-int-to-ptr) + reinterpret_cast(comp.data_ptr()); std::memcpy(buffer.data() + offset, src_ptr, sz); offset += sz; } } else { // Packed format: treat top-level data_ptr as a single contiguous buffer. - const auto *src_ptr = reinterpret_cast(info.data_ptr()); // NOLINT(performance-no-int-to-ptr) + const auto* src_ptr = + // NOLINTNEXTLINE(performance-no-int-to-ptr) + reinterpret_cast(info.data_ptr()); std::size_t total_size = 0; if (info.has_stride()) { // Use stride * height as total size (includes per-row padding if any). - total_size = static_cast(info.stride()) * - static_cast(height); + total_size = static_cast(info.stride()) * static_cast(height); } else { // Use our generic buffer-size helper (width/height/type). total_size = computeBufferSize(width, height, type); @@ -361,8 +360,7 @@ VideoFrame VideoFrame::fromOwnedInfo(const proto::OwnedVideoBuffer &owned) { // Release the FFI-owned buffer after copying the data. { - const FfiHandle owned_handle( - static_cast(owned.handle().id())); + const FfiHandle owned_handle(static_cast(owned.handle().id())); // owned_handle destroyed at end of scope → native buffer disposed. } diff --git a/src/video_source.cpp b/src/video_source.cpp index edcd7680..468b900d 100644 --- a/src/video_source.cpp +++ b/src/video_source.cpp @@ -26,11 +26,9 @@ namespace livekit { -VideoSource::VideoSource(int width, int height) - : width_(width), height_(height) { - +VideoSource::VideoSource(int width, int height) : width_(width), height_(height) { proto::FfiRequest req; - auto *msg = req.mutable_new_video_source(); + auto* msg = req.mutable_new_video_source(); msg->set_type(proto::VideoSourceType::VIDEO_SOURCE_NATIVE); msg->mutable_resolution()->set_width(width_); msg->mutable_resolution()->set_height(height_); @@ -43,15 +41,14 @@ VideoSource::VideoSource(int width, int height) handle_ = FfiHandle(resp.new_video_source().source().handle().id()); } -void VideoSource::captureFrame(const VideoFrame &frame, - const VideoCaptureOptions &options) { +void VideoSource::captureFrame(const VideoFrame& frame, const VideoCaptureOptions& options) { if (!handle_) { return; } const proto::VideoBufferInfo buf = toProto(frame); proto::FfiRequest req; - auto *msg = req.mutable_capture_video_frame(); + auto* msg = req.mutable_capture_video_frame(); msg->set_source_handle(handle_.get()); msg->mutable_buffer()->CopyFrom(buf); msg->set_timestamp_us(options.timestamp_us); @@ -65,9 +62,7 @@ void VideoSource::captureFrame(const VideoFrame &frame, } } -void VideoSource::captureFrame(const VideoFrame &frame, - std::int64_t timestamp_us, - VideoRotation rotation) { +void VideoSource::captureFrame(const VideoFrame& frame, std::int64_t timestamp_us, VideoRotation rotation) { captureFrame(frame, VideoCaptureOptions{timestamp_us, rotation}); } diff --git a/src/video_stream.cpp b/src/video_stream.cpp index a3bce022..db08a685 100644 --- a/src/video_stream.cpp +++ b/src/video_stream.cpp @@ -31,17 +31,14 @@ using proto::FfiEvent; using proto::FfiRequest; using proto::VideoStreamEvent; -std::shared_ptr -VideoStream::fromTrack(const std::shared_ptr &track, - const Options &options) { +std::shared_ptr VideoStream::fromTrack(const std::shared_ptr& track, const Options& options) { auto stream = std::shared_ptr(new VideoStream()); stream->initFromTrack(track, options); return stream; } -std::shared_ptr -VideoStream::fromParticipant(Participant &participant, TrackSource track_source, - const Options &options) { +std::shared_ptr VideoStream::fromParticipant(Participant& participant, TrackSource track_source, + const Options& options) { auto stream = std::shared_ptr(new VideoStream()); stream->initFromParticipant(participant, track_source, options); return stream; @@ -49,7 +46,7 @@ VideoStream::fromParticipant(Participant &participant, TrackSource track_source, VideoStream::~VideoStream() { close(); } -VideoStream::VideoStream(VideoStream &&other) noexcept { +VideoStream::VideoStream(VideoStream&& other) noexcept { const std::scoped_lock lock(other.mutex_); queue_ = std::move(other.queue_); capacity_ = other.capacity_; @@ -62,9 +59,10 @@ VideoStream::VideoStream(VideoStream &&other) noexcept { other.closed_ = true; } -VideoStream &VideoStream::operator=(VideoStream &&other) noexcept { - if (this == &other) +VideoStream& VideoStream::operator=(VideoStream&& other) noexcept { + if (this == &other) { return *this; + } close(); @@ -88,7 +86,7 @@ VideoStream &VideoStream::operator=(VideoStream &&other) noexcept { // --------------------- Public API --------------------- -bool VideoStream::read(VideoFrameEvent &out) { +bool VideoStream::read(VideoFrameEvent& out) { std::unique_lock lock(mutex_); cv_.wait(lock, [this] { return !queue_.empty() || eof_ || closed_; }); @@ -128,48 +126,41 @@ void VideoStream::close() { // --------------------- Internal helpers --------------------- -void VideoStream::initFromTrack(const std::shared_ptr &track, - const Options &options) { +void VideoStream::initFromTrack(const std::shared_ptr& track, const Options& options) { capacity_ = options.capacity; // Subscribe to FFI events, this is essential to get video frames from FFI. - listener_id_ = FfiClient::instance().AddListener( - [this](const proto::FfiEvent &e) { this->onFfiEvent(e); }); + listener_id_ = FfiClient::instance().AddListener([this](const proto::FfiEvent& e) { this->onFfiEvent(e); }); // Send FFI request to create a new video stream bound to this track FfiRequest req; - auto *new_video_stream = req.mutable_new_video_stream(); - new_video_stream->set_track_handle( - static_cast(track->ffi_handle_id())); + auto* new_video_stream = req.mutable_new_video_stream(); + new_video_stream->set_track_handle(static_cast(track->ffi_handle_id())); new_video_stream->set_type(proto::VideoStreamType::VIDEO_STREAM_NATIVE); new_video_stream->set_normalize_stride(true); new_video_stream->set_format(toProto(options.format)); auto resp = FfiClient::instance().sendRequest(req); if (!resp.has_new_video_stream()) { - LK_LOG_ERROR( - "VideoStream::initFromTrack: FFI response missing new_video_stream()"); + LK_LOG_ERROR("VideoStream::initFromTrack: FFI response missing new_video_stream()"); throw std::runtime_error("new_video_stream FFI request failed"); } // Adjust field names to match your proto exactly: - const auto &stream = resp.new_video_stream().stream(); + const auto& stream = resp.new_video_stream().stream(); stream_handle_ = FfiHandle(static_cast(stream.handle().id())); // TODO, do we need to cache the metadata from stream.info ? } -void VideoStream::initFromParticipant(Participant &participant, - TrackSource track_source, - const Options &options) { +void VideoStream::initFromParticipant(Participant& participant, TrackSource track_source, const Options& options) { capacity_ = options.capacity; // 1) Subscribe to FFI events - listener_id_ = FfiClient::instance().AddListener( - [this](const FfiEvent &e) { this->onFfiEvent(e); }); + listener_id_ = FfiClient::instance().AddListener([this](const FfiEvent& e) { this->onFfiEvent(e); }); // 2) Send FFI request to create a video stream from participant + track // source FfiRequest req; - auto *vs = req.mutable_video_stream_from_participant(); + auto* vs = req.mutable_video_stream_from_participant(); vs->set_participant_handle(participant.ffiHandleId()); vs->set_type(proto::VideoStreamType::VIDEO_STREAM_NATIVE); vs->set_track_source(static_cast(track_source)); @@ -178,23 +169,23 @@ void VideoStream::initFromParticipant(Participant &participant, auto resp = FfiClient::instance().sendRequest(req); // Adjust field names to match your proto exactly: - const auto &stream = resp.video_stream_from_participant().stream(); + const auto& stream = resp.video_stream_from_participant().stream(); stream_handle_ = FfiHandle(static_cast(stream.handle().id())); } -void VideoStream::onFfiEvent(const proto::FfiEvent &event) { +void VideoStream::onFfiEvent(const proto::FfiEvent& event) { // Filter for video_stream_event first. if (event.message_case() != FfiEvent::kVideoStreamEvent) { return; } - const auto &vse = event.video_stream_event(); + const auto& vse = event.video_stream_event(); // Check if this event is for our stream handle. if (vse.stream_handle() != static_cast(stream_handle_.get())) { return; } // Handle frame_received or eos. if (vse.has_frame_received()) { - const auto &fr = vse.frame_received(); + const auto& fr = vse.frame_received(); // Convert owned buffer->VideoFrame via a helper. // You should implement this static function in your VideoFrame class. @@ -211,7 +202,7 @@ void VideoStream::onFfiEvent(const proto::FfiEvent &event) { } } -void VideoStream::pushFrame(VideoFrameEvent &&ev) { +void VideoStream::pushFrame(VideoFrameEvent&& ev) { { const std::scoped_lock lock(mutex_); diff --git a/src/video_utils.cpp b/src/video_utils.cpp index 4f510ade..19bd4300 100644 --- a/src/video_utils.cpp +++ b/src/video_utils.cpp @@ -29,65 +29,64 @@ namespace livekit { proto::VideoBufferType toProto(const VideoBufferType t) { switch (t) { - case VideoBufferType::ARGB: - return proto::VideoBufferType::ARGB; - case VideoBufferType::ABGR: - return proto::VideoBufferType::ABGR; - case VideoBufferType::RGBA: - return proto::VideoBufferType::RGBA; - case VideoBufferType::BGRA: - return proto::VideoBufferType::BGRA; - case VideoBufferType::RGB24: - return proto::VideoBufferType::RGB24; - case VideoBufferType::I420: - return proto::VideoBufferType::I420; - case VideoBufferType::I420A: - return proto::VideoBufferType::I420A; - case VideoBufferType::I422: - return proto::VideoBufferType::I422; - case VideoBufferType::I444: - return proto::VideoBufferType::I444; - case VideoBufferType::I010: - return proto::VideoBufferType::I010; - case VideoBufferType::NV12: - return proto::VideoBufferType::NV12; - default: - throw std::runtime_error("Unknown VideoBufferType in toProto"); + case VideoBufferType::ARGB: + return proto::VideoBufferType::ARGB; + case VideoBufferType::ABGR: + return proto::VideoBufferType::ABGR; + case VideoBufferType::RGBA: + return proto::VideoBufferType::RGBA; + case VideoBufferType::BGRA: + return proto::VideoBufferType::BGRA; + case VideoBufferType::RGB24: + return proto::VideoBufferType::RGB24; + case VideoBufferType::I420: + return proto::VideoBufferType::I420; + case VideoBufferType::I420A: + return proto::VideoBufferType::I420A; + case VideoBufferType::I422: + return proto::VideoBufferType::I422; + case VideoBufferType::I444: + return proto::VideoBufferType::I444; + case VideoBufferType::I010: + return proto::VideoBufferType::I010; + case VideoBufferType::NV12: + return proto::VideoBufferType::NV12; + default: + throw std::runtime_error("Unknown VideoBufferType in toProto"); } } // Map proto enum -> SDK enum VideoBufferType fromProto(const proto::VideoBufferType t) { switch (t) { - case proto::VideoBufferType::ARGB: - return VideoBufferType::ARGB; - case proto::VideoBufferType::ABGR: - return VideoBufferType::ABGR; - case proto::VideoBufferType::RGBA: - return VideoBufferType::RGBA; - case proto::VideoBufferType::BGRA: - return VideoBufferType::BGRA; - case proto::VideoBufferType::RGB24: - return VideoBufferType::RGB24; - case proto::VideoBufferType::I420: - return VideoBufferType::I420; - case proto::VideoBufferType::I420A: - return VideoBufferType::I420A; - case proto::VideoBufferType::I422: - return VideoBufferType::I422; - case proto::VideoBufferType::I444: - return VideoBufferType::I444; - case proto::VideoBufferType::I010: - return VideoBufferType::I010; - case proto::VideoBufferType::NV12: - return VideoBufferType::NV12; - default: - throw std::runtime_error("Unknown proto::VideoBufferType in fromProto"); + case proto::VideoBufferType::ARGB: + return VideoBufferType::ARGB; + case proto::VideoBufferType::ABGR: + return VideoBufferType::ABGR; + case proto::VideoBufferType::RGBA: + return VideoBufferType::RGBA; + case proto::VideoBufferType::BGRA: + return VideoBufferType::BGRA; + case proto::VideoBufferType::RGB24: + return VideoBufferType::RGB24; + case proto::VideoBufferType::I420: + return VideoBufferType::I420; + case proto::VideoBufferType::I420A: + return VideoBufferType::I420A; + case proto::VideoBufferType::I422: + return VideoBufferType::I422; + case proto::VideoBufferType::I444: + return VideoBufferType::I444; + case proto::VideoBufferType::I010: + return VideoBufferType::I010; + case proto::VideoBufferType::NV12: + return VideoBufferType::NV12; + default: + throw std::runtime_error("Unknown proto::VideoBufferType in fromProto"); } } -std::optional -toProto(const std::optional &metadata) { +std::optional toProto(const std::optional& metadata) { if (!metadata.has_value()) { return std::nullopt; } @@ -107,8 +106,7 @@ toProto(const std::optional &metadata) { return proto_metadata; } -std::optional -fromProto(const proto::FrameMetadata &metadata) { +std::optional fromProto(const proto::FrameMetadata& metadata) { VideoFrameMetadata out; if (metadata.has_user_timestamp()) { out.user_timestamp_us = metadata.user_timestamp(); @@ -124,7 +122,7 @@ fromProto(const proto::FrameMetadata &metadata) { return out; } -proto::VideoBufferInfo toProto(const VideoFrame &frame) { +proto::VideoBufferInfo toProto(const VideoFrame& frame) { proto::VideoBufferInfo info; const int w = frame.width(); @@ -139,8 +137,8 @@ proto::VideoBufferInfo toProto(const VideoFrame &frame) { // Compute plane layout for the current format const auto planes = frame.planeInfos(); - for (const auto &plane : planes) { - auto *cmpt = info.add_components(); + for (const auto& plane : planes) { + auto* cmpt = info.add_components(); cmpt->set_data_ptr(static_cast(plane.data_ptr)); cmpt->set_stride(plane.stride); cmpt->set_size(plane.size); @@ -149,25 +147,25 @@ proto::VideoBufferInfo toProto(const VideoFrame &frame) { // Stride for main packed formats. std::uint32_t stride = 0; switch (frame.type()) { - case VideoBufferType::ARGB: - case VideoBufferType::ABGR: - case VideoBufferType::RGBA: - case VideoBufferType::BGRA: - stride = static_cast(w) * 4; - break; - case VideoBufferType::RGB24: - stride = static_cast(w) * 3; - break; - default: - stride = 0; // not used / unknown for planar formats - break; + case VideoBufferType::ARGB: + case VideoBufferType::ABGR: + case VideoBufferType::RGBA: + case VideoBufferType::BGRA: + stride = static_cast(w) * 4; + break; + case VideoBufferType::RGB24: + stride = static_cast(w) * 3; + break; + default: + stride = 0; // not used / unknown for planar formats + break; } info.set_stride(stride); return info; } -VideoFrame fromOwnedProto(const proto::OwnedVideoBuffer &owned) { - const auto &info = owned.info(); +VideoFrame fromOwnedProto(const proto::OwnedVideoBuffer& owned) { + const auto& info = owned.info(); const int width = static_cast(info.width()); const int height = static_cast(info.height()); @@ -177,14 +175,15 @@ VideoFrame fromOwnedProto(const proto::OwnedVideoBuffer &owned) { VideoFrame frame = VideoFrame::create(width, height, type); // Copy from the FFI-provided buffer into our own backing storage - auto *dst = frame.data(); + auto* dst = frame.data(); const std::size_t dst_size = frame.dataSize(); const auto src_ptr = info.data_ptr(); if (src_ptr == 0) { throw std::runtime_error("fromOwnedProto: info.data_ptr is null"); } - const auto *src = reinterpret_cast(src_ptr); // NOLINT(performance-no-int-to-ptr) + // NOLINTNEXTLINE(performance-no-int-to-ptr) + const auto* src = reinterpret_cast(src_ptr); std::memcpy(dst, src, dst_size); @@ -197,20 +196,18 @@ VideoFrame fromOwnedProto(const proto::OwnedVideoBuffer &owned) { return frame; } -VideoFrame convertViaFfi(const VideoFrame &frame, VideoBufferType dst, - bool flip_y) { +VideoFrame convertViaFfi(const VideoFrame& frame, VideoBufferType dst, bool flip_y) { proto::FfiRequest req; - auto *vc = req.mutable_video_convert(); + auto* vc = req.mutable_video_convert(); vc->set_flip_y(flip_y); vc->set_dst_type(toProto(dst)); vc->mutable_buffer()->CopyFrom(toProto(frame)); const proto::FfiResponse resp = FfiClient::instance().sendRequest(req); if (!resp.has_video_convert()) { - throw std::runtime_error( - "convertViaFfi: FfiResponse missing video_convert"); + throw std::runtime_error("convertViaFfi: FfiResponse missing video_convert"); } - const auto &vc_resp = resp.video_convert(); + const auto& vc_resp = resp.video_convert(); if (!vc_resp.error().empty()) { throw std::runtime_error("convertViaFfi: " + vc_resp.error()); } diff --git a/src/video_utils.h b/src/video_utils.h index fe273d66..c2c9bcfb 100644 --- a/src/video_utils.h +++ b/src/video_utils.h @@ -23,15 +23,12 @@ namespace livekit { // Video FFI Utils -proto::VideoBufferInfo toProto(const VideoFrame &frame); -VideoFrame fromOwnedProto(const proto::OwnedVideoBuffer &owned); -VideoFrame convertViaFfi(const VideoFrame &frame, VideoBufferType dst, - bool flip_y); +proto::VideoBufferInfo toProto(const VideoFrame& frame); +VideoFrame fromOwnedProto(const proto::OwnedVideoBuffer& owned); +VideoFrame convertViaFfi(const VideoFrame& frame, VideoBufferType dst, bool flip_y); proto::VideoBufferType toProto(const VideoBufferType t); VideoBufferType fromProto(const proto::VideoBufferType t); -std::optional -toProto(const std::optional &metadata); -std::optional -fromProto(const proto::FrameMetadata &metadata); +std::optional toProto(const std::optional& metadata); +std::optional fromProto(const proto::FrameMetadata& metadata); } // namespace livekit