diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..e04cbbb --- /dev/null +++ b/.clang-format @@ -0,0 +1,24 @@ +--- +BasedOnStyle: LLVM +Language: Cpp +Standard: c++20 +ColumnLimit: 120 +IndentWidth: 2 +ContinuationIndentWidth: 4 +NamespaceIndentation: All +AccessModifierOffset: -2 +PointerAlignment: Right +QualifierAlignment: Left +ReferenceAlignment: Right +AllowShortFunctionsOnASingleLine: Inline +AllowShortLambdasOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false +AlwaysBreakTemplateDeclarations: Yes +BreakBeforeBraces: Attach +InsertBraces: true +IncludeBlocks: Preserve +SortIncludes: CaseSensitive +FixNamespaceComments: true +SpaceAfterTemplateKeyword: true +... diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..d7ff6bb --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,48 @@ +--- +# Every disabled check has a reason; everything else is fixed, or NOLINT(check) with a reason where it can't be. +Checks: > + -*, + bugprone-*, + cert-*, + clang-analyzer-*, + concurrency-*, + cppcoreguidelines-*, + misc-*, + modernize-*, + performance-*, + portability-*, + readability-*, + -modernize-use-trailing-return-type, + -misc-include-cleaner, + -misc-multiple-inheritance, + -cppcoreguidelines-owning-memory, + -cppcoreguidelines-pro-bounds-pointer-arithmetic, + -cppcoreguidelines-pro-bounds-constant-array-index, + -cppcoreguidelines-pro-bounds-avoid-unchecked-container-access, + -cppcoreguidelines-pro-type-static-cast-downcast + +# modernize-use-trailing-return-type: libwebrtc and pybind11 APIs, which we override and mirror, use leading returns +# misc-include-cleaner: pybind11 and libwebrtc are used through umbrella headers the check maps to internal ones +# misc-multiple-inheritance: libwebrtc observer interfaces (not pure virtual) are implemented next to our bases +# cppcoreguidelines-owning-memory: libwebrtc takes raw `new` into its refcounting, and there's no GSL owner<> +# cppcoreguidelines-pro-bounds-*: libwebrtc frames and audio are raw planes with strides, indexed per sample +# cppcoreguidelines-pro-type-static-cast-downcast: libwebrtc has no RTTI, tracks are downcast by kind() as it does + +WarningsAsErrors: '*' +HeaderFilterRegex: '.*/python-webrtc/cpp/src/.*' +FormatStyle: file + +CheckOptions: + # running out of memory terminates anyway, and so does calling an empty std::function, a bug + bugprone-exception-escape.IgnoredExceptions: 'bad_alloc,bad_array_new_length,length_error,bad_function_call' + # pybind11 dataclass-like models (def_readwrite) are plain structs + misc-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic: true + # `m` for the pybind11 module, `pc` for a peer connection, coordinates and iterators + readability-identifier-length.IgnoredVariableNames: '^(m|pc|id|it|x|y|u|v|uv|i|j)$' + readability-identifier-length.IgnoredParameterNames: '^(m|pc|id|it|x|y|u|v|uv|i|j)$' + readability-identifier-length.IgnoredLoopCounterNames: '^[ijkxy_]$' + # a width and a height, in the order everything takes them + bugprone-easily-swappable-parameters.IgnoredParameterNames: '"";iterator;Iterator;begin;Begin;end;End;first;First;last;Last;lhs;LHS;rhs;RHS;width;height' + cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor: true + cppcoreguidelines-special-member-functions.AllowMissingMoveFunctionsWhenCopyIsDeleted: true +... diff --git a/.github/scripts/tidy.sh b/.github/scripts/tidy.sh new file mode 100755 index 0000000..1af4bf2 --- /dev/null +++ b/.github/scripts/tidy.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash +# +# Runs clang-tidy (see .clang-tidy) over the extension sources, against a configured (not built) tree in build/tidy. +# +# .github/scripts/tidy.sh [run-clang-tidy arguments, e.g. -fix or a file regex] +# +# The LLVM version is pinned to match clang-format in the Makefile. + +set -euo pipefail + +SRC="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +BUILD="${WRTC_TIDY_BUILD_DIR:-$SRC/build/tidy}" +LLVM_VERSION=22.1.8 + +if [ ! -x "$BUILD/venv/bin/python" ]; then + uv venv -q "$BUILD/venv" --python 3.13 +fi +uv pip install -q --python "$BUILD/venv/bin/python" cmake ninja "pybind11>=3.0" "clang-tidy==$LLVM_VERSION" +export PATH="$BUILD/venv/bin:$PATH" + +# libwebrtc for Linux is compiled against Chromium's libc++, which needs Clang +if [ "$(uname)" = Linux ]; then + export CC="${CC:-clang}" CXX="${CXX:-clang++}" +fi +cmake -S "$SRC" -B "$BUILD" -G Ninja \ + -DCMAKE_BUILD_TYPE=Debug \ + -DPython_EXECUTABLE="$BUILD/venv/bin/python" \ + -Dpybind11_DIR="$("$BUILD/venv/bin/python" -m pybind11 --cmakedir)" > /dev/null + +EXTRA=() +if [ "$(uname)" = Darwin ]; then + # the wheel's clang doesn't know the SDK Apple Clang uses implicitly + EXTRA+=(-extra-arg=-isysroot"$(xcrun --show-sdk-path)") +fi + +cd "$SRC" +python "$BUILD/venv/bin/run-clang-tidy.py" -p "$BUILD" -quiet -j "$(getconf _NPROCESSORS_ONLN)" \ + ${EXTRA[@]+"${EXTRA[@]}"} "$@" "$SRC/python-webrtc/cpp/src/" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a48fc6f..e46255f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,11 +20,20 @@ permissions: jobs: lint: runs-on: ubuntu-latest + env: + WRTC_CACHE_DIR: ${{ github.workspace }}/.wrtc-cache steps: - uses: actions/checkout@v7 - uses: astral-sh/setup-uv@v7 - run: uvx ruff check - run: uvx ruff format --check + - run: make format-check + # clang-tidy reads the libwebrtc headers, from the cache the wheels use + - uses: actions/cache@v6 + with: + path: .wrtc-cache + key: libwebrtc-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('cmake/libwebrtc.cmake', 'cmake/libcxx/*.sha256') }} + - run: make tidy sdist: runs-on: ubuntu-latest diff --git a/CMakeLists.txt b/CMakeLists.txt index e1001aa..97e0ee1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,6 +15,8 @@ project(python_webrtc set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) +# for clang-tidy (make tidy) and editors +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) if(MSVC) # libwebrtc is linked against the static release CRT set(CMAKE_MSVC_RUNTIME_LIBRARY MultiThreaded) diff --git a/Makefile b/Makefile index 8f2e971..1a299c3 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,8 @@ -.PHONY: dev test asan tsan lint format stub wheels doc clean +.PHONY: dev test asan tsan lint format format-check tidy stub wheels doc clean + +# pinned to the clang-tidy of .github/scripts/tidy.sh +CLANG_FORMAT := uvx clang-format==22.1.8 +CPP_SRC = $(shell find python-webrtc/cpp/src -name '*.cpp' -o -name '*.h') # editable install; the extension is rebuilt automatically on import after C++ changes dev: @@ -16,13 +20,21 @@ asan: tsan: SANITIZE=thread .github/scripts/sanitizers-macos.sh $(O) -lint: +lint: format-check uvx ruff check uvx ruff format --check format: uvx ruff check --fix uvx ruff format + $(CLANG_FORMAT) -i $(CPP_SRC) + +format-check: + @$(CLANG_FORMAT) --dry-run --Werror $(CPP_SRC) + +# clang-tidy over the extension, see .clang-tidy +tidy: + .github/scripts/tidy.sh $(O) stub: uv run --no-sync pybind11-stubgen wrtc -o build/stubs diff --git a/python-webrtc/cpp/src/enums/enums.h b/python-webrtc/cpp/src/enums/enums.h index d2f7654..ea0792c 100644 --- a/python-webrtc/cpp/src/enums/enums.h +++ b/python-webrtc/cpp/src/enums/enums.h @@ -5,8 +5,11 @@ // that can be found in the LICENSE.md file in the root of the project. // -#pragma once +#ifndef PYTHON_WEBRTC_ENUMS_ENUMS_H_ +#define PYTHON_WEBRTC_ENUMS_ENUMS_H_ +#include +#include #include #include #include @@ -32,48 +35,54 @@ namespace python_webrtc { - enum RTCIceComponent { kRtp, kRtcp }; + enum class RTCIceComponent : uint8_t { kRtp, kRtcp }; // the media type of a kind of track ("audio" or "video") std::optional mediaTypeOf(const std::string &kind); // A C++ enum that Python sees as the str enum `name` of webrtc.enums, converted by the values listed here. // Every file that binds one must include this header, or pybind11 silently uses its own caster. - template struct StrEnum {}; + template + struct StrEnum {}; - template struct IsStrEnum : std::false_type {}; - template struct IsStrEnum::values)>> : std::true_type {}; + template + struct IsStrEnum : std::false_type {}; + template + struct IsStrEnum::values)>> : std::true_type {}; - template<> struct StrEnum { + template <> + struct StrEnum { using State = webrtc::PeerConnectionInterface::PeerConnectionState; - static constexpr char name[] = "RTCPeerConnectionState"; - static constexpr std::pair values[] = { + static constexpr auto name = pybind11::detail::const_name("RTCPeerConnectionState"); + static constexpr auto values = std::to_array>({ {State::kNew, "new"}, {State::kConnecting, "connecting"}, {State::kConnected, "connected"}, {State::kDisconnected, "disconnected"}, {State::kFailed, "failed"}, {State::kClosed, "closed"}, - }; + }); }; - template<> struct StrEnum { + template <> + struct StrEnum { using State = webrtc::PeerConnectionInterface::SignalingState; - static constexpr char name[] = "RTCSignalingState"; - static constexpr std::pair values[] = { + static constexpr auto name = pybind11::detail::const_name("RTCSignalingState"); + static constexpr auto values = std::to_array>({ {State::kStable, "stable"}, {State::kHaveLocalOffer, "have-local-offer"}, {State::kHaveLocalPrAnswer, "have-local-pranswer"}, {State::kHaveRemoteOffer, "have-remote-offer"}, {State::kHaveRemotePrAnswer, "have-remote-pranswer"}, {State::kClosed, "closed"}, - }; + }); }; - template<> struct StrEnum { + template <> + struct StrEnum { using State = webrtc::PeerConnectionInterface::IceConnectionState; - static constexpr char name[] = "RTCIceConnectionState"; - static constexpr std::pair values[] = { + static constexpr auto name = pybind11::detail::const_name("RTCIceConnectionState"); + static constexpr auto values = std::to_array>({ {State::kIceConnectionNew, "new"}, {State::kIceConnectionChecking, "checking"}, {State::kIceConnectionConnected, "connected"}, @@ -82,81 +91,89 @@ namespace python_webrtc { {State::kIceConnectionDisconnected, "disconnected"}, {State::kIceConnectionClosed, "closed"}, {State::kIceConnectionMax, "max"}, - }; + }); }; - template<> struct StrEnum { + template <> + struct StrEnum { using State = webrtc::PeerConnectionInterface::IceGatheringState; - static constexpr char name[] = "RTCIceGatheringState"; - static constexpr std::pair values[] = { + static constexpr auto name = pybind11::detail::const_name("RTCIceGatheringState"); + static constexpr auto values = std::to_array>({ {State::kIceGatheringNew, "new"}, {State::kIceGatheringGathering, "gathering"}, {State::kIceGatheringComplete, "complete"}, - }; + }); }; - template<> struct StrEnum { - static constexpr char name[] = "RTCSdpType"; - static constexpr std::pair values[] = { + template <> + struct StrEnum { + static constexpr auto name = pybind11::detail::const_name("RTCSdpType"); + static constexpr auto values = std::to_array>({ {webrtc::SdpType::kOffer, "offer"}, {webrtc::SdpType::kPrAnswer, "pranswer"}, {webrtc::SdpType::kAnswer, "answer"}, {webrtc::SdpType::kRollback, "rollback"}, - }; + }); }; - template<> struct StrEnum { + template <> + struct StrEnum { using State = webrtc::MediaStreamTrackInterface::TrackState; - static constexpr char name[] = "MediaStreamTrackState"; - static constexpr std::pair values[] = { + static constexpr auto name = pybind11::detail::const_name("MediaStreamTrackState"); + static constexpr auto values = std::to_array>({ {State::kLive, "live"}, {State::kEnded, "ended"}, - }; + }); }; - template<> struct StrEnum { + template <> + struct StrEnum { using Direction = webrtc::RtpTransceiverDirection; - static constexpr char name[] = "TransceiverDirection"; - static constexpr std::pair values[] = { + static constexpr auto name = pybind11::detail::const_name("TransceiverDirection"); + static constexpr auto values = std::to_array>({ {Direction::kSendRecv, "sendrecv"}, {Direction::kSendOnly, "sendonly"}, {Direction::kRecvOnly, "recvonly"}, {Direction::kInactive, "inactive"}, {Direction::kStopped, "stopped"}, - }; + }); }; - template<> struct StrEnum { - static constexpr char name[] = "MediaType"; - static constexpr std::pair values[] = { + template <> + struct StrEnum { + static constexpr auto name = pybind11::detail::const_name("MediaType"); + static constexpr auto values = std::to_array>({ {webrtc::MediaType::AUDIO, "audio"}, {webrtc::MediaType::VIDEO, "video"}, {webrtc::MediaType::DATA, "data"}, {webrtc::MediaType::UNSUPPORTED, "unsupported"}, - }; + }); }; - template<> struct StrEnum { - static constexpr char name[] = "RTCIceComponent"; - static constexpr std::pair values[] = { + template <> + struct StrEnum { + static constexpr auto name = pybind11::detail::const_name("RTCIceComponent"); + static constexpr auto values = std::to_array>({ {RTCIceComponent::kRtp, "rtp"}, {RTCIceComponent::kRtcp, "rtcp"}, - }; + }); }; - template<> struct StrEnum { - static constexpr char name[] = "RTCIceRole"; - static constexpr std::pair values[] = { + template <> + struct StrEnum { + static constexpr auto name = pybind11::detail::const_name("RTCIceRole"); + static constexpr auto values = std::to_array>({ {webrtc::IceRole::ICEROLE_CONTROLLING, "controlling"}, {webrtc::IceRole::ICEROLE_CONTROLLED, "controlled"}, {webrtc::IceRole::ICEROLE_UNKNOWN, "unknown"}, - }; + }); }; - template<> struct StrEnum { + template <> + struct StrEnum { using State = webrtc::IceTransportState; - static constexpr char name[] = "RTCIceTransportState"; - static constexpr std::pair values[] = { + static constexpr auto name = pybind11::detail::const_name("RTCIceTransportState"); + static constexpr auto values = std::to_array>({ {State::kNew, "new"}, {State::kChecking, "checking"}, {State::kConnected, "connected"}, @@ -164,78 +181,85 @@ namespace python_webrtc { {State::kDisconnected, "disconnected"}, {State::kFailed, "failed"}, {State::kClosed, "closed"}, - }; + }); }; - template<> struct StrEnum { - static constexpr char name[] = "CricketIceGatheringState"; - static constexpr std::pair values[] = { + template <> + struct StrEnum { + static constexpr auto name = pybind11::detail::const_name("CricketIceGatheringState"); + static constexpr auto values = std::to_array>({ {webrtc::IceGatheringState::kIceGatheringNew, "new"}, {webrtc::IceGatheringState::kIceGatheringGathering, "gathering"}, {webrtc::IceGatheringState::kIceGatheringComplete, "complete"}, - }; + }); }; - template<> struct StrEnum { + template <> + struct StrEnum { using State = webrtc::DtlsTransportState; - static constexpr char name[] = "DtlsTransportState"; - static constexpr std::pair values[] = { + static constexpr auto name = pybind11::detail::const_name("DtlsTransportState"); + static constexpr auto values = std::to_array>({ {State::kNew, "new"}, {State::kConnecting, "connecting"}, {State::kConnected, "connected"}, {State::kClosed, "closed"}, {State::kFailed, "failed"}, - }; + }); }; - template<> struct StrEnum { + template <> + struct StrEnum { using State = webrtc::SctpTransportState; - static constexpr char name[] = "SctpTransportState"; - static constexpr std::pair values[] = { + static constexpr auto name = pybind11::detail::const_name("SctpTransportState"); + static constexpr auto values = std::to_array>({ {State::kNew, "new"}, {State::kConnecting, "connecting"}, {State::kConnected, "connected"}, {State::kClosed, "closed"}, - }; + }); }; - template<> struct StrEnum { + template <> + struct StrEnum { using State = webrtc::DataChannelInterface::DataState; - static constexpr char name[] = "RTCDataChannelState"; - static constexpr std::pair values[] = { + static constexpr auto name = pybind11::detail::const_name("RTCDataChannelState"); + static constexpr auto values = std::to_array>({ {State::kConnecting, "connecting"}, {State::kOpen, "open"}, {State::kClosing, "closing"}, {State::kClosed, "closed"}, - }; + }); }; - template<> struct StrEnum { - static constexpr char name[] = "RTCPriorityType"; - static constexpr std::pair values[] = { + template <> + struct StrEnum { + static constexpr auto name = pybind11::detail::const_name("RTCPriorityType"); + static constexpr auto values = std::to_array>({ {webrtc::Priority::kVeryLow, "very-low"}, {webrtc::Priority::kLow, "low"}, {webrtc::Priority::kMedium, "medium"}, {webrtc::Priority::kHigh, "high"}, - }; + }); }; - template<> struct StrEnum { + template <> + struct StrEnum { using Preference = webrtc::DegradationPreference; - static constexpr char name[] = "RTCDegradationPreference"; - static constexpr std::pair values[] = { + static constexpr auto name = pybind11::detail::const_name("RTCDegradationPreference"); + static constexpr auto values = std::to_array>({ {Preference::MAINTAIN_FRAMERATE, "maintain-framerate"}, {Preference::MAINTAIN_RESOLUTION, "maintain-resolution"}, {Preference::BALANCED, "balanced"}, {Preference::MAINTAIN_FRAMERATE_AND_RESOLUTION, "maintain-framerate-and-resolution"}, - }; + }); }; // NONE is None in Python - template<> struct StrEnum { + template <> + struct StrEnum { using Detail = webrtc::RTCErrorDetailType; - static constexpr char name[] = "RTCErrorDetailType"; - static constexpr std::pair values[] = { + static constexpr auto name = pybind11::detail::const_name("RTCErrorDetailType"); + static constexpr auto values = std::to_array>({ {Detail::DATA_CHANNEL_FAILURE, "data-channel-failure"}, {Detail::DTLS_FAILURE, "dtls-failure"}, {Detail::FINGERPRINT_FAILURE, "fingerprint-failure"}, @@ -243,43 +267,47 @@ namespace python_webrtc { {Detail::SDP_SYNTAX_ERROR, "sdp-syntax-error"}, {Detail::HARDWARE_ENCODER_NOT_AVAILABLE, "hardware-encoder-not-available"}, {Detail::HARDWARE_ENCODER_ERROR, "hardware-encoder-error"}, - }; + }); }; - template<> struct StrEnum { + template <> + struct StrEnum { using Policy = webrtc::PeerConnectionInterface::IceTransportsType; - static constexpr char name[] = "RTCIceTransportPolicy"; - static constexpr std::pair values[] = { + static constexpr auto name = pybind11::detail::const_name("RTCIceTransportPolicy"); + static constexpr auto values = std::to_array>({ {Policy::kAll, "all"}, {Policy::kRelay, "relay"}, - }; + }); }; - template<> struct StrEnum { + template <> + struct StrEnum { using Policy = webrtc::PeerConnectionInterface::BundlePolicy; - static constexpr char name[] = "RTCBundlePolicy"; - static constexpr std::pair values[] = { + static constexpr auto name = pybind11::detail::const_name("RTCBundlePolicy"); + static constexpr auto values = std::to_array>({ {Policy::kBundlePolicyBalanced, "balanced"}, {Policy::kBundlePolicyMaxCompat, "max-compat"}, {Policy::kBundlePolicyMaxBundle, "max-bundle"}, - }; + }); }; - template<> struct StrEnum { + template <> + struct StrEnum { using Policy = webrtc::PeerConnectionInterface::RtcpMuxPolicy; - static constexpr char name[] = "RTCRtcpMuxPolicy"; - static constexpr std::pair values[] = { + static constexpr auto name = pybind11::detail::const_name("RTCRtcpMuxPolicy"); + static constexpr auto values = std::to_array>({ {Policy::kRtcpMuxPolicyRequire, "require"}, - }; + }); }; - template<> struct StrEnum { + template <> + struct StrEnum { using Policy = webrtc::CryptoOptions::Srtp::CryptexPolicy; - static constexpr char name[] = "RTCRtpHeaderEncryptionPolicy"; - static constexpr std::pair values[] = { + static constexpr auto name = pybind11::detail::const_name("RTCRtpHeaderEncryptionPolicy"); + static constexpr auto values = std::to_array>({ {Policy::kNegotiate, "negotiate"}, {Policy::kRequire, "require"}, - }; + }); }; } // namespace python_webrtc @@ -287,18 +315,19 @@ namespace python_webrtc { namespace pybind11::detail { // not the caster of py::enum_ and py::native_enum - template + template struct type_caster_enum_type_enabled::value>> : std::false_type {}; - template class type_caster::value>> { + template + class type_caster::value>> { using Enum = python_webrtc::StrEnum; public: - PYBIND11_TYPE_CASTER(T, const_name("webrtc.enums.") + const_name(Enum::name)); + PYBIND11_TYPE_CASTER(T, const_name("webrtc.enums.") + Enum::name); // a member of the Python enum, or its value: like a wrong type, another string is a TypeError (as it is for // a WebIDL enum) - bool load(handle src, bool) { + bool load(handle src, bool /*unused*/) { if (!isinstance(src)) { return false; } @@ -312,15 +341,17 @@ namespace pybind11::detail { return false; } - static handle cast(T src, return_value_policy, handle) { + static handle cast(T src, return_value_policy /*unused*/, handle /*unused*/) { for (const auto &[member, memberValue] : Enum::values) { if (member == src) { // looked up when converting, as webrtc.enums may be imported after this module - return module_::import("webrtc.enums").attr(Enum::name)(memberValue).release(); + return module_::import("webrtc.enums").attr(Enum::name.text)(memberValue).release(); } } - throw value_error(std::string(Enum::name) + " has no member for " + std::to_string(static_cast(src))); + throw value_error(std::string(Enum::name.text) + " has no member for " + std::to_string(static_cast(src))); } }; } // namespace pybind11::detail + +#endif // PYTHON_WEBRTC_ENUMS_ENUMS_H_ diff --git a/python-webrtc/cpp/src/exceptions.cpp b/python-webrtc/cpp/src/exceptions.cpp index e3ade2d..c259187 100644 --- a/python-webrtc/cpp/src/exceptions.cpp +++ b/python-webrtc/cpp/src/exceptions.cpp @@ -16,7 +16,7 @@ namespace python_webrtc { webrtc::RTCError closedError(const std::string &method, const std::string &interface) { return {webrtc::RTCErrorType::INVALID_STATE, "Failed to execute '" + method + "' on '" + interface + - "': The RTCPeerConnection's signalingState is 'closed'."}; + "': The RTCPeerConnection's signalingState is 'closed'."}; } pybind11::object rtcErrorToPython(const webrtc::RTCError &error, std::optional sdpLineNumber) { @@ -24,14 +24,16 @@ namespace python_webrtc { if (error.sctp_cause_code()) { sctpCauseCode = pybind11::int_(*error.sctp_cause_code()); } - pybind11::object lineNumber = sdpLineNumber ? pybind11::object(pybind11::int_(*sdpLineNumber)) : pybind11::none(); + const pybind11::object lineNumber = + sdpLineNumber ? pybind11::object(pybind11::int_(*sdpLineNumber)) : pybind11::none(); pybind11::object detail = pybind11::none(); if (error.error_detail() != webrtc::RTCErrorDetailType::NONE) { detail = pybind11::cast(error.error_detail()); } // the exception classes are defined in Python, to be subclassed and constructed like any Python exception - return pybind11::module_::import("webrtc.exceptions").attr("_from_native")( - std::string(ToString(error.type())), std::string(error.message()), detail, sctpCauseCode, lineNumber); + return pybind11::module_::import("webrtc.exceptions") + .attr("_from_native")(std::string(ToString(error.type())), std::string(error.message()), detail, sctpCauseCode, + lineNumber); } pybind11::object RTCCallbackException::ToPython() const { @@ -39,23 +41,23 @@ namespace python_webrtc { } void Exceptions::Init(pybind11::module &m) { - pybind11::class_(m, "RTCCallbackException") - .def("toPython", &RTCCallbackException::ToPython); + pybind11::class_(m, "RTCCallbackException").def("toPython", &RTCCallbackException::ToPython); - static pybind11::exception baseExc(m, "PythonWebRTCExceptionBase"); + static const pybind11::exception baseExc(m, "PythonWebRTCExceptionBase"); pybind11::register_exception(m, "PythonWebRTCException", baseExc); pybind11::register_exception(m, "SdpParseException", baseExc); - // registered last to be tried first - pybind11::register_exception_translator([](std::exception_ptr p) { + // registered last to be tried first; pybind11's translator type takes the pointer by value + // NOLINTNEXTLINE(performance-unnecessary-value-param) + pybind11::register_exception_translator([](std::exception_ptr error) { try { - if (p) { - std::rethrow_exception(p); + if (error) { + std::rethrow_exception(error); } } catch (const RTCException &e) { auto exc = rtcErrorToPython(e.error()); - PyErr_SetObject(reinterpret_cast(Py_TYPE(exc.ptr())), exc.ptr()); + PyErr_SetObject(exc.get_type().ptr(), exc.ptr()); } }); } diff --git a/python-webrtc/cpp/src/exceptions.h b/python-webrtc/cpp/src/exceptions.h index 7c1decd..454a962 100644 --- a/python-webrtc/cpp/src/exceptions.h +++ b/python-webrtc/cpp/src/exceptions.h @@ -5,7 +5,8 @@ // that can be found in the LICENSE.md file in the root of the project. // -#pragma once +#ifndef PYTHON_WEBRTC_EXCEPTIONS_H_ +#define PYTHON_WEBRTC_EXCEPTIONS_H_ #include #include @@ -30,11 +31,9 @@ namespace python_webrtc { // (see webrtc/exceptions.py). class RTCException : public PythonWebRTCException { public: - explicit RTCException(webrtc::RTCError error) - : PythonWebRTCException(error.message()), _error(std::move(error)) {} + explicit RTCException(webrtc::RTCError error) : PythonWebRTCException(error.message()), _error(std::move(error)) {} - RTCException(webrtc::RTCErrorType type, const std::string &msg) - : PythonWebRTCException(msg), _error(type, msg) {} + RTCException(webrtc::RTCErrorType type, const std::string &msg) : PythonWebRTCException(msg), _error(type, msg) {} [[nodiscard]] const webrtc::RTCError &error() const { return _error; } @@ -76,3 +75,5 @@ namespace python_webrtc { }; } // namespace python_webrtc + +#endif // PYTHON_WEBRTC_EXCEPTIONS_H_ diff --git a/python-webrtc/cpp/src/functions/functions.cpp b/python-webrtc/cpp/src/functions/functions.cpp index 08d5a56..4e03649 100644 --- a/python-webrtc/cpp/src/functions/functions.cpp +++ b/python-webrtc/cpp/src/functions/functions.cpp @@ -6,8 +6,8 @@ // #include "functions.h" -#include "get_user_media.h" #include "../utils/gil.h" +#include "get_user_media.h" namespace python_webrtc { @@ -17,4 +17,4 @@ namespace python_webrtc { pybind11::arg("width"), pybind11::arg("height"), pybind11::arg("frameRate")); } -} +} // namespace python_webrtc diff --git a/python-webrtc/cpp/src/functions/functions.h b/python-webrtc/cpp/src/functions/functions.h index 6e1354b..8e57e15 100644 --- a/python-webrtc/cpp/src/functions/functions.h +++ b/python-webrtc/cpp/src/functions/functions.h @@ -5,7 +5,8 @@ // that can be found in the LICENSE.md file in the root of the project. // -#pragma once +#ifndef PYTHON_WEBRTC_FUNCTIONS_FUNCTIONS_H_ +#define PYTHON_WEBRTC_FUNCTIONS_FUNCTIONS_H_ #include @@ -16,4 +17,6 @@ namespace python_webrtc { static void Init(pybind11::module &m); }; -} +} // namespace python_webrtc + +#endif // PYTHON_WEBRTC_FUNCTIONS_FUNCTIONS_H_ diff --git a/python-webrtc/cpp/src/functions/get_user_media.cpp b/python-webrtc/cpp/src/functions/get_user_media.cpp index b3c31a1..c293cab 100644 --- a/python-webrtc/cpp/src/functions/get_user_media.cpp +++ b/python-webrtc/cpp/src/functions/get_user_media.cpp @@ -18,8 +18,8 @@ namespace python_webrtc { namespace { // a synthetic microphone, as the audio device of the factory records nothing - webrtc::scoped_refptr CreateMicrophoneTrack( - const std::shared_ptr &factory) { + webrtc::scoped_refptr + CreateMicrophoneTrack(const std::shared_ptr &factory) { auto source = webrtc::make_ref_counted(); source->StartMicrophone(); auto track = factory->factory()->CreateAudioTrack(webrtc::CreateRandomUuid(), source.get()); @@ -27,8 +27,8 @@ namespace python_webrtc { return track; } - webrtc::scoped_refptr CreateCameraTrack( - const std::shared_ptr &factory, int width, int height, double frameRate) { + webrtc::scoped_refptr + CreateCameraTrack(const std::shared_ptr &factory, int width, int height, double frameRate) { auto source = webrtc::make_ref_counted(false, std::nullopt); source->StartCamera(width, height, frameRate); auto track = factory->factory()->CreateVideoTrack(source, webrtc::CreateRandomUuid()); diff --git a/python-webrtc/cpp/src/functions/get_user_media.h b/python-webrtc/cpp/src/functions/get_user_media.h index 82ce192..b27f95f 100644 --- a/python-webrtc/cpp/src/functions/get_user_media.h +++ b/python-webrtc/cpp/src/functions/get_user_media.h @@ -5,7 +5,8 @@ // that can be found in the LICENSE.md file in the root of the project. // -#pragma once +#ifndef PYTHON_WEBRTC_FUNCTIONS_GET_USER_MEDIA_H_ +#define PYTHON_WEBRTC_FUNCTIONS_GET_USER_MEDIA_H_ #include @@ -17,4 +18,6 @@ namespace python_webrtc { // (https://github.com/MarshalX/python-webrtc/issues/169, https://github.com/MarshalX/python-webrtc/issues/170) std::shared_ptr GetUserMedia(bool audio, bool video, int width, int height, double frameRate); -} +} // namespace python_webrtc + +#endif // PYTHON_WEBRTC_FUNCTIONS_GET_USER_MEDIA_H_ diff --git a/python-webrtc/cpp/src/interfaces/create_session_description_observer.cpp b/python-webrtc/cpp/src/interfaces/create_session_description_observer.cpp index 8939e6f..eab326d 100644 --- a/python-webrtc/cpp/src/interfaces/create_session_description_observer.cpp +++ b/python-webrtc/cpp/src/interfaces/create_session_description_observer.cpp @@ -22,4 +22,4 @@ namespace python_webrtc { _onFailure(RTCCallbackException(std::move(error))); } -} +} // namespace python_webrtc diff --git a/python-webrtc/cpp/src/interfaces/create_session_description_observer.h b/python-webrtc/cpp/src/interfaces/create_session_description_observer.h index a3a7559..f12169f 100644 --- a/python-webrtc/cpp/src/interfaces/create_session_description_observer.h +++ b/python-webrtc/cpp/src/interfaces/create_session_description_observer.h @@ -5,11 +5,14 @@ // that can be found in the LICENSE.md file in the root of the project. // -#pragma once +#ifndef PYTHON_WEBRTC_INTERFACES_CREATE_SESSION_DESCRIPTION_OBSERVER_H_ +#define PYTHON_WEBRTC_INTERFACES_CREATE_SESSION_DESCRIPTION_OBSERVER_H_ #include "rtc_peer_connection.h" -namespace webrtc { class RTCError; } +namespace webrtc { + class RTCError; +} namespace python_webrtc { @@ -17,12 +20,12 @@ namespace python_webrtc { public: CreateSessionDescriptionObserver(std::weak_ptr peerConnection, std::function &onSuccess, - std::function &onFailure) : - _peerConnection(std::move(peerConnection)), _onSuccess(onSuccess), _onFailure(onFailure) {} + std::function &onFailure) + : _peerConnection(std::move(peerConnection)), _onSuccess(onSuccess), _onFailure(onFailure) {} - void OnSuccess(webrtc::SessionDescriptionInterface *) override; + void OnSuccess(webrtc::SessionDescriptionInterface *description) override; - void OnFailure(webrtc::RTCError) override; + void OnFailure(webrtc::RTCError error) override; private: // the connection may be gone by the time the description is created @@ -32,3 +35,5 @@ namespace python_webrtc { }; } // namespace python_webrtc + +#endif // PYTHON_WEBRTC_INTERFACES_CREATE_SESSION_DESCRIPTION_OBSERVER_H_ diff --git a/python-webrtc/cpp/src/interfaces/interfaces.cpp b/python-webrtc/cpp/src/interfaces/interfaces.cpp index 0ae4aa8..da6db65 100644 --- a/python-webrtc/cpp/src/interfaces/interfaces.cpp +++ b/python-webrtc/cpp/src/interfaces/interfaces.cpp @@ -7,23 +7,23 @@ #include "interfaces.h" -#include "peer_connection_factory.h" -#include "media_stream_track.h" -#include "media_stream.h" -#include "rtc_ice_transport.h" -#include "rtc_dtls_transport.h" -#include "rtc_sctp_transport.h" -#include "rtc_rtp_sender.h" -#include "rtc_rtp_receiver.h" -#include "rtc_rtp_transceiver.h" -#include "rtc_data_channel.h" -#include "rtc_dtmf_sender.h" -#include "rtc_peer_connection.h" #include "../media/media_stream_track_processor.h" #include "../media/track_generator.h" #include "../media/video_frame_buffer.h" #include "../utils/alive_count.h" #include "../utils/gil.h" +#include "media_stream.h" +#include "media_stream_track.h" +#include "peer_connection_factory.h" +#include "rtc_data_channel.h" +#include "rtc_dtls_transport.h" +#include "rtc_dtmf_sender.h" +#include "rtc_ice_transport.h" +#include "rtc_peer_connection.h" +#include "rtc_rtp_receiver.h" +#include "rtc_rtp_sender.h" +#include "rtc_rtp_transceiver.h" +#include "rtc_sctp_transport.h" #include #include @@ -47,23 +47,26 @@ namespace python_webrtc { RTCPeerConnection::Init(m); // the native objects alive, by type, so tests can check that none leaks - m.def("_alive", []() { - return std::map{ - {"RTCPeerConnection", AliveCount::count.load()}, - {"MediaStreamTrack", MediaStreamTrack::holder().Alive()}, - {"MediaStream", MediaStream::holder().Alive()}, - {"RTCRtpTransceiver", RTCRtpTransceiver::holder().Alive()}, - {"RTCRtpSender", RTCRtpSender::holder().Alive()}, - {"RTCRtpReceiver", RTCRtpReceiver::holder().Alive()}, - {"RTCDTMFSender", RTCDTMFSender::holder().Alive()}, - {"RTCDataChannel", RTCDataChannel::holder().Alive()}, - {"RTCSctpTransport", RTCSctpTransport::holder().Alive()}, - {"RTCDtlsTransport", RTCDtlsTransport::holder().Alive()}, - {"RTCIceTransport", RTCIceTransport::holder().Alive()}, - {"MediaStreamTrackProcessor", AliveCount::count.load()}, - {"TrackGenerator", AliveCount::count.load()}, - {"VideoFrameBuffer", AliveCount::count.load()}, - }; - }, nogil()); + m.def( + "_alive", + []() { + return std::map{ + {"RTCPeerConnection", AliveCount::count.load()}, + {"MediaStreamTrack", MediaStreamTrack::holder().Alive()}, + {"MediaStream", MediaStream::holder().Alive()}, + {"RTCRtpTransceiver", RTCRtpTransceiver::holder().Alive()}, + {"RTCRtpSender", RTCRtpSender::holder().Alive()}, + {"RTCRtpReceiver", RTCRtpReceiver::holder().Alive()}, + {"RTCDTMFSender", RTCDTMFSender::holder().Alive()}, + {"RTCDataChannel", RTCDataChannel::holder().Alive()}, + {"RTCSctpTransport", RTCSctpTransport::holder().Alive()}, + {"RTCDtlsTransport", RTCDtlsTransport::holder().Alive()}, + {"RTCIceTransport", RTCIceTransport::holder().Alive()}, + {"MediaStreamTrackProcessor", AliveCount::count.load()}, + {"TrackGenerator", AliveCount::count.load()}, + {"VideoFrameBuffer", AliveCount::count.load()}, + }; + }, + nogil()); } -} +} // namespace python_webrtc diff --git a/python-webrtc/cpp/src/interfaces/interfaces.h b/python-webrtc/cpp/src/interfaces/interfaces.h index d517ae2..5c88681 100644 --- a/python-webrtc/cpp/src/interfaces/interfaces.h +++ b/python-webrtc/cpp/src/interfaces/interfaces.h @@ -5,7 +5,8 @@ // that can be found in the LICENSE.md file in the root of the project. // -#pragma once +#ifndef PYTHON_WEBRTC_INTERFACES_INTERFACES_H_ +#define PYTHON_WEBRTC_INTERFACES_INTERFACES_H_ #include @@ -16,4 +17,6 @@ namespace python_webrtc { static void Init(pybind11::module &m); }; -} +} // namespace python_webrtc + +#endif // PYTHON_WEBRTC_INTERFACES_INTERFACES_H_ diff --git a/python-webrtc/cpp/src/interfaces/media_stream.cpp b/python-webrtc/cpp/src/interfaces/media_stream.cpp index abb3803..de80a37 100644 --- a/python-webrtc/cpp/src/interfaces/media_stream.cpp +++ b/python-webrtc/cpp/src/interfaces/media_stream.cpp @@ -14,54 +14,55 @@ namespace python_webrtc { - MediaStream::MediaStream( - std::shared_ptr factory, webrtc::scoped_refptr stream) + MediaStream::MediaStream(std::shared_ptr factory, + webrtc::scoped_refptr stream) : _factory(std::move(factory)), _stream(std::move(stream)) { - for (const auto &track: tracks()) { + for (const auto &track : tracks()) { _known.insert(track.get()); } // see AliveGuard - _factory->_signalingThread->PostTask(_alive.Guard([this]() { _stream->RegisterObserver(this); })); + _factory->signalingThread()->PostTask(_alive.Guard([this]() { _stream->RegisterObserver(this); })); } MediaStream::~MediaStream() { - BlockingDestructor release("MediaStream"); - _factory->_signalingThread->BlockingCall([this]() { _stream->UnregisterObserver(this); }); + const BlockingDestructor release("MediaStream"); + _factory->signalingThread()->BlockingCall([this]() { _stream->UnregisterObserver(this); }); DropListeners(); } void MediaStream::OnChanged() { - std::vector> added, removed; + std::vector> added; + std::vector> removed; { - std::lock_guard lock(_tracksMutex); + const std::scoped_lock lock(_tracksMutex); std::set current; - for (const auto &track: tracks()) { + for (const auto &track : tracks()) { current.insert(track.get()); - if (!_known.count(track.get())) { + if (!_known.contains(track.get())) { added.push_back(track); } } - for (auto track: _known) { - if (!current.count(track)) { - removed.push_back(webrtc::scoped_refptr(track)); + for (auto *track : _known) { + if (!current.contains(track)) { + removed.emplace_back(track); } } _known = std::move(current); } - for (const auto &track: removed) { + for (const auto &track : removed) { Emit("removetrack", MediaStreamTrack::holder().GetOrCreate(_factory, track)); } - for (const auto &track: added) { + for (const auto &track : added) { Emit("addtrack", MediaStreamTrack::holder().GetOrCreate(_factory, track)); } } std::vector> MediaStream::tracks() { auto tracks = std::vector>(); - for (auto const &track: _stream->GetAudioTracks()) { + for (const auto &track : _stream->GetAudioTracks()) { tracks.emplace_back(track); } - for (auto const &track: _stream->GetVideoTracks()) { + for (const auto &track : _stream->GetVideoTracks()) { tracks.emplace_back(track); } return tracks; @@ -70,7 +71,7 @@ namespace python_webrtc { std::vector> MediaStream::SyncTracks() { // Python keeps the wrappers, the holder finds them std::vector> tracks; - for (auto const &track: this->tracks()) { + for (const auto &track : this->tracks()) { tracks.push_back(MediaStreamTrack::holder().GetOrCreate(_factory, track)); } return tracks; @@ -101,7 +102,7 @@ namespace python_webrtc { bool MediaStream::GetActive() { auto active = false; - for (auto const &track: SyncTracks()) { + for (const auto &track : SyncTracks()) { active = active || track->active(); } @@ -111,7 +112,7 @@ namespace python_webrtc { std::vector> MediaStream::GetAudioTracks() { auto tracks = std::vector>(); - for (auto const &track: SyncTracks()) { + for (const auto &track : SyncTracks()) { if (track->track()->kind() == webrtc::MediaStreamTrackInterface::kAudioKind) { tracks.push_back(track); } @@ -123,7 +124,7 @@ namespace python_webrtc { std::vector> MediaStream::GetVideoTracks() { auto tracks = std::vector>(); - for (auto const &track: SyncTracks()) { + for (const auto &track : SyncTracks()) { if (track->track()->kind() == webrtc::MediaStreamTrackInterface::kVideoKind) { tracks.push_back(track); } @@ -138,7 +139,7 @@ namespace python_webrtc { std::optional> MediaStream::GetTrackById(const std::string &id) { // by the id Python sees, which differs from the libwebrtc one for remote tracks - for (const auto &track: GetTracks()) { + for (const auto &track : GetTracks()) { if (track->GetId() == id) { return track; } @@ -150,7 +151,7 @@ namespace python_webrtc { auto track = mediaStreamTrack->track(); { // changes made by Python fire no events - std::lock_guard lock(_tracksMutex); + const std::scoped_lock lock(_tracksMutex); _known.insert(track.get()); } @@ -165,7 +166,7 @@ namespace python_webrtc { auto track = mediaStreamTrack.track(); { // changes made by Python fire no events - std::lock_guard lock(_tracksMutex); + const std::scoped_lock lock(_tracksMutex); _known.erase(track.get()); } @@ -179,16 +180,17 @@ namespace python_webrtc { std::shared_ptr MediaStream::Clone() { auto clonedStream = _factory->factory()->CreateLocalMediaStream(webrtc::CreateRandomUuid()); - for (auto const &track: this->tracks()) { - if (track->kind() == track->kAudioKind) { - auto audioTrack = dynamic_cast(track.get()); - auto source = audioTrack->GetSource(); + for (const auto &track : this->tracks()) { + if (track->kind() == webrtc::MediaStreamTrackInterface::kAudioKind) { + auto *audioTrack = dynamic_cast(track.get()); + auto *source = audioTrack->GetSource(); auto clonedTrack = _factory->factory()->CreateAudioTrack(webrtc::CreateRandomUuid(), source); clonedStream->AddTrack(clonedTrack); } else { - auto videoTrack = dynamic_cast(track.get()); - auto source = videoTrack->GetSource(); - auto clonedTrack = _factory->factory()->CreateVideoTrack(webrtc::scoped_refptr(source), webrtc::CreateRandomUuid()); + auto *videoTrack = dynamic_cast(track.get()); + auto *source = videoTrack->GetSource(); + auto clonedTrack = _factory->factory()->CreateVideoTrack( + webrtc::scoped_refptr(source), webrtc::CreateRandomUuid()); clonedStream->AddTrack(clonedTrack); } } @@ -200,7 +202,7 @@ namespace python_webrtc { auto factory = PeerConnectionFactory::GetOrCreateDefault(); auto stream = MediaStream::holder().GetOrCreate( factory, factory->factory()->CreateLocalMediaStream(webrtc::CreateRandomUuid())); - for (const auto &track: tracks) { + for (const auto &track : tracks) { stream->AddTrack(track); } return stream; @@ -208,7 +210,7 @@ namespace python_webrtc { InstanceHolder &MediaStream::holder() { // never destroyed: wrappers may outlive static destructors - static auto holder = new InstanceHolder(); + static auto *holder = new InstanceHolder(); return *holder; } diff --git a/python-webrtc/cpp/src/interfaces/media_stream.h b/python-webrtc/cpp/src/interfaces/media_stream.h index 924b4e0..a4cce27 100644 --- a/python-webrtc/cpp/src/interfaces/media_stream.h +++ b/python-webrtc/cpp/src/interfaces/media_stream.h @@ -5,7 +5,8 @@ // that can be found in the LICENSE.md file in the root of the project. // -#pragma once +#ifndef PYTHON_WEBRTC_INTERFACES_MEDIA_STREAM_H_ +#define PYTHON_WEBRTC_INTERFACES_MEDIA_STREAM_H_ #include #include @@ -20,28 +21,24 @@ #include #include -#include "peer_connection_factory.h" -#include "media_stream_track.h" #include "../utils/alive_guard.h" #include "../utils/listeners.h" - -namespace webrtc { - - class MediaStreamInterface; - - class MediaStreamTrackInterface; - -} +#include "media_stream_track.h" +#include "peer_connection_factory.h" namespace python_webrtc { // Emits addtrack and removetrack when libwebrtc changes the tracks of a remote stream class MediaStream : public webrtc::ObserverInterface, public Listeners { public: - MediaStream(std::shared_ptr, webrtc::scoped_refptr); + MediaStream(std::shared_ptr factory, + webrtc::scoped_refptr stream); ~MediaStream() override; + MediaStream(const MediaStream &) = delete; + MediaStream &operator=(const MediaStream &) = delete; + // ObserverInterface, on the signaling thread void OnChanged() override; @@ -64,11 +61,11 @@ namespace python_webrtc { std::vector> GetTracks(); - std::optional> GetTrackById(const std::string &); + std::optional> GetTrackById(const std::string &id); - void AddTrack(const std::shared_ptr &); + void AddTrack(const std::shared_ptr &mediaStreamTrack); - void RemoveTrack(MediaStreamTrack &); + void RemoveTrack(MediaStreamTrack &mediaStreamTrack); std::shared_ptr Clone(); @@ -78,7 +75,6 @@ namespace python_webrtc { // wrappers of the current tracks of the stream; the stream owns them, so their state outlives Python references std::vector> SyncTracks(); - std::shared_ptr _factory; webrtc::scoped_refptr _stream; @@ -91,3 +87,5 @@ namespace python_webrtc { }; } // namespace python_webrtc + +#endif // PYTHON_WEBRTC_INTERFACES_MEDIA_STREAM_H_ diff --git a/python-webrtc/cpp/src/interfaces/media_stream_track.cpp b/python-webrtc/cpp/src/interfaces/media_stream_track.cpp index d014a71..bf5b998 100644 --- a/python-webrtc/cpp/src/interfaces/media_stream_track.cpp +++ b/python-webrtc/cpp/src/interfaces/media_stream_track.cpp @@ -7,21 +7,21 @@ #include "media_stream_track.h" +#include #include -#include "rtc_video_track_source.h" #include "../utils/gil.h" #include "../utils/libwebrtc_thread.h" +#include "rtc_video_track_source.h" namespace python_webrtc { MediaStreamTrack::MediaStreamTrack(std::shared_ptr factory, webrtc::scoped_refptr track) - : _factory(std::move(factory)), - _track(std::move(track)), + : _factory(std::move(factory)), _track(std::move(track)), _source(SourceControl::Find(_track.get(), _track->id())) { // see AliveGuard - _factory->_signalingThread->PostTask(_alive.Guard([this]() { + _factory->signalingThread()->PostTask(_alive.Guard([this]() { _track->RegisterObserver(this); _observing = true; AttachMonitor(); @@ -29,10 +29,10 @@ namespace python_webrtc { } MediaStreamTrack::~MediaStreamTrack() { - BlockingDestructor release("MediaStreamTrack"); + const BlockingDestructor release("MediaStreamTrack"); // after this the track can't notify us anymore: it notifies on the same thread - _factory->_signalingThread->BlockingCall([this]() { + _factory->signalingThread()->BlockingCall([this]() { DetachMonitor(); if (_observing) { _track->UnregisterObserver(this); @@ -42,15 +42,7 @@ namespace python_webrtc { _track = nullptr; // released as the listeners are (see DropListeners) - { - PythonEntry entry; - if (!entry) { - (void) _constraints.release(); - } else if (_constraints) { - pybind11::gil_scoped_acquire gil; - pybind11::object dropped = std::move(_constraints); - } - } + ReleasePythonObject(_constraints); DropListeners(); } @@ -100,7 +92,7 @@ namespace python_webrtc { void MediaStreamTrack::Stop() { _stopped = true; _surfacedEnded.Reset(); - _factory->_signalingThread->BlockingCall([this]() { StopOnSignalingThread(); }); + _factory->signalingThread()->BlockingCall([this]() { StopOnSignalingThread(); }); } void MediaStreamTrack::StopOnSignalingThread() { @@ -115,7 +107,7 @@ namespace python_webrtc { void MediaStreamTrack::AddEndObserver(const std::shared_ptr &observer) { { - std::lock_guard lock(_endObserversMutex); + const std::scoped_lock lock(_endObserversMutex); if (!_ended) { _endObservers.push_back(observer); return; @@ -127,10 +119,10 @@ namespace python_webrtc { void MediaStreamTrack::NotifyEnded() { std::vector> observers; { - std::lock_guard lock(_endObserversMutex); + const std::scoped_lock lock(_endObserversMutex); observers.swap(_endObservers); } - for (auto &observer: observers) { + for (auto &observer : observers) { if (auto locked = observer.lock()) { locked->OnTrackEnded(); } @@ -140,7 +132,7 @@ namespace python_webrtc { void MediaStreamTrack::OnChanged() { if (_track->state() == webrtc::MediaStreamTrackInterface::TrackState::kEnded && !_ended) { // ended by the remote peer or by renegotiation, rather than by stop() - bool emit = !_stopped; + const bool emit = !_stopped; if (emit) { // the track is live until its ended event (see Surfaced) _surfacedEnded.Changed(IsTracked(), false); @@ -164,7 +156,7 @@ namespace python_webrtc { _muted = true; { // a remote track has its own id, rather than the one in the description, which every receiver of it shares - std::lock_guard lock(_idMutex); + const std::scoped_lock lock(_idMutex); _id = webrtc::CreateRandomUuid(); _label = _track->kind() == webrtc::MediaStreamTrackInterface::kAudioKind ? "remote audio" : "remote video"; } @@ -177,7 +169,7 @@ namespace python_webrtc { // an ended track stays as it was return; } - bool previous = _muted.exchange(muted); + const bool previous = _muted.exchange(muted); if (previous != muted) { // a held remote track keeps showing the previous value until its event is delivered _surfacedMuted.Changed(IsTracked(), previous); @@ -214,7 +206,7 @@ namespace python_webrtc { } std::string MediaStreamTrack::GetId() { - std::lock_guard lock(_idMutex); + const std::scoped_lock lock(_idMutex); return _id ? *_id : _track->id(); } @@ -223,19 +215,20 @@ namespace python_webrtc { } std::string MediaStreamTrack::GetLabel() { - std::lock_guard lock(_idMutex); + const std::scoped_lock lock(_idMutex); return _label; } void MediaStreamTrack::SetLabel(const std::string &label) { - std::lock_guard lock(_idMutex); + const std::scoped_lock lock(_idMutex); _label = label; } webrtc::MediaType MediaStreamTrack::GetKind() { if (_track->kind() == webrtc::MediaStreamTrackInterface::kAudioKind) { return webrtc::MediaType::AUDIO; - } else if (_track->kind() == webrtc::MediaStreamTrackInterface::kVideoKind) { + } + if (_track->kind() == webrtc::MediaStreamTrackInterface::kVideoKind) { return webrtc::MediaType::VIDEO; } @@ -243,11 +236,11 @@ namespace python_webrtc { } webrtc::MediaStreamTrackInterface::TrackState MediaStreamTrack::GetReadyState() { - bool ended = _ended || _track->state() == webrtc::MediaStreamTrackInterface::TrackState::kEnded; + const bool ended = _ended || _track->state() == webrtc::MediaStreamTrackInterface::TrackState::kEnded; // without listeners (outside of an event loop), no event is going to surface it - return (HasListeners() ? _surfacedEnded.Get(ended) : ended) - ? webrtc::MediaStreamTrackInterface::TrackState::kEnded - : webrtc::MediaStreamTrackInterface::TrackState::kLive; + const bool surfaced = HasListeners() ? _surfacedEnded.Get(ended) : ended; + return surfaced ? webrtc::MediaStreamTrackInterface::TrackState::kEnded + : webrtc::MediaStreamTrackInterface::TrackState::kLive; } bool MediaStreamTrack::GetMuted() { @@ -260,12 +253,12 @@ namespace python_webrtc { std::optional> camera; bool microphone = false; { - gil_release release; + const gil_release release; video = _monitor.video(); audio = _monitor.audio(); camera = GetCamera(); if (_source) { - std::lock_guard lock(_source->mutex); + const std::scoped_lock lock(_source->mutex); microphone = _source->microphone; } } @@ -303,10 +296,11 @@ namespace python_webrtc { if (!_source) { return std::nullopt; } - std::lock_guard lock(_source->mutex); - int width, height; - double frameRate; - if (!_source->camera || !_source->camera->IsCamera(&width, &height, &frameRate)) { + const std::scoped_lock lock(_source->mutex); + int width{}; + int height{}; + double frameRate = NAN; + if ((_source->camera == nullptr) || !_source->camera->IsCamera(&width, &height, &frameRate)) { return std::nullopt; } return std::make_tuple(width, height, frameRate); @@ -316,8 +310,8 @@ namespace python_webrtc { if (!_source || _ended) { return false; } - std::lock_guard lock(_source->mutex); - if (!_source->camera) { + const std::scoped_lock lock(_source->mutex); + if (_source->camera == nullptr) { return false; } _source->camera->StartCamera(width, height, frameRate); @@ -334,14 +328,18 @@ namespace python_webrtc { std::string MediaStreamTrack::GetContentHint() { if (_track->kind() == webrtc::MediaStreamTrackInterface::kAudioKind) { - std::lock_guard lock(_contentHintMutex); + const std::scoped_lock lock(_contentHintMutex); return _audioContentHint; } switch (static_cast(_track.get())->content_hint()) { - case webrtc::VideoTrackInterface::ContentHint::kFluid: return "motion"; - case webrtc::VideoTrackInterface::ContentHint::kDetailed: return "detail"; - case webrtc::VideoTrackInterface::ContentHint::kText: return "text"; - default: return ""; + case webrtc::VideoTrackInterface::ContentHint::kFluid: + return "motion"; + case webrtc::VideoTrackInterface::ContentHint::kDetailed: + return "detail"; + case webrtc::VideoTrackInterface::ContentHint::kText: + return "text"; + default: + return ""; } } @@ -349,13 +347,13 @@ namespace python_webrtc { // hints of the other kind, and unknown ones, are ignored if (_track->kind() == webrtc::MediaStreamTrackInterface::kAudioKind) { if (hint.empty() || hint == "speech" || hint == "speaking" || hint == "music") { - std::lock_guard lock(_contentHintMutex); + const std::scoped_lock lock(_contentHintMutex); _audioContentHint = hint; } return; } using ContentHint = webrtc::VideoTrackInterface::ContentHint; - auto track = static_cast(_track.get()); + auto *track = static_cast(_track.get()); if (hint.empty()) { track->set_content_hint(ContentHint::kNone); } else if (hint == "motion") { @@ -371,12 +369,13 @@ namespace python_webrtc { auto label = webrtc::CreateRandomUuid(); webrtc::scoped_refptr clonedTrack = nullptr; - if (_track->kind() == _track->kAudioKind) { - auto audioTrack = dynamic_cast(_track.get()); + if (_track->kind() == webrtc::MediaStreamTrackInterface::kAudioKind) { + auto *audioTrack = dynamic_cast(_track.get()); clonedTrack = _factory->factory()->CreateAudioTrack(label, audioTrack->GetSource()); } else { - auto videoTrack = dynamic_cast(_track.get()); - clonedTrack = _factory->factory()->CreateVideoTrack(webrtc::scoped_refptr(videoTrack->GetSource()), label); + auto *videoTrack = dynamic_cast(_track.get()); + clonedTrack = _factory->factory()->CreateVideoTrack( + webrtc::scoped_refptr(videoTrack->GetSource()), label); } if (_source) { @@ -392,16 +391,18 @@ namespace python_webrtc { } MediaStreamTrack::operator webrtc::scoped_refptr() { - return webrtc::scoped_refptr(dynamic_cast(_track.get())); + return webrtc::scoped_refptr( + dynamic_cast(_track.get())); } MediaStreamTrack::operator webrtc::scoped_refptr() { - return webrtc::scoped_refptr(dynamic_cast(_track.get())); + return webrtc::scoped_refptr( + dynamic_cast(_track.get())); } InstanceHolder &MediaStreamTrack::holder() { // never destroyed: wrappers may outlive static destructors - static auto holder = new InstanceHolder(); + static auto *holder = new InstanceHolder(); return *holder; } diff --git a/python-webrtc/cpp/src/interfaces/media_stream_track.h b/python-webrtc/cpp/src/interfaces/media_stream_track.h index 71b403d..e3e0ae1 100644 --- a/python-webrtc/cpp/src/interfaces/media_stream_track.h +++ b/python-webrtc/cpp/src/interfaces/media_stream_track.h @@ -5,7 +5,8 @@ // that can be found in the LICENSE.md file in the root of the project. // -#pragma once +#ifndef PYTHON_WEBRTC_INTERFACES_MEDIA_STREAM_TRACK_H_ +#define PYTHON_WEBRTC_INTERFACES_MEDIA_STREAM_TRACK_H_ #include #include @@ -20,7 +21,7 @@ #include -#include "peer_connection_factory.h" +#include "../enums/enums.h" #include "../media/source_control.h" #include "../media/track_monitor.h" #include "../utils/alive_guard.h" @@ -28,7 +29,7 @@ #include "../utils/instance_holder.h" #include "../utils/listeners.h" #include "../utils/surfaced.h" -#include "../enums/enums.h" +#include "peer_connection_factory.h" namespace python_webrtc { @@ -42,11 +43,14 @@ namespace python_webrtc { class MediaStreamTrack : public webrtc::ObserverInterface, public Listeners { public: - explicit MediaStreamTrack( - std::shared_ptr, webrtc::scoped_refptr); + explicit MediaStreamTrack(std::shared_ptr factory, + webrtc::scoped_refptr track); ~MediaStreamTrack() override; + MediaStreamTrack(const MediaStreamTrack &) = delete; + MediaStreamTrack &operator=(const MediaStreamTrack &) = delete; + static void Init(pybind11::module &m); static InstanceHolder &holder(); @@ -60,7 +64,7 @@ namespace python_webrtc { bool GetEnabled(); - void SetEnabled(bool); + void SetEnabled(bool enabled); std::string GetId(); @@ -176,3 +180,5 @@ namespace python_webrtc { }; } // namespace python_webrtc + +#endif // PYTHON_WEBRTC_INTERFACES_MEDIA_STREAM_TRACK_H_ diff --git a/python-webrtc/cpp/src/interfaces/peer_connection_factory.cpp b/python-webrtc/cpp/src/interfaces/peer_connection_factory.cpp index 2064d6a..5c00c53 100644 --- a/python-webrtc/cpp/src/interfaces/peer_connection_factory.cpp +++ b/python-webrtc/cpp/src/interfaces/peer_connection_factory.cpp @@ -12,10 +12,10 @@ #include "../utils/instance_holder.h" #include "../utils/libwebrtc_thread.h" +#include +#include #include #include -#include -#include #include #include #include @@ -36,21 +36,19 @@ namespace python_webrtc { // Royalty-free codecs only (the prebuilts have no H.264). - using VideoEncoderFactory = webrtc::VideoEncoderFactoryTemplate< - webrtc::LibvpxVp8EncoderTemplateAdapter, - webrtc::LibvpxVp9EncoderTemplateAdapter, - webrtc::LibaomAv1EncoderTemplateAdapter>; + using VideoEncoderFactory = webrtc::VideoEncoderFactoryTemplate; - using VideoDecoderFactory = webrtc::VideoDecoderFactoryTemplate< - webrtc::LibvpxVp8DecoderTemplateAdapter, - webrtc::LibvpxVp9DecoderTemplateAdapter, - webrtc::Dav1dDecoderTemplateAdapter>; + using VideoDecoderFactory = + webrtc::VideoDecoderFactoryTemplate; std::weak_ptr PeerConnectionFactory::_default{}; std::mutex PeerConnectionFactory::_mutex{}; std::atomic PeerConnectionFactory::_alive{0}; - PeerConnectionFactory::PeerConnectionFactory() : _generation(forks.load()) { + PeerConnectionFactory::PeerConnectionFactory() : _generation(Forks().load()) { _alive++; _workerThread = webrtc::Thread::CreateWithSocketServer(); @@ -73,22 +71,15 @@ namespace python_webrtc { assert(result); _workerThread->BlockingCall([this]() { - onLibwebrtcThread = true; + OnLibwebrtcThread() = true; _audioDeviceModule = webrtc::make_ref_counted(); }); - _signalingThread->BlockingCall([]() { onLibwebrtcThread = true; }); + _signalingThread->BlockingCall([]() { OnLibwebrtcThread() = true; }); _factory = webrtc::CreatePeerConnectionFactory( - _workerThread.get(), - _workerThread.get(), - _signalingThread.get(), - _audioDeviceModule, - webrtc::CreateBuiltinAudioEncoderFactory(), - webrtc::CreateBuiltinAudioDecoderFactory(), - std::make_unique(), - std::make_unique(), - nullptr, - nullptr); + _workerThread.get(), _workerThread.get(), _signalingThread.get(), _audioDeviceModule, + webrtc::CreateBuiltinAudioEncoderFactory(), webrtc::CreateBuiltinAudioDecoderFactory(), + std::make_unique(), std::make_unique(), nullptr, nullptr); assert(_factory); webrtc::PeerConnectionFactoryInterface::Options options; @@ -98,13 +89,11 @@ namespace python_webrtc { PeerConnectionFactory::~PeerConnectionFactory() { // stopping the threads waits for their tasks, which may be waiting for the GIL - BlockingDestructor release("PeerConnectionFactory"); + const BlockingDestructor release("PeerConnectionFactory"); _factory = nullptr; - _workerThread->BlockingCall([this]() { - this->_audioDeviceModule = nullptr; - }); + _workerThread->BlockingCall([this]() { this->_audioDeviceModule = nullptr; }); _workerThread->Stop(); _signalingThread->Stop(); @@ -116,23 +105,24 @@ namespace python_webrtc { } void RunOnSignalingThread(PeerConnectionFactory &factory, const std::function &function) { - gil_release_if_held release; - factory._signalingThread->BlockingCall([&]() { function(); }); + const gil_release_if_held release; + factory.signalingThread()->BlockingCall([&]() { function(); }); } std::shared_ptr PeerConnectionFactory::Create() { #ifdef __APPLE__ // libwebrtc runs its task queues on libdispatch, which crashes in the child of a fork - if (forks.load() > 0) { - throw std::runtime_error("python-webrtc can't be used in the child of a fork on macOS (libdispatch doesn't support " - "it): use the spawn start method of multiprocessing"); + if (Forks().load() > 0) { + throw std::runtime_error( + "python-webrtc can't be used in the child of a fork on macOS (libdispatch doesn't support " + "it): use the spawn start method of multiprocessing"); } #endif return {new PeerConnectionFactory(), &PeerConnectionFactory::Destroy}; } std::shared_ptr PeerConnectionFactory::GetOrCreateDefault() { - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); auto factory = _default.lock(); if (!factory) { factory = Create(); @@ -143,7 +133,7 @@ namespace python_webrtc { void PeerConnectionFactory::Destroy(PeerConnectionFactory *factory) { // leaked while the interpreter finalizes or in a forked child: its threads may hang or be gone - if (!PythonAlive() || factory->_generation != forks.load()) { + if (!PythonAlive() || factory->_generation != Forks().load()) { return; } // the last owner may be released by a task on one of the factory threads, which can't stop itself @@ -160,7 +150,7 @@ namespace python_webrtc { } void PeerConnectionFactory::Init(pybind11::module &m) { - [[maybe_unused]] bool result = webrtc::InitializeSSL(); + [[maybe_unused]] const bool result = webrtc::InitializeSSL(); assert(result); #ifndef _WIN32 @@ -175,7 +165,7 @@ namespace python_webrtc { _mutex.unlock(); }, []() { - forks++; + Forks()++; _default.reset(); Wakeup::UnlockAfterFork(); _mutex.unlock(); diff --git a/python-webrtc/cpp/src/interfaces/peer_connection_factory.h b/python-webrtc/cpp/src/interfaces/peer_connection_factory.h index dc095b7..38e748b 100644 --- a/python-webrtc/cpp/src/interfaces/peer_connection_factory.h +++ b/python-webrtc/cpp/src/interfaces/peer_connection_factory.h @@ -5,7 +5,8 @@ // that can be found in the LICENSE.md file in the root of the project. // -#pragma once +#ifndef PYTHON_WEBRTC_INTERFACES_PEER_CONNECTION_FACTORY_H_ +#define PYTHON_WEBRTC_INTERFACES_PEER_CONNECTION_FACTORY_H_ #include #include @@ -13,18 +14,12 @@ #include #include -#include #include +#include #include #include -namespace webrtc { - - class PeerConnectionFactoryInterface; - -} // namespace webrtc - namespace python_webrtc { // Owned by every wrapper created with it, it's destroyed together with the last of them. @@ -34,6 +29,9 @@ namespace python_webrtc { ~PeerConnectionFactory(); + PeerConnectionFactory(const PeerConnectionFactory &) = delete; + PeerConnectionFactory &operator=(const PeerConnectionFactory &) = delete; + static std::shared_ptr Create(); // The factory shared by everything created without an explicit one, alive while anything uses it. @@ -45,14 +43,18 @@ namespace python_webrtc { static void Dispose(); + webrtc::Thread *signalingThread() { return _signalingThread.get(); } + + webrtc::Thread *workerThread() { return _workerThread.get(); } + + private: std::unique_ptr _signalingThread; std::unique_ptr _workerThread; - private: // of the process the factory was created in (see forks) const int _generation; - static void Destroy(PeerConnectionFactory *); + static void Destroy(PeerConnectionFactory *factory); static std::weak_ptr _default; static std::mutex _mutex; @@ -64,3 +66,5 @@ namespace python_webrtc { }; } // namespace python_webrtc + +#endif // PYTHON_WEBRTC_INTERFACES_PEER_CONNECTION_FACTORY_H_ diff --git a/python-webrtc/cpp/src/interfaces/rtc_audio_track_source.cpp b/python-webrtc/cpp/src/interfaces/rtc_audio_track_source.cpp index 479a703..720a339 100644 --- a/python-webrtc/cpp/src/interfaces/rtc_audio_track_source.cpp +++ b/python-webrtc/cpp/src/interfaces/rtc_audio_track_source.cpp @@ -24,15 +24,15 @@ namespace python_webrtc { } void RTCAudioTrackSource::AddSink(webrtc::AudioTrackSinkInterface *sink) { - std::lock_guard lock(_sinkMutex); - if (std::find(_sinks.begin(), _sinks.end(), sink) == _sinks.end()) { + const std::scoped_lock lock(_sinkMutex); + if (std::ranges::find(_sinks, sink) == _sinks.end()) { _sinks.push_back(sink); } } void RTCAudioTrackSource::RemoveSink(webrtc::AudioTrackSinkInterface *sink) { - std::lock_guard lock(_sinkMutex); - _sinks.erase(std::remove(_sinks.begin(), _sinks.end(), sink), _sinks.end()); + const std::scoped_lock lock(_sinkMutex); + std::erase(_sinks, sink); } void RTCAudioTrackSource::End() { @@ -41,29 +41,35 @@ namespace python_webrtc { } } - void RTCAudioTrackSource::PushSamples( - const void *samples, int bitsPerSample, int sampleRate, size_t channels, size_t frames) { - std::lock_guard lock(_sinkMutex); - for (auto sink: _sinks) { + void RTCAudioTrackSource::PushSamples(const void *samples, int bitsPerSample, int sampleRate, size_t channels, + size_t frames) { + const std::scoped_lock lock(_sinkMutex); + for (auto *sink : _sinks) { sink->OnData(samples, bitsPerSample, sampleRate, channels, frames, std::nullopt); } } void RTCAudioTrackSource::StartMicrophone() { constexpr int sampleRate = 48000; - constexpr size_t frames = sampleRate / 100; + constexpr auto interval = std::chrono::milliseconds(10); + constexpr size_t frames = sampleRate * interval.count() / 1000; + constexpr int bitsPerSample = 16; + // quiet noise (within 256 of silence), from a linear congruential generator (Numerical Recipes constants) + constexpr uint32_t multiplier = 1664525; + constexpr uint32_t increment = 1013904223; + constexpr int seedShift = 16; + constexpr int amplitude = 256; // the thread never holds a reference to the source, so the source isn't destroyed on it - auto tick = [this, samples = std::vector(frames), seed = uint32_t(1)]() mutable { - for (auto &sample: samples) { - // quiet noise (within 256 of silence), from a linear congruential generator (Numerical Recipes constants) - seed = seed * 1664525 + 1013904223; - sample = static_cast(static_cast(seed >> 16) % 512 - 256); + auto tick = [this, samples = std::vector(frames), seed = uint32_t{1}]() mutable { + for (auto &sample : samples) { + seed = (seed * multiplier) + increment; + sample = static_cast((static_cast(seed >> seedShift) % (2 * amplitude)) - amplitude); } - PushSamples(samples.data(), 16, sampleRate, 1, frames); + PushSamples(samples.data(), bitsPerSample, sampleRate, 1, frames); }; _control = std::make_shared(); _control->microphone = true; - _microphone.Start(std::chrono::milliseconds(10), std::move(tick)); + _microphone.Start(interval, std::move(tick)); } } // namespace python_webrtc diff --git a/python-webrtc/cpp/src/interfaces/rtc_audio_track_source.h b/python-webrtc/cpp/src/interfaces/rtc_audio_track_source.h index b8f7594..146d6ae 100644 --- a/python-webrtc/cpp/src/interfaces/rtc_audio_track_source.h +++ b/python-webrtc/cpp/src/interfaces/rtc_audio_track_source.h @@ -5,7 +5,8 @@ // that can be found in the LICENSE.md file in the root of the project. // -#pragma once +#ifndef PYTHON_WEBRTC_INTERFACES_RTC_AUDIO_TRACK_SOURCE_H_ +#define PYTHON_WEBRTC_INTERFACES_RTC_AUDIO_TRACK_SOURCE_H_ #include #include @@ -39,9 +40,9 @@ namespace python_webrtc { // ends the tracks of the source; must be called on the signaling thread, where they observe it void End(); - void AddSink(webrtc::AudioTrackSinkInterface *) override; + void AddSink(webrtc::AudioTrackSinkInterface * /*unused*/) override; - void RemoveSink(webrtc::AudioTrackSinkInterface *) override; + void RemoveSink(webrtc::AudioTrackSinkInterface * /*unused*/) override; private: // guards the sinks (the sender, processors), removed (and maybe destroyed) on other threads than the one pushing @@ -55,3 +56,5 @@ namespace python_webrtc { }; } // namespace python_webrtc + +#endif // PYTHON_WEBRTC_INTERFACES_RTC_AUDIO_TRACK_SOURCE_H_ diff --git a/python-webrtc/cpp/src/interfaces/rtc_data_channel.cpp b/python-webrtc/cpp/src/interfaces/rtc_data_channel.cpp index f1d38f4..2323768 100644 --- a/python-webrtc/cpp/src/interfaces/rtc_data_channel.cpp +++ b/python-webrtc/cpp/src/interfaces/rtc_data_channel.cpp @@ -15,14 +15,14 @@ namespace python_webrtc { - RTCDataChannel::RTCDataChannel( - std::shared_ptr factory, webrtc::scoped_refptr channel) + RTCDataChannel::RTCDataChannel(std::shared_ptr factory, + webrtc::scoped_refptr channel) : _factory(std::move(factory)), _channel(std::move(channel)) { Hold(); // open only once the open event is delivered _surfacedState.Surface(DataState::kConnecting); // see AliveGuard - _factory->_signalingThread->PostTask(_alive.Guard([this]() { + _factory->signalingThread()->PostTask(_alive.Guard([this]() { _lastState = _channel->state(); if (_lastState != DataState::kOpen && _lastState != DataState::kConnecting) { _surfacedState.Surface(_lastState); @@ -38,10 +38,10 @@ namespace python_webrtc { } RTCDataChannel::~RTCDataChannel() { - BlockingDestructor release("RTCDataChannel"); + const BlockingDestructor release("RTCDataChannel"); // callbacks run on the signaling thread, so after this none of them can be running or start again - _factory->_signalingThread->BlockingCall([this]() { + _factory->signalingThread()->BlockingCall([this]() { // a newer wrapper of the channel may have taken its single observer slot if (holder().TakeObserver(_channel.get(), this)) { _channel->UnregisterObserver(); @@ -85,7 +85,7 @@ namespace python_webrtc { InstanceHolder &RTCDataChannel::holder() { // never destroyed: wrappers may outlive static destructors - static auto holder = new InstanceHolder(); + static auto *holder = new InstanceHolder(); return *holder; } @@ -98,7 +98,7 @@ namespace python_webrtc { _lastState = state; { - std::lock_guard lock(_closeMutex); + const std::scoped_lock lock(_closeMutex); if (_closeRequested && state == DataState::kClosing) { // closing locally has shown the closing state already, and fires no event return; @@ -107,33 +107,34 @@ namespace python_webrtc { } switch (state) { - case DataState::kOpen: - Emit("open", state); - break; - case DataState::kClosing: - Emit("closing", state); - break; - case DataState::kClosed: { - auto error = _channel->error(); - // closing locally isn't an error, even if queued messages couldn't be sent - bool closedLocally; - { - std::lock_guard lock(_closeMutex); - closedLocally = _closeRequested; - } - if (!error.ok() && !closedLocally) { - Emit("error", RTCCallbackException(std::move(error))); - } - Emit("close", state); - break; + case DataState::kOpen: + Emit("open", state); + break; + case DataState::kClosing: + Emit("closing", state); + break; + case DataState::kClosed: { + auto error = _channel->error(); + // closing locally isn't an error, even if queued messages couldn't be sent + bool closedLocally = false; + { + const std::scoped_lock lock(_closeMutex); + closedLocally = _closeRequested; } - default: - break; + if (!error.ok() && !closedLocally) { + Emit("error", RTCCallbackException(std::move(error))); + } + Emit("close", state); + break; + } + default: + break; } } void RTCDataChannel::OnMessage(const webrtc::DataBuffer &buffer) { - Emit("message", DataChannelMessage{std::string(buffer.data.cdata(), buffer.size()), buffer.binary}); + Emit("message", + DataChannelMessage{.data = std::string(buffer.data.cdata(), buffer.size()), .binary = buffer.binary}); } void RTCDataChannel::OnBufferedAmountChange(uint64_t sentDataSize) { @@ -142,7 +143,7 @@ namespace python_webrtc { } void RTCDataChannel::OnAnnounced() { - std::lock_guard lock(_closeMutex); + const std::scoped_lock lock(_closeMutex); // a channel announced by the remote peer is open when its datachannel event fires if (_lastState == DataState::kConnecting || _lastState == DataState::kOpen) { _surfacedState.Surface(DataState::kOpen); @@ -192,14 +193,17 @@ namespace python_webrtc { webrtc::Priority RTCDataChannel::GetPriority() { // the ranges Chromium maps priority values to + constexpr int veryLowMax = 192; + constexpr int lowMax = 384; + constexpr int mediumMax = 768; auto value = _channel->priority().value(); - if (value <= 192) { + if (value <= veryLowMax) { return webrtc::Priority::kVeryLow; } - if (value <= 384) { + if (value <= lowMax) { return webrtc::Priority::kLow; } - if (value <= 768) { + if (value <= mediumMax) { return webrtc::Priority::kMedium; } return webrtc::Priority::kHigh; @@ -210,7 +214,7 @@ namespace python_webrtc { } void RTCDataChannel::SurfaceState(DataState state) { - std::lock_guard lock(_closeMutex); + const std::scoped_lock lock(_closeMutex); // an open event that was queued before closing locally doesn't reopen the channel if (_closeRequested && state == DataState::kOpen) { return; @@ -242,19 +246,24 @@ namespace python_webrtc { } bool RTCDataChannel::DecreaseBufferedAmount(uint64_t sent) { + auto decrease = [sent](uint64_t value) { return value > sent ? value - sent : 0; }; uint64_t amount = _bufferedAmount.load(); - uint64_t decreased; - do { - decreased = amount > sent ? amount - sent : 0; - } while (!_bufferedAmount.compare_exchange_weak(amount, decreased)); + uint64_t decreased = decrease(amount); + while (!_bufferedAmount.compare_exchange_weak(amount, decreased)) { + decreased = decrease(amount); + } auto threshold = _bufferedAmountLowThreshold.load(); return amount > threshold && decreased <= threshold; } - static RTCException sendQueueFullError() { - return {webrtc::RTCErrorType::RESOURCE_EXHAUSTED, "The send queue of the RTCDataChannel is full"}; - } + namespace { + + RTCException sendQueueFullError() { + return {webrtc::RTCErrorType::RESOURCE_EXHAUSTED, "The send queue of the RTCDataChannel is full"}; + } + + } // namespace void RTCDataChannel::Send(const std::string &data, bool binary) { if (GetReadyState() != DataState::kOpen) { @@ -283,7 +292,7 @@ namespace python_webrtc { // read before locking: it's a call to the signaling thread, which takes the lock in OnStateChange auto current = _channel->state(); { - std::lock_guard lock(_closeMutex); + const std::scoped_lock lock(_closeMutex); auto state = _surfacedState.Get(current); if (state == DataState::kClosing || state == DataState::kClosed) { return; diff --git a/python-webrtc/cpp/src/interfaces/rtc_data_channel.h b/python-webrtc/cpp/src/interfaces/rtc_data_channel.h index 20bea2e..c8925c4 100644 --- a/python-webrtc/cpp/src/interfaces/rtc_data_channel.h +++ b/python-webrtc/cpp/src/interfaces/rtc_data_channel.h @@ -5,7 +5,8 @@ // that can be found in the LICENSE.md file in the root of the project. // -#pragma once +#ifndef PYTHON_WEBRTC_INTERFACES_RTC_DATA_CHANNEL_H_ +#define PYTHON_WEBRTC_INTERFACES_RTC_DATA_CHANNEL_H_ #include #include @@ -18,13 +19,13 @@ #include -#include "peer_connection_factory.h" +#include "../enums/enums.h" #include "../utils/alive_guard.h" #include "../utils/instance_holder.h" #include "../utils/listeners.h" #include "../utils/locked_function.h" #include "../utils/surfaced.h" -#include "../enums/enums.h" +#include "peer_connection_factory.h" namespace python_webrtc { @@ -39,10 +40,14 @@ namespace python_webrtc { using DataState = webrtc::DataChannelInterface::DataState; // Starts holding its events (see Listeners::Hold), Python releases them once it has the channel - RTCDataChannel(std::shared_ptr, webrtc::scoped_refptr); + RTCDataChannel(std::shared_ptr factory, + webrtc::scoped_refptr channel); ~RTCDataChannel() override; + RTCDataChannel(const RTCDataChannel &) = delete; + RTCDataChannel &operator=(const RTCDataChannel &) = delete; + static void Init(pybind11::module &m); static InstanceHolder &holder(); @@ -129,3 +134,5 @@ namespace python_webrtc { }; } // namespace python_webrtc + +#endif // PYTHON_WEBRTC_INTERFACES_RTC_DATA_CHANNEL_H_ diff --git a/python-webrtc/cpp/src/interfaces/rtc_dtls_transport.cpp b/python-webrtc/cpp/src/interfaces/rtc_dtls_transport.cpp index 65ba0be..dbda994 100644 --- a/python-webrtc/cpp/src/interfaces/rtc_dtls_transport.cpp +++ b/python-webrtc/cpp/src/interfaces/rtc_dtls_transport.cpp @@ -10,37 +10,42 @@ #include #include "../exceptions.h" +#include "../utils/buffer.h" #include "../utils/gil.h" #include "../utils/libwebrtc_thread.h" namespace python_webrtc { - static std::vector copyCertificates(const webrtc::DtlsTransportInformation& information) { - auto certificates = information.remote_ssl_certificates(); - if (certificates) { - auto size = certificates->GetSize(); + namespace { - auto derCertificates = std::vector(); - derCertificates.reserve(size); + std::vector copyCertificates(const webrtc::DtlsTransportInformation &information) { + const auto *certificates = information.remote_ssl_certificates(); + if (certificates != nullptr) { + auto size = certificates->GetSize(); - for (unsigned long i = 0; i < size; ++i) { - webrtc::Buffer buffer; - certificates->Get(i).ToDER(&buffer); - derCertificates.emplace_back(std::move(buffer)); + auto derCertificates = std::vector(); + derCertificates.reserve(size); + + for (unsigned long i = 0; i < size; ++i) { + webrtc::Buffer buffer; + certificates->Get(i).ToDER(&buffer); + derCertificates.emplace_back(std::move(buffer)); + } + + return derCertificates; } - return derCertificates; + return {}; } - return {}; - } + } // namespace - RTCDtlsTransport::RTCDtlsTransport( - std::shared_ptr factory, webrtc::scoped_refptr transport - ) : _factory(std::move(factory)), _transport(std::move(transport)) { + RTCDtlsTransport::RTCDtlsTransport(std::shared_ptr factory, + webrtc::scoped_refptr transport) + : _factory(std::move(factory)), _transport(std::move(transport)) { _iceTransport = RTCIceTransport::holder().GetOrCreate(_factory, _transport->ice_transport()); - _factory->_workerThread->BlockingCall([this]() { + _factory->workerThread()->BlockingCall([this]() { _transport->RegisterObserver(this); holder().SetObserver(_transport.get(), this); _observing = true; @@ -56,10 +61,10 @@ namespace python_webrtc { } RTCDtlsTransport::~RTCDtlsTransport() { - BlockingDestructor release("RTCDtlsTransport"); + const BlockingDestructor release("RTCDtlsTransport"); // callbacks run on the network thread, so after this none of them can be running or start again - _factory->_workerThread->BlockingCall([this]() { Unobserve(); }); + _factory->workerThread()->BlockingCall([this]() { Unobserve(); }); _iceTransport = nullptr; _transport = nullptr; @@ -70,28 +75,28 @@ namespace python_webrtc { Listeners::BindClass(m, "RTCDtlsTransport") .def_property_readonly("iceTransport", nogil_fn(&RTCDtlsTransport::GetIceTransport)) .def_property_readonly("state", nogil_fn(&RTCDtlsTransport::GetState)) - .def("getRemoteCertificates", [](RTCDtlsTransport &self) { - pybind11::list certificates; - for (const auto &certificate: self.GetRemoteCertificates()) { - auto data = reinterpret_cast(certificate.data()); - certificates.append(pybind11::bytes(data, certificate.size())); - } - return certificates; - }) + .def("getRemoteCertificates", + [](RTCDtlsTransport &self) { + pybind11::list certificates; + for (const auto &certificate : self.GetRemoteCertificates()) { + certificates.append(Bytes(certificate.data(), certificate.size())); + } + return certificates; + }) .def("_surfaceState", &RTCDtlsTransport::SurfaceState, nogil(), pybind11::arg("state")); } InstanceHolder &RTCDtlsTransport::holder() { // never destroyed: wrappers may outlive static destructors - static auto holder = new InstanceHolder(); + static auto *holder = new InstanceHolder(); return *holder; } void RTCDtlsTransport::OnStateChange(webrtc::DtlsTransportInformation information) { - bool changed; - webrtc::DtlsTransportState previous; + bool changed = false; + webrtc::DtlsTransportState previous{}; { - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); previous = _state; changed = _state != information.state(); _state = information.state(); @@ -141,18 +146,19 @@ namespace python_webrtc { } webrtc::DtlsTransportState RTCDtlsTransport::GetState() { - webrtc::DtlsTransportState state; + webrtc::DtlsTransportState state{}; { - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); state = _state; } return _surfacedState.Get(state); } std::vector RTCDtlsTransport::GetRemoteCertificates() { - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); std::vector certificates; - for (const auto &certificate: _certificates) { + certificates.reserve(_certificates.size()); + for (const auto &certificate : _certificates) { certificates.emplace_back(certificate.data(), certificate.size()); } return certificates; diff --git a/python-webrtc/cpp/src/interfaces/rtc_dtls_transport.h b/python-webrtc/cpp/src/interfaces/rtc_dtls_transport.h index 4e96423..5603f0e 100644 --- a/python-webrtc/cpp/src/interfaces/rtc_dtls_transport.h +++ b/python-webrtc/cpp/src/interfaces/rtc_dtls_transport.h @@ -5,7 +5,8 @@ // that can be found in the LICENSE.md file in the root of the project. // -#pragma once +#ifndef PYTHON_WEBRTC_INTERFACES_RTC_DTLS_TRANSPORT_H_ +#define PYTHON_WEBRTC_INTERFACES_RTC_DTLS_TRANSPORT_H_ #include #include @@ -15,30 +16,34 @@ #include -#include "peer_connection_factory.h" -#include "rtc_ice_transport.h" +#include "../enums/enums.h" #include "../utils/instance_holder.h" #include "../utils/listeners.h" #include "../utils/surfaced.h" -#include "../enums/enums.h" +#include "peer_connection_factory.h" +#include "rtc_ice_transport.h" namespace python_webrtc { class RTCDtlsTransport : public webrtc::DtlsTransportObserverInterface, public Listeners, public SingleObserverSlot { public: - explicit RTCDtlsTransport(std::shared_ptr, webrtc::scoped_refptr); + explicit RTCDtlsTransport(std::shared_ptr factory, + webrtc::scoped_refptr transport); ~RTCDtlsTransport() override; + RTCDtlsTransport(const RTCDtlsTransport &) = delete; + RTCDtlsTransport &operator=(const RTCDtlsTransport &) = delete; + static void Init(pybind11::module &m); static InstanceHolder &holder(); webrtc::scoped_refptr transport() { return _transport; } - void OnStateChange(webrtc::DtlsTransportInformation) override; + void OnStateChange(webrtc::DtlsTransportInformation info) override; - void OnError(webrtc::RTCError) override; + void OnError(webrtc::RTCError error) override; // a closed connection fires no events of its transports, which show their current state void OnPeerConnectionClosed(); @@ -75,3 +80,5 @@ namespace python_webrtc { }; } // namespace python_webrtc + +#endif // PYTHON_WEBRTC_INTERFACES_RTC_DTLS_TRANSPORT_H_ diff --git a/python-webrtc/cpp/src/interfaces/rtc_dtmf_sender.cpp b/python-webrtc/cpp/src/interfaces/rtc_dtmf_sender.cpp index 40de318..1e5e71d 100644 --- a/python-webrtc/cpp/src/interfaces/rtc_dtmf_sender.cpp +++ b/python-webrtc/cpp/src/interfaces/rtc_dtmf_sender.cpp @@ -15,21 +15,21 @@ namespace python_webrtc { - RTCDTMFSender::RTCDTMFSender( - std::shared_ptr factory, webrtc::scoped_refptr dtmf) + RTCDTMFSender::RTCDTMFSender(std::shared_ptr factory, + webrtc::scoped_refptr dtmf) : _factory(std::move(factory)), _dtmf(std::move(dtmf)) { // see AliveGuard - _factory->_signalingThread->PostTask(_alive.Guard([this]() { + _factory->signalingThread()->PostTask(_alive.Guard([this]() { _dtmf->RegisterObserver(this); holder().SetObserver(_dtmf.get(), this); })); } RTCDTMFSender::~RTCDTMFSender() { - BlockingDestructor release("RTCDTMFSender"); + const BlockingDestructor release("RTCDTMFSender"); // callbacks run on the signaling thread, so after this none of them can be running or start again - _factory->_signalingThread->BlockingCall([this]() { + _factory->signalingThread()->BlockingCall([this]() { // a newer wrapper of the sender may have taken its single observer slot if (holder().TakeObserver(_dtmf.get(), this)) { _dtmf->UnregisterObserver(); @@ -42,26 +42,27 @@ namespace python_webrtc { Listeners::BindClass(m, "RTCDTMFSender") .def_property_readonly("toneBuffer", nogil_fn(&RTCDTMFSender::GetToneBuffer)) .def_property_readonly("canInsertDTMF", nogil_fn(&RTCDTMFSender::GetCanInsertDtmf)) - .def("insertDTMF", &RTCDTMFSender::InsertDtmf, nogil(), - pybind11::arg("tones"), pybind11::arg("duration"), pybind11::arg("interToneGap")) - .def("_surfaceBuffer", &RTCDTMFSender::SurfaceBuffer, nogil(), - pybind11::arg("buffer"), pybind11::arg("insertion")); + .def("insertDTMF", &RTCDTMFSender::InsertDtmf, nogil(), pybind11::arg("tones"), pybind11::arg("duration"), + pybind11::arg("interToneGap")) + .def("_surfaceBuffer", &RTCDTMFSender::SurfaceBuffer, nogil(), pybind11::arg("buffer"), + pybind11::arg("insertion")); } InstanceHolder &RTCDTMFSender::holder() { // never destroyed: wrappers may outlive static destructors - static auto holder = new InstanceHolder(); + static auto *holder = new InstanceHolder(); return *holder; } - void RTCDTMFSender::SetTransceiver(std::function()> transceiver) { + void + RTCDTMFSender::SetTransceiver(std::function()> transceiver) { _transceiver.Set(std::move(transceiver)); } void RTCDTMFSender::OnToneChange(const std::string &tone, const std::string &toneBuffer) { - uint64_t insertion; + uint64_t insertion = 0; { - std::lock_guard lock(_bufferMutex); + const std::scoped_lock lock(_bufferMutex); insertion = _insertions; } Emit("tonechange", tone, toneBuffer, insertion); @@ -84,20 +85,23 @@ namespace python_webrtc { } { // tone changes of the tones inserted before don't change the buffer anymore - std::lock_guard lock(_bufferMutex); + const std::scoped_lock lock(_bufferMutex); ++_insertions; } // libwebrtc takes longer tones and gaps than the specification - if (!_dtmf->InsertDtmf(tones, std::max(duration, 70), std::max(interToneGap, 50)) && !tones.empty()) { + constexpr int minDuration = 70; + constexpr int minInterToneGap = 50; + if (!_dtmf->InsertDtmf(tones, std::max(duration, minDuration), std::max(interToneGap, minInterToneGap)) && + !tones.empty()) { throw RTCException(webrtc::RTCErrorType::INVALID_STATE, "The DTMF sender can't send tones yet"); } - std::lock_guard lock(_bufferMutex); + const std::scoped_lock lock(_bufferMutex); _surfacedBuffer = tones; } std::string RTCDTMFSender::GetToneBuffer() { { - std::lock_guard lock(_bufferMutex); + const std::scoped_lock lock(_bufferMutex); if (HasListeners() && _surfacedBuffer) { return *_surfacedBuffer; } @@ -106,7 +110,7 @@ namespace python_webrtc { } void RTCDTMFSender::SurfaceBuffer(const std::string &buffer, uint64_t insertion) { - std::lock_guard lock(_bufferMutex); + const std::scoped_lock lock(_bufferMutex); // a tone played before the last insertDTMF() doesn't change what it set if (insertion == _insertions) { _surfacedBuffer = buffer; diff --git a/python-webrtc/cpp/src/interfaces/rtc_dtmf_sender.h b/python-webrtc/cpp/src/interfaces/rtc_dtmf_sender.h index e924e6c..c906a67 100644 --- a/python-webrtc/cpp/src/interfaces/rtc_dtmf_sender.h +++ b/python-webrtc/cpp/src/interfaces/rtc_dtmf_sender.h @@ -5,7 +5,8 @@ // that can be found in the LICENSE.md file in the root of the project. // -#pragma once +#ifndef PYTHON_WEBRTC_INTERFACES_RTC_DTMF_SENDER_H_ +#define PYTHON_WEBRTC_INTERFACES_RTC_DTMF_SENDER_H_ #include #include @@ -18,21 +19,25 @@ #include -#include "peer_connection_factory.h" #include "../utils/alive_guard.h" #include "../utils/instance_holder.h" #include "../utils/listeners.h" #include "../utils/locked_function.h" +#include "peer_connection_factory.h" namespace python_webrtc { // Sends DTMF tones on an audio sender (webrtc.RTCDTMFSender) class RTCDTMFSender : public webrtc::DtmfSenderObserverInterface, public Listeners, public SingleObserverSlot { public: - RTCDTMFSender(std::shared_ptr, webrtc::scoped_refptr); + RTCDTMFSender(std::shared_ptr factory, + webrtc::scoped_refptr dtmf); ~RTCDTMFSender() override; + RTCDTMFSender(const RTCDTMFSender &) = delete; + RTCDTMFSender &operator=(const RTCDTMFSender &) = delete; + static void Init(pybind11::module &m); static InstanceHolder &holder(); @@ -67,3 +72,5 @@ namespace python_webrtc { }; } // namespace python_webrtc + +#endif // PYTHON_WEBRTC_INTERFACES_RTC_DTMF_SENDER_H_ diff --git a/python-webrtc/cpp/src/interfaces/rtc_ice_transport.cpp b/python-webrtc/cpp/src/interfaces/rtc_ice_transport.cpp index c8c44fe..f8a7e8f 100644 --- a/python-webrtc/cpp/src/interfaces/rtc_ice_transport.cpp +++ b/python-webrtc/cpp/src/interfaces/rtc_ice_transport.cpp @@ -46,6 +46,9 @@ namespace python_webrtc { channel = nullptr; } + StandaloneIce(const StandaloneIce &) = delete; + StandaloneIce &operator=(const StandaloneIce &) = delete; + webrtc::Environment env; std::unique_ptr networkManager; std::unique_ptr socketFactory; @@ -54,25 +57,23 @@ namespace python_webrtc { webrtc::scoped_refptr transport; }; - RTCIceTransport::RTCIceTransport( - std::shared_ptr factory, webrtc::scoped_refptr transport) + RTCIceTransport::RTCIceTransport(std::shared_ptr factory, + webrtc::scoped_refptr transport) : _factory(std::move(factory)), _transport(std::move(transport)) { - _factory->_workerThread->BlockingCall([this]() { - auto internal = _transport->internal(); + _factory->workerThread()->BlockingCall([this]() { + auto *internal = _transport->internal(); if (internal) { auto alive = _alive; - internal->SubscribeIceTransportStateChanged( - this, [this, alive](webrtc::IceTransportInternal *transport) { - if (*alive) { - OnStateChanged(transport); - } - }); - internal->AddGatheringStateCallback( - this, [this, alive](webrtc::IceTransportInternal *transport) { - if (*alive) { - OnGatheringStateChanged(transport); - } - }); + internal->SubscribeIceTransportStateChanged(this, [this, alive](webrtc::IceTransportInternal *transport) { + if (*alive) { + OnStateChanged(transport); + } + }); + internal->AddGatheringStateCallback(this, [this, alive](webrtc::IceTransportInternal *transport) { + if (*alive) { + OnGatheringStateChanged(transport); + } + }); _subscribed = internal; } TakeSnapshot(); @@ -80,10 +81,10 @@ namespace python_webrtc { } RTCIceTransport::~RTCIceTransport() { - BlockingDestructor release("RTCIceTransport"); + const BlockingDestructor release("RTCIceTransport"); // callbacks run on the network thread, so after this none of them can be running or start again - _factory->_workerThread->BlockingCall([this]() { + _factory->workerThread()->BlockingCall([this]() { *_alive = false; // the internal transport is gone (with its callbacks) once the ice transport is cleared if (_subscribed && _transport->internal() == _subscribed) { @@ -103,7 +104,8 @@ namespace python_webrtc { void RTCIceTransport::Init(pybind11::module &m) { Listeners::BindClass(m, "RTCIceTransport") // a transport of its own - .def(pybind11::init(nogil_factory(+[]() { return CreateStandalone(PeerConnectionFactory::GetOrCreateDefault()); }))) + .def(pybind11::init( + nogil_factory(+[]() { return CreateStandalone(PeerConnectionFactory::GetOrCreateDefault()); }))) .def_property_readonly("component", nogil_fn(&RTCIceTransport::GetComponent)) .def_property_readonly("gatheringState", nogil_fn(&RTCIceTransport::GetGatheringState)) .def_property_readonly("role", nogil_fn(&RTCIceTransport::GetRole)) @@ -114,11 +116,10 @@ namespace python_webrtc { .def("getLocalParameters", &RTCIceTransport::GetLocalParameters, nogil()) .def("getRemoteParameters", &RTCIceTransport::GetRemoteParameters, nogil()) .def("gather", &RTCIceTransport::Gather, nogil(), pybind11::arg("policy"), pybind11::arg("iceServers")) - .def("start", &RTCIceTransport::Start, nogil(), - pybind11::arg("usernameFragment"), pybind11::arg("password"), pybind11::arg("role")) - .def("addRemoteCandidate", &RTCIceTransport::AddStandaloneRemoteCandidate, nogil(), - pybind11::arg("candidate"), pybind11::arg("sdpMid"), pybind11::arg("sdpMLineIndex"), - pybind11::arg("usernameFragment")) + .def("start", &RTCIceTransport::Start, nogil(), pybind11::arg("usernameFragment"), pybind11::arg("password"), + pybind11::arg("role")) + .def("addRemoteCandidate", &RTCIceTransport::AddStandaloneRemoteCandidate, nogil(), pybind11::arg("candidate"), + pybind11::arg("sdpMid"), pybind11::arg("sdpMLineIndex"), pybind11::arg("usernameFragment")) .def("stop", &RTCIceTransport::StopStandalone, nogil()) .def_property_readonly("_standalone", &RTCIceTransport::IsStandalone) .def("_surfaceState", &RTCIceTransport::SurfaceState, nogil(), pybind11::arg("state")) @@ -128,18 +129,21 @@ namespace python_webrtc { InstanceHolder &RTCIceTransport::holder() { // never destroyed: wrappers may outlive static destructors - static auto holder = new InstanceHolder(); + static auto *holder = new InstanceHolder(); return *holder; } RTCIceTransport::StateChange RTCIceTransport::TakeSnapshot() { - std::lock_guard lock(_mutex); - StateChange change{_state, _state, _gatheringState, _gatheringState}; + const std::scoped_lock lock(_mutex); + StateChange change{.previousState = _state, + .state = _state, + .previousGatheringState = _gatheringState, + .gatheringState = _gatheringState}; if (_stopped) { return change; } - auto internal = _transport->internal(); - if (internal) { + auto *internal = _transport->internal(); + if (internal != nullptr) { _component = internal->component() == 1 ? RTCIceComponent::kRtp : RTCIceComponent::kRtcp; _role = internal->GetIceRole(); _state = internal->GetIceTransportState(); @@ -163,9 +167,9 @@ namespace python_webrtc { } void RTCIceTransport::OnRTCDtlsTransportStopped() { - webrtc::IceTransportState previous; + webrtc::IceTransportState previous{}; { - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); previous = _state; _state = webrtc::IceTransportState::kClosed; } @@ -180,7 +184,7 @@ namespace python_webrtc { void RTCIceTransport::OnPeerConnectionClosed() { Mute(); { - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); _gatheringState = _surfacedGatheringState.Get(_gatheringState); _gatheringFrozen = true; } @@ -188,7 +192,7 @@ namespace python_webrtc { _surfacedGatheringState.Reset(); } - void RTCIceTransport::OnStateChanged(webrtc::IceTransportInternal *) { + void RTCIceTransport::OnStateChanged(webrtc::IceTransportInternal * /*unused*/) { auto change = TakeSnapshot(); if (change.state == change.previousState) { return; @@ -206,7 +210,7 @@ namespace python_webrtc { Emit("statechange", change.state); } - void RTCIceTransport::OnGatheringStateChanged(webrtc::IceTransportInternal *) { + void RTCIceTransport::OnGatheringStateChanged(webrtc::IceTransportInternal * /*unused*/) { auto change = TakeSnapshot(); auto current = change.gatheringState; if (current == change.previousGatheringState) { @@ -227,14 +231,14 @@ namespace python_webrtc { } RTCIceComponent RTCIceTransport::GetComponent() { - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); return _component; } webrtc::IceGatheringState RTCIceTransport::GetGatheringState() { - webrtc::IceGatheringState state; + webrtc::IceGatheringState state{}; { - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); state = _gatheringState; } return _surfacedGatheringState.Get(state); @@ -242,16 +246,16 @@ namespace python_webrtc { webrtc::IceRole RTCIceTransport::GetRole() { { - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); if (!_roleKnown) { return webrtc::IceRole::ICEROLE_UNKNOWN; } } - auto role = _factory->_workerThread->BlockingCall([this]() { - auto internal = _transport ? _transport->internal() : nullptr; + auto role = _factory->workerThread()->BlockingCall([this]() { + auto *internal = _transport ? _transport->internal() : nullptr; return internal ? internal->GetIceRole() : webrtc::IceRole::ICEROLE_UNKNOWN; }); - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); // a closed transport has the last one if (role != webrtc::IceRole::ICEROLE_UNKNOWN) { _role = role; @@ -260,14 +264,14 @@ namespace python_webrtc { } void RTCIceTransport::SetRoleKnown() { - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); _roleKnown = true; } webrtc::IceTransportState RTCIceTransport::GetState() { - webrtc::IceTransportState state; + webrtc::IceTransportState state{}; { - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); state = _state; } return _surfacedState.Get(state); @@ -275,14 +279,14 @@ namespace python_webrtc { std::optional> RTCIceTransport::GetSelectedCandidatePair() { { - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); if (_stopped) { return std::nullopt; } } std::optional> result; - _factory->_workerThread->BlockingCall([&]() { - auto internal = _transport ? _transport->internal() : nullptr; + _factory->workerThread()->BlockingCall([&]() { + auto *internal = _transport ? _transport->internal() : nullptr; auto pair = internal ? internal->GetSelectedCandidatePair() : std::nullopt; if (!pair) { return; @@ -291,17 +295,17 @@ namespace python_webrtc { auto mid = internal->transport_name(); auto local = webrtc::CreateIceCandidate(mid, 0, pair->local_candidate()); auto remote = webrtc::CreateIceCandidate(mid, 0, pair->remote_candidate()); - auto signaled = pair->remote_candidate().is_prflx() ? FindSignaledCandidate(pair->remote_candidate()) - : std::nullopt; - bool peerReflexive = pair->remote_candidate().is_prflx() && !signaled; + auto signaled = + pair->remote_candidate().is_prflx() ? FindSignaledCandidate(pair->remote_candidate()) : std::nullopt; + const bool peerReflexive = pair->remote_candidate().is_prflx() && !signaled; result.emplace(IceCandidateInit(*local), signaled ? *signaled : IceCandidateInit(*remote), peerReflexive); }); return result; } std::optional RTCIceTransport::FindSignaledCandidate(const webrtc::Candidate &candidate) { - std::lock_guard lock(_mutex); - for (const auto &signaled: _remoteCandidates) { + const std::scoped_lock lock(_mutex); + for (const auto &signaled : _remoteCandidates) { webrtc::SdpParseError error; auto parsed = webrtc::IceCandidate::Create(signaled.sdpMid, signaled.sdpMLineIndex, signaled.candidate, &error); if (parsed && parsed->candidate().address().port() == candidate.address().port() && @@ -312,44 +316,49 @@ namespace python_webrtc { return std::nullopt; } - static void addCandidate(std::vector &candidates, const IceCandidateInit &candidate) { - if (candidate.candidate.empty()) { - return; - } - for (const auto &known: candidates) { - if (known.candidate == candidate.candidate) { + namespace { + + void addCandidate(std::vector &candidates, const IceCandidateInit &candidate) { + if (candidate.candidate.empty()) { return; } + for (const auto &known : candidates) { + if (known.candidate == candidate.candidate) { + return; + } + } + candidates.push_back(candidate); } - candidates.push_back(candidate); - } + + } // namespace void RTCIceTransport::AddLocalCandidate(const IceCandidateInit &candidate) { - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); addCandidate(_localCandidates, candidate); } void RTCIceTransport::AddRemoteCandidate(const IceCandidateInit &candidate) { - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); addCandidate(_remoteCandidates, candidate); } std::vector RTCIceTransport::GetLocalCandidates() { - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); if (_standalone && HasListeners()) { // the candidates of a transport of its own come with their events - return {_localCandidates.begin(), _localCandidates.begin() + std::min(_surfacedLocal, _localCandidates.size())}; + const auto surfaced = static_cast(std::min(_surfacedLocal, _localCandidates.size())); + return {_localCandidates.begin(), _localCandidates.begin() + surfaced}; } return _localCandidates; } void RTCIceTransport::SurfaceLocalCandidate() { - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); ++_surfacedLocal; } std::vector RTCIceTransport::GetRemoteCandidates() { - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); return _remoteCandidates; } @@ -359,7 +368,7 @@ namespace python_webrtc { std::optional> RTCIceTransport::GetParameters(bool local) { { - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); if (_standalone) { return local ? _localParameters : _remoteParameters; } @@ -376,14 +385,14 @@ namespace python_webrtc { return GetParameters(false); } - std::shared_ptr RTCIceTransport::CreateStandalone( - const std::shared_ptr &factory) { + std::shared_ptr + RTCIceTransport::CreateStandalone(const std::shared_ptr &factory) { std::shared_ptr standalone; webrtc::scoped_refptr transport; - std::pair parameters( - webrtc::CreateRandomString(webrtc::ICE_UFRAG_LENGTH), webrtc::CreateRandomString(webrtc::ICE_PWD_LENGTH)); - factory->_workerThread->BlockingCall([&]() { - standalone = std::make_shared(factory->_workerThread.get()); + std::pair parameters(webrtc::CreateRandomString(webrtc::ICE_UFRAG_LENGTH), + webrtc::CreateRandomString(webrtc::ICE_PWD_LENGTH)); + factory->workerThread()->BlockingCall([&]() { + standalone = std::make_shared(factory->workerThread()); webrtc::IceTransportInit init(standalone->env); init.set_port_allocator(standalone->allocator.get()); standalone->channel = webrtc::P2PTransportChannel::Create("", 1, std::move(init)); @@ -394,12 +403,12 @@ namespace python_webrtc { auto wrapper = holder().GetOrCreate(factory, transport); { - std::lock_guard lock(wrapper->_mutex); + const std::scoped_lock lock(wrapper->_mutex); wrapper->_standalone = std::move(standalone); wrapper->_localParameters = parameters; } - factory->_workerThread->BlockingCall([&]() { - auto internal = transport->internal(); + factory->workerThread()->BlockingCall([&]() { + auto *internal = transport->internal(); auto alive = wrapper->_alive; auto *self = wrapper.get(); internal->SubscribeCandidateGathered( @@ -413,8 +422,8 @@ namespace python_webrtc { // both ends took the same role: the one told to switch does (RFC 8445 section 7.3.1.1) internal->SubscribeRoleConflict(self, [alive](webrtc::IceTransportInternal *transport) { if (*alive) { - transport->SetIceRole(transport->GetIceRole() == webrtc::ICEROLE_CONTROLLING - ? webrtc::ICEROLE_CONTROLLED : webrtc::ICEROLE_CONTROLLING); + transport->SetIceRole(transport->GetIceRole() == webrtc::ICEROLE_CONTROLLING ? webrtc::ICEROLE_CONTROLLED + : webrtc::ICEROLE_CONTROLLING); } }); internal->SetCandidatePairChangeCallback([self, alive](const webrtc::CandidatePairChangeEvent &) { @@ -426,11 +435,11 @@ namespace python_webrtc { return wrapper; } - void RTCIceTransport::Gather( - webrtc::PeerConnectionInterface::IceTransportsType policy, const std::vector &iceServers) { + void RTCIceTransport::Gather(webrtc::PeerConnectionInterface::IceTransportsType policy, + const std::vector &iceServers) { auto servers = toIceServers(iceServers); webrtc::RTCError error; - _factory->_workerThread->BlockingCall([&]() { + _factory->workerThread()->BlockingCall([&]() { webrtc::ServerAddresses stunServers; std::vector turnServers; error = webrtc::ParseIceServersOrError(servers, &stunServers, &turnServers); @@ -438,8 +447,8 @@ namespace python_webrtc { return; } _standalone->allocator->SetConfiguration(stunServers, turnServers, 0, webrtc::NO_PRUNE); - _standalone->allocator->SetCandidateFilter( - policy == webrtc::PeerConnectionInterface::kRelay ? webrtc::CF_RELAY : webrtc::CF_ALL); + _standalone->allocator->SetCandidateFilter(policy == webrtc::PeerConnectionInterface::kRelay ? webrtc::CF_RELAY + : webrtc::CF_ALL); _transport->internal()->MaybeStartGathering(); }); if (!error.ok()) { @@ -449,14 +458,14 @@ namespace python_webrtc { } void RTCIceTransport::Start(const std::string &usernameFragment, const std::string &password, webrtc::IceRole role) { - bool changed; + bool changed = false; { - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); if (_startedRole && *_startedRole != role) { throw RTCException(webrtc::RTCErrorType::INVALID_STATE, "The transport started with another role"); } _startedRole = role; - std::pair parameters(usernameFragment, password); + const std::pair parameters(usernameFragment, password); changed = _remoteParameters && *_remoteParameters != parameters; _remoteParameters = parameters; _roleKnown = true; @@ -464,8 +473,8 @@ namespace python_webrtc { _remoteCandidates.clear(); } } - _factory->_workerThread->BlockingCall([&]() { - auto internal = _transport->internal(); + _factory->workerThread()->BlockingCall([&]() { + auto *internal = _transport->internal(); if (changed) { // the candidates were for the previous remote agent internal->RemoveAllRemoteCandidates(); @@ -476,31 +485,30 @@ namespace python_webrtc { SurfaceCurrent(); } - void RTCIceTransport::AddStandaloneRemoteCandidate( - const std::string &candidate, const std::string &sdpMid, int sdpMLineIndex, - const std::optional &usernameFragment) { + void RTCIceTransport::AddStandaloneRemoteCandidate(const std::string &candidate, const std::string &sdpMid, + int sdpMLineIndex, + const std::optional &usernameFragment) { IceCandidateInit init(sdpMid, sdpMLineIndex, usernameFragment); init.candidate = candidate; webrtc::SdpParseError error; auto parsed = webrtc::IceCandidate::Create(sdpMid, sdpMLineIndex, candidate, &error); if (!parsed) { - throw RTCException(webrtc::RTCErrorType::INTERNAL_ERROR, "Failed to parse the ICE candidate: " + error.description); + throw RTCException(webrtc::RTCErrorType::INTERNAL_ERROR, + "Failed to parse the ICE candidate: " + error.description); } AddRemoteCandidate(init); - _factory->_workerThread->BlockingCall([&]() { - _transport->internal()->AddRemoteCandidate(parsed->candidate()); - }); + _factory->workerThread()->BlockingCall([&]() { _transport->internal()->AddRemoteCandidate(parsed->candidate()); }); SurfaceCurrent(); } void RTCIceTransport::StopStandalone() { - _factory->_workerThread->BlockingCall([&]() { + _factory->workerThread()->BlockingCall([&]() { // nothing more to connect with, and no more events *_alive = false; _transport->internal()->RemoveAllRemoteCandidates(); }); { - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); _stopped = true; _state = webrtc::IceTransportState::kClosed; } @@ -510,15 +518,15 @@ namespace python_webrtc { } void RTCIceTransport::SurfaceCurrent() { - _factory->_workerThread->BlockingCall([this]() { TakeSnapshot(); }); - std::lock_guard lock(_mutex); + _factory->workerThread()->BlockingCall([this]() { TakeSnapshot(); }); + const std::scoped_lock lock(_mutex); _surfacedState.Surface(_state); _surfacedGatheringState.Surface(_gatheringState); } void RTCIceTransport::CreatedByDescription() { { - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); if (_gatheringState == webrtc::IceGatheringState::kIceGatheringNew) { return; } @@ -535,7 +543,7 @@ namespace python_webrtc { auto pair = GetSelectedCandidatePair(); auto key = pair ? std::get<0>(*pair).candidate + "|" + std::get<1>(*pair).candidate : std::string(); { - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); if (key == _selectedPair) { return; } diff --git a/python-webrtc/cpp/src/interfaces/rtc_ice_transport.h b/python-webrtc/cpp/src/interfaces/rtc_ice_transport.h index 46d2f03..b99a361 100644 --- a/python-webrtc/cpp/src/interfaces/rtc_ice_transport.h +++ b/python-webrtc/cpp/src/interfaces/rtc_ice_transport.h @@ -5,7 +5,8 @@ // that can be found in the LICENSE.md file in the root of the project. // -#pragma once +#ifndef PYTHON_WEBRTC_INTERFACES_RTC_ICE_TRANSPORT_H_ +#define PYTHON_WEBRTC_INTERFACES_RTC_ICE_TRANSPORT_H_ #include #include @@ -19,7 +20,6 @@ #include #include -#include "peer_connection_factory.h" #include "../enums/enums.h" #include "../models/python_webrtc/rtc_configuration.h" #include "../models/python_webrtc/rtc_ice_candidate.h" @@ -27,6 +27,7 @@ #include "../utils/listeners.h" #include "../utils/locked_function.h" #include "../utils/surfaced.h" +#include "peer_connection_factory.h" namespace python_webrtc { @@ -35,10 +36,14 @@ namespace python_webrtc { class RTCIceTransport : public Listeners { public: - explicit RTCIceTransport(std::shared_ptr, webrtc::scoped_refptr); + explicit RTCIceTransport(std::shared_ptr factory, + webrtc::scoped_refptr transport); ~RTCIceTransport() override; + RTCIceTransport(const RTCIceTransport &) = delete; + RTCIceTransport &operator=(const RTCIceTransport &) = delete; + static void Init(pybind11::module &m); static InstanceHolder &holder(); @@ -108,7 +113,8 @@ namespace python_webrtc { bool IsStandalone() { return _standalone != nullptr; } - void Gather(webrtc::PeerConnectionInterface::IceTransportsType policy, const std::vector &iceServers); + void Gather(webrtc::PeerConnectionInterface::IceTransportsType policy, + const std::vector &iceServers); // the remote parameters (flushing the remote candidates if they changed) and the role void Start(const std::string &usernameFragment, const std::string &password, webrtc::IceRole role); @@ -137,9 +143,9 @@ namespace python_webrtc { // reads the states of the transport, on the network thread StateChange TakeSnapshot(); - void OnStateChanged(webrtc::IceTransportInternal *); + void OnStateChanged(webrtc::IceTransportInternal * /*unused*/); - void OnGatheringStateChanged(webrtc::IceTransportInternal *); + void OnGatheringStateChanged(webrtc::IceTransportInternal * /*unused*/); std::optional> GetParameters(bool local); @@ -183,3 +189,5 @@ namespace python_webrtc { }; } // namespace python_webrtc + +#endif // PYTHON_WEBRTC_INTERFACES_RTC_ICE_TRANSPORT_H_ diff --git a/python-webrtc/cpp/src/interfaces/rtc_peer_connection.cpp b/python-webrtc/cpp/src/interfaces/rtc_peer_connection.cpp index 1885245..43c84cd 100644 --- a/python-webrtc/cpp/src/interfaces/rtc_peer_connection.cpp +++ b/python-webrtc/cpp/src/interfaces/rtc_peer_connection.cpp @@ -11,20 +11,22 @@ #include #include #include +#include #include +#include #include #include #include #include -#include "peer_connection_factory.h" -#include "create_session_description_observer.h" -#include "set_session_description_observer.h" -#include "stats_collector_callback.h" #include "../models/python_webrtc/rtc_ice_candidate.h" #include "../utils/gil.h" #include "../utils/libwebrtc_thread.h" +#include "create_session_description_observer.h" +#include "peer_connection_factory.h" +#include "set_session_description_observer.h" +#include "stats_collector_callback.h" namespace python_webrtc { @@ -44,13 +46,13 @@ namespace python_webrtc { connection->_heldGathering.Hold(); _connection = connection; } - for (auto &iceTransport: iceTransports) { + for (auto &iceTransport : iceTransports) { if (!iceTransport->IsHeld()) { iceTransport->Hold(); _iceTransports.push_back(std::move(iceTransport)); } } - for (const auto &transceiver: pc->GetTransceivers()) { + for (const auto &transceiver : pc->GetTransceivers()) { auto track = MediaStreamTrack::holder().Find(transceiver->receiver()->track().get()); if (track) { track->HoldEnded(); @@ -62,13 +64,16 @@ namespace python_webrtc { // also when the operation fails before it starts ~HeldOperationEvents() { Release(); } + HeldOperationEvents(const HeldOperationEvents &) = delete; + HeldOperationEvents &operator=(const HeldOperationEvents &) = delete; + void Release() { - std::lock_guard lock(_mutex); - for (const auto &track: _tracks) { + const std::scoped_lock lock(_mutex); + for (const auto &track : _tracks) { track->ReleaseEnded(); } _tracks.clear(); - for (const auto &iceTransport: _iceTransports) { + for (const auto &iceTransport : _iceTransports) { iceTransport->Release(); } _iceTransports.clear(); @@ -94,8 +99,7 @@ namespace python_webrtc { webrtc::PeerConnectionDependencies dependencies(this); - auto result = _factory->factory()->CreatePeerConnectionOrError( - configuration, std::move(dependencies)); + auto result = _factory->factory()->CreatePeerConnectionOrError(configuration, std::move(dependencies)); if (!result.ok()) { throw RTCException(result.error()); @@ -106,7 +110,7 @@ namespace python_webrtc { RTCPeerConnection::~RTCPeerConnection() { // destroying the peer connection blocks on the signaling thread, which may be waiting for the GIL - BlockingDestructor release("RTCPeerConnection"); + const BlockingDestructor release("RTCPeerConnection"); // closing stops the connection from calling this observer Close(); @@ -114,7 +118,7 @@ namespace python_webrtc { // released here, without the GIL, as it blocks on the signaling thread webrtc::scoped_refptr closed; { - std::lock_guard lock(_connectionMutex); + const std::scoped_lock lock(_connectionMutex); closed = std::move(_closedConnection); } closed = nullptr; @@ -126,39 +130,44 @@ namespace python_webrtc { .def(pybind11::init(nogil_factory(+[](const std::optional &configuration) { return std::shared_ptr(new RTCPeerConnection(configuration), DeleteOffLibwebrtcThread()); }))) - .def("createOffer", WithCallbacks(&RTCPeerConnection::CreateOffer), - pybind11::arg("onSuccess"), pybind11::arg("onFailure"), pybind11::arg("iceRestart"), - pybind11::arg("voiceActivityDetection")) - .def("createAnswer", WithCallbacks(&RTCPeerConnection::CreateAnswer), - pybind11::arg("onSuccess"), pybind11::arg("onFailure"), pybind11::arg("voiceActivityDetection")) - .def("setLocalDescription", WithCallbacks(&RTCPeerConnection::SetLocalDescription), - pybind11::arg("onSuccess"), pybind11::arg("onFailure"), pybind11::arg("description")) + .def("createOffer", WithCallbacks(&RTCPeerConnection::CreateOffer), pybind11::arg("onSuccess"), + pybind11::arg("onFailure"), pybind11::arg("iceRestart"), pybind11::arg("voiceActivityDetection")) + .def("createAnswer", WithCallbacks(&RTCPeerConnection::CreateAnswer), pybind11::arg("onSuccess"), + pybind11::arg("onFailure"), pybind11::arg("voiceActivityDetection")) + .def("setLocalDescription", WithCallbacks(&RTCPeerConnection::SetLocalDescription), pybind11::arg("onSuccess"), + pybind11::arg("onFailure"), pybind11::arg("description")) .def("setRemoteDescription", WithCallbacks(&RTCPeerConnection::SetRemoteDescription), pybind11::arg("onSuccess"), pybind11::arg("onFailure"), pybind11::arg("description")) - .def("addIceCandidate", WithCallbacks(&RTCPeerConnection::AddIceCandidate), - pybind11::arg("onSuccess"), pybind11::arg("onFailure"), pybind11::arg("candidate"), - pybind11::arg("sdpMid"), pybind11::arg("sdpMLineIndex"), pybind11::arg("usernameFragment")) + .def("addIceCandidate", WithCallbacks(&RTCPeerConnection::AddIceCandidate), pybind11::arg("onSuccess"), + pybind11::arg("onFailure"), pybind11::arg("candidate"), pybind11::arg("sdpMid"), + pybind11::arg("sdpMLineIndex"), pybind11::arg("usernameFragment")) .def("addTrack", pybind11::overload_cast>>( - &RTCPeerConnection::AddTrack), nogil(), pybind11::arg("track"), pybind11::arg("stream")) + &RTCPeerConnection::AddTrack), + nogil(), pybind11::arg("track"), pybind11::arg("stream")) .def("addTrack", pybind11::overload_cast &>( - &RTCPeerConnection::AddTrack), nogil(), pybind11::arg("track"), pybind11::arg("streams")) + &RTCPeerConnection::AddTrack), + nogil(), pybind11::arg("track"), pybind11::arg("streams")) .def("removeTrack", &RTCPeerConnection::RemoveTrack, nogil(), pybind11::arg("sender")) .def("addTransceiver", - pybind11::overload_cast> &>( - &RTCPeerConnection::AddTransceiver), nogil(), pybind11::arg("kind"), pybind11::arg("init")) + pybind11::overload_cast> &>( + &RTCPeerConnection::AddTransceiver), + nogil(), pybind11::arg("kind"), pybind11::arg("init")) .def("addTransceiver", - pybind11::overload_cast> &>( - &RTCPeerConnection::AddTransceiver), nogil(), pybind11::arg("track"), pybind11::arg("init")) + pybind11::overload_cast> &>( + &RTCPeerConnection::AddTransceiver), + nogil(), pybind11::arg("track"), pybind11::arg("init")) .def("getTransceivers", &RTCPeerConnection::GetTransceivers, nogil()) .def("getSenders", &RTCPeerConnection::GetSenders, nogil()) .def("getReceivers", &RTCPeerConnection::GetReceivers, nogil()) - .def("createDataChannel", &RTCPeerConnection::CreateDataChannel, nogil(), - pybind11::arg("label"), pybind11::arg("ordered"), pybind11::arg("maxPacketLifeTime"), - pybind11::arg("maxRetransmits"), pybind11::arg("protocol"), pybind11::arg("negotiated"), - pybind11::arg("id"), pybind11::arg("priority")) - .def("getStats", WithCallbacks(&RTCPeerConnection::GetStats), pybind11::arg("onSuccess"), pybind11::arg("onFailure")) + .def("createDataChannel", &RTCPeerConnection::CreateDataChannel, nogil(), pybind11::arg("label"), + pybind11::arg("ordered"), pybind11::arg("maxPacketLifeTime"), pybind11::arg("maxRetransmits"), + pybind11::arg("protocol"), pybind11::arg("negotiated"), pybind11::arg("id"), pybind11::arg("priority")) + .def("getStats", WithCallbacks(&RTCPeerConnection::GetStats), pybind11::arg("onSuccess"), + pybind11::arg("onFailure")) .def("restartIce", &RTCPeerConnection::RestartIce, nogil()) .def("getConfiguration", &RTCPeerConnection::GetConfiguration, nogil()) .def("setConfiguration", &RTCPeerConnection::SetConfiguration, nogil(), pybind11::arg("configuration")) @@ -177,14 +186,11 @@ namespace python_webrtc { .def_property_readonly("canTrickleIceCandidates", nogil_fn(&RTCPeerConnection::GetCanTrickleIceCandidates)) .def("_shouldFireNegotiationNeededEvent", &RTCPeerConnection::ShouldFireNegotiationNeededEvent, nogil(), pybind11::arg("eventId")) - .def("_surfaceSignalingState", &RTCPeerConnection::SurfaceSignalingState, nogil(), - pybind11::arg("state")) + .def("_surfaceSignalingState", &RTCPeerConnection::SurfaceSignalingState, nogil(), pybind11::arg("state")) .def("_surfaceIceConnectionState", &RTCPeerConnection::SurfaceIceConnectionState, nogil(), pybind11::arg("state")) - .def("_surfaceIceGatheringState", &RTCPeerConnection::SurfaceIceGatheringState, nogil(), - pybind11::arg("state")) - .def("_surfaceConnectionState", &RTCPeerConnection::SurfaceConnectionState, nogil(), - pybind11::arg("state")) + .def("_surfaceIceGatheringState", &RTCPeerConnection::SurfaceIceGatheringState, nogil(), pybind11::arg("state")) + .def("_surfaceConnectionState", &RTCPeerConnection::SurfaceConnectionState, nogil(), pybind11::arg("state")) .def("_refreshDescriptions", &RTCPeerConnection::RefreshDescriptions, nogil()) .def_static("_connectionOf", &RTCPeerConnection::ConnectionOf, nogil(), pybind11::arg("sender")) .def("_applyDescriptions", &RTCPeerConnection::ApplyDescriptions, nogil(), @@ -205,12 +211,12 @@ namespace python_webrtc { } webrtc::scoped_refptr RTCPeerConnection::connection() { - std::lock_guard lock(_connectionMutex); + const std::scoped_lock lock(_connectionMutex); return _jinglePeerConnection; } webrtc::scoped_refptr RTCPeerConnection::closedConnection() { - std::lock_guard lock(_connectionMutex); + const std::scoped_lock lock(_connectionMutex); return _closedConnection; } @@ -218,16 +224,16 @@ namespace python_webrtc { return !connection(); } - template + template std::shared_ptr RTCPeerConnection::Wrap(Wrappers &wrappers, webrtc::scoped_refptr object) { - if (!onLibwebrtcThread) { + if (!OnLibwebrtcThread()) { // on the signaling thread, which wraps objects too: the lock isn't held while waiting for it - gil_release_if_held release; - return _factory->_signalingThread->BlockingCall([&]() { return Wrap(wrappers, std::move(object)); }); + const gil_release_if_held release; + return _factory->signalingThread()->BlockingCall([&]() { return Wrap(wrappers, std::move(object)); }); } std::shared_ptr wrapper; { - std::lock_guard lock(_wrappersMutex); + const std::scoped_lock lock(_wrappersMutex); auto it = wrappers.find(object.get()); if (it != wrappers.end()) { return it->second; @@ -240,19 +246,19 @@ namespace python_webrtc { return wrapper; } - template - std::vector> RTCPeerConnection::Sync( - Wrappers &wrappers, const std::vector> &objects) { - if (!onLibwebrtcThread) { + template + std::vector> RTCPeerConnection::Sync(Wrappers &wrappers, + const std::vector> &objects) { + if (!OnLibwebrtcThread()) { // see Wrap - gil_release_if_held release; - return _factory->_signalingThread->BlockingCall([&]() { return Sync(wrappers, objects); }); + const gil_release_if_held release; + return _factory->signalingThread()->BlockingCall([&]() { return Sync(wrappers, objects); }); } std::vector> result; Wrappers current; { - std::lock_guard lock(_wrappersMutex); - for (const auto &object: objects) { + const std::scoped_lock lock(_wrappersMutex); + for (const auto &object : objects) { auto it = wrappers.find(object.get()); auto wrapper = it != wrappers.end() ? it->second : T::holder().GetOrCreate(_factory, object); current[object.get()] = wrapper; @@ -260,17 +266,17 @@ namespace python_webrtc { } std::swap(wrappers, current); } - for (const auto &wrapper: result) { + for (const auto &wrapper : result) { Adopt(wrapper); } // wrappers of the objects that are gone are released here, out of the lock return result; } - template + template std::vector> RTCPeerConnection::Unkept(const std::vector> &objects) { std::vector> result; - for (const auto &object: objects) { + for (const auto &object : objects) { auto wrapper = T::holder().GetOrCreate(_factory, object); Adopt(wrapper); result.push_back(std::move(wrapper)); @@ -286,7 +292,7 @@ namespace python_webrtc { decltype(_channels) channels; decltype(_dtlsTransports) dtlsTransports; { - std::lock_guard lock(_wrappersMutex); + const std::scoped_lock lock(_wrappersMutex); std::swap(channels, _channels); std::swap(dtlsTransports, _dtlsTransports); std::swap(transceivers, _transceivers); @@ -334,117 +340,121 @@ namespace python_webrtc { }); } - static webrtc::scoped_refptr transceiverOf( - const webrtc::scoped_refptr &pc, - const webrtc::scoped_refptr &sender) { - for (const auto &transceiver: pc->GetTransceivers()) { - if (transceiver->sender() == sender) { - return transceiver; + namespace { + + webrtc::scoped_refptr + transceiverOf(const webrtc::scoped_refptr &pc, + const webrtc::scoped_refptr &sender) { + for (const auto &transceiver : pc->GetTransceivers()) { + if (transceiver->sender() == sender) { + return transceiver; + } } + return nullptr; } - return nullptr; - } - static webrtc::scoped_refptr transceiverOf( - const webrtc::scoped_refptr &pc, - const webrtc::scoped_refptr &receiver) { - for (const auto &transceiver: pc->GetTransceivers()) { - if (transceiver->receiver() == receiver) { - return transceiver; + webrtc::scoped_refptr + transceiverOf(const webrtc::scoped_refptr &pc, + const webrtc::scoped_refptr &receiver) { + for (const auto &transceiver : pc->GetTransceivers()) { + if (transceiver->receiver() == receiver) { + return transceiver; + } } + return nullptr; } - return nullptr; - } - // The media section a transceiver negotiated, in the current remote (for the sender) or local (for the receiver) - // description; null until it's negotiated, and when it's rejected. On the signaling thread. - static const webrtc::MediaContentDescription *negotiatedContent( - const webrtc::scoped_refptr &pc, - const webrtc::scoped_refptr &transceiver, bool remote) { - auto mid = transceiver ? transceiver->mid() : std::nullopt; - auto description = remote ? pc->current_remote_description() : pc->current_local_description(); - auto content = mid && description ? description->description()->GetContentByName(*mid) : nullptr; - return content && !content->rejected ? content->media_description() : nullptr; - } + // The media section a transceiver negotiated, in the current remote (for the sender) or local (for the receiver) + // description; null until it's negotiated, and when it's rejected. On the signaling thread. + const webrtc::MediaContentDescription * + negotiatedContent(const webrtc::scoped_refptr &pc, + const webrtc::scoped_refptr &transceiver, bool remote) { + auto mid = transceiver ? transceiver->mid() : std::nullopt; + const auto *description = remote ? pc->current_remote_description() : pc->current_local_description(); + const auto *content = + mid && (description != nullptr) ? description->description()->GetContentByName(*mid) : nullptr; + return (content != nullptr) && !content->rejected ? content->media_description() : nullptr; + } - static bool isSupported(const webrtc::Codec &codec, const webrtc::RtpCapabilities &capabilities, - webrtc::MediaType kind) { - return std::any_of(capabilities.codecs.begin(), capabilities.codecs.end(), [&](const auto &capability) { - return absl::EqualsIgnoreCase(capability.name, codec.name) && - capability.clock_rate == codec.clockrate && - (kind != webrtc::MediaType::AUDIO || static_cast(capability.num_channels.value_or(1)) == codec.channels); - }); - } + bool isSupported(const webrtc::Codec &codec, const webrtc::RtpCapabilities &capabilities, webrtc::MediaType kind) { + return std::ranges::any_of(capabilities.codecs, [&](const auto &capability) { + return absl::EqualsIgnoreCase(capability.name, codec.name) && capability.clock_rate == codec.clockrate && + (kind != webrtc::MediaType::AUDIO || + static_cast(capability.num_channels.value_or(1)) == codec.channels); + }); + } - // the codecs of a media section this side can use (the remote peer may list unknown ones) - static std::vector supportedCodecs( - const webrtc::MediaContentDescription &content, const webrtc::RtpCapabilities &capabilities) { - std::set kept; - for (const auto &codec: content.codecs()) { - if (codec.GetResiliencyType() != webrtc::Codec::ResiliencyType::kRtx && - isSupported(codec, capabilities, content.type())) { - kept.insert(codec.id); + // the codecs of a media section this side can use (the remote peer may list unknown ones) + std::vector supportedCodecs(const webrtc::MediaContentDescription &content, + const webrtc::RtpCapabilities &capabilities) { + std::set kept; + for (const auto &codec : content.codecs()) { + if (codec.GetResiliencyType() != webrtc::Codec::ResiliencyType::kRtx && + isSupported(codec, capabilities, content.type())) { + kept.insert(codec.id); + } } - } - std::vector codecs; - for (const auto &codec: content.codecs()) { - // a retransmission codec goes with the codec it retransmits - int associated; - bool rtx = codec.GetResiliencyType() == webrtc::Codec::ResiliencyType::kRtx; - if (rtx ? codec.GetParam(webrtc::kCodecParamAssociatedPayloadType, &associated) && kept.count(associated) - : kept.count(codec.id)) { - codecs.push_back(codec.ToCodecParameters()); + std::vector codecs; + for (const auto &codec : content.codecs()) { + // a retransmission codec goes with the codec it retransmits + int associated = 0; + const bool rtx = codec.GetResiliencyType() == webrtc::Codec::ResiliencyType::kRtx; + const auto *associatedParam = static_cast(webrtc::kCodecParamAssociatedPayloadType); + if (rtx ? codec.GetParam(associatedParam, &associated) && kept.contains(associated) : kept.contains(codec.id)) { + codecs.push_back(codec.ToCodecParameters()); + } } + return codecs; } - return codecs; - } - webrtc::scoped_refptr RTCPeerConnection::TransceiverOf( - const webrtc::scoped_refptr &sender) { + } // namespace + + webrtc::scoped_refptr + RTCPeerConnection::TransceiverOf(const webrtc::scoped_refptr &sender) { auto pc = connection(); return pc ? transceiverOf(pc, sender) : nullptr; } - std::vector RTCPeerConnection::NegotiatedCodecs( - const webrtc::scoped_refptr &sender) { + std::vector + RTCPeerConnection::NegotiatedCodecs(const webrtc::scoped_refptr &sender) { auto pc = connection(); if (!pc) { return {}; } - return _factory->_signalingThread->BlockingCall([&]() { - auto content = negotiatedContent(pc, transceiverOf(pc, sender), true); + return _factory->signalingThread()->BlockingCall([&]() { + const auto *content = negotiatedContent(pc, transceiverOf(pc, sender), true); return content ? supportedCodecs(*content, _factory->factory()->GetRtpSenderCapabilities(content->type())) : std::vector(); }); } - std::vector RTCPeerConnection::NegotiatedCodecs( - const webrtc::scoped_refptr &receiver) { + std::vector + RTCPeerConnection::NegotiatedCodecs(const webrtc::scoped_refptr &receiver) { auto pc = connection(); if (!pc) { return {}; } - return _factory->_signalingThread->BlockingCall([&]() { - auto content = negotiatedContent(pc, transceiverOf(pc, receiver), false); + return _factory->signalingThread()->BlockingCall([&]() { + const auto *content = negotiatedContent(pc, transceiverOf(pc, receiver), false); return content ? supportedCodecs(*content, _factory->factory()->GetRtpReceiverCapabilities(content->type())) : std::vector(); }); } - std::vector RTCPeerConnection::NegotiatedHeaderExtensions( - const webrtc::scoped_refptr &receiver) { + std::vector + RTCPeerConnection::NegotiatedHeaderExtensions(const webrtc::scoped_refptr &receiver) { auto pc = connection(); if (!pc) { return {}; } - return _factory->_signalingThread->BlockingCall([&]() { - auto content = negotiatedContent(pc, transceiverOf(pc, receiver), false); + return _factory->signalingThread()->BlockingCall([&]() { + const auto *content = negotiatedContent(pc, transceiverOf(pc, receiver), false); return content ? content->rtp_header_extensions() : std::vector(); }); } - webrtc::scoped_refptr RTCPeerConnection::StatsConnection( - const std::function &onFailure) { + webrtc::scoped_refptr + RTCPeerConnection::StatsConnection(const std::function &onFailure) { auto pc = connection(); if (!pc) { pc = closedConnection(); @@ -488,10 +498,9 @@ namespace python_webrtc { pc->GetStats(receiver, webrtc::make_ref_counted(onSuccess, std::move(excluded))); } - void RTCPeerConnection::CreateOffer( - std::function &onSuccess, - std::function &onFailure, - bool iceRestart, bool voiceActivityDetection) { + void RTCPeerConnection::CreateOffer(std::function &onSuccess, + std::function &onFailure, bool iceRestart, + bool voiceActivityDetection) { auto pc = connection(); auto state = pc ? pc->signaling_state() : SignalingState::kClosed; if (state == SignalingState::kClosed) { @@ -500,107 +509,112 @@ namespace python_webrtc { } if (state != SignalingState::kStable && state != SignalingState::kHaveLocalOffer) { onFailure(RTCCallbackException(webrtc::RTCErrorType::INVALID_STATE, - "Failed to execute 'createOffer' on 'RTCPeerConnection': Called in wrong state: " + - std::string(webrtc::PeerConnectionInterface::AsString(state)))); + "Failed to execute 'createOffer' on 'RTCPeerConnection': Called in wrong state: " + + std::string(webrtc::PeerConnectionInterface::AsString(state)))); return; } - auto observer = new webrtc::RefCountedObject(weak_from_this(), onSuccess, onFailure); + auto observer = webrtc::make_ref_counted(weak_from_this(), onSuccess, onFailure); auto options = webrtc::PeerConnectionInterface::RTCOfferAnswerOptions(); options.ice_restart = iceRestart; options.voice_activity_detection = voiceActivityDetection; - pc->CreateOffer(observer, options); + pc->CreateOffer(observer.get(), options); } - void RTCPeerConnection::CreateAnswer( - std::function &onSuccess, - std::function &onFailure, - bool voiceActivityDetection) { + void RTCPeerConnection::CreateAnswer(std::function &onSuccess, + std::function &onFailure, + bool voiceActivityDetection) { auto pc = connection(); if (!pc || pc->signaling_state() == SignalingState::kClosed) { onFailure(RTCCallbackException(closedError("createAnswer"))); return; } - auto observer = new webrtc::RefCountedObject(weak_from_this(), onSuccess, onFailure); + auto observer = webrtc::make_ref_counted(weak_from_this(), onSuccess, onFailure); auto options = webrtc::PeerConnectionInterface::RTCOfferAnswerOptions(); options.voice_activity_detection = voiceActivityDetection; - pc->CreateAnswer(observer, options); + pc->CreateAnswer(observer.get(), options); } void RTCPeerConnection::SaveCreatedDescription(const RTCSessionDescriptionInit &description) { - std::lock_guard lock(_createdMutex); + const std::scoped_lock lock(_createdMutex); (description.type == webrtc::SdpType::kOffer ? _lastOffer : _lastAnswer) = description.sdp; } - // A remote TCP candidate on a port Fetch blocks is ignored, as the WebRTC specification requires - // (https://fetch.spec.whatwg.org/#bad-port) - static bool isBlockedCandidate(const webrtc::Candidate &candidate) { - static const std::set badPorts = { - 0, 1, 7, 9, 11, 13, 15, 17, 19, 20, 21, 22, 23, 25, 37, 42, 43, 53, 69, 77, 79, 87, 95, 101, 102, 103, 104, - 109, 110, 111, 113, 115, 117, 119, 123, 135, 137, 139, 143, 161, 179, 389, 427, 465, 512, 513, 514, 515, 526, - 530, 531, 532, 540, 548, 554, 556, 563, 587, 601, 636, 989, 990, 993, 995, 1719, 1720, 1723, 2049, 3659, 4045, - 4190, 5060, 5061, 6000, 6566, 6665, 6666, 6667, 6668, 6669, 6679, 6697, 10080}; - return candidate.protocol() == "tcp" && badPorts.count(candidate.address().port()) > 0; - } - - static void removeBlockedCandidates(webrtc::SessionDescriptionInterface &description) { - std::vector> blocked; - for (size_t i = 0; i < description.number_of_mediasections(); ++i) { - auto candidates = description.candidates(i); - for (size_t j = 0; candidates && j < candidates->count(); ++j) { - auto candidate = candidates->at(j); - if (isBlockedCandidate(candidate->candidate())) { - blocked.push_back(webrtc::CreateIceCandidate( - candidate->sdp_mid(), candidate->sdp_mline_index(), candidate->candidate())); + namespace { + + // A remote TCP candidate on a port Fetch blocks is ignored, as the WebRTC specification requires + // (https://fetch.spec.whatwg.org/#bad-port) + bool isBlockedCandidate(const webrtc::Candidate &candidate) { + static const std::set badPorts = { + 0, 1, 7, 9, 11, 13, 15, 17, 19, 20, 21, 22, 23, 25, 37, 42, 43, + 53, 69, 77, 79, 87, 95, 101, 102, 103, 104, 109, 110, 111, 113, 115, 117, 119, + 123, 135, 137, 139, 143, 161, 179, 389, 427, 465, 512, 513, 514, 515, 526, 530, 531, + 532, 540, 548, 554, 556, 563, 587, 601, 636, 989, 990, 993, 995, 1719, 1720, 1723, 2049, + 3659, 4045, 4190, 5060, 5061, 6000, 6566, 6665, 6666, 6667, 6668, 6669, 6679, 6697, 10080}; + return candidate.protocol() == "tcp" && badPorts.contains(candidate.address().port()); + } + + void removeBlockedCandidates(webrtc::SessionDescriptionInterface &description) { + std::vector> blocked; + for (size_t i = 0; i < description.number_of_mediasections(); ++i) { + const auto *candidates = description.candidates(i); + for (size_t j = 0; (candidates != nullptr) && j < candidates->count(); ++j) { + const auto *candidate = candidates->at(j); + if (isBlockedCandidate(candidate->candidate())) { + blocked.push_back( + webrtc::CreateIceCandidate(candidate->sdp_mid(), candidate->sdp_mline_index(), candidate->candidate())); + } } } + for (const auto &candidate : blocked) { + description.RemoveCandidate(candidate.get()); + } } - for (const auto &candidate: blocked) { - description.RemoveCandidate(candidate.get()); - } - } - // the line of an SDP parse error, counting from 1 - static std::optional lineNumber(const std::string &sdp, const std::string &line) { - auto pos = line.empty() ? std::string::npos : sdp.find(line); - if (pos == std::string::npos) { - return std::nullopt; + // the line of an SDP parse error, counting from 1 + std::optional lineNumber(const std::string &sdp, const std::string &line) { + auto pos = line.empty() ? std::string::npos : sdp.find(line); + if (pos == std::string::npos) { + return std::nullopt; + } + return 1 + static_cast(std::count(sdp.begin(), sdp.begin() + static_cast(pos), '\n')); } - return 1 + static_cast(std::count(sdp.begin(), sdp.begin() + static_cast(pos), '\n')); - } - static std::unique_ptr parseDescription( - const RTCSessionDescriptionInit &init, std::optional &error) { - webrtc::SdpParseError parseError; - auto description = webrtc::CreateSessionDescription(init.type, init.sdp, &parseError); - if (!description) { - webrtc::RTCError rtcError(webrtc::RTCErrorType::OPERATION_ERROR_WITH_DATA, - "Failed to parse the session description: " + parseError.description + - (parseError.line.empty() ? "" : " (" + parseError.line + ")")); - rtcError.set_error_detail(webrtc::RTCErrorDetailType::SDP_SYNTAX_ERROR); - error.emplace(std::move(rtcError), lineNumber(init.sdp, parseError.line)); + std::unique_ptr parseDescription(const RTCSessionDescriptionInit &init, + std::optional &error) { + webrtc::SdpParseError parseError; + auto description = webrtc::CreateSessionDescription(init.type, init.sdp, &parseError); + if (!description) { + webrtc::RTCError rtcError(webrtc::RTCErrorType::OPERATION_ERROR_WITH_DATA, + "Failed to parse the session description: " + parseError.description + + (parseError.line.empty() ? "" : " (" + parseError.line + ")")); + rtcError.set_error_detail(webrtc::RTCErrorDetailType::SDP_SYNTAX_ERROR); + error.emplace(std::move(rtcError), lineNumber(init.sdp, parseError.line)); + } + return description; } - return description; - } - static bool canSetLocal(webrtc::SdpType type, RTCPeerConnection::SignalingState state) { - using SignalingState = RTCPeerConnection::SignalingState; - if (type == webrtc::SdpType::kOffer) { - return state == SignalingState::kStable || state == SignalingState::kHaveLocalOffer; - } - if (type == webrtc::SdpType::kRollback) { - return state == SignalingState::kHaveLocalOffer || state == SignalingState::kHaveLocalPrAnswer; + bool canSetLocal(webrtc::SdpType type, RTCPeerConnection::SignalingState state) { + using SignalingState = RTCPeerConnection::SignalingState; + if (type == webrtc::SdpType::kOffer) { + return state == SignalingState::kStable || state == SignalingState::kHaveLocalOffer; + } + if (type == webrtc::SdpType::kRollback) { + return state == SignalingState::kHaveLocalOffer || state == SignalingState::kHaveLocalPrAnswer; + } + return state == SignalingState::kHaveRemoteOffer || state == SignalingState::kHaveLocalPrAnswer; } - return state == SignalingState::kHaveRemoteOffer || state == SignalingState::kHaveLocalPrAnswer; - } - std::function RTCPeerConnection::Completion( - std::function &onSuccess, std::function &onFailure, - const webrtc::scoped_refptr &pc, DescriptionKind kind) { - bool remote = kind == DescriptionKind::kRemote; + } // namespace + + std::function + RTCPeerConnection::Completion(std::function &onSuccess, std::function &onFailure, + const webrtc::scoped_refptr &pc, + DescriptionKind kind) { + const bool remote = kind == DescriptionKind::kRemote; // a local description starts gathering, the candidates come after it auto held = std::make_shared(pc, IceTransports(), remote ? nullptr : shared_from_this()); if (remote) { @@ -622,7 +636,7 @@ namespace python_webrtc { } onSuccess(); // their gathering events come after the operation - for (const auto &iceTransport: created) { + for (const auto &iceTransport : created) { iceTransport->CreatedByDescription(); } } else { @@ -633,10 +647,9 @@ namespace python_webrtc { }; } - void RTCPeerConnection::SetLocalDescription( - std::function &onSuccess, - std::function &onFailure, - const std::optional &init) { + void RTCPeerConnection::SetLocalDescription(std::function &onSuccess, + std::function &onFailure, + const std::optional &init) { auto pc = connection(); auto state = pc ? pc->signaling_state() : SignalingState::kClosed; if (state == SignalingState::kClosed) { @@ -644,7 +657,8 @@ namespace python_webrtc { return; } if (init && !canSetLocal(init->type, state)) { - onFailure(RTCCallbackException(webrtc::RTCErrorType::INVALID_STATE, + onFailure(RTCCallbackException( + webrtc::RTCErrorType::INVALID_STATE, "Failed to execute 'setLocalDescription' on 'RTCPeerConnection': The description type doesn't match " "the signaling state.")); return; @@ -652,10 +666,13 @@ namespace python_webrtc { if (!init || (init->sdp.empty() && init->type != webrtc::SdpType::kRollback)) { // without a type, an answer in the states that wait for one, an offer otherwise - bool waitsForAnswer = state == SignalingState::kHaveRemoteOffer || state == SignalingState::kHaveLocalPrAnswer; - auto type = init ? init->type : (waitsForAnswer ? webrtc::SdpType::kAnswer : webrtc::SdpType::kOffer); + const bool waitsForAnswer = + state == SignalingState::kHaveRemoteOffer || state == SignalingState::kHaveLocalPrAnswer; + const auto implicitType = waitsForAnswer ? webrtc::SdpType::kAnswer : webrtc::SdpType::kOffer; + auto type = init ? init->type : implicitType; auto complete = Completion(onSuccess, onFailure, pc, DescriptionKind::kLocal); - if (type == webrtc::SdpType::kOffer || (type == webrtc::SdpType::kAnswer && state == SignalingState::kHaveRemoteOffer)) { + if (type == webrtc::SdpType::kOffer || + (type == webrtc::SdpType::kAnswer && state == SignalingState::kHaveRemoteOffer)) { // libwebrtc creates the offer or the answer the signaling state calls for pc->SetLocalDescription(webrtc::make_ref_counted(std::move(complete))); } else { @@ -665,10 +682,11 @@ namespace python_webrtc { } if (init->type != webrtc::SdpType::kRollback) { - std::lock_guard lock(_createdMutex); + const std::scoped_lock lock(_createdMutex); auto &last = init->type == webrtc::SdpType::kOffer ? _lastOffer : _lastAnswer; if (init->sdp != last) { - onFailure(RTCCallbackException(webrtc::RTCErrorType::INVALID_MODIFICATION, + onFailure(RTCCallbackException( + webrtc::RTCErrorType::INVALID_MODIFICATION, "Failed to execute 'setLocalDescription' on 'RTCPeerConnection': The SDP does not match the previously " "generated SDP for this type")); return; @@ -682,12 +700,12 @@ namespace python_webrtc { return; } pc->SetLocalDescription(std::move(description), webrtc::make_ref_counted( - Completion(onSuccess, onFailure, pc, DescriptionKind::kLocal))); + Completion(onSuccess, onFailure, pc, DescriptionKind::kLocal))); } - void RTCPeerConnection::SetImplicitAnswer( - const webrtc::scoped_refptr &pc, webrtc::SdpType type, - std::function complete, const std::function &onFailure) { + void RTCPeerConnection::SetImplicitAnswer(const webrtc::scoped_refptr &pc, + webrtc::SdpType type, std::function complete, + const std::function &onFailure) { auto observer = webrtc::make_ref_counted(std::move(complete)); auto apply = [pc, observer, type, onFailure](const std::string &sdp) { std::optional error; @@ -700,25 +718,24 @@ namespace python_webrtc { }; std::string lastAnswer; { - std::lock_guard lock(_createdMutex); + const std::scoped_lock lock(_createdMutex); lastAnswer = _lastAnswer; } if (!lastAnswer.empty()) { apply(lastAnswer); return; } - std::function created = [apply](RTCSessionDescription answer) { + std::function created = [apply](const RTCSessionDescription &answer) { apply(answer.init().sdp); }; std::function failed = onFailure; - pc->CreateAnswer(new webrtc::RefCountedObject(weak_from_this(), created, failed), - webrtc::PeerConnectionInterface::RTCOfferAnswerOptions()); + auto answerObserver = webrtc::make_ref_counted(weak_from_this(), created, failed); + pc->CreateAnswer(answerObserver.get(), webrtc::PeerConnectionInterface::RTCOfferAnswerOptions()); } - void RTCPeerConnection::SetRemoteDescription( - std::function &onSuccess, - std::function &onFailure, - const RTCSessionDescriptionInit &init) { + void RTCPeerConnection::SetRemoteDescription(std::function &onSuccess, + std::function &onFailure, + const RTCSessionDescriptionInit &init) { auto pc = connection(); auto state = pc ? pc->signaling_state() : SignalingState::kClosed; if (state == SignalingState::kClosed) { @@ -727,18 +744,21 @@ namespace python_webrtc { } // the state is checked before the SDP is parsed - bool answer = init.type == webrtc::SdpType::kAnswer || init.type == webrtc::SdpType::kPrAnswer; - bool rollback = init.type == webrtc::SdpType::kRollback; + const bool answer = init.type == webrtc::SdpType::kAnswer || init.type == webrtc::SdpType::kPrAnswer; + const bool rollback = init.type == webrtc::SdpType::kRollback; if ((answer && state != SignalingState::kHaveLocalOffer && state != SignalingState::kHaveRemotePrAnswer) || (rollback && state != SignalingState::kHaveRemoteOffer && state != SignalingState::kHaveRemotePrAnswer)) { - onFailure(RTCCallbackException(webrtc::RTCErrorType::INVALID_STATE, + onFailure(RTCCallbackException( + webrtc::RTCErrorType::INVALID_STATE, "Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': Called in wrong state: " + - std::string(webrtc::PeerConnectionInterface::AsString(state)))); + std::string(webrtc::PeerConnectionInterface::AsString(state)))); return; } - auto complete = Completion(onSuccess, onFailure, pc, DescriptionKind::kRemote); - auto apply = [init, complete, onFailure](webrtc::scoped_refptr pc) { + // shared by the rollback and the application of the description + auto complete = std::make_shared>( + Completion(onSuccess, onFailure, pc, DescriptionKind::kRemote)); + auto apply = [init, complete, onFailure](const webrtc::scoped_refptr &pc) { std::optional error; auto description = parseDescription(init, error); if (error) { @@ -746,7 +766,9 @@ namespace python_webrtc { return; } removeBlockedCandidates(*description); - pc->SetRemoteDescription(std::move(description), webrtc::make_ref_counted(complete)); + pc->SetRemoteDescription(std::move(description), + webrtc::make_ref_counted( + [complete](webrtc::RTCError error) { (*complete)(std::move(error)); })); }; if (init.type == webrtc::SdpType::kOffer && state == SignalingState::kHaveLocalOffer) { @@ -757,9 +779,9 @@ namespace python_webrtc { auto self = weak.lock(); auto pc = self ? self->connection() : nullptr; if (!rollbackError.ok()) { - complete(std::move(rollbackError)); + (*complete)(std::move(rollbackError)); } else if (!pc) { - complete(closedError("setRemoteDescription")); + (*complete)(closedError("setRemoteDescription")); } else { apply(pc); } @@ -772,99 +794,119 @@ namespace python_webrtc { apply(pc); } - // End-of-candidates candidates of a local description, one per transport (the first media section of each) - static std::vector endOfCandidates(const webrtc::SessionDescriptionInterface *description) { - std::vector result; - if (!description) { + namespace { + + // End-of-candidates candidates of a local description, one per transport (the first media section of each) + std::vector endOfCandidates(const webrtc::SessionDescriptionInterface *description) { + std::vector result; + if (description == nullptr) { + return result; + } + const auto *session = description->description(); + const auto *bundle = session->GetGroupByName(static_cast(webrtc::GROUP_TYPE_BUNDLE)); + bool bundleDone = false; + int index = 0; + for (const auto &content : session->contents()) { + auto mid = content.mid(); + const bool bundled = (bundle != nullptr) && bundle->HasContentName(mid); + if (!content.rejected && !(bundled && bundleDone)) { + const auto *transport = session->GetTransportInfoByName(mid); + std::optional ufrag; + if ((transport != nullptr) && !transport->description.ice_ufrag.empty()) { + ufrag = transport->description.ice_ufrag; + } + result.emplace_back(mid, index, ufrag); + bundleDone = bundleDone || bundled; + } + ++index; + } return result; } - auto session = description->description(); - auto bundle = session->GetGroupByName(webrtc::GROUP_TYPE_BUNDLE); - bool bundleDone = false; - int index = 0; - for (const auto &content: session->contents()) { - auto mid = content.mid(); - bool bundled = bundle && bundle->HasContentName(mid); - if (!content.rejected && !(bundled && bundleDone)) { - auto transport = session->GetTransportInfoByName(mid); - std::optional ufrag; - if (transport && !transport->description.ice_ufrag.empty()) { - ufrag = transport->description.ice_ufrag; + + constexpr std::string_view kMidPrefix = "a=mid:"; + + // Adds a=end-of-candidates to the media sections of a description (of these mids, or all of them) + std::string addEndOfCandidates(const std::string &sdp, const std::set *mids = nullptr) { + std::string result; + bool inMedia = false; + bool rejected = false; + bool ended = false; + std::string mid; + auto endSection = [&]() { + if (inMedia && !rejected && !ended && (!mids || mids->contains(mid))) { + result += "a=end-of-candidates\r\n"; } - result.emplace_back(mid, index, ufrag); - bundleDone = bundleDone || bundled; + }; + size_t pos = 0; + while (pos < sdp.size()) { + auto end = sdp.find("\r\n", pos); + auto line = sdp.substr(pos, end == std::string::npos ? std::string::npos : end - pos); + if (line.starts_with("m=")) { + endSection(); + inMedia = true; + ended = false; + // a rejected media section has port 0 + auto space = line.find(' '); + rejected = space != std::string::npos && line.compare(space + 1, 2, "0 ") == 0; + mid.clear(); + } else if (line == "a=end-of-candidates") { + ended = true; + } else if (line.starts_with(kMidPrefix)) { + mid = line.substr(kMidPrefix.size()); + } + result += line + "\r\n"; + if (end == std::string::npos) { + break; + } + pos = end + 2; } - ++index; + endSection(); + return result; } - return result; - } - // Adds a=end-of-candidates to the media sections of a description (of these mids, or all of them) - static std::string addEndOfCandidates(const std::string &sdp, const std::set *mids = nullptr) { - std::string result; - bool inMedia = false, rejected = false, ended = false; - std::string mid; - auto endSection = [&]() { - if (inMedia && !rejected && !ended && (!mids || mids->count(mid))) { - result += "a=end-of-candidates\r\n"; - } - }; - size_t pos = 0; - while (pos < sdp.size()) { - auto end = sdp.find("\r\n", pos); - auto line = sdp.substr(pos, end == std::string::npos ? std::string::npos : end - pos); - if (line.rfind("m=", 0) == 0) { - endSection(); - inMedia = true; - ended = false; - // a rejected media section has port 0 - auto space = line.find(' '); - rejected = space != std::string::npos && line.compare(space + 1, 2, "0 ") == 0; - mid.clear(); - } else if (line == "a=end-of-candidates") { - ended = true; - } else if (line.rfind("a=mid:", 0) == 0) { - mid = line.substr(6); - } - result += line + "\r\n"; - if (end == std::string::npos) { - break; - } - pos = end + 2; + std::vector + liveDescriptions(const webrtc::scoped_refptr &pc) { + return {pc->current_local_description(), pc->current_remote_description(), pc->pending_local_description(), + pc->pending_remote_description()}; } - endSection(); - return result; - } - static std::vector liveDescriptions( - const webrtc::scoped_refptr &pc) { - return {pc->current_local_description(), pc->current_remote_description(), - pc->pending_local_description(), pc->pending_remote_description()}; - } + } // namespace - RTCPeerConnection::DescriptionView RTCPeerConnection::ReadDescription( - const webrtc::scoped_refptr &pc, DescriptionKind kind) { + RTCPeerConnection::DescriptionView + RTCPeerConnection::ReadDescription(const webrtc::scoped_refptr &pc, + DescriptionKind kind) { DescriptionView view; switch (kind) { - case DescriptionKind::kLocal: view.description = pc->local_description(); break; - case DescriptionKind::kRemote: view.description = pc->remote_description(); break; - case DescriptionKind::kCurrentLocal: view.description = pc->current_local_description(); break; - case DescriptionKind::kCurrentRemote: view.description = pc->current_remote_description(); break; - case DescriptionKind::kPendingLocal: view.description = pc->pending_local_description(); break; - case DescriptionKind::kPendingRemote: view.description = pc->pending_remote_description(); break; - } - if (!view.description) { + case DescriptionKind::kLocal: + view.description = pc->local_description(); + break; + case DescriptionKind::kRemote: + view.description = pc->remote_description(); + break; + case DescriptionKind::kCurrentLocal: + view.description = pc->current_local_description(); + break; + case DescriptionKind::kCurrentRemote: + view.description = pc->current_remote_description(); + break; + case DescriptionKind::kPendingLocal: + view.description = pc->pending_local_description(); + break; + case DescriptionKind::kPendingRemote: + view.description = pc->pending_remote_description(); + break; + } + if (view.description == nullptr) { return view; } - auto &init = view.init.emplace( - RTCSessionDescriptionInit::Wrap(const_cast(view.description))); - bool local = kind == DescriptionKind::kLocal || kind == DescriptionKind::kCurrentLocal || - kind == DescriptionKind::kPendingLocal; + auto &init = view.init.emplace(RTCSessionDescriptionInit::Wrap(view.description)); + const bool local = kind == DescriptionKind::kLocal || kind == DescriptionKind::kCurrentLocal || + kind == DescriptionKind::kPendingLocal; if (local && pc->ice_gathering_state() == IceGatheringState::kIceGatheringComplete) { init.sdp = addEndOfCandidates(init.sdp); } else if (!local && view.description == pc->remote_description()) { // the end of remote candidates, from addIceCandidate - std::lock_guard lock(_remoteEndOfCandidatesMutex); + const std::scoped_lock lock(_remoteEndOfCandidatesMutex); if (_remoteEndOfCandidatesDescription == view.description && !_remoteEndOfCandidates.empty()) { init.sdp = addEndOfCandidates(init.sdp, &_remoteEndOfCandidates); } @@ -883,7 +925,7 @@ namespace python_webrtc { bool shown = false; { // the descriptions as the last event that changed them left them, until the next one - std::lock_guard lock(_descriptionsMutex); + const std::scoped_lock lock(_descriptionsMutex); if (HasListeners() && _shown && _shownGeneration == _descriptionsGeneration) { view = _shown->kinds[static_cast(kind)]; live = _shown->live; @@ -892,36 +934,37 @@ namespace python_webrtc { } if (!shown) { // the description is owned by the peer connection and must be read on the signaling thread - _factory->_signalingThread->BlockingCall([&]() { + _factory->signalingThread()->BlockingCall([&]() { view = ReadDescription(pc, kind); live = liveDescriptions(pc); }); } - std::lock_guard lock(_descriptionsMutex); + const std::scoped_lock lock(_descriptionsMutex); // while events are delivered, a description only changes with them; without events it's always current - bool refresh = shown || !HasListeners() || _descriptionsCachedGeneration != _descriptionsGeneration; + const bool refresh = shown || !HasListeners() || _descriptionsCachedGeneration != _descriptionsGeneration; _descriptionsCachedGeneration = _descriptionsGeneration; return FindOrCreateDescription(view, live, refresh); } std::shared_ptr RTCPeerConnection::FindOrCreateDescription( - const DescriptionView &view, const std::vector &live, - bool refresh) { + const DescriptionView &view, const std::vector &live, bool refresh) { // descriptions that aren't set anymore are forgotten, so a new one at the same address is a new object std::vector>> kept; std::shared_ptr result; - for (auto &entry: _descriptions) { - if (std::find(live.begin(), live.end(), entry.first) == live.end()) { + for (auto &entry : _descriptions) { + if (std::ranges::find(live, entry.first) == live.end()) { continue; } - auto &init = entry.second->init(); - if (entry.first == view.description && init.type == view.init->type && (!refresh || init.sdp == view.init->sdp)) { + const auto &init = entry.second->init(); + if (view.init && entry.first == view.description && init.type == view.init->type && + (!refresh || init.sdp == view.init->sdp)) { result = entry.second; } kept.push_back(std::move(entry)); } - if (view.description && !result) { + // set with the description + if (view.init && !result) { result = std::make_shared(*view.init); kept.emplace_back(view.description, result); } @@ -963,7 +1006,7 @@ namespace python_webrtc { snapshot.kinds[kind] = ReadDescription(pc, static_cast(kind)); } snapshot.live = liveDescriptions(pc); - std::lock_guard lock(_descriptionsMutex); + const std::scoped_lock lock(_descriptionsMutex); auto id = ++_lastSnapshot; _snapshots.emplace(id, std::move(snapshot)); while (_snapshots.size() > kMaxPendingSnapshots) { @@ -973,7 +1016,7 @@ namespace python_webrtc { } void RTCPeerConnection::ApplyDescriptions(std::optional snapshot) { - std::lock_guard lock(_descriptionsMutex); + const std::scoped_lock lock(_descriptionsMutex); auto it = _snapshots.find(snapshot.value_or(_completionSnapshot)); if (it == _snapshots.end()) { return; @@ -985,7 +1028,7 @@ namespace python_webrtc { } void RTCPeerConnection::RefreshDescriptions() { - std::lock_guard lock(_descriptionsMutex); + const std::scoped_lock lock(_descriptionsMutex); ++_descriptionsGeneration; } @@ -994,8 +1037,8 @@ namespace python_webrtc { return pc ? pc->can_trickle_ice_candidates() : std::nullopt; } - std::shared_ptr RTCPeerConnection::AddTrack( - MediaStreamTrack &mediaStreamTrack, const std::vector &mediaStreams) { + std::shared_ptr RTCPeerConnection::AddTrack(MediaStreamTrack &mediaStreamTrack, + const std::vector &mediaStreams) { auto pc = connection(); if (!pc) { throw RTCException(closedError("addTrack")); @@ -1003,7 +1046,7 @@ namespace python_webrtc { std::vector streamIds; streamIds.reserve(mediaStreams.size()); - for (auto const &stream: mediaStreams) { + for (const auto &stream : mediaStreams) { streamIds.emplace_back(stream->stream()->id()); } @@ -1016,8 +1059,9 @@ namespace python_webrtc { return Wrap(_senders, result.value()); } - std::shared_ptr RTCPeerConnection::AddTrack( - MediaStreamTrack &mediaStreamTrack, std::optional> mediaStream) { + std::shared_ptr + RTCPeerConnection::AddTrack(MediaStreamTrack &mediaStreamTrack, + std::optional> mediaStream) { std::vector mediaStreams; if (mediaStream) { mediaStreams.push_back(&mediaStream->get()); @@ -1032,8 +1076,9 @@ namespace python_webrtc { } auto senders = pc->GetSenders(); - if (std::find(senders.begin(), senders.end(), sender.sender()) == senders.end()) { - throw RTCException(webrtc::RTCErrorType::INVALID_PARAMETER, "The sender was not created by this RTCPeerConnection"); + if (std::ranges::find(senders, sender.sender()) == senders.end()) { + throw RTCException(webrtc::RTCErrorType::INVALID_PARAMETER, + "The sender was not created by this RTCPeerConnection"); } auto error = pc->RemoveTrackOrError(sender.sender()); @@ -1043,8 +1088,9 @@ namespace python_webrtc { QueueNegotiationNeeded(); } - std::shared_ptr RTCPeerConnection::AddTransceiver( - webrtc::MediaType kind, std::optional> &init) { + std::shared_ptr + RTCPeerConnection::AddTransceiver(webrtc::MediaType kind, + std::optional> &init) { auto pc = connection(); if (!pc) { throw RTCException(closedError("addTransceiver")); @@ -1052,8 +1098,9 @@ namespace python_webrtc { return AddedTransceiver(init ? pc->AddTransceiver(kind, init->get()) : pc->AddTransceiver(kind)); } - std::shared_ptr RTCPeerConnection::AddTransceiver( - MediaStreamTrack &track, std::optional> &init) { + std::shared_ptr + RTCPeerConnection::AddTransceiver(MediaStreamTrack &track, + std::optional> &init) { auto pc = connection(); if (!pc) { throw RTCException(closedError("addTransceiver")); @@ -1075,7 +1122,8 @@ namespace python_webrtc { return Sync(_transceivers, pc->GetTransceivers()); } auto closed = closedConnection(); - return closed ? Unkept(closed->GetTransceivers()) : std::vector>(); + return closed ? Unkept(closed->GetTransceivers()) + : std::vector>(); } std::vector> RTCPeerConnection::GetSenders() { @@ -1094,9 +1142,10 @@ namespace python_webrtc { return {}; } - std::shared_ptr RTCPeerConnection::CreateDataChannel( - const std::string &label, bool ordered, std::optional maxPacketLifeTime, std::optional maxRetransmits, - const std::string &protocol, bool negotiated, std::optional id, webrtc::Priority priority) { + std::shared_ptr + RTCPeerConnection::CreateDataChannel(const std::string &label, bool ordered, std::optional maxPacketLifeTime, + std::optional maxRetransmits, const std::string &protocol, bool negotiated, + std::optional id, webrtc::Priority priority) { auto pc = connection(); if (!pc || pc->signaling_state() == SignalingState::kClosed) { throw RTCException(closedError("createDataChannel")); @@ -1116,17 +1165,19 @@ namespace python_webrtc { auto error = result.MoveError(); // an id in use, no id left, or too many channels: the arguments were checked by Python already throw RTCException(error.type() == webrtc::RTCErrorType::INVALID_STATE - ? error.type() : webrtc::RTCErrorType::UNSUPPORTED_OPERATION, error.message()); + ? error.type() + : webrtc::RTCErrorType::UNSUPPORTED_OPERATION, + error.message()); } QueueNegotiationNeeded(); return Wrap(_channels, result.MoveValue()); } std::optional> RTCPeerConnection::GetSctp() { - if (!onLibwebrtcThread) { + if (!OnLibwebrtcThread()) { // see Wrap - gil_release_if_held release; - return _factory->_signalingThread->BlockingCall([this]() { return GetSctp(); }); + const gil_release_if_held release; + return _factory->signalingThread()->BlockingCall([this]() { return GetSctp(); }); } auto pc = connection(); auto transport = pc ? pc->GetSctpTransport() : nullptr; @@ -1135,7 +1186,7 @@ namespace python_webrtc { } std::shared_ptr previous; - std::lock_guard lock(_wrappersMutex); + const std::scoped_lock lock(_wrappersMutex); if (!_sctp || _sctp->transport() != transport) { previous = std::move(_sctp); _sctp = RTCSctpTransport::holder().GetOrCreate(_factory, transport); @@ -1151,22 +1202,22 @@ namespace python_webrtc { if (!pc) { return {}; } - return _factory->_signalingThread->BlockingCall([&pc]() -> std::optional { + return _factory->signalingThread()->BlockingCall([&pc]() -> std::optional { auto sctpSize = [](const webrtc::SessionDescriptionInterface *description) -> std::optional { if (!description) { return {}; } - for (const auto &content: description->description()->contents()) { - auto *sctp = content.media_description() ? content.media_description()->as_sctp() : nullptr; + for (const auto &content : description->description()->contents()) { + const auto *sctp = content.media_description() ? content.media_description()->as_sctp() : nullptr; if (sctp && !content.rejected) { return sctp->max_message_size(); } } return {}; }; - double canSendSize = sctpSize(pc->current_local_description()).value_or(kLocalMaxMessageSize); + const double canSendSize = sctpSize(pc->current_local_description()).value_or(kLocalMaxMessageSize); // updated once the description is negotiated, when an answer is set - double remoteSize = sctpSize(pc->current_remote_description()).value_or(kDefaultRemoteMaxMessageSize); + const double remoteSize = sctpSize(pc->current_remote_description()).value_or(kDefaultRemoteMaxMessageSize); if (remoteSize == 0 && canSendSize == 0) { return std::numeric_limits::infinity(); } @@ -1178,7 +1229,7 @@ namespace python_webrtc { } ConfigurationInit RTCPeerConnection::GetConfiguration() { - std::lock_guard lock(_connectionMutex); + const std::scoped_lock lock(_connectionMutex); return _configuration; } @@ -1189,11 +1240,11 @@ namespace python_webrtc { } { - std::lock_guard lock(_connectionMutex); + const std::scoped_lock lock(_connectionMutex); if (init.alwaysNegotiateDataChannels != _configuration.alwaysNegotiateDataChannels || init.rtpHeaderEncryptionPolicy != _configuration.rtpHeaderEncryptionPolicy) { throw RTCException(webrtc::RTCErrorType::INVALID_MODIFICATION, - "alwaysNegotiateDataChannels and rtpHeaderEncryptionPolicy can't be changed"); + "alwaysNegotiateDataChannels and rtpHeaderEncryptionPolicy can't be changed"); } } auto error = pc->SetConfiguration(init.Apply(pc->GetConfiguration())); @@ -1201,7 +1252,7 @@ namespace python_webrtc { throw RTCException(error); } - std::lock_guard lock(_connectionMutex); + const std::scoped_lock lock(_connectionMutex); auto certificates = _configuration.certificates; _configuration = init; if (!_configuration.certificates) { @@ -1212,7 +1263,7 @@ namespace python_webrtc { void RTCPeerConnection::RestartIce() { auto pc = connection(); // before the first local description, there are no credentials to replace: the first offer has new ones - if (pc && pc->local_description()) { + if (pc && (pc->local_description() != nullptr)) { pc->RestartIce(); QueueNegotiationNeeded(); } @@ -1221,7 +1272,7 @@ namespace python_webrtc { void RTCPeerConnection::QueueNegotiationNeeded() { // libwebrtc posts the negotiationneeded event of a change to the signaling thread: once the thread ran it, // the event is queued during the call - _factory->_signalingThread->BlockingCall([]() {}); + _factory->signalingThread()->BlockingCall([]() {}); } bool RTCPeerConnection::ShouldFireNegotiationNeededEvent(uint32_t eventId) { @@ -1233,8 +1284,8 @@ namespace python_webrtc { // closing changes states, but a closed connection fires no events, nor do its channels and transports Mute(); { - std::lock_guard lock(_wrappersMutex); - for (auto &channel: _channels) { + const std::scoped_lock lock(_wrappersMutex); + for (auto &channel : _channels) { channel.second->OnPeerConnectionClosed(); } } @@ -1242,7 +1293,7 @@ namespace python_webrtc { webrtc::scoped_refptr pc; { - std::lock_guard lock(_connectionMutex); + const std::scoped_lock lock(_connectionMutex); pc = std::move(_jinglePeerConnection); if (pc) { _closedConnection = pc; @@ -1251,7 +1302,7 @@ namespace python_webrtc { if (pc) { pc->Close(); - for (const auto &transceiver: pc->GetTransceivers()) { + for (const auto &transceiver : pc->GetTransceivers()) { Wrap(_transceivers, transceiver)->GetReceiver()->GetTrack()->OnPeerConnectionClosed(); } } @@ -1315,7 +1366,7 @@ namespace python_webrtc { Emit("signalingstatechange", newState, SnapshotDescriptions()); } - void RTCPeerConnection::OnIceConnectionChange(IceConnectionState) { + void RTCPeerConnection::OnIceConnectionChange(IceConnectionState /*unused*/) { // the legacy state, iceConnectionState is the standardized one } @@ -1331,8 +1382,8 @@ namespace python_webrtc { Emit("connectionstatechange", newState); } - template - void RTCPeerConnection::EmitGathering(const char *name, Args... args) { + template + void RTCPeerConnection::EmitGathering(const char *name, const Args &...args) { _heldGathering.Emit([this, name, args...]() { Emit(name, args...); }); } @@ -1346,12 +1397,12 @@ namespace python_webrtc { // every transport ends its candidates with an empty one auto pc = connection(); - for (auto &candidate: endOfCandidates(pc ? pc->local_description() : nullptr)) { + for (auto &candidate : endOfCandidates(pc ? pc->local_description() : nullptr)) { EmitGathering("icecandidate", candidate); } // then, in a single task, the ICE transports and the connection complete, and the candidates end std::vector> iceTransports; - for (const auto &iceTransport: IceTransports()) { + for (const auto &iceTransport : IceTransports()) { if (iceTransport->IsHeld()) { // Python doesn't have it yet: its completion waits along with its other events iceTransport->EmitGatheringComplete(); @@ -1364,8 +1415,8 @@ namespace python_webrtc { void RTCPeerConnection::OnIceCandidate(const webrtc::IceCandidateInterface *candidate) { // the candidate is only valid during the call - if (candidate) { - IceCandidateInit init(*candidate); + if (candidate != nullptr) { + const IceCandidateInit init(*candidate); if (auto iceTransport = IceTransportByMid(candidate->sdp_mid())) { iceTransport->AddLocalCandidate(init); } @@ -1378,20 +1429,24 @@ namespace python_webrtc { Emit("icecandidateerror", address, port, url, errorCode, errorText); } - // the DTLS transports of the media sections and of the data channels, some of them shared (bundled) - static std::vector> dtlsTransports( - const webrtc::scoped_refptr &pc) { - std::vector> transports; - for (const auto &transceiver: pc->GetTransceivers()) { - transports.push_back(transceiver->sender()->dtls_transport()); - transports.push_back(transceiver->receiver()->dtls_transport()); - } - if (auto sctp = pc->GetSctpTransport()) { - transports.push_back(sctp->dtls_transport()); + namespace { + + // the DTLS transports of the media sections and of the data channels, some of them shared (bundled) + std::vector> + dtlsTransports(const webrtc::scoped_refptr &pc) { + std::vector> transports; + for (const auto &transceiver : pc->GetTransceivers()) { + transports.push_back(transceiver->sender()->dtls_transport()); + transports.push_back(transceiver->receiver()->dtls_transport()); + } + if (auto sctp = pc->GetSctpTransport()) { + transports.push_back(sctp->dtls_transport()); + } + std::erase(transports, nullptr); + return transports; } - transports.erase(std::remove(transports.begin(), transports.end(), nullptr), transports.end()); - return transports; - } + + } // namespace std::vector> RTCPeerConnection::IceTransports() { std::vector> iceTransports; @@ -1399,10 +1454,10 @@ namespace python_webrtc { if (!pc) { return iceTransports; } - for (const auto &transport: dtlsTransports(pc)) { + for (const auto &transport : dtlsTransports(pc)) { auto dtls = RTCDtlsTransport::holder().Find(transport.get()); auto ice = dtls ? dtls->GetIceTransport() : nullptr; - if (ice && std::find(iceTransports.begin(), iceTransports.end(), ice) == iceTransports.end()) { + if (ice && std::ranges::find(iceTransports, ice) == iceTransports.end()) { iceTransports.push_back(ice); } } @@ -1423,8 +1478,8 @@ namespace python_webrtc { return created; } std::vector> wrappers; - for (const auto &transport: dtlsTransports(pc)) { - bool existed = RTCDtlsTransport::holder().Find(transport.get()) != nullptr; + for (const auto &transport : dtlsTransports(pc)) { + const bool existed = RTCDtlsTransport::holder().Find(transport.get()) != nullptr; auto wrapper = RTCDtlsTransport::holder().GetOrCreate(_factory, transport); if (!existed) { // Python doesn't have the transports yet, their events wait for it @@ -1432,7 +1487,7 @@ namespace python_webrtc { wrapper->GetIceTransport()->Hold(); Adopt(wrapper); } - if (std::find(wrappers.begin(), wrappers.end(), wrapper) == wrappers.end()) { + if (std::ranges::find(wrappers, wrapper) == wrappers.end()) { if (!existed) { created.push_back(wrapper->GetIceTransport()); } @@ -1440,7 +1495,7 @@ namespace python_webrtc { } } { - std::lock_guard lock(_wrappersMutex); + const std::scoped_lock lock(_wrappersMutex); std::swap(_dtlsTransports, wrappers); } // wrappers of transports that are gone are released here, out of the lock @@ -1455,7 +1510,7 @@ namespace python_webrtc { std::vector> transports; webrtc::scoped_refptr sctpTransport; - _factory->_signalingThread->BlockingCall([&]() { + _factory->signalingThread()->BlockingCall([&]() { transports = dtlsTransports(pc); sctpTransport = pc->GetSctpTransport(); }); @@ -1463,7 +1518,7 @@ namespace python_webrtc { if (auto sctp = sctpTransport ? RTCSctpTransport::holder().Find(sctpTransport.get()) : nullptr) { sctp->OnPeerConnectionClosed(); } - for (const auto &transport: transports) { + for (const auto &transport : transports) { if (auto dtls = RTCDtlsTransport::holder().Find(transport.get())) { dtls->OnPeerConnectionClosed(); dtls->GetIceTransport()->OnPeerConnectionClosed(); @@ -1479,7 +1534,7 @@ namespace python_webrtc { auto state = pc->signaling_state(); if (state == SignalingState::kStable || state == SignalingState::kHaveLocalPrAnswer || state == SignalingState::kHaveRemotePrAnswer) { - for (const auto &iceTransport: IceTransports()) { + for (const auto &iceTransport : IceTransports()) { iceTransport->SetRoleKnown(); } } @@ -1490,10 +1545,10 @@ namespace python_webrtc { auto pc = connection(); if (mid.empty() && pc) { // signaled by the index of its media section - _factory->_signalingThread->BlockingCall([&]() { - auto description = pc->remote_description(); - auto &contents = description ? description->description()->contents() - : std::vector(); + _factory->signalingThread()->BlockingCall([&]() { + const auto *description = pc->remote_description(); + const auto &contents = + description ? description->description()->contents() : std::vector(); if (candidate.sdpMLineIndex >= 0 && static_cast(candidate.sdpMLineIndex) < contents.size()) { mid = contents[candidate.sdpMLineIndex].mid(); } @@ -1506,37 +1561,37 @@ namespace python_webrtc { void RTCPeerConnection::RecordRemoteDescriptionCandidates() { auto pc = connection(); - auto description = pc ? pc->remote_description() : nullptr; - if (!description) { + const auto *description = pc ? pc->remote_description() : nullptr; + if (description == nullptr) { return; } const auto &contents = description->description()->contents(); for (size_t index = 0; index < contents.size(); ++index) { auto iceTransport = IceTransportByMid(contents[index].mid()); - auto candidates = description->candidates(index); - if (!iceTransport || !candidates) { + const auto *candidates = description->candidates(index); + if (!iceTransport || (candidates == nullptr)) { continue; } - for (const auto &candidate: candidates->candidates()) { + for (const auto &candidate : candidates->candidates()) { iceTransport->AddRemoteCandidate(IceCandidateInit(*candidate)); } } } - std::optional> RTCPeerConnection::IceParameters( - const webrtc::IceTransportInterface *iceTransport, bool local) { + std::optional> + RTCPeerConnection::IceParameters(const webrtc::IceTransportInterface *iceTransport, bool local) { auto pc = connection(); if (!pc) { return {}; } - return _factory->_signalingThread->BlockingCall([&]() -> std::optional> { - auto description = local ? pc->local_description() : pc->remote_description(); + return _factory->signalingThread()->BlockingCall([&]() -> std::optional> { + const auto *description = local ? pc->local_description() : pc->remote_description(); if (!description) { return std::nullopt; } - for (const auto &content: description->description()->contents()) { + for (const auto &content : description->description()->contents()) { auto dtls = pc->LookupDtlsTransportByMid(content.mid()); - auto info = description->description()->GetTransportInfoByName(content.mid()); + const auto *info = description->description()->GetTransportInfoByName(content.mid()); if (dtls && dtls->ice_transport().get() == iceTransport && info) { return std::make_pair(info->description.ice_ufrag, info->description.ice_pwd); } @@ -1545,57 +1600,74 @@ namespace python_webrtc { }); } - void RTCPeerConnection::OnIceSelectedCandidatePairChanged(const webrtc::CandidatePairChangeEvent &) { + void RTCPeerConnection::OnIceSelectedCandidatePairChanged(const webrtc::CandidatePairChangeEvent & /*unused*/) { // the event doesn't tell which transport changed, the ICE transports that Python has check themselves - for (const auto &ice: IceTransports()) { + for (const auto &ice : IceTransports()) { ice->CheckSelectedCandidatePair(); } } - void RTCPeerConnection::AddIceCandidate( - std::function &onSuccess, std::function &onFailure, - const std::string &candidate, const std::optional &sdpMid, std::optional sdpMLineIndex, - const std::optional &usernameFragment) { - auto pc = connection(); - if (!pc) { - onFailure(RTCCallbackException(closedError("addIceCandidate"))); - return; - } + namespace { - // the media sections the candidate is for (all of them for a null mid and index), and whether its ufrag is known - std::optional error; - std::set mids; - const webrtc::SessionDescriptionInterface *remote = nullptr; - _factory->_signalingThread->BlockingCall([&]() { - remote = pc->remote_description(); - if (!remote) { - error.emplace(webrtc::RTCErrorType::INVALID_STATE, "The remote description was null"); - return; + // The media sections a candidate is for (all of them for a null mid and index), into mids, and whether its ufrag + // is known. On the signaling thread. + std::optional candidateSections(const webrtc::SessionDescriptionInterface *remote, + const std::optional &sdpMid, + std::optional sdpMLineIndex, + const std::optional &usernameFragment, + std::set &mids) { + if (remote == nullptr) { + return RTCCallbackException(webrtc::RTCErrorType::INVALID_STATE, "The remote description was null"); } - auto session = remote->description(); - auto &contents = session->contents(); + const auto *session = remote->description(); + const auto &contents = session->contents(); for (size_t i = 0; i < contents.size(); ++i) { - bool matches = sdpMid ? contents[i].mid() == *sdpMid - : sdpMLineIndex ? static_cast(i) == *sdpMLineIndex : true; + bool matches = true; + if (sdpMid) { + matches = contents[i].mid() == *sdpMid; + } else if (sdpMLineIndex) { + matches = std::cmp_equal(i, *sdpMLineIndex); + } if (matches && !contents[i].rejected) { mids.insert(contents[i].mid()); } } if (mids.empty() && (sdpMid || sdpMLineIndex)) { - error.emplace(webrtc::RTCErrorType::UNSUPPORTED_OPERATION, "The media section of the candidate was not found"); - return; + return RTCCallbackException(webrtc::RTCErrorType::UNSUPPORTED_OPERATION, + "The media section of the candidate was not found"); } if (usernameFragment) { - bool known = false; - for (const auto &mid: mids) { - auto transport = session->GetTransportInfoByName(mid); - known = known || (transport && transport->description.ice_ufrag == *usernameFragment); - } + const bool known = std::ranges::any_of(mids, [&](const std::string &mid) { + const auto *transport = session->GetTransportInfoByName(mid); + return transport != nullptr && transport->description.ice_ufrag == *usernameFragment; + }); if (!known) { - error.emplace(webrtc::RTCErrorType::UNSUPPORTED_OPERATION, - "The usernameFragment doesn't match the remote description"); + return RTCCallbackException(webrtc::RTCErrorType::UNSUPPORTED_OPERATION, + "The usernameFragment doesn't match the remote description"); } } + return std::nullopt; + } + + } // namespace + + void RTCPeerConnection::AddIceCandidate(std::function &onSuccess, + std::function &onFailure, + const std::string &candidate, const std::optional &sdpMid, + std::optional sdpMLineIndex, + const std::optional &usernameFragment) { + auto pc = connection(); + if (!pc) { + onFailure(RTCCallbackException(closedError("addIceCandidate"))); + return; + } + + std::optional error; + std::set mids; + const webrtc::SessionDescriptionInterface *remote = nullptr; + _factory->signalingThread()->BlockingCall([&]() { + remote = pc->remote_description(); + error = candidateSections(remote, sdpMid, sdpMLineIndex, usernameFragment, mids); }); if (error) { onFailure(*error); @@ -1605,7 +1677,7 @@ namespace python_webrtc { if (candidate.empty()) { // the end of candidates, which libwebrtc doesn't take: the remote description shows it { - std::lock_guard lock(_remoteEndOfCandidatesMutex); + const std::scoped_lock lock(_remoteEndOfCandidatesMutex); if (_remoteEndOfCandidatesDescription != remote) { _remoteEndOfCandidates.clear(); _remoteEndOfCandidatesDescription = remote; @@ -1618,11 +1690,11 @@ namespace python_webrtc { } webrtc::SdpParseError parseError; - auto iceCandidate = webrtc::IceCandidate::Create( - sdpMid.value_or(""), sdpMLineIndex.value_or(0), candidate, &parseError); + auto iceCandidate = + webrtc::IceCandidate::Create(sdpMid.value_or(""), sdpMLineIndex.value_or(0), candidate, &parseError); if (!iceCandidate) { onFailure(RTCCallbackException(webrtc::RTCErrorType::UNSUPPORTED_OPERATION, - "Failed to parse the ICE candidate: " + parseError.description)); + "Failed to parse the ICE candidate: " + parseError.description)); return; } @@ -1666,27 +1738,28 @@ namespace python_webrtc { Emit("datachannel", channel); } - void RTCPeerConnection::OnAddStream(webrtc::scoped_refptr) {} + void RTCPeerConnection::OnAddStream(webrtc::scoped_refptr /*unused*/) {} - void RTCPeerConnection::OnRemoveStream(webrtc::scoped_refptr) {} + void RTCPeerConnection::OnRemoveStream(webrtc::scoped_refptr /*unused*/) {} - void RTCPeerConnection::OnAddTrack(webrtc::scoped_refptr, - const std::vector> &) {} + void + RTCPeerConnection::OnAddTrack(webrtc::scoped_refptr /*unused*/, + const std::vector> & /*unused*/) {} void RTCPeerConnection::OnTrack(webrtc::scoped_refptr transceiver) { // a rejected media section (port 0) negotiates no track, but libwebrtc still reports it auto pc = connection(); - auto description = pc ? pc->remote_description() : nullptr; + const auto *description = pc ? pc->remote_description() : nullptr; auto mid = transceiver->mid(); - if (description && mid) { - auto content = description->description()->GetContentByName(*mid); - if (content && content->rejected) { + if ((description != nullptr) && mid) { + const auto *content = description->description()->GetContentByName(*mid); + if ((content != nullptr) && content->rejected) { return; } } { - std::lock_guard lock(_remoteStreamsMutex); + const std::scoped_lock lock(_remoteStreamsMutex); _trackFired.insert(transceiver.get()); } EmitTrack(transceiver); @@ -1703,49 +1776,53 @@ namespace python_webrtc { auto wrapper = Wrap(_transceivers, transceiver); auto receiver = Wrap(_receivers, transceiver->receiver()); std::vector> streams; - for (const auto &stream: transceiver->receiver()->streams()) { + for (const auto &stream : transceiver->receiver()->streams()) { streams.push_back(MediaStream::holder().GetOrCreate(_factory, stream)); } Emit("track", wrapper, receiver, streams); } - static std::vector remoteStreamIds( - const webrtc::scoped_refptr &transceiver) { - std::vector ids; - for (const auto &stream: transceiver->receiver()->streams()) { - ids.push_back(stream->id()); + namespace { + + std::vector + remoteStreamIds(const webrtc::scoped_refptr &transceiver) { + std::vector ids; + for (const auto &stream : transceiver->receiver()->streams()) { + ids.push_back(stream->id()); + } + return ids; } - return ids; - } + + } // namespace void RTCPeerConnection::SnapshotRemoteStreams(const webrtc::scoped_refptr &pc) { std::map> before; - for (const auto &transceiver: pc->GetTransceivers()) { + for (const auto &transceiver : pc->GetTransceivers()) { before[transceiver.get()] = remoteStreamIds(transceiver); } - std::lock_guard lock(_remoteStreamsMutex); + const std::scoped_lock lock(_remoteStreamsMutex); _remoteStreamsBefore = std::move(before); _trackFired.clear(); } void RTCPeerConnection::FireRemoteStreamChanges() { auto pc = connection(); - auto description = pc ? pc->remote_description() : nullptr; - if (!description) { + const auto *description = pc ? pc->remote_description() : nullptr; + if (description == nullptr) { return; } std::map> before; std::set fired; { - std::lock_guard lock(_remoteStreamsMutex); + const std::scoped_lock lock(_remoteStreamsMutex); std::swap(before, _remoteStreamsBefore); std::swap(fired, _trackFired); } - for (const auto &transceiver: pc->GetTransceivers()) { + for (const auto &transceiver : pc->GetTransceivers()) { auto mid = transceiver->mid(); - auto content = mid ? description->description()->GetContentByName(*mid) : nullptr; + const auto *content = mid ? description->description()->GetContentByName(*mid) : nullptr; auto previous = before.find(transceiver.get()); - if (!content || content->rejected || fired.count(transceiver.get()) || previous == before.end() || + if ((content == nullptr) || content->rejected || fired.contains(transceiver.get()) || previous == before.end() || !webrtc::RtpTransceiverDirectionHasSend(content->media_description()->direction())) { continue; } diff --git a/python-webrtc/cpp/src/interfaces/rtc_peer_connection.h b/python-webrtc/cpp/src/interfaces/rtc_peer_connection.h index 1446fef..9c0b090 100644 --- a/python-webrtc/cpp/src/interfaces/rtc_peer_connection.h +++ b/python-webrtc/cpp/src/interfaces/rtc_peer_connection.h @@ -5,7 +5,8 @@ // that can be found in the LICENSE.md file in the root of the project. // -#pragma once +#ifndef PYTHON_WEBRTC_INTERFACES_RTC_PEER_CONNECTION_H_ +#define PYTHON_WEBRTC_INTERFACES_RTC_PEER_CONNECTION_H_ #include #include @@ -23,23 +24,23 @@ #include #include -#include #include +#include -#include "../utils/alive_count.h" #include "../exceptions.h" #include "../models/python_webrtc/rtc_configuration.h" #include "../models/python_webrtc/rtc_session_description.h" +#include "../utils/alive_count.h" #include "../utils/held_events.h" #include "../utils/listeners.h" #include "../utils/surfaced.h" -#include "media_stream_track.h" #include "media_stream.h" +#include "media_stream_track.h" +#include "rtc_data_channel.h" #include "rtc_rtp_sender.h" #include "rtc_rtp_transceiver.h" #include "rtc_sctp_transport.h" -#include "rtc_data_channel.h" namespace webrtc { struct PeerConnectionDependencies; @@ -51,58 +52,61 @@ namespace python_webrtc { // An operation that completes after close() fails, rather than never settling as in a browser, which would // leave a Python caller waiting forever. - class RTCPeerConnection - : public webrtc::PeerConnectionObserver, public Listeners, public std::enable_shared_from_this { + class RTCPeerConnection : public webrtc::PeerConnectionObserver, + public Listeners, + public std::enable_shared_from_this { public: using SignalingState = webrtc::PeerConnectionInterface::SignalingState; using IceConnectionState = webrtc::PeerConnectionInterface::IceConnectionState; using IceGatheringState = webrtc::PeerConnectionInterface::IceGatheringState; using PeerConnectionState = webrtc::PeerConnectionInterface::PeerConnectionState; - explicit RTCPeerConnection(const std::optional &); + explicit RTCPeerConnection(const std::optional &init); static void Init(pybind11::module &m); ~RTCPeerConnection() override; + RTCPeerConnection(const RTCPeerConnection &) = delete; + RTCPeerConnection &operator=(const RTCPeerConnection &) = delete; + // A reference taken in a callback of libwebrtc, released on a thread of its own: the connection can't be // destroyed on the signaling thread in the middle of its own callback static void ReleaseElsewhere(std::shared_ptr &&connection); - void CreateOffer( - std::function &, std::function &, - bool iceRestart, bool voiceActivityDetection); + void CreateOffer(std::function &onSuccess, + std::function &onFailure, bool iceRestart, + bool voiceActivityDetection); - void CreateAnswer( - std::function &, std::function &, - bool voiceActivityDetection); + void CreateAnswer(std::function &onSuccess, + std::function &onFailure, bool voiceActivityDetection); // the last description createOffer or createAnswer made, which are the only ones setLocalDescription takes void SaveCreatedDescription(const RTCSessionDescriptionInit &description); - void SetLocalDescription( - std::function &, std::function &, - const std::optional &); + void SetLocalDescription(std::function &onSuccess, std::function &onFailure, + const std::optional &init); - void SetRemoteDescription( - std::function &, std::function &, const RTCSessionDescriptionInit &); + void SetRemoteDescription(std::function &onSuccess, std::function &onFailure, + const RTCSessionDescriptionInit &init); - void AddIceCandidate( - std::function &, std::function &, const std::string &candidate, - const std::optional &sdpMid, std::optional sdpMLineIndex, - const std::optional &usernameFragment); + void AddIceCandidate(std::function &onSuccess, std::function &onFailure, + const std::string &candidate, const std::optional &sdpMid, + std::optional sdpMLineIndex, const std::optional &usernameFragment); - std::shared_ptr AddTrack(MediaStreamTrack &, std::optional>); + std::shared_ptr AddTrack(MediaStreamTrack &mediaStreamTrack, + std::optional> mediaStream); - std::shared_ptr AddTrack(MediaStreamTrack &, const std::vector &); + std::shared_ptr AddTrack(MediaStreamTrack &mediaStreamTrack, + const std::vector &mediaStreams); - void RemoveTrack(RTCRtpSender &); + void RemoveTrack(RTCRtpSender &sender); - std::shared_ptr AddTransceiver( - webrtc::MediaType, std::optional> &); + std::shared_ptr + AddTransceiver(webrtc::MediaType kind, std::optional> &init); - std::shared_ptr AddTransceiver( - MediaStreamTrack &, std::optional> &); + std::shared_ptr + AddTransceiver(MediaStreamTrack &track, std::optional> &init); std::vector> GetTransceivers(); @@ -110,19 +114,21 @@ namespace python_webrtc { std::vector> GetReceivers(); - std::shared_ptr CreateDataChannel( - const std::string &label, bool ordered, std::optional maxPacketLifeTime, std::optional maxRetransmits, - const std::string &protocol, bool negotiated, std::optional id, webrtc::Priority priority); + std::shared_ptr CreateDataChannel(const std::string &label, bool ordered, + std::optional maxPacketLifeTime, + std::optional maxRetransmits, const std::string &protocol, + bool negotiated, std::optional id, + webrtc::Priority priority); std::optional> GetSctp(); - void GetStats(std::function &, std::function &); + void GetStats(std::function &onSuccess, std::function &onFailure); void RestartIce(); ConfigurationInit GetConfiguration(); - void SetConfiguration(const ConfigurationInit &); + void SetConfiguration(const ConfigurationInit &init); void Close(); @@ -176,26 +182,28 @@ namespace python_webrtc { bool IsClosed(); // the transceiver of a sender, while it's in the connection - webrtc::scoped_refptr TransceiverOf( - const webrtc::scoped_refptr &sender); + webrtc::scoped_refptr + TransceiverOf(const webrtc::scoped_refptr &sender); // The negotiated codecs this side can use: libwebrtc reports none for an inactive sender (receiver), and // lists remote codecs it doesn't know - std::vector NegotiatedCodecs( - const webrtc::scoped_refptr &sender); + std::vector + NegotiatedCodecs(const webrtc::scoped_refptr &sender); - std::vector NegotiatedCodecs( - const webrtc::scoped_refptr &receiver); + std::vector + NegotiatedCodecs(const webrtc::scoped_refptr &receiver); // the header extensions negotiated for a receiver (in the local description) - std::vector NegotiatedHeaderExtensions( - const webrtc::scoped_refptr &receiver); + std::vector + NegotiatedHeaderExtensions(const webrtc::scoped_refptr &receiver); void CollectStats(const webrtc::scoped_refptr &sender, - std::function &, std::function &); + std::function &onSuccess, + std::function &onFailure); void CollectStats(const webrtc::scoped_refptr &receiver, - std::function &, std::function &); + std::function &onSuccess, + std::function &onFailure); // PeerConnectionObserver implementation, on the signaling thread. void OnSignalingChange(SignalingState newState) override; @@ -237,7 +245,14 @@ namespace python_webrtc { // until it completes, so that they come after it class HeldOperationEvents; - enum class DescriptionKind { kLocal, kRemote, kCurrentLocal, kCurrentRemote, kPendingLocal, kPendingRemote }; + enum class DescriptionKind : uint8_t { + kLocal, + kRemote, + kCurrentLocal, + kCurrentRemote, + kPendingLocal, + kPendingRemote + }; static constexpr size_t kDescriptionKinds = 6; @@ -253,7 +268,7 @@ namespace python_webrtc { std::vector live; }; - template + template using Wrappers = std::unordered_map>; // Python threads may call close() concurrently with other methods (the GIL is released) @@ -264,35 +279,36 @@ namespace python_webrtc { // The connection to collect the stats of, even closed, with its stats cache cleared: libwebrtc reuses a report // for 50 ms, the stats are the current ones. None fails the request. - webrtc::scoped_refptr StatsConnection( - const std::function &onFailure); + webrtc::scoped_refptr + StatsConnection(const std::function &onFailure); // the wrapper of a libwebrtc object of this connection, which the connection keeps while it's open - template - std::shared_ptr Wrap(Wrappers &, webrtc::scoped_refptr); + template + std::shared_ptr Wrap(Wrappers &wrappers, webrtc::scoped_refptr object); // wrappers of the current objects of this connection; wrappers of the objects that are gone are released - template - std::vector> Sync(Wrappers &, const std::vector> &); + template + std::vector> Sync(Wrappers &wrappers, + const std::vector> &objects); // wrappers of the objects of a closed connection, which doesn't keep them (see ReleaseWrappers) - template - std::vector> Unkept(const std::vector> &); + template + std::vector> Unkept(const std::vector> &objects); void ReleaseWrappers(); // gives a wrapper what it needs from the connection - void Adopt(const std::shared_ptr &); + void Adopt(const std::shared_ptr &sender); - void Adopt(const std::shared_ptr &); + void Adopt(const std::shared_ptr &receiver); - void Adopt(const std::shared_ptr &); + void Adopt(const std::shared_ptr &transceiver); - void Adopt(const std::shared_ptr &); + void Adopt(const std::shared_ptr &channel); - void Adopt(const std::shared_ptr &); + void Adopt(const std::shared_ptr &sctp); - void Adopt(const std::shared_ptr &); + void Adopt(const std::shared_ptr &dtls); // the maxMessageSize of the SCTP transport, from the descriptions until the transport knows it std::optional MaxMessageSize(); @@ -300,31 +316,33 @@ namespace python_webrtc { // waits for the negotiationneeded event a change may have caused to be emitted void QueueNegotiationNeeded(); - std::shared_ptr AddedTransceiver( - webrtc::RTCErrorOr> result); + std::shared_ptr + AddedTransceiver(webrtc::RTCErrorOr> result); // A provisional answer, or the final one after it, without SDP: the last answer createAnswer made, or a new one // (libwebrtc makes only final answers itself, and offers in have-local-pranswer) - void SetImplicitAnswer( - const webrtc::scoped_refptr &pc, webrtc::SdpType type, - std::function complete, const std::function &onFailure); + void SetImplicitAnswer(const webrtc::scoped_refptr &pc, webrtc::SdpType type, + std::function complete, + const std::function &onFailure); // What completes setLocalDescription (kLocal) or setRemoteDescription (kRemote); created right before the // operation starts, as it holds its events - std::function Completion( - std::function &onSuccess, std::function &onFailure, - const webrtc::scoped_refptr &pc, DescriptionKind kind); + std::function Completion(std::function &onSuccess, + std::function &onFailure, + const webrtc::scoped_refptr &pc, + DescriptionKind kind); - std::shared_ptr GetDescription(DescriptionKind); + std::shared_ptr GetDescription(DescriptionKind kind); // on the signaling thread - DescriptionView ReadDescription(const webrtc::scoped_refptr &, DescriptionKind); + DescriptionView ReadDescription(const webrtc::scoped_refptr &pc, + DescriptionKind kind); // the wrapper of a description that is set: the same object for as long as it's set, unless its SDP changed // meanwhile (refresh), under _descriptionsMutex - std::shared_ptr FindOrCreateDescription( - const DescriptionView &view, const std::vector &live, - bool refresh); + std::shared_ptr + FindOrCreateDescription(const DescriptionView &view, + const std::vector &live, bool refresh); // The descriptions as an operation or event left them, shown from when Python gets it (see ApplyDescriptions). // On the signaling thread. @@ -352,18 +370,18 @@ namespace python_webrtc { void RecordRemoteDescriptionCandidates(); // the username fragment and the password of an ICE transport in the local or the remote description - std::optional> IceParameters( - const webrtc::IceTransportInterface *iceTransport, bool local); + std::optional> IceParameters(const webrtc::IceTransportInterface *iceTransport, + bool local); // candidates and gathering state changes wait for the local description that caused them to be set - template - void EmitGathering(const char *name, Args... args); + template + void EmitGathering(const char *name, const Args &...args); void EmitTrack(const webrtc::scoped_refptr &transceiver); // The remote streams of each track before a remote description is set, to fire track events for tracks // moved to other streams. On the signaling thread. - void SnapshotRemoteStreams(const webrtc::scoped_refptr &); + void SnapshotRemoteStreams(const webrtc::scoped_refptr &pc); void FireRemoteStreamChanges(); @@ -426,4 +444,6 @@ namespace python_webrtc { Wrappers _channels; }; -} +} // namespace python_webrtc + +#endif // PYTHON_WEBRTC_INTERFACES_RTC_PEER_CONNECTION_H_ diff --git a/python-webrtc/cpp/src/interfaces/rtc_rtp_receiver.cpp b/python-webrtc/cpp/src/interfaces/rtc_rtp_receiver.cpp index 07a9169..0c7b560 100644 --- a/python-webrtc/cpp/src/interfaces/rtc_rtp_receiver.cpp +++ b/python-webrtc/cpp/src/interfaces/rtc_rtp_receiver.cpp @@ -13,31 +13,31 @@ #include #include -#include "rtc_peer_connection.h" #include "../enums/enums.h" #include "../utils/gil.h" #include "../utils/libwebrtc_thread.h" +#include "rtc_peer_connection.h" namespace python_webrtc { - RTCRtpReceiver::RTCRtpReceiver( - std::shared_ptr factory, webrtc::scoped_refptr receiver - ) : _factory(std::move(factory)), _receiver(std::move(receiver)), - _track(MediaStreamTrack::holder().GetOrCreate(_factory, _receiver->track())) { + RTCRtpReceiver::RTCRtpReceiver(std::shared_ptr factory, + webrtc::scoped_refptr receiver) + : _factory(std::move(factory)), _receiver(std::move(receiver)), + _track(MediaStreamTrack::holder().GetOrCreate(_factory, _receiver->track())) { // the track of a receiver is a remote one _track->MarkRemote(); // see AliveGuard - _factory->_signalingThread->PostTask(_alive.Guard([this]() { + _factory->signalingThread()->PostTask(_alive.Guard([this]() { _receiver->SetObserver(this); holder().SetObserver(_receiver.get(), this); })); } RTCRtpReceiver::~RTCRtpReceiver() { - BlockingDestructor release("RTCRtpReceiver"); + const BlockingDestructor release("RTCRtpReceiver"); // callbacks run on the signaling thread, so after this none of them can be running or start again - _factory->_signalingThread->BlockingCall([this]() { + _factory->signalingThread()->BlockingCall([this]() { // a newer wrapper of the receiver may have taken its single observer slot if (holder().TakeObserver(_receiver.get(), this)) { _receiver->SetObserver(nullptr); @@ -45,7 +45,7 @@ namespace python_webrtc { }); } - void RTCRtpReceiver::OnFirstPacketReceived(webrtc::MediaType) { + void RTCRtpReceiver::OnFirstPacketReceived(webrtc::MediaType /*mediaType*/) { _track->SetMuted(false); } @@ -60,24 +60,25 @@ namespace python_webrtc { .def_property("jitterBufferTarget", nogil_fn(&RTCRtpReceiver::GetJitterBufferTarget), nogil_fn(&RTCRtpReceiver::SetJitterBufferTarget)) .def("getParameters", &RTCRtpReceiver::GetParameters, nogil()) - .def("getStats", WithCallbacks(&RTCRtpReceiver::GetStats), pybind11::arg("onSuccess"), pybind11::arg("onFailure")) + .def("getStats", WithCallbacks(&RTCRtpReceiver::GetStats), pybind11::arg("onSuccess"), + pybind11::arg("onFailure")) .def_static("getCapabilities", &RTCRtpReceiver::GetCapabilities, nogil(), pybind11::arg("kind")) .def("_getSources", &RTCRtpReceiver::GetSources, nogil()); } InstanceHolder &RTCRtpReceiver::holder() { // never destroyed: wrappers may outlive static destructors - static auto holder = new InstanceHolder(); + static auto *holder = new InstanceHolder(); return *holder; } void RTCRtpReceiver::SetConnection(std::weak_ptr connection) { - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); _connection = std::move(connection); } std::shared_ptr RTCRtpReceiver::GetConnection() { - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); return _connection.lock(); } @@ -91,7 +92,7 @@ namespace python_webrtc { auto wrapper = RTCDtlsTransport::holder().GetOrCreate(_factory, transport); std::shared_ptr previous; - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); if (!_transport || _transport->transport() != transport) { previous = std::move(_transport); _transport = std::move(wrapper); @@ -119,16 +120,18 @@ namespace python_webrtc { } std::optional RTCRtpReceiver::GetJitterBufferTarget() { - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); return _jitterBufferTarget; } void RTCRtpReceiver::SetJitterBufferTarget(std::optional target) { { - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); _jitterBufferTarget = target; } - _receiver->SetJitterBufferMinimumDelay(target ? std::optional(*target / 1000) : std::nullopt); + // milliseconds in Python, seconds in libwebrtc + constexpr double msPerSecond = 1000; + _receiver->SetJitterBufferMinimumDelay(target ? std::optional(*target / msPerSecond) : std::nullopt); } void RTCRtpReceiver::GetStats(std::function &onSuccess, @@ -145,7 +148,7 @@ namespace python_webrtc { std::vector sources; // libwebrtc times the packets with its monotonic clock auto offset = static_cast(webrtc::TimeUTCMillis() - webrtc::TimeMillis()); - for (const auto &source: _receiver->GetSources()) { + for (const auto &source : _receiver->GetSources()) { auto level = source.audio_level(); sources.emplace_back(source.source_type() == webrtc::RtpSourceType::SSRC, source.source_id(), static_cast(source.timestamp().ms()) + offset, source.rtp_timestamp(), diff --git a/python-webrtc/cpp/src/interfaces/rtc_rtp_receiver.h b/python-webrtc/cpp/src/interfaces/rtc_rtp_receiver.h index 63fa1ff..93c7f71 100644 --- a/python-webrtc/cpp/src/interfaces/rtc_rtp_receiver.h +++ b/python-webrtc/cpp/src/interfaces/rtc_rtp_receiver.h @@ -5,7 +5,8 @@ // that can be found in the LICENSE.md file in the root of the project. // -#pragma once +#ifndef PYTHON_WEBRTC_INTERFACES_RTC_RTP_RECEIVER_H_ +#define PYTHON_WEBRTC_INTERFACES_RTC_RTP_RECEIVER_H_ #include #include @@ -18,11 +19,11 @@ #include #include -#include "peer_connection_factory.h" -#include "media_stream_track.h" -#include "rtc_dtls_transport.h" #include "../exceptions.h" #include "../utils/alive_guard.h" +#include "media_stream_track.h" +#include "peer_connection_factory.h" +#include "rtc_dtls_transport.h" namespace python_webrtc { @@ -34,10 +35,14 @@ namespace python_webrtc { // audio level in -dBov) using Source = std::tuple>; - explicit RTCRtpReceiver(std::shared_ptr, webrtc::scoped_refptr); + explicit RTCRtpReceiver(std::shared_ptr factory, + webrtc::scoped_refptr receiver); ~RTCRtpReceiver() override; + RTCRtpReceiver(const RTCRtpReceiver &) = delete; + RTCRtpReceiver &operator=(const RTCRtpReceiver &) = delete; + static void Init(pybind11::module &m); static InstanceHolder &holder(); @@ -45,9 +50,9 @@ namespace python_webrtc { webrtc::scoped_refptr receiver() { return _receiver; } // RtpReceiverObserverInterface: media arrived, which unmutes the track - void OnFirstPacketReceived(webrtc::MediaType) override; + void OnFirstPacketReceived(webrtc::MediaType mediaType) override; - void OnFirstPacketReceivedAfterReceptiveChange(webrtc::MediaType) override; + void OnFirstPacketReceivedAfterReceptiveChange(webrtc::MediaType mediaType) override; // the connection of the receiver, which knows what was negotiated for it; set by the connection void SetConnection(std::weak_ptr connection); @@ -63,7 +68,7 @@ namespace python_webrtc { void SetJitterBufferTarget(std::optional target); - void GetStats(std::function &, std::function &); + void GetStats(std::function &onSuccess, std::function &onFailure); // the sources of the packets of the last 10 seconds, the most recent first std::vector GetSources(); @@ -89,3 +94,5 @@ namespace python_webrtc { }; } // namespace python_webrtc + +#endif // PYTHON_WEBRTC_INTERFACES_RTC_RTP_RECEIVER_H_ diff --git a/python-webrtc/cpp/src/interfaces/rtc_rtp_sender.cpp b/python-webrtc/cpp/src/interfaces/rtc_rtp_sender.cpp index 4d68c08..709a8f0 100644 --- a/python-webrtc/cpp/src/interfaces/rtc_rtp_sender.cpp +++ b/python-webrtc/cpp/src/interfaces/rtc_rtp_sender.cpp @@ -11,14 +11,14 @@ #include #include -#include "rtc_peer_connection.h" #include "../enums/enums.h" #include "../utils/gil.h" +#include "rtc_peer_connection.h" namespace python_webrtc { - RTCRtpSender::RTCRtpSender( - std::shared_ptr factory, webrtc::scoped_refptr sender) + RTCRtpSender::RTCRtpSender(std::shared_ptr factory, + webrtc::scoped_refptr sender) : _factory(std::move(factory)), _sender(std::move(sender)) {} void RTCRtpSender::Init(pybind11::module &m) { @@ -28,8 +28,8 @@ namespace python_webrtc { .def_property_readonly("kind", nogil_fn(&RTCRtpSender::GetKind)) .def_property_readonly("dtmf", nogil_fn(&RTCRtpSender::GetDtmf)) .def("getParameters", &RTCRtpSender::GetParameters, nogil()) - .def("setParameters", WithCallbacks(&RTCRtpSender::SetParameters), - pybind11::arg("onSuccess"), pybind11::arg("onFailure"), pybind11::arg("parameters")) + .def("setParameters", WithCallbacks(&RTCRtpSender::SetParameters), pybind11::arg("onSuccess"), + pybind11::arg("onFailure"), pybind11::arg("parameters")) .def("replaceTrack", &RTCRtpSender::ReplaceTrack, nogil(), pybind11::arg("track")) .def("setStreams", &RTCRtpSender::SetStreams, nogil(), pybind11::arg("streamIds")) .def("getStreamIds", &RTCRtpSender::GetStreamIds, nogil()) @@ -43,12 +43,12 @@ namespace python_webrtc { InstanceHolder &RTCRtpSender::holder() { // never destroyed: wrappers may outlive static destructors - static auto holder = new InstanceHolder(); + static auto *holder = new InstanceHolder(); return *holder; } void RTCRtpSender::SetConnection(std::weak_ptr connection) { - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); _connection = std::move(connection); if (_dtmf) { _dtmf->SetTransceiver(TransceiverGetter()); @@ -56,7 +56,7 @@ namespace python_webrtc { } std::shared_ptr RTCRtpSender::GetConnection() { - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); return _connection.lock(); } @@ -73,7 +73,7 @@ namespace python_webrtc { auto wrapper = MediaStreamTrack::holder().GetOrCreate(_factory, track); std::shared_ptr previous; - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); if (!_track || _track->track() != track) { previous = std::move(_track); _track = std::move(wrapper); @@ -91,7 +91,7 @@ namespace python_webrtc { auto wrapper = RTCDtlsTransport::holder().GetOrCreate(_factory, transport); std::shared_ptr previous; - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); if (!_transport || _transport->transport() != transport) { previous = std::move(_transport); _transport = std::move(wrapper); @@ -111,7 +111,7 @@ namespace python_webrtc { auto dtmf = _sender->GetDtmfSender(); // see GetTrack auto wrapper = RTCDTMFSender::holder().GetOrCreate(_factory, dtmf); - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); if (wrapper && _dtmf != wrapper) { _dtmf = std::move(wrapper); _dtmf->SetTransceiver(TransceiverGetter()); @@ -122,7 +122,7 @@ namespace python_webrtc { webrtc::RtpParameters RTCRtpSender::GetParameters() { { // the same parameters until they expire - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); if (_lastParameters) { return *_lastParameters; } @@ -135,18 +135,18 @@ namespace python_webrtc { if (!negotiated.empty()) { parameters.codecs = std::move(negotiated); } - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); _lastParameters = parameters; return parameters; } std::optional RTCRtpSender::GetLastParameters() { - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); return _lastParameters; } void RTCRtpSender::ExpireParameters(const std::optional &transactionId) { - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); if (_lastParameters && (!transactionId || _lastParameters->transaction_id == *transactionId)) { _lastParameters.reset(); } @@ -156,15 +156,15 @@ namespace python_webrtc { std::function &onFailure, const webrtc::RtpParameters ¶meters) { { - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); if (!_lastParameters) { onFailure(RTCCallbackException(webrtc::RTCErrorType::INVALID_STATE, - "getParameters() must be called before setParameters(), in the same task")); + "getParameters() must be called before setParameters(), in the same task")); return; } if (_lastParameters->transaction_id != parameters.transaction_id) { onFailure(RTCCallbackException(webrtc::RTCErrorType::INVALID_MODIFICATION, - "The transactionId doesn't match the one of the last getParameters()")); + "The transactionId doesn't match the one of the last getParameters()")); return; } } @@ -177,7 +177,7 @@ namespace python_webrtc { } if (!sameLayers) { onFailure(RTCCallbackException(webrtc::RTCErrorType::INVALID_MODIFICATION, - "The encodings of the sender changed since getParameters()")); + "The encodings of the sender changed since getParameters()")); return; } auto current = parameters; diff --git a/python-webrtc/cpp/src/interfaces/rtc_rtp_sender.h b/python-webrtc/cpp/src/interfaces/rtc_rtp_sender.h index 2824f62..92357d6 100644 --- a/python-webrtc/cpp/src/interfaces/rtc_rtp_sender.h +++ b/python-webrtc/cpp/src/interfaces/rtc_rtp_sender.h @@ -5,7 +5,8 @@ // that can be found in the LICENSE.md file in the root of the project. // -#pragma once +#ifndef PYTHON_WEBRTC_INTERFACES_RTC_RTP_SENDER_H_ +#define PYTHON_WEBRTC_INTERFACES_RTC_RTP_SENDER_H_ #include #include @@ -17,12 +18,12 @@ #include #include -#include "peer_connection_factory.h" +#include "../enums/enums.h" +#include "../exceptions.h" #include "media_stream_track.h" +#include "peer_connection_factory.h" #include "rtc_dtls_transport.h" #include "rtc_dtmf_sender.h" -#include "../exceptions.h" -#include "../enums/enums.h" namespace python_webrtc { @@ -30,7 +31,8 @@ namespace python_webrtc { class RTCRtpSender { public: - explicit RTCRtpSender(std::shared_ptr, webrtc::scoped_refptr); + explicit RTCRtpSender(std::shared_ptr factory, + webrtc::scoped_refptr sender); static void Init(pybind11::module &m); @@ -55,8 +57,8 @@ namespace python_webrtc { webrtc::RtpParameters GetParameters(); - void SetParameters(std::function &, std::function &, - const webrtc::RtpParameters &); + void SetParameters(std::function &onSuccess, std::function &onFailure, + const webrtc::RtpParameters ¶meters); // The parameters getParameters returned last, which setParameters takes until they expire (Python expires them // once the task that got them ends, as the specification requires) @@ -72,7 +74,7 @@ namespace python_webrtc { std::vector GetStreamIds(); - void GetStats(std::function &, std::function &); + void GetStats(std::function &onSuccess, std::function &onFailure); // whether its transceiver is stopping or stopped (or gone), where the sender can't be changed anymore bool IsTransceiverStopped(); @@ -96,3 +98,5 @@ namespace python_webrtc { }; } // namespace python_webrtc + +#endif // PYTHON_WEBRTC_INTERFACES_RTC_RTP_SENDER_H_ diff --git a/python-webrtc/cpp/src/interfaces/rtc_rtp_transceiver.cpp b/python-webrtc/cpp/src/interfaces/rtc_rtp_transceiver.cpp index d295b69..e85c736 100644 --- a/python-webrtc/cpp/src/interfaces/rtc_rtp_transceiver.cpp +++ b/python-webrtc/cpp/src/interfaces/rtc_rtp_transceiver.cpp @@ -7,9 +7,9 @@ #include "rtc_rtp_transceiver.h" -#include "rtc_peer_connection.h" #include "../exceptions.h" #include "../utils/gil.h" +#include "rtc_peer_connection.h" namespace python_webrtc { @@ -28,7 +28,8 @@ namespace python_webrtc { .def_property_readonly("stopped", nogil_fn(&RTCRtpTransceiver::GetStopped)) .def_property_readonly("stopping", nogil_fn(&RTCRtpTransceiver::GetStopping)) .def_property_readonly("kind", nogil_fn(&RTCRtpTransceiver::GetKind)) - .def_property("direction", nogil_fn(&RTCRtpTransceiver::GetDirection), nogil_fn(&RTCRtpTransceiver::SetDirection)) + .def_property("direction", nogil_fn(&RTCRtpTransceiver::GetDirection), + nogil_fn(&RTCRtpTransceiver::SetDirection)) .def_property_readonly("currentDirection", nogil_fn(&RTCRtpTransceiver::GetCurrentDirection)) .def("setCodecPreferences", &RTCRtpTransceiver::SetCodecPreferences, nogil(), pybind11::arg("codecs")) .def("getCodecPreferences", &RTCRtpTransceiver::GetCodecPreferences, nogil()) @@ -41,21 +42,17 @@ namespace python_webrtc { InstanceHolder &RTCRtpTransceiver::holder() { // never destroyed: wrappers may outlive static destructors - static auto holder = new InstanceHolder(); + static auto *holder = new InstanceHolder(); return *holder; } void RTCRtpTransceiver::SetConnection(std::weak_ptr connection) { - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); _connection = std::move(connection); } std::optional RTCRtpTransceiver::GetMid() { - if (_transceiver->mid()) { - return _transceiver->mid().value(); - } - - return {}; + return _transceiver->mid(); } std::shared_ptr RTCRtpTransceiver::GetSender() { @@ -90,17 +87,13 @@ namespace python_webrtc { } std::optional RTCRtpTransceiver::GetCurrentDirection() { - if (_transceiver->current_direction()) { - return _transceiver->current_direction().value(); - } - - return {}; + return _transceiver->current_direction(); } void RTCRtpTransceiver::Stop() { std::shared_ptr connection; { - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); connection = _connection.lock(); } if (!connection || connection->IsClosed()) { diff --git a/python-webrtc/cpp/src/interfaces/rtc_rtp_transceiver.h b/python-webrtc/cpp/src/interfaces/rtc_rtp_transceiver.h index 1edffd0..be3c779 100644 --- a/python-webrtc/cpp/src/interfaces/rtc_rtp_transceiver.h +++ b/python-webrtc/cpp/src/interfaces/rtc_rtp_transceiver.h @@ -5,7 +5,8 @@ // that can be found in the LICENSE.md file in the root of the project. // -#pragma once +#ifndef PYTHON_WEBRTC_INTERFACES_RTC_RTP_TRANSCEIVER_H_ +#define PYTHON_WEBRTC_INTERFACES_RTC_RTP_TRANSCEIVER_H_ #include #include @@ -18,10 +19,10 @@ #include +#include "../enums/enums.h" #include "peer_connection_factory.h" #include "rtc_rtp_receiver.h" #include "rtc_rtp_sender.h" -#include "../enums/enums.h" namespace python_webrtc { @@ -29,7 +30,8 @@ namespace python_webrtc { class RTCRtpTransceiver { public: - RTCRtpTransceiver(std::shared_ptr, webrtc::scoped_refptr); + RTCRtpTransceiver(std::shared_ptr factory, + webrtc::scoped_refptr transceiver); static void Init(pybind11::module &m); @@ -52,7 +54,7 @@ namespace python_webrtc { webrtc::RtpTransceiverDirection GetDirection(); - void SetDirection(webrtc::RtpTransceiverDirection); + void SetDirection(webrtc::RtpTransceiverDirection direction); std::optional GetCurrentDirection(); @@ -82,3 +84,5 @@ namespace python_webrtc { }; } // namespace python_webrtc + +#endif // PYTHON_WEBRTC_INTERFACES_RTC_RTP_TRANSCEIVER_H_ diff --git a/python-webrtc/cpp/src/interfaces/rtc_sctp_transport.cpp b/python-webrtc/cpp/src/interfaces/rtc_sctp_transport.cpp index c445ec0..25f813c 100644 --- a/python-webrtc/cpp/src/interfaces/rtc_sctp_transport.cpp +++ b/python-webrtc/cpp/src/interfaces/rtc_sctp_transport.cpp @@ -11,11 +11,11 @@ namespace python_webrtc { - RTCSctpTransport::RTCSctpTransport( - std::shared_ptr factory, webrtc::scoped_refptr transport - ) : _factory(std::move(factory)), _transport(std::move(transport)) { + RTCSctpTransport::RTCSctpTransport(std::shared_ptr factory, + webrtc::scoped_refptr transport) + : _factory(std::move(factory)), _transport(std::move(transport)) { webrtc::scoped_refptr dtlsTransport; - _factory->_workerThread->BlockingCall([this, &dtlsTransport]() { + _factory->workerThread()->BlockingCall([this, &dtlsTransport]() { dtlsTransport = _transport->dtls_transport(); _transport->RegisterObserver(this); holder().SetObserver(_transport.get(), this); @@ -31,10 +31,10 @@ namespace python_webrtc { } RTCSctpTransport::~RTCSctpTransport() { - BlockingDestructor release("RTCSctpTransport"); + const BlockingDestructor release("RTCSctpTransport"); // callbacks run on the network thread, so after this none of them can be running or start again - _factory->_workerThread->BlockingCall([this]() { Stop(); }); + _factory->workerThread()->BlockingCall([this]() { Stop(); }); _dtlsTransport = nullptr; _transport = nullptr; @@ -52,7 +52,7 @@ namespace python_webrtc { InstanceHolder &RTCSctpTransport::holder() { // never destroyed: wrappers may outlive static destructors - static auto holder = new InstanceHolder(); + static auto *holder = new InstanceHolder(); return *holder; } @@ -87,7 +87,7 @@ namespace python_webrtc { webrtc::SctpTransportInformation RTCSctpTransport::Information() { // the information is owned by the network thread - return _factory->_workerThread->BlockingCall([this]() { return _transport->Information(); }); + return _factory->workerThread()->BlockingCall([this]() { return _transport->Information(); }); } webrtc::SctpTransportState RTCSctpTransport::GetState() { diff --git a/python-webrtc/cpp/src/interfaces/rtc_sctp_transport.h b/python-webrtc/cpp/src/interfaces/rtc_sctp_transport.h index d24fbc6..3779c6b 100644 --- a/python-webrtc/cpp/src/interfaces/rtc_sctp_transport.h +++ b/python-webrtc/cpp/src/interfaces/rtc_sctp_transport.h @@ -5,7 +5,8 @@ // that can be found in the LICENSE.md file in the root of the project. // -#pragma once +#ifndef PYTHON_WEBRTC_INTERFACES_RTC_SCTP_TRANSPORT_H_ +#define PYTHON_WEBRTC_INTERFACES_RTC_SCTP_TRANSPORT_H_ #include #include @@ -13,27 +14,31 @@ #include -#include "rtc_dtls_transport.h" +#include "../enums/enums.h" #include "../utils/listeners.h" #include "../utils/locked_function.h" #include "../utils/surfaced.h" -#include "../enums/enums.h" +#include "rtc_dtls_transport.h" namespace python_webrtc { class RTCSctpTransport : public webrtc::SctpTransportObserverInterface, public Listeners, public SingleObserverSlot { public: - explicit RTCSctpTransport(std::shared_ptr, webrtc::scoped_refptr); + explicit RTCSctpTransport(std::shared_ptr factory, + webrtc::scoped_refptr transport); ~RTCSctpTransport() override; + RTCSctpTransport(const RTCSctpTransport &) = delete; + RTCSctpTransport &operator=(const RTCSctpTransport &) = delete; + static void Init(pybind11::module &m); static InstanceHolder &holder(); webrtc::scoped_refptr transport() { return _transport; } - void OnStateChange(webrtc::SctpTransportInformation) override; + void OnStateChange(webrtc::SctpTransportInformation info) override; // a closed connection fires no events of its transports, which show their current state void OnPeerConnectionClosed(); @@ -72,3 +77,5 @@ namespace python_webrtc { }; } // namespace python_webrtc + +#endif // PYTHON_WEBRTC_INTERFACES_RTC_SCTP_TRANSPORT_H_ diff --git a/python-webrtc/cpp/src/interfaces/rtc_video_track_source.cpp b/python-webrtc/cpp/src/interfaces/rtc_video_track_source.cpp index f69506b..960c2f4 100644 --- a/python-webrtc/cpp/src/interfaces/rtc_video_track_source.cpp +++ b/python-webrtc/cpp/src/interfaces/rtc_video_track_source.cpp @@ -21,7 +21,7 @@ namespace python_webrtc { RTCVideoTrackSource::~RTCVideoTrackSource() { if (_control) { - std::lock_guard lock(_control->mutex); + const std::scoped_lock lock(_control->mutex); _control->camera = nullptr; } } @@ -31,8 +31,9 @@ namespace python_webrtc { _control = std::make_shared(); _control->camera = this; } - auto interval = std::chrono::microseconds(static_cast(1000000 / frameRate)); - std::lock_guard lock(_cameraMutex); + constexpr double usPerSecond = 1e6; + auto interval = std::chrono::microseconds(static_cast(usPerSecond / frameRate)); + const std::scoped_lock lock(_cameraMutex); // the previous camera stops first: one thread draws at a time _camera = nullptr; _cameraWidth = width; @@ -40,13 +41,12 @@ namespace python_webrtc { _cameraFrameRate = frameRate; _camera = std::make_unique(); // the thread never holds a reference to the source, so the source isn't destroyed on it - _camera->Start(interval, [this, width, height, frame = uint32_t(0)]() mutable { - DrawFrame(width, height, frame++); - }); + _camera->Start(interval, + [this, width, height, frame = uint32_t{0}]() mutable { DrawFrame(width, height, frame++); }); } bool RTCVideoTrackSource::IsCamera(int *width, int *height, double *frameRate) { - std::lock_guard lock(_cameraMutex); + const std::scoped_lock lock(_cameraMutex); if (!_camera) { return false; } @@ -60,23 +60,30 @@ namespace python_webrtc { // a gradient moving across the frame, with a moving block, so that encoders have work to do auto buffer = webrtc::I420Buffer::Create(width, height); for (int y = 0; y < height; ++y) { - auto row = buffer->MutableDataY() + y * buffer->StrideY(); + auto *row = buffer->MutableDataY() + (static_cast(y) * buffer->StrideY()); for (int x = 0; x < width; ++x) { - row[x] = static_cast(x + y + frame * 4); + row[x] = static_cast(x + y + (frame * 4)); } } - std::memset(buffer->MutableDataU(), static_cast(64 + frame % 128), buffer->StrideU() * ((height + 1) / 2)); - std::memset(buffer->MutableDataV(), static_cast(192 - frame % 128), buffer->StrideV() * ((height + 1) / 2)); - int block = std::min(width, height) / 4; - int left = static_cast((frame * 8) % std::max(1, width - block)); + // the colors drift through a range of the chroma planes, the block is white + constexpr uint32_t chromaRange = 128; + constexpr int uBase = 64; + constexpr int vBase = 192; + constexpr int white = 255; + constexpr uint32_t blockSpeed = 8; + std::memset(buffer->MutableDataU(), static_cast(uBase + (frame % chromaRange)), + static_cast(buffer->StrideU()) * ((height + 1) / 2)); + std::memset(buffer->MutableDataV(), static_cast(vBase - (frame % chromaRange)), + static_cast(buffer->StrideV()) * ((height + 1) / 2)); + const int block = std::min(width, height) / 4; + const int left = static_cast((frame * blockSpeed) % std::max(1, width - block)); for (int y = 0; y < block; ++y) { - std::memset(buffer->MutableDataY() + (height / 3 + y) * buffer->StrideY() + left, 255, block); + std::memset(buffer->MutableDataY() + (static_cast((height / 3) + y) * buffer->StrideY()) + left, + white, block); } - PushFrame(webrtc::VideoFrame::Builder() - .set_video_frame_buffer(buffer) - .set_timestamp_us(webrtc::TimeMicros()) - .build()); + PushFrame( + webrtc::VideoFrame::Builder().set_video_frame_buffer(buffer).set_timestamp_us(webrtc::TimeMicros()).build()); } void RTCVideoTrackSource::PushFrame(const webrtc::VideoFrame &frame) { diff --git a/python-webrtc/cpp/src/interfaces/rtc_video_track_source.h b/python-webrtc/cpp/src/interfaces/rtc_video_track_source.h index 2546abd..8ff970a 100644 --- a/python-webrtc/cpp/src/interfaces/rtc_video_track_source.h +++ b/python-webrtc/cpp/src/interfaces/rtc_video_track_source.h @@ -5,7 +5,8 @@ // that can be found in the LICENSE.md file in the root of the project. // -#pragma once +#ifndef PYTHON_WEBRTC_INTERFACES_RTC_VIDEO_TRACK_SOURCE_H_ +#define PYTHON_WEBRTC_INTERFACES_RTC_VIDEO_TRACK_SOURCE_H_ #include #include @@ -31,6 +32,9 @@ namespace python_webrtc { ~RTCVideoTrackSource() override; + RTCVideoTrackSource(const RTCVideoTrackSource &) = delete; + RTCVideoTrackSource &operator=(const RTCVideoTrackSource &) = delete; + // Starts the synthetic camera, or restarts it with another size and frame rate void StartCamera(int width, int height, double frameRate); @@ -89,3 +93,5 @@ namespace python_webrtc { }; } // namespace python_webrtc + +#endif // PYTHON_WEBRTC_INTERFACES_RTC_VIDEO_TRACK_SOURCE_H_ diff --git a/python-webrtc/cpp/src/interfaces/set_session_description_observer.h b/python-webrtc/cpp/src/interfaces/set_session_description_observer.h index 0cf0e18..507153c 100644 --- a/python-webrtc/cpp/src/interfaces/set_session_description_observer.h +++ b/python-webrtc/cpp/src/interfaces/set_session_description_observer.h @@ -5,7 +5,8 @@ // that can be found in the LICENSE.md file in the root of the project. // -#pragma once +#ifndef PYTHON_WEBRTC_INTERFACES_SET_SESSION_DESCRIPTION_OBSERVER_H_ +#define PYTHON_WEBRTC_INTERFACES_SET_SESSION_DESCRIPTION_OBSERVER_H_ #include #include @@ -40,3 +41,5 @@ namespace python_webrtc { }; } // namespace python_webrtc + +#endif // PYTHON_WEBRTC_INTERFACES_SET_SESSION_DESCRIPTION_OBSERVER_H_ diff --git a/python-webrtc/cpp/src/interfaces/stats_collector_callback.h b/python-webrtc/cpp/src/interfaces/stats_collector_callback.h index 7581c40..44b4a8e 100644 --- a/python-webrtc/cpp/src/interfaces/stats_collector_callback.h +++ b/python-webrtc/cpp/src/interfaces/stats_collector_callback.h @@ -5,7 +5,8 @@ // that can be found in the LICENSE.md file in the root of the project. // -#pragma once +#ifndef PYTHON_WEBRTC_INTERFACES_STATS_COLLECTOR_CALLBACK_H_ +#define PYTHON_WEBRTC_INTERFACES_STATS_COLLECTOR_CALLBACK_H_ #include #include @@ -29,8 +30,8 @@ namespace python_webrtc { return; } auto filtered = webrtc::RTCStatsReport::Create(report->timestamp()); - for (const auto &stats: *report) { - if (!_excluded.count(stats.type())) { + for (const auto &stats : *report) { + if (!_excluded.contains(stats.type())) { filtered->AddStats(stats.copy()); } } @@ -43,3 +44,5 @@ namespace python_webrtc { }; } // namespace python_webrtc + +#endif // PYTHON_WEBRTC_INTERFACES_STATS_COLLECTOR_CALLBACK_H_ diff --git a/python-webrtc/cpp/src/media/audio_samples.cpp b/python-webrtc/cpp/src/media/audio_samples.cpp index d0f0762..18d5c91 100644 --- a/python-webrtc/cpp/src/media/audio_samples.cpp +++ b/python-webrtc/cpp/src/media/audio_samples.cpp @@ -19,7 +19,7 @@ namespace python_webrtc { namespace { - enum class SampleType { U8, S16, S32, F32 }; + enum class SampleType : uint8_t { U8, S16, S32, F32 }; struct SampleFormat { SampleType type; @@ -29,100 +29,106 @@ namespace python_webrtc { constexpr std::string_view kPlanarSuffix = "-planar"; constexpr double kS16Scale = 32768.0; constexpr double kS32Scale = 2147483648.0; + // unsigned 8-bit samples are offset by half their range + constexpr int kU8Offset = 128; + constexpr double kU8Scale = 128.0; + // how far each type sits below s32 + constexpr int kU8Shift = 24; + constexpr int kS16Shift = 16; SampleFormat ParseFormat(const std::string &format) { std::string_view type = format; - bool planar = type.size() > kPlanarSuffix.size() && - type.substr(type.size() - kPlanarSuffix.size()) == kPlanarSuffix; + const bool planar = + type.size() > kPlanarSuffix.size() && type.substr(type.size() - kPlanarSuffix.size()) == kPlanarSuffix; if (planar) { type.remove_suffix(kPlanarSuffix.size()); } if (type == "u8") { - return {SampleType::U8, planar}; + return {.type = SampleType::U8, .planar = planar}; } if (type == "s16") { - return {SampleType::S16, planar}; + return {.type = SampleType::S16, .planar = planar}; } if (type == "s32") { - return {SampleType::S32, planar}; + return {.type = SampleType::S32, .planar = planar}; } if (type == "f32") { - return {SampleType::F32, planar}; + return {.type = SampleType::F32, .planar = planar}; } throw pybind11::value_error("Unsupported sample format " + format); } size_t Size(SampleType type) { switch (type) { - case SampleType::U8: - return sizeof(uint8_t); - case SampleType::S16: - return sizeof(int16_t); - default: - return sizeof(int32_t); + case SampleType::U8: + return sizeof(uint8_t); + case SampleType::S16: + return sizeof(int16_t); + default: + return sizeof(int32_t); } } // unaligned, as the buffers of Python may be - template - T Load(const uint8_t *p) { + template + T Load(const uint8_t *data) { T value; - std::memcpy(&value, p, sizeof(T)); + std::memcpy(&value, data, sizeof(T)); return value; } - template - void Store(uint8_t *p, T value) { - std::memcpy(p, &value, sizeof(T)); + template + void Store(uint8_t *data, T value) { + std::memcpy(data, &value, sizeof(T)); } // the sample as s32, the widest type, so integer conversions are exact shifts - int32_t ReadInt(const uint8_t *p, SampleType type) { + int32_t ReadInt(const uint8_t *data, SampleType type) { switch (type) { - case SampleType::U8: - return static_cast(static_cast(*p - 128) << 24); - case SampleType::S16: - return static_cast(static_cast(Load(p)) << 16); - case SampleType::S32: - return Load(p); - default: { - double scaled = std::clamp(static_cast(Load(p)), -1.0, 1.0) * kS32Scale; - return static_cast(std::clamp(scaled, -kS32Scale, kS32Scale - 1)); - } + case SampleType::U8: + return static_cast(static_cast(*data - kU8Offset) << kU8Shift); + case SampleType::S16: + return static_cast(static_cast(Load(data)) << kS16Shift); + case SampleType::S32: + return Load(data); + default: { + const double scaled = std::clamp(static_cast(Load(data)), -1.0, 1.0) * kS32Scale; + return static_cast(std::clamp(scaled, -kS32Scale, kS32Scale - 1)); + } } } - float ReadFloat(const uint8_t *p, SampleType type) { + float ReadFloat(const uint8_t *data, SampleType type) { switch (type) { - case SampleType::U8: - return static_cast((static_cast(*p) - 128) / 128.0); - case SampleType::S16: - return static_cast(Load(p) / kS16Scale); - case SampleType::S32: - return static_cast(Load(p) / kS32Scale); - default: - return Load(p); + case SampleType::U8: + return static_cast((static_cast(*data) - kU8Offset) / kU8Scale); + case SampleType::S16: + return static_cast(Load(data) / kS16Scale); + case SampleType::S32: + return static_cast(Load(data) / kS32Scale); + default: + return Load(data); } } - void Convert(const uint8_t *from, SampleType fromType, uint8_t *to, SampleType toType) { + void Convert(const uint8_t *from, SampleType fromType, uint8_t *target, SampleType toType) { if (fromType == toType) { - std::memcpy(to, from, Size(toType)); + std::memcpy(target, from, Size(toType)); return; } switch (toType) { - case SampleType::F32: - Store(to, ReadFloat(from, fromType)); - break; - case SampleType::S32: - Store(to, ReadInt(from, fromType)); - break; - case SampleType::S16: - Store(to, static_cast(ReadInt(from, fromType) >> 16)); - break; - case SampleType::U8: - *to = static_cast((ReadInt(from, fromType) >> 24) + 128); - break; + case SampleType::F32: + Store(target, ReadFloat(from, fromType)); + break; + case SampleType::S32: + Store(target, ReadInt(from, fromType)); + break; + case SampleType::S16: + Store(target, static_cast(ReadInt(from, fromType) >> kS16Shift)); + break; + case SampleType::U8: + *target = static_cast((ReadInt(from, fromType) >> kU8Shift) + kU8Offset); + break; } } @@ -136,18 +142,17 @@ namespace python_webrtc { } void AudioSamples::Copy(const pybind11::buffer &source, const std::string &sourceFormatName, size_t channels, - size_t frames, const pybind11::buffer &destination, - const std::string &destinationFormatName, size_t planeIndex, size_t frameOffset, - size_t frameCount) { + size_t frames, const pybind11::buffer &destination, const std::string &destinationFormatName, + size_t planeIndex, size_t frameOffset, size_t frameCount) { auto sourceFormat = ParseFormat(sourceFormatName); auto destinationFormat = ParseFormat(destinationFormatName); auto sourceInfo = ContiguousBuffer(source); auto destinationInfo = ContiguousBuffer(destination, true); auto sourceSize = static_cast(sourceInfo.size * sourceInfo.itemsize); auto destinationSize = static_cast(destinationInfo.size * destinationInfo.itemsize); - size_t sourceSample = Size(sourceFormat.type); - size_t destinationSample = Size(destinationFormat.type); - size_t copiedChannels = destinationFormat.planar ? 1 : channels; + const size_t sourceSample = Size(sourceFormat.type); + const size_t destinationSample = Size(destinationFormat.type); + const size_t copiedChannels = destinationFormat.planar ? 1 : channels; // divided rather than multiplied, which could overflow if (channels == 0 || frameOffset > frames || frameCount > frames - frameOffset || @@ -157,16 +162,16 @@ namespace python_webrtc { throw pybind11::value_error("The copy is out of the bounds of the samples or of the destination"); } - auto from = static_cast(sourceInfo.ptr); - auto to = static_cast(destinationInfo.ptr); - gil_release release; + const auto *from = static_cast(sourceInfo.ptr); + auto *target = static_cast(destinationInfo.ptr); + const gil_release release; for (size_t frame = 0; frame < frameCount; ++frame) { - for (size_t c = 0; c < copiedChannels; ++c) { - size_t channel = destinationFormat.planar ? planeIndex : c; - size_t sourceIndex = sourceFormat.planar ? channel * frames + frameOffset + frame - : (frameOffset + frame) * channels + channel; - size_t destinationIndex = frame * copiedChannels + c; - Convert(from + sourceIndex * sourceSample, sourceFormat.type, to + destinationIndex * destinationSample, + for (size_t copied = 0; copied < copiedChannels; ++copied) { + const size_t channel = destinationFormat.planar ? planeIndex : copied; + const size_t sourceIndex = sourceFormat.planar ? (channel * frames) + frameOffset + frame + : ((frameOffset + frame) * channels) + channel; + const size_t destinationIndex = (frame * copiedChannels) + copied; + Convert(from + (sourceIndex * sourceSample), sourceFormat.type, target + (destinationIndex * destinationSample), destinationFormat.type); } } diff --git a/python-webrtc/cpp/src/media/audio_samples.h b/python-webrtc/cpp/src/media/audio_samples.h index 060104a..6a87aea 100644 --- a/python-webrtc/cpp/src/media/audio_samples.h +++ b/python-webrtc/cpp/src/media/audio_samples.h @@ -5,7 +5,8 @@ // that can be found in the LICENSE.md file in the root of the project. // -#pragma once +#ifndef PYTHON_WEBRTC_MEDIA_AUDIO_SAMPLES_H_ +#define PYTHON_WEBRTC_MEDIA_AUDIO_SAMPLES_H_ #include #include @@ -26,3 +27,5 @@ namespace python_webrtc { }; } // namespace python_webrtc + +#endif // PYTHON_WEBRTC_MEDIA_AUDIO_SAMPLES_H_ diff --git a/python-webrtc/cpp/src/media/media.h b/python-webrtc/cpp/src/media/media.h index aee9943..226b0d5 100644 --- a/python-webrtc/cpp/src/media/media.h +++ b/python-webrtc/cpp/src/media/media.h @@ -5,7 +5,8 @@ // that can be found in the LICENSE.md file in the root of the project. // -#pragma once +#ifndef PYTHON_WEBRTC_MEDIA_MEDIA_H_ +#define PYTHON_WEBRTC_MEDIA_MEDIA_H_ #include @@ -18,3 +19,5 @@ namespace python_webrtc { }; } // namespace python_webrtc + +#endif // PYTHON_WEBRTC_MEDIA_MEDIA_H_ diff --git a/python-webrtc/cpp/src/media/media_stream_track_processor.cpp b/python-webrtc/cpp/src/media/media_stream_track_processor.cpp index ace47ae..d4574cc 100644 --- a/python-webrtc/cpp/src/media/media_stream_track_processor.cpp +++ b/python-webrtc/cpp/src/media/media_stream_track_processor.cpp @@ -12,21 +12,21 @@ #include +#include "../utils/buffer.h" #include "../utils/gil.h" #include "../utils/libwebrtc_thread.h" namespace python_webrtc { - MediaStreamTrackProcessor::MediaStreamTrackProcessor( - std::shared_ptr factory, webrtc::scoped_refptr track, - size_t maxBufferSize) - : _factory(std::move(factory)), - _track(std::move(track)), + MediaStreamTrackProcessor::MediaStreamTrackProcessor(std::shared_ptr factory, + webrtc::scoped_refptr track, + size_t maxBufferSize) + : _factory(std::move(factory)), _track(std::move(track)), _video(_track->kind() == webrtc::MediaStreamTrackInterface::kVideoKind), _maxBufferSize(std::max(1, maxBufferSize)) {} - std::shared_ptr MediaStreamTrackProcessor::Create(std::shared_ptr track, - size_t maxBufferSize) { + std::shared_ptr + MediaStreamTrackProcessor::Create(const std::shared_ptr &track, size_t maxBufferSize) { // Python keeps the track's wrapper, so the collector sees handlers of the track referencing the processor std::shared_ptr processor( new MediaStreamTrackProcessor(track->factory(), track->track(), maxBufferSize), DeleteOffLibwebrtcThread()); @@ -39,7 +39,7 @@ namespace python_webrtc { } MediaStreamTrackProcessor::~MediaStreamTrackProcessor() { - BlockingDestructor release("MediaStreamTrackProcessor"); + const BlockingDestructor release("MediaStreamTrackProcessor"); Detach(); DropListeners(); } @@ -57,7 +57,7 @@ namespace python_webrtc { } void MediaStreamTrackProcessor::Attach() { - std::lock_guard lock(_attachMutex); + const std::scoped_lock lock(_attachMutex); if (_attached) { return; } @@ -70,7 +70,7 @@ namespace python_webrtc { } void MediaStreamTrackProcessor::Detach() { - std::lock_guard lock(_attachMutex); + const std::scoped_lock lock(_attachMutex); if (!_attached) { return; } @@ -84,7 +84,7 @@ namespace python_webrtc { } void MediaStreamTrackProcessor::Push(Item item) { - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); if (_ended) { return; } @@ -105,8 +105,10 @@ namespace python_webrtc { } void MediaStreamTrackProcessor::OnFrame(const webrtc::VideoFrame &frame) { - Push(VideoItem{frame.video_frame_buffer(), frame.timestamp_us(), static_cast(frame.rotation()), - frame.rtp_timestamp()}); + Push(VideoItem{.buffer = frame.video_frame_buffer(), + .timestampUs = frame.timestamp_us(), + .rotation = static_cast(frame.rotation()), + .rtpTimestamp = frame.rtp_timestamp()}); } void MediaStreamTrackProcessor::OnData(const void *audioData, int bitsPerSample, int sampleRate, size_t channels, @@ -115,11 +117,16 @@ namespace python_webrtc { } void MediaStreamTrackProcessor::OnData(const void *audioData, int bitsPerSample, int sampleRate, size_t channels, - size_t frames, std::optional absoluteCaptureTimestampMs) { - size_t size = frames * channels * (bitsPerSample / 8); + size_t frames, std::optional /*absoluteCaptureTimestampMs*/) { + const size_t size = frames * channels * (bitsPerSample / 8); std::vector data(size); std::memcpy(data.data(), audioData, size); - Push(AudioItem{std::move(data), bitsPerSample, sampleRate, channels, frames, webrtc::TimeMicros()}); + Push(AudioItem{.data = std::move(data), + .bitsPerSample = bitsPerSample, + .sampleRate = sampleRate, + .channels = channels, + .frames = frames, + .timestampUs = webrtc::TimeMicros()}); } void MediaStreamTrackProcessor::OnWakeup() { @@ -127,7 +134,7 @@ namespace python_webrtc { } void MediaStreamTrackProcessor::OnTrackEnded() { - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); if (_ended) { return; } @@ -140,7 +147,7 @@ namespace python_webrtc { void MediaStreamTrackProcessor::Cancel() { { - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); _ended = true; _queue.clear(); } @@ -148,21 +155,21 @@ namespace python_webrtc { } void MediaStreamTrackProcessor::AckWakeup() { - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); _wakePending = false; } bool MediaStreamTrackProcessor::GetEnded() { - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); return _ended && _queue.empty(); } pybind11::object MediaStreamTrackProcessor::Read() { std::optional item; - bool ended; + bool ended = false; { - gil_release release; - std::lock_guard lock(_mutex); + const gil_release release; + const std::scoped_lock lock(_mutex); if (!_queue.empty()) { item = std::move(_queue.front()); _queue.pop_front(); @@ -171,22 +178,22 @@ namespace python_webrtc { } if (ended && !item) { // the sink is detached here rather than on the thread that ended the track - gil_release release; + const gil_release release; Detach(); } if (!item) { return pybind11::none(); } - if (auto video = std::get_if(&*item)) { + if (auto *video = std::get_if(&*item)) { std::shared_ptr buffer; { - gil_release release; + const gil_release release; buffer = VideoFrameBuffer::FromWebrtc(video->buffer); } return pybind11::make_tuple(buffer, video->timestampUs, video->rotation, video->rtpTimestamp); } auto &audio = std::get(*item); - pybind11::bytes data(reinterpret_cast(audio.data.data()), audio.data.size()); + const pybind11::bytes data = Bytes(audio.data.data(), audio.data.size()); return pybind11::make_tuple(data, audio.bitsPerSample, audio.sampleRate, audio.channels, audio.frames, audio.timestampUs); } diff --git a/python-webrtc/cpp/src/media/media_stream_track_processor.h b/python-webrtc/cpp/src/media/media_stream_track_processor.h index 882a01f..8734024 100644 --- a/python-webrtc/cpp/src/media/media_stream_track_processor.h +++ b/python-webrtc/cpp/src/media/media_stream_track_processor.h @@ -5,7 +5,8 @@ // that can be found in the LICENSE.md file in the root of the project. // -#pragma once +#ifndef PYTHON_WEBRTC_MEDIA_MEDIA_STREAM_TRACK_PROCESSOR_H_ +#define PYTHON_WEBRTC_MEDIA_MEDIA_STREAM_TRACK_PROCESSOR_H_ #include #include @@ -22,11 +23,11 @@ #include +#include "../interfaces/media_stream_track.h" #include "../utils/alive_count.h" +#include "../utils/listeners.h" #include "video_frame_buffer.h" #include "wakeup.h" -#include "../interfaces/media_stream_track.h" -#include "../utils/listeners.h" namespace python_webrtc { @@ -38,11 +39,14 @@ namespace python_webrtc { public webrtc::AudioTrackSinkInterface, public std::enable_shared_from_this { public: - static std::shared_ptr Create(std::shared_ptr track, + static std::shared_ptr Create(const std::shared_ptr &track, size_t maxBufferSize); ~MediaStreamTrackProcessor() override; + MediaStreamTrackProcessor(const MediaStreamTrackProcessor &) = delete; + MediaStreamTrackProcessor &operator=(const MediaStreamTrackProcessor &) = delete; + static void Init(pybind11::module &m); // the oldest media queued: (VideoFrameBuffer, timestampUs, rotation, rtpTimestamp) or @@ -129,3 +133,5 @@ namespace python_webrtc { }; } // namespace python_webrtc + +#endif // PYTHON_WEBRTC_MEDIA_MEDIA_STREAM_TRACK_PROCESSOR_H_ diff --git a/python-webrtc/cpp/src/media/playout_audio_device.cpp b/python-webrtc/cpp/src/media/playout_audio_device.cpp index 4b5b68d..68203f2 100644 --- a/python-webrtc/cpp/src/media/playout_audio_device.cpp +++ b/python-webrtc/cpp/src/media/playout_audio_device.cpp @@ -9,20 +9,22 @@ #include #include +#include #include namespace python_webrtc { namespace { - int32_t DeviceName(uint16_t index, char name[webrtc::kAdmMaxDeviceNameSize], char guid[webrtc::kAdmMaxGuidSize]) { + // the single device, without a guid + int32_t DeviceName(uint16_t index, std::span name, char *guid) { if (index != 0) { return -1; } - std::strncpy(name, "python-webrtc", webrtc::kAdmMaxDeviceNameSize - 1); - name[webrtc::kAdmMaxDeviceNameSize - 1] = '\0'; - if (guid) { - guid[0] = '\0'; + std::strncpy(name.data(), "python-webrtc", name.size() - 1); + name.back() = '\0'; + if (guid != nullptr) { + *guid = '\0'; } return 0; } @@ -30,7 +32,8 @@ namespace python_webrtc { } // namespace PlayoutAudioDevice::~PlayoutAudioDevice() { - StopPlayout(); + // this class's own, as in any destructor + PlayoutAudioDevice::StopPlayout(); } int32_t PlayoutAudioDevice::ActiveAudioLayer(AudioLayer *audioLayer) const { @@ -39,13 +42,13 @@ namespace python_webrtc { } int32_t PlayoutAudioDevice::RegisterAudioCallback(webrtc::AudioTransport *audioCallback) { - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); _transport = audioCallback; return 0; } int32_t PlayoutAudioDevice::Init() { - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); _initialized = true; return 0; } @@ -53,24 +56,22 @@ namespace python_webrtc { int32_t PlayoutAudioDevice::Terminate() { StopPlayout(); StopRecording(); - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); _initialized = false; return 0; } bool PlayoutAudioDevice::Initialized() const { - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); return _initialized; } - int32_t PlayoutAudioDevice::PlayoutDeviceName(uint16_t index, char name[webrtc::kAdmMaxDeviceNameSize], - char guid[webrtc::kAdmMaxGuidSize]) { - return DeviceName(index, name, guid); + int32_t PlayoutAudioDevice::PlayoutDeviceName(uint16_t index, char *name, char *guid) { + return DeviceName(index, std::span(name, webrtc::kAdmMaxDeviceNameSize), guid); } - int32_t PlayoutAudioDevice::RecordingDeviceName(uint16_t index, char name[webrtc::kAdmMaxDeviceNameSize], - char guid[webrtc::kAdmMaxGuidSize]) { - return DeviceName(index, name, guid); + int32_t PlayoutAudioDevice::RecordingDeviceName(uint16_t index, char *name, char *guid) { + return DeviceName(index, std::span(name, webrtc::kAdmMaxDeviceNameSize), guid); } int32_t PlayoutAudioDevice::PlayoutIsAvailable(bool *available) { @@ -79,13 +80,13 @@ namespace python_webrtc { } int32_t PlayoutAudioDevice::InitPlayout() { - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); _playoutInitialized = true; return 0; } bool PlayoutAudioDevice::PlayoutIsInitialized() const { - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); return _playoutInitialized; } @@ -95,25 +96,25 @@ namespace python_webrtc { } int32_t PlayoutAudioDevice::InitRecording() { - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); _recordingInitialized = true; return 0; } bool PlayoutAudioDevice::RecordingIsInitialized() const { - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); return _recordingInitialized; } int32_t PlayoutAudioDevice::StartPlayout() { - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); if (_playout) { return 0; } _playout = std::make_unique(); std::vector samples(kFrames * kChannels); _playout->Start(std::chrono::milliseconds(kPullIntervalMs), [this, samples = std::move(samples)]() mutable { - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); if (!_transport) { return; } @@ -129,7 +130,7 @@ namespace python_webrtc { int32_t PlayoutAudioDevice::StopPlayout() { std::unique_ptr playout; { - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); playout = std::move(_playout); _playoutInitialized = false; } @@ -139,25 +140,25 @@ namespace python_webrtc { } bool PlayoutAudioDevice::Playing() const { - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); return static_cast(_playout); } int32_t PlayoutAudioDevice::StartRecording() { - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); _recording = true; return 0; } int32_t PlayoutAudioDevice::StopRecording() { - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); _recording = false; _recordingInitialized = false; return 0; } bool PlayoutAudioDevice::Recording() const { - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); return _recording; } diff --git a/python-webrtc/cpp/src/media/playout_audio_device.h b/python-webrtc/cpp/src/media/playout_audio_device.h index b25850a..83c2b66 100644 --- a/python-webrtc/cpp/src/media/playout_audio_device.h +++ b/python-webrtc/cpp/src/media/playout_audio_device.h @@ -5,7 +5,8 @@ // that can be found in the LICENSE.md file in the root of the project. // -#pragma once +#ifndef PYTHON_WEBRTC_MEDIA_PLAYOUT_AUDIO_DEVICE_H_ +#define PYTHON_WEBRTC_MEDIA_PLAYOUT_AUDIO_DEVICE_H_ #include #include @@ -28,6 +29,11 @@ namespace python_webrtc { ~PlayoutAudioDevice() override; + PlayoutAudioDevice() = default; + + PlayoutAudioDevice(const PlayoutAudioDevice &) = delete; + PlayoutAudioDevice &operator=(const PlayoutAudioDevice &) = delete; + int32_t ActiveAudioLayer(AudioLayer *audioLayer) const override; int32_t RegisterAudioCallback(webrtc::AudioTransport *audioCallback) override; @@ -42,19 +48,18 @@ namespace python_webrtc { int16_t RecordingDevices() override { return 1; } - int32_t PlayoutDeviceName(uint16_t index, char name[webrtc::kAdmMaxDeviceNameSize], - char guid[webrtc::kAdmMaxGuidSize]) override; + // name and guid point to kAdmMaxDeviceNameSize and kAdmMaxGuidSize chars + int32_t PlayoutDeviceName(uint16_t index, char *name, char *guid) override; - int32_t RecordingDeviceName(uint16_t index, char name[webrtc::kAdmMaxDeviceNameSize], - char guid[webrtc::kAdmMaxGuidSize]) override; + int32_t RecordingDeviceName(uint16_t index, char *name, char *guid) override; - int32_t SetPlayoutDevice(uint16_t index) override { return 0; } + int32_t SetPlayoutDevice(uint16_t /*index*/) override { return 0; } - int32_t SetPlayoutDevice(WindowsDeviceType device) override { return 0; } + int32_t SetPlayoutDevice(WindowsDeviceType /*device*/) override { return 0; } - int32_t SetRecordingDevice(uint16_t index) override { return 0; } + int32_t SetRecordingDevice(uint16_t /*index*/) override { return 0; } - int32_t SetRecordingDevice(WindowsDeviceType device) override { return 0; } + int32_t SetRecordingDevice(WindowsDeviceType /*device*/) override { return 0; } int32_t PlayoutIsAvailable(bool *available) override; @@ -90,45 +95,45 @@ namespace python_webrtc { int32_t SpeakerVolumeIsAvailable(bool *available) override; - int32_t SetSpeakerVolume(uint32_t volume) override { return -1; } + int32_t SetSpeakerVolume(uint32_t /*volume*/) override { return -1; } - int32_t SpeakerVolume(uint32_t *volume) const override { return -1; } + int32_t SpeakerVolume(uint32_t * /*volume*/) const override { return -1; } - int32_t MaxSpeakerVolume(uint32_t *maxVolume) const override { return -1; } + int32_t MaxSpeakerVolume(uint32_t * /*maxVolume*/) const override { return -1; } - int32_t MinSpeakerVolume(uint32_t *minVolume) const override { return -1; } + int32_t MinSpeakerVolume(uint32_t * /*minVolume*/) const override { return -1; } int32_t MicrophoneVolumeIsAvailable(bool *available) override; - int32_t SetMicrophoneVolume(uint32_t volume) override { return -1; } + int32_t SetMicrophoneVolume(uint32_t /*volume*/) override { return -1; } - int32_t MicrophoneVolume(uint32_t *volume) const override { return -1; } + int32_t MicrophoneVolume(uint32_t * /*volume*/) const override { return -1; } - int32_t MaxMicrophoneVolume(uint32_t *maxVolume) const override { return -1; } + int32_t MaxMicrophoneVolume(uint32_t * /*maxVolume*/) const override { return -1; } - int32_t MinMicrophoneVolume(uint32_t *minVolume) const override { return -1; } + int32_t MinMicrophoneVolume(uint32_t * /*minVolume*/) const override { return -1; } int32_t SpeakerMuteIsAvailable(bool *available) override; - int32_t SetSpeakerMute(bool enable) override { return -1; } + int32_t SetSpeakerMute(bool /*enable*/) override { return -1; } - int32_t SpeakerMute(bool *enabled) const override { return -1; } + int32_t SpeakerMute(bool * /*enabled*/) const override { return -1; } int32_t MicrophoneMuteIsAvailable(bool *available) override; - int32_t SetMicrophoneMute(bool enable) override { return -1; } + int32_t SetMicrophoneMute(bool /*enable*/) override { return -1; } - int32_t MicrophoneMute(bool *enabled) const override { return -1; } + int32_t MicrophoneMute(bool * /*enabled*/) const override { return -1; } int32_t StereoPlayoutIsAvailable(bool *available) const override; - int32_t SetStereoPlayout(bool enable) override { return 0; } + int32_t SetStereoPlayout(bool /*enable*/) override { return 0; } int32_t StereoPlayout(bool *enabled) const override; int32_t StereoRecordingIsAvailable(bool *available) const override; - int32_t SetStereoRecording(bool enable) override { return 0; } + int32_t SetStereoRecording(bool /*enable*/) override { return 0; } int32_t StereoRecording(bool *enabled) const override; @@ -140,11 +145,11 @@ namespace python_webrtc { bool BuiltInNSIsAvailable() const override { return false; } - int32_t EnableBuiltInAEC(bool enable) override { return -1; } + int32_t EnableBuiltInAEC(bool /*enable*/) override { return -1; } - int32_t EnableBuiltInAGC(bool enable) override { return -1; } + int32_t EnableBuiltInAGC(bool /*enable*/) override { return -1; } - int32_t EnableBuiltInNS(bool enable) override { return -1; } + int32_t EnableBuiltInNS(bool /*enable*/) override { return -1; } private: mutable std::mutex _mutex; @@ -158,3 +163,5 @@ namespace python_webrtc { }; } // namespace python_webrtc + +#endif // PYTHON_WEBRTC_MEDIA_PLAYOUT_AUDIO_DEVICE_H_ diff --git a/python-webrtc/cpp/src/media/source_control.h b/python-webrtc/cpp/src/media/source_control.h index e0b7da8..ce2e733 100644 --- a/python-webrtc/cpp/src/media/source_control.h +++ b/python-webrtc/cpp/src/media/source_control.h @@ -5,7 +5,8 @@ // that can be found in the LICENSE.md file in the root of the project. // -#pragma once +#ifndef PYTHON_WEBRTC_MEDIA_SOURCE_CONTROL_H_ +#define PYTHON_WEBRTC_MEDIA_SOURCE_CONTROL_H_ #include #include @@ -29,7 +30,7 @@ namespace python_webrtc { static void Register(const webrtc::MediaStreamTrackInterface *track, const std::string &trackId, const std::shared_ptr &control) { - std::lock_guard lock(RegistryMutex()); + const std::scoped_lock lock(RegistryMutex()); auto ®istry = Registry(); for (auto it = registry.begin(); it != registry.end();) { it = it->second.expired() ? registry.erase(it) : std::next(it); @@ -39,7 +40,7 @@ namespace python_webrtc { static std::shared_ptr Find(const webrtc::MediaStreamTrackInterface *track, const std::string &trackId) { - std::lock_guard lock(RegistryMutex()); + const std::scoped_lock lock(RegistryMutex()); auto ®istry = Registry(); auto it = registry.find({track, trackId}); return it != registry.end() ? it->second.lock() : nullptr; @@ -52,14 +53,16 @@ namespace python_webrtc { static Map &Registry() { // never destroyed: wrappers may outlive static destructors - static auto registry = new Map(); + static auto *registry = new Map(); return *registry; } static std::mutex &RegistryMutex() { - static auto mutex = new std::mutex(); + static auto *mutex = new std::mutex(); return *mutex; } }; } // namespace python_webrtc + +#endif // PYTHON_WEBRTC_MEDIA_SOURCE_CONTROL_H_ diff --git a/python-webrtc/cpp/src/media/track_generator.cpp b/python-webrtc/cpp/src/media/track_generator.cpp index 94bb535..0ddbfa7 100644 --- a/python-webrtc/cpp/src/media/track_generator.cpp +++ b/python-webrtc/cpp/src/media/track_generator.cpp @@ -7,6 +7,7 @@ #include "track_generator.h" +#include #include #include #include @@ -27,6 +28,7 @@ namespace python_webrtc { // libwebrtc takes audio in 10 ms frames constexpr int kAudioFramesPerSecond = 100; + constexpr int kBitsPerSample = sizeof(int16_t) * CHAR_BIT; // libwebrtc's native rates: its resampler crashes on much lower ones constexpr int kMinSampleRate = 8000; constexpr int kMaxSampleRate = 384000; @@ -55,7 +57,7 @@ namespace python_webrtc { std::shared_ptr TrackGenerator::GetTrack() { // wrapped out of the lock: wrapping may wait for the signaling thread (the holder finds a live wrapper) auto wrapped = MediaStreamTrack::holder().GetOrCreate(_factory, _webrtcTrack); - std::lock_guard lock(_trackMutex); + const std::scoped_lock lock(_trackMutex); auto track = _track.lock(); if (!track) { track = std::move(wrapped); @@ -75,7 +77,7 @@ namespace python_webrtc { } std::shared_ptr TrackGenerator::Create(const std::string &kind) { - return std::shared_ptr(new TrackGenerator(kind), DeleteOffLibwebrtcThread()); + return {new TrackGenerator(kind), DeleteOffLibwebrtcThread()}; } void TrackGenerator::Init(pybind11::module &m) { @@ -85,8 +87,8 @@ namespace python_webrtc { .def_property_readonly("kind", &TrackGenerator::GetKind) .def_property_readonly("live", nogil_fn(&TrackGenerator::GetLive)) .def_property("muted", nogil_fn(&TrackGenerator::GetMuted), nogil_fn(&TrackGenerator::SetMuted)) - .def("writeVideo", &TrackGenerator::WriteVideo, nogil(), pybind11::arg("buffer"), - pybind11::arg("timestampUs"), pybind11::arg("rotation")) + .def("writeVideo", &TrackGenerator::WriteVideo, nogil(), pybind11::arg("buffer"), pybind11::arg("timestampUs"), + pybind11::arg("rotation")) .def("writeAudio", &TrackGenerator::WriteAudio, pybind11::arg("samples"), pybind11::arg("sampleRate"), pybind11::arg("channels"), pybind11::arg("frames")) .def("close", &TrackGenerator::Close, nogil()); @@ -97,8 +99,7 @@ namespace python_webrtc { return false; } auto track = _track.lock(); - return track ? track->active() - : _webrtcTrack->state() == webrtc::MediaStreamTrackInterface::TrackState::kLive; + return track ? track->active() : _webrtcTrack->state() == webrtc::MediaStreamTrackInterface::TrackState::kLive; } bool TrackGenerator::GetMuted() { @@ -112,8 +113,7 @@ namespace python_webrtc { } } - void TrackGenerator::WriteVideo(const std::shared_ptr &buffer, int64_t timestampUs, - int rotation) { + void TrackGenerator::WriteVideo(const std::shared_ptr &buffer, int64_t timestampUs, int rotation) { if (!_video) { throw pybind11::type_error("An audio generator takes AudioData"); } @@ -138,36 +138,35 @@ namespace python_webrtc { if (sampleRate < kMinSampleRate || sampleRate > kMaxSampleRate || channels == 0 || channels > webrtc::kMaxNumberOfAudioChannels || static_cast(sampleRate / kAudioFramesPerSecond) * channels > webrtc::AudioFrame::kMaxDataSizeSamples) { - throw pybind11::value_error("Audio of " + std::to_string(channels) + " channels at " + - std::to_string(sampleRate) + " Hz isn't supported: up to " + - std::to_string(webrtc::kMaxNumberOfAudioChannels) + " channels from " + - std::to_string(kMinSampleRate) + " to " + std::to_string(kMaxSampleRate) + - " Hz, and " + std::to_string(webrtc::AudioFrame::kMaxDataSizeSamples) + - " samples every 10 ms"); + throw pybind11::value_error( + "Audio of " + std::to_string(channels) + " channels at " + std::to_string(sampleRate) + + " Hz isn't supported: up to " + std::to_string(webrtc::kMaxNumberOfAudioChannels) + " channels from " + + std::to_string(kMinSampleRate) + " to " + std::to_string(kMaxSampleRate) + " Hz, and " + + std::to_string(webrtc::AudioFrame::kMaxDataSizeSamples) + " samples every 10 ms"); } - std::string_view data = samples; + const std::string_view data = samples; if (frames > data.size() || data.size() != frames * channels * sizeof(int16_t)) { throw pybind11::value_error("The samples don't have the given number of frames"); } - gil_release release; + const gil_release release; if (!GetLive() || _muted) { return; } - std::lock_guard lock(_audioMutex); + const std::scoped_lock lock(_audioMutex); if (sampleRate != _pendingRate || channels != _pendingChannels) { _pending.clear(); _pendingRate = sampleRate; _pendingChannels = channels; } - size_t offset = _pending.size(); - _pending.resize(offset + frames * channels); + const size_t offset = _pending.size(); + _pending.resize(offset + (frames * channels)); std::memcpy(_pending.data() + offset, data.data(), data.size()); - size_t chunkFrames = static_cast(sampleRate / kAudioFramesPerSecond); - size_t chunk = chunkFrames * channels; + const auto chunkFrames = static_cast(sampleRate / kAudioFramesPerSecond); + const size_t chunk = chunkFrames * channels; size_t sent = 0; while (chunk > 0 && _pending.size() - sent >= chunk) { - _audioSource->PushSamples(_pending.data() + sent, sizeof(int16_t) * 8, sampleRate, channels, chunkFrames); + _audioSource->PushSamples(_pending.data() + sent, kBitsPerSample, sampleRate, channels, chunkFrames); sent += chunk; } _pending.erase(_pending.begin(), _pending.begin() + static_cast(sent)); @@ -175,7 +174,7 @@ namespace python_webrtc { void TrackGenerator::Close() { // the tracks observe their source on the signaling thread - _factory->_signalingThread->BlockingCall([this]() { + _factory->signalingThread()->BlockingCall([this]() { if (_video) { _videoSource->End(); } else { diff --git a/python-webrtc/cpp/src/media/track_generator.h b/python-webrtc/cpp/src/media/track_generator.h index dcc7ac9..3966153 100644 --- a/python-webrtc/cpp/src/media/track_generator.h +++ b/python-webrtc/cpp/src/media/track_generator.h @@ -5,7 +5,8 @@ // that can be found in the LICENSE.md file in the root of the project. // -#pragma once +#ifndef PYTHON_WEBRTC_MEDIA_TRACK_GENERATOR_H_ +#define PYTHON_WEBRTC_MEDIA_TRACK_GENERATOR_H_ #include #include @@ -18,12 +19,12 @@ #include -#include "../utils/alive_count.h" -#include "video_frame_buffer.h" #include "../interfaces/media_stream_track.h" #include "../interfaces/peer_connection_factory.h" #include "../interfaces/rtc_audio_track_source.h" #include "../interfaces/rtc_video_track_source.h" +#include "../utils/alive_count.h" +#include "video_frame_buffer.h" namespace python_webrtc { @@ -40,7 +41,7 @@ namespace python_webrtc { // the wrapper of the track, owned by Python once it has it std::shared_ptr GetTrack(); - std::string GetKind() { return _video ? "video" : "audio"; } + std::string GetKind() const { return _video ? "video" : "audio"; } // whether the track can still take frames: it ends with the generator or with stop() bool GetLive(); @@ -62,9 +63,7 @@ namespace python_webrtc { struct EndState : TrackEndObserver { std::atomic ended = false; - void OnTrackEnded() override { - ended = true; - } + void OnTrackEnded() override { ended = true; } }; AliveCount _counted; @@ -88,3 +87,5 @@ namespace python_webrtc { }; } // namespace python_webrtc + +#endif // PYTHON_WEBRTC_MEDIA_TRACK_GENERATOR_H_ diff --git a/python-webrtc/cpp/src/media/track_monitor.h b/python-webrtc/cpp/src/media/track_monitor.h index 8d44a1d..d25e9d0 100644 --- a/python-webrtc/cpp/src/media/track_monitor.h +++ b/python-webrtc/cpp/src/media/track_monitor.h @@ -5,7 +5,8 @@ // that can be found in the LICENSE.md file in the root of the project. // -#pragma once +#ifndef PYTHON_WEBRTC_MEDIA_TRACK_MONITOR_H_ +#define PYTHON_WEBRTC_MEDIA_TRACK_MONITOR_H_ #include #include @@ -38,46 +39,49 @@ namespace python_webrtc { void OnFrame(const webrtc::VideoFrame &frame) override { auto now = webrtc::TimeMicros(); - std::lock_guard lock(_mutex); + const std::scoped_lock lock(_mutex); if (_video && (_video->width != frame.width() || _video->height != frame.height())) { // the rate is measured from the new size on, as the source changed _count = 0; } - _video = Video{frame.width(), frame.height(), std::nullopt}; + _video = Video{.width = frame.width(), .height = frame.height(), .frameRate = std::nullopt}; _times[_count % _times.size()] = now; _count++; } - void OnData(const void *audioData, int bitsPerSample, int sampleRate, size_t channels, size_t frames) override { - std::lock_guard lock(_mutex); - _audio = Audio{sampleRate, static_cast(channels), bitsPerSample}; + // NOLINTNEXTLINE(bugprone-easily-swappable-parameters): libwebrtc's signature + void OnData(const void * /*audioData*/, int bitsPerSample, int sampleRate, size_t channels, + size_t /*frames*/) override { + const std::scoped_lock lock(_mutex); + _audio = Audio{.sampleRate = sampleRate, .channels = static_cast(channels), .bitsPerSample = bitsPerSample}; } void OnData(const void *audioData, int bitsPerSample, int sampleRate, size_t channels, size_t frames, - std::optional absoluteCaptureTimestampMs) override { + std::optional /*absoluteCaptureTimestampMs*/) override { OnData(audioData, bitsPerSample, sampleRate, channels, frames); } std::optional