diff --git a/CMakeLists.txt b/CMakeLists.txt index 15b221f16..4240b3cdb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,555 +1,586 @@ -cmake_minimum_required(VERSION 3.15) -cmake_policy(SET CMP0091 NEW) -cmake_policy(SET CMP0042 NEW) - -macro(assign_bool var) - if (${ARGN}) - set(${var} ON) - else() - set(${var} OFF) - endif() -endmacro() - -assign_bool(use_custom_libcxx "TRUE") -assign_bool(is_arm32 "y$ENV{TARGET_ARCH}" STREQUAL "yarm") -assign_bool(is_arm64 "y$ENV{TARGET_ARCH}" STREQUAL "yarm64") - -if (APPLE AND ${is_arm64}) - set(CMAKE_OSX_ARCHITECTURES "arm64" CACHE INTERNAL "" FORCE) -endif() - -if (WIN32 AND ${use_custom_libcxx}) - set(CMAKE_C_COMPILER "clang-cl" CACHE STRING "" FORCE) - set(CMAKE_CXX_COMPILER "clang-cl" CACHE STRING "" FORCE) -endif() - -set(MODULE wrtc) -project(${MODULE}) - -include(ExternalProject) -find_package(Git REQUIRED) -find_package(Threads REQUIRED) - -# depot_tools -# ----------------------------------------------------------------------------- - -ExternalProject_Add( - project_depot_tools - - GIT_REPOSITORY https://chromium.googlesource.com/chromium/tools/depot_tools.git - GIT_TAG 4ce8ba39a3488397a2d1494f167020f21de502f3 - - PREFIX ${CMAKE_BINARY_DIR}/external/depot_tools/prefix - TMP_DIR ${CMAKE_BINARY_DIR}/external/depot_tools/tmp - STAMP_DIR ${CMAKE_BINARY_DIR}/external/depot_tools/stamp - DOWNLOAD_DIR ${CMAKE_BINARY_DIR}/external/depot_tools/download - SOURCE_DIR ${CMAKE_BINARY_DIR}/external/depot_tools/src - BINARY_DIR ${CMAKE_BINARY_DIR}/external/depot_tools/build - - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND "" -) - -ExternalProject_Get_Property(project_depot_tools SOURCE_DIR) -set(depot_tools_install_dir ${SOURCE_DIR}) - -# libc++ -# ----------------------------------------------------------------------------- - -set(libwebrtc_binary_dir ${CMAKE_BINARY_DIR}/external/libwebrtc/build/${CMAKE_BUILD_TYPE}) -set(libwebrtc_src_dir ${CMAKE_BINARY_DIR}/external/libwebrtc/download/src) - -add_library(libc++ OBJECT IMPORTED) -add_dependencies(libc++ libwebrtc) - -if(WIN32) - set(obj_ext .obj) +cmake_minimum_required(VERSION 3.15) +cmake_policy(SET CMP0091 NEW) +cmake_policy(SET CMP0042 NEW) + +macro(assign_bool var) + if (${ARGN}) + set(${var} ON) + else() + set(${var} OFF) + endif() +endmacro() + +if (WIN32) + assign_bool(use_custom_libcxx "FALSE") else() - set(obj_ext .o) + assign_bool(use_custom_libcxx "TRUE") endif() - +assign_bool(is_arm32 "y$ENV{TARGET_ARCH}" STREQUAL "yarm") +assign_bool(is_arm64 "y$ENV{TARGET_ARCH}" STREQUAL "yarm64") + +if (APPLE AND ${is_arm64}) + set(CMAKE_OSX_ARCHITECTURES "arm64" CACHE INTERNAL "" FORCE) +endif() + +if (WIN32 AND ${use_custom_libcxx}) + set(CMAKE_C_COMPILER "clang-cl" CACHE STRING "" FORCE) + set(CMAKE_CXX_COMPILER "clang-cl" CACHE STRING "" FORCE) +endif() + +set(MODULE wrtc) +project(${MODULE}) + +include(ExternalProject) +find_package(Git REQUIRED) +find_package(Threads REQUIRED) + +# depot_tools +# ----------------------------------------------------------------------------- + +ExternalProject_Add( + project_depot_tools + + GIT_REPOSITORY https://chromium.googlesource.com/chromium/tools/depot_tools.git + GIT_TAG 4ce8ba39a3488397a2d1494f167020f21de502f3 + + PREFIX ${CMAKE_BINARY_DIR}/external/depot_tools/prefix + TMP_DIR ${CMAKE_BINARY_DIR}/external/depot_tools/tmp + STAMP_DIR ${CMAKE_BINARY_DIR}/external/depot_tools/stamp + DOWNLOAD_DIR ${CMAKE_BINARY_DIR}/external/depot_tools/download + SOURCE_DIR ${CMAKE_BINARY_DIR}/external/depot_tools/src + BINARY_DIR ${CMAKE_BINARY_DIR}/external/depot_tools/build + + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" +) + +ExternalProject_Get_Property(project_depot_tools SOURCE_DIR) +set(depot_tools_install_dir ${SOURCE_DIR}) + +# libc++ +# ----------------------------------------------------------------------------- + +set(libwebrtc_binary_dir ${CMAKE_BINARY_DIR}/external/libwebrtc/build/${CMAKE_BUILD_TYPE}) +set(libwebrtc_src_dir ${CMAKE_BINARY_DIR}/external/libwebrtc/download/src) + +add_library(libc++ OBJECT IMPORTED) +add_dependencies(libc++ libwebrtc) + +if(WIN32) + set(obj_ext .obj) +else() + set(obj_ext .o) +endif() + set(libc++_objects algorithm${obj_ext} any${obj_ext} atomic${obj_ext} barrier${obj_ext} bind${obj_ext} + call_once${obj_ext} charconv${obj_ext} chrono${obj_ext} condition_variable${obj_ext} condition_variable_destructor${obj_ext} d2fixed${obj_ext} d2s${obj_ext} + directory_iterator${obj_ext} + error_category${obj_ext} exception${obj_ext} f2s${obj_ext} - format${obj_ext} + filesystem_error${obj_ext} functional${obj_ext} future${obj_ext} hash${obj_ext} ios.instantiations${obj_ext} ios${obj_ext} - iostream${obj_ext} + iostream${obj_ext} locale${obj_ext} memory${obj_ext} mutex${obj_ext} mutex_destructor${obj_ext} new${obj_ext} - optional${obj_ext} + new_handler${obj_ext} + new_helpers${obj_ext} + operations${obj_ext} + path${obj_ext} random${obj_ext} random_shuffle${obj_ext} regex${obj_ext} shared_mutex${obj_ext} stdexcept${obj_ext} - string${obj_ext} + string${obj_ext} strstream${obj_ext} system_error${obj_ext} thread${obj_ext} typeinfo${obj_ext} - utility${obj_ext} valarray${obj_ext} - variant${obj_ext} vector${obj_ext} verbose_abort${obj_ext} ) if(WIN32) list(APPEND libc++_objects - legacy_debug_handler${obj_ext} - legacy_pointer_safety${obj_ext} locale_win32${obj_ext} support${obj_ext} thread_win32${obj_ext} - verbose_abort${obj_ext} ) endif() - -list(TRANSFORM libc++_objects PREPEND ${libwebrtc_binary_dir}/obj/buildtools/third_party/libc++/libc++/) - -set_property(TARGET libc++ APPEND PROPERTY IMPORTED_CONFIGURATIONS DEBUG) -set_target_properties(libc++ PROPERTIES IMPORTED_OBJECTS_DEBUG "${libc++_objects}" IMPORTED_OBJECTS "${libc++_objects}") - -# NOTE(mroberts): I would like this to be INTERFACE. -# -# https://gitlab.kitware.com/cmake/cmake/issues/15052 -# -# target_include_directories(libc++ SYSTEM INTERFACE "${libc++_include_dir}") - -# libc++abi -# ----------------------------------------------------------------------------- - -add_library(libc++abi OBJECT IMPORTED) -add_dependencies(libc++abi libwebrtc) - -set(libc++abi_objects - abort_message.o - cxa_aux_runtime.o - cxa_default_handlers.o - cxa_demangle.o - cxa_exception.o - cxa_exception_storage.o - cxa_guard.o - cxa_handlers.o - cxa_personality.o - cxa_vector.o - cxa_virtual.o - fallback_malloc.o - private_typeinfo.o - stdlib_exception.o - stdlib_stdexcept.o - stdlib_typeinfo.o -) -list(TRANSFORM libc++abi_objects PREPEND ${libwebrtc_binary_dir}/obj/buildtools/third_party/libc++abi/libc++abi/) - -set_property(TARGET libc++abi APPEND PROPERTY IMPORTED_CONFIGURATIONS DEBUG) -set_target_properties(libc++abi PROPERTIES IMPORTED_OBJECTS_DEBUG "${libc++abi_objects}" IMPORTED_OBJECTS "${libc++abi_objects}") - -# NOTE(mroberts): I would like this to be INTERFACE. -# -# https://gitlab.kitware.com/cmake/cmake/issues/15052 -# -# target_include_directories(libc++abi SYSTEM INTERFACE "${libc++abi_include_dir}") - -# libwebrtc -# ----------------------------------------------------------------------------- - -# https://chromiumdash.appspot.com/branches -# M87: branch-heads/4280 -# M91: branch-heads/4472 -# M92: branch-heads/4515 -# M94: branch-heads/4606 -# M98: branch-heads/4758 + +list(TRANSFORM libc++_objects PREPEND ${libwebrtc_binary_dir}/obj/buildtools/third_party/libc++/libc++/) + +set_property(TARGET libc++ APPEND PROPERTY IMPORTED_CONFIGURATIONS DEBUG) +set_target_properties(libc++ PROPERTIES IMPORTED_OBJECTS_DEBUG "${libc++_objects}" IMPORTED_OBJECTS "${libc++_objects}") + +# NOTE(mroberts): I would like this to be INTERFACE. +# +# https://gitlab.kitware.com/cmake/cmake/issues/15052 +# +# target_include_directories(libc++ SYSTEM INTERFACE "${libc++_include_dir}") + +# libc++abi +# ----------------------------------------------------------------------------- + +add_library(libc++abi OBJECT IMPORTED) +add_dependencies(libc++abi libwebrtc) + +set(libc++abi_objects + abort_message.o + cxa_aux_runtime.o + cxa_default_handlers.o + cxa_demangle.o + cxa_exception.o + cxa_exception_storage.o + cxa_guard.o + cxa_handlers.o + cxa_personality.o + cxa_vector.o + cxa_virtual.o + fallback_malloc.o + private_typeinfo.o + stdlib_exception.o + stdlib_stdexcept.o + stdlib_typeinfo.o +) +list(TRANSFORM libc++abi_objects PREPEND ${libwebrtc_binary_dir}/obj/buildtools/third_party/libc++abi/libc++abi/) + +set_property(TARGET libc++abi APPEND PROPERTY IMPORTED_CONFIGURATIONS DEBUG) +set_target_properties(libc++abi PROPERTIES IMPORTED_OBJECTS_DEBUG "${libc++abi_objects}" IMPORTED_OBJECTS "${libc++abi_objects}") + +# NOTE(mroberts): I would like this to be INTERFACE. +# +# https://gitlab.kitware.com/cmake/cmake/issues/15052 +# +# target_include_directories(libc++abi SYSTEM INTERFACE "${libc++abi_include_dir}") + +# libwebrtc +# ----------------------------------------------------------------------------- + +# https://chromiumdash.appspot.com/branches +# M87: branch-heads/4280 +# M91: branch-heads/4472 +# M92: branch-heads/4515 +# M94: branch-heads/4606 +# M98: branch-heads/4758 # M102: branch-heads/5005 # M106: branch-heads/5249 # M110: branch-heads/5481 # M114: branch-heads/5735 -set(WEBRTC_REVISION branch-heads/5735) - -file(STRINGS nix.gni NIX_GN_GEN_ARGS) -list(APPEND GN_GEN_ARGS - rtc_build_examples=false - rtc_use_x11=false - rtc_enable_protobuf=false - rtc_include_pulse_audio=false - rtc_include_tests=false - use_lld=false - "${NIX_GN_GEN_ARGS}" -) - -if (WIN32) - # The escaping is very strange here - list(APPEND GN_GEN_ARGS - "target_cpu=\\\"$ENV{TARGET_ARCH}\\\"" - ) -else() - list(APPEND GN_GEN_ARGS - "target_cpu=\"$ENV{TARGET_ARCH}\"" - ) -endif() - -if (${use_custom_libcxx}) - list(APPEND GN_GEN_ARGS - use_custom_libcxx=true - ) -else() - list(APPEND GN_GEN_ARGS - use_custom_libcxx=false - ) -endif() - -if (${is_arm32} OR ${is_arm64}) - list(APPEND GN_GEN_ARGS - rtc_build_tools=true - ) -else() - list(APPEND GN_GEN_ARGS - rtc_build_tools=false - ) -endif() - -if (CMAKE_BUILD_TYPE STREQUAL "Debug") - list(APPEND GN_GEN_ARGS is_debug=true) -else() - list(APPEND GN_GEN_ARGS is_debug=false) -endif() -string(REPLACE ";" " " GN_GEN_ARGS "${GN_GEN_ARGS}") - -if(WIN32) - set(suffix bat) - set(PLATFORM windows) -else() - set(suffix sh) - if(APPLE) - set(PLATFORM darwin) - else() - set(PLATFORM linux) - endif() +set(DEFAULT_WEBRTC_REVISION branch-heads/5735) +if(DEFINED ENV{WEBRTC_REVISION} AND NOT "$ENV{WEBRTC_REVISION}" STREQUAL "") + set(DEFAULT_WEBRTC_REVISION "$ENV{WEBRTC_REVISION}") endif() - -if (WIN32) +set(WEBRTC_REVISION "${DEFAULT_WEBRTC_REVISION}" CACHE STRING "libwebrtc branch/tag/ref to fetch") +message(STATUS "Using WEBRTC_REVISION=${WEBRTC_REVISION}") + +file(STRINGS nix.gni NIX_GN_GEN_ARGS) +list(APPEND GN_GEN_ARGS + rtc_build_examples=false + rtc_use_x11=false + rtc_enable_protobuf=false + rtc_include_pulse_audio=false + rtc_include_tests=false + use_lld=false + "${NIX_GN_GEN_ARGS}" +) + +if (WIN32) + # The escaping is very strange here + list(APPEND GN_GEN_ARGS + "target_cpu=\\\"$ENV{TARGET_ARCH}\\\"" + ) +else() + list(APPEND GN_GEN_ARGS + "target_cpu=\"$ENV{TARGET_ARCH}\"" + ) +endif() + +if (${use_custom_libcxx}) + list(APPEND GN_GEN_ARGS + use_custom_libcxx=true + ) +else() + list(APPEND GN_GEN_ARGS + use_custom_libcxx=false + ) +endif() + +if (${is_arm32} OR ${is_arm64}) + list(APPEND GN_GEN_ARGS + rtc_build_tools=true + ) +else() + list(APPEND GN_GEN_ARGS + rtc_build_tools=false + ) +endif() + +if (CMAKE_BUILD_TYPE STREQUAL "Debug") + list(APPEND GN_GEN_ARGS is_debug=true) +else() + list(APPEND GN_GEN_ARGS is_debug=false) +endif() +string(REPLACE ";" " " GN_GEN_ARGS "${GN_GEN_ARGS}") + +if(WIN32) + set(suffix bat) + set(PLATFORM windows) +else() + set(suffix sh) + if(APPLE) + set(PLATFORM darwin) + else() + set(PLATFORM linux) + endif() +endif() + +if (WIN32 AND ${use_custom_libcxx}) set(byproducts ${libc++_objects} ${libwebrtc_binary_dir}/obj/webrtc.lib ${libwebrtc_binary_dir}/obj/api/libjingle_peerconnection_api.lib ) +elseif(WIN32) + set(byproducts + ${libwebrtc_binary_dir}/obj/webrtc.lib + ${libwebrtc_binary_dir}/obj/api/create_peerconnection_factory.lib + ) else() set(byproducts ${libc++_objects} ${libc++abi_objects} - ${libwebrtc_binary_dir}/obj/libwebrtc.a - ${libwebrtc_binary_dir}/obj/api/libjingle_peerconnection_api.a - ${libwebrtc_binary_dir}/obj/api/video_codecs/libbuiltin_video_encoder_factory.a - ${libwebrtc_binary_dir}/obj/api/video_codecs/libbuiltin_video_decoder_factory.a - ${libwebrtc_binary_dir}/obj/media/librtc_internal_video_codecs.a - ) -endif() - -ExternalProject_Add( - project_libwebrtc - - PREFIX ${CMAKE_BINARY_DIR}/external/libwebrtc/prefix - TMP_DIR ${CMAKE_BINARY_DIR}/external/libwebrtc/tmp - STAMP_DIR ${CMAKE_BINARY_DIR}/external/libwebrtc/stamp - DOWNLOAD_DIR ${CMAKE_BINARY_DIR}/external/libwebrtc/download - SOURCE_DIR ${CMAKE_BINARY_DIR}/external/libwebrtc/download/src - BINARY_DIR ${CMAKE_BINARY_DIR}/external/libwebrtc/build/${CMAKE_BUILD_TYPE} - - BUILD_BYPRODUCTS ${byproducts} - - DOWNLOAD_COMMAND ${CMAKE_COMMAND} -E env DEPOT_TOOLS=${depot_tools_install_dir} PLATFORM=${PLATFORM} WEBRTC_REVISION=${WEBRTC_REVISION} ${CMAKE_SOURCE_DIR}/scripts/download-webrtc.${suffix} - CONFIGURE_COMMAND ${CMAKE_COMMAND} -E env BINARY_DIR= DEPOT_TOOLS=${depot_tools_install_dir} GN_GEN_ARGS=${GN_GEN_ARGS} SOURCE_DIR= ${CMAKE_SOURCE_DIR}/scripts/configure-webrtc.${suffix} - BUILD_COMMAND ${CMAKE_COMMAND} -E env CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} DEPOT_TOOLS=${depot_tools_install_dir} ${CMAKE_SOURCE_DIR}/scripts/build-webrtc.${suffix} - INSTALL_COMMAND "" -) - -add_dependencies(project_libwebrtc project_depot_tools) - -ExternalProject_Get_Property(project_libwebrtc DOWNLOAD_DIR) -set(libwebrtc_source_dir "${DOWNLOAD_DIR}") - -ExternalProject_Get_Property(project_libwebrtc BINARY_DIR) -set(libwebrtc_binary_dir "${BINARY_DIR}") - -add_library(libwebrtc STATIC IMPORTED) -add_dependencies(libwebrtc project_libwebrtc) - -if(WIN32) - set_property(TARGET libwebrtc PROPERTY IMPORTED_LOCATION "${libwebrtc_binary_dir}/obj/webrtc.lib") -else() - set_property(TARGET libwebrtc PROPERTY IMPORTED_LOCATION "${libwebrtc_binary_dir}/obj/libwebrtc.a") -endif() - -add_library(libpeerconnection STATIC IMPORTED) -add_dependencies(libpeerconnection project_libwebrtc) - + ${libwebrtc_binary_dir}/obj/libwebrtc.a + ${libwebrtc_binary_dir}/obj/api/libjingle_peerconnection_api.a + ${libwebrtc_binary_dir}/obj/api/video_codecs/libbuiltin_video_encoder_factory.a + ${libwebrtc_binary_dir}/obj/api/video_codecs/libbuiltin_video_decoder_factory.a + ${libwebrtc_binary_dir}/obj/media/librtc_internal_video_codecs.a + ) +endif() + +ExternalProject_Add( + project_libwebrtc + + PREFIX ${CMAKE_BINARY_DIR}/external/libwebrtc/prefix + TMP_DIR ${CMAKE_BINARY_DIR}/external/libwebrtc/tmp + STAMP_DIR ${CMAKE_BINARY_DIR}/external/libwebrtc/stamp + DOWNLOAD_DIR ${CMAKE_BINARY_DIR}/external/libwebrtc/download + SOURCE_DIR ${CMAKE_BINARY_DIR}/external/libwebrtc/download/src + BINARY_DIR ${CMAKE_BINARY_DIR}/external/libwebrtc/build/${CMAKE_BUILD_TYPE} + + BUILD_BYPRODUCTS ${byproducts} + + DOWNLOAD_COMMAND ${CMAKE_COMMAND} -E env DEPOT_TOOLS=${depot_tools_install_dir} PLATFORM=${PLATFORM} WEBRTC_REVISION=${WEBRTC_REVISION} ${CMAKE_SOURCE_DIR}/scripts/download-webrtc.${suffix} + CONFIGURE_COMMAND ${CMAKE_COMMAND} -E env BINARY_DIR= DEPOT_TOOLS=${depot_tools_install_dir} GN_GEN_ARGS=${GN_GEN_ARGS} SOURCE_DIR= ${CMAKE_SOURCE_DIR}/scripts/configure-webrtc.${suffix} + BUILD_COMMAND ${CMAKE_COMMAND} -E env CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} DEPOT_TOOLS=${depot_tools_install_dir} USE_CUSTOM_LIBCXX=${use_custom_libcxx} ${CMAKE_SOURCE_DIR}/scripts/build-webrtc.${suffix} + INSTALL_COMMAND "" +) + +add_dependencies(project_libwebrtc project_depot_tools) + +ExternalProject_Get_Property(project_libwebrtc DOWNLOAD_DIR) +set(libwebrtc_source_dir "${DOWNLOAD_DIR}") + +ExternalProject_Get_Property(project_libwebrtc BINARY_DIR) +set(libwebrtc_binary_dir "${BINARY_DIR}") + +add_library(libwebrtc STATIC IMPORTED) +add_dependencies(libwebrtc project_libwebrtc) + +if(WIN32) + set_property(TARGET libwebrtc PROPERTY IMPORTED_LOCATION "${libwebrtc_binary_dir}/obj/webrtc.lib") +else() + set_property(TARGET libwebrtc PROPERTY IMPORTED_LOCATION "${libwebrtc_binary_dir}/obj/libwebrtc.a") +endif() + +add_library(libpeerconnection STATIC IMPORTED) +add_dependencies(libpeerconnection project_libwebrtc) + if(WIN32) - set_property(TARGET libpeerconnection PROPERTY IMPORTED_LOCATION "${libwebrtc_binary_dir}/obj/api/libjingle_peerconnection_api.lib") + set_property(TARGET libpeerconnection PROPERTY IMPORTED_LOCATION "${libwebrtc_binary_dir}/obj/api/create_peerconnection_factory.lib") else() set_property(TARGET libpeerconnection PROPERTY IMPORTED_LOCATION "${libwebrtc_binary_dir}/obj/api/libjingle_peerconnection_api.a") endif() - -# Video codec factories (audio factories are already in libwebrtc.a) -add_library(libbuiltin_video_encoder_factory STATIC IMPORTED) -add_dependencies(libbuiltin_video_encoder_factory project_libwebrtc) -set_property(TARGET libbuiltin_video_encoder_factory PROPERTY IMPORTED_LOCATION "${libwebrtc_binary_dir}/obj/api/video_codecs/libbuiltin_video_encoder_factory.a") - -add_library(libbuiltin_video_decoder_factory STATIC IMPORTED) -add_dependencies(libbuiltin_video_decoder_factory project_libwebrtc) -set_property(TARGET libbuiltin_video_decoder_factory PROPERTY IMPORTED_LOCATION "${libwebrtc_binary_dir}/obj/api/video_codecs/libbuiltin_video_decoder_factory.a") - -add_library(librtc_internal_video_codecs STATIC IMPORTED) -add_dependencies(librtc_internal_video_codecs project_libwebrtc) -set_property(TARGET librtc_internal_video_codecs PROPERTY IMPORTED_LOCATION "${libwebrtc_binary_dir}/obj/media/librtc_internal_video_codecs.a") - -set(libc++_include_dir - "${libwebrtc_source_dir}/src/buildtools/third_party/libc++/trunk/include" - "${libwebrtc_source_dir}/src/buildtools/third_party/libc++" -) -set(libc++abi_include_dir "${libwebrtc_source_dir}/src/buildtools/third_party/libc++abi/trunk/include") - -# NOTE(mroberts): I would like this to be INTERFACE. -# -# https://gitlab.kitware.com/cmake/cmake/issues/15052 -# -# target_include_directories(libwebrtc SYSTEM INTERFACE -# ${libwebrtc_source_dir} -# ${libwebrtc_source_dir}/webrtc -# ${libwebrtc_source_dir}/webrtc/third_party/abseil-cpp -# ${libwebrtc_source_dir}/webrtc/third_party/libyuv/include -# ) - -# catch2 -# ----------------------------------------------------------------------------- - -ExternalProject_Add( - project_catch2 - - GIT_REPOSITORY https://github.com/catchorg/Catch2.git - GIT_TAG ee1450f268dfd5c13aa8670ba97e93cabaf2e15d - - PREFIX ${CMAKE_BINARY_DIR}/external/catch2/prefix - TMP_DIR ${CMAKE_BINARY_DIR}/external/catch2/tmp - STAMP_DIR ${CMAKE_BINARY_DIR}/external/catch2/stamp - DOWNLOAD_DIR ${CMAKE_BINARY_DIR}/external/catch2/download - SOURCE_DIR ${CMAKE_BINARY_DIR}/external/catch2/src - BINARY_DIR ${CMAKE_BINARY_DIR}/external/catch2/build - - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND "" -) - -ExternalProject_Get_Property(project_catch2 SOURCE_DIR) -set(catch2_install_dir ${SOURCE_DIR}) - -# node-webrtc -# ----------------------------------------------------------------------------- - -file(GLOB_RECURSE MODULE_SRC src/*.cc src/*.hh) -add_library(${MODULE} SHARED ${MODULE_SRC} ${CMAKE_JS_SRC}) -set_target_properties(${MODULE} PROPERTIES PREFIX "" SUFFIX ".node") - -set_property(TARGET ${MODULE} PROPERTY CXX_STANDARD 20) - -# NOTE(mroberts): Workaround for -# -# https://gitlab.kitware.com/cmake/cmake/issues/15052 -# -# is to include all the header files here. -target_include_directories(${MODULE} SYSTEM PRIVATE - ${CMAKE_JS_INC} - ${CMAKE_SOURCE_DIR}/node_modules - ${libwebrtc_source_dir} - ${libwebrtc_source_dir}/webrtc - ${libwebrtc_source_dir}/webrtc/third_party/abseil-cpp - ${libwebrtc_source_dir}/webrtc/third_party/libyuv/include -) - -if(CMAKE_BUILD_TYPE STREQUAL "Debug") - add_dependencies(${MODULE} project_catch2) - target_include_directories(${MODULE} SYSTEM PRIVATE - ${catch2_install_dir}/single_include - ) + +if(NOT WIN32) + # Video codec factories (audio factories are already in libwebrtc.a) + add_library(libbuiltin_video_encoder_factory STATIC IMPORTED) + add_dependencies(libbuiltin_video_encoder_factory project_libwebrtc) + set_property(TARGET libbuiltin_video_encoder_factory PROPERTY IMPORTED_LOCATION "${libwebrtc_binary_dir}/obj/api/video_codecs/libbuiltin_video_encoder_factory.a") + + add_library(libbuiltin_video_decoder_factory STATIC IMPORTED) + add_dependencies(libbuiltin_video_decoder_factory project_libwebrtc) + set_property(TARGET libbuiltin_video_decoder_factory PROPERTY IMPORTED_LOCATION "${libwebrtc_binary_dir}/obj/api/video_codecs/libbuiltin_video_decoder_factory.a") + + add_library(librtc_internal_video_codecs STATIC IMPORTED) + add_dependencies(librtc_internal_video_codecs project_libwebrtc) + set_property(TARGET librtc_internal_video_codecs PROPERTY IMPORTED_LOCATION "${libwebrtc_binary_dir}/obj/media/librtc_internal_video_codecs.a") endif() - -target_include_directories(${MODULE} PRIVATE - ${CMAKE_SOURCE_DIR} -) - + +set(libc++_include_dir + "${libwebrtc_source_dir}/src/buildtools/third_party/libc++/trunk/include" + "${libwebrtc_source_dir}/src/buildtools/third_party/libc++" +) +set(libc++abi_include_dir "${libwebrtc_source_dir}/src/buildtools/third_party/libc++abi/trunk/include") + +# NOTE(mroberts): I would like this to be INTERFACE. +# +# https://gitlab.kitware.com/cmake/cmake/issues/15052 +# +# target_include_directories(libwebrtc SYSTEM INTERFACE +# ${libwebrtc_source_dir} +# ${libwebrtc_source_dir}/webrtc +# ${libwebrtc_source_dir}/webrtc/third_party/abseil-cpp +# ${libwebrtc_source_dir}/webrtc/third_party/libyuv/include +# ) + +# catch2 +# ----------------------------------------------------------------------------- + +ExternalProject_Add( + project_catch2 + + GIT_REPOSITORY https://github.com/catchorg/Catch2.git + GIT_TAG ee1450f268dfd5c13aa8670ba97e93cabaf2e15d + + PREFIX ${CMAKE_BINARY_DIR}/external/catch2/prefix + TMP_DIR ${CMAKE_BINARY_DIR}/external/catch2/tmp + STAMP_DIR ${CMAKE_BINARY_DIR}/external/catch2/stamp + DOWNLOAD_DIR ${CMAKE_BINARY_DIR}/external/catch2/download + SOURCE_DIR ${CMAKE_BINARY_DIR}/external/catch2/src + BINARY_DIR ${CMAKE_BINARY_DIR}/external/catch2/build + + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" +) + +ExternalProject_Get_Property(project_catch2 SOURCE_DIR) +set(catch2_install_dir ${SOURCE_DIR}) + +# node-webrtc +# ----------------------------------------------------------------------------- + +file(GLOB_RECURSE MODULE_SRC src/*.cc src/*.hh) +add_library(${MODULE} SHARED ${MODULE_SRC} ${CMAKE_JS_SRC}) +set_target_properties(${MODULE} PROPERTIES PREFIX "" SUFFIX ".node") + +set_property(TARGET ${MODULE} PROPERTY CXX_STANDARD 20) + +# NOTE(mroberts): Workaround for +# +# https://gitlab.kitware.com/cmake/cmake/issues/15052 +# +# is to include all the header files here. +target_include_directories(${MODULE} SYSTEM PRIVATE + ${CMAKE_JS_INC} + ${CMAKE_SOURCE_DIR}/node_modules + ${libwebrtc_source_dir} + ${libwebrtc_source_dir}/webrtc + ${libwebrtc_source_dir}/webrtc/third_party/abseil-cpp + ${libwebrtc_source_dir}/webrtc/third_party/libyuv/include +) + +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + add_dependencies(${MODULE} project_catch2) + target_include_directories(${MODULE} SYSTEM PRIVATE + ${catch2_install_dir}/single_include + ) +endif() + +target_include_directories(${MODULE} PRIVATE + ${CMAKE_SOURCE_DIR} +) + target_link_libraries(${MODULE} PRIVATE ${CMAKE_THREAD_LIBS_INIT} libpeerconnection libwebrtc - libbuiltin_video_encoder_factory - libbuiltin_video_decoder_factory - librtc_internal_video_codecs ${CMAKE_JS_LIB} ) -target_compile_definitions(${MODULE} PRIVATE - -DNAPI_VERSION=3 - -DUSE_BUILTIN_SW_CODECS -) - +if(NOT WIN32) + target_link_libraries(${MODULE} PRIVATE + libbuiltin_video_encoder_factory + libbuiltin_video_decoder_factory + librtc_internal_video_codecs + ) +endif() + +target_compile_definitions(${MODULE} PRIVATE + -DNAPI_VERSION=3 + -DUSE_BUILTIN_SW_CODECS +) + if(WIN32) set_property(TARGET ${MODULE} PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>" ) + target_compile_options(${MODULE} PRIVATE + /FI${CMAKE_SOURCE_DIR}/src/webrtc_compat.hh + ) + target_link_libraries(${MODULE} PRIVATE + crypt32.lib dmoguids.lib msdmo.lib secur32.lib - winmm.lib - wmcodecdspuuid.lib - ws2_32.lib - Iphlpapi.lib - delayimp.lib - ) - + winmm.lib + wmcodecdspuuid.lib + ws2_32.lib + Iphlpapi.lib + delayimp.lib + ) + if (${use_custom_libcxx}) target_include_directories(${MODULE} SYSTEM PRIVATE ${libc++_include_dir} ) - target_link_libraries(${MODULE} PRIVATE - libc++ - ) - - target_link_options(${MODULE} PRIVATE - /defaultlib:libcmt - /defaultlib:libcpmt - /nodefaultlib:libc++ - /nodefaultlib:c++ - ) - - target_compile_definitions(${MODULE} PRIVATE - -D_LIBCPP_ABI_NAMESPACE=Cr - -D_LIBCPP_ABI_VERSION=2 - -D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS - -D_LIBCPP_HAS_NO_VENDOR_AVAILABILITY_ANNOTATIONS - ) - endif() - - target_compile_definitions(${MODULE} PRIVATE - -D_WINSOCKAPI_ - -DINCL_EXTRA_HTON_FUNCTIONS - -DNOGDI - -DNOMINMAX - -DWEBRTC_WIN - "$<$:-D_HAS_ITERATOR_DEBUGGING=0>" - ) -else() - target_link_libraries(${MODULE} PRIVATE - -lc - -lm - ) - - target_compile_options(${MODULE} PRIVATE - -fno-rtti - -fvisibility=hidden - -fsafe-buffer-usage-suggestions - -Wall - -Wextra - -Wpedantic - -Wno-missing-field-initializers - -Wno-uninitialized - -Wno-unused-local-typedefs - -Wno-unused-variable - "$<$:-g>" - ) - - target_compile_definitions(${MODULE} PRIVATE - -DWEBRTC_POSIX=1 - "$<$:-DDEBUG>" - ) - - if (${use_custom_libcxx}) - # NOTE(mroberts): Workaround for - # - # https://gitlab.kitware.com/cmake/cmake/issues/15052 - # - # is to include all the header files here. - target_include_directories(${MODULE} SYSTEM PRIVATE - ${libc++_include_dir} - ${libc++abi_include_dir} - ) - - # We statically link the same libc++ and libc++abi libraries that libwebrtc builds against. - target_link_libraries(${MODULE} PRIVATE - libc++ - libc++abi - -nostdlib++ # Requires clang, but that's OK, we have nix :) - ) - target_compile_options(${MODULE} PRIVATE - -nostdlib++ - -nostdinc++ - -nodefaultlibs - ) - - target_compile_definitions(${MODULE} PRIVATE - -D_LIBCPP_ABI_NAMESPACE=Cr - -D_LIBCPP_ABI_VERSION=2 + /clang:-nostdinc++ ) - endif() - if (APPLE) target_link_libraries(${MODULE} PRIVATE - "-framework AppKit" - "-framework AVFoundation" - ) - - target_compile_options(${MODULE} PRIVATE - -Weverything - -Wno-c++98-compat - -Wno-c++98-compat-pedantic - -Wno-covered-switch-default - -Wno-documentation - -Wno-exit-time-destructors - -Wno-float-conversion - -Wno-global-constructors - -Wno-padded - -Wno-shadow - -Wno-shadow-field-in-constructor - -Wno-shorten-64-to-32 - -Wno-sign-conversion - -Wno-thread-safety-negative - -Wno-unused-template - -Wno-weak-vtables - ) - - target_compile_definitions(${MODULE} PRIVATE - -DWEBRTC_MAC - -DWEBRTC_IOS - ) - else() - target_compile_definitions(${MODULE} PRIVATE - -DWEBRTC_LINUX - ) - - target_compile_options(${MODULE} PRIVATE - -fpermissive + libc++ ) - endif() -endif() + + target_link_options(${MODULE} PRIVATE + /defaultlib:libcmt + /defaultlib:libcpmt + /nodefaultlib:libc++ + /nodefaultlib:c++ + ) + + target_compile_definitions(${MODULE} PRIVATE + -D_LIBCPP_ABI_NAMESPACE=Cr + -D_LIBCPP_ABI_VERSION=2 + -D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS + -D_LIBCPP_HAS_NO_VENDOR_AVAILABILITY_ANNOTATIONS + ) + endif() + + target_compile_definitions(${MODULE} PRIVATE + -D_WINSOCKAPI_ + -DINCL_EXTRA_HTON_FUNCTIONS + -DNOGDI + -DNOMINMAX + -DWEBRTC_WIN + "$<$:-D_HAS_ITERATOR_DEBUGGING=0>" + ) +else() + target_link_libraries(${MODULE} PRIVATE + -lc + -lm + ) + + target_compile_options(${MODULE} PRIVATE + -fno-rtti + -fvisibility=hidden + -fsafe-buffer-usage-suggestions + -Wall + -Wextra + -Wpedantic + -Wno-missing-field-initializers + -Wno-uninitialized + -Wno-unused-local-typedefs + -Wno-unused-variable + "$<$:-g>" + ) + + target_compile_definitions(${MODULE} PRIVATE + -DWEBRTC_POSIX=1 + "$<$:-DDEBUG>" + ) + + if (${use_custom_libcxx}) + # NOTE(mroberts): Workaround for + # + # https://gitlab.kitware.com/cmake/cmake/issues/15052 + # + # is to include all the header files here. + target_include_directories(${MODULE} SYSTEM PRIVATE + ${libc++_include_dir} + ${libc++abi_include_dir} + ) + + # We statically link the same libc++ and libc++abi libraries that libwebrtc builds against. + target_link_libraries(${MODULE} PRIVATE + libc++ + libc++abi + -nostdlib++ # Requires clang, but that's OK, we have nix :) + ) + + target_compile_options(${MODULE} PRIVATE + -nostdlib++ + -nostdinc++ + -nodefaultlibs + ) + + target_compile_definitions(${MODULE} PRIVATE + -D_LIBCPP_ABI_NAMESPACE=Cr + -D_LIBCPP_ABI_VERSION=2 + ) + endif() + + if (APPLE) + target_link_libraries(${MODULE} PRIVATE + "-framework AppKit" + "-framework AVFoundation" + ) + + target_compile_options(${MODULE} PRIVATE + -Weverything + -Wno-c++98-compat + -Wno-c++98-compat-pedantic + -Wno-covered-switch-default + -Wno-documentation + -Wno-exit-time-destructors + -Wno-float-conversion + -Wno-global-constructors + -Wno-padded + -Wno-shadow + -Wno-shadow-field-in-constructor + -Wno-shorten-64-to-32 + -Wno-sign-conversion + -Wno-thread-safety-negative + -Wno-unused-template + -Wno-weak-vtables + ) + + target_compile_definitions(${MODULE} PRIVATE + -DWEBRTC_MAC + -DWEBRTC_IOS + ) + else() + target_compile_definitions(${MODULE} PRIVATE + -DWEBRTC_LINUX + ) + + target_compile_options(${MODULE} PRIVATE + -fpermissive + ) + endif() +endif() diff --git a/README.md b/README.md index 5bcd9ce4d..de852e2b1 100644 --- a/README.md +++ b/README.md @@ -1,74 +1,90 @@ -

-      - -

- -[![NPM](https://img.shields.io/npm/v/@roamhq/wrtc.svg)](https://www.npmjs.com/package/@roamhq/wrtc) - -node-webrtc is a Node.js Native Addon that provides bindings to [WebRTC -M106](https://webrtc.googlesource.com/src/+/branch-heads/5249). This project is +

+      + +

+ +[![NPM](https://img.shields.io/npm/v/@roamhq/wrtc.svg)](https://www.npmjs.com/package/@roamhq/wrtc) + +node-webrtc is a Node.js Native Addon that provides bindings to libwebrtc. This project is aiming for spec-compliance and will eventually be tested using the W3C's [web-platform-tests](https://github.com/web-platform-tests/wpt) project. A number of [nonstandard APIs](docs/nonstandard-apis.md) for testing are also included. -## Install - -``` -npm install @roamhq/wrtc -``` - -Installing from NPM downloads a prebuilt binary for your operating system × -architecture, based on optional dependency filters. - +This fork defaults to `branch-heads/5735` and supports overriding the upstream +libwebrtc ref at build time with `WEBRTC_REVISION=`. + +## Install + +``` +npm install @roamhq/wrtc +``` + +Installing from NPM downloads a prebuilt binary for your operating system × +architecture, based on optional dependency filters. + To install a debug build or cross-compile, you should [build from source](docs/build-from-source.md). -## Supported Platforms - -The following platforms are confirmed to work with node-webrtc and have -prebuilt binaries available. Since node-webrtc targets [N-API version -3](https://nodejs.org/api/n-api.html), there may be additional platforms -supported that are not listed here. If your platform is not supported, you may -still be able to [build from source](docs/build-from-source.md). +To experiment with a different upstream libwebrtc revision when debugging a +native networking issue: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
LinuxmacOSWindows
x64arm64x64arm64x64
Node20?
22?
+```bash +WEBRTC_REVISION=branch-heads/5735 npm run build +``` -## Examples +or on PowerShell: -See [node-webrtc/node-webrtc-examples](https://github.com/node-webrtc/node-webrtc-examples). +```powershell +$env:WEBRTC_REVISION="branch-heads/5735" +npm run build +``` + +## Supported Platforms + +The following platforms are confirmed to work with node-webrtc and have +prebuilt binaries available. Since node-webrtc targets [N-API version +3](https://nodejs.org/api/n-api.html), there may be additional platforms +supported that are not listed here. If your platform is not supported, you may +still be able to [build from source](docs/build-from-source.md). + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
LinuxmacOSWindows
x64arm64x64arm64x64
Node20?
22?
+ +## Examples + +See [node-webrtc/node-webrtc-examples](https://github.com/node-webrtc/node-webrtc-examples). diff --git a/package.json b/package.json index 0f2b7f3ad..f2cba7337 100644 --- a/package.json +++ b/package.json @@ -1,68 +1,67 @@ -{ - "name": "@roamhq/wrtc", - "description": "Standards-compliant WebRTC implementation for Node", - "keywords": [ - "webrtc", - "p2p", - "peer" - ], - "version": "0.10.0", - "author": "Jack Duvall (https://duvallj.pw)", - "homepage": "https://github.com/WonderInventions/node-webrtc", - "bugs": "https://github.com/WonderInventions/node-webrtc/issues", - "license": "BSD-2-Clause", - "repository": { - "type": "git", - "url": "git+ssh://git@github.com/WonderInventions/node-webrtc.git" - }, - "main": "lib/index.js", - "types": "types/index.d.ts", - "browser": "lib/browser.js", - "files": [ - "AUTHORS", - "CHANGELOG.md", - "lib", - "types" - ], - "optionalDependencies": { - "@roamhq/wrtc-darwin-arm64": "0.10.0", - "@roamhq/wrtc-darwin-x64": "0.10.0", - "@roamhq/wrtc-linux-arm64": "0.10.0", - "@roamhq/wrtc-linux-x64": "0.10.0", - "@roamhq/wrtc-win32-x64": "0.10.0", - "domexception": "^4.0.0" - }, - "devDependencies": { - "@eslint/eslintrc": "^3.2.0", - "@eslint/js": "^9.17.0", - "@fidm/x509": "^1.2.1", - "cmake-js": "^7.3.0", - "cross-env": "^5.1.4", - "eslint": "^9.17.0", - "eslint-config-prettier": "^9.1.0", - "globals": "^15.14.0", - "husky": "^9.1.7", - "lint-staged": "^15.2.11", - "node-addon-api": "^7.0.0", - "patch-package": "^8.0.0", - "prettier": "^3.4.2", - "recursive-copy": "^2.0.14", - "simple-peer": "~9.7.0", - "tape": "^5.6.1", - "temp": "^0.9.4" - }, +{ + "name": "@roamhq/wrtc", + "description": "Standards-compliant WebRTC implementation for Node", + "keywords": [ + "webrtc", + "p2p", + "peer" + ], + "version": "0.10.0", + "author": "Jack Duvall (https://duvallj.pw)", + "homepage": "https://github.com/WonderInventions/node-webrtc", + "bugs": "https://github.com/WonderInventions/node-webrtc/issues", + "license": "BSD-2-Clause", + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/WonderInventions/node-webrtc.git" + }, + "main": "lib/index.js", + "types": "types/index.d.ts", + "browser": "lib/browser.js", + "files": [ + "AUTHORS", + "CHANGELOG.md", + "lib", + "types" + ], + "optionalDependencies": { + "@roamhq/wrtc-darwin-arm64": "0.10.0", + "@roamhq/wrtc-darwin-x64": "0.10.0", + "@roamhq/wrtc-linux-arm64": "0.10.0", + "@roamhq/wrtc-linux-x64": "0.10.0", + "@roamhq/wrtc-win32-x64": "0.10.0", + "domexception": "^4.0.0" + }, + "devDependencies": { + "@eslint/eslintrc": "^3.2.0", + "@eslint/js": "^9.17.0", + "@fidm/x509": "^1.2.1", + "cmake-js": "^7.3.0", + "cross-env": "^5.1.4", + "eslint": "^9.17.0", + "eslint-config-prettier": "^9.1.0", + "globals": "^15.14.0", + "husky": "^9.1.7", + "lint-staged": "^15.2.11", + "node-addon-api": "^7.0.0", + "patch-package": "^8.0.0", + "prettier": "^3.4.2", + "recursive-copy": "^2.0.14", + "simple-peer": "~9.7.0", + "tape": "^5.6.1", + "temp": "^0.9.4" + }, "scripts": { "patch": "patch-package --error-on-warn", "build": "node scripts/build-from-source.js", "make-prebuilt": "node scripts/make-prebuilt.js", "install-example": "node scripts/install-example.js", "lint": "eslint lib/*.js lib/**/*.js test/*.js test/**/*.js scripts/*.js", - "test": "node --expose-gc test/all.js", - "prepare": "husky" + "test": "node --expose-gc test/all.js" }, - "lint-staged": { - "*.js": "eslint --cache --fix", - "*.{js,css,md}": "prettier --write", - "*.{cc,h}": "clang-format -i" - } -} \ No newline at end of file + "lint-staged": { + "*.js": "eslint --cache --fix", + "*.{js,css,md}": "prettier --write", + "*.{cc,h}": "clang-format -i" + } +} diff --git a/scripts/build-from-source.js b/scripts/build-from-source.js index 8931fa1cd..aeae14788 100755 --- a/scripts/build-from-source.js +++ b/scripts/build-from-source.js @@ -1,70 +1,79 @@ -#!/usr/bin/env node -/* eslint no-console:0, no-process-env:0 */ -"use strict"; - -const os = require("os"); -const path = require("path"); -const { spawnSync } = require("child_process"); -const { platform, arch, buildFolder } = require("./build-vars.js"); - -const args = ["-O", buildFolder, "-a", arch]; - -if (process.env.DEBUG) { - args.push(...["--debug", "--CDCMAKE_EXPORT_COMPILE_COMMANDS=1"]); -} - -if (platform === "win32") { - args.push(...["-G", "Ninja"]); -} - -if (arch !== os.arch()) { - args.push( - `--CDCMAKE_TOOLCHAIN_FILE=toolchains/${platform}-${arch}.toolchain`, - ); -} - -function main() { - // Resolve cmake-js path before modifying PATH, since it lives in node_modules/.bin - const cmakeJs = path.resolve( - __dirname, - "..", - "node_modules", - ".bin", - "cmake-js", - ); - +#!/usr/bin/env node +/* eslint no-console:0, no-process-env:0 */ +"use strict"; + +const os = require("os"); +const path = require("path"); +const { spawnSync } = require("child_process"); +const { platform, arch, buildFolder } = require("./build-vars.js"); + +const args = ["-O", buildFolder, "-a", arch]; + +if (process.env.DEBUG) { + args.push(...["--debug", "--CDCMAKE_EXPORT_COMPILE_COMMANDS=1"]); +} + +if (platform === "win32") { + args.push(...["-G", "Ninja"]); +} + +if (arch !== os.arch()) { + args.push( + `--CDCMAKE_TOOLCHAIN_FILE=toolchains/${platform}-${arch}.toolchain`, + ); +} + +function main() { + // Resolve cmake-js path before modifying PATH, since it lives in node_modules/.bin + const cmakeJs = path.resolve( + __dirname, + "..", + "node_modules", + ".bin", + "cmake-js", + ); + if (platform === "win32") { // Explicitly find the real rc.exe from the Windows SDK and pass it to // CMake, since cmake-js may still find the npm "rc" package otherwise. - const { stdout } = spawnSync("where", ["rc.exe"], { encoding: "utf-8" }); - if (stdout) { - args.push(`--CDCMAKE_RC_COMPILER="${stdout.replace(/\\/g, "/")}"`); + const explicitRc = process.env.WRTC_RC_COMPILER; + if (explicitRc) { + args.push(`--CDCMAKE_RC_COMPILER="${explicitRc.replace(/\\/g, "/")}"`); + } else { + const { stdout } = spawnSync("where", ["rc.exe"], { encoding: "utf-8" }); + const firstMatch = stdout + .split(/\r?\n/) + .map((line) => line.trim()) + .find(Boolean); + if (firstMatch) { + args.push(`--CDCMAKE_RC_COMPILER="${firstMatch.replace(/\\/g, "/")}"`); + } } } - - console.log("Running cmake-js " + args.join(" ")); - let { status } = spawnSync(cmakeJs, ["configure", ...args], { - shell: true, - stdio: "inherit", - }); - if (status) { - throw new Error("cmake-js configure failed for wrtc"); - } - - console.log("Running cmake-js build"); - status = spawnSync(cmakeJs, ["build", ...args], { - shell: true, - stdio: "inherit", - }).status; - if (status) { - throw new Error("cmake-js build failed for wrtc"); - } - - console.log("Built wrtc"); -} - -module.exports = main; - -if (require.main === module) { - main(); -} + + console.log("Running cmake-js " + args.join(" ")); + let { status } = spawnSync(cmakeJs, ["configure", ...args], { + shell: true, + stdio: "inherit", + }); + if (status) { + throw new Error("cmake-js configure failed for wrtc"); + } + + console.log("Running cmake-js build"); + status = spawnSync(cmakeJs, ["build", ...args], { + shell: true, + stdio: "inherit", + }).status; + if (status) { + throw new Error("cmake-js build failed for wrtc"); + } + + console.log("Built wrtc"); +} + +module.exports = main; + +if (require.main === module) { + main(); +} diff --git a/scripts/build-webrtc.bat b/scripts/build-webrtc.bat index debfb1430..863e0c162 100644 --- a/scripts/build-webrtc.bat +++ b/scripts/build-webrtc.bat @@ -1,20 +1,24 @@ -@ECHO OFF -SET EL=0 - +@ECHO OFF +SET EL=0 + ECHO Add depot_tools to PATH set PATH=%DEPOT_TOOLS%;%PATH% IF %ERRORLEVEL% NEQ 0 GOTO ERROR ECHO ninja -call autoninja webrtc libjingle_peerconnection libc++ libc++abi builtin_video_encoder_factory builtin_video_decoder_factory rtc_internal_video_codecs +IF /I "%USE_CUSTOM_LIBCXX%"=="true" ( + call autoninja webrtc create_peerconnection_factory libc++ +) ELSE ( + call autoninja webrtc create_peerconnection_factory +) IF %ERRORLEVEL% NEQ 0 GOTO ERROR - -GOTO DONE - -:ERROR -ECHO ERRORLEVEL^: %ERRORLEVEL% -SET EL=%ERRORLEVEL% - -:DONE - -EXIT /b %EL% + +GOTO DONE + +:ERROR +ECHO ERRORLEVEL^: %ERRORLEVEL% +SET EL=%ERRORLEVEL% + +:DONE + +EXIT /b %EL% diff --git a/scripts/configure-webrtc.bat b/scripts/configure-webrtc.bat index f5de32087..6494bbd05 100644 --- a/scripts/configure-webrtc.bat +++ b/scripts/configure-webrtc.bat @@ -1,28 +1,44 @@ -@ECHO OFF -SET EL=0 - -ECHO Add depot_tools to PATH -set PATH=%DEPOT_TOOLS%;%PATH% -IF %ERRORLEVEL% NEQ 0 GOTO ERROR - +@ECHO OFF +SET EL=0 + +ECHO Add depot_tools to PATH +set PATH=%DEPOT_TOOLS%;%PATH% +IF %ERRORLEVEL% NEQ 0 GOTO ERROR + ECHO SET DEPOT_TOOLS_WIN_TOOLCHAIN=0 SET DEPOT_TOOLS_WIN_TOOLCHAIN=0 IF %ERRORLEVEL% NEQ 0 GOTO ERROR -ECHO cd SOURCE_DIR -cd %SOURCE_DIR% +IF NOT EXIST "C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools" GOTO AFTER_VS_OVERRIDE +ECHO SET GYP_MSVS_OVERRIDE_PATH=C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools +SET "GYP_MSVS_OVERRIDE_PATH=C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools" +ECHO SET GYP_MSVS_VERSION=2022 +SET "GYP_MSVS_VERSION=2022" +ECHO SET vs2022_install=C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools +SET "vs2022_install=C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools" +IF NOT EXIST "C:\Program Files (x86)\Windows Kits\10" GOTO AFTER_GN_WIN_ARGS +ECHO SET WINDOWSSDKDIR=C:\Program Files (x86)\Windows Kits\10 +SET "WINDOWSSDKDIR=C:\Program Files (x86)\Windows Kits\10" +ECHO SET WDK_DIR=C:\Program Files (x86)\Windows Kits\10 +SET "WDK_DIR=C:\Program Files (x86)\Windows Kits\10" +:AFTER_GN_WIN_ARGS +:AFTER_VS_OVERRIDE IF %ERRORLEVEL% NEQ 0 GOTO ERROR -ECHO gn gen BINARY_DIR "--args=GN_GEN_ARGS" -CALL gn gen %BINARY_DIR% "--args=%GN_GEN_ARGS%" +ECHO cd SOURCE_DIR +cd %SOURCE_DIR% IF %ERRORLEVEL% NEQ 0 GOTO ERROR - -GOTO DONE - -:ERROR -ECHO ERRORLEVEL^: %ERRORLEVEL% -SET EL=%ERRORLEVEL% - -:DONE - -EXIT /b %EL% + +ECHO gn gen BINARY_DIR "--args=GN_GEN_ARGS" +CALL gn gen %BINARY_DIR% "--args=%GN_GEN_ARGS%" +IF %ERRORLEVEL% NEQ 0 GOTO ERROR + +GOTO DONE + +:ERROR +ECHO ERRORLEVEL^: %ERRORLEVEL% +SET EL=%ERRORLEVEL% + +:DONE + +EXIT /b %EL% diff --git a/scripts/download-webrtc.bat b/scripts/download-webrtc.bat index 9db1042ba..5f1a09785 100644 --- a/scripts/download-webrtc.bat +++ b/scripts/download-webrtc.bat @@ -1,47 +1,49 @@ -@ECHO OFF -SET EL=0 - -ECHO Add depot_tools to PATH -set PATH=%DEPOT_TOOLS%;%PATH% -IF %ERRORLEVEL% NEQ 0 GOTO ERROR - -ECHO SET DEPOT_TOOLS_WIN_TOOLCHAIN=0 -SET DEPOT_TOOLS_WIN_TOOLCHAIN=0 -IF %ERRORLEVEL% NEQ 0 GOTO ERROR - -ECHO gclient config -CALL gclient config --unmanaged --spec solutions=[{\"name\":\"src\",\"url\":\"https://webrtc.googlesource.com/src.git\"}] -IF %ERRORLEVEL% NEQ 0 GOTO ERROR - +@ECHO OFF +SET EL=0 + +ECHO Add depot_tools to PATH +set PATH=%DEPOT_TOOLS%;%PATH% +IF %ERRORLEVEL% NEQ 0 GOTO ERROR + +ECHO SET DEPOT_TOOLS_WIN_TOOLCHAIN=0 +SET DEPOT_TOOLS_WIN_TOOLCHAIN=0 +IF %ERRORLEVEL% NEQ 0 GOTO ERROR + +ECHO gclient config +CALL gclient config --unmanaged --spec solutions=[{\"name\":\"src\",\"url\":\"https://webrtc.googlesource.com/src.git\"}] +IF %ERRORLEVEL% NEQ 0 GOTO ERROR + ECHO gclient sync -CALL gclient sync --nohooks --with_branch_heads -r %WEBRTC_REVISION% -R -IF %ERRORLEVEL% NEQ 0 GOTO ERROR - -ECHO lastchange -CALL python src\build\util\lastchange.py -o src\build\util\LASTCHANGE -IF %ERRORLEVEL% NEQ 0 GOTO ERROR - +CALL gclient sync --jobs 1 --nohooks --with_branch_heads -r %WEBRTC_REVISION% -R +IF %ERRORLEVEL% NEQ 0 GOTO ERROR + +ECHO lastchange +CALL python src\build\util\lastchange.py -o src\build\util\LASTCHANGE +IF %ERRORLEVEL% NEQ 0 GOTO ERROR + ECHO update toolchain CALL python src\build\vs_toolchain.py update --force IF %ERRORLEVEL% NEQ 0 GOTO ERROR +CALL python "%~dp0patch-vs-toolchain.py" src\build\vs_toolchain.py +IF %ERRORLEVEL% NEQ 0 GOTO ERROR ECHO download clang CALL python3 src\tools\clang\scripts\update.py IF %ERRORLEVEL% NEQ 0 GOTO ERROR - -ECHO rmdir webrtc -rmdir webrtc - -ECHO mklink /D webrtc src -mklink /D webrtc src -IF %ERRORLEVEL% NEQ 0 GOTO ERROR - -GOTO DONE - -:ERROR -ECHO ERRORLEVEL^: %ERRORLEVEL% -SET EL=%ERRORLEVEL% - -:DONE - -EXIT /b %EL% + +ECHO rmdir webrtc +rmdir webrtc + +ECHO mklink /J webrtc src +mklink /J webrtc src +IF %ERRORLEVEL% NEQ 0 GOTO ERROR + +GOTO DONE + +:ERROR +ECHO ERRORLEVEL^: %ERRORLEVEL% +SET EL=%ERRORLEVEL% + +:DONE + +EXIT /b %EL% diff --git a/scripts/download-webrtc.sh b/scripts/download-webrtc.sh index 83616ab0b..3af5500cd 100755 --- a/scripts/download-webrtc.sh +++ b/scripts/download-webrtc.sh @@ -1,17 +1,17 @@ -#!/usr/bin/env bash - -set -e - -set -v - -export PATH=$DEPOT_TOOLS:$PATH - -gclient config --unmanaged --spec 'solutions=[{"name":"src","url":"https://webrtc.googlesource.com/src.git"}]' - -gclient sync --shallow --no-history --nohooks --with_branch_heads -r ${WEBRTC_REVISION} -R - -vpython3 src/tools/clang/scripts/update.py - -rm -f webrtc - -ln -s src webrtc +#!/usr/bin/env bash + +set -e + +set -v + +export PATH=$DEPOT_TOOLS:$PATH + +gclient config --unmanaged --spec 'solutions=[{"name":"src","url":"https://webrtc.googlesource.com/src.git"}]' + +gclient sync --jobs 1 --shallow --no-history --nohooks --with_branch_heads -r ${WEBRTC_REVISION} -R + +vpython3 src/tools/clang/scripts/update.py + +rm -f webrtc + +ln -s src webrtc diff --git a/scripts/patch-vs-toolchain.py b/scripts/patch-vs-toolchain.py new file mode 100644 index 000000000..3988099e2 --- /dev/null +++ b/scripts/patch-vs-toolchain.py @@ -0,0 +1,33 @@ +from pathlib import Path +import sys + + +def main() -> int: + if len(sys.argv) != 2: + print("usage: patch-vs-toolchain.py ", file=sys.stderr) + return 2 + + path = Path(sys.argv[1]) + text = path.read_text(encoding="utf-8") + old = """ else: + raise Exception('%s not found in "%s"\\r\\nYou must install ' + 'Windows 10 SDK version %s including the ' + '"Debugging Tools for Windows" feature.' % + (debug_file, full_path, SDK_VERSION)) +""" + new = """ else: + print('%s not found in "%s"; skipping debugger runtime copy.' % + (debug_file, full_path)) + return +""" + if old not in text: + print(f"expected block not found in {path}", file=sys.stderr) + return 1 + + path.write_text(text.replace(old, new, 1), encoding="utf-8") + print(f"patched {path}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/src/binding.cc b/src/binding.cc index ca6aafafe..db3678ed6 100644 --- a/src/binding.cc +++ b/src/binding.cc @@ -1,69 +1,98 @@ -/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license that can be found - * in the LICENSE.md file in the root of the source tree. All contributing - * project authors may be found in the AUTHORS file in the root of the source - * tree. - */ +/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license that can be found + * in the LICENSE.md file in the root of the source tree. All contributing + * project authors may be found in the AUTHORS file in the root of the source + * tree. + */ #include +#include +#include #include #include - -#include "src/interfaces/media_stream.hh" -#include "src/interfaces/media_stream_track.hh" -#include "src/interfaces/rtc_audio_sink.hh" -#include "src/interfaces/rtc_audio_source.hh" -#include "src/interfaces/rtc_data_channel.hh" -#include "src/interfaces/rtc_dtls_transport.hh" -#include "src/interfaces/rtc_ice_transport.hh" -#include "src/interfaces/rtc_peer_connection.hh" -#include "src/interfaces/rtc_peer_connection/peer_connection_factory.hh" -#include "src/interfaces/rtc_rtp_receiver.hh" -#include "src/interfaces/rtc_rtp_sender.hh" -#include "src/interfaces/rtc_rtp_transceiver.hh" -#include "src/interfaces/rtc_sctp_transport.hh" -#include "src/interfaces/rtc_video_sink.hh" -#include "src/interfaces/rtc_video_source.hh" -#include "src/methods/get_display_media.hh" -#include "src/methods/get_user_media.hh" -#include "src/methods/i420_helpers.hh" -#include "src/node/error_factory.hh" - -#ifdef DEBUG -#include "src/test.hh" -#endif - +#include + +#include "src/interfaces/media_stream.hh" +#include "src/interfaces/media_stream_track.hh" +#include "src/interfaces/rtc_audio_sink.hh" +#include "src/interfaces/rtc_audio_source.hh" +#include "src/interfaces/rtc_data_channel.hh" +#include "src/interfaces/rtc_dtls_transport.hh" +#include "src/interfaces/rtc_ice_transport.hh" +#include "src/interfaces/rtc_peer_connection.hh" +#include "src/interfaces/rtc_peer_connection/peer_connection_factory.hh" +#include "src/interfaces/rtc_rtp_receiver.hh" +#include "src/interfaces/rtc_rtp_sender.hh" +#include "src/interfaces/rtc_rtp_transceiver.hh" +#include "src/interfaces/rtc_sctp_transport.hh" +#include "src/interfaces/rtc_video_sink.hh" +#include "src/interfaces/rtc_video_source.hh" +#include "src/methods/get_display_media.hh" +#include "src/methods/get_user_media.hh" +#include "src/methods/i420_helpers.hh" +#include "src/node/error_factory.hh" + +#ifdef DEBUG +#include "src/test.hh" +#endif + static void dispose(void *) { node_webrtc::PeerConnectionFactory::Dispose(); } -static Napi::Object Init(Napi::Env env, Napi::Object exports) { - node_webrtc::ErrorFactory::Init(env, exports); - node_webrtc::GetDisplayMedia::Init(env, exports); - node_webrtc::GetUserMedia::Init(env, exports); - node_webrtc::I420Helpers::Init(env, exports); - node_webrtc::MediaStream::Init(env, exports); - node_webrtc::MediaStreamTrack::Init(env, exports); - node_webrtc::PeerConnectionFactory::Init(env, exports); - node_webrtc::RTCAudioSink::Init(env, exports); - node_webrtc::RTCAudioSource::Init(env, exports); - node_webrtc::RTCDataChannel::Init(env, exports); - node_webrtc::RTCIceTransport::Init(env, exports); - node_webrtc::RTCDtlsTransport::Init(env, exports); - node_webrtc::RTCPeerConnection::Init(env, exports); - node_webrtc::RTCRtpReceiver::Init(env, exports); - node_webrtc::RTCRtpSender::Init(env, exports); - node_webrtc::RTCRtpTransceiver::Init(env, exports); - node_webrtc::RTCSctpTransport::Init(env, exports); - node_webrtc::RTCVideoSink::Init(env, exports); - node_webrtc::RTCVideoSource::Init(env, exports); -#if DEBUG && defined(__x86_64__) - node_webrtc::Test::Init(env, exports); -#endif - - auto status = napi_add_env_cleanup_hook(env, dispose, nullptr); - assert(status == napi_ok); - (void)status; // Ignore unused variable warning in release builds - - return exports; +static webrtc::LoggingSeverity parseLogSeverity(const char* value) { + if (value == nullptr || *value == '\0') { + return webrtc::LoggingSeverity::LS_INFO; + } + if (std::strcmp(value, "verbose") == 0) { + return webrtc::LoggingSeverity::LS_VERBOSE; + } + if (std::strcmp(value, "warning") == 0) { + return webrtc::LoggingSeverity::LS_WARNING; + } + if (std::strcmp(value, "error") == 0) { + return webrtc::LoggingSeverity::LS_ERROR; + } + if (std::strcmp(value, "none") == 0) { + return webrtc::LoggingSeverity::LS_NONE; + } + return webrtc::LoggingSeverity::LS_INFO; } -NODE_API_MODULE(wrtc_napi, Init) +static Napi::Object Init(Napi::Env env, Napi::Object exports) { + if (const char* nativeLog = std::getenv("WRTC_NATIVE_LOG")) { + const auto severity = parseLogSeverity(nativeLog); + webrtc::LogMessage::SetLogToStderr(true); + webrtc::LogMessage::LogToDebug(severity); + RTC_LOG(LS_INFO) << "[wrtc] native logging enabled"; + } + + node_webrtc::ErrorFactory::Init(env, exports); + node_webrtc::GetDisplayMedia::Init(env, exports); + node_webrtc::GetUserMedia::Init(env, exports); + node_webrtc::I420Helpers::Init(env, exports); + node_webrtc::MediaStream::Init(env, exports); + node_webrtc::MediaStreamTrack::Init(env, exports); + node_webrtc::PeerConnectionFactory::Init(env, exports); + node_webrtc::RTCAudioSink::Init(env, exports); + node_webrtc::RTCAudioSource::Init(env, exports); + node_webrtc::RTCDataChannel::Init(env, exports); + node_webrtc::RTCIceTransport::Init(env, exports); + node_webrtc::RTCDtlsTransport::Init(env, exports); + node_webrtc::RTCPeerConnection::Init(env, exports); + node_webrtc::RTCRtpReceiver::Init(env, exports); + node_webrtc::RTCRtpSender::Init(env, exports); + node_webrtc::RTCRtpTransceiver::Init(env, exports); + node_webrtc::RTCSctpTransport::Init(env, exports); + node_webrtc::RTCVideoSink::Init(env, exports); + node_webrtc::RTCVideoSource::Init(env, exports); +#if DEBUG && defined(__x86_64__) + node_webrtc::Test::Init(env, exports); +#endif + + auto status = napi_add_env_cleanup_hook(env, dispose, nullptr); + assert(status == napi_ok); + (void)status; // Ignore unused variable warning in release builds + + return exports; +} + +NODE_API_MODULE(wrtc_napi, Init) diff --git a/src/dictionaries/node_webrtc/rtc_session_description_init.cc b/src/dictionaries/node_webrtc/rtc_session_description_init.cc index 02df816d0..0f95a9a17 100644 --- a/src/dictionaries/node_webrtc/rtc_session_description_init.cc +++ b/src/dictionaries/node_webrtc/rtc_session_description_init.cc @@ -1,96 +1,98 @@ -#include "src/dictionaries/node_webrtc/rtc_session_description_init.hh" - -#include - -#include -#include - -#include "src/dictionaries/macros/napi.hh" -#include "src/functional/curry.hh" -#include "src/functional/operators.hh" -#include "src/functional/validation.hh" - +#include "src/dictionaries/node_webrtc/rtc_session_description_init.hh" + +#include + +#include +#include + +#include "src/dictionaries/macros/napi.hh" +#include "src/functional/curry.hh" +#include "src/functional/operators.hh" +#include "src/functional/validation.hh" + namespace node_webrtc { #define RTC_SESSION_DESCRIPTION_INIT_FN CreateValidRTCSessionDescriptionInit +static webrtc::SdpType ToWebrtcSdpType(const RTCSdpType type) { + switch (type) { + case RTCSdpType::kOffer: + return webrtc::SdpType::kOffer; + case RTCSdpType::kPrAnswer: + return webrtc::SdpType::kPrAnswer; + case RTCSdpType::kAnswer: + return webrtc::SdpType::kAnswer; + case RTCSdpType::kRollback: + return webrtc::SdpType::kRollback; + } + return webrtc::SdpType::kOffer; +} + static Validation RTC_SESSION_DESCRIPTION_INIT_FN(const RTCSdpType type, const std::string &sdp) { return Pure(CreateRTCSessionDescriptionInit(type, sdp)); } - -TO_NAPI_IMPL(RTCSessionDescriptionInit, pair) { - auto env = pair.first; - Napi::EscapableHandleScope scope(env); - NODE_WEBRTC_CREATE_OBJECT_OR_RETURN(env, object) - NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "type", pair.second.type) - NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "sdp", pair.second.sdp) - return Pure(scope.Escape(object)); -} - + +TO_NAPI_IMPL(RTCSessionDescriptionInit, pair) { + auto env = pair.first; + Napi::EscapableHandleScope scope(env); + NODE_WEBRTC_CREATE_OBJECT_OR_RETURN(env, object) + NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "type", pair.second.type) + NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "sdp", pair.second.sdp) + return Pure(scope.Escape(object)); +} + CONVERTER_IMPL(RTCSessionDescriptionInit, webrtc::SessionDescriptionInterface *, init) { - std::string type_; - switch (init.type) { - case RTCSdpType::kOffer: - type_ = "offer"; - break; - case RTCSdpType::kPrAnswer: - type_ = "pranswer"; - break; - case RTCSdpType::kAnswer: - type_ = "answer"; - break; - case RTCSdpType::kRollback: - type_ = "rollback"; - } webrtc::SdpParseError error; - auto description = webrtc::CreateSessionDescription(type_, init.sdp, &error); + auto description = + webrtc::CreateSessionDescription(ToWebrtcSdpType(init.type), init.sdp, + &error); if (!description) { return Validation::Invalid( error.description); } - return Pure(description); -} - -CONVERTER_IMPL(webrtc::SessionDescriptionInterface *, RTCSessionDescriptionInit, - description) { - return Converter::Convert(description); -} - -CONVERTER_IMPL(const webrtc::SessionDescriptionInterface *, - RTCSessionDescriptionInit, description) { - if (!description) { - return Validation::Invalid( - "RTCSessionDescription is null"); - } - std::string sdp; - if (!description->ToString(&sdp)) { - return Validation::Invalid( - "Failed to print the SDP. This is pretty weird. File a bug on " - "https://github.com/node-webrtc/node-webrtc"); - } - return curry(CreateRTCSessionDescriptionInit) % - From(description->type()) * Pure(sdp); -} - -FROM_NAPI_IMPL(webrtc::SessionDescriptionInterface *, pair) { - return From(pair) - .FlatMap( - Converter::Convert); + return Pure(description.release()); } - -TO_NAPI_IMPL(const webrtc::SessionDescriptionInterface *, pair) { - return From(pair.second) - .FlatMap([env = pair.first](auto value) { - return From(std::make_pair(env, value)); - }); -} - -} // namespace node_webrtc - -#define DICT(X) RTC_SESSION_DESCRIPTION_INIT##X -#include "src/dictionaries/macros/impls.hh" -#undef DICT + +CONVERTER_IMPL(webrtc::SessionDescriptionInterface *, RTCSessionDescriptionInit, + description) { + return Converter::Convert(description); +} + +CONVERTER_IMPL(const webrtc::SessionDescriptionInterface *, + RTCSessionDescriptionInit, description) { + if (!description) { + return Validation::Invalid( + "RTCSessionDescription is null"); + } + std::string sdp; + if (!description->ToString(&sdp)) { + return Validation::Invalid( + "Failed to print the SDP. This is pretty weird. File a bug on " + "https://github.com/node-webrtc/node-webrtc"); + } + return curry(CreateRTCSessionDescriptionInit) % + From(description->type()) * Pure(sdp); +} + +FROM_NAPI_IMPL(webrtc::SessionDescriptionInterface *, pair) { + return From(pair) + .FlatMap( + Converter::Convert); +} + +TO_NAPI_IMPL(const webrtc::SessionDescriptionInterface *, pair) { + return From(pair.second) + .FlatMap([env = pair.first](auto value) { + return From(std::make_pair(env, value)); + }); +} + +} // namespace node_webrtc + +#define DICT(X) RTC_SESSION_DESCRIPTION_INIT##X +#include "src/dictionaries/macros/impls.hh" +#undef DICT diff --git a/src/dictionaries/webrtc/data_channel_init.cc b/src/dictionaries/webrtc/data_channel_init.cc index 250205e37..2735493f0 100644 --- a/src/dictionaries/webrtc/data_channel_init.cc +++ b/src/dictionaries/webrtc/data_channel_init.cc @@ -2,56 +2,57 @@ #include #include +#include #include - -#include - -#include "src/enums/node_webrtc/rtc_priority_type.hh" -#include "src/functional/maybe.hh" -#include "src/functional/validation.hh" - -namespace node_webrtc { - -#define DATA_CHANNEL_INIT_FN CreateDataChannelInit -#define DATA_CHANNEL_INIT_LIST \ - DICT_DEFAULT(bool, ordered, "ordered", true) \ - DICT_OPTIONAL(uint32_t, maxPacketLifeTime, "maxPacketLifeTime") \ - DICT_OPTIONAL(uint32_t, maxRetransmits, "maxRetransmits") \ - DICT_DEFAULT(std::string, protocol, "protocol", "") \ - DICT_DEFAULT(bool, negotiated, "negotiated", false) \ - DICT_OPTIONAL(uint32_t, id, "id") \ - DICT_DEFAULT(RTCPriorityType, priority, "priority", RTCPriorityType::kLow) - -static Validation DATA_CHANNEL_INIT_FN( - const bool ordered, const Maybe maxPacketLifeTime, - const Maybe maxRetransmits, const std::string &protocol, - const bool negotiated, const Maybe id, const RTCPriorityType) { - if (id.FromMaybe(0) > UINT16_MAX) { - return Validation::Invalid( - "id must be between 0 and 65534, inclusive"); - } - if (maxPacketLifeTime.IsJust() && maxRetransmits.IsJust()) { - return Validation::Invalid( - "You cannot set both maxPacketLifeTime and maxRetransmits"); - } + +#include + +#include "src/enums/node_webrtc/rtc_priority_type.hh" +#include "src/functional/maybe.hh" +#include "src/functional/validation.hh" + +namespace node_webrtc { + +#define DATA_CHANNEL_INIT_FN CreateDataChannelInit +#define DATA_CHANNEL_INIT_LIST \ + DICT_DEFAULT(bool, ordered, "ordered", true) \ + DICT_OPTIONAL(uint32_t, maxPacketLifeTime, "maxPacketLifeTime") \ + DICT_OPTIONAL(uint32_t, maxRetransmits, "maxRetransmits") \ + DICT_DEFAULT(std::string, protocol, "protocol", "") \ + DICT_DEFAULT(bool, negotiated, "negotiated", false) \ + DICT_OPTIONAL(uint32_t, id, "id") \ + DICT_DEFAULT(RTCPriorityType, priority, "priority", RTCPriorityType::kLow) + +static Validation DATA_CHANNEL_INIT_FN( + const bool ordered, const Maybe maxPacketLifeTime, + const Maybe maxRetransmits, const std::string &protocol, + const bool negotiated, const Maybe id, const RTCPriorityType) { + if (id.FromMaybe(0) > UINT16_MAX) { + return Validation::Invalid( + "id must be between 0 and 65534, inclusive"); + } + if (maxPacketLifeTime.IsJust() && maxRetransmits.IsJust()) { + return Validation::Invalid( + "You cannot set both maxPacketLifeTime and maxRetransmits"); + } webrtc::DataChannelInit init; init.ordered = ordered; init.maxRetransmitTime = maxPacketLifeTime - .Map([](auto i) { return absl::make_optional(static_cast(i)); }) - .FromMaybe(absl::optional()); + .Map([](auto i) { return std::make_optional(static_cast(i)); }) + .FromMaybe(std::optional()); init.maxRetransmits = maxRetransmits - .Map([](auto i) { return absl::make_optional(static_cast(i)); }) - .FromMaybe(absl::optional()); - init.protocol = protocol; - init.negotiated = negotiated; - init.id = id.Map([](auto i) { return static_cast(i); }).FromMaybe(-1); - return Pure(init); -} - -} // namespace node_webrtc - -#define DICT(X) DATA_CHANNEL_INIT##X -#include "src/dictionaries/macros/impls.hh" -#undef DICT + .Map([](auto i) { return std::make_optional(static_cast(i)); }) + .FromMaybe(std::optional()); + init.protocol = protocol; + init.negotiated = negotiated; + init.id = id.Map([](auto i) { return static_cast(i); }).FromMaybe(-1); + return Pure(init); +} + +} // namespace node_webrtc + +#define DICT(X) DATA_CHANNEL_INIT##X +#include "src/dictionaries/macros/impls.hh" +#undef DICT diff --git a/src/dictionaries/webrtc/ice_candidate_interface.cc b/src/dictionaries/webrtc/ice_candidate_interface.cc index fce5e01f1..d5eacb122 100644 --- a/src/dictionaries/webrtc/ice_candidate_interface.cc +++ b/src/dictionaries/webrtc/ice_candidate_interface.cc @@ -1,145 +1,142 @@ -#include "src/dictionaries/webrtc/ice_candidate_interface.hh" - -#include -#include - -#include -#include -#include -#include - -#include "src/converters.hh" -#include "src/dictionaries/macros/napi.hh" -#include "src/enums/node_webrtc/rtc_ice_candidate_type.hh" -#include "src/enums/node_webrtc/rtc_ice_component.hh" -#include "src/functional/maybe.hh" // IWYU pragma: keep -#include "src/functional/validation.hh" - -namespace node_webrtc { - -#define ICE_CANDIDATE_INTERFACE_FN CreateIceCandidateInterface -#define ICE_CANDIDATE_INTERFACE_LIST \ - DICT_DEFAULT(std::string, candidate, "candidate", "") \ - DICT_DEFAULT(std::string, sdpMid, "sdpMid", "") \ - DICT_DEFAULT(int, sdpMLineIndex, "sdpMLineIndex", 0) \ - DICT_OPTIONAL(std::string, usernameFragment, "usernameFragment") - -static Validation -ICE_CANDIDATE_INTERFACE_FN(const std::string &candidate, - const std::string &sdpMid, const int sdpMLineIndex, - const Maybe &) { - webrtc::SdpParseError error; - // NOLINTBEGIN(readability-suspicious-call-argument) - auto candidate_ = - webrtc::CreateIceCandidate(sdpMid, sdpMLineIndex, candidate, &error); - // NOLINTEND(readability-suspicious-call-argument) - if (!candidate_) { - return Validation::Invalid( - error.description); - } - return Pure(candidate_); -} - -FROM_NAPI_IMPL(std::shared_ptr, napi_value) { - return From(napi_value) - .Map([](auto candidate) { - return std::shared_ptr(candidate); - }); -} - -TO_NAPI_IMPL(webrtc::IceCandidateInterface *, pair) { - auto env = pair.first; - Napi::EscapableHandleScope scope(env); - - auto value = pair.second; - if (!value) { - return Validation::Invalid("RTCIceCandidate is null"); - } - - std::string candidate_string; - if (!value->ToString(&candidate_string)) { - return Validation::Invalid( - "Failed to print the candidate string. This is pretty weird. File a " - "bug on https://github.com/node-webrtc/node-webrtc"); - } - +#include "src/dictionaries/webrtc/ice_candidate_interface.hh" + +#include +#include + +#include +#include +#include +#include + +#include "src/converters.hh" +#include "src/dictionaries/macros/napi.hh" +#include "src/enums/node_webrtc/rtc_ice_candidate_type.hh" +#include "src/enums/node_webrtc/rtc_ice_component.hh" +#include "src/functional/maybe.hh" // IWYU pragma: keep +#include "src/functional/validation.hh" + +namespace node_webrtc { + +#define ICE_CANDIDATE_INTERFACE_FN CreateIceCandidateInterface +#define ICE_CANDIDATE_INTERFACE_LIST \ + DICT_DEFAULT(std::string, candidate, "candidate", "") \ + DICT_DEFAULT(std::string, sdpMid, "sdpMid", "") \ + DICT_DEFAULT(int, sdpMLineIndex, "sdpMLineIndex", 0) \ + DICT_OPTIONAL(std::string, usernameFragment, "usernameFragment") + +static Validation +ICE_CANDIDATE_INTERFACE_FN(const std::string &candidate, + const std::string &sdpMid, const int sdpMLineIndex, + const Maybe &) { + webrtc::SdpParseError error; + // NOLINTBEGIN(readability-suspicious-call-argument) + auto candidate_ = + webrtc::CreateIceCandidate(sdpMid, sdpMLineIndex, candidate, &error); + // NOLINTEND(readability-suspicious-call-argument) + if (!candidate_) { + return Validation::Invalid( + error.description); + } + return Pure(candidate_); +} + +FROM_NAPI_IMPL(std::shared_ptr, napi_value) { + return From(napi_value) + .Map([](auto candidate) { + return std::shared_ptr(candidate); + }); +} + +TO_NAPI_IMPL(webrtc::IceCandidateInterface *, pair) { + auto env = pair.first; + Napi::EscapableHandleScope scope(env); + + auto value = pair.second; + if (!value) { + return Validation::Invalid("RTCIceCandidate is null"); + } + + std::string candidate_string; + if (!value->ToString(&candidate_string)) { + return Validation::Invalid( + "Failed to print the candidate string. This is pretty weird. File a " + "bug on https://github.com/node-webrtc/node-webrtc"); + } + auto candidate = value->candidate(); auto component = candidate.component() == 1 ? RTCIceComponent::kRtp : RTCIceComponent::kRtcp; - const auto &candidate_type = candidate.type(); + const auto candidate_type = candidate.type(); auto type = RTCIceCandidateType::kHost; - if (candidate_type == static_cast(cricket::LOCAL_PORT_TYPE)) { + if (candidate_type == webrtc::IceCandidateType::kHost) { type = RTCIceCandidateType::kHost; - } else if (candidate_type == - static_cast(cricket::STUN_PORT_TYPE)) { + } else if (candidate_type == webrtc::IceCandidateType::kSrflx) { type = RTCIceCandidateType::kSrflx; - } else if (candidate_type == - static_cast(cricket::RELAY_PORT_TYPE)) { + } else if (candidate_type == webrtc::IceCandidateType::kRelay) { type = RTCIceCandidateType::kRelay; - } else if (candidate_type == - static_cast(cricket::PRFLX_PORT_TYPE)) { + } else if (candidate_type == webrtc::IceCandidateType::kPrflx) { type = RTCIceCandidateType::kPrflx; } - - NODE_WEBRTC_CREATE_OBJECT_OR_RETURN(env, object) - NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "candidate", - candidate_string) - - const auto &mid = value->sdp_mid(); - if (mid.empty()) { - object.Set("sdpMid", env.Null()); - } else { - NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "sdpMid", mid) - } - - auto mLineIndex = value->sdp_mline_index(); - if (mLineIndex < 0) { - object.Set("sdpMLineIndex", env.Null()); - } else { - NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "sdpMLineIndex", - mLineIndex) - } - - NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "foundation", - candidate.foundation()) - NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "component", component) - NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "priority", - candidate.priority()) - NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "address", - candidate.address().hostname()) - NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "protocol", - candidate.protocol()) - NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "port", - candidate.address().port()) - NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "type", type) - - const auto &tcpType = candidate.tcptype(); - if (tcpType.empty()) { - object.Set("tcpType", env.Null()); - } else { - NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "tcpType", - candidate.tcptype()) - } - - if (type == RTCIceCandidateType::kHost) { - object.Set("relatedAddress", env.Null()); - object.Set("relatedPort", env.Null()); - } else { - NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN( - env, object, "relatedAddress", candidate.related_address().hostname()) - NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "relatedPort", - candidate.related_address().port()) - } - - NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "usernameFragment", - candidate.username()) - - return Pure(scope.Escape(object)); -} - -} // namespace node_webrtc - -#define DICT(X) ICE_CANDIDATE_INTERFACE##X -#include "src/dictionaries/macros/impls.hh" -#undef DICT + + NODE_WEBRTC_CREATE_OBJECT_OR_RETURN(env, object) + NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "candidate", + candidate_string) + + const auto &mid = value->sdp_mid(); + if (mid.empty()) { + object.Set("sdpMid", env.Null()); + } else { + NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "sdpMid", mid) + } + + auto mLineIndex = value->sdp_mline_index(); + if (mLineIndex < 0) { + object.Set("sdpMLineIndex", env.Null()); + } else { + NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "sdpMLineIndex", + mLineIndex) + } + + NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "foundation", + candidate.foundation()) + NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "component", component) + NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "priority", + candidate.priority()) + NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "address", + candidate.address().hostname()) + NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "protocol", + candidate.protocol()) + NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "port", + candidate.address().port()) + NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "type", type) + + const auto &tcpType = candidate.tcptype(); + if (tcpType.empty()) { + object.Set("tcpType", env.Null()); + } else { + NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "tcpType", + candidate.tcptype()) + } + + if (type == RTCIceCandidateType::kHost) { + object.Set("relatedAddress", env.Null()); + object.Set("relatedPort", env.Null()); + } else { + NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN( + env, object, "relatedAddress", candidate.related_address().hostname()) + NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "relatedPort", + candidate.related_address().port()) + } + + NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "usernameFragment", + candidate.username()) + + return Pure(scope.Escape(object)); +} + +} // namespace node_webrtc + +#define DICT(X) ICE_CANDIDATE_INTERFACE##X +#include "src/dictionaries/macros/impls.hh" +#undef DICT diff --git a/src/dictionaries/webrtc/ice_candidate_interface.hh b/src/dictionaries/webrtc/ice_candidate_interface.hh index 6e601d2f0..7ede080e9 100644 --- a/src/dictionaries/webrtc/ice_candidate_interface.hh +++ b/src/dictionaries/webrtc/ice_candidate_interface.hh @@ -2,20 +2,18 @@ #include -#include "src/converters/napi.hh" +#include -namespace webrtc { -class IceCandidateInterface; -} +#include "src/converters/napi.hh" #define ICE_CANDIDATE_INTERFACE webrtc::IceCandidateInterface * - -#define DICT(X) ICE_CANDIDATE_INTERFACE##X -#include "src/dictionaries/macros/decls.hh" -#undef DICT - -namespace node_webrtc { - -DECLARE_FROM_NAPI(std::shared_ptr) - -} // namespace node_webrtc + +#define DICT(X) ICE_CANDIDATE_INTERFACE##X +#include "src/dictionaries/macros/decls.hh" +#undef DICT + +namespace node_webrtc { + +DECLARE_FROM_NAPI(std::shared_ptr) + +} // namespace node_webrtc diff --git a/src/dictionaries/webrtc/rtc_stats.cc b/src/dictionaries/webrtc/rtc_stats.cc index e3d361cfb..bc0ff07d9 100644 --- a/src/dictionaries/webrtc/rtc_stats.cc +++ b/src/dictionaries/webrtc/rtc_stats.cc @@ -1,34 +1,35 @@ -#include "src/dictionaries/webrtc/rtc_stats.hh" - -#include -#include - -#include -#include - -#include "src/dictionaries/macros/napi.hh" -#include "src/dictionaries/webrtc/rtc_stats_member_interface.hh" // IWYU pragma: keep -#include "src/functional/validation.hh" - -namespace node_webrtc { - -TO_NAPI_IMPL(const webrtc::RTCStats *, pair) { - auto env = pair.first; - Napi::EscapableHandleScope scope(env); - auto value = pair.second; - NODE_WEBRTC_CREATE_OBJECT_OR_RETURN(env, stats) - NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, stats, "id", value->id()) - NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN( - env, stats, "timestamp", - static_cast(value->timestamp().us()) / 1000.0) - NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, stats, "type", - std::string(value->type())) - for (const webrtc::RTCStatsMemberInterface *member : value->Members()) { - if (member->is_defined()) { - NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, stats, member->name(), member) +#include "src/dictionaries/webrtc/rtc_stats.hh" + +#include +#include + +#include +#include + +#include "src/dictionaries/macros/napi.hh" +#include "src/dictionaries/webrtc/rtc_stats_member_interface.hh" // IWYU pragma: keep +#include "src/functional/validation.hh" + +namespace node_webrtc { + +TO_NAPI_IMPL(const webrtc::RTCStats *, pair) { + auto env = pair.first; + Napi::EscapableHandleScope scope(env); + auto value = pair.second; + NODE_WEBRTC_CREATE_OBJECT_OR_RETURN(env, stats) + NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, stats, "id", value->id()) + NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN( + env, stats, "timestamp", + static_cast(value->timestamp().us()) / 1000.0) + NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, stats, "type", + std::string(value->type())) + for (const auto &attribute : value->Attributes()) { + if (attribute.has_value()) { + NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, stats, attribute.name(), + &attribute) } } - return Pure(scope.Escape(stats)); -} - -} // namespace node_webrtc + return Pure(scope.Escape(stats)); +} + +} // namespace node_webrtc diff --git a/src/dictionaries/webrtc/rtc_stats_member_interface.cc b/src/dictionaries/webrtc/rtc_stats_member_interface.cc index 748c79bc6..b39e1359d 100644 --- a/src/dictionaries/webrtc/rtc_stats_member_interface.cc +++ b/src/dictionaries/webrtc/rtc_stats_member_interface.cc @@ -1,86 +1,86 @@ #include "src/dictionaries/webrtc/rtc_stats_member_interface.hh" #include -#include +#include #include -#include #include #include -#include +#include #include "src/converters.hh" namespace node_webrtc { -TO_NAPI_IMPL(const webrtc::RTCStatsMemberInterface *, pair) { +TO_NAPI_IMPL(const webrtc::Attribute *, pair) { auto env = pair.first; auto value = pair.second; - switch (value->type()) { - case webrtc::RTCStatsMemberInterface::Type::kBool: // bool + + if (!value || !value->has_value()) { + return Validation::Invalid("RTC stats attribute is undefined"); + } + + if (value->holds_alternative()) { + return From(std::make_pair(env, value->get())); + } + if (value->holds_alternative()) { + return From(std::make_pair(env, value->get())); + } + if (value->holds_alternative()) { + return From(std::make_pair(env, value->get())); + } + if (value->holds_alternative()) { + return From(std::make_pair(env, value->get())); + } + if (value->holds_alternative()) { + return From(std::make_pair(env, value->get())); + } + if (value->holds_alternative()) { + return From(std::make_pair(env, value->get())); + } + if (value->holds_alternative()) { + return From(std::make_pair(env, value->get())); + } + if (value->holds_alternative>()) { return From( - std::make_pair(env, *value->cast_to>())); - case webrtc::RTCStatsMemberInterface::Type::kInt32: // int32_t - return From(std::make_pair( - env, *value->cast_to>())); - case webrtc::RTCStatsMemberInterface::Type::kUint32: // uint32_t - return From(std::make_pair( - env, *value->cast_to>())); - case webrtc::RTCStatsMemberInterface::Type::kInt64: // int64_t - return From(std::make_pair( - env, *value->cast_to>())); - case webrtc::RTCStatsMemberInterface::Type::kUint64: // uint64_t - return From(std::make_pair( - env, *value->cast_to>())); - case webrtc::RTCStatsMemberInterface::Type::kDouble: // double + std::make_pair(env, value->get>())); + } + if (value->holds_alternative>()) { return From( - std::make_pair(env, *value->cast_to>())); - case webrtc::RTCStatsMemberInterface::Type::kString: // std::string - return From(std::make_pair( - env, *value->cast_to>())); - case webrtc::RTCStatsMemberInterface::Type:: - kSequenceBool: // std::vector - return From(std::make_pair( - env, *value->cast_to>>())); - case webrtc::RTCStatsMemberInterface::Type:: - kSequenceInt32: // std::vector - return From(std::make_pair( - env, *value->cast_to>>())); - case webrtc::RTCStatsMemberInterface::Type:: - kSequenceUint32: // std::vector - return From(std::make_pair( - env, *value->cast_to>>())); - case webrtc::RTCStatsMemberInterface::Type:: - kSequenceInt64: // std::vector - return From(std::make_pair( - env, *value->cast_to>>())); - case webrtc::RTCStatsMemberInterface::Type:: - kSequenceUint64: // std::vector - return From(std::make_pair( - env, *value->cast_to>>())); - case webrtc::RTCStatsMemberInterface::Type:: - kSequenceDouble: // std::vector - return From(std::make_pair( - env, *value->cast_to>>())); - case webrtc::RTCStatsMemberInterface::Type:: - kSequenceString: // std::vector - return From(std::make_pair( - env, - *value->cast_to>>())); - - case webrtc::RTCStatsMemberInterface::Type::kMapStringUint64: - return From(std::make_pair( - env, *value->cast_to< - webrtc::RTCStatsMember>>())); - case webrtc::RTCStatsMemberInterface::Type::kMapStringDouble: - return From(std::make_pair( - env, *value->cast_to< - webrtc::RTCStatsMember>>())); - default: - return Validation::Invalid( - "RTCStatsMemberInterface type not supported, file a bug against " - "node-webrtc"); + std::make_pair(env, value->get>())); + } + if (value->holds_alternative>()) { + return From( + std::make_pair(env, value->get>())); } + if (value->holds_alternative>()) { + return From( + std::make_pair(env, value->get>())); + } + if (value->holds_alternative>()) { + return From( + std::make_pair(env, value->get>())); + } + if (value->holds_alternative>()) { + return From( + std::make_pair(env, value->get>())); + } + if (value->holds_alternative>()) { + return From( + std::make_pair(env, value->get>())); + } + if (value->holds_alternative>()) { + return From( + std::make_pair(env, value->get>())); + } + if (value->holds_alternative>()) { + return From( + std::make_pair(env, value->get>())); + } + + return Validation::Invalid( + "RTC stats attribute type not supported, file a bug against " + "node-webrtc"); } } // namespace node_webrtc diff --git a/src/dictionaries/webrtc/rtc_stats_member_interface.hh b/src/dictionaries/webrtc/rtc_stats_member_interface.hh index 7255485f0..f87fbb85f 100644 --- a/src/dictionaries/webrtc/rtc_stats_member_interface.hh +++ b/src/dictionaries/webrtc/rtc_stats_member_interface.hh @@ -3,11 +3,11 @@ #include "src/converters/napi.hh" namespace webrtc { -class RTCStatsMemberInterface; +class Attribute; } namespace node_webrtc { -DECLARE_TO_NAPI(const webrtc::RTCStatsMemberInterface *) +DECLARE_TO_NAPI(const webrtc::Attribute *) } // namespace node_webrtc diff --git a/src/dictionaries/webrtc/rtc_stats_report.cc b/src/dictionaries/webrtc/rtc_stats_report.cc index 60fcb2457..4869d534f 100644 --- a/src/dictionaries/webrtc/rtc_stats_report.cc +++ b/src/dictionaries/webrtc/rtc_stats_report.cc @@ -1,79 +1,79 @@ -#include "src/dictionaries/webrtc/rtc_stats_report.hh" - -#include -#include // IWYU pragma: keep -#include // IWYU pragma: keep - -#include "src/converters/object.hh" -#include "src/dictionaries/webrtc/rtc_stats.hh" // IWYU pragma: keep -#include "src/functional/validation.hh" - -namespace node_webrtc { - -static Validation CreateMap(Napi::Env env) { - return GetRequired(env.Global(), "Map") - .FlatMap([](auto mapConstructor) { - auto env = mapConstructor.Env(); - Napi::EscapableHandleScope scope(env); - auto map = mapConstructor.New({}); - if (env.IsExceptionPending()) { - return Validation::Invalid( - env.GetAndClearPendingException().Message()); - } - return Pure(scope.Escape(map).template As()); - }); -} - -static Maybe SetMap(Napi::Object map, Napi::Value key, - Napi::Value value) { - return GetRequired(map.Env().Global(), "Map") - .FlatMap([](auto mapConstructor) { - return GetRequired(mapConstructor, "prototype"); - }) - .FlatMap([](auto mapPrototype) { - return GetRequired(mapPrototype, "set"); - }) - .FlatMap>([map, key, value](auto set) { - auto env = map.Env(); - Napi::HandleScope scope(env); - set.Call(map, {key, value}); - if (env.IsExceptionPending()) { - return Validation>::Invalid( - env.GetAndClearPendingException().Message()); - } - return Pure(MakeNothing()); - }) - .FromValidation([](auto errors) { return MakeJust(errors); }); -} - -template -static Maybe DoSet(Napi::Object map, std::string const &key, T value) { - auto env = map.Env(); - Napi::HandleScope scope(env); - auto maybeKey = From(std::make_pair(env, key)); - if (maybeKey.IsInvalid()) { - return MakeJust(maybeKey.ToErrors()); - } - auto maybeValue = From(std::make_pair(env, value)); - if (maybeValue.IsInvalid()) { - return MakeJust(maybeValue.ToErrors()); - } - return SetMap(map, maybeKey.UnsafeFromValid(), maybeValue.UnsafeFromValid()); -} - -TO_NAPI_IMPL(rtc::scoped_refptr, pair) { - return CreateMap(pair.first) - .FlatMap([value = pair.second](auto map) { - auto env = map.Env(); - Napi::EscapableHandleScope scope(env); - for (const webrtc::RTCStats &stats : *value) { - auto result = DoSet(map, stats.id(), &stats); - if (result.IsJust()) { - return Validation::Invalid(result.UnsafeFromJust()); - } - } - return Pure(scope.Escape(map)); - }); -} - -} // namespace node_webrtc +#include "src/dictionaries/webrtc/rtc_stats_report.hh" + +#include +#include // IWYU pragma: keep +#include // IWYU pragma: keep + +#include "src/converters/object.hh" +#include "src/dictionaries/webrtc/rtc_stats.hh" // IWYU pragma: keep +#include "src/functional/validation.hh" + +namespace node_webrtc { + +static Validation CreateMap(Napi::Env env) { + return GetRequired(env.Global(), "Map") + .FlatMap([](auto mapConstructor) { + auto env = mapConstructor.Env(); + Napi::EscapableHandleScope scope(env); + auto map = mapConstructor.New({}); + if (env.IsExceptionPending()) { + return Validation::Invalid( + env.GetAndClearPendingException().Message()); + } + return Pure(scope.Escape(map).template As()); + }); +} + +static Maybe SetMap(Napi::Object map, Napi::Value key, + Napi::Value value) { + return GetRequired(map.Env().Global(), "Map") + .FlatMap([](auto mapConstructor) { + return GetRequired(mapConstructor, "prototype"); + }) + .FlatMap([](auto mapPrototype) { + return GetRequired(mapPrototype, "set"); + }) + .FlatMap>([map, key, value](auto set) { + auto env = map.Env(); + Napi::HandleScope scope(env); + set.Call(map, {key, value}); + if (env.IsExceptionPending()) { + return Validation>::Invalid( + env.GetAndClearPendingException().Message()); + } + return Pure(MakeNothing()); + }) + .FromValidation([](auto errors) { return MakeJust(errors); }); +} + +template +static Maybe DoSet(Napi::Object map, std::string const &key, T value) { + auto env = map.Env(); + Napi::HandleScope scope(env); + auto maybeKey = From(std::make_pair(env, key)); + if (maybeKey.IsInvalid()) { + return MakeJust(maybeKey.ToErrors()); + } + auto maybeValue = From(std::make_pair(env, value)); + if (maybeValue.IsInvalid()) { + return MakeJust(maybeValue.ToErrors()); + } + return SetMap(map, maybeKey.UnsafeFromValid(), maybeValue.UnsafeFromValid()); +} + +TO_NAPI_IMPL(webrtc::scoped_refptr, pair) { + return CreateMap(pair.first) + .FlatMap([value = pair.second](auto map) { + auto env = map.Env(); + Napi::EscapableHandleScope scope(env); + for (const webrtc::RTCStats &stats : *value) { + auto result = DoSet(map, stats.id(), &stats); + if (result.IsJust()) { + return Validation::Invalid(result.UnsafeFromJust()); + } + } + return Pure(scope.Escape(map)); + }); +} + +} // namespace node_webrtc diff --git a/src/dictionaries/webrtc/rtc_stats_report.hh b/src/dictionaries/webrtc/rtc_stats_report.hh index 948e9e837..865d8aa7e 100644 --- a/src/dictionaries/webrtc/rtc_stats_report.hh +++ b/src/dictionaries/webrtc/rtc_stats_report.hh @@ -2,15 +2,13 @@ #include "src/converters/napi.hh" -namespace rtc { -template class scoped_refptr; -} namespace webrtc { class RTCStatsReport; +template class scoped_refptr; } namespace node_webrtc { -DECLARE_TO_NAPI(rtc::scoped_refptr) +DECLARE_TO_NAPI(webrtc::scoped_refptr) } // namespace node_webrtc diff --git a/src/dictionaries/webrtc/rtp_codec_capability.cc b/src/dictionaries/webrtc/rtp_codec_capability.cc index 1df4bdc60..043179d7a 100644 --- a/src/dictionaries/webrtc/rtp_codec_capability.cc +++ b/src/dictionaries/webrtc/rtp_codec_capability.cc @@ -1,92 +1,93 @@ -#include "src/dictionaries/webrtc/rtp_codec_capability.hh" - +#include "src/dictionaries/webrtc/rtp_codec_capability.hh" + #include #include #include #include +#include #include - -#include "src/converters.hh" -#include "src/converters/object.hh" -#include "src/dictionaries/macros/napi.hh" -#include "src/functional/curry.hh" -#include "src/functional/maybe.hh" -#include "src/functional/operators.hh" -#include "src/functional/validation.hh" - -namespace node_webrtc { - -TO_NAPI_IMPL(webrtc::RtpCodecCapability, pair) { - auto env = pair.first; - Napi::EscapableHandleScope scope(env); - auto capabilities = pair.second; - NODE_WEBRTC_CREATE_OBJECT_OR_RETURN(env, object) - NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "mimeType", - capabilities.mime_type()) - if (capabilities.clock_rate) { - NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "clockRate", - *capabilities.clock_rate) - } - if (capabilities.num_channels) { - NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "channels", - *capabilities.num_channels) - } - if (!capabilities.parameters.empty()) { - std::string fmtp; - uint64_t i = 0; - for (const auto ¶m : capabilities.parameters) { - fmtp += param.first + "=" + param.second; - if (i < capabilities.parameters.size() - 1) { - fmtp += ";"; - } - i++; - } - NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "sdpFmtpLine", fmtp) - } - return Pure(scope.Escape(object)); -} - -static webrtc::RtpCodecCapability -CreateRtpCodecCapability(std::string const &mimeType, uint64_t clockRate, - Maybe channels, - Maybe const &maybeSdpFmtpLine) { + +#include "src/converters.hh" +#include "src/converters/object.hh" +#include "src/dictionaries/macros/napi.hh" +#include "src/functional/curry.hh" +#include "src/functional/maybe.hh" +#include "src/functional/operators.hh" +#include "src/functional/validation.hh" + +namespace node_webrtc { + +TO_NAPI_IMPL(webrtc::RtpCodecCapability, pair) { + auto env = pair.first; + Napi::EscapableHandleScope scope(env); + auto capabilities = pair.second; + NODE_WEBRTC_CREATE_OBJECT_OR_RETURN(env, object) + NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "mimeType", + capabilities.mime_type()) + if (capabilities.clock_rate) { + NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "clockRate", + *capabilities.clock_rate) + } + if (capabilities.num_channels) { + NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "channels", + *capabilities.num_channels) + } + if (!capabilities.parameters.empty()) { + std::string fmtp; + uint64_t i = 0; + for (const auto ¶m : capabilities.parameters) { + fmtp += param.first + "=" + param.second; + if (i < capabilities.parameters.size() - 1) { + fmtp += ";"; + } + i++; + } + NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "sdpFmtpLine", fmtp) + } + return Pure(scope.Escape(object)); +} + +static webrtc::RtpCodecCapability +CreateRtpCodecCapability(std::string const &mimeType, uint64_t clockRate, + Maybe channels, + Maybe const &maybeSdpFmtpLine) { webrtc::RtpCodecCapability result; auto indexOfSlash = mimeType.find('/'); auto kindString = mimeType.substr(0, indexOfSlash); auto nameString = mimeType.substr(indexOfSlash + 1); - result.kind = kindString == "audio" ? cricket::MEDIA_TYPE_AUDIO - : cricket::MEDIA_TYPE_VIDEO; - result.name = nameString; - result.clock_rate = clockRate; - if (channels.IsJust()) { - result.num_channels = channels.UnsafeFromJust(); - } - if (maybeSdpFmtpLine.IsJust()) { - auto sdpFmtpLine = maybeSdpFmtpLine.UnsafeFromJust() + ";"; - size_t pos = 0; - std::string keyValue; - while ((pos = sdpFmtpLine.find(";")) != std::string::npos) { - keyValue = sdpFmtpLine.substr(0, pos); - sdpFmtpLine.erase(0, pos + 1); - auto indexOfEquals = keyValue.find('='); - auto key = keyValue.substr(0, indexOfEquals); - auto value = keyValue.substr(indexOfEquals + 1); - result.parameters[key] = value; - } - } - return result; -} - -FROM_NAPI_IMPL(webrtc::RtpCodecCapability, value) { - return From(value).FlatMap( - [](auto object) { - return curry(CreateRtpCodecCapability) % - GetRequired(object, "mimeType") * - GetRequired(object, "clockRate") * - GetOptional(object, "channels") * - GetOptional(object, "sdpFmtpLine"); - }); -} - -} // namespace node_webrtc + result.kind = kindString == "audio" ? webrtc::MediaType::AUDIO + : webrtc::MediaType::VIDEO; + result.name = nameString; + result.clock_rate = clockRate; + if (channels.IsJust()) { + result.num_channels = channels.UnsafeFromJust(); + } + if (maybeSdpFmtpLine.IsJust()) { + auto sdpFmtpLine = maybeSdpFmtpLine.UnsafeFromJust() + ";"; + size_t pos = 0; + std::string keyValue; + while ((pos = sdpFmtpLine.find(";")) != std::string::npos) { + keyValue = sdpFmtpLine.substr(0, pos); + sdpFmtpLine.erase(0, pos + 1); + auto indexOfEquals = keyValue.find('='); + auto key = keyValue.substr(0, indexOfEquals); + auto value = keyValue.substr(indexOfEquals + 1); + result.parameters[key] = value; + } + } + return result; +} + +FROM_NAPI_IMPL(webrtc::RtpCodecCapability, value) { + return From(value).FlatMap( + [](auto object) { + return curry(CreateRtpCodecCapability) % + GetRequired(object, "mimeType") * + GetRequired(object, "clockRate") * + GetOptional(object, "channels") * + GetOptional(object, "sdpFmtpLine"); + }); +} + +} // namespace node_webrtc diff --git a/src/dictionaries/webrtc/rtp_codec_parameters.cc b/src/dictionaries/webrtc/rtp_codec_parameters.cc index a6a815ba8..f176b2da2 100644 --- a/src/dictionaries/webrtc/rtp_codec_parameters.cc +++ b/src/dictionaries/webrtc/rtp_codec_parameters.cc @@ -1,99 +1,100 @@ -#include "src/dictionaries/webrtc/rtp_codec_parameters.hh" - -#include -#include -#include -#include -#include - +#include "src/dictionaries/webrtc/rtp_codec_parameters.hh" + +#include +#include +#include +#include +#include + #include #include +#include #include - -#include "src/converters.hh" -#include "src/converters/object.hh" -#include "src/dictionaries/macros/napi.hh" -#include "src/functional/curry.hh" -#include "src/functional/maybe.hh" -#include "src/functional/operators.hh" -#include "src/functional/validation.hh" - -namespace node_webrtc { - -TO_NAPI_IMPL(webrtc::RtpCodecParameters, pair) { - auto env = pair.first; - Napi::EscapableHandleScope scope(env); - auto params = pair.second; - NODE_WEBRTC_CREATE_OBJECT_OR_RETURN(env, object) - NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "payloadType", - params.payload_type) - NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "mimeType", - params.mime_type()) - if (params.clock_rate) { - NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "clockRate", - *params.clock_rate) - } - if (params.num_channels) { - NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "channels", - *params.num_channels) - } - if (!params.parameters.empty()) { - std::string fmtp("a=fmtp:" + std::to_string(params.payload_type)); - uint64_t i = 0; - for (const auto ¶m : params.parameters) { - fmtp += " " + param.first + "=" + param.second; - if (i < params.parameters.size() - 1) { - fmtp += ";"; - } - i++; - } - NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "sdpFmtpLine", fmtp) - } - return Pure(scope.Escape(object)); -} - -static webrtc::RtpCodecParameters NapiToRtpCodecParameters( - const uint8_t payloadType, const std::string &mimeType, - const uint64_t clockRate, const node_webrtc::Maybe channels, - const node_webrtc::Maybe &maybeSdpFmtpLine) { + +#include "src/converters.hh" +#include "src/converters/object.hh" +#include "src/dictionaries/macros/napi.hh" +#include "src/functional/curry.hh" +#include "src/functional/maybe.hh" +#include "src/functional/operators.hh" +#include "src/functional/validation.hh" + +namespace node_webrtc { + +TO_NAPI_IMPL(webrtc::RtpCodecParameters, pair) { + auto env = pair.first; + Napi::EscapableHandleScope scope(env); + auto params = pair.second; + NODE_WEBRTC_CREATE_OBJECT_OR_RETURN(env, object) + NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "payloadType", + params.payload_type) + NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "mimeType", + params.mime_type()) + if (params.clock_rate) { + NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "clockRate", + *params.clock_rate) + } + if (params.num_channels) { + NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "channels", + *params.num_channels) + } + if (!params.parameters.empty()) { + std::string fmtp("a=fmtp:" + std::to_string(params.payload_type)); + uint64_t i = 0; + for (const auto ¶m : params.parameters) { + fmtp += " " + param.first + "=" + param.second; + if (i < params.parameters.size() - 1) { + fmtp += ";"; + } + i++; + } + NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "sdpFmtpLine", fmtp) + } + return Pure(scope.Escape(object)); +} + +static webrtc::RtpCodecParameters NapiToRtpCodecParameters( + const uint8_t payloadType, const std::string &mimeType, + const uint64_t clockRate, const node_webrtc::Maybe channels, + const node_webrtc::Maybe &maybeSdpFmtpLine) { webrtc::RtpCodecParameters result; auto indexOfSlash = mimeType.find('/'); auto kindString = mimeType.substr(0, indexOfSlash); auto nameString = mimeType.substr(indexOfSlash + 1); - result.kind = kindString == "audio" ? cricket::MEDIA_TYPE_AUDIO - : cricket::MEDIA_TYPE_VIDEO; - result.name = nameString; - result.payload_type = payloadType; - result.clock_rate = clockRate; - if (channels.IsJust()) { - result.num_channels = channels.UnsafeFromJust(); - } - if (maybeSdpFmtpLine.IsJust()) { - auto sdpFmtpLine = maybeSdpFmtpLine.UnsafeFromJust() + ";"; - size_t pos = 0; - std::string keyValue; - while ((pos = sdpFmtpLine.find(";")) != std::string::npos) { - keyValue = sdpFmtpLine.substr(0, pos); - sdpFmtpLine.erase(0, pos + 1); - auto indexOfEquals = keyValue.find('='); - auto key = keyValue.substr(0, indexOfEquals); - auto value = keyValue.substr(indexOfEquals + 1); - result.parameters[key] = value; - } - } - return result; -} - -FROM_NAPI_IMPL(webrtc::RtpCodecParameters, value) { - return From(value).FlatMap( - [](auto object) { - return curry(NapiToRtpCodecParameters) % - GetRequired(object, "payloadType") * - GetRequired(object, "mimeType") * - GetRequired(object, "clockRate") * - GetOptional(object, "channels") * - GetOptional(object, "sdpFmtpLine"); - }); -} - -} // namespace node_webrtc + result.kind = kindString == "audio" ? webrtc::MediaType::AUDIO + : webrtc::MediaType::VIDEO; + result.name = nameString; + result.payload_type = payloadType; + result.clock_rate = clockRate; + if (channels.IsJust()) { + result.num_channels = channels.UnsafeFromJust(); + } + if (maybeSdpFmtpLine.IsJust()) { + auto sdpFmtpLine = maybeSdpFmtpLine.UnsafeFromJust() + ";"; + size_t pos = 0; + std::string keyValue; + while ((pos = sdpFmtpLine.find(";")) != std::string::npos) { + keyValue = sdpFmtpLine.substr(0, pos); + sdpFmtpLine.erase(0, pos + 1); + auto indexOfEquals = keyValue.find('='); + auto key = keyValue.substr(0, indexOfEquals); + auto value = keyValue.substr(indexOfEquals + 1); + result.parameters[key] = value; + } + } + return result; +} + +FROM_NAPI_IMPL(webrtc::RtpCodecParameters, value) { + return From(value).FlatMap( + [](auto object) { + return curry(NapiToRtpCodecParameters) % + GetRequired(object, "payloadType") * + GetRequired(object, "mimeType") * + GetRequired(object, "clockRate") * + GetOptional(object, "channels") * + GetOptional(object, "sdpFmtpLine"); + }); +} + +} // namespace node_webrtc diff --git a/src/dictionaries/webrtc/rtp_source.cc b/src/dictionaries/webrtc/rtp_source.cc index 399b7266a..bc55b4b78 100644 --- a/src/dictionaries/webrtc/rtp_source.cc +++ b/src/dictionaries/webrtc/rtp_source.cc @@ -1,25 +1,25 @@ -#include "src/dictionaries/webrtc/rtp_source.hh" - -#include - -#include -#include - -#include "src/dictionaries/macros/napi.hh" -#include "src/functional/validation.hh" - -namespace node_webrtc { - -TO_NAPI_IMPL(webrtc::RtpSource, pair) { +#include "src/dictionaries/webrtc/rtp_source.hh" + +#include + +#include +#include + +#include "src/dictionaries/macros/napi.hh" +#include "src/functional/validation.hh" + +namespace node_webrtc { + +TO_NAPI_IMPL(webrtc::RtpSource, pair) { auto env = pair.first; Napi::EscapableHandleScope scope(env); auto source = pair.second; NODE_WEBRTC_CREATE_OBJECT_OR_RETURN(env, object) NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "timestamp", - source.timestamp_ms()) + source.timestamp().ms()) NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "source", source.source_id()) return Pure(scope.Escape(object)); } - -} // namespace node_webrtc + +} // namespace node_webrtc diff --git a/src/dictionaries/webrtc/video_frame_buffer.cc b/src/dictionaries/webrtc/video_frame_buffer.cc index a0d1f3e2c..feaf8d71a 100644 --- a/src/dictionaries/webrtc/video_frame_buffer.cc +++ b/src/dictionaries/webrtc/video_frame_buffer.cc @@ -1,91 +1,93 @@ -#include "src/dictionaries/webrtc/video_frame_buffer.hh" - -#include -#include - -#include "src/dictionaries/node_webrtc/image_data.hh" -#include "src/functional/validation.hh" - -namespace node_webrtc { - -static rtc::scoped_refptr +#include "src/dictionaries/webrtc/video_frame_buffer.hh" + +#include +#include + +#include "src/dictionaries/node_webrtc/image_data.hh" +#include "src/functional/validation.hh" + +namespace node_webrtc { + +static webrtc::scoped_refptr CreateI420Buffer(I420ImageData i420Frame) { - auto buffer = - webrtc::I420Buffer::Create(i420Frame.width(), i420Frame.height()); - memcpy(buffer->MutableDataY(), i420Frame.dataY().data(), - i420Frame.dataY().size_bytes()); - memcpy(buffer->MutableDataU(), i420Frame.dataU().data(), - i420Frame.dataU().size_bytes()); - memcpy(buffer->MutableDataV(), i420Frame.dataV().data(), - i420Frame.dataV().size_bytes()); - return buffer; -} - -CONVERTER_IMPL(I420ImageData, rtc::scoped_refptr, value) { + auto buffer = + webrtc::I420Buffer::Create(i420Frame.width(), i420Frame.height()); + memcpy(buffer->MutableDataY(), i420Frame.dataY().data(), + i420Frame.dataY().size_bytes()); + memcpy(buffer->MutableDataU(), i420Frame.dataU().data(), + i420Frame.dataU().size_bytes()); + memcpy(buffer->MutableDataV(), i420Frame.dataV().data(), + i420Frame.dataV().size_bytes()); + return buffer; +} + +CONVERTER_IMPL(I420ImageData, webrtc::scoped_refptr, + value) { return Pure(CreateI420Buffer(value)); } -TO_NAPI_IMPL(rtc::scoped_refptr, pair) { - auto value = pair.second; - return value->type() == webrtc::VideoFrameBuffer::Type::kI420 - ? From(std::make_pair(pair.first, value->GetI420())) - : Validation::Invalid( - "Unsupported RTCVideoFrame type (file a node-webrtc bug, " - "please!)"); -} - -CONVERT_VIA(Napi::Value, I420ImageData, rtc::scoped_refptr) - -TO_NAPI_IMPL(const webrtc::I420BufferInterface *, pair) { - auto env = pair.first; - Napi::EscapableHandleScope scope(env); - auto value = pair.second; - - auto dstYStride = value->width(); - auto dstUVStride = (value->width() + 1) / 2; - auto uvHeight = (value->height() + 1) / 2; - - auto sizeOfDstYPlane = dstYStride * value->height(); - auto sizeOfDstUPlane = dstUVStride * uvHeight; - auto sizeOfDstVPlane = dstUVStride * uvHeight; - - auto byteLength = sizeOfDstYPlane + sizeOfDstUPlane + sizeOfDstVPlane; - auto maybeArrayBuffer = Napi::ArrayBuffer::New(env, byteLength); - if (maybeArrayBuffer.Env().IsExceptionPending()) { - return Validation::Invalid( - maybeArrayBuffer.Env().GetAndClearPendingException().Message()); - } - auto data = static_cast(maybeArrayBuffer.Data()); - - auto srcYPlane = value->DataY(); - auto srcUPlane = value->DataU(); - auto srcVPlane = value->DataV(); - -#pragma clang unsafe_buffer_usage begin - auto dstYPlane = data; - auto dstUPlane = data + sizeOfDstYPlane; - auto dstVPlane = dstUPlane + sizeOfDstUPlane; -#pragma clang unsafe_buffer_usage end - - int rc = libyuv::I420Copy( - srcYPlane, value->StrideY(), srcUPlane, value->StrideU(), srcVPlane, - value->StrideV(), dstYPlane, dstYStride, dstUPlane, dstUVStride, - dstVPlane, dstUVStride, value->width(), value->height()); - - if (rc != 0) { - return Validation::Invalid("Failed to copy I420 buffer: " + - std::to_string(rc)); - } - - // FIXME(mroberts): How to create a Uint8ClampedArray? - auto maybeUint8Array = - Napi::Uint8Array::New(env, byteLength, maybeArrayBuffer, 0); - if (maybeUint8Array.Env().IsExceptionPending()) { - return Validation::Invalid( - maybeUint8Array.Env().GetAndClearPendingException().Message()); - } - - return Pure(scope.Escape(maybeUint8Array)); -} - -} // namespace node_webrtc +TO_NAPI_IMPL(webrtc::scoped_refptr, pair) { + auto value = pair.second; + return value->type() == webrtc::VideoFrameBuffer::Type::kI420 + ? From(std::make_pair(pair.first, value->GetI420())) + : Validation::Invalid( + "Unsupported RTCVideoFrame type (file a node-webrtc bug, " + "please!)"); +} + +CONVERT_VIA(Napi::Value, I420ImageData, + webrtc::scoped_refptr) + +TO_NAPI_IMPL(const webrtc::I420BufferInterface *, pair) { + auto env = pair.first; + Napi::EscapableHandleScope scope(env); + auto value = pair.second; + + auto dstYStride = value->width(); + auto dstUVStride = (value->width() + 1) / 2; + auto uvHeight = (value->height() + 1) / 2; + + auto sizeOfDstYPlane = dstYStride * value->height(); + auto sizeOfDstUPlane = dstUVStride * uvHeight; + auto sizeOfDstVPlane = dstUVStride * uvHeight; + + auto byteLength = sizeOfDstYPlane + sizeOfDstUPlane + sizeOfDstVPlane; + auto maybeArrayBuffer = Napi::ArrayBuffer::New(env, byteLength); + if (maybeArrayBuffer.Env().IsExceptionPending()) { + return Validation::Invalid( + maybeArrayBuffer.Env().GetAndClearPendingException().Message()); + } + auto data = static_cast(maybeArrayBuffer.Data()); + + auto srcYPlane = value->DataY(); + auto srcUPlane = value->DataU(); + auto srcVPlane = value->DataV(); + +#pragma clang unsafe_buffer_usage begin + auto dstYPlane = data; + auto dstUPlane = data + sizeOfDstYPlane; + auto dstVPlane = dstUPlane + sizeOfDstUPlane; +#pragma clang unsafe_buffer_usage end + + int rc = libyuv::I420Copy( + srcYPlane, value->StrideY(), srcUPlane, value->StrideU(), srcVPlane, + value->StrideV(), dstYPlane, dstYStride, dstUPlane, dstUVStride, + dstVPlane, dstUVStride, value->width(), value->height()); + + if (rc != 0) { + return Validation::Invalid("Failed to copy I420 buffer: " + + std::to_string(rc)); + } + + // FIXME(mroberts): How to create a Uint8ClampedArray? + auto maybeUint8Array = + Napi::Uint8Array::New(env, byteLength, maybeArrayBuffer, 0); + if (maybeUint8Array.Env().IsExceptionPending()) { + return Validation::Invalid( + maybeUint8Array.Env().GetAndClearPendingException().Message()); + } + + return Pure(scope.Escape(maybeUint8Array)); +} + +} // namespace node_webrtc diff --git a/src/dictionaries/webrtc/video_frame_buffer.hh b/src/dictionaries/webrtc/video_frame_buffer.hh index 055d19e57..7620a0901 100644 --- a/src/dictionaries/webrtc/video_frame_buffer.hh +++ b/src/dictionaries/webrtc/video_frame_buffer.hh @@ -1,29 +1,27 @@ -#pragma once - -#include "src/converters.hh" -#include "src/converters/napi.hh" - -namespace rtc { -template class scoped_refptr; -} +#pragma once + +#include "src/converters.hh" +#include "src/converters/napi.hh" + namespace webrtc { +template class scoped_refptr; class I420Buffer; } -namespace webrtc { -class I420BufferInterface; -} -namespace webrtc { -class VideoFrameBuffer; -} - -namespace node_webrtc { - +namespace webrtc { +class I420BufferInterface; +} +namespace webrtc { +class VideoFrameBuffer; +} + +namespace node_webrtc { + class I420ImageData; -DECLARE_CONVERTER(I420ImageData, rtc::scoped_refptr) +DECLARE_CONVERTER(I420ImageData, webrtc::scoped_refptr) -DECLARE_FROM_NAPI(rtc::scoped_refptr) +DECLARE_FROM_NAPI(webrtc::scoped_refptr) DECLARE_TO_NAPI(const webrtc::I420BufferInterface *) -DECLARE_TO_NAPI(rtc::scoped_refptr) +DECLARE_TO_NAPI(webrtc::scoped_refptr) } // namespace node_webrtc diff --git a/src/enums/webrtc/ice_role.hh b/src/enums/webrtc/ice_role.hh index 2a0b6fcdc..436d9c9dd 100644 --- a/src/enums/webrtc/ice_role.hh +++ b/src/enums/webrtc/ice_role.hh @@ -1,17 +1,17 @@ -#pragma once - -#include - +#pragma once + +#include + // IWYU pragma: no_include "src/enums/macros/impls.hh" -#define ICE_ROLE cricket::IceRole +#define ICE_ROLE webrtc::IceRole #define ICE_ROLE_NAME "RTCIceRole" #define ICE_ROLE_LIST \ ENUM_SUPPORTED(ICE_ROLE::ICEROLE_CONTROLLING, "controlling") \ ENUM_SUPPORTED(ICE_ROLE::ICEROLE_CONTROLLED, "controlled") \ - ENUM_UNSUPPORTED(ICE_ROLE::ICEROLE_UNKNOWN, "unknown", \ - "\"unknown\" is not a valid RTCIceRole") - -#define ENUM(X) ICE_ROLE##X -#include "src/enums/macros/decls.hh" -#undef ENUM + ENUM_UNSUPPORTED(ICE_ROLE::ICEROLE_UNKNOWN, "unknown", \ + "\"unknown\" is not a valid RTCIceRole") + +#define ENUM(X) ICE_ROLE##X +#include "src/enums/macros/decls.hh" +#undef ENUM diff --git a/src/enums/webrtc/media_type.cc b/src/enums/webrtc/media_type.cc index ae785e724..8414b3107 100644 --- a/src/enums/webrtc/media_type.cc +++ b/src/enums/webrtc/media_type.cc @@ -1,5 +1,5 @@ -#include "src/enums/webrtc/media_type.hh" - -#define ENUM(X) CRICKET_MEDIA_TYPE##X +#include "src/enums/webrtc/media_type.hh" + +#define ENUM(X) WEBRTC_MEDIA_TYPE##X #include "src/enums/macros/impls.hh" #undef ENUM diff --git a/src/enums/webrtc/media_type.hh b/src/enums/webrtc/media_type.hh index 3490d87ea..63aec2221 100644 --- a/src/enums/webrtc/media_type.hh +++ b/src/enums/webrtc/media_type.hh @@ -1,19 +1,19 @@ -#pragma once - -#include - +#pragma once + +#include + // IWYU pragma: no_include "src/enums/macros/impls.hh" // FIXME(mroberts): I'm not sure that "data" should be valid. -#define CRICKET_MEDIA_TYPE cricket::MediaType -#define CRICKET_MEDIA_TYPE_NAME "kind" -#define CRICKET_MEDIA_TYPE_LIST \ - ENUM_SUPPORTED(CRICKET_MEDIA_TYPE::MEDIA_TYPE_AUDIO, "audio") \ - ENUM_SUPPORTED(CRICKET_MEDIA_TYPE::MEDIA_TYPE_VIDEO, "video") \ - ENUM_SUPPORTED(CRICKET_MEDIA_TYPE::MEDIA_TYPE_DATA, "data") \ - ENUM_UNSUPPORTED(CRICKET_MEDIA_TYPE::MEDIA_TYPE_UNSUPPORTED, "unsupported", \ +#define WEBRTC_MEDIA_TYPE webrtc::MediaType +#define WEBRTC_MEDIA_TYPE_NAME "kind" +#define WEBRTC_MEDIA_TYPE_LIST \ + ENUM_SUPPORTED(WEBRTC_MEDIA_TYPE::AUDIO, "audio") \ + ENUM_SUPPORTED(WEBRTC_MEDIA_TYPE::VIDEO, "video") \ + ENUM_SUPPORTED(WEBRTC_MEDIA_TYPE::DATA, "data") \ + ENUM_UNSUPPORTED(WEBRTC_MEDIA_TYPE::UNSUPPORTED, "unsupported", \ "Unsupported media type is not supported") -#define ENUM(X) CRICKET_MEDIA_TYPE##X +#define ENUM(X) WEBRTC_MEDIA_TYPE##X #include "src/enums/macros/decls.hh" #undef ENUM diff --git a/src/interfaces/media_stream.cc b/src/interfaces/media_stream.cc index b0d8369bc..ae582187d 100644 --- a/src/interfaces/media_stream.cc +++ b/src/interfaces/media_stream.cc @@ -1,365 +1,366 @@ -/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license that can be found - * in the LICENSE.md file in the root of the source tree. All contributing - * project authors may be found in the AUTHORS file in the root of the source - * tree. - */ -#include "src/interfaces/media_stream.hh" - +/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license that can be found + * in the LICENSE.md file in the root of the source tree. All contributing + * project authors may be found in the AUTHORS file in the root of the source + * tree. + */ +#include "src/interfaces/media_stream.hh" + #include #include #include #include #include +#include #include "src/converters.hh" #include "src/converters/arguments.hh" #include "src/converters/napi.hh" -#include "src/dictionaries/node_webrtc/rtc_media_stream_init.hh" -#include "src/functional/either.hh" -#include "src/functional/maybe.hh" -#include "src/interfaces/media_stream_track.hh" -#include "src/interfaces/rtc_peer_connection/peer_connection_factory.hh" - -namespace node_webrtc { - -Napi::FunctionReference &MediaStream::constructor() { - static Napi::FunctionReference constructor; - return constructor; -} - -MediaStream::Impl::Impl(PeerConnectionFactory *factory) - : _factory(factory ? factory : PeerConnectionFactory::GetOrCreateDefault()), - _stream( - _factory->factory()->CreateLocalMediaStream(rtc::CreateRandomUuid())), - _shouldReleaseFactory(!factory) {} - -MediaStream::Impl::Impl(std::vector &tracks, - PeerConnectionFactory *factory) - : _factory(factory ? factory - : tracks.empty() ? PeerConnectionFactory::GetOrCreateDefault() - : tracks[0]->factory()), - _stream( - _factory->factory()->CreateLocalMediaStream(rtc::CreateRandomUuid())), - _shouldReleaseFactory(!factory && tracks.empty()) { - for (auto const &track : tracks) { - auto const kind = track->track()->kind(); - if (kind == webrtc::AudioTrackInterface::kAudioKind) { - _stream->AddTrack(rtc::scoped_refptr( - static_cast(track->track().get()))); - } else if (kind == webrtc::VideoTrackInterface::kVideoKind) { - _stream->AddTrack(rtc::scoped_refptr( - static_cast(track->track().get()))); - } - } -} - -MediaStream::Impl::Impl( - rtc::scoped_refptr &stream, - PeerConnectionFactory *factory) - : _factory(factory ? factory : PeerConnectionFactory::GetOrCreateDefault()), - _stream(stream), _shouldReleaseFactory(!factory) {} - -MediaStream::Impl::Impl(const RTCMediaStreamInit &init, - PeerConnectionFactory *factory) - : _factory(factory ? factory : PeerConnectionFactory::GetOrCreateDefault()), - _stream(_factory->factory()->CreateLocalMediaStream(init.id)), - _shouldReleaseFactory(!factory) {} - -MediaStream::Impl::~Impl() { - Napi::HandleScope scope(PeerConnectionFactory::constructor().Env()); - - if (_shouldReleaseFactory) { - PeerConnectionFactory::Release(); - } -} - -std::vector> -MediaStream::tracks() { - auto tracks = - std::vector>(); - for (auto const &track : _impl._stream->GetAudioTracks()) { - tracks.emplace_back(track); - } - for (auto const &track : _impl._stream->GetVideoTracks()) { - tracks.emplace_back(track); - } - return tracks; -} - -rtc::scoped_refptr MediaStream::stream() { - return _impl._stream; -} - -MediaStream::MediaStream(const Napi::CallbackInfo &info) - : AsyncObjectWrap("MediaStream", info) { - auto maybeEither = From>> - COMMA // Either1 - Remote MediaStream OR Either2 - Either< - std::vector - COMMA // Either2 - Array of MediaStreamTracks OR Either3 - Either>>>>( - Arguments(info)); // Maybe - Optional RTCMediaStreamInit dictionary - if (maybeEither.IsInvalid()) { - Napi::TypeError::New(info.Env(), maybeEither.ToErrors()[0]) - .ThrowAsJavaScriptException(); - return; - } - auto either1 = maybeEither.UnsafeFromValid(); - - if (either1.IsLeft()) { - // 1. Remote MediaStream - auto pair = either1.UnsafeFromLeft(); - // FIXME(mroberts): There is a safer way to do this. - auto factory = PeerConnectionFactory::Unwrap(std::get<0>(pair)); - auto stream = *std::get<1>(pair).Data(); - _impl = MediaStream::Impl(stream, factory); - } else { - auto either2 = either1.UnsafeFromRight(); - if (either2.IsLeft()) { - // 2. Local MediaStream, Array of MediaStreamTracks - auto tracks = either2.UnsafeFromLeft(); - _impl = MediaStream::Impl(tracks); - } else { - auto either3 = either2.UnsafeFromRight(); - if (either3.IsLeft()) { - // 3. Local MediaStream, existing MediaStream - auto existingStream = either3.UnsafeFromLeft(); - auto factory = existingStream->_impl._factory; - // NOTE(jack): we need to keep these as RefPtr so they don't get - // garbage-collected in the middle of constructing them all - std::vector> tracks; - for (auto const &track : existingStream->tracks()) { - tracks.emplace_back( - MediaStreamTrack::wrap()->GetOrCreate(factory, track)); - } - // Now that they are all created and inside RefPtrs, the tracks will - // live until the end of this block. So it's safe to convert them back - // into pointers and call the _impl constructor - std::vector raw_tracks; - std::transform(tracks.begin(), tracks.end(), - std::back_inserter(raw_tracks), - [](auto track) -> auto { return track; }); - _impl = MediaStream::Impl(raw_tracks, factory); - } else { - // Check if RTCMediaStreamInit was provided - auto maybeMediaStreamInit = either3.UnsafeFromRight(); - if (maybeMediaStreamInit.IsJust()) { - // 4. Local MediaStream with Custom MediaStreamId - _impl = MediaStream::Impl(maybeMediaStreamInit.UnsafeFromJust()); - } else { - // 5. Local MediaStream - _impl = MediaStream::Impl(); - } - } - } - } -} - -MediaStream::~MediaStream() { - Napi::HandleScope scope(PeerConnectionFactory::constructor().Env()); - - wrap()->Release(this); -} - -Napi::Value MediaStream::GetId(const Napi::CallbackInfo &info) { - CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), _impl._stream->id(), result, - Napi::Value) - return result; -} - -Napi::Value MediaStream::GetActive(const Napi::CallbackInfo &info) { - auto active = false; - for (auto const &track : tracks()) { - auto mediaStreamTrack = _track_wrap.GetOrCreate(_impl._factory, track); - active = active || mediaStreamTrack->active(); - } - CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), active, result, Napi::Value) - return result; -} - -Napi::Value MediaStream::GetAudioTracks(const Napi::CallbackInfo &info) { - auto tracks = std::vector(); - for (auto const &track : _impl._stream->GetAudioTracks()) { - auto mediaStreamTrack = _track_wrap.GetOrCreate(_impl._factory, track); - tracks.push_back(mediaStreamTrack); - } - CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), tracks, result, Napi::Value) - return result; -} - -Napi::Value MediaStream::GetVideoTracks(const Napi::CallbackInfo &info) { - auto tracks = std::vector(); - for (auto const &track : _impl._stream->GetVideoTracks()) { - auto mediaStreamTrack = _track_wrap.GetOrCreate(_impl._factory, track); - tracks.push_back(mediaStreamTrack); - } - CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), tracks, result, Napi::Value) - return result; -} - -Napi::Value MediaStream::GetTracks(const Napi::CallbackInfo &info) { - auto tracks = std::vector(); - for (auto const &track : this->tracks()) { - auto mediaStreamTrack = _track_wrap.GetOrCreate(_impl._factory, track); - tracks.push_back(mediaStreamTrack); - } - CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), tracks, result, Napi::Value) - return result; -} - -Napi::Value MediaStream::GetTrackById(const Napi::CallbackInfo &info) { - CONVERT_ARGS_OR_THROW_AND_RETURN_NAPI(info, label, std::string) - auto audioTrack = _impl._stream->FindAudioTrack(label); - if (audioTrack) { - auto track = _track_wrap.GetOrCreate(_impl._factory, audioTrack); - CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), track.ptr(), result, - Napi::Value) - return result; - } - auto videoTrack = _impl._stream->FindVideoTrack(label); - if (videoTrack) { - auto track = _track_wrap.GetOrCreate(_impl._factory, videoTrack); - CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), track.ptr(), result, - Napi::Value) - return result; - } - return info.Env().Null(); -} - -Napi::Value MediaStream::AddTrack(const Napi::CallbackInfo &info) { - CONVERT_ARGS_OR_THROW_AND_RETURN_NAPI(info, mediaStreamTrack, - MediaStreamTrack *) - auto stream = _impl._stream; - auto track = mediaStreamTrack->track(); - if (track->kind() == track->kAudioKind) { - stream->AddTrack(rtc::scoped_refptr( - static_cast(track.get()))); - } else if (track->kind() == track->kVideoKind) { - stream->AddTrack(rtc::scoped_refptr( - static_cast(track.get()))); - } - return info.Env().Undefined(); -} - -Napi::Value MediaStream::RemoveTrack(const Napi::CallbackInfo &info) { - CONVERT_ARGS_OR_THROW_AND_RETURN_NAPI(info, mediaStreamTrack, - MediaStreamTrack *) - auto stream = _impl._stream; - auto track = mediaStreamTrack->track(); - if (track->kind() == track->kAudioKind) { - // TODO(jack): also make these remove from the _track_wrap, so we don't keep - // around old tracks - stream->RemoveTrack(rtc::scoped_refptr( - static_cast(track.get()))); - } else { - stream->RemoveTrack(rtc::scoped_refptr( - static_cast(track.get()))); - } - return info.Env().Undefined(); -} - -Napi::Value MediaStream::Clone(const Napi::CallbackInfo &info) { - auto clonedStream = _impl._factory->factory()->CreateLocalMediaStream( - rtc::CreateRandomUuid()); - for (auto const &track : this->tracks()) { - if (track->kind() == track->kAudioKind) { - auto audioTrack = static_cast(track.get()); - auto source = audioTrack->GetSource(); - auto clonedTrack = _impl._factory->factory()->CreateAudioTrack( - rtc::CreateRandomUuid(), source); - clonedStream->AddTrack(clonedTrack); - } else { - auto videoTrack = static_cast(track.get()); - auto source = rtc::scoped_refptr( - videoTrack->GetSource()); - auto clonedTrack = _impl._factory->factory()->CreateVideoTrack( - source, rtc::CreateRandomUuid()); - clonedStream->AddTrack(clonedTrack); - } - } - auto mediaStream = RefPtr( - MediaStream::wrap()->GetOrCreate(_impl._factory, clonedStream)); - CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), mediaStream.ptr(), result, - Napi::Value) - return result; -} - -Wrap, - PeerConnectionFactory *> * -MediaStream::wrap() { - static auto wrap = - new node_webrtc::Wrap, - PeerConnectionFactory *>(MediaStream::Create); - return wrap; -} - -MediaStream * -MediaStream::Create(PeerConnectionFactory *factory, - rtc::scoped_refptr stream) { - auto env = MediaStream::constructor().Env(); - Napi::HandleScope scope(env); - - auto object = MediaStream::constructor().New( - {factory->Value(), - Napi::External>::New( - env, &stream)}); - - auto unwrapped = Unwrap(object); - return unwrapped; -} - -void MediaStream::Init(Napi::Env env, Napi::Object exports) { - Napi::HandleScope scope(env); - - Napi::Function func = DefineClass( - env, "MediaStream", - { - InstanceAccessor("id", &MediaStream::GetId, nullptr), - InstanceAccessor("active", &MediaStream::GetActive, nullptr), - InstanceMethod("getAudioTracks", &MediaStream::GetAudioTracks), - InstanceMethod("getVideoTracks", &MediaStream::GetVideoTracks), - InstanceMethod("getTracks", &MediaStream::GetTracks), - InstanceMethod("getTrackById", &MediaStream::GetTrackById), - InstanceMethod("addTrack", &MediaStream::AddTrack), - InstanceMethod("removeTrack", &MediaStream::RemoveTrack), - InstanceMethod("clone", &MediaStream::Clone), - }); - - constructor() = Napi::Persistent(func); - constructor().SuppressDestruct(); - - exports.Set("MediaStream", func); -} - -FROM_NAPI_IMPL(MediaStream *, value) { - return From(value).FlatMap( - [](Napi::Object object) { - auto isMediaStream = false; - napi_instanceof(object.Env(), object, - MediaStream::constructor().Value(), &isMediaStream); - - if (object.Env().IsExceptionPending()) { - return Validation::Invalid( - object.Env().GetAndClearPendingException().Message()); - } - if (!isMediaStream) { - return Validation::Invalid( - "This is not an instance of MediaStream"); - } - - return Pure(MediaStream::Unwrap(object)); - }); -} - -TO_NAPI_IMPL(MediaStream *, pair) { - return Pure(pair.second->Value().As()); -} - -} // namespace node_webrtc +#include "src/dictionaries/node_webrtc/rtc_media_stream_init.hh" +#include "src/functional/either.hh" +#include "src/functional/maybe.hh" +#include "src/interfaces/media_stream_track.hh" +#include "src/interfaces/rtc_peer_connection/peer_connection_factory.hh" + +namespace node_webrtc { + +Napi::FunctionReference &MediaStream::constructor() { + static Napi::FunctionReference constructor; + return constructor; +} + +MediaStream::Impl::Impl(PeerConnectionFactory *factory) + : _factory(factory ? factory : PeerConnectionFactory::GetOrCreateDefault()), + _stream( + _factory->factory()->CreateLocalMediaStream(rtc::CreateRandomUuid())), + _shouldReleaseFactory(!factory) {} + +MediaStream::Impl::Impl(std::vector &tracks, + PeerConnectionFactory *factory) + : _factory(factory ? factory + : tracks.empty() ? PeerConnectionFactory::GetOrCreateDefault() + : tracks[0]->factory()), + _stream( + _factory->factory()->CreateLocalMediaStream(rtc::CreateRandomUuid())), + _shouldReleaseFactory(!factory && tracks.empty()) { + for (auto const &track : tracks) { + auto const kind = track->track()->kind(); + if (kind == webrtc::AudioTrackInterface::kAudioKind) { + _stream->AddTrack(rtc::scoped_refptr( + static_cast(track->track().get()))); + } else if (kind == webrtc::VideoTrackInterface::kVideoKind) { + _stream->AddTrack(rtc::scoped_refptr( + static_cast(track->track().get()))); + } + } +} + +MediaStream::Impl::Impl( + rtc::scoped_refptr &stream, + PeerConnectionFactory *factory) + : _factory(factory ? factory : PeerConnectionFactory::GetOrCreateDefault()), + _stream(stream), _shouldReleaseFactory(!factory) {} + +MediaStream::Impl::Impl(const RTCMediaStreamInit &init, + PeerConnectionFactory *factory) + : _factory(factory ? factory : PeerConnectionFactory::GetOrCreateDefault()), + _stream(_factory->factory()->CreateLocalMediaStream(init.id)), + _shouldReleaseFactory(!factory) {} + +MediaStream::Impl::~Impl() { + Napi::HandleScope scope(PeerConnectionFactory::constructor().Env()); + + if (_shouldReleaseFactory) { + PeerConnectionFactory::Release(); + } +} + +std::vector> +MediaStream::tracks() { + auto tracks = + std::vector>(); + for (auto const &track : _impl._stream->GetAudioTracks()) { + tracks.emplace_back(track); + } + for (auto const &track : _impl._stream->GetVideoTracks()) { + tracks.emplace_back(track); + } + return tracks; +} + +rtc::scoped_refptr MediaStream::stream() { + return _impl._stream; +} + +MediaStream::MediaStream(const Napi::CallbackInfo &info) + : AsyncObjectWrap("MediaStream", info) { + auto maybeEither = From>> + COMMA // Either1 - Remote MediaStream OR Either2 + Either< + std::vector + COMMA // Either2 - Array of MediaStreamTracks OR Either3 + Either>>>>( + Arguments(info)); // Maybe - Optional RTCMediaStreamInit dictionary + if (maybeEither.IsInvalid()) { + Napi::TypeError::New(info.Env(), maybeEither.ToErrors()[0]) + .ThrowAsJavaScriptException(); + return; + } + auto either1 = maybeEither.UnsafeFromValid(); + + if (either1.IsLeft()) { + // 1. Remote MediaStream + auto pair = either1.UnsafeFromLeft(); + // FIXME(mroberts): There is a safer way to do this. + auto factory = PeerConnectionFactory::Unwrap(std::get<0>(pair)); + auto stream = *std::get<1>(pair).Data(); + _impl = MediaStream::Impl(stream, factory); + } else { + auto either2 = either1.UnsafeFromRight(); + if (either2.IsLeft()) { + // 2. Local MediaStream, Array of MediaStreamTracks + auto tracks = either2.UnsafeFromLeft(); + _impl = MediaStream::Impl(tracks); + } else { + auto either3 = either2.UnsafeFromRight(); + if (either3.IsLeft()) { + // 3. Local MediaStream, existing MediaStream + auto existingStream = either3.UnsafeFromLeft(); + auto factory = existingStream->_impl._factory; + // NOTE(jack): we need to keep these as RefPtr so they don't get + // garbage-collected in the middle of constructing them all + std::vector> tracks; + for (auto const &track : existingStream->tracks()) { + tracks.emplace_back( + MediaStreamTrack::wrap()->GetOrCreate(factory, track)); + } + // Now that they are all created and inside RefPtrs, the tracks will + // live until the end of this block. So it's safe to convert them back + // into pointers and call the _impl constructor + std::vector raw_tracks; + std::transform(tracks.begin(), tracks.end(), + std::back_inserter(raw_tracks), + [](auto track) -> auto { return track; }); + _impl = MediaStream::Impl(raw_tracks, factory); + } else { + // Check if RTCMediaStreamInit was provided + auto maybeMediaStreamInit = either3.UnsafeFromRight(); + if (maybeMediaStreamInit.IsJust()) { + // 4. Local MediaStream with Custom MediaStreamId + _impl = MediaStream::Impl(maybeMediaStreamInit.UnsafeFromJust()); + } else { + // 5. Local MediaStream + _impl = MediaStream::Impl(); + } + } + } + } +} + +MediaStream::~MediaStream() { + Napi::HandleScope scope(PeerConnectionFactory::constructor().Env()); + + wrap()->Release(this); +} + +Napi::Value MediaStream::GetId(const Napi::CallbackInfo &info) { + CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), _impl._stream->id(), result, + Napi::Value) + return result; +} + +Napi::Value MediaStream::GetActive(const Napi::CallbackInfo &info) { + auto active = false; + for (auto const &track : tracks()) { + auto mediaStreamTrack = _track_wrap.GetOrCreate(_impl._factory, track); + active = active || mediaStreamTrack->active(); + } + CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), active, result, Napi::Value) + return result; +} + +Napi::Value MediaStream::GetAudioTracks(const Napi::CallbackInfo &info) { + auto tracks = std::vector(); + for (auto const &track : _impl._stream->GetAudioTracks()) { + auto mediaStreamTrack = _track_wrap.GetOrCreate(_impl._factory, track); + tracks.push_back(mediaStreamTrack); + } + CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), tracks, result, Napi::Value) + return result; +} + +Napi::Value MediaStream::GetVideoTracks(const Napi::CallbackInfo &info) { + auto tracks = std::vector(); + for (auto const &track : _impl._stream->GetVideoTracks()) { + auto mediaStreamTrack = _track_wrap.GetOrCreate(_impl._factory, track); + tracks.push_back(mediaStreamTrack); + } + CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), tracks, result, Napi::Value) + return result; +} + +Napi::Value MediaStream::GetTracks(const Napi::CallbackInfo &info) { + auto tracks = std::vector(); + for (auto const &track : this->tracks()) { + auto mediaStreamTrack = _track_wrap.GetOrCreate(_impl._factory, track); + tracks.push_back(mediaStreamTrack); + } + CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), tracks, result, Napi::Value) + return result; +} + +Napi::Value MediaStream::GetTrackById(const Napi::CallbackInfo &info) { + CONVERT_ARGS_OR_THROW_AND_RETURN_NAPI(info, label, std::string) + auto audioTrack = _impl._stream->FindAudioTrack(label); + if (audioTrack) { + auto track = _track_wrap.GetOrCreate(_impl._factory, audioTrack); + CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), track.ptr(), result, + Napi::Value) + return result; + } + auto videoTrack = _impl._stream->FindVideoTrack(label); + if (videoTrack) { + auto track = _track_wrap.GetOrCreate(_impl._factory, videoTrack); + CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), track.ptr(), result, + Napi::Value) + return result; + } + return info.Env().Null(); +} + +Napi::Value MediaStream::AddTrack(const Napi::CallbackInfo &info) { + CONVERT_ARGS_OR_THROW_AND_RETURN_NAPI(info, mediaStreamTrack, + MediaStreamTrack *) + auto stream = _impl._stream; + auto track = mediaStreamTrack->track(); + if (track->kind() == track->kAudioKind) { + stream->AddTrack(rtc::scoped_refptr( + static_cast(track.get()))); + } else if (track->kind() == track->kVideoKind) { + stream->AddTrack(rtc::scoped_refptr( + static_cast(track.get()))); + } + return info.Env().Undefined(); +} + +Napi::Value MediaStream::RemoveTrack(const Napi::CallbackInfo &info) { + CONVERT_ARGS_OR_THROW_AND_RETURN_NAPI(info, mediaStreamTrack, + MediaStreamTrack *) + auto stream = _impl._stream; + auto track = mediaStreamTrack->track(); + if (track->kind() == track->kAudioKind) { + // TODO(jack): also make these remove from the _track_wrap, so we don't keep + // around old tracks + stream->RemoveTrack(rtc::scoped_refptr( + static_cast(track.get()))); + } else { + stream->RemoveTrack(rtc::scoped_refptr( + static_cast(track.get()))); + } + return info.Env().Undefined(); +} + +Napi::Value MediaStream::Clone(const Napi::CallbackInfo &info) { + auto clonedStream = _impl._factory->factory()->CreateLocalMediaStream( + rtc::CreateRandomUuid()); + for (auto const &track : this->tracks()) { + if (track->kind() == track->kAudioKind) { + auto audioTrack = static_cast(track.get()); + auto source = audioTrack->GetSource(); + auto clonedTrack = _impl._factory->factory()->CreateAudioTrack( + rtc::CreateRandomUuid(), source); + clonedStream->AddTrack(clonedTrack); + } else { + auto videoTrack = static_cast(track.get()); + auto source = rtc::scoped_refptr( + videoTrack->GetSource()); + auto clonedTrack = _impl._factory->factory()->CreateVideoTrack( + source, rtc::CreateRandomUuid()); + clonedStream->AddTrack(clonedTrack); + } + } + auto mediaStream = RefPtr( + MediaStream::wrap()->GetOrCreate(_impl._factory, clonedStream)); + CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), mediaStream.ptr(), result, + Napi::Value) + return result; +} + +Wrap, + PeerConnectionFactory *> * +MediaStream::wrap() { + static auto wrap = + new node_webrtc::Wrap, + PeerConnectionFactory *>(MediaStream::Create); + return wrap; +} + +MediaStream * +MediaStream::Create(PeerConnectionFactory *factory, + rtc::scoped_refptr stream) { + auto env = MediaStream::constructor().Env(); + Napi::HandleScope scope(env); + + auto object = MediaStream::constructor().New( + {factory->Value(), + Napi::External>::New( + env, &stream)}); + + auto unwrapped = Unwrap(object); + return unwrapped; +} + +void MediaStream::Init(Napi::Env env, Napi::Object exports) { + Napi::HandleScope scope(env); + + Napi::Function func = DefineClass( + env, "MediaStream", + { + InstanceAccessor("id", &MediaStream::GetId, nullptr), + InstanceAccessor("active", &MediaStream::GetActive, nullptr), + InstanceMethod("getAudioTracks", &MediaStream::GetAudioTracks), + InstanceMethod("getVideoTracks", &MediaStream::GetVideoTracks), + InstanceMethod("getTracks", &MediaStream::GetTracks), + InstanceMethod("getTrackById", &MediaStream::GetTrackById), + InstanceMethod("addTrack", &MediaStream::AddTrack), + InstanceMethod("removeTrack", &MediaStream::RemoveTrack), + InstanceMethod("clone", &MediaStream::Clone), + }); + + constructor() = Napi::Persistent(func); + constructor().SuppressDestruct(); + + exports.Set("MediaStream", func); +} + +FROM_NAPI_IMPL(MediaStream *, value) { + return From(value).FlatMap( + [](Napi::Object object) { + auto isMediaStream = false; + napi_instanceof(object.Env(), object, + MediaStream::constructor().Value(), &isMediaStream); + + if (object.Env().IsExceptionPending()) { + return Validation::Invalid( + object.Env().GetAndClearPendingException().Message()); + } + if (!isMediaStream) { + return Validation::Invalid( + "This is not an instance of MediaStream"); + } + + return Pure(MediaStream::Unwrap(object)); + }); +} + +TO_NAPI_IMPL(MediaStream *, pair) { + return Pure(pair.second->Value().As()); +} + +} // namespace node_webrtc diff --git a/src/interfaces/media_stream_track.cc b/src/interfaces/media_stream_track.cc index 41f961c60..286b32a0f 100644 --- a/src/interfaces/media_stream_track.cc +++ b/src/interfaces/media_stream_track.cc @@ -1,273 +1,273 @@ -/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license that can be found - * in the LICENSE.md file in the root of the source tree. All contributing - * project authors may be found in the AUTHORS file in the root of the source - * tree. - */ +/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license that can be found + * in the LICENSE.md file in the root of the source tree. All contributing + * project authors may be found in the AUTHORS file in the root of the source + * tree. + */ #include "src/interfaces/media_stream_track.hh" +#include #include -#include -#include #include #include -#include - -#include "src/converters.hh" -#include "src/converters/interfaces.hh" -#include "src/dictionaries/node_webrtc/media_track_settings.hh" -#include "src/interfaces/rtc_peer_connection/peer_connection_factory.hh" - -namespace node_webrtc { - -Napi::FunctionReference &MediaStreamTrack::constructor() { - static Napi::FunctionReference constructor; - return constructor; -} - -MediaStreamTrack::MediaStreamTrack(const Napi::CallbackInfo &info) - : AsyncObjectWrapWithLoop("MediaStreamTrack", *this, - info) { - auto env = info.Env(); - - if (info.Length() != 2 || !info[0].IsObject() || !info[1].IsExternal()) { - Napi::TypeError::New(env, "You cannot construct a MediaStreamTrack") - .ThrowAsJavaScriptException(); - return; - } - - // FIXME(mroberts): There is a safer conversion here. - _factory = PeerConnectionFactory::Unwrap(info[0].ToObject()); - auto track = - *info[1] - .As>>() - .Data(); - - _track = std::move(track); - _track->RegisterObserver(this); - - // NOTE(mroberts): This doesn't actually matter yet. - _enabled = false; -} - -MediaStreamTrack::~MediaStreamTrack() { - _track = nullptr; - - Napi::HandleScope scope(PeerConnectionFactory::constructor().Env()); - - wrap()->Release(this); -} - -void MediaStreamTrack::Stop() { - // SAFETY: `[=]` is a capture-by-copy, which will create a new reference to - // the _track shared_ptr, so using that _track will be ok even if the current - // object is destructed. - // `this`'s pointer value needs to get copied for the `UnregisterObserver` to - // work properly, but we actually don't care about use-after-free, since we - // know when this is called the state should not be updating any more, and - // we only need to remove it from the track's list. - // This whole thing needs to be done because in M94, the `OnChanged()` method - // becomes called from the worker thread, but UnregisterObserver wants to be - // called on the signalling thread, and if they both do that at the same time, - // there is a deadlock :) - auto thisCopy = static_cast(this); - rtc::scoped_refptr trackCopy = _track; - _factory->SignalingThread()->PostTask([=]() { // NOLINT - trackCopy->UnregisterObserver(thisCopy); - }); - _ended = true; - _enabled = _track->enabled(); - AsyncObjectWrapWithLoop::Stop(); -} - -void MediaStreamTrack::OnChanged() { - if (_track->state() == - webrtc::MediaStreamTrackInterface::TrackState::kEnded) { - Stop(); - } -} - -void MediaStreamTrack::OnPeerConnectionClosed() { Stop(); } - -Napi::Value MediaStreamTrack::GetEnabled(const Napi::CallbackInfo &info) { - CONVERT_OR_THROW_AND_RETURN_NAPI( - info.Env(), _ended ? _enabled : _track->enabled(), result, Napi::Value) - return result; -} - -void MediaStreamTrack::SetEnabled(const Napi::CallbackInfo &info, - const Napi::Value &value) { - auto maybeEnabled = From(value); - if (maybeEnabled.IsInvalid()) { - Napi::TypeError::New(info.Env(), maybeEnabled.ToErrors()[0]) - .ThrowAsJavaScriptException(); - return; - } - auto enabled = maybeEnabled.UnsafeFromValid(); - if (_ended) { - _enabled = enabled; - } else { - _track->set_enabled(enabled); - } -} - -Napi::Value MediaStreamTrack::GetId(const Napi::CallbackInfo &info) { - CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), _track->id(), result, - Napi::Value) - return result; -} - -Napi::Value MediaStreamTrack::GetKind(const Napi::CallbackInfo &info) { - CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), _track->kind(), result, - Napi::Value) - return result; -} - -Napi::Value MediaStreamTrack::GetReadyState(const Napi::CallbackInfo &info) { - auto state = _ended ? webrtc::MediaStreamTrackInterface::TrackState::kEnded - : _track->state(); - CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), state, result, Napi::Value) - return result; -} - -Napi::Value MediaStreamTrack::GetMuted(const Napi::CallbackInfo &info) { - CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), false, result, Napi::Value) - return result; -} - -Napi::Value MediaStreamTrack::Clone(const Napi::CallbackInfo &) { - auto label = rtc::CreateRandomUuid(); - rtc::scoped_refptr clonedTrack = nullptr; - if (_track->kind() == _track->kAudioKind) { - auto audioTrack = static_cast(_track.get()); - clonedTrack = - _factory->factory()->CreateAudioTrack(label, audioTrack->GetSource()); - } else { - auto videoTrack = static_cast(_track.get()); - auto source = rtc::scoped_refptr( - videoTrack->GetSource()); - clonedTrack = _factory->factory()->CreateVideoTrack(source, label); - } - auto clonedMediaStreamTrack = wrap()->GetOrCreate(_factory, clonedTrack); - if (_ended) { - clonedMediaStreamTrack->Stop(); - } - return clonedMediaStreamTrack->Value(); -} - -Napi::Value MediaStreamTrack::JsStop(const Napi::CallbackInfo &info) { - Stop(); - return info.Env().Undefined(); -} - -Napi::Value MediaStreamTrack::GetSettings(const Napi::CallbackInfo &info) { - // FIXME(jack): find some way to get the real settings. - if (_track->kind() == _track->kAudioKind) { - auto settings = AudioMediaTrackSettings{}; - settings.deviceId = "FakeAudioDevice"; - settings.groupId = "FakeDeviceGroup"; - settings.autoGainControl = false; - settings.echoCancellation = false; - settings.noiseSuppression = false; - settings.channelCount = 1; - settings.sampleRate = 48000; - settings.sampleSize = 16; - settings.latency = 0.0; - settings.volume = 0.25; - CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), settings, result, Napi::Value) - return result; - } - assert(_track->kind() == _track->kVideoKind); - auto settings = VideoMediaTrackSettings{}; - settings.deviceId = "FakeVideoDevice"; - settings.groupId = "FakeDeviceGroup"; - settings.height = 480; - settings.width = 640; - settings.aspectRatio = 640.0 / 480.0; - settings.frameRate = 30.0; - settings.facingMode = "user"; - CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), settings, result, Napi::Value) - return result; -} - -Wrap, - PeerConnectionFactory *> * -MediaStreamTrack::wrap() { - static auto wrap = new node_webrtc::Wrap< - MediaStreamTrack *, rtc::scoped_refptr, - PeerConnectionFactory *>(MediaStreamTrack::Create); - return wrap; -} - -MediaStreamTrack *MediaStreamTrack::Create( - PeerConnectionFactory *factory, - rtc::scoped_refptr track) { - auto env = constructor().Env(); - Napi::HandleScope scope(env); - - auto mediaStreamTrack = constructor().New( - {factory->Value(), - Napi::External>:: - New(env, &track)}); - - return Unwrap(mediaStreamTrack); -} - -void MediaStreamTrack::Init(Napi::Env env, Napi::Object exports) { - auto func = DefineClass( - env, "MediaStreamTrack", - { - InstanceAccessor("enabled", &MediaStreamTrack::GetEnabled, - &MediaStreamTrack::SetEnabled), - InstanceAccessor("id", &MediaStreamTrack::GetId, nullptr), - InstanceAccessor("kind", &MediaStreamTrack::GetKind, nullptr), - InstanceAccessor("readyState", &MediaStreamTrack::GetReadyState, - nullptr), - InstanceAccessor("muted", &MediaStreamTrack::GetMuted, nullptr), - InstanceMethod("clone", &MediaStreamTrack::Clone), - InstanceMethod("stop", &MediaStreamTrack::JsStop), - InstanceMethod("getSettings", &MediaStreamTrack::GetSettings), - }); - - constructor() = Napi::Persistent(func); - constructor().SuppressDestruct(); - - exports.Set("MediaStreamTrack", func); -} - -CONVERTER_IMPL(MediaStreamTrack *, - rtc::scoped_refptr, - mediaStreamTrack) { - auto track = mediaStreamTrack->track(); - if (track->kind() != webrtc::MediaStreamTrackInterface::kAudioKind) { - return Validation>::Invalid( - "Expected an audio MediaStreamTrack"); - } - rtc::scoped_refptr audioTrack( - static_cast(track.get())); - return Pure(audioTrack); -} - -CONVERTER_IMPL(MediaStreamTrack *, - rtc::scoped_refptr, - mediaStreamTrack) { - auto track = mediaStreamTrack->track(); - if (track->kind() != webrtc::MediaStreamTrackInterface::kVideoKind) { - return Validation>::Invalid( - "Expected a video MediaStreamTrack"); - } - rtc::scoped_refptr videoTrack( - static_cast(track.get())); - return Pure(videoTrack); -} - -CONVERT_INTERFACE_TO_AND_FROM_NAPI(MediaStreamTrack, "MediaStreamTrack") - -CONVERT_VIA(Napi::Value, MediaStreamTrack *, - rtc::scoped_refptr) -CONVERT_VIA(Napi::Value, MediaStreamTrack *, - rtc::scoped_refptr) - -} // namespace node_webrtc +#include +#include + +#include "src/converters.hh" +#include "src/converters/interfaces.hh" +#include "src/dictionaries/node_webrtc/media_track_settings.hh" +#include "src/interfaces/rtc_peer_connection/peer_connection_factory.hh" + +namespace node_webrtc { + +Napi::FunctionReference &MediaStreamTrack::constructor() { + static Napi::FunctionReference constructor; + return constructor; +} + +MediaStreamTrack::MediaStreamTrack(const Napi::CallbackInfo &info) + : AsyncObjectWrapWithLoop("MediaStreamTrack", *this, + info) { + auto env = info.Env(); + + if (info.Length() != 2 || !info[0].IsObject() || !info[1].IsExternal()) { + Napi::TypeError::New(env, "You cannot construct a MediaStreamTrack") + .ThrowAsJavaScriptException(); + return; + } + + // FIXME(mroberts): There is a safer conversion here. + _factory = PeerConnectionFactory::Unwrap(info[0].ToObject()); + auto track = + *info[1] + .As>>() + .Data(); + + _track = std::move(track); + _track->RegisterObserver(this); + + // NOTE(mroberts): This doesn't actually matter yet. + _enabled = false; +} + +MediaStreamTrack::~MediaStreamTrack() { + _track = nullptr; + + Napi::HandleScope scope(PeerConnectionFactory::constructor().Env()); + + wrap()->Release(this); +} + +void MediaStreamTrack::Stop() { + // SAFETY: `[=]` is a capture-by-copy, which will create a new reference to + // the _track shared_ptr, so using that _track will be ok even if the current + // object is destructed. + // `this`'s pointer value needs to get copied for the `UnregisterObserver` to + // work properly, but we actually don't care about use-after-free, since we + // know when this is called the state should not be updating any more, and + // we only need to remove it from the track's list. + // This whole thing needs to be done because in M94, the `OnChanged()` method + // becomes called from the worker thread, but UnregisterObserver wants to be + // called on the signalling thread, and if they both do that at the same time, + // there is a deadlock :) + auto thisCopy = static_cast(this); + rtc::scoped_refptr trackCopy = _track; + _factory->SignalingThread()->PostTask([=]() { // NOLINT + trackCopy->UnregisterObserver(thisCopy); + }); + _ended = true; + _enabled = _track->enabled(); + AsyncObjectWrapWithLoop::Stop(); +} + +void MediaStreamTrack::OnChanged() { + if (_track->state() == + webrtc::MediaStreamTrackInterface::TrackState::kEnded) { + Stop(); + } +} + +void MediaStreamTrack::OnPeerConnectionClosed() { Stop(); } + +Napi::Value MediaStreamTrack::GetEnabled(const Napi::CallbackInfo &info) { + CONVERT_OR_THROW_AND_RETURN_NAPI( + info.Env(), _ended ? _enabled : _track->enabled(), result, Napi::Value) + return result; +} + +void MediaStreamTrack::SetEnabled(const Napi::CallbackInfo &info, + const Napi::Value &value) { + auto maybeEnabled = From(value); + if (maybeEnabled.IsInvalid()) { + Napi::TypeError::New(info.Env(), maybeEnabled.ToErrors()[0]) + .ThrowAsJavaScriptException(); + return; + } + auto enabled = maybeEnabled.UnsafeFromValid(); + if (_ended) { + _enabled = enabled; + } else { + _track->set_enabled(enabled); + } +} + +Napi::Value MediaStreamTrack::GetId(const Napi::CallbackInfo &info) { + CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), _track->id(), result, + Napi::Value) + return result; +} + +Napi::Value MediaStreamTrack::GetKind(const Napi::CallbackInfo &info) { + CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), _track->kind(), result, + Napi::Value) + return result; +} + +Napi::Value MediaStreamTrack::GetReadyState(const Napi::CallbackInfo &info) { + auto state = _ended ? webrtc::MediaStreamTrackInterface::TrackState::kEnded + : _track->state(); + CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), state, result, Napi::Value) + return result; +} + +Napi::Value MediaStreamTrack::GetMuted(const Napi::CallbackInfo &info) { + CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), false, result, Napi::Value) + return result; +} + +Napi::Value MediaStreamTrack::Clone(const Napi::CallbackInfo &) { + auto label = rtc::CreateRandomUuid(); + rtc::scoped_refptr clonedTrack = nullptr; + if (_track->kind() == _track->kAudioKind) { + auto audioTrack = static_cast(_track.get()); + clonedTrack = + _factory->factory()->CreateAudioTrack(label, audioTrack->GetSource()); + } else { + auto videoTrack = static_cast(_track.get()); + auto source = rtc::scoped_refptr( + videoTrack->GetSource()); + clonedTrack = _factory->factory()->CreateVideoTrack(source, label); + } + auto clonedMediaStreamTrack = wrap()->GetOrCreate(_factory, clonedTrack); + if (_ended) { + clonedMediaStreamTrack->Stop(); + } + return clonedMediaStreamTrack->Value(); +} + +Napi::Value MediaStreamTrack::JsStop(const Napi::CallbackInfo &info) { + Stop(); + return info.Env().Undefined(); +} + +Napi::Value MediaStreamTrack::GetSettings(const Napi::CallbackInfo &info) { + // FIXME(jack): find some way to get the real settings. + if (_track->kind() == _track->kAudioKind) { + auto settings = AudioMediaTrackSettings{}; + settings.deviceId = "FakeAudioDevice"; + settings.groupId = "FakeDeviceGroup"; + settings.autoGainControl = false; + settings.echoCancellation = false; + settings.noiseSuppression = false; + settings.channelCount = 1; + settings.sampleRate = 48000; + settings.sampleSize = 16; + settings.latency = 0.0; + settings.volume = 0.25; + CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), settings, result, Napi::Value) + return result; + } + assert(_track->kind() == _track->kVideoKind); + auto settings = VideoMediaTrackSettings{}; + settings.deviceId = "FakeVideoDevice"; + settings.groupId = "FakeDeviceGroup"; + settings.height = 480; + settings.width = 640; + settings.aspectRatio = 640.0 / 480.0; + settings.frameRate = 30.0; + settings.facingMode = "user"; + CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), settings, result, Napi::Value) + return result; +} + +Wrap, + PeerConnectionFactory *> * +MediaStreamTrack::wrap() { + static auto wrap = new node_webrtc::Wrap< + MediaStreamTrack *, rtc::scoped_refptr, + PeerConnectionFactory *>(MediaStreamTrack::Create); + return wrap; +} + +MediaStreamTrack *MediaStreamTrack::Create( + PeerConnectionFactory *factory, + rtc::scoped_refptr track) { + auto env = constructor().Env(); + Napi::HandleScope scope(env); + + auto mediaStreamTrack = constructor().New( + {factory->Value(), + Napi::External>:: + New(env, &track)}); + + return Unwrap(mediaStreamTrack); +} + +void MediaStreamTrack::Init(Napi::Env env, Napi::Object exports) { + auto func = DefineClass( + env, "MediaStreamTrack", + { + InstanceAccessor("enabled", &MediaStreamTrack::GetEnabled, + &MediaStreamTrack::SetEnabled), + InstanceAccessor("id", &MediaStreamTrack::GetId, nullptr), + InstanceAccessor("kind", &MediaStreamTrack::GetKind, nullptr), + InstanceAccessor("readyState", &MediaStreamTrack::GetReadyState, + nullptr), + InstanceAccessor("muted", &MediaStreamTrack::GetMuted, nullptr), + InstanceMethod("clone", &MediaStreamTrack::Clone), + InstanceMethod("stop", &MediaStreamTrack::JsStop), + InstanceMethod("getSettings", &MediaStreamTrack::GetSettings), + }); + + constructor() = Napi::Persistent(func); + constructor().SuppressDestruct(); + + exports.Set("MediaStreamTrack", func); +} + +CONVERTER_IMPL(MediaStreamTrack *, + rtc::scoped_refptr, + mediaStreamTrack) { + auto track = mediaStreamTrack->track(); + if (track->kind() != webrtc::MediaStreamTrackInterface::kAudioKind) { + return Validation>::Invalid( + "Expected an audio MediaStreamTrack"); + } + rtc::scoped_refptr audioTrack( + static_cast(track.get())); + return Pure(audioTrack); +} + +CONVERTER_IMPL(MediaStreamTrack *, + rtc::scoped_refptr, + mediaStreamTrack) { + auto track = mediaStreamTrack->track(); + if (track->kind() != webrtc::MediaStreamTrackInterface::kVideoKind) { + return Validation>::Invalid( + "Expected a video MediaStreamTrack"); + } + rtc::scoped_refptr videoTrack( + static_cast(track.get())); + return Pure(videoTrack); +} + +CONVERT_INTERFACE_TO_AND_FROM_NAPI(MediaStreamTrack, "MediaStreamTrack") + +CONVERT_VIA(Napi::Value, MediaStreamTrack *, + rtc::scoped_refptr) +CONVERT_VIA(Napi::Value, MediaStreamTrack *, + rtc::scoped_refptr) + +} // namespace node_webrtc diff --git a/src/interfaces/rtc_audio_source.cc b/src/interfaces/rtc_audio_source.cc index 8d87dea84..0719352a2 100644 --- a/src/interfaces/rtc_audio_source.cc +++ b/src/interfaces/rtc_audio_source.cc @@ -1,59 +1,60 @@ -/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license that can be found - * in the LICENSE.md file in the root of the source tree. All contributing - * project authors may be found in the AUTHORS file in the root of the source - * tree. - */ +/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license that can be found + * in the LICENSE.md file in the root of the source tree. All contributing + * project authors may be found in the AUTHORS file in the root of the source + * tree. + */ #include "src/interfaces/rtc_audio_source.hh" #include #include +#include #include "src/converters.hh" #include "src/converters/arguments.hh" #include "src/functional/maybe.hh" -#include "src/interfaces/media_stream_track.hh" - -namespace node_webrtc { - -Napi::FunctionReference &RTCAudioSource::constructor() { - static Napi::FunctionReference constructor; - return constructor; -} - -RTCAudioSource::RTCAudioSource(const Napi::CallbackInfo &info) - : Napi::ObjectWrap(info) { - _source = rtc::make_ref_counted(); -} - -Napi::Value RTCAudioSource::CreateTrack(const Napi::CallbackInfo &) { - // TODO(mroberts): Again, we have some implicit factory we are threading - // around. How to handle? - auto factory = PeerConnectionFactory::GetOrCreateDefault(); - auto track = factory->factory()->CreateAudioTrack(rtc::CreateRandomUuid(), - _source.get()); - return _track_wrap.GetOrCreate(factory, track)->Value(); -} - -Napi::Value RTCAudioSource::OnData(const Napi::CallbackInfo &info) { - CONVERT_ARGS_OR_THROW_AND_RETURN_NAPI(info, dict, RTCOnDataEventDict) - _source->PushData(dict); - return info.Env().Undefined(); -} - -void RTCAudioSource::Init(Napi::Env env, Napi::Object exports) { - Napi::HandleScope scope(env); - - Napi::Function func = - DefineClass(env, "RTCAudioSource", - {InstanceMethod("createTrack", &RTCAudioSource::CreateTrack), - InstanceMethod("onData", &RTCAudioSource::OnData)}); - - constructor() = Napi::Persistent(func); - constructor().SuppressDestruct(); - - exports.Set("RTCAudioSource", func); -} - -} // namespace node_webrtc +#include "src/interfaces/media_stream_track.hh" + +namespace node_webrtc { + +Napi::FunctionReference &RTCAudioSource::constructor() { + static Napi::FunctionReference constructor; + return constructor; +} + +RTCAudioSource::RTCAudioSource(const Napi::CallbackInfo &info) + : Napi::ObjectWrap(info) { + _source = rtc::make_ref_counted(); +} + +Napi::Value RTCAudioSource::CreateTrack(const Napi::CallbackInfo &) { + // TODO(mroberts): Again, we have some implicit factory we are threading + // around. How to handle? + auto factory = PeerConnectionFactory::GetOrCreateDefault(); + auto track = factory->factory()->CreateAudioTrack(rtc::CreateRandomUuid(), + _source.get()); + return _track_wrap.GetOrCreate(factory, track)->Value(); +} + +Napi::Value RTCAudioSource::OnData(const Napi::CallbackInfo &info) { + CONVERT_ARGS_OR_THROW_AND_RETURN_NAPI(info, dict, RTCOnDataEventDict) + _source->PushData(dict); + return info.Env().Undefined(); +} + +void RTCAudioSource::Init(Napi::Env env, Napi::Object exports) { + Napi::HandleScope scope(env); + + Napi::Function func = + DefineClass(env, "RTCAudioSource", + {InstanceMethod("createTrack", &RTCAudioSource::CreateTrack), + InstanceMethod("onData", &RTCAudioSource::OnData)}); + + constructor() = Napi::Persistent(func); + constructor().SuppressDestruct(); + + exports.Set("RTCAudioSource", func); +} + +} // namespace node_webrtc diff --git a/src/interfaces/rtc_data_channel.cc b/src/interfaces/rtc_data_channel.cc index 65485a1bf..b8efbeb4c 100644 --- a/src/interfaces/rtc_data_channel.cc +++ b/src/interfaces/rtc_data_channel.cc @@ -1,383 +1,387 @@ -/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license that can be found - * in the LICENSE.md file in the root of the source tree. All contributing - * project authors may be found in the AUTHORS file in the root of the source - * tree. - */ -#include "src/interfaces/rtc_data_channel.hh" +/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license that can be found + * in the LICENSE.md file in the root of the source tree. All contributing + * project authors may be found in the AUTHORS file in the root of the source + * tree. + */ +#include "src/interfaces/rtc_data_channel.hh" + +#include + +#include +#include +#include + +#include "src/enums/node_webrtc/binary_type.hh" +#include "src/enums/webrtc/data_state.hh" +#include "src/interfaces/rtc_peer_connection/peer_connection_factory.hh" +#include "src/node/error_factory.hh" +#include "src/node/events.hh" + +namespace node_webrtc { -#include +namespace { -#include -#include -#include +Napi::Value OptionalIntToNapi(Napi::Env env, const std::optional &value) { + return value ? Napi::Number::New(env, *value) : env.Null(); +} -#include "src/enums/node_webrtc/binary_type.hh" -#include "src/enums/webrtc/data_state.hh" -#include "src/interfaces/rtc_peer_connection/peer_connection_factory.hh" -#include "src/node/error_factory.hh" -#include "src/node/events.hh" - -namespace node_webrtc { +} // namespace Napi::FunctionReference &RTCDataChannel::constructor() { static Napi::FunctionReference constructor; return constructor; -} - -DataChannelObserver::DataChannelObserver( - PeerConnectionFactory *factory, - rtc::scoped_refptr jingleDataChannel) - : _factory(factory), _jingleDataChannel(std::move(jingleDataChannel)) { - _jingleDataChannel->RegisterObserver(this); -} - -void DataChannelObserver::OnStateChange() { - auto state = _jingleDataChannel->state(); - Enqueue(Callback1::Create([state](RTCDataChannel &channel) { - RTCDataChannel::HandleStateChange(channel, state); - })); -} - -void DataChannelObserver::OnMessage(const webrtc::DataBuffer &buffer) { - Enqueue(Callback1::Create([buffer](RTCDataChannel &channel) { - RTCDataChannel::HandleMessage(channel, buffer); - })); -} - -static void requeue(DataChannelObserver &observer, RTCDataChannel &channel) { - while (auto event = observer.Dequeue()) { - channel.Dispatch(std::move(event)); - } -} - -RTCDataChannel::RTCDataChannel(const Napi::CallbackInfo &info) - : AsyncObjectWrapWithLoop("RTCDataChannel", *this, info), - _binaryType(BinaryType::kArrayBuffer) { - auto env = info.Env(); - - if (!info.IsConstructCall() || !info[0].IsExternal()) { - Napi::TypeError::New( - env, "Use the new operator to construct the RTCDataChannel.") - .ThrowAsJavaScriptException(); - return; - } - - auto observer = - info[0].As>().Data(); - - _factory = observer->_factory; - - _jingleDataChannel = observer->_jingleDataChannel; - _jingleDataChannel->RegisterObserver(this); - - // Re-queue cached observer events - requeue(*observer, *this); - - delete observer; +} + +DataChannelObserver::DataChannelObserver( + PeerConnectionFactory *factory, + rtc::scoped_refptr jingleDataChannel) + : _factory(factory), _jingleDataChannel(std::move(jingleDataChannel)) { + _jingleDataChannel->RegisterObserver(this); +} + +void DataChannelObserver::OnStateChange() { + auto state = _jingleDataChannel->state(); + Enqueue(Callback1::Create([state](RTCDataChannel &channel) { + RTCDataChannel::HandleStateChange(channel, state); + })); +} + +void DataChannelObserver::OnMessage(const webrtc::DataBuffer &buffer) { + Enqueue(Callback1::Create([buffer](RTCDataChannel &channel) { + RTCDataChannel::HandleMessage(channel, buffer); + })); +} + +static void requeue(DataChannelObserver &observer, RTCDataChannel &channel) { + while (auto event = observer.Dequeue()) { + channel.Dispatch(std::move(event)); + } +} + +RTCDataChannel::RTCDataChannel(const Napi::CallbackInfo &info) + : AsyncObjectWrapWithLoop("RTCDataChannel", *this, info), + _binaryType(BinaryType::kArrayBuffer) { + auto env = info.Env(); + + if (!info.IsConstructCall() || !info[0].IsExternal()) { + Napi::TypeError::New( + env, "Use the new operator to construct the RTCDataChannel.") + .ThrowAsJavaScriptException(); + return; + } + + auto observer = + info[0].As>().Data(); + + _factory = observer->_factory; + + _jingleDataChannel = observer->_jingleDataChannel; + _jingleDataChannel->RegisterObserver(this); + + // Re-queue cached observer events + requeue(*observer, *this); + + delete observer; // NOTE(mroberts): These doesn't actually matter yet. _cached_id = 0; - _cached_max_packet_life_time = 0; - _cached_max_retransmits = 0; + _cached_max_packet_life_time = std::nullopt; + _cached_max_retransmits = std::nullopt; _cached_negotiated = false; _cached_ordered = false; _cached_buffered_amount = 0; } - -RTCDataChannel::~RTCDataChannel() { wrap()->Release(this); } - -void RTCDataChannel::CleanupInternals() { - if (_jingleDataChannel == nullptr) { - return; - } + +RTCDataChannel::~RTCDataChannel() { wrap()->Release(this); } + +void RTCDataChannel::CleanupInternals() { + if (_jingleDataChannel == nullptr) { + return; + } _jingleDataChannel->UnregisterObserver(); _cached_id = _jingleDataChannel->id(); _cached_label = _jingleDataChannel->label(); - _cached_max_packet_life_time = _jingleDataChannel->maxRetransmitTime(); - _cached_max_retransmits = _jingleDataChannel->maxRetransmits(); + _cached_max_packet_life_time = _jingleDataChannel->maxPacketLifeTime(); + _cached_max_retransmits = _jingleDataChannel->maxRetransmitsOpt(); _cached_negotiated = _jingleDataChannel->negotiated(); _cached_ordered = _jingleDataChannel->ordered(); _cached_protocol = _jingleDataChannel->protocol(); _cached_buffered_amount = _jingleDataChannel->buffered_amount(); - _jingleDataChannel = nullptr; -} - -void RTCDataChannel::OnPeerConnectionClosed() { - if (_jingleDataChannel != nullptr) { - Stop(); - } -} - -void RTCDataChannel::OnStateChange() { - auto state = _jingleDataChannel->state(); - if (state == webrtc::DataChannelInterface::kClosed) { - CleanupInternals(); - } - Dispatch(CreateCallback( - [this, state]() { RTCDataChannel::HandleStateChange(*this, state); })); -} - -void RTCDataChannel::HandleStateChange( - RTCDataChannel &channel, webrtc::DataChannelInterface::DataState state) { - auto env = channel.Env(); - Napi::HandleScope scope(env); - auto object = Napi::Object::New(env); - if (state == webrtc::DataChannelInterface::kClosed) { - object.Set("type", Napi::String::New(env, "close")); - } else if (state == webrtc::DataChannelInterface::kOpen) { - object.Set("type", Napi::String::New(env, "open")); - } - channel.MakeCallback("dispatchEvent", {object}); - if (state == webrtc::DataChannelInterface::kClosed) { - channel.Stop(); - } -} - -void RTCDataChannel::OnMessage(const webrtc::DataBuffer &buffer) { - Dispatch(CreateCallback( - [this, buffer]() { RTCDataChannel::HandleMessage(*this, buffer); })); -} - -void RTCDataChannel::HandleMessage(RTCDataChannel &channel, - const webrtc::DataBuffer &buffer) { - bool binary = buffer.binary; - size_t size = buffer.size(); - - auto env = channel.Env(); - Napi::HandleScope scope(env); - Napi::Value value; - if (binary) { - char *message = new char[size]; - memcpy(message, buffer.data.data(), size); - auto array = - Napi::ArrayBuffer::New(env, message, size, [](Napi::Env, void *buffer) { - delete[] static_cast(buffer); - }); - value = array; - } else { - // SAFETY: this reinterpret_cast is correct; if the message is not binary, - // it should be a valid UTF-8 string. - // NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast) - auto str = Napi::String::New( - env, reinterpret_cast(buffer.data.data()), size); - // NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast) - value = str; - } - auto object = Napi::Object::New(env); - object.Set("type", "message"); - object.Set("data", value); - channel.MakeCallback("dispatchEvent", {object}); -} - -Napi::Value RTCDataChannel::Send(const Napi::CallbackInfo &info) { - auto env = info.Env(); - if (_jingleDataChannel != nullptr) { - if (_jingleDataChannel->state() != - webrtc::DataChannelInterface::DataState::kOpen) { - Napi::Error(env, ErrorFactory::CreateInvalidStateError( - env, "RTCDataChannel.readyState is not 'open'")) - .ThrowAsJavaScriptException(); - return env.Undefined(); - } - if (info[0].IsString()) { - auto str = info[0].ToString(); - auto data = str.Utf8Value(); - - webrtc::DataBuffer buffer(data); - _jingleDataChannel->Send(buffer); - } else { - Napi::ArrayBuffer arraybuffer; - size_t byte_offset = 0; - size_t byte_length = 0; - - if (info[0].IsTypedArray()) { - auto typedArray = info[0].As(); - arraybuffer = typedArray.ArrayBuffer(); - byte_offset = typedArray.ByteOffset(); - byte_length = typedArray.ByteLength(); - } else if (info[0].IsDataView()) { - auto dataView = info[0].As(); - arraybuffer = dataView.ArrayBuffer(); - byte_offset = dataView.ByteOffset(); - byte_length = dataView.ByteLength(); - } else if (info[0].IsArrayBuffer()) { - arraybuffer = info[0].As(); - byte_length = arraybuffer.ByteLength(); - } else { - Napi::TypeError::New(env, "Expected a Blob or ArrayBuffer") - .ThrowAsJavaScriptException(); - return env.Undefined(); - } - -#pragma clang unsafe_buffer_usage begin - auto content = static_cast(arraybuffer.Data()); - rtc::CopyOnWriteBuffer buffer(content + byte_offset, byte_length); -#pragma clang unsafe_buffer_usage end - - webrtc::DataBuffer data_buffer(buffer, true); - _jingleDataChannel->Send(data_buffer); - } - } else { - Napi::Error(env, ErrorFactory::CreateInvalidStateError( - env, "RTCDataChannel.readyState is not 'open'")) - .ThrowAsJavaScriptException(); - return env.Undefined(); - } - - return env.Undefined(); -} - -Napi::Value RTCDataChannel::Close(const Napi::CallbackInfo &info) { - if (_jingleDataChannel != nullptr) { - _jingleDataChannel->Close(); - } - return info.Env().Undefined(); -} - -Napi::Value RTCDataChannel::GetBufferedAmount(const Napi::CallbackInfo &info) { - uint64_t buffered_amount = _jingleDataChannel != nullptr - ? _jingleDataChannel->buffered_amount() - : _cached_buffered_amount; - CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), buffered_amount, result, - Napi::Value) - return result; -} - -Napi::Value RTCDataChannel::GetId(const Napi::CallbackInfo &info) { - auto id = _jingleDataChannel ? _jingleDataChannel->id() : _cached_id; - CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), id, result, Napi::Value) - return result; -} - -Napi::Value RTCDataChannel::GetLabel(const Napi::CallbackInfo &info) { - auto label = _jingleDataChannel != nullptr ? _jingleDataChannel->label() - : _cached_label; - CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), label, result, Napi::Value) - return result; -} - + _jingleDataChannel = nullptr; +} + +void RTCDataChannel::OnPeerConnectionClosed() { + if (_jingleDataChannel != nullptr) { + Stop(); + } +} + +void RTCDataChannel::OnStateChange() { + auto state = _jingleDataChannel->state(); + if (state == webrtc::DataChannelInterface::kClosed) { + CleanupInternals(); + } + Dispatch(CreateCallback( + [this, state]() { RTCDataChannel::HandleStateChange(*this, state); })); +} + +void RTCDataChannel::HandleStateChange( + RTCDataChannel &channel, webrtc::DataChannelInterface::DataState state) { + auto env = channel.Env(); + Napi::HandleScope scope(env); + auto object = Napi::Object::New(env); + if (state == webrtc::DataChannelInterface::kClosed) { + object.Set("type", Napi::String::New(env, "close")); + } else if (state == webrtc::DataChannelInterface::kOpen) { + object.Set("type", Napi::String::New(env, "open")); + } + channel.MakeCallback("dispatchEvent", {object}); + if (state == webrtc::DataChannelInterface::kClosed) { + channel.Stop(); + } +} + +void RTCDataChannel::OnMessage(const webrtc::DataBuffer &buffer) { + Dispatch(CreateCallback( + [this, buffer]() { RTCDataChannel::HandleMessage(*this, buffer); })); +} + +void RTCDataChannel::HandleMessage(RTCDataChannel &channel, + const webrtc::DataBuffer &buffer) { + bool binary = buffer.binary; + size_t size = buffer.size(); + + auto env = channel.Env(); + Napi::HandleScope scope(env); + Napi::Value value; + if (binary) { + char *message = new char[size]; + memcpy(message, buffer.data.data(), size); + auto array = + Napi::ArrayBuffer::New(env, message, size, [](Napi::Env, void *buffer) { + delete[] static_cast(buffer); + }); + value = array; + } else { + // SAFETY: this reinterpret_cast is correct; if the message is not binary, + // it should be a valid UTF-8 string. + // NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast) + auto str = Napi::String::New( + env, reinterpret_cast(buffer.data.data()), size); + // NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast) + value = str; + } + auto object = Napi::Object::New(env); + object.Set("type", "message"); + object.Set("data", value); + channel.MakeCallback("dispatchEvent", {object}); +} + +Napi::Value RTCDataChannel::Send(const Napi::CallbackInfo &info) { + auto env = info.Env(); + if (_jingleDataChannel != nullptr) { + if (_jingleDataChannel->state() != + webrtc::DataChannelInterface::DataState::kOpen) { + Napi::Error(env, ErrorFactory::CreateInvalidStateError( + env, "RTCDataChannel.readyState is not 'open'")) + .ThrowAsJavaScriptException(); + return env.Undefined(); + } + if (info[0].IsString()) { + auto str = info[0].ToString(); + auto data = str.Utf8Value(); + + webrtc::DataBuffer buffer(data); + _jingleDataChannel->Send(buffer); + } else { + Napi::ArrayBuffer arraybuffer; + size_t byte_offset = 0; + size_t byte_length = 0; + + if (info[0].IsTypedArray()) { + auto typedArray = info[0].As(); + arraybuffer = typedArray.ArrayBuffer(); + byte_offset = typedArray.ByteOffset(); + byte_length = typedArray.ByteLength(); + } else if (info[0].IsDataView()) { + auto dataView = info[0].As(); + arraybuffer = dataView.ArrayBuffer(); + byte_offset = dataView.ByteOffset(); + byte_length = dataView.ByteLength(); + } else if (info[0].IsArrayBuffer()) { + arraybuffer = info[0].As(); + byte_length = arraybuffer.ByteLength(); + } else { + Napi::TypeError::New(env, "Expected a Blob or ArrayBuffer") + .ThrowAsJavaScriptException(); + return env.Undefined(); + } + +#pragma clang unsafe_buffer_usage begin + auto content = static_cast(arraybuffer.Data()); + rtc::CopyOnWriteBuffer buffer(content + byte_offset, byte_length); +#pragma clang unsafe_buffer_usage end + + webrtc::DataBuffer data_buffer(buffer, true); + _jingleDataChannel->Send(data_buffer); + } + } else { + Napi::Error(env, ErrorFactory::CreateInvalidStateError( + env, "RTCDataChannel.readyState is not 'open'")) + .ThrowAsJavaScriptException(); + return env.Undefined(); + } + + return env.Undefined(); +} + +Napi::Value RTCDataChannel::Close(const Napi::CallbackInfo &info) { + if (_jingleDataChannel != nullptr) { + _jingleDataChannel->Close(); + } + return info.Env().Undefined(); +} + +Napi::Value RTCDataChannel::GetBufferedAmount(const Napi::CallbackInfo &info) { + uint64_t buffered_amount = _jingleDataChannel != nullptr + ? _jingleDataChannel->buffered_amount() + : _cached_buffered_amount; + CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), buffered_amount, result, + Napi::Value) + return result; +} + +Napi::Value RTCDataChannel::GetId(const Napi::CallbackInfo &info) { + auto id = _jingleDataChannel ? _jingleDataChannel->id() : _cached_id; + CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), id, result, Napi::Value) + return result; +} + +Napi::Value RTCDataChannel::GetLabel(const Napi::CallbackInfo &info) { + auto label = _jingleDataChannel != nullptr ? _jingleDataChannel->label() + : _cached_label; + CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), label, result, Napi::Value) + return result; +} + Napi::Value RTCDataChannel::GetMaxPacketLifeTime(const Napi::CallbackInfo &info) { auto max_packet_life_time = _jingleDataChannel - ? _jingleDataChannel->maxRetransmitTime() + ? _jingleDataChannel->maxPacketLifeTime() : _cached_max_packet_life_time; - CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), max_packet_life_time, result, - Napi::Value) - return result; + return OptionalIntToNapi(info.Env(), max_packet_life_time); } Napi::Value RTCDataChannel::GetMaxRetransmits(const Napi::CallbackInfo &info) { auto max_retransmits = _jingleDataChannel - ? _jingleDataChannel->maxRetransmits() + ? _jingleDataChannel->maxRetransmitsOpt() : _cached_max_retransmits; - CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), max_retransmits, result, - Napi::Value) - return result; + return OptionalIntToNapi(info.Env(), max_retransmits); } - -Napi::Value RTCDataChannel::GetNegotiated(const Napi::CallbackInfo &info) { - auto negotiated = _jingleDataChannel ? _jingleDataChannel->negotiated() - : _cached_negotiated; - CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), negotiated, result, Napi::Value) - return result; -} - -Napi::Value RTCDataChannel::GetOrdered(const Napi::CallbackInfo &info) { - auto ordered = - _jingleDataChannel ? _jingleDataChannel->ordered() : _cached_ordered; - CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), ordered, result, Napi::Value) - return result; -} - -Napi::Value RTCDataChannel::GetPriority(const Napi::CallbackInfo &info) { - std::string priority = "high"; - CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), priority, result, Napi::Value) - return result; -} - -Napi::Value RTCDataChannel::GetProtocol(const Napi::CallbackInfo &info) { - auto protocol = - _jingleDataChannel ? _jingleDataChannel->protocol() : _cached_protocol; - CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), protocol, result, Napi::Value) - return result; -} - -Napi::Value RTCDataChannel::GetReadyState(const Napi::CallbackInfo &info) { - auto state = _jingleDataChannel ? _jingleDataChannel->state() - : webrtc::DataChannelInterface::kClosed; - CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), state, result, Napi::Value) - return result; -} - -Napi::Value RTCDataChannel::GetBinaryType(const Napi::CallbackInfo &info) { - CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), _binaryType, result, Napi::Value) - return result; -} - -void RTCDataChannel::SetBinaryType(const Napi::CallbackInfo &info, - const Napi::Value &value) { - auto maybeBinaryType = From(value); - if (maybeBinaryType.IsInvalid()) { - Napi::TypeError::New(info.Env(), maybeBinaryType.ToErrors()[0]) - .ThrowAsJavaScriptException(); - return; - } - _binaryType = maybeBinaryType.UnsafeFromValid(); -} - -Wrap, - node_webrtc::DataChannelObserver *> * -RTCDataChannel::wrap() { - static auto wrap = - new node_webrtc::Wrap, - node_webrtc::DataChannelObserver *>( - RTCDataChannel::Create); - return wrap; -} - -RTCDataChannel *RTCDataChannel::Create( - node_webrtc::DataChannelObserver *observer, - // TODO(jack): see if this is actually needed to keep the ref alive for - // long enough, or if it can be deleted. - rtc::scoped_refptr) { // NOLINT - auto env = constructor().Env(); - Napi::HandleScope scope(env); - - auto object = constructor().New( - {Napi::External::New(env, observer)}); - - auto unwrapped = Unwrap(object); - return unwrapped; -} - -void RTCDataChannel::Init(Napi::Env env, Napi::Object exports) { - auto func = DefineClass( - env, "RTCDataChannel", - {InstanceAccessor("bufferedAmount", &RTCDataChannel::GetBufferedAmount, - nullptr), - InstanceAccessor("id", &RTCDataChannel::GetId, nullptr), - InstanceAccessor("label", &RTCDataChannel::GetLabel, nullptr), - InstanceAccessor("maxPacketLifeTime", - &RTCDataChannel::GetMaxPacketLifeTime, nullptr), - InstanceAccessor("maxRetransmits", &RTCDataChannel::GetMaxRetransmits, - nullptr), - InstanceAccessor("negotiated", &RTCDataChannel::GetNegotiated, nullptr), - InstanceAccessor("ordered", &RTCDataChannel::GetOrdered, nullptr), - InstanceAccessor("priority", &RTCDataChannel::GetPriority, nullptr), - InstanceAccessor("protocol", &RTCDataChannel::GetProtocol, nullptr), - InstanceAccessor("binaryType", &RTCDataChannel::GetBinaryType, - &RTCDataChannel::SetBinaryType), - InstanceAccessor("readyState", &RTCDataChannel::GetReadyState, nullptr), - InstanceMethod("close", &RTCDataChannel::Close), - InstanceMethod("_send", &RTCDataChannel::Send)}); - - constructor() = Napi::Persistent(func); - constructor().SuppressDestruct(); - - exports.Set("RTCDataChannel", func); -} - -} // namespace node_webrtc + +Napi::Value RTCDataChannel::GetNegotiated(const Napi::CallbackInfo &info) { + auto negotiated = _jingleDataChannel ? _jingleDataChannel->negotiated() + : _cached_negotiated; + CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), negotiated, result, Napi::Value) + return result; +} + +Napi::Value RTCDataChannel::GetOrdered(const Napi::CallbackInfo &info) { + auto ordered = + _jingleDataChannel ? _jingleDataChannel->ordered() : _cached_ordered; + CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), ordered, result, Napi::Value) + return result; +} + +Napi::Value RTCDataChannel::GetPriority(const Napi::CallbackInfo &info) { + std::string priority = "high"; + CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), priority, result, Napi::Value) + return result; +} + +Napi::Value RTCDataChannel::GetProtocol(const Napi::CallbackInfo &info) { + auto protocol = + _jingleDataChannel ? _jingleDataChannel->protocol() : _cached_protocol; + CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), protocol, result, Napi::Value) + return result; +} + +Napi::Value RTCDataChannel::GetReadyState(const Napi::CallbackInfo &info) { + auto state = _jingleDataChannel ? _jingleDataChannel->state() + : webrtc::DataChannelInterface::kClosed; + CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), state, result, Napi::Value) + return result; +} + +Napi::Value RTCDataChannel::GetBinaryType(const Napi::CallbackInfo &info) { + CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), _binaryType, result, Napi::Value) + return result; +} + +void RTCDataChannel::SetBinaryType(const Napi::CallbackInfo &info, + const Napi::Value &value) { + auto maybeBinaryType = From(value); + if (maybeBinaryType.IsInvalid()) { + Napi::TypeError::New(info.Env(), maybeBinaryType.ToErrors()[0]) + .ThrowAsJavaScriptException(); + return; + } + _binaryType = maybeBinaryType.UnsafeFromValid(); +} + +Wrap, + node_webrtc::DataChannelObserver *> * +RTCDataChannel::wrap() { + static auto wrap = + new node_webrtc::Wrap, + node_webrtc::DataChannelObserver *>( + RTCDataChannel::Create); + return wrap; +} + +RTCDataChannel *RTCDataChannel::Create( + node_webrtc::DataChannelObserver *observer, + // TODO(jack): see if this is actually needed to keep the ref alive for + // long enough, or if it can be deleted. + rtc::scoped_refptr) { // NOLINT + auto env = constructor().Env(); + Napi::HandleScope scope(env); + + auto object = constructor().New( + {Napi::External::New(env, observer)}); + + auto unwrapped = Unwrap(object); + return unwrapped; +} + +void RTCDataChannel::Init(Napi::Env env, Napi::Object exports) { + auto func = DefineClass( + env, "RTCDataChannel", + {InstanceAccessor("bufferedAmount", &RTCDataChannel::GetBufferedAmount, + nullptr), + InstanceAccessor("id", &RTCDataChannel::GetId, nullptr), + InstanceAccessor("label", &RTCDataChannel::GetLabel, nullptr), + InstanceAccessor("maxPacketLifeTime", + &RTCDataChannel::GetMaxPacketLifeTime, nullptr), + InstanceAccessor("maxRetransmits", &RTCDataChannel::GetMaxRetransmits, + nullptr), + InstanceAccessor("negotiated", &RTCDataChannel::GetNegotiated, nullptr), + InstanceAccessor("ordered", &RTCDataChannel::GetOrdered, nullptr), + InstanceAccessor("priority", &RTCDataChannel::GetPriority, nullptr), + InstanceAccessor("protocol", &RTCDataChannel::GetProtocol, nullptr), + InstanceAccessor("binaryType", &RTCDataChannel::GetBinaryType, + &RTCDataChannel::SetBinaryType), + InstanceAccessor("readyState", &RTCDataChannel::GetReadyState, nullptr), + InstanceMethod("close", &RTCDataChannel::Close), + InstanceMethod("_send", &RTCDataChannel::Send)}); + + constructor() = Napi::Persistent(func); + constructor().SuppressDestruct(); + + exports.Set("RTCDataChannel", func); +} + +} // namespace node_webrtc diff --git a/src/interfaces/rtc_data_channel.hh b/src/interfaces/rtc_data_channel.hh index 1067b3e23..e6d2381d1 100644 --- a/src/interfaces/rtc_data_channel.hh +++ b/src/interfaces/rtc_data_channel.hh @@ -1,121 +1,123 @@ -/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license that can be found - * in the LICENSE.md file in the root of the source tree. All contributing - * project authors may be found in the AUTHORS file in the root of the source - * tree. - */ +/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license that can be found + * in the LICENSE.md file in the root of the source tree. All contributing + * project authors may be found in the AUTHORS file in the root of the source + * tree. + */ #pragma once +#include + #include #include - -#include "src/enums/node_webrtc/binary_type.hh" -#include "src/interfaces/rtc_peer_connection/peer_connection_factory.hh" -#include "src/node/async_object_wrap_with_loop.hh" -#include "src/node/event_queue.hh" -#include "src/node/ref_ptr.hh" -#include "src/node/wrap.hh" - -namespace node_webrtc { - -class DataChannelObserver; - -class RTCDataChannel : public AsyncObjectWrapWithLoop, - public webrtc::DataChannelObserver { - friend class node_webrtc::DataChannelObserver; - -public: - explicit RTCDataChannel(const Napi::CallbackInfo &); - ~RTCDataChannel() override; - - RTCDataChannel(const RTCDataChannel &) = delete; - RTCDataChannel(RTCDataChannel &&) = delete; - RTCDataChannel &operator=(const RTCDataChannel &) = delete; - RTCDataChannel &operator=(RTCDataChannel &&) = delete; - - static Napi::FunctionReference &constructor(); - - static void Init(Napi::Env, Napi::Object); - - // - // DataChannelObserver implementation. - // - void OnStateChange() override; - void OnMessage(const webrtc::DataBuffer &buffer) override; - - void OnPeerConnectionClosed(); - - static ::node_webrtc::Wrap, - node_webrtc::DataChannelObserver *> * - wrap(); - -private: - static RTCDataChannel * - Create(node_webrtc::DataChannelObserver *, - rtc::scoped_refptr); - - static void HandleStateChange(RTCDataChannel &, - webrtc::DataChannelInterface::DataState); - static void HandleMessage(RTCDataChannel &, const webrtc::DataBuffer &buffer); - - Napi::Value Send(const Napi::CallbackInfo &); - Napi::Value Close(const Napi::CallbackInfo &); - - Napi::Value GetBufferedAmount(const Napi::CallbackInfo &); - Napi::Value GetId(const Napi::CallbackInfo &); - Napi::Value GetLabel(const Napi::CallbackInfo &); - Napi::Value GetMaxPacketLifeTime(const Napi::CallbackInfo &); - Napi::Value GetMaxRetransmits(const Napi::CallbackInfo &); - Napi::Value GetNegotiated(const Napi::CallbackInfo &); - Napi::Value GetOrdered(const Napi::CallbackInfo &); - Napi::Value GetPriority(const Napi::CallbackInfo &); - Napi::Value GetProtocol(const Napi::CallbackInfo &); - Napi::Value GetBinaryType(const Napi::CallbackInfo &); - Napi::Value GetReadyState(const Napi::CallbackInfo &); - void SetBinaryType(const Napi::CallbackInfo &, const Napi::Value &); - - void CleanupInternals(); - + +#include "src/enums/node_webrtc/binary_type.hh" +#include "src/interfaces/rtc_peer_connection/peer_connection_factory.hh" +#include "src/node/async_object_wrap_with_loop.hh" +#include "src/node/event_queue.hh" +#include "src/node/ref_ptr.hh" +#include "src/node/wrap.hh" + +namespace node_webrtc { + +class DataChannelObserver; + +class RTCDataChannel : public AsyncObjectWrapWithLoop, + public webrtc::DataChannelObserver { + friend class node_webrtc::DataChannelObserver; + +public: + explicit RTCDataChannel(const Napi::CallbackInfo &); + ~RTCDataChannel() override; + + RTCDataChannel(const RTCDataChannel &) = delete; + RTCDataChannel(RTCDataChannel &&) = delete; + RTCDataChannel &operator=(const RTCDataChannel &) = delete; + RTCDataChannel &operator=(RTCDataChannel &&) = delete; + + static Napi::FunctionReference &constructor(); + + static void Init(Napi::Env, Napi::Object); + + // + // DataChannelObserver implementation. + // + void OnStateChange() override; + void OnMessage(const webrtc::DataBuffer &buffer) override; + + void OnPeerConnectionClosed(); + + static ::node_webrtc::Wrap, + node_webrtc::DataChannelObserver *> * + wrap(); + +private: + static RTCDataChannel * + Create(node_webrtc::DataChannelObserver *, + rtc::scoped_refptr); + + static void HandleStateChange(RTCDataChannel &, + webrtc::DataChannelInterface::DataState); + static void HandleMessage(RTCDataChannel &, const webrtc::DataBuffer &buffer); + + Napi::Value Send(const Napi::CallbackInfo &); + Napi::Value Close(const Napi::CallbackInfo &); + + Napi::Value GetBufferedAmount(const Napi::CallbackInfo &); + Napi::Value GetId(const Napi::CallbackInfo &); + Napi::Value GetLabel(const Napi::CallbackInfo &); + Napi::Value GetMaxPacketLifeTime(const Napi::CallbackInfo &); + Napi::Value GetMaxRetransmits(const Napi::CallbackInfo &); + Napi::Value GetNegotiated(const Napi::CallbackInfo &); + Napi::Value GetOrdered(const Napi::CallbackInfo &); + Napi::Value GetPriority(const Napi::CallbackInfo &); + Napi::Value GetProtocol(const Napi::CallbackInfo &); + Napi::Value GetBinaryType(const Napi::CallbackInfo &); + Napi::Value GetReadyState(const Napi::CallbackInfo &); + void SetBinaryType(const Napi::CallbackInfo &, const Napi::Value &); + + void CleanupInternals(); + BinaryType _binaryType; int _cached_id; std::string _cached_label; - uint16_t _cached_max_packet_life_time; - uint16_t _cached_max_retransmits; + std::optional _cached_max_packet_life_time; + std::optional _cached_max_retransmits; bool _cached_negotiated; bool _cached_ordered; std::string _cached_protocol; - uint64_t _cached_buffered_amount; - RefPtr _factory; - rtc::scoped_refptr _jingleDataChannel; -}; - -class DataChannelObserver : public EventQueue, - public webrtc::DataChannelObserver { - friend class RTCDataChannel; - -public: - DataChannelObserver( - PeerConnectionFactory *factory, - rtc::scoped_refptr jingleDataChannel); - ~DataChannelObserver() override = default; - - DataChannelObserver(const DataChannelObserver &) = delete; - DataChannelObserver(DataChannelObserver &&) = delete; - DataChannelObserver &operator=(const DataChannelObserver &) = delete; - DataChannelObserver &operator=(DataChannelObserver &&) = delete; - - void OnStateChange() override; - void OnMessage(const webrtc::DataBuffer &buffer) override; - - rtc::scoped_refptr channel() { - return _jingleDataChannel; - } - -private: - RefPtr _factory; - rtc::scoped_refptr _jingleDataChannel; -}; - -} // namespace node_webrtc + uint64_t _cached_buffered_amount; + RefPtr _factory; + rtc::scoped_refptr _jingleDataChannel; +}; + +class DataChannelObserver : public EventQueue, + public webrtc::DataChannelObserver { + friend class RTCDataChannel; + +public: + DataChannelObserver( + PeerConnectionFactory *factory, + rtc::scoped_refptr jingleDataChannel); + ~DataChannelObserver() override = default; + + DataChannelObserver(const DataChannelObserver &) = delete; + DataChannelObserver(DataChannelObserver &&) = delete; + DataChannelObserver &operator=(const DataChannelObserver &) = delete; + DataChannelObserver &operator=(DataChannelObserver &&) = delete; + + void OnStateChange() override; + void OnMessage(const webrtc::DataBuffer &buffer) override; + + rtc::scoped_refptr channel() { + return _jingleDataChannel; + } + +private: + RefPtr _factory; + rtc::scoped_refptr _jingleDataChannel; +}; + +} // namespace node_webrtc diff --git a/src/interfaces/rtc_ice_transport.cc b/src/interfaces/rtc_ice_transport.cc index 8d7c5e656..941ff6aff 100644 --- a/src/interfaces/rtc_ice_transport.cc +++ b/src/interfaces/rtc_ice_transport.cc @@ -1,240 +1,247 @@ -/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license that can be found - * in the LICENSE.md file in the root of the source tree. All contributing - * project authors may be found in the AUTHORS file in the root of the source - * tree. - */ -#include "src/interfaces/rtc_ice_transport.hh" - -#include "src/converters/arguments.hh" -#include "src/enums/webrtc/ice_connection_state.hh" -#include "src/enums/webrtc/ice_gathering_state.hh" -#include "src/enums/webrtc/ice_role.hh" -#include "src/enums/webrtc/ice_transport_state.hh" -#include "src/interfaces/rtc_peer_connection/peer_connection_factory.hh" - -namespace node_webrtc { - -Napi::FunctionReference &RTCIceTransport::constructor() { - static Napi::FunctionReference constructor; - return constructor; -} - -RTCIceTransport::RTCIceTransport(const Napi::CallbackInfo &info) - : AsyncObjectWrapWithLoop("RTCIceTransport", *this, info) { - if (info.Length() != 2 || !info[0].IsObject() || !info[1].IsExternal()) { - Napi::TypeError::New(info.Env(), "You cannot construct an RTCIceTransport") - .ThrowAsJavaScriptException(); - return; - } - - _factory = PeerConnectionFactory::Unwrap(info[0].ToObject()); - auto transport = - *info[1] - .As>>() - .Data(); - - _transport = std::move(transport); - +/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license that can be found + * in the LICENSE.md file in the root of the source tree. All contributing + * project authors may be found in the AUTHORS file in the root of the source + * tree. + */ +#include "src/interfaces/rtc_ice_transport.hh" + +#include "src/converters/arguments.hh" +#include "src/enums/webrtc/ice_connection_state.hh" +#include "src/enums/webrtc/ice_gathering_state.hh" +#include "src/enums/webrtc/ice_role.hh" +#include "src/enums/webrtc/ice_transport_state.hh" +#include "src/interfaces/rtc_peer_connection/peer_connection_factory.hh" + +namespace node_webrtc { + +Napi::FunctionReference &RTCIceTransport::constructor() { + static Napi::FunctionReference constructor; + return constructor; +} + +RTCIceTransport::RTCIceTransport(const Napi::CallbackInfo &info) + : AsyncObjectWrapWithLoop("RTCIceTransport", *this, info) { + if (info.Length() != 2 || !info[0].IsObject() || !info[1].IsExternal()) { + Napi::TypeError::New(info.Env(), "You cannot construct an RTCIceTransport") + .ThrowAsJavaScriptException(); + return; + } + + _factory = PeerConnectionFactory::Unwrap(info[0].ToObject()); + auto transport = + *info[1] + .As>>() + .Data(); + + _transport = std::move(transport); + _factory->WorkerThread()->BlockingCall([this]() { auto internal = _transport->internal(); if (internal) { - internal->SignalIceTransportStateChanged.connect( - this, &RTCIceTransport::OnStateChanged); - internal->SignalGatheringState.connect( - this, &RTCIceTransport::OnGatheringStateChanged); + internal->SubscribeIceTransportStateChanged( + this, [this](webrtc::IceTransportInternal *transport) { + OnStateChanged(transport); + }); + internal->AddGatheringStateCallback( + this, [this](webrtc::IceTransportInternal *transport) { + OnGatheringStateChanged(transport); + }); } TakeSnapshot(); if (_state == webrtc::IceTransportState::kClosed) { Stop(); - } - }); -} - -void RTCIceTransport::TakeSnapshot() { - std::lock_guard lock(_mutex); - auto internal = _transport->internal(); - if (internal) { - _role = internal->GetIceRole(); - _component = internal->component() == 1 ? RTCIceComponent::kRtp - : RTCIceComponent::kRtcp; - _state = internal->GetIceTransportState(); - _gathering_state = internal->gathering_state(); + } + }); +} + +void RTCIceTransport::TakeSnapshot() { + std::lock_guard lock(_mutex); + auto internal = _transport->internal(); + if (internal) { + _role = internal->GetIceRole(); + _component = internal->component() == 1 ? RTCIceComponent::kRtp + : RTCIceComponent::kRtcp; + _state = internal->GetIceTransportState(); + _gathering_state = internal->gathering_state(); } else { _state = webrtc::IceTransportState::kClosed; - _gathering_state = cricket::IceGatheringState::kIceGatheringComplete; + _gathering_state = webrtc::IceGatheringState::kIceGatheringComplete; } } - -RTCIceTransport::~RTCIceTransport() { wrap()->Release(this); } - + +RTCIceTransport::~RTCIceTransport() { wrap()->Release(this); } + void RTCIceTransport::OnRTCDtlsTransportStopped() { std::lock_guard lock(_mutex); _state = webrtc::IceTransportState::kClosed; - _gathering_state = cricket::IceGatheringState::kIceGatheringComplete; + _gathering_state = webrtc::IceGatheringState::kIceGatheringComplete; Stop(); } void RTCIceTransport::Stop() { - // _factory->_workerThread->Invoke(RTC_FROM_HERE, [this]() { - // _transport->internal()->SignalIceTransportStateChanged.disconnect(this); - // _transport->internal()->SignalGatheringState.disconnect(this); - // }); - AsyncObjectWrapWithLoop::Stop(); -} - -Wrap, - PeerConnectionFactory *> * -RTCIceTransport::wrap() { - static auto wrap = - new node_webrtc::Wrap, - PeerConnectionFactory *>(RTCIceTransport::Create); - return wrap; -} - -RTCIceTransport *RTCIceTransport::Create( - PeerConnectionFactory *factory, - rtc::scoped_refptr transport) { - auto env = constructor().Env(); - Napi::HandleScope scope(env); - - auto object = constructor().New( - {factory->Value(), - Napi::External>::New( - env, &transport)}); - - auto unwrapped = Unwrap(object); - return unwrapped; -} - -void RTCIceTransport::OnStateChanged(cricket::IceTransportInternal *) { - TakeSnapshot(); - - Dispatch(CreateCallback([this]() { - auto env = Env(); - Napi::HandleScope scope(env); - auto event = Napi::Object::New(env); - event.Set("type", Napi::String::New(env, "statechange")); - MakeCallback("dispatchEvent", {event}); - })); - - if (_state == webrtc::IceTransportState::kClosed) { - Stop(); + if (_transport) { + auto internal = _transport->internal(); + if (internal) { + internal->RemoveGatheringStateCallback(this); + } } + AsyncObjectWrapWithLoop::Stop(); } - -void RTCIceTransport::OnGatheringStateChanged(cricket::IceTransportInternal *) { - TakeSnapshot(); - - Dispatch(CreateCallback([this]() { - auto env = Env(); - Napi::HandleScope scope(env); - auto event = Napi::Object::New(env); - event.Set("type", Napi::String::New(env, "gatheringstatechange")); - MakeCallback("dispatchEvent", {event}); - })); -} - -Napi::Value RTCIceTransport::GetRole(const Napi::CallbackInfo &info) { - std::lock_guard lock(_mutex); - CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), _role, result, Napi::Value) - return result; -} - -Napi::Value RTCIceTransport::GetComponent(const Napi::CallbackInfo &info) { - std::lock_guard lock(_mutex); - CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), _component, result, Napi::Value) - return result; -} - -Napi::Value RTCIceTransport::GetState(const Napi::CallbackInfo &info) { - std::lock_guard lock(_mutex); - CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), _state, result, Napi::Value) - return result; -} - -Napi::Value RTCIceTransport::GetGatheringState(const Napi::CallbackInfo &info) { - std::lock_guard lock(_mutex); + +Wrap, + PeerConnectionFactory *> * +RTCIceTransport::wrap() { + static auto wrap = + new node_webrtc::Wrap, + PeerConnectionFactory *>(RTCIceTransport::Create); + return wrap; +} + +RTCIceTransport *RTCIceTransport::Create( + PeerConnectionFactory *factory, + rtc::scoped_refptr transport) { + auto env = constructor().Env(); + Napi::HandleScope scope(env); + + auto object = constructor().New( + {factory->Value(), + Napi::External>::New( + env, &transport)}); + + auto unwrapped = Unwrap(object); + return unwrapped; +} + +void RTCIceTransport::OnStateChanged(webrtc::IceTransportInternal *) { + TakeSnapshot(); + + Dispatch(CreateCallback([this]() { + auto env = Env(); + Napi::HandleScope scope(env); + auto event = Napi::Object::New(env); + event.Set("type", Napi::String::New(env, "statechange")); + MakeCallback("dispatchEvent", {event}); + })); + + if (_state == webrtc::IceTransportState::kClosed) { + Stop(); + } +} + +void RTCIceTransport::OnGatheringStateChanged( + webrtc::IceTransportInternal *) { + TakeSnapshot(); + + Dispatch(CreateCallback([this]() { + auto env = Env(); + Napi::HandleScope scope(env); + auto event = Napi::Object::New(env); + event.Set("type", Napi::String::New(env, "gatheringstatechange")); + MakeCallback("dispatchEvent", {event}); + })); +} + +Napi::Value RTCIceTransport::GetRole(const Napi::CallbackInfo &info) { + std::lock_guard lock(_mutex); + CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), _role, result, Napi::Value) + return result; +} + +Napi::Value RTCIceTransport::GetComponent(const Napi::CallbackInfo &info) { + std::lock_guard lock(_mutex); + CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), _component, result, Napi::Value) + return result; +} + +Napi::Value RTCIceTransport::GetState(const Napi::CallbackInfo &info) { + std::lock_guard lock(_mutex); + CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), _state, result, Napi::Value) + return result; +} + +Napi::Value RTCIceTransport::GetGatheringState(const Napi::CallbackInfo &info) { + std::lock_guard lock(_mutex); webrtc::PeerConnectionInterface::IceGatheringState state = {}; switch (_gathering_state) { - case cricket::IceGatheringState::kIceGatheringNew: + case webrtc::IceGatheringState::kIceGatheringNew: state = webrtc::PeerConnectionInterface::IceGatheringState::kIceGatheringNew; break; - case cricket::IceGatheringState::kIceGatheringGathering: + case webrtc::IceGatheringState::kIceGatheringGathering: state = webrtc::PeerConnectionInterface::IceGatheringState:: kIceGatheringGathering; break; - case cricket::IceGatheringState::kIceGatheringComplete: + case webrtc::IceGatheringState::kIceGatheringComplete: state = webrtc::PeerConnectionInterface::IceGatheringState:: kIceGatheringComplete; break; - } - CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), state, result, Napi::Value) - return result; -} - -Napi::Value -RTCIceTransport::GetLocalCandidates(const Napi::CallbackInfo &info) { - Napi::Error::New(info.Env(), "Not yet implemented!") - .ThrowAsJavaScriptException(); - return info.Env().Undefined(); -} - -Napi::Value -RTCIceTransport::GetRemoteCandidates(const Napi::CallbackInfo &info) { - Napi::Error::New(info.Env(), "Not yet implemented!") - .ThrowAsJavaScriptException(); - return info.Env().Undefined(); -} - -Napi::Value -RTCIceTransport::GetSelectedCandidatePair(const Napi::CallbackInfo &info) { - Napi::Error::New(info.Env(), "Not yet implemented!") - .ThrowAsJavaScriptException(); - return info.Env().Undefined(); -} - -Napi::Value -RTCIceTransport::GetLocalParameters(const Napi::CallbackInfo &info) { - Napi::Error::New(info.Env(), "Not yet implemented!") - .ThrowAsJavaScriptException(); - return info.Env().Undefined(); -} - -Napi::Value -RTCIceTransport::GetRemoteParameters(const Napi::CallbackInfo &info) { - Napi::Error::New(info.Env(), "Not yet implemented!") - .ThrowAsJavaScriptException(); - return info.Env().Undefined(); -} - -void RTCIceTransport::Init(Napi::Env env, Napi::Object exports) { - auto func = DefineClass( - env, "RTCIceTransport", - {InstanceAccessor("role", &RTCIceTransport::GetRole, nullptr), - InstanceAccessor("component", &RTCIceTransport::GetComponent, nullptr), - InstanceAccessor("state", &RTCIceTransport::GetState, nullptr), - InstanceAccessor("gatheringState", &RTCIceTransport::GetGatheringState, - nullptr), - InstanceMethod("getLocalCandidates", - &RTCIceTransport::GetLocalCandidates), - InstanceMethod("getRemoteCandidates", - &RTCIceTransport::GetRemoteCandidates), - InstanceMethod("getSelectedCandidatePair", - &RTCIceTransport::GetSelectedCandidatePair), - InstanceMethod("getLocalParameters", - &RTCIceTransport::GetLocalParameters), - InstanceMethod("getRemoteParameters", - &RTCIceTransport::GetRemoteParameters)}); - - constructor() = Napi::Persistent(func); - constructor().SuppressDestruct(); - - exports.Set("RTCIceTransport", func); -} - -} // namespace node_webrtc + } + CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), state, result, Napi::Value) + return result; +} + +Napi::Value +RTCIceTransport::GetLocalCandidates(const Napi::CallbackInfo &info) { + Napi::Error::New(info.Env(), "Not yet implemented!") + .ThrowAsJavaScriptException(); + return info.Env().Undefined(); +} + +Napi::Value +RTCIceTransport::GetRemoteCandidates(const Napi::CallbackInfo &info) { + Napi::Error::New(info.Env(), "Not yet implemented!") + .ThrowAsJavaScriptException(); + return info.Env().Undefined(); +} + +Napi::Value +RTCIceTransport::GetSelectedCandidatePair(const Napi::CallbackInfo &info) { + Napi::Error::New(info.Env(), "Not yet implemented!") + .ThrowAsJavaScriptException(); + return info.Env().Undefined(); +} + +Napi::Value +RTCIceTransport::GetLocalParameters(const Napi::CallbackInfo &info) { + Napi::Error::New(info.Env(), "Not yet implemented!") + .ThrowAsJavaScriptException(); + return info.Env().Undefined(); +} + +Napi::Value +RTCIceTransport::GetRemoteParameters(const Napi::CallbackInfo &info) { + Napi::Error::New(info.Env(), "Not yet implemented!") + .ThrowAsJavaScriptException(); + return info.Env().Undefined(); +} + +void RTCIceTransport::Init(Napi::Env env, Napi::Object exports) { + auto func = DefineClass( + env, "RTCIceTransport", + {InstanceAccessor("role", &RTCIceTransport::GetRole, nullptr), + InstanceAccessor("component", &RTCIceTransport::GetComponent, nullptr), + InstanceAccessor("state", &RTCIceTransport::GetState, nullptr), + InstanceAccessor("gatheringState", &RTCIceTransport::GetGatheringState, + nullptr), + InstanceMethod("getLocalCandidates", + &RTCIceTransport::GetLocalCandidates), + InstanceMethod("getRemoteCandidates", + &RTCIceTransport::GetRemoteCandidates), + InstanceMethod("getSelectedCandidatePair", + &RTCIceTransport::GetSelectedCandidatePair), + InstanceMethod("getLocalParameters", + &RTCIceTransport::GetLocalParameters), + InstanceMethod("getRemoteParameters", + &RTCIceTransport::GetRemoteParameters)}); + + constructor() = Napi::Persistent(func); + constructor().SuppressDestruct(); + + exports.Set("RTCIceTransport", func); +} + +} // namespace node_webrtc diff --git a/src/interfaces/rtc_ice_transport.hh b/src/interfaces/rtc_ice_transport.hh index 9bfd1ec72..4d6a557a5 100644 --- a/src/interfaces/rtc_ice_transport.hh +++ b/src/interfaces/rtc_ice_transport.hh @@ -1,88 +1,81 @@ -/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license that can be found - * in the LICENSE.md file in the root of the source tree. All contributing - * project authors may be found in the AUTHORS file in the root of the source - * tree. - */ -#pragma once - -#include - +/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license that can be found + * in the LICENSE.md file in the root of the source tree. All contributing + * project authors may be found in the AUTHORS file in the root of the source + * tree. + */ +#pragma once + +#include + #include #include #include #include -#include #include "src/enums/node_webrtc/rtc_ice_component.hh" #include "src/node/async_object_wrap_with_loop.hh" #include "src/node/ref_ptr.hh" #include "src/node/wrap.hh" -namespace cricket { -class IceTransportInternal; -} - namespace node_webrtc { - -class PeerConnectionFactory; - -class RTCIceTransport - : public AsyncObjectWrapWithLoop, - public sigslot::has_slots { + +class PeerConnectionFactory; + +class RTCIceTransport : public AsyncObjectWrapWithLoop { public: - explicit RTCIceTransport(const Napi::CallbackInfo &); - ~RTCIceTransport() override; - - RTCIceTransport(const RTCIceTransport &) = delete; - RTCIceTransport(RTCIceTransport &&) = delete; - RTCIceTransport &operator=(const RTCIceTransport &) = delete; - RTCIceTransport &operator=(RTCIceTransport &&) = delete; - - static void Init(Napi::Env, Napi::Object); - - static ::node_webrtc::Wrap, - PeerConnectionFactory *> * - wrap(); - - void OnRTCDtlsTransportStopped(); - -protected: - void Stop() override; - -private: - static Napi::FunctionReference &constructor(); - - static RTCIceTransport * - Create(PeerConnectionFactory *, - rtc::scoped_refptr); - - void OnStateChanged(cricket::IceTransportInternal *); - void OnGatheringStateChanged(cricket::IceTransportInternal *); - - void TakeSnapshot(); - - Napi::Value GetRole(const Napi::CallbackInfo &); - Napi::Value GetComponent(const Napi::CallbackInfo &); - Napi::Value GetState(const Napi::CallbackInfo &); - Napi::Value GetGatheringState(const Napi::CallbackInfo &); - - Napi::Value GetLocalCandidates(const Napi::CallbackInfo &); - Napi::Value GetRemoteCandidates(const Napi::CallbackInfo &); - Napi::Value GetSelectedCandidatePair(const Napi::CallbackInfo &); - Napi::Value GetLocalParameters(const Napi::CallbackInfo &); - Napi::Value GetRemoteParameters(const Napi::CallbackInfo &); - - RTCIceComponent _component = RTCIceComponent::kRtp; - RefPtr _factory; - cricket::IceGatheringState _gathering_state = - cricket::IceGatheringState::kIceGatheringNew; + explicit RTCIceTransport(const Napi::CallbackInfo &); + ~RTCIceTransport() override; + + RTCIceTransport(const RTCIceTransport &) = delete; + RTCIceTransport(RTCIceTransport &&) = delete; + RTCIceTransport &operator=(const RTCIceTransport &) = delete; + RTCIceTransport &operator=(RTCIceTransport &&) = delete; + + static void Init(Napi::Env, Napi::Object); + + static ::node_webrtc::Wrap, + PeerConnectionFactory *> * + wrap(); + + void OnRTCDtlsTransportStopped(); + +protected: + void Stop() override; + +private: + static Napi::FunctionReference &constructor(); + + static RTCIceTransport * + Create(PeerConnectionFactory *, + rtc::scoped_refptr); + + void OnStateChanged(webrtc::IceTransportInternal *); + void OnGatheringStateChanged(webrtc::IceTransportInternal *); + + void TakeSnapshot(); + + Napi::Value GetRole(const Napi::CallbackInfo &); + Napi::Value GetComponent(const Napi::CallbackInfo &); + Napi::Value GetState(const Napi::CallbackInfo &); + Napi::Value GetGatheringState(const Napi::CallbackInfo &); + + Napi::Value GetLocalCandidates(const Napi::CallbackInfo &); + Napi::Value GetRemoteCandidates(const Napi::CallbackInfo &); + Napi::Value GetSelectedCandidatePair(const Napi::CallbackInfo &); + Napi::Value GetLocalParameters(const Napi::CallbackInfo &); + Napi::Value GetRemoteParameters(const Napi::CallbackInfo &); + + RTCIceComponent _component = RTCIceComponent::kRtp; + RefPtr _factory; + webrtc::IceGatheringState _gathering_state = + webrtc::IceGatheringState::kIceGatheringNew; std::mutex _mutex{}; - cricket::IceRole _role = cricket::IceRole::ICEROLE_UNKNOWN; + webrtc::IceRole _role = webrtc::IceRole::ICEROLE_UNKNOWN; webrtc::IceTransportState _state = webrtc::IceTransportState::kNew; rtc::scoped_refptr _transport; }; - -} // namespace node_webrtc + +} // namespace node_webrtc diff --git a/src/interfaces/rtc_peer_connection.cc b/src/interfaces/rtc_peer_connection.cc index 628af31c8..440b310f5 100644 --- a/src/interfaces/rtc_peer_connection.cc +++ b/src/interfaces/rtc_peer_connection.cc @@ -1,1042 +1,1043 @@ -/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license that can be found - * in the LICENSE.md file in the root of the source tree. All contributing - * project authors may be found in the AUTHORS file in the root of the source - * tree. - */ +/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license that can be found + * in the LICENSE.md file in the root of the source tree. All contributing + * project authors may be found in the AUTHORS file in the root of the source + * tree. + */ #include "src/interfaces/rtc_peer_connection.hh" #include -#include +#include #include #include #include -#include -#include -#include - -#include "src/converters.hh" -#include "src/converters/absl.hh" // IWYU pragma: keep. Needed for conversions -#include "src/converters/arguments.hh" -#include "src/converters/interfaces.hh" // IWYU pragma: keep. Needed for conversions. -#include "src/converters/napi.hh" -#include "src/dictionaries/macros/napi.hh" -#include "src/dictionaries/node_webrtc/rtc_answer_options.hh" -#include "src/dictionaries/node_webrtc/rtc_offer_options.hh" -#include "src/dictionaries/node_webrtc/rtc_session_description_init.hh" -#include "src/dictionaries/node_webrtc/some_error.hh" -#include "src/dictionaries/webrtc/data_channel_init.hh" -#include "src/dictionaries/webrtc/ice_candidate_interface.hh" -#include "src/dictionaries/webrtc/rtc_configuration.hh" // IWYU pragma: keep. Needed for conversions. -#include "src/dictionaries/webrtc/rtc_error.hh" -#include "src/dictionaries/webrtc/rtp_transceiver_init.hh" -#include "src/enums/webrtc/ice_connection_state.hh" // IWYU pragma: keep. Needed for conversions -#include "src/enums/webrtc/ice_gathering_state.hh" // IWYU pragma: keep. Needed for conversions -#include "src/enums/webrtc/media_type.hh" // IWYU pragma: keep. Needed for conversions. -#include "src/enums/webrtc/peer_connection_state.hh" // IWYU pragma: keep. Needed for conversions. -#include "src/enums/webrtc/signaling_state.hh" // IWYU pragma: keep. Needed for conversions. -#include "src/functional/either.hh" -#include "src/functional/maybe.hh" -#include "src/interfaces/media_stream.hh" -#include "src/interfaces/media_stream_track.hh" -#include "src/interfaces/rtc_data_channel.hh" -#include "src/interfaces/rtc_peer_connection/create_session_description_observer.hh" -#include "src/interfaces/rtc_peer_connection/peer_connection_factory.hh" -#include "src/interfaces/rtc_peer_connection/rtc_stats_collector.hh" -#include "src/interfaces/rtc_peer_connection/set_session_description_observer.hh" -#include "src/interfaces/rtc_rtp_receiver.hh" -#include "src/interfaces/rtc_rtp_sender.hh" -#include "src/interfaces/rtc_rtp_transceiver.hh" -#include "src/interfaces/rtc_sctp_transport.hh" -#include "src/node/error_factory.hh" -#include "src/node/events.hh" -#include "src/node/promise.hh" -#include "src/node/ref_ptr.hh" -#include "src/node/utility.hh" - -namespace node_webrtc { - -Napi::FunctionReference &RTCPeerConnection::constructor() { - static Napi::FunctionReference constructor; - return constructor; -} - -// -// PeerConnection -// - -RTCPeerConnection::RTCPeerConnection(const Napi::CallbackInfo &info) - : AsyncObjectWrapWithLoop("RTCPeerConnection", *this, - info) { - auto env = info.Env(); - - if (!info.IsConstructCall()) { - Napi::TypeError::New( - env, "Use the new operator to construct the RTCPeerConnection.") - .ThrowAsJavaScriptException(); - return; - } - - CONVERT_ARGS_OR_THROW_AND_RETURN_VOID_NAPI(info, maybeConfiguration, - Maybe) - - auto configuration = maybeConfiguration.FromMaybe(ExtendedRTCConfiguration()); - - // TODO(mroberts): Read `factory` (non-standard) from RTCConfiguration? - _factory = PeerConnectionFactory::GetOrCreateDefault(); - _shouldReleaseFactory = true; +#include +#include +#include + +#include "src/converters.hh" +#include "src/converters/absl.hh" // IWYU pragma: keep. Needed for conversions +#include "src/converters/arguments.hh" +#include "src/converters/interfaces.hh" // IWYU pragma: keep. Needed for conversions. +#include "src/converters/napi.hh" +#include "src/dictionaries/macros/napi.hh" +#include "src/dictionaries/node_webrtc/rtc_answer_options.hh" +#include "src/dictionaries/node_webrtc/rtc_offer_options.hh" +#include "src/dictionaries/node_webrtc/rtc_session_description_init.hh" +#include "src/dictionaries/node_webrtc/some_error.hh" +#include "src/dictionaries/webrtc/data_channel_init.hh" +#include "src/dictionaries/webrtc/ice_candidate_interface.hh" +#include "src/dictionaries/webrtc/rtc_configuration.hh" // IWYU pragma: keep. Needed for conversions. +#include "src/dictionaries/webrtc/rtc_error.hh" +#include "src/dictionaries/webrtc/rtp_transceiver_init.hh" +#include "src/enums/webrtc/ice_connection_state.hh" // IWYU pragma: keep. Needed for conversions +#include "src/enums/webrtc/ice_gathering_state.hh" // IWYU pragma: keep. Needed for conversions +#include "src/enums/webrtc/media_type.hh" // IWYU pragma: keep. Needed for conversions. +#include "src/enums/webrtc/peer_connection_state.hh" // IWYU pragma: keep. Needed for conversions. +#include "src/enums/webrtc/signaling_state.hh" // IWYU pragma: keep. Needed for conversions. +#include "src/functional/either.hh" +#include "src/functional/maybe.hh" +#include "src/interfaces/media_stream.hh" +#include "src/interfaces/media_stream_track.hh" +#include "src/interfaces/rtc_data_channel.hh" +#include "src/interfaces/rtc_peer_connection/create_session_description_observer.hh" +#include "src/interfaces/rtc_peer_connection/peer_connection_factory.hh" +#include "src/interfaces/rtc_peer_connection/rtc_stats_collector.hh" +#include "src/interfaces/rtc_peer_connection/set_session_description_observer.hh" +#include "src/interfaces/rtc_rtp_receiver.hh" +#include "src/interfaces/rtc_rtp_sender.hh" +#include "src/interfaces/rtc_rtp_transceiver.hh" +#include "src/interfaces/rtc_sctp_transport.hh" +#include "src/node/error_factory.hh" +#include "src/node/events.hh" +#include "src/node/promise.hh" +#include "src/node/ref_ptr.hh" +#include "src/node/utility.hh" + +namespace node_webrtc { + +Napi::FunctionReference &RTCPeerConnection::constructor() { + static Napi::FunctionReference constructor; + return constructor; +} + +// +// PeerConnection +// + +RTCPeerConnection::RTCPeerConnection(const Napi::CallbackInfo &info) + : AsyncObjectWrapWithLoop("RTCPeerConnection", *this, + info) { + auto env = info.Env(); + + if (!info.IsConstructCall()) { + Napi::TypeError::New( + env, "Use the new operator to construct the RTCPeerConnection.") + .ThrowAsJavaScriptException(); + return; + } + + CONVERT_ARGS_OR_THROW_AND_RETURN_VOID_NAPI(info, maybeConfiguration, + Maybe) + + auto configuration = maybeConfiguration.FromMaybe(ExtendedRTCConfiguration()); + + // TODO(mroberts): Read `factory` (non-standard) from RTCConfiguration? + _factory = PeerConnectionFactory::GetOrCreateDefault(); + _shouldReleaseFactory = true; auto portAllocator = - std::unique_ptr(new cricket::BasicPortAllocator( - _factory->getNetworkManager(), _factory->getSocketFactory())); + std::unique_ptr(new webrtc::BasicPortAllocator( + _factory->env(), _factory->getNetworkManager(), + _factory->getSocketFactory())); _port_range = configuration.portRange; portAllocator->SetPortRange(_port_range.min.FromMaybe(0), _port_range.max.FromMaybe(65535)); - - auto deps = webrtc::PeerConnectionDependencies(this); - deps.allocator = std::move(portAllocator); - auto maybePeerConnection = _factory->factory()->CreatePeerConnectionOrError( - configuration.configuration, std::move(deps)); - - if (!maybePeerConnection.ok()) { - Napi::Error::New(env, maybePeerConnection.MoveError().message()) - .ThrowAsJavaScriptException(); - return; - } - - _jinglePeerConnection = maybePeerConnection.MoveValue(); -} - -RTCPeerConnection::~RTCPeerConnection() { - // Clear the wrap caches before releasing the WebRTC peer connection. - // This releases all scoped_refptr references to WebRTC objects while - // the signaling thread may still be active, preventing crashes during - // cleanup. - _data_channel_wrap.clear(); - _stream_wrap.clear(); - _receiver_wrap.clear(); - _transceiver_wrap.clear(); - _sender_wrap.clear(); - _transport_wrap.clear(); - _channels.clear(); - - _jinglePeerConnection = nullptr; - if (_factory) { - if (_shouldReleaseFactory) { - PeerConnectionFactory::Release(); - } - _factory = nullptr; - } -} - -void RTCPeerConnection::OnSignalingChange( - webrtc::PeerConnectionInterface::SignalingState state) { - Dispatch(CreateCallback([this, state]() { - MakeCallback("onsignalingstatechange", {}); - if (state == webrtc::PeerConnectionInterface::kClosed) { - Stop(); - } - })); -} - -void RTCPeerConnection::OnIceConnectionChange( - webrtc::PeerConnectionInterface::IceConnectionState) { - Dispatch(CreateCallback([this]() { - MakeCallback("oniceconnectionstatechange", {}); - MakeCallback("onconnectionstatechange", {}); - })); -} - -void RTCPeerConnection::OnIceGatheringChange( - webrtc::PeerConnectionInterface::IceGatheringState) { - Dispatch(CreateCallback( - [this]() { MakeCallback("onicegatheringstatechange", {}); })); -} - -void RTCPeerConnection::OnIceCandidate( - const webrtc::IceCandidateInterface *ice_candidate) { - std::string error; - - std::string sdp; - if (!ice_candidate->ToString(&sdp)) { - error = "Failed to print the candidate string. This is pretty weird. File " - "a bug on https://github.com/node-webrtc/node-webrtc"; - return; - } - - webrtc::SdpParseError parseError; - auto candidate = - std::shared_ptr(webrtc::CreateIceCandidate( - ice_candidate->sdp_mid(), ice_candidate->sdp_mline_index(), sdp, - &parseError)); - if (!parseError.description.empty()) { - error = parseError.description; - } else if (!candidate) { - error = "Failed to copy RTCIceCandidate"; - } - - Dispatch(CreateCallback([this, candidate, error]() { - if (error.empty()) { - auto env = Env(); - auto maybeCandidate = - From(std::make_pair(env, candidate.get())); - if (maybeCandidate.IsValid()) { - MakeCallback("onicecandidate", {maybeCandidate.UnsafeFromValid()}); - } - } - })); -} - -static Validation CreateRTCPeerConnectionIceErrorEvent( - const Napi::Value address, const Napi::Value port, const Napi::Value url, - const Napi::Value errorCode, const Napi::Value errorText) { - auto env = address.Env(); - Napi::EscapableHandleScope scope(env); - NODE_WEBRTC_CREATE_OBJECT_OR_RETURN(env, object) - NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "address", address) - NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "port", port) - NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "url", url) - NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "errorCode", errorCode) - NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "errorText", errorText) - return Pure(scope.Escape(object)); -} - -void RTCPeerConnection::OnIceCandidateError(const std::string &address, - int port, const std::string &url, - int error_code, - const std::string &error_text) { - Dispatch(CreateCallback( - [this, address, port, url, error_code, error_text]() { - auto env = Env(); - auto maybeEvent = Validation::Join( - curry(CreateRTCPeerConnectionIceErrorEvent) % - From(std::make_pair(env, address)) * - From(std::make_pair(env, port)) * - From(std::make_pair(env, url)) * - From(std::make_pair(env, error_code)) * - From(std::make_pair(env, error_text))); - if (maybeEvent.IsValid()) { - MakeCallback("onicecandidateerror", {maybeEvent.UnsafeFromValid()}); - } - })); -} - -void RTCPeerConnection::OnDataChannel( - rtc::scoped_refptr channel) { - auto observer = new DataChannelObserver(_factory, channel); - Dispatch(CreateCallback([this, observer]() { - auto channel = - _data_channel_wrap.GetOrCreate(observer, observer->channel()); - MakeCallback("ondatachannel", {channel->Value()}); - })); -} - -void RTCPeerConnection::OnAddStream( - rtc::scoped_refptr) {} - -void RTCPeerConnection::OnAddTrack( - rtc::scoped_refptr receiver, - const std::vector> - &streams) { -// TODO(jack): once libwebrtc fully deprecates kPlanB, we can remove this -#ifndef _MSC_VER -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" -#endif - if (_jinglePeerConnection->GetConfiguration().sdp_semantics != - webrtc::SdpSemantics::kPlanB) { - return; - } -#ifndef _MSC_VER -#pragma clang diagnostic pop -#endif - Dispatch(CreateCallback([this, receiver, streams]() { - if (_factory == nullptr) { - // We have closed, but have not processed close event to stop the event - // loop yet. In that case, we should not be broadcasting this event - return; - } - auto mediaStreams = std::vector(); - for (auto const &stream : streams) { - auto mediaStream = _stream_wrap.GetOrCreate(_factory, stream); - mediaStreams.push_back(mediaStream); - } - CONVERT_OR_THROW_AND_RETURN_VOID_NAPI(Env(), mediaStreams, streamArray, - Napi::Value) - MakeCallback("ontrack", - {_receiver_wrap.GetOrCreate(_factory, receiver)->Value(), - streamArray, Env().Null()}); - })); -} - -void RTCPeerConnection::OnTrack( - rtc::scoped_refptr transceiver) { - auto receiver = transceiver->receiver(); - auto streams = receiver->streams(); - Dispatch(CreateCallback([this, transceiver, receiver, - streams]() { - if (_factory == nullptr) { - // We have closed, but have not processed close event to stop the - // event loop yet. In that case, we should not be broadcasting this - // event - return; - } - auto mediaStreams = std::vector(); - for (auto const &stream : streams) { - auto mediaStream = _stream_wrap.GetOrCreate(_factory, stream); - mediaStreams.push_back(mediaStream); - } - CONVERT_OR_THROW_AND_RETURN_VOID_NAPI(Env(), mediaStreams, streamArray, - Napi::Value) - MakeCallback( - "ontrack", - {_receiver_wrap.GetOrCreate(_factory, receiver)->Value(), streamArray, - _transceiver_wrap.GetOrCreate(_factory, transceiver)->Value()}); - })); -} - -void RTCPeerConnection::OnRemoveStream( - rtc::scoped_refptr) {} - -void RTCPeerConnection::OnRenegotiationNeeded() { - Dispatch(CreateCallback( - [this]() { MakeCallback("onnegotiationneeded", {}); })); -} - -Napi::Value RTCPeerConnection::AddTrack(const Napi::CallbackInfo &info) { - auto env = info.Env(); - if (!_jinglePeerConnection) { - Napi::Error(env, ErrorFactory::CreateInvalidStateError( - env, "Cannot addTrack; RTCPeerConnection is closed")) - .ThrowAsJavaScriptException(); - return env.Undefined(); - } - CONVERT_ARGS_OR_THROW_AND_RETURN_NAPI( - info, pair, - std::tuple>>) - auto mediaStreamTrack = std::get<0>(pair); - Maybe> mediaStreams = std::get<1>(pair); - std::vector streamIds; - if (mediaStreams.IsJust()) { - streamIds.reserve(mediaStreams.UnsafeFromJust().size()); - for (auto const &stream : mediaStreams.UnsafeFromJust()) { - streamIds.emplace_back(stream->stream()->id()); - } - } - auto result = - _jinglePeerConnection->AddTrack(mediaStreamTrack->track(), streamIds); - if (!result.ok()) { - CONVERT_OR_THROW_AND_RETURN_NAPI(env, &result.error(), error, Napi::Value) - Napi::Error(env, error).ThrowAsJavaScriptException(); - return env.Undefined(); - } - auto rtpSender = result.value(); - return _sender_wrap.GetOrCreate(_factory, rtpSender)->Value(); -} - -Napi::Value RTCPeerConnection::AddTransceiver(const Napi::CallbackInfo &info) { - auto env = info.Env(); - if (!_jinglePeerConnection) { - Napi::Error::New(env, "Cannot addTransceiver; RTCPeerConnection is closed") - .ThrowAsJavaScriptException(); - return env.Undefined(); - } - - if (_jinglePeerConnection->GetConfiguration().sdp_semantics != - webrtc::SdpSemantics::kUnifiedPlan) { - Napi::Error::New( - env, - "AddTransceiver is only available with Unified Plan SdpSemanticsAbort") - .ThrowAsJavaScriptException(); - return env.Undefined(); - } - + + auto deps = webrtc::PeerConnectionDependencies(this); + deps.allocator = std::move(portAllocator); + auto maybePeerConnection = _factory->factory()->CreatePeerConnectionOrError( + configuration.configuration, std::move(deps)); + + if (!maybePeerConnection.ok()) { + Napi::Error::New(env, maybePeerConnection.MoveError().message()) + .ThrowAsJavaScriptException(); + return; + } + + _jinglePeerConnection = maybePeerConnection.MoveValue(); +} + +RTCPeerConnection::~RTCPeerConnection() { + // Clear the wrap caches before releasing the WebRTC peer connection. + // This releases all scoped_refptr references to WebRTC objects while + // the signaling thread may still be active, preventing crashes during + // cleanup. + _data_channel_wrap.clear(); + _stream_wrap.clear(); + _receiver_wrap.clear(); + _transceiver_wrap.clear(); + _sender_wrap.clear(); + _transport_wrap.clear(); + _channels.clear(); + + _jinglePeerConnection = nullptr; + if (_factory) { + if (_shouldReleaseFactory) { + PeerConnectionFactory::Release(); + } + _factory = nullptr; + } +} + +void RTCPeerConnection::OnSignalingChange( + webrtc::PeerConnectionInterface::SignalingState state) { + Dispatch(CreateCallback([this, state]() { + MakeCallback("onsignalingstatechange", {}); + if (state == webrtc::PeerConnectionInterface::kClosed) { + Stop(); + } + })); +} + +void RTCPeerConnection::OnIceConnectionChange( + webrtc::PeerConnectionInterface::IceConnectionState) { + Dispatch(CreateCallback([this]() { + MakeCallback("oniceconnectionstatechange", {}); + MakeCallback("onconnectionstatechange", {}); + })); +} + +void RTCPeerConnection::OnIceGatheringChange( + webrtc::PeerConnectionInterface::IceGatheringState) { + Dispatch(CreateCallback( + [this]() { MakeCallback("onicegatheringstatechange", {}); })); +} + +void RTCPeerConnection::OnIceCandidate( + const webrtc::IceCandidateInterface *ice_candidate) { + std::string error; + + std::string sdp; + if (!ice_candidate->ToString(&sdp)) { + error = "Failed to print the candidate string. This is pretty weird. File " + "a bug on https://github.com/node-webrtc/node-webrtc"; + return; + } + + webrtc::SdpParseError parseError; + auto candidate = + std::shared_ptr(webrtc::CreateIceCandidate( + ice_candidate->sdp_mid(), ice_candidate->sdp_mline_index(), sdp, + &parseError)); + if (!parseError.description.empty()) { + error = parseError.description; + } else if (!candidate) { + error = "Failed to copy RTCIceCandidate"; + } + + Dispatch(CreateCallback([this, candidate, error]() { + if (error.empty()) { + auto env = Env(); + auto maybeCandidate = + From(std::make_pair(env, candidate.get())); + if (maybeCandidate.IsValid()) { + MakeCallback("onicecandidate", {maybeCandidate.UnsafeFromValid()}); + } + } + })); +} + +static Validation CreateRTCPeerConnectionIceErrorEvent( + const Napi::Value address, const Napi::Value port, const Napi::Value url, + const Napi::Value errorCode, const Napi::Value errorText) { + auto env = address.Env(); + Napi::EscapableHandleScope scope(env); + NODE_WEBRTC_CREATE_OBJECT_OR_RETURN(env, object) + NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "address", address) + NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "port", port) + NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "url", url) + NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "errorCode", errorCode) + NODE_WEBRTC_CONVERT_AND_SET_OR_RETURN(env, object, "errorText", errorText) + return Pure(scope.Escape(object)); +} + +void RTCPeerConnection::OnIceCandidateError(const std::string &address, + int port, const std::string &url, + int error_code, + const std::string &error_text) { + Dispatch(CreateCallback( + [this, address, port, url, error_code, error_text]() { + auto env = Env(); + auto maybeEvent = Validation::Join( + curry(CreateRTCPeerConnectionIceErrorEvent) % + From(std::make_pair(env, address)) * + From(std::make_pair(env, port)) * + From(std::make_pair(env, url)) * + From(std::make_pair(env, error_code)) * + From(std::make_pair(env, error_text))); + if (maybeEvent.IsValid()) { + MakeCallback("onicecandidateerror", {maybeEvent.UnsafeFromValid()}); + } + })); +} + +void RTCPeerConnection::OnDataChannel( + rtc::scoped_refptr channel) { + auto observer = new DataChannelObserver(_factory, channel); + Dispatch(CreateCallback([this, observer]() { + auto channel = + _data_channel_wrap.GetOrCreate(observer, observer->channel()); + MakeCallback("ondatachannel", {channel->Value()}); + })); +} + +void RTCPeerConnection::OnAddStream( + rtc::scoped_refptr) {} + +void RTCPeerConnection::OnAddTrack( + rtc::scoped_refptr receiver, + const std::vector> + &streams) { +// TODO(jack): once libwebrtc fully deprecates kPlanB, we can remove this +#ifndef _MSC_VER +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#endif + if (_jinglePeerConnection->GetConfiguration().sdp_semantics != + webrtc::SdpSemantics::kPlanB) { + return; + } +#ifndef _MSC_VER +#pragma clang diagnostic pop +#endif + Dispatch(CreateCallback([this, receiver, streams]() { + if (_factory == nullptr) { + // We have closed, but have not processed close event to stop the event + // loop yet. In that case, we should not be broadcasting this event + return; + } + auto mediaStreams = std::vector(); + for (auto const &stream : streams) { + auto mediaStream = _stream_wrap.GetOrCreate(_factory, stream); + mediaStreams.push_back(mediaStream); + } + CONVERT_OR_THROW_AND_RETURN_VOID_NAPI(Env(), mediaStreams, streamArray, + Napi::Value) + MakeCallback("ontrack", + {_receiver_wrap.GetOrCreate(_factory, receiver)->Value(), + streamArray, Env().Null()}); + })); +} + +void RTCPeerConnection::OnTrack( + rtc::scoped_refptr transceiver) { + auto receiver = transceiver->receiver(); + auto streams = receiver->streams(); + Dispatch(CreateCallback([this, transceiver, receiver, + streams]() { + if (_factory == nullptr) { + // We have closed, but have not processed close event to stop the + // event loop yet. In that case, we should not be broadcasting this + // event + return; + } + auto mediaStreams = std::vector(); + for (auto const &stream : streams) { + auto mediaStream = _stream_wrap.GetOrCreate(_factory, stream); + mediaStreams.push_back(mediaStream); + } + CONVERT_OR_THROW_AND_RETURN_VOID_NAPI(Env(), mediaStreams, streamArray, + Napi::Value) + MakeCallback( + "ontrack", + {_receiver_wrap.GetOrCreate(_factory, receiver)->Value(), streamArray, + _transceiver_wrap.GetOrCreate(_factory, transceiver)->Value()}); + })); +} + +void RTCPeerConnection::OnRemoveStream( + rtc::scoped_refptr) {} + +void RTCPeerConnection::OnRenegotiationNeeded() { + Dispatch(CreateCallback( + [this]() { MakeCallback("onnegotiationneeded", {}); })); +} + +Napi::Value RTCPeerConnection::AddTrack(const Napi::CallbackInfo &info) { + auto env = info.Env(); + if (!_jinglePeerConnection) { + Napi::Error(env, ErrorFactory::CreateInvalidStateError( + env, "Cannot addTrack; RTCPeerConnection is closed")) + .ThrowAsJavaScriptException(); + return env.Undefined(); + } + CONVERT_ARGS_OR_THROW_AND_RETURN_NAPI( + info, pair, + std::tuple>>) + auto mediaStreamTrack = std::get<0>(pair); + Maybe> mediaStreams = std::get<1>(pair); + std::vector streamIds; + if (mediaStreams.IsJust()) { + streamIds.reserve(mediaStreams.UnsafeFromJust().size()); + for (auto const &stream : mediaStreams.UnsafeFromJust()) { + streamIds.emplace_back(stream->stream()->id()); + } + } + auto result = + _jinglePeerConnection->AddTrack(mediaStreamTrack->track(), streamIds); + if (!result.ok()) { + CONVERT_OR_THROW_AND_RETURN_NAPI(env, &result.error(), error, Napi::Value) + Napi::Error(env, error).ThrowAsJavaScriptException(); + return env.Undefined(); + } + auto rtpSender = result.value(); + return _sender_wrap.GetOrCreate(_factory, rtpSender)->Value(); +} + +Napi::Value RTCPeerConnection::AddTransceiver(const Napi::CallbackInfo &info) { + auto env = info.Env(); + if (!_jinglePeerConnection) { + Napi::Error::New(env, "Cannot addTransceiver; RTCPeerConnection is closed") + .ThrowAsJavaScriptException(); + return env.Undefined(); + } + + if (_jinglePeerConnection->GetConfiguration().sdp_semantics != + webrtc::SdpSemantics::kUnifiedPlan) { + Napi::Error::New( + env, + "AddTransceiver is only available with Unified Plan SdpSemanticsAbort") + .ThrowAsJavaScriptException(); + return env.Undefined(); + } + CONVERT_ARGS_OR_THROW_AND_RETURN_NAPI( info, args, - std::tuple COMMA + std::tuple COMMA Maybe>) - Either kindOrTrack = + Either kindOrTrack = std::get<0>(args); - Maybe maybeInit = std::get<1>(args); - auto result = - kindOrTrack.IsLeft() - ? maybeInit.IsNothing() - ? _jinglePeerConnection->AddTransceiver( - kindOrTrack.UnsafeFromLeft()) - : _jinglePeerConnection->AddTransceiver( - kindOrTrack.UnsafeFromLeft(), maybeInit.UnsafeFromJust()) - : maybeInit.IsNothing() ? _jinglePeerConnection->AddTransceiver( - kindOrTrack.UnsafeFromRight()->track()) - : _jinglePeerConnection->AddTransceiver( - kindOrTrack.UnsafeFromRight()->track(), - maybeInit.UnsafeFromJust()); - if (!result.ok()) { - CONVERT_OR_THROW_AND_RETURN_NAPI(env, &result.error(), error, Napi::Value) - Napi::Error(env, error).ThrowAsJavaScriptException(); - return env.Undefined(); - } - auto rtpTransceiver = result.value(); - return _transceiver_wrap.GetOrCreate(_factory, rtpTransceiver)->Value(); -} - -Napi::Value RTCPeerConnection::RemoveTrack(const Napi::CallbackInfo &info) { - auto env = info.Env(); - if (!_jinglePeerConnection) { - Napi::Error(env, - ErrorFactory::CreateInvalidStateError( - env, "Cannot removeTrack; RTCPeerConnection is closed")) - .ThrowAsJavaScriptException(); - return env.Undefined(); - } - CONVERT_ARGS_OR_THROW_AND_RETURN_NAPI(info, sender, RTCRtpSender *) - auto senders = _jinglePeerConnection->GetSenders(); - if (std::find(senders.begin(), senders.end(), sender->sender()) == - senders.end()) { - Napi::Error( - env, ErrorFactory::CreateInvalidAccessError(env, "Cannot removeTrack")) - .ThrowAsJavaScriptException(); - return env.Undefined(); - } - auto error = _jinglePeerConnection->RemoveTrackOrError(sender->sender()); - if (!error.ok()) { - CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), &error, result, Napi::Value) - Napi::Error(info.Env(), result).ThrowAsJavaScriptException(); - } - return env.Undefined(); -} - -Napi::Value RTCPeerConnection::CreateOffer(const Napi::CallbackInfo &info) { - auto env = info.Env(); - CREATE_DEFERRED(env, deferred) - - auto maybeOptions = - From>(Arguments(info)).Map([](auto maybeOptions) { - return maybeOptions.FromMaybe(RTCOfferOptions()); - }); - if (maybeOptions.IsInvalid()) { - Reject(deferred, SomeError(maybeOptions.ToErrors()[0])); - return deferred.Promise(); - } - - if (!_jinglePeerConnection || - _jinglePeerConnection->signaling_state() == - webrtc::PeerConnectionInterface::SignalingState::kClosed) { - Reject(deferred, - ErrorFactory::CreateInvalidStateError( - env, "Failed to execute 'createOffer' on 'RTCPeerConnection': " - "The RTCPeerConnection's signalingState is 'closed'.")); - return deferred.Promise(); - } - - auto observer = - rtc::make_ref_counted(this, deferred); - _jinglePeerConnection->CreateOffer(observer.get(), - maybeOptions.UnsafeFromValid().options); - - return deferred.Promise(); -} - -Napi::Value RTCPeerConnection::CreateAnswer(const Napi::CallbackInfo &info) { - auto env = info.Env(); - CREATE_DEFERRED(env, deferred) - - CONVERT_ARGS_OR_REJECT_AND_RETURN_NAPI(deferred, info, maybeOptions, - Maybe) - - if (!_jinglePeerConnection || - _jinglePeerConnection->signaling_state() == - webrtc::PeerConnectionInterface::SignalingState::kClosed) { - Reject(deferred, - ErrorFactory::CreateInvalidStateError( - env, "Failed to execute 'createAnswer' on 'RTCPeerConnection': " - "The RTCPeerConnection's signalingState is 'closed'.")); - return deferred.Promise(); - } - - auto options = maybeOptions.Map([](auto options) { return options.options; }) - .OrDefault(); - auto observer = new rtc::RefCountedObject( - this, deferred); - _jinglePeerConnection->CreateAnswer(observer, options); - - return deferred.Promise(); -} - -Napi::Value -RTCPeerConnection::SetLocalDescription(const Napi::CallbackInfo &info) { - auto env = info.Env(); - CREATE_DEFERRED(env, deferred) - - CONVERT_ARGS_OR_REJECT_AND_RETURN_NAPI(deferred, info, descriptionInit, - RTCSessionDescriptionInit) - if (descriptionInit.sdp.empty()) { - descriptionInit.sdp = _lastSdp.sdp; - } - - auto maybeRawDescription = - From(descriptionInit); - if (maybeRawDescription.IsInvalid()) { - Reject(deferred, maybeRawDescription.ToErrors()[0]); - return deferred.Promise(); - } - auto rawDescription = maybeRawDescription.UnsafeFromValid(); - std::unique_ptr description( - rawDescription); - - if (!_jinglePeerConnection || - _jinglePeerConnection->signaling_state() == - webrtc::PeerConnectionInterface::SignalingState::kClosed) { - Reject( - deferred, - ErrorFactory::CreateInvalidStateError( - env, - "Failed to execute 'setLocalDescription' on 'RTCPeerConnection': " - "The RTCPeerConnection's signalingState is 'closed'.")); - return deferred.Promise(); - } - - auto observer = - new rtc::RefCountedObject(this, deferred); - _jinglePeerConnection->SetLocalDescription(observer, description.release()); - - return deferred.Promise(); -} - -Napi::Value -RTCPeerConnection::SetRemoteDescription(const Napi::CallbackInfo &info) { - auto env = info.Env(); - CREATE_DEFERRED(env, deferred) - - CONVERT_ARGS_OR_REJECT_AND_RETURN_NAPI(deferred, info, rawDescription, - webrtc::SessionDescriptionInterface *) - std::unique_ptr description( - rawDescription); - - if (!_jinglePeerConnection || - _jinglePeerConnection->signaling_state() == - webrtc::PeerConnectionInterface::SignalingState::kClosed) { - Reject( - deferred, - ErrorFactory::CreateInvalidStateError( - env, - "Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': " - "The RTCPeerConnection's signalingState is 'closed'.")); - return deferred.Promise(); - } - - auto observer = - rtc::make_ref_counted(this, deferred); - _jinglePeerConnection->SetRemoteDescription(observer.get(), - description.release()); - - return deferred.Promise(); -} - -Napi::Value RTCPeerConnection::AddIceCandidate(const Napi::CallbackInfo &info) { - auto env = info.Env(); - CREATE_DEFERRED(env, deferred) - - CONVERT_ARGS_OR_REJECT_AND_RETURN_NAPI( - deferred, info, maybeCandidate, - Maybe>) - - Dispatch(CreatePromise(deferred, [this, maybeCandidate]( - auto deferred) { - if (!_jinglePeerConnection) { - Reject(deferred, - SomeError( - "Failed to set ICE candidate; RTCPeerConnection is closed.")); - return; - } - if (_jinglePeerConnection->signaling_state() == - webrtc::PeerConnectionInterface::SignalingState::kClosed) { - Reject(deferred, - SomeError("Failed to set ICE candidate; RTCPeerConnection has a " - "closed signaling state.")); - return; - } - - if (maybeCandidate.IsJust()) { - // TODO(jack): Currently, it doesn't seem the underlying libwebrtc - // supports end-of-candidates. I hope upgrading fixes this. - if (!_jinglePeerConnection->AddIceCandidate( - maybeCandidate.UnsafeFromJust().get())) { - Reject(deferred, SomeError("Failed to set ICE candidate.")); - return; - } - } - - Resolve(deferred, this->Env().Undefined()); - })); - - return deferred.Promise(); -} - -Napi::Value -RTCPeerConnection::CreateDataChannel(const Napi::CallbackInfo &info) { - auto env = info.Env(); - if (!_jinglePeerConnection) { - Napi::Error( - env, - ErrorFactory::CreateInvalidStateError( - env, - "Failed to execute 'createDataChannel' on 'RTCPeerConnection': " - "The RTCPeerConnection is closed.")) - .ThrowAsJavaScriptException(); - return env.Undefined(); - } - if (_jinglePeerConnection->signaling_state() == - webrtc::PeerConnectionInterface::SignalingState::kClosed) { - Napi::Error( - env, - ErrorFactory::CreateInvalidStateError( - env, - "Failed to execute 'createDataChannel' on 'RTCPeerConnection': " - "The RTCPeerConnection's signaling state is 'closed'.")) - .ThrowAsJavaScriptException(); - return env.Undefined(); - } - - CONVERT_ARGS_OR_THROW_AND_RETURN_NAPI( - info, args, std::tuple>) - - auto label = std::get<0>(args); - auto dataChannelInit = std::get<1>(args).FromMaybe(webrtc::DataChannelInit()); - - auto maybeDataChannel = - _jinglePeerConnection->CreateDataChannelOrError(label, &dataChannelInit); - if (!maybeDataChannel.ok()) { - // TODO: add error message - Napi::Error(env, ErrorFactory::CreateInvalidStateError( - env, "'createDataChannel' failed")) - .ThrowAsJavaScriptException(); - return env.Undefined(); - } - auto dataChannel = maybeDataChannel.MoveValue(); - - // This memory is freed in rtc_data_channel.cc, in the constructor. - auto observer = new DataChannelObserver(_factory, dataChannel); - auto channel = - _data_channel_wrap.GetOrCreate(observer, observer->channel()); // NOLINT - _channels.emplace_back(channel); - - return channel->Value(); -} - -Napi::Value -RTCPeerConnection::GetConfiguration(const Napi::CallbackInfo &info) { - auto configuration = - _jinglePeerConnection - ? ExtendedRTCConfiguration(_jinglePeerConnection->GetConfiguration(), - _port_range) - : _cached_configuration; - CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), configuration, result, - Napi::Value) - return result; -} - -Napi::Value -RTCPeerConnection::SetConfiguration(const Napi::CallbackInfo &info) { - auto env = info.Env(); - - CONVERT_ARGS_OR_THROW_AND_RETURN_NAPI( - info, configuration, webrtc::PeerConnectionInterface::RTCConfiguration) - - if (!_jinglePeerConnection) { - Napi::Error(env, ErrorFactory::CreateInvalidStateError( - env, "RTCPeerConnection is closed")) - .ThrowAsJavaScriptException(); - return env.Undefined(); - } - - auto rtcError = _jinglePeerConnection->SetConfiguration(configuration); - if (!rtcError.ok()) { - CONVERT_OR_THROW_AND_RETURN_NAPI(env, &rtcError, error, Napi::Value) - Napi::Error(env, error).ThrowAsJavaScriptException(); - return env.Undefined(); - } - - return env.Undefined(); -} - -Napi::Value RTCPeerConnection::GetReceivers(const Napi::CallbackInfo &info) { - std::vector receivers; - if (_jinglePeerConnection) { - for (const auto &receiver : _jinglePeerConnection->GetReceivers()) { - auto new_receiver = _receiver_wrap.GetOrCreate(_factory, receiver); - receivers.emplace_back(new_receiver); - } - } - CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), receivers, result, Napi::Value) - return result; -} - -Napi::Value RTCPeerConnection::GetSenders(const Napi::CallbackInfo &info) { - std::vector senders; - if (_jinglePeerConnection) { - for (const auto &sender : _jinglePeerConnection->GetSenders()) { - senders.emplace_back(_sender_wrap.GetOrCreate(_factory, sender)); - } - } - CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), senders, result, Napi::Value) - return result; -} - -Napi::Value RTCPeerConnection::GetStats(const Napi::CallbackInfo &info) { - auto env = info.Env(); - - CREATE_DEFERRED(env, deferred) - - if (!_jinglePeerConnection) { - Reject(deferred, - ErrorFactory::CreateError(env, "RTCPeerConnection is closed")); - return deferred.Promise(); - } - - CONVERT_ARGS_OR_REJECT_AND_RETURN_NAPI(deferred, info, maybeSelector, - Maybe) - - auto callback = rtc::make_ref_counted(this, deferred); - if (maybeSelector.IsJust()) { - auto selector = maybeSelector.UnsafeFromJust(); - auto track = selector->track(); - // A little silly that the Javascript API is based off Tracks, but the - // internal API needs an explicit sender or receiver. - // Copying [what blink - // does](https://chromium.googlesource.com/chromium/src/+/refs/tags/114.0.5735.227/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc#1791) - // and iterating through all senders and receivers until we find a match. - size_t track_uses = 0U; - - rtc::scoped_refptr track_sender = nullptr; - for (const auto &sender : _jinglePeerConnection->GetSenders()) { - if (sender->track() == track) { - ++track_uses; - track_sender = sender; - } - } - - rtc::scoped_refptr track_receiver = nullptr; - for (const auto &receiver : _jinglePeerConnection->GetReceivers()) { - if (receiver->track() == track) { - ++track_uses; - track_receiver = receiver; - } - } - - if (track_uses == 0U) { - Reject(deferred, - ErrorFactory::CreateInvalidAccessError( - env, "There is no sender or receiver for the track.")); - return deferred.Promise(); - } - - if (track_uses > 1U) { - Reject(deferred, - ErrorFactory::CreateInvalidAccessError( - env, - "There are more than one sender or receiver for the track.")); - return deferred.Promise(); - } - - if (track_sender != nullptr) { - assert(track_receiver == nullptr); - _jinglePeerConnection->GetStats(track_sender, callback); - } else { - assert(track_receiver != nullptr); - _jinglePeerConnection->GetStats(track_receiver, callback); - } - } else { - // null selector - _jinglePeerConnection->GetStats(callback.get()); - } - - return deferred.Promise(); -} - -Napi::Value RTCPeerConnection::GetTransceivers(const Napi::CallbackInfo &info) { - std::vector transceivers; - if (_jinglePeerConnection && - _jinglePeerConnection->GetConfiguration().sdp_semantics == - webrtc::SdpSemantics::kUnifiedPlan) { - for (const auto &transceiver : _jinglePeerConnection->GetTransceivers()) { - transceivers.emplace_back( - _transceiver_wrap.GetOrCreate(_factory, transceiver)); - } - } - CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), transceivers, result, - Napi::Value) - return result; -} - -Napi::Value RTCPeerConnection::UpdateIce(const Napi::CallbackInfo &info) { - return info.Env().Undefined(); -} - -Napi::Value RTCPeerConnection::Close(const Napi::CallbackInfo &info) { - if (_jinglePeerConnection) { - _cached_configuration = ExtendedRTCConfiguration( - _jinglePeerConnection->GetConfiguration(), _port_range); - _jinglePeerConnection->Close(); - // NOTE(mroberts): Perhaps another way to do this is to just register all - // remote MediaStreamTracks against this RTCPeerConnection, not unlike what - // we do with RTCDataChannels. - if (_jinglePeerConnection->GetConfiguration().sdp_semantics == - webrtc::SdpSemantics::kUnifiedPlan) { - for (const auto &transceiver : _jinglePeerConnection->GetTransceivers()) { - auto track = MediaStreamTrack::wrap()->GetOrCreate( - _factory, transceiver->receiver()->track()); - track->OnPeerConnectionClosed(); - } - } - for (auto channel : _channels) { - channel->OnPeerConnectionClosed(); - } - } - - // Clear the wrap caches before releasing the WebRTC peer connection. - // This releases all scoped_refptr references to WebRTC objects while - // the signaling thread is still active, preventing crashes during cleanup. - _data_channel_wrap.clear(); - _stream_wrap.clear(); - _receiver_wrap.clear(); - _transceiver_wrap.clear(); - _sender_wrap.clear(); - _transport_wrap.clear(); - _channels.clear(); - - _jinglePeerConnection = nullptr; - - if (_factory) { - if (_shouldReleaseFactory) { - PeerConnectionFactory::Release(); - } - _factory = nullptr; - } - - return info.Env().Undefined(); -} - -Napi::Value RTCPeerConnection::RestartIce(const Napi::CallbackInfo &info) { - (void)info; - if (_jinglePeerConnection) { - _jinglePeerConnection->RestartIce(); - } - return info.Env().Undefined(); -} - -Napi::Value -RTCPeerConnection::GetCanTrickleIceCandidates(const Napi::CallbackInfo &info) { - auto env = info.Env(); - if (!_jinglePeerConnection) { - return env.Null(); - } - auto canTrickleIceCandidates = - _jinglePeerConnection->can_trickle_ice_candidates(); - CONVERT_OR_THROW_AND_RETURN_NAPI(env, canTrickleIceCandidates, result, - Napi::Value) - return result; -} - -Napi::Value -RTCPeerConnection::GetConnectionState(const Napi::CallbackInfo &info) { - auto env = info.Env(); - - auto connectionState = - _jinglePeerConnection - ? _jinglePeerConnection->peer_connection_state() - : webrtc::PeerConnectionInterface::PeerConnectionState::kClosed; - - CONVERT_OR_THROW_AND_RETURN_NAPI(env, connectionState, result, Napi::Value) - return result; -} - -Napi::Value -RTCPeerConnection::GetCurrentLocalDescription(const Napi::CallbackInfo &info) { - auto env = info.Env(); - Napi::Value result = env.Null(); - if (_jinglePeerConnection && - _jinglePeerConnection->current_local_description()) { - CONVERT_OR_THROW_AND_RETURN_NAPI( - env, _jinglePeerConnection->current_local_description(), description, - Napi::Value) - result = description; - } - return result; -} - -Napi::Value -RTCPeerConnection::GetLocalDescription(const Napi::CallbackInfo &info) { - auto env = info.Env(); - Napi::Value result = env.Null(); - if (_jinglePeerConnection && _jinglePeerConnection->local_description()) { - CONVERT_OR_THROW_AND_RETURN_NAPI(env, - _jinglePeerConnection->local_description(), - description, Napi::Value) - result = description; - } - return result; -} - -Napi::Value -RTCPeerConnection::GetPendingLocalDescription(const Napi::CallbackInfo &info) { - auto env = info.Env(); - Napi::Value result = env.Null(); - if (_jinglePeerConnection && - _jinglePeerConnection->pending_local_description()) { - CONVERT_OR_THROW_AND_RETURN_NAPI( - env, _jinglePeerConnection->pending_local_description(), description, - Napi::Value) - result = description; - } - return result; -} - -Napi::Value -RTCPeerConnection::GetCurrentRemoteDescription(const Napi::CallbackInfo &info) { - auto env = info.Env(); - Napi::Value result = env.Null(); - if (_jinglePeerConnection && - _jinglePeerConnection->current_remote_description()) { - CONVERT_OR_THROW_AND_RETURN_NAPI( - env, _jinglePeerConnection->current_remote_description(), description, - Napi::Value) - result = description; - } - return result; -} - -Napi::Value -RTCPeerConnection::GetRemoteDescription(const Napi::CallbackInfo &info) { - auto env = info.Env(); - Napi::Value result = env.Null(); - if (_jinglePeerConnection && _jinglePeerConnection->remote_description()) { - CONVERT_OR_THROW_AND_RETURN_NAPI( - env, _jinglePeerConnection->remote_description(), description, - Napi::Value) - result = description; - } - return result; -} - -Napi::Value -RTCPeerConnection::GetPendingRemoteDescription(const Napi::CallbackInfo &info) { - auto env = info.Env(); - Napi::Value result = env.Null(); - if (_jinglePeerConnection && - _jinglePeerConnection->pending_remote_description()) { - CONVERT_OR_THROW_AND_RETURN_NAPI( - env, _jinglePeerConnection->pending_remote_description(), description, - Napi::Value) - result = description; - } - return result; -} - -Napi::Value RTCPeerConnection::GetSctp(const Napi::CallbackInfo &info) { - return _jinglePeerConnection && _jinglePeerConnection->GetSctpTransport() - ? _transport_wrap - .GetOrCreate(_factory, - _jinglePeerConnection->GetSctpTransport()) - ->Value() - : info.Env().Null(); -} - -Napi::Value -RTCPeerConnection::GetSignalingState(const Napi::CallbackInfo &info) { - auto signalingState = - _jinglePeerConnection - ? _jinglePeerConnection->signaling_state() - : webrtc::PeerConnectionInterface::SignalingState ::kClosed; - CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), signalingState, result, - Napi::Value) - return result; -} - -Napi::Value -RTCPeerConnection::GetIceConnectionState(const Napi::CallbackInfo &info) { - auto iceConnectionState = - _jinglePeerConnection - ? _jinglePeerConnection->standardized_ice_connection_state() - : webrtc::PeerConnectionInterface::IceConnectionState:: - kIceConnectionClosed; - CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), iceConnectionState, result, - Napi::Value) - return result; -} - -Napi::Value -RTCPeerConnection::GetIceGatheringState(const Napi::CallbackInfo &info) { - auto iceGatheringState = _jinglePeerConnection - ? _jinglePeerConnection->ice_gathering_state() - : webrtc::PeerConnectionInterface:: - IceGatheringState::kIceGatheringComplete; - CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), iceGatheringState, result, - Napi::Value) - return result; -} - -void RTCPeerConnection::SaveLastSdp(const RTCSessionDescriptionInit &lastSdp) { - this->_lastSdp = lastSdp; -} - -void RTCPeerConnection::Init(Napi::Env env, Napi::Object exports) { - auto func = DefineClass( - env, "RTCPeerConnection", - {InstanceMethod("addTrack", &RTCPeerConnection::AddTrack), - InstanceMethod("addTransceiver", &RTCPeerConnection::AddTransceiver), - InstanceMethod("removeTrack", &RTCPeerConnection::RemoveTrack), - InstanceMethod("createOffer", &RTCPeerConnection::CreateOffer), - InstanceMethod("createAnswer", &RTCPeerConnection::CreateAnswer), - InstanceMethod("setLocalDescription", - &RTCPeerConnection::SetLocalDescription), - InstanceMethod("setRemoteDescription", - &RTCPeerConnection::SetRemoteDescription), - InstanceMethod("getConfiguration", &RTCPeerConnection::GetConfiguration), - InstanceMethod("setConfiguration", &RTCPeerConnection::SetConfiguration), - InstanceMethod("restartIce", &RTCPeerConnection::RestartIce), - InstanceMethod("getReceivers", &RTCPeerConnection::GetReceivers), - InstanceMethod("getSenders", &RTCPeerConnection::GetSenders), - InstanceMethod("getStats", &RTCPeerConnection::GetStats), - InstanceMethod("getTransceivers", &RTCPeerConnection::GetTransceivers), - InstanceMethod("updateIce", &RTCPeerConnection::UpdateIce), - InstanceMethod("addIceCandidate", &RTCPeerConnection::AddIceCandidate), - InstanceMethod("createDataChannel", - &RTCPeerConnection::CreateDataChannel), - InstanceMethod("close", &RTCPeerConnection::Close), - InstanceAccessor("canTrickleIceCandidates", - &RTCPeerConnection::GetCanTrickleIceCandidates, - nullptr), - InstanceAccessor("connectionState", - &RTCPeerConnection::GetConnectionState, nullptr), - InstanceAccessor("currentLocalDescription", - &RTCPeerConnection::GetCurrentLocalDescription, - nullptr), - InstanceAccessor("localDescription", - &RTCPeerConnection::GetLocalDescription, nullptr), - InstanceAccessor("pendingLocalDescription", - &RTCPeerConnection::GetPendingLocalDescription, - nullptr), - InstanceAccessor("currentRemoteDescription", - &RTCPeerConnection::GetCurrentRemoteDescription, - nullptr), - InstanceAccessor("remoteDescription", - &RTCPeerConnection::GetRemoteDescription, nullptr), - InstanceAccessor("pendingRemoteDescription", - &RTCPeerConnection::GetPendingRemoteDescription, - nullptr), - InstanceAccessor("sctp", &RTCPeerConnection::GetSctp, nullptr), - InstanceAccessor("signalingState", &RTCPeerConnection::GetSignalingState, - nullptr), - InstanceAccessor("iceConnectionState", - &RTCPeerConnection::GetIceConnectionState, nullptr), - InstanceAccessor("iceGatheringState", - &RTCPeerConnection::GetIceGatheringState, nullptr)}); - - constructor() = Napi::Persistent(func); - constructor().SuppressDestruct(); - - exports.Set("RTCPeerConnection", func); -} - -} // namespace node_webrtc + Maybe maybeInit = std::get<1>(args); + auto result = + kindOrTrack.IsLeft() + ? maybeInit.IsNothing() + ? _jinglePeerConnection->AddTransceiver( + kindOrTrack.UnsafeFromLeft()) + : _jinglePeerConnection->AddTransceiver( + kindOrTrack.UnsafeFromLeft(), maybeInit.UnsafeFromJust()) + : maybeInit.IsNothing() ? _jinglePeerConnection->AddTransceiver( + kindOrTrack.UnsafeFromRight()->track()) + : _jinglePeerConnection->AddTransceiver( + kindOrTrack.UnsafeFromRight()->track(), + maybeInit.UnsafeFromJust()); + if (!result.ok()) { + CONVERT_OR_THROW_AND_RETURN_NAPI(env, &result.error(), error, Napi::Value) + Napi::Error(env, error).ThrowAsJavaScriptException(); + return env.Undefined(); + } + auto rtpTransceiver = result.value(); + return _transceiver_wrap.GetOrCreate(_factory, rtpTransceiver)->Value(); +} + +Napi::Value RTCPeerConnection::RemoveTrack(const Napi::CallbackInfo &info) { + auto env = info.Env(); + if (!_jinglePeerConnection) { + Napi::Error(env, + ErrorFactory::CreateInvalidStateError( + env, "Cannot removeTrack; RTCPeerConnection is closed")) + .ThrowAsJavaScriptException(); + return env.Undefined(); + } + CONVERT_ARGS_OR_THROW_AND_RETURN_NAPI(info, sender, RTCRtpSender *) + auto senders = _jinglePeerConnection->GetSenders(); + if (std::find(senders.begin(), senders.end(), sender->sender()) == + senders.end()) { + Napi::Error( + env, ErrorFactory::CreateInvalidAccessError(env, "Cannot removeTrack")) + .ThrowAsJavaScriptException(); + return env.Undefined(); + } + auto error = _jinglePeerConnection->RemoveTrackOrError(sender->sender()); + if (!error.ok()) { + CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), &error, result, Napi::Value) + Napi::Error(info.Env(), result).ThrowAsJavaScriptException(); + } + return env.Undefined(); +} + +Napi::Value RTCPeerConnection::CreateOffer(const Napi::CallbackInfo &info) { + auto env = info.Env(); + CREATE_DEFERRED(env, deferred) + + auto maybeOptions = + From>(Arguments(info)).Map([](auto maybeOptions) { + return maybeOptions.FromMaybe(RTCOfferOptions()); + }); + if (maybeOptions.IsInvalid()) { + Reject(deferred, SomeError(maybeOptions.ToErrors()[0])); + return deferred.Promise(); + } + + if (!_jinglePeerConnection || + _jinglePeerConnection->signaling_state() == + webrtc::PeerConnectionInterface::SignalingState::kClosed) { + Reject(deferred, + ErrorFactory::CreateInvalidStateError( + env, "Failed to execute 'createOffer' on 'RTCPeerConnection': " + "The RTCPeerConnection's signalingState is 'closed'.")); + return deferred.Promise(); + } + + auto observer = + rtc::make_ref_counted(this, deferred); + _jinglePeerConnection->CreateOffer(observer.get(), + maybeOptions.UnsafeFromValid().options); + + return deferred.Promise(); +} + +Napi::Value RTCPeerConnection::CreateAnswer(const Napi::CallbackInfo &info) { + auto env = info.Env(); + CREATE_DEFERRED(env, deferred) + + CONVERT_ARGS_OR_REJECT_AND_RETURN_NAPI(deferred, info, maybeOptions, + Maybe) + + if (!_jinglePeerConnection || + _jinglePeerConnection->signaling_state() == + webrtc::PeerConnectionInterface::SignalingState::kClosed) { + Reject(deferred, + ErrorFactory::CreateInvalidStateError( + env, "Failed to execute 'createAnswer' on 'RTCPeerConnection': " + "The RTCPeerConnection's signalingState is 'closed'.")); + return deferred.Promise(); + } + + auto options = maybeOptions.Map([](auto options) { return options.options; }) + .OrDefault(); + auto observer = new rtc::RefCountedObject( + this, deferred); + _jinglePeerConnection->CreateAnswer(observer, options); + + return deferred.Promise(); +} + +Napi::Value +RTCPeerConnection::SetLocalDescription(const Napi::CallbackInfo &info) { + auto env = info.Env(); + CREATE_DEFERRED(env, deferred) + + CONVERT_ARGS_OR_REJECT_AND_RETURN_NAPI(deferred, info, descriptionInit, + RTCSessionDescriptionInit) + if (descriptionInit.sdp.empty()) { + descriptionInit.sdp = _lastSdp.sdp; + } + + auto maybeRawDescription = + From(descriptionInit); + if (maybeRawDescription.IsInvalid()) { + Reject(deferred, maybeRawDescription.ToErrors()[0]); + return deferred.Promise(); + } + auto rawDescription = maybeRawDescription.UnsafeFromValid(); + std::unique_ptr description( + rawDescription); + + if (!_jinglePeerConnection || + _jinglePeerConnection->signaling_state() == + webrtc::PeerConnectionInterface::SignalingState::kClosed) { + Reject( + deferred, + ErrorFactory::CreateInvalidStateError( + env, + "Failed to execute 'setLocalDescription' on 'RTCPeerConnection': " + "The RTCPeerConnection's signalingState is 'closed'.")); + return deferred.Promise(); + } + + auto observer = + new rtc::RefCountedObject(this, deferred); + _jinglePeerConnection->SetLocalDescription(observer, description.release()); + + return deferred.Promise(); +} + +Napi::Value +RTCPeerConnection::SetRemoteDescription(const Napi::CallbackInfo &info) { + auto env = info.Env(); + CREATE_DEFERRED(env, deferred) + + CONVERT_ARGS_OR_REJECT_AND_RETURN_NAPI(deferred, info, rawDescription, + webrtc::SessionDescriptionInterface *) + std::unique_ptr description( + rawDescription); + + if (!_jinglePeerConnection || + _jinglePeerConnection->signaling_state() == + webrtc::PeerConnectionInterface::SignalingState::kClosed) { + Reject( + deferred, + ErrorFactory::CreateInvalidStateError( + env, + "Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': " + "The RTCPeerConnection's signalingState is 'closed'.")); + return deferred.Promise(); + } + + auto observer = + rtc::make_ref_counted(this, deferred); + _jinglePeerConnection->SetRemoteDescription(observer.get(), + description.release()); + + return deferred.Promise(); +} + +Napi::Value RTCPeerConnection::AddIceCandidate(const Napi::CallbackInfo &info) { + auto env = info.Env(); + CREATE_DEFERRED(env, deferred) + + CONVERT_ARGS_OR_REJECT_AND_RETURN_NAPI( + deferred, info, maybeCandidate, + Maybe>) + + Dispatch(CreatePromise(deferred, [this, maybeCandidate]( + auto deferred) { + if (!_jinglePeerConnection) { + Reject(deferred, + SomeError( + "Failed to set ICE candidate; RTCPeerConnection is closed.")); + return; + } + if (_jinglePeerConnection->signaling_state() == + webrtc::PeerConnectionInterface::SignalingState::kClosed) { + Reject(deferred, + SomeError("Failed to set ICE candidate; RTCPeerConnection has a " + "closed signaling state.")); + return; + } + + if (maybeCandidate.IsJust()) { + // TODO(jack): Currently, it doesn't seem the underlying libwebrtc + // supports end-of-candidates. I hope upgrading fixes this. + if (!_jinglePeerConnection->AddIceCandidate( + maybeCandidate.UnsafeFromJust().get())) { + Reject(deferred, SomeError("Failed to set ICE candidate.")); + return; + } + } + + Resolve(deferred, this->Env().Undefined()); + })); + + return deferred.Promise(); +} + +Napi::Value +RTCPeerConnection::CreateDataChannel(const Napi::CallbackInfo &info) { + auto env = info.Env(); + if (!_jinglePeerConnection) { + Napi::Error( + env, + ErrorFactory::CreateInvalidStateError( + env, + "Failed to execute 'createDataChannel' on 'RTCPeerConnection': " + "The RTCPeerConnection is closed.")) + .ThrowAsJavaScriptException(); + return env.Undefined(); + } + if (_jinglePeerConnection->signaling_state() == + webrtc::PeerConnectionInterface::SignalingState::kClosed) { + Napi::Error( + env, + ErrorFactory::CreateInvalidStateError( + env, + "Failed to execute 'createDataChannel' on 'RTCPeerConnection': " + "The RTCPeerConnection's signaling state is 'closed'.")) + .ThrowAsJavaScriptException(); + return env.Undefined(); + } + + CONVERT_ARGS_OR_THROW_AND_RETURN_NAPI( + info, args, std::tuple>) + + auto label = std::get<0>(args); + auto dataChannelInit = std::get<1>(args).FromMaybe(webrtc::DataChannelInit()); + + auto maybeDataChannel = + _jinglePeerConnection->CreateDataChannelOrError(label, &dataChannelInit); + if (!maybeDataChannel.ok()) { + // TODO: add error message + Napi::Error(env, ErrorFactory::CreateInvalidStateError( + env, "'createDataChannel' failed")) + .ThrowAsJavaScriptException(); + return env.Undefined(); + } + auto dataChannel = maybeDataChannel.MoveValue(); + + // This memory is freed in rtc_data_channel.cc, in the constructor. + auto observer = new DataChannelObserver(_factory, dataChannel); + auto channel = + _data_channel_wrap.GetOrCreate(observer, observer->channel()); // NOLINT + _channels.emplace_back(channel); + + return channel->Value(); +} + +Napi::Value +RTCPeerConnection::GetConfiguration(const Napi::CallbackInfo &info) { + auto configuration = + _jinglePeerConnection + ? ExtendedRTCConfiguration(_jinglePeerConnection->GetConfiguration(), + _port_range) + : _cached_configuration; + CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), configuration, result, + Napi::Value) + return result; +} + +Napi::Value +RTCPeerConnection::SetConfiguration(const Napi::CallbackInfo &info) { + auto env = info.Env(); + + CONVERT_ARGS_OR_THROW_AND_RETURN_NAPI( + info, configuration, webrtc::PeerConnectionInterface::RTCConfiguration) + + if (!_jinglePeerConnection) { + Napi::Error(env, ErrorFactory::CreateInvalidStateError( + env, "RTCPeerConnection is closed")) + .ThrowAsJavaScriptException(); + return env.Undefined(); + } + + auto rtcError = _jinglePeerConnection->SetConfiguration(configuration); + if (!rtcError.ok()) { + CONVERT_OR_THROW_AND_RETURN_NAPI(env, &rtcError, error, Napi::Value) + Napi::Error(env, error).ThrowAsJavaScriptException(); + return env.Undefined(); + } + + return env.Undefined(); +} + +Napi::Value RTCPeerConnection::GetReceivers(const Napi::CallbackInfo &info) { + std::vector receivers; + if (_jinglePeerConnection) { + for (const auto &receiver : _jinglePeerConnection->GetReceivers()) { + auto new_receiver = _receiver_wrap.GetOrCreate(_factory, receiver); + receivers.emplace_back(new_receiver); + } + } + CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), receivers, result, Napi::Value) + return result; +} + +Napi::Value RTCPeerConnection::GetSenders(const Napi::CallbackInfo &info) { + std::vector senders; + if (_jinglePeerConnection) { + for (const auto &sender : _jinglePeerConnection->GetSenders()) { + senders.emplace_back(_sender_wrap.GetOrCreate(_factory, sender)); + } + } + CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), senders, result, Napi::Value) + return result; +} + +Napi::Value RTCPeerConnection::GetStats(const Napi::CallbackInfo &info) { + auto env = info.Env(); + + CREATE_DEFERRED(env, deferred) + + if (!_jinglePeerConnection) { + Reject(deferred, + ErrorFactory::CreateError(env, "RTCPeerConnection is closed")); + return deferred.Promise(); + } + + CONVERT_ARGS_OR_REJECT_AND_RETURN_NAPI(deferred, info, maybeSelector, + Maybe) + + auto callback = rtc::make_ref_counted(this, deferred); + if (maybeSelector.IsJust()) { + auto selector = maybeSelector.UnsafeFromJust(); + auto track = selector->track(); + // A little silly that the Javascript API is based off Tracks, but the + // internal API needs an explicit sender or receiver. + // Copying [what blink + // does](https://chromium.googlesource.com/chromium/src/+/refs/tags/114.0.5735.227/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc#1791) + // and iterating through all senders and receivers until we find a match. + size_t track_uses = 0U; + + rtc::scoped_refptr track_sender = nullptr; + for (const auto &sender : _jinglePeerConnection->GetSenders()) { + if (sender->track() == track) { + ++track_uses; + track_sender = sender; + } + } + + rtc::scoped_refptr track_receiver = nullptr; + for (const auto &receiver : _jinglePeerConnection->GetReceivers()) { + if (receiver->track() == track) { + ++track_uses; + track_receiver = receiver; + } + } + + if (track_uses == 0U) { + Reject(deferred, + ErrorFactory::CreateInvalidAccessError( + env, "There is no sender or receiver for the track.")); + return deferred.Promise(); + } + + if (track_uses > 1U) { + Reject(deferred, + ErrorFactory::CreateInvalidAccessError( + env, + "There are more than one sender or receiver for the track.")); + return deferred.Promise(); + } + + if (track_sender != nullptr) { + assert(track_receiver == nullptr); + _jinglePeerConnection->GetStats(track_sender, callback); + } else { + assert(track_receiver != nullptr); + _jinglePeerConnection->GetStats(track_receiver, callback); + } + } else { + // null selector + _jinglePeerConnection->GetStats(callback.get()); + } + + return deferred.Promise(); +} + +Napi::Value RTCPeerConnection::GetTransceivers(const Napi::CallbackInfo &info) { + std::vector transceivers; + if (_jinglePeerConnection && + _jinglePeerConnection->GetConfiguration().sdp_semantics == + webrtc::SdpSemantics::kUnifiedPlan) { + for (const auto &transceiver : _jinglePeerConnection->GetTransceivers()) { + transceivers.emplace_back( + _transceiver_wrap.GetOrCreate(_factory, transceiver)); + } + } + CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), transceivers, result, + Napi::Value) + return result; +} + +Napi::Value RTCPeerConnection::UpdateIce(const Napi::CallbackInfo &info) { + return info.Env().Undefined(); +} + +Napi::Value RTCPeerConnection::Close(const Napi::CallbackInfo &info) { + if (_jinglePeerConnection) { + _cached_configuration = ExtendedRTCConfiguration( + _jinglePeerConnection->GetConfiguration(), _port_range); + _jinglePeerConnection->Close(); + // NOTE(mroberts): Perhaps another way to do this is to just register all + // remote MediaStreamTracks against this RTCPeerConnection, not unlike what + // we do with RTCDataChannels. + if (_jinglePeerConnection->GetConfiguration().sdp_semantics == + webrtc::SdpSemantics::kUnifiedPlan) { + for (const auto &transceiver : _jinglePeerConnection->GetTransceivers()) { + auto track = MediaStreamTrack::wrap()->GetOrCreate( + _factory, transceiver->receiver()->track()); + track->OnPeerConnectionClosed(); + } + } + for (auto channel : _channels) { + channel->OnPeerConnectionClosed(); + } + } + + // Clear the wrap caches before releasing the WebRTC peer connection. + // This releases all scoped_refptr references to WebRTC objects while + // the signaling thread is still active, preventing crashes during cleanup. + _data_channel_wrap.clear(); + _stream_wrap.clear(); + _receiver_wrap.clear(); + _transceiver_wrap.clear(); + _sender_wrap.clear(); + _transport_wrap.clear(); + _channels.clear(); + + _jinglePeerConnection = nullptr; + + if (_factory) { + if (_shouldReleaseFactory) { + PeerConnectionFactory::Release(); + } + _factory = nullptr; + } + + return info.Env().Undefined(); +} + +Napi::Value RTCPeerConnection::RestartIce(const Napi::CallbackInfo &info) { + (void)info; + if (_jinglePeerConnection) { + _jinglePeerConnection->RestartIce(); + } + return info.Env().Undefined(); +} + +Napi::Value +RTCPeerConnection::GetCanTrickleIceCandidates(const Napi::CallbackInfo &info) { + auto env = info.Env(); + if (!_jinglePeerConnection) { + return env.Null(); + } + auto canTrickleIceCandidates = + _jinglePeerConnection->can_trickle_ice_candidates(); + CONVERT_OR_THROW_AND_RETURN_NAPI(env, canTrickleIceCandidates, result, + Napi::Value) + return result; +} + +Napi::Value +RTCPeerConnection::GetConnectionState(const Napi::CallbackInfo &info) { + auto env = info.Env(); + + auto connectionState = + _jinglePeerConnection + ? _jinglePeerConnection->peer_connection_state() + : webrtc::PeerConnectionInterface::PeerConnectionState::kClosed; + + CONVERT_OR_THROW_AND_RETURN_NAPI(env, connectionState, result, Napi::Value) + return result; +} + +Napi::Value +RTCPeerConnection::GetCurrentLocalDescription(const Napi::CallbackInfo &info) { + auto env = info.Env(); + Napi::Value result = env.Null(); + if (_jinglePeerConnection && + _jinglePeerConnection->current_local_description()) { + CONVERT_OR_THROW_AND_RETURN_NAPI( + env, _jinglePeerConnection->current_local_description(), description, + Napi::Value) + result = description; + } + return result; +} + +Napi::Value +RTCPeerConnection::GetLocalDescription(const Napi::CallbackInfo &info) { + auto env = info.Env(); + Napi::Value result = env.Null(); + if (_jinglePeerConnection && _jinglePeerConnection->local_description()) { + CONVERT_OR_THROW_AND_RETURN_NAPI(env, + _jinglePeerConnection->local_description(), + description, Napi::Value) + result = description; + } + return result; +} + +Napi::Value +RTCPeerConnection::GetPendingLocalDescription(const Napi::CallbackInfo &info) { + auto env = info.Env(); + Napi::Value result = env.Null(); + if (_jinglePeerConnection && + _jinglePeerConnection->pending_local_description()) { + CONVERT_OR_THROW_AND_RETURN_NAPI( + env, _jinglePeerConnection->pending_local_description(), description, + Napi::Value) + result = description; + } + return result; +} + +Napi::Value +RTCPeerConnection::GetCurrentRemoteDescription(const Napi::CallbackInfo &info) { + auto env = info.Env(); + Napi::Value result = env.Null(); + if (_jinglePeerConnection && + _jinglePeerConnection->current_remote_description()) { + CONVERT_OR_THROW_AND_RETURN_NAPI( + env, _jinglePeerConnection->current_remote_description(), description, + Napi::Value) + result = description; + } + return result; +} + +Napi::Value +RTCPeerConnection::GetRemoteDescription(const Napi::CallbackInfo &info) { + auto env = info.Env(); + Napi::Value result = env.Null(); + if (_jinglePeerConnection && _jinglePeerConnection->remote_description()) { + CONVERT_OR_THROW_AND_RETURN_NAPI( + env, _jinglePeerConnection->remote_description(), description, + Napi::Value) + result = description; + } + return result; +} + +Napi::Value +RTCPeerConnection::GetPendingRemoteDescription(const Napi::CallbackInfo &info) { + auto env = info.Env(); + Napi::Value result = env.Null(); + if (_jinglePeerConnection && + _jinglePeerConnection->pending_remote_description()) { + CONVERT_OR_THROW_AND_RETURN_NAPI( + env, _jinglePeerConnection->pending_remote_description(), description, + Napi::Value) + result = description; + } + return result; +} + +Napi::Value RTCPeerConnection::GetSctp(const Napi::CallbackInfo &info) { + return _jinglePeerConnection && _jinglePeerConnection->GetSctpTransport() + ? _transport_wrap + .GetOrCreate(_factory, + _jinglePeerConnection->GetSctpTransport()) + ->Value() + : info.Env().Null(); +} + +Napi::Value +RTCPeerConnection::GetSignalingState(const Napi::CallbackInfo &info) { + auto signalingState = + _jinglePeerConnection + ? _jinglePeerConnection->signaling_state() + : webrtc::PeerConnectionInterface::SignalingState ::kClosed; + CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), signalingState, result, + Napi::Value) + return result; +} + +Napi::Value +RTCPeerConnection::GetIceConnectionState(const Napi::CallbackInfo &info) { + auto iceConnectionState = + _jinglePeerConnection + ? _jinglePeerConnection->standardized_ice_connection_state() + : webrtc::PeerConnectionInterface::IceConnectionState:: + kIceConnectionClosed; + CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), iceConnectionState, result, + Napi::Value) + return result; +} + +Napi::Value +RTCPeerConnection::GetIceGatheringState(const Napi::CallbackInfo &info) { + auto iceGatheringState = _jinglePeerConnection + ? _jinglePeerConnection->ice_gathering_state() + : webrtc::PeerConnectionInterface:: + IceGatheringState::kIceGatheringComplete; + CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), iceGatheringState, result, + Napi::Value) + return result; +} + +void RTCPeerConnection::SaveLastSdp(const RTCSessionDescriptionInit &lastSdp) { + this->_lastSdp = lastSdp; +} + +void RTCPeerConnection::Init(Napi::Env env, Napi::Object exports) { + auto func = DefineClass( + env, "RTCPeerConnection", + {InstanceMethod("addTrack", &RTCPeerConnection::AddTrack), + InstanceMethod("addTransceiver", &RTCPeerConnection::AddTransceiver), + InstanceMethod("removeTrack", &RTCPeerConnection::RemoveTrack), + InstanceMethod("createOffer", &RTCPeerConnection::CreateOffer), + InstanceMethod("createAnswer", &RTCPeerConnection::CreateAnswer), + InstanceMethod("setLocalDescription", + &RTCPeerConnection::SetLocalDescription), + InstanceMethod("setRemoteDescription", + &RTCPeerConnection::SetRemoteDescription), + InstanceMethod("getConfiguration", &RTCPeerConnection::GetConfiguration), + InstanceMethod("setConfiguration", &RTCPeerConnection::SetConfiguration), + InstanceMethod("restartIce", &RTCPeerConnection::RestartIce), + InstanceMethod("getReceivers", &RTCPeerConnection::GetReceivers), + InstanceMethod("getSenders", &RTCPeerConnection::GetSenders), + InstanceMethod("getStats", &RTCPeerConnection::GetStats), + InstanceMethod("getTransceivers", &RTCPeerConnection::GetTransceivers), + InstanceMethod("updateIce", &RTCPeerConnection::UpdateIce), + InstanceMethod("addIceCandidate", &RTCPeerConnection::AddIceCandidate), + InstanceMethod("createDataChannel", + &RTCPeerConnection::CreateDataChannel), + InstanceMethod("close", &RTCPeerConnection::Close), + InstanceAccessor("canTrickleIceCandidates", + &RTCPeerConnection::GetCanTrickleIceCandidates, + nullptr), + InstanceAccessor("connectionState", + &RTCPeerConnection::GetConnectionState, nullptr), + InstanceAccessor("currentLocalDescription", + &RTCPeerConnection::GetCurrentLocalDescription, + nullptr), + InstanceAccessor("localDescription", + &RTCPeerConnection::GetLocalDescription, nullptr), + InstanceAccessor("pendingLocalDescription", + &RTCPeerConnection::GetPendingLocalDescription, + nullptr), + InstanceAccessor("currentRemoteDescription", + &RTCPeerConnection::GetCurrentRemoteDescription, + nullptr), + InstanceAccessor("remoteDescription", + &RTCPeerConnection::GetRemoteDescription, nullptr), + InstanceAccessor("pendingRemoteDescription", + &RTCPeerConnection::GetPendingRemoteDescription, + nullptr), + InstanceAccessor("sctp", &RTCPeerConnection::GetSctp, nullptr), + InstanceAccessor("signalingState", &RTCPeerConnection::GetSignalingState, + nullptr), + InstanceAccessor("iceConnectionState", + &RTCPeerConnection::GetIceConnectionState, nullptr), + InstanceAccessor("iceGatheringState", + &RTCPeerConnection::GetIceGatheringState, nullptr)}); + + constructor() = Napi::Persistent(func); + constructor().SuppressDestruct(); + + exports.Set("RTCPeerConnection", func); +} + +} // namespace node_webrtc diff --git a/src/interfaces/rtc_peer_connection.hh b/src/interfaces/rtc_peer_connection.hh index d73363e78..47469a4e8 100644 --- a/src/interfaces/rtc_peer_connection.hh +++ b/src/interfaces/rtc_peer_connection.hh @@ -1,149 +1,148 @@ -/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license that can be found - * in the LICENSE.md file in the root of the source tree. All contributing - * project authors may be found in the AUTHORS file in the root of the source - * tree. - */ -#pragma once - -#include - -#include -#include -#include - -#include "src/dictionaries/node_webrtc/extended_rtc_configuration.hh" -#include "src/dictionaries/node_webrtc/rtc_session_description_init.hh" -#include "src/interfaces/media_stream.hh" -#include "src/interfaces/rtc_data_channel.hh" -#include "src/interfaces/rtc_rtp_receiver.hh" -#include "src/interfaces/rtc_rtp_sender.hh" -#include "src/interfaces/rtc_rtp_transceiver.hh" -#include "src/interfaces/rtc_sctp_transport.hh" -#include "src/node/async_object_wrap_with_loop.hh" -#include "src/node/ref_ptr.hh" -#include "src/node/wrap.hh" - +/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license that can be found + * in the LICENSE.md file in the root of the source tree. All contributing + * project authors may be found in the AUTHORS file in the root of the source + * tree. + */ +#pragma once + +#include + +#include +#include +#include + +#include "src/dictionaries/node_webrtc/extended_rtc_configuration.hh" +#include "src/dictionaries/node_webrtc/rtc_session_description_init.hh" +#include "src/interfaces/media_stream.hh" +#include "src/interfaces/rtc_data_channel.hh" +#include "src/interfaces/rtc_rtp_receiver.hh" +#include "src/interfaces/rtc_rtp_sender.hh" +#include "src/interfaces/rtc_rtp_transceiver.hh" +#include "src/interfaces/rtc_sctp_transport.hh" +#include "src/node/async_object_wrap_with_loop.hh" +#include "src/node/ref_ptr.hh" +#include "src/node/wrap.hh" + namespace webrtc { class DataChannelInterface; -class IceCandidateInterface; class MediaStreamInterface; class RtpReceiverInterface; class RtpTransceiverInterface; - -} // namespace webrtc - -namespace node_webrtc { - -class PeerConnectionFactory; - -class RTCPeerConnection : public AsyncObjectWrapWithLoop, - public webrtc::PeerConnectionObserver { -public: - RTCPeerConnection(const RTCPeerConnection &) = delete; - RTCPeerConnection(RTCPeerConnection &&) = delete; - RTCPeerConnection &operator=(const RTCPeerConnection &) = delete; - RTCPeerConnection &operator=(RTCPeerConnection &&) = delete; - explicit RTCPeerConnection(const Napi::CallbackInfo &); - - ~RTCPeerConnection() override; - - // - // PeerConnectionObserver implementation. - // - void OnSignalingChange( - webrtc::PeerConnectionInterface::SignalingState new_state) override; - void OnIceConnectionChange( - webrtc::PeerConnectionInterface::IceConnectionState new_state) override; - void OnIceGatheringChange( - webrtc::PeerConnectionInterface::IceGatheringState new_state) override; - void OnIceCandidate(const webrtc::IceCandidateInterface *candidate) override; - void OnIceCandidateError(const std::string &address, int port, - const std::string &url, int error_code, - const std::string &error_text) override; - void OnRenegotiationNeeded() override; - - void OnDataChannel( - rtc::scoped_refptr data_channel) override; - - void - OnAddStream(rtc::scoped_refptr stream) override; - void OnRemoveStream( - rtc::scoped_refptr stream) override; - - void - OnAddTrack(rtc::scoped_refptr receiver, - const std::vector> - &streams) override; - void OnTrack( - rtc::scoped_refptr transceiver) override; - - static void Init(Napi::Env, Napi::Object); - - static Napi::FunctionReference &constructor(); - - void SaveLastSdp(const RTCSessionDescriptionInit &lastSdp); - -private: - Napi::Value AddTrack(const Napi::CallbackInfo &); - Napi::Value AddTransceiver(const Napi::CallbackInfo &); - Napi::Value RemoveTrack(const Napi::CallbackInfo &); - Napi::Value CreateOffer(const Napi::CallbackInfo &); - Napi::Value CreateAnswer(const Napi::CallbackInfo &); - Napi::Value SetLocalDescription(const Napi::CallbackInfo &); - Napi::Value SetRemoteDescription(const Napi::CallbackInfo &); - Napi::Value UpdateIce(const Napi::CallbackInfo &); - Napi::Value AddIceCandidate(const Napi::CallbackInfo &); - Napi::Value CreateDataChannel(const Napi::CallbackInfo &); - /* - Napi::Value GetLocalStreams(const Napi::CallbackInfo&); - Napi::Value GetRemoteStreams(const Napi::CallbackInfo&); - Napi::Value GetStreamById(const Napi::CallbackInfo&); - Napi::Value AddStream(const Napi::CallbackInfo&); - Napi::Value RemoveStream(const Napi::CallbackInfo&); - */ - Napi::Value GetConfiguration(const Napi::CallbackInfo &); - Napi::Value SetConfiguration(const Napi::CallbackInfo &); - Napi::Value GetReceivers(const Napi::CallbackInfo &); - Napi::Value GetSenders(const Napi::CallbackInfo &); - Napi::Value GetStats(const Napi::CallbackInfo &); - Napi::Value GetTransceivers(const Napi::CallbackInfo &); - Napi::Value Close(const Napi::CallbackInfo &); - Napi::Value RestartIce(const Napi::CallbackInfo &); - - Napi::Value GetCanTrickleIceCandidates(const Napi::CallbackInfo &); - Napi::Value GetConnectionState(const Napi::CallbackInfo &); - Napi::Value GetCurrentLocalDescription(const Napi::CallbackInfo &); - Napi::Value GetLocalDescription(const Napi::CallbackInfo &); - Napi::Value GetPendingLocalDescription(const Napi::CallbackInfo &); - Napi::Value GetCurrentRemoteDescription(const Napi::CallbackInfo &); - Napi::Value GetRemoteDescription(const Napi::CallbackInfo &); - Napi::Value GetPendingRemoteDescription(const Napi::CallbackInfo &); - Napi::Value GetIceConnectionState(const Napi::CallbackInfo &); - Napi::Value GetSctp(const Napi::CallbackInfo &); - Napi::Value GetSignalingState(const Napi::CallbackInfo &); - Napi::Value GetIceGatheringState(const Napi::CallbackInfo &); - - RTCSessionDescriptionInit _lastSdp; - - UnsignedShortRange _port_range; - ExtendedRTCConfiguration _cached_configuration; - rtc::scoped_refptr _jinglePeerConnection; - - // TODO(jack): make this a RefPtr if we ever stop using the default global - // PeerConnectionFactory - PeerConnectionFactory *_factory; - bool _shouldReleaseFactory; - - std::vector _channels; - OwnedWrap _data_channel_wrap; - OwnedWrap _stream_wrap; - OwnedWrap _receiver_wrap; - OwnedWrap _transceiver_wrap; - OwnedWrap _sender_wrap; - OwnedWrap _transport_wrap; -}; - -} // namespace node_webrtc + +} // namespace webrtc + +namespace node_webrtc { + +class PeerConnectionFactory; + +class RTCPeerConnection : public AsyncObjectWrapWithLoop, + public webrtc::PeerConnectionObserver { +public: + RTCPeerConnection(const RTCPeerConnection &) = delete; + RTCPeerConnection(RTCPeerConnection &&) = delete; + RTCPeerConnection &operator=(const RTCPeerConnection &) = delete; + RTCPeerConnection &operator=(RTCPeerConnection &&) = delete; + explicit RTCPeerConnection(const Napi::CallbackInfo &); + + ~RTCPeerConnection() override; + + // + // PeerConnectionObserver implementation. + // + void OnSignalingChange( + webrtc::PeerConnectionInterface::SignalingState new_state) override; + void OnIceConnectionChange( + webrtc::PeerConnectionInterface::IceConnectionState new_state) override; + void OnIceGatheringChange( + webrtc::PeerConnectionInterface::IceGatheringState new_state) override; + void OnIceCandidate(const webrtc::IceCandidateInterface *candidate) override; + void OnIceCandidateError(const std::string &address, int port, + const std::string &url, int error_code, + const std::string &error_text) override; + void OnRenegotiationNeeded() override; + + void OnDataChannel( + rtc::scoped_refptr data_channel) override; + + void + OnAddStream(rtc::scoped_refptr stream) override; + void OnRemoveStream( + rtc::scoped_refptr stream) override; + + void + OnAddTrack(rtc::scoped_refptr receiver, + const std::vector> + &streams) override; + void OnTrack( + rtc::scoped_refptr transceiver) override; + + static void Init(Napi::Env, Napi::Object); + + static Napi::FunctionReference &constructor(); + + void SaveLastSdp(const RTCSessionDescriptionInit &lastSdp); + +private: + Napi::Value AddTrack(const Napi::CallbackInfo &); + Napi::Value AddTransceiver(const Napi::CallbackInfo &); + Napi::Value RemoveTrack(const Napi::CallbackInfo &); + Napi::Value CreateOffer(const Napi::CallbackInfo &); + Napi::Value CreateAnswer(const Napi::CallbackInfo &); + Napi::Value SetLocalDescription(const Napi::CallbackInfo &); + Napi::Value SetRemoteDescription(const Napi::CallbackInfo &); + Napi::Value UpdateIce(const Napi::CallbackInfo &); + Napi::Value AddIceCandidate(const Napi::CallbackInfo &); + Napi::Value CreateDataChannel(const Napi::CallbackInfo &); + /* + Napi::Value GetLocalStreams(const Napi::CallbackInfo&); + Napi::Value GetRemoteStreams(const Napi::CallbackInfo&); + Napi::Value GetStreamById(const Napi::CallbackInfo&); + Napi::Value AddStream(const Napi::CallbackInfo&); + Napi::Value RemoveStream(const Napi::CallbackInfo&); + */ + Napi::Value GetConfiguration(const Napi::CallbackInfo &); + Napi::Value SetConfiguration(const Napi::CallbackInfo &); + Napi::Value GetReceivers(const Napi::CallbackInfo &); + Napi::Value GetSenders(const Napi::CallbackInfo &); + Napi::Value GetStats(const Napi::CallbackInfo &); + Napi::Value GetTransceivers(const Napi::CallbackInfo &); + Napi::Value Close(const Napi::CallbackInfo &); + Napi::Value RestartIce(const Napi::CallbackInfo &); + + Napi::Value GetCanTrickleIceCandidates(const Napi::CallbackInfo &); + Napi::Value GetConnectionState(const Napi::CallbackInfo &); + Napi::Value GetCurrentLocalDescription(const Napi::CallbackInfo &); + Napi::Value GetLocalDescription(const Napi::CallbackInfo &); + Napi::Value GetPendingLocalDescription(const Napi::CallbackInfo &); + Napi::Value GetCurrentRemoteDescription(const Napi::CallbackInfo &); + Napi::Value GetRemoteDescription(const Napi::CallbackInfo &); + Napi::Value GetPendingRemoteDescription(const Napi::CallbackInfo &); + Napi::Value GetIceConnectionState(const Napi::CallbackInfo &); + Napi::Value GetSctp(const Napi::CallbackInfo &); + Napi::Value GetSignalingState(const Napi::CallbackInfo &); + Napi::Value GetIceGatheringState(const Napi::CallbackInfo &); + + RTCSessionDescriptionInit _lastSdp; + + UnsignedShortRange _port_range; + ExtendedRTCConfiguration _cached_configuration; + rtc::scoped_refptr _jinglePeerConnection; + + // TODO(jack): make this a RefPtr if we ever stop using the default global + // PeerConnectionFactory + PeerConnectionFactory *_factory; + bool _shouldReleaseFactory; + + std::vector _channels; + OwnedWrap _data_channel_wrap; + OwnedWrap _stream_wrap; + OwnedWrap _receiver_wrap; + OwnedWrap _transceiver_wrap; + OwnedWrap _sender_wrap; + OwnedWrap _transport_wrap; +}; + +} // namespace node_webrtc diff --git a/src/interfaces/rtc_peer_connection/peer_connection_factory.cc b/src/interfaces/rtc_peer_connection/peer_connection_factory.cc index e3b4c3046..f57fd8b6d 100644 --- a/src/interfaces/rtc_peer_connection/peer_connection_factory.cc +++ b/src/interfaces/rtc_peer_connection/peer_connection_factory.cc @@ -1,174 +1,164 @@ -/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license that can be found - * in the LICENSE.md file in the root of the source tree. All contributing - * project authors may be found in the AUTHORS file in the root of the source - * tree. - */ -#include "peer_connection_factory.hh" - +/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license that can be found + * in the LICENSE.md file in the root of the source tree. All contributing + * project authors may be found in the AUTHORS file in the root of the source + * tree. + */ +#include "peer_connection_factory.hh" + #include +#include #include #include #include #include -#include -#include #include #include -#include -#include +#include +#include #include #include #include #include #include "src/functional/maybe.hh" +#include "src/webrtc/packet_socket_factory_with_tls_cert_verifier.hh" #include "src/webrtc/test_audio_device_module.hh" namespace node_webrtc { - -Napi::FunctionReference &PeerConnectionFactory::constructor() { - static Napi::FunctionReference constructor; - return constructor; -} - -PeerConnectionFactory *PeerConnectionFactory::_default = nullptr; // NOLINT -std::mutex PeerConnectionFactory::_mutex{}; // NOLINT -int PeerConnectionFactory::_references = 0; // NOLINT - + +Napi::FunctionReference &PeerConnectionFactory::constructor() { + static Napi::FunctionReference constructor; + return constructor; +} + +PeerConnectionFactory *PeerConnectionFactory::_default = nullptr; // NOLINT +std::mutex PeerConnectionFactory::_mutex{}; // NOLINT +int PeerConnectionFactory::_references = 0; // NOLINT + PeerConnectionFactory::PeerConnectionFactory(const Napi::CallbackInfo &info) - : Napi::ObjectWrap(info) { + : Napi::ObjectWrap(info), + _env(webrtc::CreateEnvironment()) { auto env = info.Env(); - - if (!info.IsConstructCall()) { - Napi::TypeError::New( - env, "Use the new operator to construct a PeerConnectionFactory.") - .ThrowAsJavaScriptException(); - return; - } - - // TODO(mroberts): Read `audioLayer` from some PeerConnectionFactoryOptions? - auto audioLayer = MakeNothing(); - + + if (!info.IsConstructCall()) { + Napi::TypeError::New( + env, "Use the new operator to construct a PeerConnectionFactory.") + .ThrowAsJavaScriptException(); + return; + } + _workerThread = rtc::Thread::CreateWithSocketServer(); assert(_workerThread); - - bool result = - _workerThread->SetName("PeerConnectionFactory:workerThread", nullptr); - assert(result); - (void)result; - - result = _workerThread->Start(); - assert(result); - (void)result; - - _audioDeviceModule = _workerThread->BlockingCall([audioLayer]() { - return audioLayer - .Map([](auto audioLayer) { - // TODO(mroberts): I'm just trying to get this to compile - // right now. We need to call something like - // CreateDefaultTaskQueueFactory(). This code is currently - // unused, though. - return webrtc::AudioDeviceModule::Create(audioLayer, nullptr); - }) - .Or([]() { - return TestAudioDeviceModule::CreateTestAudioDeviceModule( - TestAudioDeviceModule::CreateZeroCapturer(48000, 1), - TestAudioDeviceModule::CreateDiscardRenderer(48000)); - }); + + bool result = + _workerThread->SetName("PeerConnectionFactory:workerThread", nullptr); + assert(result); + (void)result; + + result = _workerThread->Start(); + assert(result); + (void)result; + + _audioDeviceModule = _workerThread->BlockingCall([]() { + return TestAudioDeviceModule::CreateTestAudioDeviceModule( + TestAudioDeviceModule::CreateZeroCapturer(48000, 1), + TestAudioDeviceModule::CreateDiscardRenderer(48000)); }); - - _signalingThread = rtc::Thread::Create(); - assert(_signalingThread); - - result = _signalingThread->SetName("PeerConnectionFactory:signalingThread", - nullptr); - assert(result); - (void)result; - - result = _signalingThread->Start(); - assert(result); - (void)result; - + + _signalingThread = rtc::Thread::Create(); + assert(_signalingThread); + + result = _signalingThread->SetName("PeerConnectionFactory:signalingThread", + nullptr); + assert(result); + (void)result; + + result = _signalingThread->Start(); + assert(result); + (void)result; + _factory = webrtc::CreatePeerConnectionFactory( _workerThread.get(), _workerThread.get(), _signalingThread.get(), _audioDeviceModule, webrtc::CreateBuiltinAudioEncoderFactory(), - webrtc::CreateBuiltinAudioDecoderFactory(), - webrtc::CreateBuiltinVideoEncoderFactory(), - webrtc::CreateBuiltinVideoDecoderFactory(), nullptr, nullptr); - assert(_factory); - - webrtc::PeerConnectionFactoryInterface::Options options; - options.network_ignore_mask = 0; - _factory->SetOptions(options); + webrtc::CreateBuiltinAudioDecoderFactory(), nullptr, nullptr, nullptr, + nullptr); + assert(_factory); + + webrtc::PeerConnectionFactoryInterface::Options options; + options.network_ignore_mask = 0; + _factory->SetOptions(options); _networkManager = std::unique_ptr( - new rtc::BasicNetworkManager(_workerThread->socketserver())); + new rtc::BasicNetworkManager(_env, _workerThread->socketserver())); assert(_networkManager != nullptr); _socketFactory = std::unique_ptr( - new rtc::BasicPacketSocketFactory(_workerThread->socketserver())); + new PacketSocketFactoryWithTlsCertVerifier( + std::unique_ptr( + new rtc::BasicPacketSocketFactory( + _workerThread->socketserver())))); assert(_socketFactory != nullptr); } - -PeerConnectionFactory::~PeerConnectionFactory() { - _factory = nullptr; - - _workerThread->BlockingCall([this]() { this->_audioDeviceModule = nullptr; }); - - _workerThread->Stop(); - _signalingThread->Stop(); - - _workerThread = nullptr; - _signalingThread = nullptr; - - _networkManager = nullptr; - _socketFactory = nullptr; -} - -PeerConnectionFactory *PeerConnectionFactory::GetOrCreateDefault() { - _mutex.lock(); - _references++; - if (_references == 1) { - assert(_default == nullptr); - auto env = constructor().Env(); - Napi::HandleScope scope(env); - auto object = constructor().New({}); - auto factory = Unwrap(object); - _default = factory; - _default->Ref(); - } - _mutex.unlock(); - return _default; -} - -void PeerConnectionFactory::Release() { - _mutex.lock(); - _references--; - assert(_references >= 0); - if (!_references) { - assert(_default != nullptr); - _default->Unref(); - _default = nullptr; - } - _mutex.unlock(); -} - -void PeerConnectionFactory::Dispose() { rtc::CleanupSSL(); } - -void PeerConnectionFactory::Init(Napi::Env env, Napi::Object exports) { - bool result = rtc::InitializeSSL(); - assert(result); - (void)result; - - auto func = DefineClass(env, "RTCPeerConnectionFactory", {}); - - constructor() = Napi::Persistent(func); - constructor().SuppressDestruct(); - - exports.Set("RTCPeerConnectionFactory", func); -} - -} // namespace node_webrtc + +PeerConnectionFactory::~PeerConnectionFactory() { + _factory = nullptr; + + _workerThread->BlockingCall([this]() { this->_audioDeviceModule = nullptr; }); + + _workerThread->Stop(); + _signalingThread->Stop(); + + _workerThread = nullptr; + _signalingThread = nullptr; + + _networkManager = nullptr; + _socketFactory = nullptr; +} + +PeerConnectionFactory *PeerConnectionFactory::GetOrCreateDefault() { + _mutex.lock(); + _references++; + if (_references == 1) { + assert(_default == nullptr); + auto env = constructor().Env(); + Napi::HandleScope scope(env); + auto object = constructor().New({}); + auto factory = Unwrap(object); + _default = factory; + _default->Ref(); + } + _mutex.unlock(); + return _default; +} + +void PeerConnectionFactory::Release() { + _mutex.lock(); + _references--; + assert(_references >= 0); + if (!_references) { + assert(_default != nullptr); + _default->Unref(); + _default = nullptr; + } + _mutex.unlock(); +} + +void PeerConnectionFactory::Dispose() { rtc::CleanupSSL(); } + +void PeerConnectionFactory::Init(Napi::Env env, Napi::Object exports) { + bool result = rtc::InitializeSSL(); + assert(result); + (void)result; + + auto func = DefineClass(env, "RTCPeerConnectionFactory", {}); + + constructor() = Napi::Persistent(func); + constructor().SuppressDestruct(); + + exports.Set("RTCPeerConnectionFactory", func); +} + +} // namespace node_webrtc diff --git a/src/interfaces/rtc_peer_connection/peer_connection_factory.hh b/src/interfaces/rtc_peer_connection/peer_connection_factory.hh index bb6257007..c471efc2d 100644 --- a/src/interfaces/rtc_peer_connection/peer_connection_factory.hh +++ b/src/interfaces/rtc_peer_connection/peer_connection_factory.hh @@ -1,79 +1,83 @@ -/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license that can be found - * in the LICENSE.md file in the root of the source tree. All contributing - * project authors may be found in the AUTHORS file in the root of the source - * tree. - */ -#pragma once - +/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license that can be found + * in the LICENSE.md file in the root of the source tree. All contributing + * project authors may be found in the AUTHORS file in the root of the source + * tree. + */ +#pragma once + #include #include #include +#include #include #include #include #include - -namespace node_webrtc { - -class PeerConnectionFactory : public Napi::ObjectWrap { -public: - PeerConnectionFactory(const PeerConnectionFactory &) = delete; - PeerConnectionFactory(PeerConnectionFactory &&) = delete; - PeerConnectionFactory &operator=(const PeerConnectionFactory &) = delete; - PeerConnectionFactory &operator=(PeerConnectionFactory &&) = delete; - explicit PeerConnectionFactory(const Napi::CallbackInfo &); - - ~PeerConnectionFactory() override; - - /** - * Get or create the default PeerConnectionFactory. The default uses - * webrtc::AudioDeviceModule::AudioLayer::kDummyAudio. Call {@link Release} - * when done. - */ - static PeerConnectionFactory *GetOrCreateDefault(); - - /** - * Release a reference to the default PeerConnectionFactory. - */ - static void Release(); - - /** - * Get the underlying webrtc::PeerConnectionFactoryInterface. - */ + +namespace node_webrtc { + +class PeerConnectionFactory : public Napi::ObjectWrap { +public: + PeerConnectionFactory(const PeerConnectionFactory &) = delete; + PeerConnectionFactory(PeerConnectionFactory &&) = delete; + PeerConnectionFactory &operator=(const PeerConnectionFactory &) = delete; + PeerConnectionFactory &operator=(PeerConnectionFactory &&) = delete; + explicit PeerConnectionFactory(const Napi::CallbackInfo &); + + ~PeerConnectionFactory() override; + + /** + * Get or create the default PeerConnectionFactory. The default uses + * webrtc::AudioDeviceModule::AudioLayer::kDummyAudio. Call {@link Release} + * when done. + */ + static PeerConnectionFactory *GetOrCreateDefault(); + + /** + * Release a reference to the default PeerConnectionFactory. + */ + static void Release(); + + /** + * Get the underlying webrtc::PeerConnectionFactoryInterface. + */ rtc::scoped_refptr factory() { return _factory; } - std::unique_ptr &SignalingThread() { return _signalingThread; } - - std::unique_ptr &WorkerThread() { return _workerThread; } - - rtc::NetworkManager *getNetworkManager() { return _networkManager.get(); } - - rtc::PacketSocketFactory *getSocketFactory() { return _socketFactory.get(); } - - static void Init(Napi::Env, Napi::Object); - - static Napi::FunctionReference &constructor(); - - static void Dispose(); + const webrtc::Environment &env() const { return _env; } + std::unique_ptr &SignalingThread() { return _signalingThread; } + + std::unique_ptr &WorkerThread() { return _workerThread; } + + rtc::NetworkManager *getNetworkManager() { return _networkManager.get(); } + + rtc::PacketSocketFactory *getSocketFactory() { return _socketFactory.get(); } + + static void Init(Napi::Env, Napi::Object); + + static Napi::FunctionReference &constructor(); + + static void Dispose(); + private: + webrtc::Environment _env; std::unique_ptr _signalingThread; std::unique_ptr _workerThread; - - static PeerConnectionFactory *_default; // NOLINT - static std::mutex _mutex; // NOLINT - static int _references; // NOLINT - - rtc::scoped_refptr _factory; - rtc::scoped_refptr _audioDeviceModule; - - std::unique_ptr _networkManager; - std::unique_ptr _socketFactory; -}; - -} // namespace node_webrtc + + static PeerConnectionFactory *_default; // NOLINT + static std::mutex _mutex; // NOLINT + static int _references; // NOLINT + + rtc::scoped_refptr _factory; + rtc::scoped_refptr _audioDeviceModule; + + std::unique_ptr _networkManager; + std::unique_ptr _socketFactory; +}; + +} // namespace node_webrtc diff --git a/src/interfaces/rtc_rtp_receiver.cc b/src/interfaces/rtc_rtp_receiver.cc index 32d6578ce..8306d8c53 100644 --- a/src/interfaces/rtc_rtp_receiver.cc +++ b/src/interfaces/rtc_rtp_receiver.cc @@ -1,202 +1,202 @@ -/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license that can be found - * in the LICENSE.md file in the root of the source tree. All contributing - * project authors may be found in the AUTHORS file in the root of the source - * tree. - */ -#include "src/interfaces/rtc_rtp_receiver.hh" - -#include -#include - -#include "src/converters.hh" -#include "src/converters/arguments.hh" -#include "src/converters/interfaces.hh" -#include "src/converters/napi.hh" -#include "src/dictionaries/webrtc/rtp_capabilities.hh" -#include "src/dictionaries/webrtc/rtp_parameters.hh" -#include "src/dictionaries/webrtc/rtp_source.hh" -#include "src/enums/webrtc/media_type.hh" -#include "src/interfaces/media_stream_track.hh" -#include "src/interfaces/rtc_dtls_transport.hh" -#include "src/interfaces/rtc_peer_connection/peer_connection_factory.hh" -#include "src/node/utility.hh" - -namespace node_webrtc { - -Napi::FunctionReference &RTCRtpReceiver::constructor() { - static Napi::FunctionReference constructor; - return constructor; -} - -RTCRtpReceiver::RTCRtpReceiver(const Napi::CallbackInfo &info) - : AsyncObjectWrap("RTCRtpReceiver", info) { - if (info.Length() != 2 || !info[0].IsObject() || !info[1].IsExternal()) { - Napi::TypeError::New(info.Env(), "You cannot construct a RTCRtpReceiver") - .ThrowAsJavaScriptException(); - return; - } - - // FIXME(mroberts): There is a safer conversion here. - _factory = PeerConnectionFactory::Unwrap(info[0].ToObject()); - auto receiver = *info[1] - .As>>() - .Data(); - - _receiver = std::move(receiver); -} - -RTCRtpReceiver::~RTCRtpReceiver() { - Napi::HandleScope scope(PeerConnectionFactory::constructor().Env()); - - wrap()->Release(this); -} - -Napi::Value RTCRtpReceiver::GetTrack(const Napi::CallbackInfo &) { - return _track_wrap.GetOrCreate(_factory, _receiver->track())->Value(); -} - -Napi::Value RTCRtpReceiver::GetTransport(const Napi::CallbackInfo &info) { - auto transport = _receiver->dtls_transport(); - return transport ? _transport_wrap.GetOrCreate(_factory, transport)->Value() - : info.Env().Null(); -} - -Napi::Value RTCRtpReceiver::GetRtcpTransport(const Napi::CallbackInfo &info) { - return info.Env().Null(); -} - -Napi::Value RTCRtpReceiver::GetCapabilities(const Napi::CallbackInfo &info) { +/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license that can be found + * in the LICENSE.md file in the root of the source tree. All contributing + * project authors may be found in the AUTHORS file in the root of the source + * tree. + */ +#include "src/interfaces/rtc_rtp_receiver.hh" + +#include +#include + +#include "src/converters.hh" +#include "src/converters/arguments.hh" +#include "src/converters/interfaces.hh" +#include "src/converters/napi.hh" +#include "src/dictionaries/webrtc/rtp_capabilities.hh" +#include "src/dictionaries/webrtc/rtp_parameters.hh" +#include "src/dictionaries/webrtc/rtp_source.hh" +#include "src/enums/webrtc/media_type.hh" +#include "src/interfaces/media_stream_track.hh" +#include "src/interfaces/rtc_dtls_transport.hh" +#include "src/interfaces/rtc_peer_connection/peer_connection_factory.hh" +#include "src/node/utility.hh" + +namespace node_webrtc { + +Napi::FunctionReference &RTCRtpReceiver::constructor() { + static Napi::FunctionReference constructor; + return constructor; +} + +RTCRtpReceiver::RTCRtpReceiver(const Napi::CallbackInfo &info) + : AsyncObjectWrap("RTCRtpReceiver", info) { + if (info.Length() != 2 || !info[0].IsObject() || !info[1].IsExternal()) { + Napi::TypeError::New(info.Env(), "You cannot construct a RTCRtpReceiver") + .ThrowAsJavaScriptException(); + return; + } + + // FIXME(mroberts): There is a safer conversion here. + _factory = PeerConnectionFactory::Unwrap(info[0].ToObject()); + auto receiver = *info[1] + .As>>() + .Data(); + + _receiver = std::move(receiver); +} + +RTCRtpReceiver::~RTCRtpReceiver() { + Napi::HandleScope scope(PeerConnectionFactory::constructor().Env()); + + wrap()->Release(this); +} + +Napi::Value RTCRtpReceiver::GetTrack(const Napi::CallbackInfo &) { + return _track_wrap.GetOrCreate(_factory, _receiver->track())->Value(); +} + +Napi::Value RTCRtpReceiver::GetTransport(const Napi::CallbackInfo &info) { + auto transport = _receiver->dtls_transport(); + return transport ? _transport_wrap.GetOrCreate(_factory, transport)->Value() + : info.Env().Null(); +} + +Napi::Value RTCRtpReceiver::GetRtcpTransport(const Napi::CallbackInfo &info) { + return info.Env().Null(); +} + +Napi::Value RTCRtpReceiver::GetCapabilities(const Napi::CallbackInfo &info) { CONVERT_ARGS_OR_THROW_AND_RETURN_NAPI(info, kindString, std::string) if (kindString == "audio" || kindString == "video") { auto factory = PeerConnectionFactory::GetOrCreateDefault(); - auto kind = kindString == "audio" ? cricket::MEDIA_TYPE_AUDIO - : cricket::MEDIA_TYPE_VIDEO; + auto kind = kindString == "audio" ? webrtc::MediaType::AUDIO + : webrtc::MediaType::VIDEO; auto capabilities = factory->factory()->GetRtpReceiverCapabilities(kind); PeerConnectionFactory::Release(); CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), capabilities, result, Napi::Value) - return result; - } - return info.Env().Null(); -} - -Napi::Value RTCRtpReceiver::GetParameters(const Napi::CallbackInfo &info) { - auto parameters = _receiver->GetParameters(); - CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), parameters, result, Napi::Value) - return result; -} - -Napi::Value -RTCRtpReceiver::GetContributingSources(const Napi::CallbackInfo &info) { - auto contributingSources = std::vector(); - auto sources = _receiver->GetSources(); - for (const auto &source : sources) { - if (source.source_type() == webrtc::RtpSourceType::CSRC) { - contributingSources.push_back(source); - } - } - CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), contributingSources, result, - Napi::Value) - return result; -} - -Napi::Value -RTCRtpReceiver::GetSynchronizationSources(const Napi::CallbackInfo &info) { - auto synchronizationSources = std::vector(); - auto sources = _receiver->GetSources(); - for (const auto &source : sources) { - if (source.source_type() == webrtc::RtpSourceType::SSRC) { - synchronizationSources.push_back(source); - } - } - CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), synchronizationSources, result, - Napi::Value) - return result; -} - -Napi::Value RTCRtpReceiver::GetStats(const Napi::CallbackInfo &info) { - CREATE_DEFERRED(info.Env(), deferred) - Reject( - deferred, - Napi::Error::New( - info.Env(), - "Not yet implemented; file a feature request against node-webrtc")); - return deferred.Promise(); -} - -Wrap, - PeerConnectionFactory *> * -RTCRtpReceiver::wrap() { - static auto wrap = - new node_webrtc::Wrap, - PeerConnectionFactory *>(RTCRtpReceiver::Create); - return wrap; -} - -RTCRtpReceiver *RTCRtpReceiver::Create( - PeerConnectionFactory *factory, - rtc::scoped_refptr receiver) { - auto env = constructor().Env(); - Napi::HandleScope scope(env); - - auto object = constructor().New( - {factory->Value(), - Napi::External>::New( - env, &receiver)}); - - auto unwrapped = Unwrap(object); - return unwrapped; -} - -void RTCRtpReceiver::Init(Napi::Env env, Napi::Object exports) { - auto func = DefineClass( - env, "RTCRtpReceiver", - {InstanceAccessor("track", &RTCRtpReceiver::GetTrack, nullptr), - InstanceAccessor("transport", &RTCRtpReceiver::GetTransport, nullptr), - InstanceAccessor("rtcpTransport", &RTCRtpReceiver::GetRtcpTransport, - nullptr), - InstanceMethod("getParameters", &RTCRtpReceiver::GetParameters), - InstanceMethod("getContributingSources", - &RTCRtpReceiver::GetContributingSources), - InstanceMethod("getSynchronizationSources", - &RTCRtpReceiver::GetSynchronizationSources), - InstanceMethod("getStats", &RTCRtpReceiver::GetStats), - StaticMethod("getCapabilities", &RTCRtpReceiver::GetCapabilities)}); - - constructor() = Napi::Persistent(func); - constructor().SuppressDestruct(); - - exports.Set("RTCRtpReceiver", func); -} - -FROM_NAPI_IMPL(RTCRtpReceiver *, value) { - return From(value).FlatMap( - [](Napi::Object object) { - auto isRTCRtpReceiver = false; - napi_instanceof(object.Env(), object, - RTCRtpReceiver::constructor().Value(), - &isRTCRtpReceiver); - - if (object.Env().IsExceptionPending()) { - return Validation::Invalid( - object.Env().GetAndClearPendingException().Message()); - } - if (!isRTCRtpReceiver) { - return Validation::Invalid( - "This is not an instance of RTCRtpReceiver"); - } - - return Pure(RTCRtpReceiver::Unwrap(object)); - }); -} - -TO_NAPI_IMPL(RTCRtpReceiver *, pair) { - auto v = pair.second->Value().As(); - return Pure(v); -} - -} // namespace node_webrtc + return result; + } + return info.Env().Null(); +} + +Napi::Value RTCRtpReceiver::GetParameters(const Napi::CallbackInfo &info) { + auto parameters = _receiver->GetParameters(); + CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), parameters, result, Napi::Value) + return result; +} + +Napi::Value +RTCRtpReceiver::GetContributingSources(const Napi::CallbackInfo &info) { + auto contributingSources = std::vector(); + auto sources = _receiver->GetSources(); + for (const auto &source : sources) { + if (source.source_type() == webrtc::RtpSourceType::CSRC) { + contributingSources.push_back(source); + } + } + CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), contributingSources, result, + Napi::Value) + return result; +} + +Napi::Value +RTCRtpReceiver::GetSynchronizationSources(const Napi::CallbackInfo &info) { + auto synchronizationSources = std::vector(); + auto sources = _receiver->GetSources(); + for (const auto &source : sources) { + if (source.source_type() == webrtc::RtpSourceType::SSRC) { + synchronizationSources.push_back(source); + } + } + CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), synchronizationSources, result, + Napi::Value) + return result; +} + +Napi::Value RTCRtpReceiver::GetStats(const Napi::CallbackInfo &info) { + CREATE_DEFERRED(info.Env(), deferred) + Reject( + deferred, + Napi::Error::New( + info.Env(), + "Not yet implemented; file a feature request against node-webrtc")); + return deferred.Promise(); +} + +Wrap, + PeerConnectionFactory *> * +RTCRtpReceiver::wrap() { + static auto wrap = + new node_webrtc::Wrap, + PeerConnectionFactory *>(RTCRtpReceiver::Create); + return wrap; +} + +RTCRtpReceiver *RTCRtpReceiver::Create( + PeerConnectionFactory *factory, + rtc::scoped_refptr receiver) { + auto env = constructor().Env(); + Napi::HandleScope scope(env); + + auto object = constructor().New( + {factory->Value(), + Napi::External>::New( + env, &receiver)}); + + auto unwrapped = Unwrap(object); + return unwrapped; +} + +void RTCRtpReceiver::Init(Napi::Env env, Napi::Object exports) { + auto func = DefineClass( + env, "RTCRtpReceiver", + {InstanceAccessor("track", &RTCRtpReceiver::GetTrack, nullptr), + InstanceAccessor("transport", &RTCRtpReceiver::GetTransport, nullptr), + InstanceAccessor("rtcpTransport", &RTCRtpReceiver::GetRtcpTransport, + nullptr), + InstanceMethod("getParameters", &RTCRtpReceiver::GetParameters), + InstanceMethod("getContributingSources", + &RTCRtpReceiver::GetContributingSources), + InstanceMethod("getSynchronizationSources", + &RTCRtpReceiver::GetSynchronizationSources), + InstanceMethod("getStats", &RTCRtpReceiver::GetStats), + StaticMethod("getCapabilities", &RTCRtpReceiver::GetCapabilities)}); + + constructor() = Napi::Persistent(func); + constructor().SuppressDestruct(); + + exports.Set("RTCRtpReceiver", func); +} + +FROM_NAPI_IMPL(RTCRtpReceiver *, value) { + return From(value).FlatMap( + [](Napi::Object object) { + auto isRTCRtpReceiver = false; + napi_instanceof(object.Env(), object, + RTCRtpReceiver::constructor().Value(), + &isRTCRtpReceiver); + + if (object.Env().IsExceptionPending()) { + return Validation::Invalid( + object.Env().GetAndClearPendingException().Message()); + } + if (!isRTCRtpReceiver) { + return Validation::Invalid( + "This is not an instance of RTCRtpReceiver"); + } + + return Pure(RTCRtpReceiver::Unwrap(object)); + }); +} + +TO_NAPI_IMPL(RTCRtpReceiver *, pair) { + auto v = pair.second->Value().As(); + return Pure(v); +} + +} // namespace node_webrtc diff --git a/src/interfaces/rtc_rtp_sender.cc b/src/interfaces/rtc_rtp_sender.cc index e9e1f47b5..cd292d5f3 100644 --- a/src/interfaces/rtc_rtp_sender.cc +++ b/src/interfaces/rtc_rtp_sender.cc @@ -1,233 +1,233 @@ -/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license that can be found - * in the LICENSE.md file in the root of the source tree. All contributing - * project authors may be found in the AUTHORS file in the root of the source - * tree. - */ -#include "src/interfaces/rtc_rtp_sender.hh" - -#include - -#include "src/converters.hh" -#include "src/converters/arguments.hh" -#include "src/converters/interfaces.hh" -#include "src/converters/null.hh" -#include "src/dictionaries/webrtc/rtc_error.hh" -#include "src/dictionaries/webrtc/rtp_capabilities.hh" -#include "src/dictionaries/webrtc/rtp_parameters.hh" -#include "src/enums/webrtc/media_type.hh" -#include "src/interfaces/media_stream.hh" -#include "src/interfaces/media_stream_track.hh" -#include "src/interfaces/rtc_dtls_transport.hh" -#include "src/interfaces/rtc_peer_connection/peer_connection_factory.hh" -#include "src/node/error_factory.hh" -#include "src/node/utility.hh" - -namespace node_webrtc { - -Napi::FunctionReference &RTCRtpSender::constructor() { - static Napi::FunctionReference constructor; - return constructor; -} - -RTCRtpSender::RTCRtpSender(const Napi::CallbackInfo &info) - : AsyncObjectWrap("RTCRtpSender", info) { - if (info.Length() != 2 || !info[0].IsObject() || !info[1].IsExternal()) { - Napi::TypeError::New(info.Env(), "You cannot construct a RTCRtpSender") - .ThrowAsJavaScriptException(); - return; - } - - _factory = PeerConnectionFactory::Unwrap(info[0].ToObject()); - auto sender = - *info[1] - .As>>() - .Data(); - - _sender = std::move(sender); -} - -RTCRtpSender::~RTCRtpSender() { - Napi::HandleScope scope(PeerConnectionFactory::constructor().Env()); - - wrap()->Release(this); -} - -Napi::Value RTCRtpSender::GetTrack(const Napi::CallbackInfo &info) { - Napi::Value result = info.Env().Null(); - auto track = _sender->track(); - if (track) { - result = _track_wrap.GetOrCreate(_factory, track)->Value(); - } - return result; -} - -Napi::Value RTCRtpSender::GetTransport(const Napi::CallbackInfo &info) { - auto transport = _sender->dtls_transport(); - return transport ? _transport_wrap.GetOrCreate(_factory, transport)->Value() - : info.Env().Null(); -} - -Napi::Value RTCRtpSender::GetRtcpTransport(const Napi::CallbackInfo &info) { - return info.Env().Null(); -} - -Napi::Value RTCRtpSender::GetCapabilities(const Napi::CallbackInfo &info) { +/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license that can be found + * in the LICENSE.md file in the root of the source tree. All contributing + * project authors may be found in the AUTHORS file in the root of the source + * tree. + */ +#include "src/interfaces/rtc_rtp_sender.hh" + +#include + +#include "src/converters.hh" +#include "src/converters/arguments.hh" +#include "src/converters/interfaces.hh" +#include "src/converters/null.hh" +#include "src/dictionaries/webrtc/rtc_error.hh" +#include "src/dictionaries/webrtc/rtp_capabilities.hh" +#include "src/dictionaries/webrtc/rtp_parameters.hh" +#include "src/enums/webrtc/media_type.hh" +#include "src/interfaces/media_stream.hh" +#include "src/interfaces/media_stream_track.hh" +#include "src/interfaces/rtc_dtls_transport.hh" +#include "src/interfaces/rtc_peer_connection/peer_connection_factory.hh" +#include "src/node/error_factory.hh" +#include "src/node/utility.hh" + +namespace node_webrtc { + +Napi::FunctionReference &RTCRtpSender::constructor() { + static Napi::FunctionReference constructor; + return constructor; +} + +RTCRtpSender::RTCRtpSender(const Napi::CallbackInfo &info) + : AsyncObjectWrap("RTCRtpSender", info) { + if (info.Length() != 2 || !info[0].IsObject() || !info[1].IsExternal()) { + Napi::TypeError::New(info.Env(), "You cannot construct a RTCRtpSender") + .ThrowAsJavaScriptException(); + return; + } + + _factory = PeerConnectionFactory::Unwrap(info[0].ToObject()); + auto sender = + *info[1] + .As>>() + .Data(); + + _sender = std::move(sender); +} + +RTCRtpSender::~RTCRtpSender() { + Napi::HandleScope scope(PeerConnectionFactory::constructor().Env()); + + wrap()->Release(this); +} + +Napi::Value RTCRtpSender::GetTrack(const Napi::CallbackInfo &info) { + Napi::Value result = info.Env().Null(); + auto track = _sender->track(); + if (track) { + result = _track_wrap.GetOrCreate(_factory, track)->Value(); + } + return result; +} + +Napi::Value RTCRtpSender::GetTransport(const Napi::CallbackInfo &info) { + auto transport = _sender->dtls_transport(); + return transport ? _transport_wrap.GetOrCreate(_factory, transport)->Value() + : info.Env().Null(); +} + +Napi::Value RTCRtpSender::GetRtcpTransport(const Napi::CallbackInfo &info) { + return info.Env().Null(); +} + +Napi::Value RTCRtpSender::GetCapabilities(const Napi::CallbackInfo &info) { CONVERT_ARGS_OR_THROW_AND_RETURN_NAPI(info, kindString, std::string) if (kindString == "audio" || kindString == "video") { auto factory = PeerConnectionFactory::GetOrCreateDefault(); - auto kind = kindString == "audio" ? cricket::MEDIA_TYPE_AUDIO - : cricket::MEDIA_TYPE_VIDEO; + auto kind = kindString == "audio" ? webrtc::MediaType::AUDIO + : webrtc::MediaType::VIDEO; auto capabilities = factory->factory()->GetRtpSenderCapabilities(kind); PeerConnectionFactory::Release(); - CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), capabilities, result, - Napi::Value) - return result; - } - return info.Env().Null(); -} - -Napi::Value RTCRtpSender::GetParameters(const Napi::CallbackInfo &info) { - auto parameters = _sender->GetParameters(); - CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), parameters, result, Napi::Value) - return result; -} - -Napi::Value RTCRtpSender::SetParameters(const Napi::CallbackInfo &info) { - CREATE_DEFERRED(info.Env(), deffered) - CONVERT_ARGS_OR_REJECT_AND_RETURN_NAPI(deferred, info, parameters, - webrtc::RtpParameters) - auto error = _sender->SetParameters(parameters); - if (error.ok()) { - deferred.Resolve(info.Env().Undefined()); - } else { - CONVERT_OR_REJECT_AND_RETURN_NAPI(deferred, &error, reason, Napi::Value) - deferred.Reject(reason); - } - return deferred.Promise(); -} - -Napi::Value RTCRtpSender::GetStats(const Napi::CallbackInfo &info) { - CREATE_DEFERRED(info.Env(), deffered) - Reject( - deferred, - Napi::Error::New( - info.Env(), - "Not yet implemented; file a feature request against node-webrtc")); - return deferred.Promise(); -} - -Napi::Value RTCRtpSender::ReplaceTrack(const Napi::CallbackInfo &info) { - CREATE_DEFERRED(info.Env(), deferred) - CONVERT_ARGS_OR_REJECT_AND_RETURN_NAPI(deferred, info, maybeTrack, - Either) - auto mediaStreamTrack = maybeTrack.FromEither( - [](auto) { return nullptr; }, [](auto track) { return track; }); + CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), capabilities, result, + Napi::Value) + return result; + } + return info.Env().Null(); +} + +Napi::Value RTCRtpSender::GetParameters(const Napi::CallbackInfo &info) { + auto parameters = _sender->GetParameters(); + CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), parameters, result, Napi::Value) + return result; +} + +Napi::Value RTCRtpSender::SetParameters(const Napi::CallbackInfo &info) { + CREATE_DEFERRED(info.Env(), deffered) + CONVERT_ARGS_OR_REJECT_AND_RETURN_NAPI(deferred, info, parameters, + webrtc::RtpParameters) + auto error = _sender->SetParameters(parameters); + if (error.ok()) { + deferred.Resolve(info.Env().Undefined()); + } else { + CONVERT_OR_REJECT_AND_RETURN_NAPI(deferred, &error, reason, Napi::Value) + deferred.Reject(reason); + } + return deferred.Promise(); +} + +Napi::Value RTCRtpSender::GetStats(const Napi::CallbackInfo &info) { + CREATE_DEFERRED(info.Env(), deffered) + Reject( + deferred, + Napi::Error::New( + info.Env(), + "Not yet implemented; file a feature request against node-webrtc")); + return deferred.Promise(); +} + +Napi::Value RTCRtpSender::ReplaceTrack(const Napi::CallbackInfo &info) { + CREATE_DEFERRED(info.Env(), deferred) + CONVERT_ARGS_OR_REJECT_AND_RETURN_NAPI(deferred, info, maybeTrack, + Either) + auto mediaStreamTrack = maybeTrack.FromEither( + [](auto) { return nullptr; }, [](auto track) { return track; }); auto track = mediaStreamTrack ? mediaStreamTrack->track().get() : nullptr; if (track) { auto expectedMediaType = track->kind() == webrtc::MediaStreamTrackInterface::kAudioKind - ? cricket::MediaType::MEDIA_TYPE_AUDIO - : cricket::MediaType::MEDIA_TYPE_VIDEO; + ? webrtc::MediaType::AUDIO + : webrtc::MediaType::VIDEO; if (_sender->media_type() != expectedMediaType) { Reject(deferred, Napi::TypeError::New(info.Env(), "Kind does not match") - .Value() - .As()); - return deferred.Promise(); - } - } - _sender->SetTrack(track) - ? Resolve(deferred, info.Env().Undefined()) - : Reject(deferred, ErrorFactory::CreateInvalidStateError( - info.Env(), "Failed to replaceTrack")); - return deferred.Promise(); -} - -Napi::Value RTCRtpSender::SetStreams(const Napi::CallbackInfo &info) { - auto streams = std::vector(); - for (size_t i = 0; i < info.Length(); i++) { - auto value = info[i]; - auto maybeStream = From(value); - if (maybeStream.IsInvalid()) { - auto error = maybeStream.ToErrors()[0]; - Napi::TypeError::New(info.Env(), error).ThrowAsJavaScriptException(); - return info.Env().Undefined(); - } - auto stream = maybeStream.UnsafeFromValid(); - streams.emplace_back(stream->stream()->id()); - } - _sender->SetStreams(streams); - return info.Env().Undefined(); -} - -Wrap, - PeerConnectionFactory *> * -RTCRtpSender::wrap() { - static auto wrap = - new node_webrtc::Wrap, - PeerConnectionFactory *>(RTCRtpSender::Create); - return wrap; -} - -RTCRtpSender * -RTCRtpSender::Create(PeerConnectionFactory *factory, - rtc::scoped_refptr sender) { - auto env = constructor().Env(); - Napi::HandleScope scope(env); - - auto object = constructor().New( - {factory->Value(), - Napi::External>::New( - env, &sender)}); - - auto unwrapped = Unwrap(object); - return unwrapped; -} - -void RTCRtpSender::Init(Napi::Env env, Napi::Object exports) { - Napi::Function func = DefineClass( - env, "RTCRtpSender", - {InstanceAccessor("track", &RTCRtpSender::GetTrack, nullptr), - InstanceAccessor("transport", &RTCRtpSender::GetTransport, nullptr), - InstanceAccessor("rtcpTransport", &RTCRtpSender::GetRtcpTransport, - nullptr), - InstanceMethod("getParameters", &RTCRtpSender::GetParameters), - InstanceMethod("setParameters", &RTCRtpSender::SetParameters), - InstanceMethod("getStats", &RTCRtpSender::GetStats), - InstanceMethod("replaceTrack", &RTCRtpSender::ReplaceTrack), - InstanceMethod("setStreams", &RTCRtpSender::SetStreams), - StaticMethod("getCapabilities", &RTCRtpSender::GetCapabilities)}); - - constructor() = Napi::Persistent(func); - constructor().SuppressDestruct(); - - exports.Set("RTCRtpSender", func); -} - -FROM_NAPI_IMPL(RTCRtpSender *, value) { - return From(value).FlatMap( - [](Napi::Object object) { - auto isRTCRtpSender = false; - napi_instanceof(object.Env(), object, - RTCRtpSender::constructor().Value(), &isRTCRtpSender); - - if (object.Env().IsExceptionPending()) { - return Validation::Invalid( - object.Env().GetAndClearPendingException().Message()); - } - if (!isRTCRtpSender) { - return Validation::Invalid( - "This is not an instance of RTCRtpSender"); - } - - return Pure(RTCRtpSender::Unwrap(object)); - }); -} - -TO_NAPI_IMPL(RTCRtpSender *, pair) { - return Pure(pair.second->Value().As()); -} - -} // namespace node_webrtc + .Value() + .As()); + return deferred.Promise(); + } + } + _sender->SetTrack(track) + ? Resolve(deferred, info.Env().Undefined()) + : Reject(deferred, ErrorFactory::CreateInvalidStateError( + info.Env(), "Failed to replaceTrack")); + return deferred.Promise(); +} + +Napi::Value RTCRtpSender::SetStreams(const Napi::CallbackInfo &info) { + auto streams = std::vector(); + for (size_t i = 0; i < info.Length(); i++) { + auto value = info[i]; + auto maybeStream = From(value); + if (maybeStream.IsInvalid()) { + auto error = maybeStream.ToErrors()[0]; + Napi::TypeError::New(info.Env(), error).ThrowAsJavaScriptException(); + return info.Env().Undefined(); + } + auto stream = maybeStream.UnsafeFromValid(); + streams.emplace_back(stream->stream()->id()); + } + _sender->SetStreams(streams); + return info.Env().Undefined(); +} + +Wrap, + PeerConnectionFactory *> * +RTCRtpSender::wrap() { + static auto wrap = + new node_webrtc::Wrap, + PeerConnectionFactory *>(RTCRtpSender::Create); + return wrap; +} + +RTCRtpSender * +RTCRtpSender::Create(PeerConnectionFactory *factory, + rtc::scoped_refptr sender) { + auto env = constructor().Env(); + Napi::HandleScope scope(env); + + auto object = constructor().New( + {factory->Value(), + Napi::External>::New( + env, &sender)}); + + auto unwrapped = Unwrap(object); + return unwrapped; +} + +void RTCRtpSender::Init(Napi::Env env, Napi::Object exports) { + Napi::Function func = DefineClass( + env, "RTCRtpSender", + {InstanceAccessor("track", &RTCRtpSender::GetTrack, nullptr), + InstanceAccessor("transport", &RTCRtpSender::GetTransport, nullptr), + InstanceAccessor("rtcpTransport", &RTCRtpSender::GetRtcpTransport, + nullptr), + InstanceMethod("getParameters", &RTCRtpSender::GetParameters), + InstanceMethod("setParameters", &RTCRtpSender::SetParameters), + InstanceMethod("getStats", &RTCRtpSender::GetStats), + InstanceMethod("replaceTrack", &RTCRtpSender::ReplaceTrack), + InstanceMethod("setStreams", &RTCRtpSender::SetStreams), + StaticMethod("getCapabilities", &RTCRtpSender::GetCapabilities)}); + + constructor() = Napi::Persistent(func); + constructor().SuppressDestruct(); + + exports.Set("RTCRtpSender", func); +} + +FROM_NAPI_IMPL(RTCRtpSender *, value) { + return From(value).FlatMap( + [](Napi::Object object) { + auto isRTCRtpSender = false; + napi_instanceof(object.Env(), object, + RTCRtpSender::constructor().Value(), &isRTCRtpSender); + + if (object.Env().IsExceptionPending()) { + return Validation::Invalid( + object.Env().GetAndClearPendingException().Message()); + } + if (!isRTCRtpSender) { + return Validation::Invalid( + "This is not an instance of RTCRtpSender"); + } + + return Pure(RTCRtpSender::Unwrap(object)); + }); +} + +TO_NAPI_IMPL(RTCRtpSender *, pair) { + return Pure(pair.second->Value().As()); +} + +} // namespace node_webrtc diff --git a/src/interfaces/rtc_rtp_transceiver.cc b/src/interfaces/rtc_rtp_transceiver.cc index dbcca51a7..2e61005b8 100644 --- a/src/interfaces/rtc_rtp_transceiver.cc +++ b/src/interfaces/rtc_rtp_transceiver.cc @@ -1,182 +1,183 @@ -/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license that can be found - * in the LICENSE.md file in the root of the source tree. All contributing - * project authors may be found in the AUTHORS file in the root of the source - * tree. - */ +/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license that can be found + * in the LICENSE.md file in the root of the source tree. All contributing + * project authors may be found in the AUTHORS file in the root of the source + * tree. + */ #include "src/interfaces/rtc_rtp_transceiver.hh" +#include #include #include - -#include "src/converters/absl.hh" -#include "src/converters/arguments.hh" -#include "src/converters/interfaces.hh" -#include "src/dictionaries/webrtc/rtc_error.hh" -#include "src/dictionaries/webrtc/rtp_codec_capability.hh" -#include "src/dictionaries/webrtc/rtp_encoding_parameters.hh" -#include "src/enums/webrtc/rtp_transceiver_direction.hh" -#include "src/interfaces/rtc_peer_connection/peer_connection_factory.hh" -#include "src/interfaces/rtc_rtp_receiver.hh" -#include "src/interfaces/rtc_rtp_sender.hh" - -namespace node_webrtc { - -Napi::FunctionReference &RTCRtpTransceiver::constructor() { - static Napi::FunctionReference constructor; - return constructor; -} - -RTCRtpTransceiver::RTCRtpTransceiver(const Napi::CallbackInfo &info) - : AsyncObjectWrap("RTCRtpTransceiver", info) { - if (info.Length() != 2 || !info[0].IsObject() || !info[1].IsExternal()) { - Napi::TypeError::New(info.Env(), "You cannot construct a RTCRtpTransceiver") - .ThrowAsJavaScriptException(); - return; - } - - _factory = PeerConnectionFactory::Unwrap(info[0].ToObject()); - auto transceiver = - *info[1] - .As>>() - .Data(); - - _transceiver = std::move(transceiver); -} - -RTCRtpTransceiver::~RTCRtpTransceiver() { - Napi::HandleScope scope(PeerConnectionFactory::constructor().Env()); - - wrap()->Release(this); -} - -Napi::Value RTCRtpTransceiver::GetMid(const Napi::CallbackInfo &info) { - CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), _transceiver->mid(), result, - Napi::Value) - return result; -} - -Napi::Value RTCRtpTransceiver::GetSender(const Napi::CallbackInfo &) { - return _sender_wrap.GetOrCreate(_factory, _transceiver->sender())->Value(); -} - -Napi::Value RTCRtpTransceiver::GetReceiver(const Napi::CallbackInfo &) { - return _receiver_wrap.GetOrCreate(_factory, _transceiver->receiver()) - ->Value(); -} - -Napi::Value RTCRtpTransceiver::GetStopped(const Napi::CallbackInfo &info) { - CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), _transceiver->stopped(), result, - Napi::Value) - return result; -} - -Napi::Value RTCRtpTransceiver::GetDirection(const Napi::CallbackInfo &info) { - CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), _transceiver->direction(), - result, Napi::Value) - return result; -} - -void RTCRtpTransceiver::SetDirection(const Napi::CallbackInfo &info, - const Napi::Value &value) { - auto maybeDirection = From(value); - if (maybeDirection.IsInvalid()) { - Napi::TypeError::New(info.Env(), maybeDirection.ToErrors()[0]) - .ThrowAsJavaScriptException(); - return; - } - - auto error = - _transceiver->SetDirectionWithError(maybeDirection.UnsafeFromValid()); - if (!error.ok()) { - CONVERT_OR_THROW_AND_RETURN_VOID_NAPI(info.Env(), &error, result, - Napi::Value) - Napi::Error(info.Env(), result).ThrowAsJavaScriptException(); - } -} - -Napi::Value -RTCRtpTransceiver::GetCurrentDirection(const Napi::CallbackInfo &info) { - CONVERT_OR_THROW_AND_RETURN_NAPI( - info.Env(), _transceiver->current_direction(), result, Napi::Value) - return result; -} - -Napi::Value RTCRtpTransceiver::Stop(const Napi::CallbackInfo &info) { - auto error = _transceiver->StopStandard(); - if (!error.ok()) { - CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), &error, result, Napi::Value) - Napi::Error(info.Env(), result).ThrowAsJavaScriptException(); - } - return info.Env().Undefined(); -} - -Napi::Value + +#include "src/converters/absl.hh" +#include "src/converters/arguments.hh" +#include "src/converters/interfaces.hh" +#include "src/dictionaries/webrtc/rtc_error.hh" +#include "src/dictionaries/webrtc/rtp_codec_capability.hh" +#include "src/dictionaries/webrtc/rtp_encoding_parameters.hh" +#include "src/enums/webrtc/rtp_transceiver_direction.hh" +#include "src/interfaces/rtc_peer_connection/peer_connection_factory.hh" +#include "src/interfaces/rtc_rtp_receiver.hh" +#include "src/interfaces/rtc_rtp_sender.hh" + +namespace node_webrtc { + +Napi::FunctionReference &RTCRtpTransceiver::constructor() { + static Napi::FunctionReference constructor; + return constructor; +} + +RTCRtpTransceiver::RTCRtpTransceiver(const Napi::CallbackInfo &info) + : AsyncObjectWrap("RTCRtpTransceiver", info) { + if (info.Length() != 2 || !info[0].IsObject() || !info[1].IsExternal()) { + Napi::TypeError::New(info.Env(), "You cannot construct a RTCRtpTransceiver") + .ThrowAsJavaScriptException(); + return; + } + + _factory = PeerConnectionFactory::Unwrap(info[0].ToObject()); + auto transceiver = + *info[1] + .As>>() + .Data(); + + _transceiver = std::move(transceiver); +} + +RTCRtpTransceiver::~RTCRtpTransceiver() { + Napi::HandleScope scope(PeerConnectionFactory::constructor().Env()); + + wrap()->Release(this); +} + +Napi::Value RTCRtpTransceiver::GetMid(const Napi::CallbackInfo &info) { + CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), _transceiver->mid(), result, + Napi::Value) + return result; +} + +Napi::Value RTCRtpTransceiver::GetSender(const Napi::CallbackInfo &) { + return _sender_wrap.GetOrCreate(_factory, _transceiver->sender())->Value(); +} + +Napi::Value RTCRtpTransceiver::GetReceiver(const Napi::CallbackInfo &) { + return _receiver_wrap.GetOrCreate(_factory, _transceiver->receiver()) + ->Value(); +} + +Napi::Value RTCRtpTransceiver::GetStopped(const Napi::CallbackInfo &info) { + CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), _transceiver->stopped(), result, + Napi::Value) + return result; +} + +Napi::Value RTCRtpTransceiver::GetDirection(const Napi::CallbackInfo &info) { + CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), _transceiver->direction(), + result, Napi::Value) + return result; +} + +void RTCRtpTransceiver::SetDirection(const Napi::CallbackInfo &info, + const Napi::Value &value) { + auto maybeDirection = From(value); + if (maybeDirection.IsInvalid()) { + Napi::TypeError::New(info.Env(), maybeDirection.ToErrors()[0]) + .ThrowAsJavaScriptException(); + return; + } + + auto error = + _transceiver->SetDirectionWithError(maybeDirection.UnsafeFromValid()); + if (!error.ok()) { + CONVERT_OR_THROW_AND_RETURN_VOID_NAPI(info.Env(), &error, result, + Napi::Value) + Napi::Error(info.Env(), result).ThrowAsJavaScriptException(); + } +} + +Napi::Value +RTCRtpTransceiver::GetCurrentDirection(const Napi::CallbackInfo &info) { + CONVERT_OR_THROW_AND_RETURN_NAPI( + info.Env(), _transceiver->current_direction(), result, Napi::Value) + return result; +} + +Napi::Value RTCRtpTransceiver::Stop(const Napi::CallbackInfo &info) { + auto error = _transceiver->StopStandard(); + if (!error.ok()) { + CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), &error, result, Napi::Value) + Napi::Error(info.Env(), result).ThrowAsJavaScriptException(); + } + return info.Env().Undefined(); +} + + Napi::Value RTCRtpTransceiver::SetCodecPreferences(const Napi::CallbackInfo &info) { CONVERT_ARGS_OR_THROW_AND_RETURN_NAPI(info, codecs, std::vector) - auto capabilities = rtc::ArrayView(codecs); + auto capabilities = webrtc::ArrayView(codecs); auto error = _transceiver->SetCodecPreferences(capabilities); - if (!error.ok()) { - CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), &error, result, Napi::Value) - Napi::Error(info.Env(), result).ThrowAsJavaScriptException(); - } - return info.Env().Undefined(); -} - -Wrap, - PeerConnectionFactory *> * -RTCRtpTransceiver::wrap() { - static auto wrap = - new node_webrtc::Wrap, - PeerConnectionFactory *>(RTCRtpTransceiver::Create); - return wrap; -} - -RTCRtpTransceiver *RTCRtpTransceiver::Create( - PeerConnectionFactory *factory, - rtc::scoped_refptr transceiver) { - auto env = constructor().Env(); - Napi::HandleScope scope(env); - - auto object = constructor().New( - {factory->Value(), - Napi::External>::New( - env, &transceiver)}); - - auto unwrapped = Unwrap(object); - return unwrapped; -} - -void RTCRtpTransceiver::Init(Napi::Env env, Napi::Object exports) { - auto func = DefineClass( - env, "RTCRtpTransceiver", - { - InstanceAccessor("mid", &RTCRtpTransceiver::GetMid, nullptr), - InstanceAccessor("sender", &RTCRtpTransceiver::GetSender, nullptr), - InstanceAccessor("receiver", &RTCRtpTransceiver::GetReceiver, - nullptr), - InstanceAccessor("stopped", &RTCRtpTransceiver::GetStopped, nullptr), - InstanceAccessor("direction", &RTCRtpTransceiver::GetDirection, - &RTCRtpTransceiver::SetDirection), - InstanceAccessor("currentDirection", - &RTCRtpTransceiver::GetCurrentDirection, nullptr), - InstanceMethod("stop", &RTCRtpTransceiver::Stop), - InstanceMethod("setCodecPreferences", - &RTCRtpTransceiver::SetCodecPreferences), - }); - - constructor() = Napi::Persistent(func); - constructor().SuppressDestruct(); - - exports.Set("RTCRtpTransceiver", func); -} - -TO_NAPI_IMPL(RTCRtpTransceiver *, pair) { - return Pure(pair.second->Value().As()); -} - -} // namespace node_webrtc + if (!error.ok()) { + CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), &error, result, Napi::Value) + Napi::Error(info.Env(), result).ThrowAsJavaScriptException(); + } + return info.Env().Undefined(); +} + +Wrap, + PeerConnectionFactory *> * +RTCRtpTransceiver::wrap() { + static auto wrap = + new node_webrtc::Wrap, + PeerConnectionFactory *>(RTCRtpTransceiver::Create); + return wrap; +} + +RTCRtpTransceiver *RTCRtpTransceiver::Create( + PeerConnectionFactory *factory, + rtc::scoped_refptr transceiver) { + auto env = constructor().Env(); + Napi::HandleScope scope(env); + + auto object = constructor().New( + {factory->Value(), + Napi::External>::New( + env, &transceiver)}); + + auto unwrapped = Unwrap(object); + return unwrapped; +} + +void RTCRtpTransceiver::Init(Napi::Env env, Napi::Object exports) { + auto func = DefineClass( + env, "RTCRtpTransceiver", + { + InstanceAccessor("mid", &RTCRtpTransceiver::GetMid, nullptr), + InstanceAccessor("sender", &RTCRtpTransceiver::GetSender, nullptr), + InstanceAccessor("receiver", &RTCRtpTransceiver::GetReceiver, + nullptr), + InstanceAccessor("stopped", &RTCRtpTransceiver::GetStopped, nullptr), + InstanceAccessor("direction", &RTCRtpTransceiver::GetDirection, + &RTCRtpTransceiver::SetDirection), + InstanceAccessor("currentDirection", + &RTCRtpTransceiver::GetCurrentDirection, nullptr), + InstanceMethod("stop", &RTCRtpTransceiver::Stop), + InstanceMethod("setCodecPreferences", + &RTCRtpTransceiver::SetCodecPreferences), + }); + + constructor() = Napi::Persistent(func); + constructor().SuppressDestruct(); + + exports.Set("RTCRtpTransceiver", func); +} + +TO_NAPI_IMPL(RTCRtpTransceiver *, pair) { + return Pure(pair.second->Value().As()); +} + +} // namespace node_webrtc diff --git a/src/interfaces/rtc_video_source.cc b/src/interfaces/rtc_video_source.cc index a2aa7a5e7..a3f634d34 100644 --- a/src/interfaces/rtc_video_source.cc +++ b/src/interfaces/rtc_video_source.cc @@ -1,117 +1,118 @@ -/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license that can be found - * in the LICENSE.md file in the root of the source tree. All contributing - * project authors may be found in the AUTHORS file in the root of the source - * tree. - */ -#include "src/interfaces/rtc_video_source.hh" - +/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license that can be found + * in the LICENSE.md file in the root of the source tree. All contributing + * project authors may be found in the AUTHORS file in the root of the source + * tree. + */ +#include "src/interfaces/rtc_video_source.hh" + #include #include #include +#include #include #include "src/converters/absl.hh" #include "src/converters/arguments.hh" -#include "src/converters/napi.hh" -#include "src/dictionaries/webrtc/video_frame_buffer.hh" -#include "src/functional/maybe.hh" -#include "src/interfaces/media_stream_track.hh" - -#include - -namespace node_webrtc { - -Napi::FunctionReference &RTCVideoSource::constructor() { - static Napi::FunctionReference constructor; - return constructor; -} - -RTCVideoSource::RTCVideoSource(const Napi::CallbackInfo &info) - : Napi::ObjectWrap(info) { - New(info); -} - -Napi::Value RTCVideoSource::New(const Napi::CallbackInfo &info) { - auto env = info.Env(); - - if (!info.IsConstructCall()) { - Napi::TypeError::New(env, - "Use the new operator to construct an RTCVideoSource.") - .ThrowAsJavaScriptException(); - return info.Env().Undefined(); - } - - CONVERT_ARGS_OR_THROW_AND_RETURN_NAPI(info, maybeInit, - Maybe) - auto init = maybeInit.FromMaybe(RTCVideoSourceInit()); - - auto needsDenoising = init.needsDenoising - .Map([](auto needsDenoising) { - return absl::optional(needsDenoising); - }) - .FromMaybe(absl::optional()); - - _source = new rtc::RefCountedObject(init.isScreencast, - needsDenoising); - - return info.Env().Undefined(); -} - -Napi::Value RTCVideoSource::CreateTrack(const Napi::CallbackInfo &) { - // TODO(mroberts): Again, we have some implicit factory we are threading - // around. How to handle? - auto factory = PeerConnectionFactory::GetOrCreateDefault(); - auto track = - factory->factory()->CreateVideoTrack(_source, rtc::CreateRandomUuid()); - return _track_wrap.GetOrCreate(factory, track)->Value(); -} - -Napi::Value RTCVideoSource::OnFrame(const Napi::CallbackInfo &info) { - CONVERT_ARGS_OR_THROW_AND_RETURN_NAPI(info, buffer, - rtc::scoped_refptr) - - auto now = std::chrono::time_point_cast( - std::chrono::system_clock::now()); - uint64_t nowInUs = now.time_since_epoch().count(); - - webrtc::VideoFrame::Builder builder; - auto frame = builder.set_timestamp_us(static_cast(nowInUs)) - .set_video_frame_buffer(buffer) - .build(); - _source->PushFrame(frame); - return info.Env().Undefined(); -} - -Napi::Value RTCVideoSource::GetNeedsDenoising(const Napi::CallbackInfo &info) { - CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), _source->needs_denoising(), - result, Napi::Value) - return result; -} - -Napi::Value RTCVideoSource::GetIsScreencast(const Napi::CallbackInfo &info) { - CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), _source->is_screencast(), result, - Napi::Value) - return result; -} - -void RTCVideoSource::Init(Napi::Env env, Napi::Object exports) { - Napi::HandleScope scope(env); - - Napi::Function func = DefineClass( - env, "RTCVideoSource", - {InstanceMethod("createTrack", &RTCVideoSource::CreateTrack), - InstanceMethod("onFrame", &RTCVideoSource::OnFrame), - InstanceAccessor("needsDenoising", &RTCVideoSource::GetNeedsDenoising, - nullptr), - InstanceAccessor("isScreencast", &RTCVideoSource::GetIsScreencast, - nullptr)}); - - constructor() = Napi::Persistent(func); - constructor().SuppressDestruct(); - - exports.Set("RTCVideoSource", func); -} - -} // namespace node_webrtc +#include "src/converters/napi.hh" +#include "src/dictionaries/webrtc/video_frame_buffer.hh" +#include "src/functional/maybe.hh" +#include "src/interfaces/media_stream_track.hh" + +#include + +namespace node_webrtc { + +Napi::FunctionReference &RTCVideoSource::constructor() { + static Napi::FunctionReference constructor; + return constructor; +} + +RTCVideoSource::RTCVideoSource(const Napi::CallbackInfo &info) + : Napi::ObjectWrap(info) { + New(info); +} + +Napi::Value RTCVideoSource::New(const Napi::CallbackInfo &info) { + auto env = info.Env(); + + if (!info.IsConstructCall()) { + Napi::TypeError::New(env, + "Use the new operator to construct an RTCVideoSource.") + .ThrowAsJavaScriptException(); + return info.Env().Undefined(); + } + + CONVERT_ARGS_OR_THROW_AND_RETURN_NAPI(info, maybeInit, + Maybe) + auto init = maybeInit.FromMaybe(RTCVideoSourceInit()); + + auto needsDenoising = init.needsDenoising + .Map([](auto needsDenoising) { + return absl::optional(needsDenoising); + }) + .FromMaybe(absl::optional()); + + _source = new rtc::RefCountedObject(init.isScreencast, + needsDenoising); + + return info.Env().Undefined(); +} + +Napi::Value RTCVideoSource::CreateTrack(const Napi::CallbackInfo &) { + // TODO(mroberts): Again, we have some implicit factory we are threading + // around. How to handle? + auto factory = PeerConnectionFactory::GetOrCreateDefault(); + auto track = + factory->factory()->CreateVideoTrack(_source, rtc::CreateRandomUuid()); + return _track_wrap.GetOrCreate(factory, track)->Value(); +} + +Napi::Value RTCVideoSource::OnFrame(const Napi::CallbackInfo &info) { + CONVERT_ARGS_OR_THROW_AND_RETURN_NAPI(info, buffer, + rtc::scoped_refptr) + + auto now = std::chrono::time_point_cast( + std::chrono::system_clock::now()); + uint64_t nowInUs = now.time_since_epoch().count(); + + webrtc::VideoFrame::Builder builder; + auto frame = builder.set_timestamp_us(static_cast(nowInUs)) + .set_video_frame_buffer(buffer) + .build(); + _source->PushFrame(frame); + return info.Env().Undefined(); +} + +Napi::Value RTCVideoSource::GetNeedsDenoising(const Napi::CallbackInfo &info) { + CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), _source->needs_denoising(), + result, Napi::Value) + return result; +} + +Napi::Value RTCVideoSource::GetIsScreencast(const Napi::CallbackInfo &info) { + CONVERT_OR_THROW_AND_RETURN_NAPI(info.Env(), _source->is_screencast(), result, + Napi::Value) + return result; +} + +void RTCVideoSource::Init(Napi::Env env, Napi::Object exports) { + Napi::HandleScope scope(env); + + Napi::Function func = DefineClass( + env, "RTCVideoSource", + {InstanceMethod("createTrack", &RTCVideoSource::CreateTrack), + InstanceMethod("onFrame", &RTCVideoSource::OnFrame), + InstanceAccessor("needsDenoising", &RTCVideoSource::GetNeedsDenoising, + nullptr), + InstanceAccessor("isScreencast", &RTCVideoSource::GetIsScreencast, + nullptr)}); + + constructor() = Napi::Persistent(func); + constructor().SuppressDestruct(); + + exports.Set("RTCVideoSource", func); +} + +} // namespace node_webrtc diff --git a/src/methods/get_user_media.cc b/src/methods/get_user_media.cc index 15aceefc9..ced29fc5d 100644 --- a/src/methods/get_user_media.cc +++ b/src/methods/get_user_media.cc @@ -1,161 +1,162 @@ -/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license that can be found - * in the LICENSE.md file in the root of the source tree. All contributing - * project authors may be found in the AUTHORS file in the root of the source - * tree. - */ -#include "src/methods/get_user_media.hh" - +/* Copyright (c) 2019 The node-webrtc project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license that can be found + * in the LICENSE.md file in the root of the source tree. All contributing + * project authors may be found in the AUTHORS file in the root of the source + * tree. + */ +#include "src/methods/get_user_media.hh" + #include #include #include +#include #include "src/converters.hh" #include "src/converters/arguments.hh" #include "src/converters/object.hh" -#include "src/functional/curry.hh" -#include "src/functional/either.hh" -#include "src/functional/maybe.hh" -#include "src/functional/operators.hh" -#include "src/functional/validation.hh" -#include "src/interfaces/media_stream.hh" -#include "src/interfaces/rtc_peer_connection/peer_connection_factory.hh" -#include "src/interfaces/rtc_video_source.hh" -#include "src/node/utility.hh" - -// TODO(mroberts): Expand support for other members. -struct MediaTrackConstraintSet { - node_webrtc::Maybe width; - node_webrtc::Maybe height; - - static MediaTrackConstraintSet - Create(const node_webrtc::Maybe width, - const node_webrtc::Maybe height) { - return {width, height}; - } -}; - -template <> -struct node_webrtc::Converter { - static node_webrtc::Validation - Convert(const Napi::Value value) { - return node_webrtc::From(value) - .FlatMap([](auto object) { - return curry(MediaTrackConstraintSet::Create) % - node_webrtc::GetOptional(object, "width") * - node_webrtc::GetOptional(object, "height"); - }); - } -}; - -struct MediaTrackConstraints : public MediaTrackConstraintSet { - std::vector advanced; - - static MediaTrackConstraints - Create(const MediaTrackConstraintSet set, - const std::vector &advanced) { - MediaTrackConstraints constraints; - constraints.width = set.width; - constraints.height = set.height; - constraints.advanced = advanced; - return constraints; - } -}; - -template <> struct node_webrtc::Converter { - static node_webrtc::Validation - Convert(const Napi::Value value) { - return node_webrtc::From(value) - .FlatMap([&value](auto object) { - return curry(MediaTrackConstraints::Create) % - node_webrtc::From(value) * - node_webrtc::GetOptional>( - object, "advanced", - std::vector()); - }); - } -}; - -struct MediaStreamConstraints { - node_webrtc::Maybe> audio; - node_webrtc::Maybe> video; - - static node_webrtc::Validation Create( - const node_webrtc::Maybe> - &audio, - const node_webrtc::Maybe> - &video) { - return audio.IsNothing() && video.IsNothing() - ? node_webrtc::Validation::Invalid( - R"(Must specify at least "audio" or "video")") - : node_webrtc::Validation::Valid( - {audio, video}); - } -}; - -template <> struct node_webrtc::Converter { - static node_webrtc::Validation - Convert(const Napi::Value value) { - return node_webrtc::From(value) - .FlatMap([](auto object) { - return node_webrtc::Validation::Join( - curry(MediaStreamConstraints::Create) % - node_webrtc::GetOptional< - node_webrtc::Either>(object, - "audio") * - node_webrtc::GetOptional< - node_webrtc::Either>(object, - "video")); - }); - } -}; - -Napi::Value -node_webrtc::GetUserMedia::GetUserMediaImpl(const Napi::CallbackInfo &info) { - CREATE_DEFERRED(info.Env(), deferred) - - CONVERT_ARGS_OR_REJECT_AND_RETURN_NAPI(deferred, info, constraints, - MediaStreamConstraints) - - auto factory = node_webrtc::PeerConnectionFactory::GetOrCreateDefault(); - auto stream = - factory->factory()->CreateLocalMediaStream(rtc::CreateRandomUuid()); - - auto audio = - constraints.audio - .Map([](auto constraint) { return constraint.FromLeft(true); }) - .FromMaybe(false); - - auto video = - constraints.video - .Map([](auto constraint) { return constraint.FromLeft(true); }) - .FromMaybe(false); - +#include "src/functional/curry.hh" +#include "src/functional/either.hh" +#include "src/functional/maybe.hh" +#include "src/functional/operators.hh" +#include "src/functional/validation.hh" +#include "src/interfaces/media_stream.hh" +#include "src/interfaces/rtc_peer_connection/peer_connection_factory.hh" +#include "src/interfaces/rtc_video_source.hh" +#include "src/node/utility.hh" + +// TODO(mroberts): Expand support for other members. +struct MediaTrackConstraintSet { + node_webrtc::Maybe width; + node_webrtc::Maybe height; + + static MediaTrackConstraintSet + Create(const node_webrtc::Maybe width, + const node_webrtc::Maybe height) { + return {width, height}; + } +}; + +template <> +struct node_webrtc::Converter { + static node_webrtc::Validation + Convert(const Napi::Value value) { + return node_webrtc::From(value) + .FlatMap([](auto object) { + return curry(MediaTrackConstraintSet::Create) % + node_webrtc::GetOptional(object, "width") * + node_webrtc::GetOptional(object, "height"); + }); + } +}; + +struct MediaTrackConstraints : public MediaTrackConstraintSet { + std::vector advanced; + + static MediaTrackConstraints + Create(const MediaTrackConstraintSet set, + const std::vector &advanced) { + MediaTrackConstraints constraints; + constraints.width = set.width; + constraints.height = set.height; + constraints.advanced = advanced; + return constraints; + } +}; + +template <> struct node_webrtc::Converter { + static node_webrtc::Validation + Convert(const Napi::Value value) { + return node_webrtc::From(value) + .FlatMap([&value](auto object) { + return curry(MediaTrackConstraints::Create) % + node_webrtc::From(value) * + node_webrtc::GetOptional>( + object, "advanced", + std::vector()); + }); + } +}; + +struct MediaStreamConstraints { + node_webrtc::Maybe> audio; + node_webrtc::Maybe> video; + + static node_webrtc::Validation Create( + const node_webrtc::Maybe> + &audio, + const node_webrtc::Maybe> + &video) { + return audio.IsNothing() && video.IsNothing() + ? node_webrtc::Validation::Invalid( + R"(Must specify at least "audio" or "video")") + : node_webrtc::Validation::Valid( + {audio, video}); + } +}; + +template <> struct node_webrtc::Converter { + static node_webrtc::Validation + Convert(const Napi::Value value) { + return node_webrtc::From(value) + .FlatMap([](auto object) { + return node_webrtc::Validation::Join( + curry(MediaStreamConstraints::Create) % + node_webrtc::GetOptional< + node_webrtc::Either>(object, + "audio") * + node_webrtc::GetOptional< + node_webrtc::Either>(object, + "video")); + }); + } +}; + +Napi::Value +node_webrtc::GetUserMedia::GetUserMediaImpl(const Napi::CallbackInfo &info) { + CREATE_DEFERRED(info.Env(), deferred) + + CONVERT_ARGS_OR_REJECT_AND_RETURN_NAPI(deferred, info, constraints, + MediaStreamConstraints) + + auto factory = node_webrtc::PeerConnectionFactory::GetOrCreateDefault(); + auto stream = + factory->factory()->CreateLocalMediaStream(rtc::CreateRandomUuid()); + + auto audio = + constraints.audio + .Map([](auto constraint) { return constraint.FromLeft(true); }) + .FromMaybe(false); + + auto video = + constraints.video + .Map([](auto constraint) { return constraint.FromLeft(true); }) + .FromMaybe(false); + if (audio) { - cricket::AudioOptions options; + webrtc::AudioOptions options; auto source = factory->factory()->CreateAudioSource(options); auto track = factory->factory()->CreateAudioTrack(rtc::CreateRandomUuid(), source.get()); - stream->AddTrack(track); - } - - if (video) { - auto source = rtc::scoped_refptr( - new rtc::RefCountedObject()); - auto track = - factory->factory()->CreateVideoTrack(source, rtc::CreateRandomUuid()); - stream->AddTrack(track); - } - - auto media_stream = - RefPtr(MediaStream::wrap()->GetOrCreate(factory, stream)); - // TODO(jack): this may end up being the wrong lifetime, if Resolve can't - // automatically Ref it... - node_webrtc::Resolve(deferred, media_stream.ptr()); - return deferred.Promise(); -} - -void node_webrtc::GetUserMedia::Init(Napi::Env env, Napi::Object exports) { - exports.Set("getUserMedia", Napi::Function::New(env, GetUserMediaImpl)); -} + stream->AddTrack(track); + } + + if (video) { + auto source = rtc::scoped_refptr( + new rtc::RefCountedObject()); + auto track = + factory->factory()->CreateVideoTrack(source, rtc::CreateRandomUuid()); + stream->AddTrack(track); + } + + auto media_stream = + RefPtr(MediaStream::wrap()->GetOrCreate(factory, stream)); + // TODO(jack): this may end up being the wrong lifetime, if Resolve can't + // automatically Ref it... + node_webrtc::Resolve(deferred, media_stream.ptr()); + return deferred.Promise(); +} + +void node_webrtc::GetUserMedia::Init(Napi::Env env, Napi::Object exports) { + exports.Set("getUserMedia", Napi::Function::New(env, GetUserMediaImpl)); +} diff --git a/src/webrtc/packet_socket_factory_with_tls_cert_verifier.cc b/src/webrtc/packet_socket_factory_with_tls_cert_verifier.cc new file mode 100644 index 000000000..88cc098f5 --- /dev/null +++ b/src/webrtc/packet_socket_factory_with_tls_cert_verifier.cc @@ -0,0 +1,94 @@ +/* Copyright (c) 2026 The node-webrtc project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license that can be found + * in the LICENSE.md file in the root of the source tree. All contributing + * project authors may be found in the AUTHORS file in the root of the source + * tree. + */ +#include "src/webrtc/packet_socket_factory_with_tls_cert_verifier.hh" + +#include + +#include + +#include "src/webrtc/windows_platform_certificate_verifier.hh" + +namespace node_webrtc { + +PacketSocketFactoryWithTlsCertVerifier::PacketSocketFactoryWithTlsCertVerifier( + std::unique_ptr inner) + : inner_(std::move(inner)), + tls_cert_verifier_(CreateWindowsPlatformCertificateVerifier()) {} + +PacketSocketFactoryWithTlsCertVerifier::~PacketSocketFactoryWithTlsCertVerifier() = + default; + +std::unique_ptr +PacketSocketFactoryWithTlsCertVerifier::CreateUdpSocket( + const webrtc::Environment& env, + const webrtc::SocketAddress& address, + uint16_t min_port, + uint16_t max_port) { + return inner_->CreateUdpSocket(env, address, min_port, max_port); +} + +std::unique_ptr +PacketSocketFactoryWithTlsCertVerifier::CreateServerTcpSocket( + const webrtc::Environment& env, + const webrtc::SocketAddress& local_address, + uint16_t min_port, + uint16_t max_port, + int opts) { + return inner_->CreateServerTcpSocket(env, local_address, min_port, max_port, + opts); +} + +std::unique_ptr +PacketSocketFactoryWithTlsCertVerifier::CreateClientTcpSocket( + const webrtc::Environment& env, + const webrtc::SocketAddress& local_address, + const webrtc::SocketAddress& remote_address, + const webrtc::PacketSocketTcpOptions& tcp_options) { + return inner_->CreateClientTcpSocket(env, local_address, remote_address, + WithTlsVerifier(tcp_options)); +} + +std::unique_ptr +PacketSocketFactoryWithTlsCertVerifier::CreateAsyncDnsResolver() { + return inner_->CreateAsyncDnsResolver(); +} + +std::unique_ptr +PacketSocketFactoryWithTlsCertVerifier::CreateClientUdpSocket( + const webrtc::Environment& env, + const webrtc::SocketAddress& local_address, + const webrtc::SocketAddress& remote_address, + uint16_t min_port, + uint16_t max_port, + const webrtc::PacketSocketTcpOptions& options) { + return inner_->CreateClientUdpSocket(env, local_address, remote_address, + min_port, max_port, + WithTlsVerifier(options)); +} + +webrtc::PacketSocketTcpOptions +PacketSocketFactoryWithTlsCertVerifier::WithTlsVerifier( + const webrtc::PacketSocketTcpOptions& options) const { + auto wrapped = options; + +#if defined(WEBRTC_WIN) + const bool wants_secure_tls = + (options.opts & webrtc::PacketSocketFactory::OPT_TLS) != 0; + if (wants_secure_tls && wrapped.tls_cert_verifier == nullptr && + tls_cert_verifier_ != nullptr) { + RTC_LOG(LS_INFO) + << "PacketSocketFactoryWithTlsCertVerifier: injecting Windows TLS " + "certificate verifier"; + wrapped.tls_cert_verifier = tls_cert_verifier_.get(); + } +#endif + + return wrapped; +} + +} // namespace node_webrtc diff --git a/src/webrtc/packet_socket_factory_with_tls_cert_verifier.hh b/src/webrtc/packet_socket_factory_with_tls_cert_verifier.hh new file mode 100644 index 000000000..b73dd7cf5 --- /dev/null +++ b/src/webrtc/packet_socket_factory_with_tls_cert_verifier.hh @@ -0,0 +1,57 @@ +/* Copyright (c) 2026 The node-webrtc project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license that can be found + * in the LICENSE.md file in the root of the source tree. All contributing + * project authors may be found in the AUTHORS file in the root of the source + * tree. + */ +#pragma once + +#include + +#include + +namespace node_webrtc { + +class PacketSocketFactoryWithTlsCertVerifier final + : public webrtc::PacketSocketFactory { + public: + explicit PacketSocketFactoryWithTlsCertVerifier( + std::unique_ptr inner); + ~PacketSocketFactoryWithTlsCertVerifier() override; + + std::unique_ptr CreateUdpSocket( + const webrtc::Environment& env, + const webrtc::SocketAddress& address, + uint16_t min_port, + uint16_t max_port) override; + std::unique_ptr CreateServerTcpSocket( + const webrtc::Environment& env, + const webrtc::SocketAddress& local_address, + uint16_t min_port, + uint16_t max_port, + int opts) override; + std::unique_ptr CreateClientTcpSocket( + const webrtc::Environment& env, + const webrtc::SocketAddress& local_address, + const webrtc::SocketAddress& remote_address, + const webrtc::PacketSocketTcpOptions& tcp_options) override; + std::unique_ptr CreateAsyncDnsResolver() + override; + std::unique_ptr CreateClientUdpSocket( + const webrtc::Environment& env, + const webrtc::SocketAddress& local_address, + const webrtc::SocketAddress& remote_address, + uint16_t min_port, + uint16_t max_port, + const webrtc::PacketSocketTcpOptions& options) override; + + private: + webrtc::PacketSocketTcpOptions WithTlsVerifier( + const webrtc::PacketSocketTcpOptions& options) const; + + std::unique_ptr inner_; + std::unique_ptr tls_cert_verifier_; +}; + +} // namespace node_webrtc diff --git a/src/webrtc/windows_platform_certificate_verifier.cc b/src/webrtc/windows_platform_certificate_verifier.cc new file mode 100644 index 000000000..1f95acb19 --- /dev/null +++ b/src/webrtc/windows_platform_certificate_verifier.cc @@ -0,0 +1,195 @@ +/* Copyright (c) 2026 The node-webrtc project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license that can be found + * in the LICENSE.md file in the root of the source tree. All contributing + * project authors may be found in the AUTHORS file in the root of the source + * tree. + */ +#include "src/webrtc/windows_platform_certificate_verifier.hh" + +#if defined(WEBRTC_WIN) + +#include +#include + +#include +#include +#include + +#include +#include + +namespace node_webrtc { +namespace { + +struct CertContextDeleter { + void operator()(const CERT_CONTEXT* cert) const { + if (cert != nullptr) { + CertFreeCertificateContext(cert); + } + } +}; + +struct CertStoreDeleter { + void operator()(const void* store) const { + if (store != nullptr) { + CertCloseStore(static_cast(const_cast(store)), 0); + } + } +}; + +struct CertChainContextDeleter { + void operator()(const CERT_CHAIN_CONTEXT* chain) const { + if (chain != nullptr) { + CertFreeCertificateChain(chain); + } + } +}; + +using UniqueCertContext = + std::unique_ptr; +using UniqueCertStore = std::unique_ptr; +using UniqueCertChainContext = + std::unique_ptr; + +UniqueCertContext CreateCertificateContext( + const webrtc::SSLCertificate& certificate) { + webrtc::Buffer der; + certificate.ToDER(&der); + if (der.empty()) { + RTC_LOG(LS_WARNING) + << "WindowsPlatformCertificateVerifier: empty DER certificate"; + return UniqueCertContext(nullptr); + } + + return UniqueCertContext(CertCreateCertificateContext( + X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + reinterpret_cast(der.data()), + static_cast(der.size()))); +} + +class WindowsPlatformCertificateVerifier final + : public webrtc::SSLCertificateVerifier { + public: + bool VerifyChain(const webrtc::SSLCertChain& chain) override { + if (chain.GetSize() == 0) { + RTC_LOG(LS_WARNING) + << "WindowsPlatformCertificateVerifier: empty certificate chain"; + return false; + } + + UniqueCertContext leaf_context(CreateCertificateContext(chain.Get(0))); + if (!leaf_context) { + RTC_LOG(LS_WARNING) + << "WindowsPlatformCertificateVerifier: failed to create leaf " + "certificate context, error=" + << GetLastError(); + return false; + } + + UniqueCertStore intermediate_store( + CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0, + CERT_STORE_CREATE_NEW_FLAG, nullptr)); + if (!intermediate_store) { + RTC_LOG(LS_WARNING) + << "WindowsPlatformCertificateVerifier: failed to create " + "intermediate store, error=" + << GetLastError(); + return false; + } + + for (size_t i = 1; i < chain.GetSize(); ++i) { + UniqueCertContext cert_context(CreateCertificateContext(chain.Get(i))); + if (!cert_context) { + RTC_LOG(LS_WARNING) + << "WindowsPlatformCertificateVerifier: failed to create " + "intermediate certificate context at index " + << i << ", error=" << GetLastError(); + return false; + } + + if (!CertAddCertificateContextToStore( + static_cast(const_cast(intermediate_store.get())), + cert_context.get(), CERT_STORE_ADD_ALWAYS, nullptr)) { + RTC_LOG(LS_WARNING) + << "WindowsPlatformCertificateVerifier: failed to add " + "intermediate certificate to store at index " + << i << ", error=" << GetLastError(); + return false; + } + } + + LPCSTR usage_identifiers[] = {szOID_PKIX_KP_SERVER_AUTH}; + CERT_CHAIN_PARA chain_parameters = {}; + chain_parameters.cbSize = sizeof(chain_parameters); + chain_parameters.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR; + chain_parameters.RequestedUsage.Usage.cUsageIdentifier = 1; + chain_parameters.RequestedUsage.Usage.rgpszUsageIdentifier = + const_cast(usage_identifiers); + + PCCERT_CHAIN_CONTEXT raw_chain_context = nullptr; + if (!CertGetCertificateChain( + nullptr, leaf_context.get(), nullptr, + static_cast(const_cast(intermediate_store.get())), + &chain_parameters, 0, nullptr, &raw_chain_context)) { + RTC_LOG(LS_WARNING) + << "WindowsPlatformCertificateVerifier: CertGetCertificateChain " + "failed, error=" + << GetLastError(); + return false; + } + + UniqueCertChainContext chain_context(raw_chain_context); + + CERT_CHAIN_POLICY_PARA policy_parameters = {}; + policy_parameters.cbSize = sizeof(policy_parameters); + + CERT_CHAIN_POLICY_STATUS policy_status = {}; + policy_status.cbSize = sizeof(policy_status); + + if (!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_BASE, + chain_context.get(), + &policy_parameters, + &policy_status)) { + RTC_LOG(LS_WARNING) + << "WindowsPlatformCertificateVerifier: " + "CertVerifyCertificateChainPolicy failed, error=" + << GetLastError(); + return false; + } + + if (policy_status.dwError != 0) { + RTC_LOG(LS_WARNING) + << "WindowsPlatformCertificateVerifier: certificate chain rejected, " + "policy_error=" + << policy_status.dwError; + return false; + } + + RTC_LOG(LS_INFO) + << "WindowsPlatformCertificateVerifier: platform trust validation ok"; + return true; + } +}; + +} // namespace + +std::unique_ptr +CreateWindowsPlatformCertificateVerifier() { + return std::make_unique(); +} + +} // namespace node_webrtc + +#else + +namespace node_webrtc { + +std::unique_ptr +CreateWindowsPlatformCertificateVerifier() { + return nullptr; +} + +} // namespace node_webrtc + +#endif diff --git a/src/webrtc/windows_platform_certificate_verifier.hh b/src/webrtc/windows_platform_certificate_verifier.hh new file mode 100644 index 000000000..6afe6e036 --- /dev/null +++ b/src/webrtc/windows_platform_certificate_verifier.hh @@ -0,0 +1,19 @@ +/* Copyright (c) 2026 The node-webrtc project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license that can be found + * in the LICENSE.md file in the root of the source tree. All contributing + * project authors may be found in the AUTHORS file in the root of the source + * tree. + */ +#pragma once + +#include + +#include + +namespace node_webrtc { + +std::unique_ptr +CreateWindowsPlatformCertificateVerifier(); + +} // namespace node_webrtc diff --git a/src/webrtc_compat.hh b/src/webrtc_compat.hh new file mode 100644 index 000000000..015e537d1 --- /dev/null +++ b/src/webrtc_compat.hh @@ -0,0 +1,7 @@ +#pragma once + +// libwebrtc main moved large parts of the former rtc::* API surface into the +// webrtc namespace. Keep the existing addon sources compiling while we port the +// remaining API deltas incrementally. +namespace webrtc {} +namespace rtc = webrtc;