diff --git a/README.md b/README.md index 70142511f..bab25308f 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ # Open WebRTC Toolkit Native SDK +## About this branch +This branch is working on enabling WebAssembly build. Some WebRTC modules could be used for WebTransport and WebCodecs based streaming. + ## Introduction The Open WebRTC Toolkit(OWT) client SDK for native Windows/Linux/Android/iOS applications is built upon the W3C WebRTC standard to accelerate the development of real time communication applications on these platforms. It supports peer to peer communication, and conference mode communication working with diff --git a/scripts/prepare_dev.py b/scripts/prepare_dev.py index d6ed6a8fe..90f1af354 100644 --- a/scripts/prepare_dev.py +++ b/scripts/prepare_dev.py @@ -19,6 +19,8 @@ LIBSRTP_PATH = os.path.join(THIRD_PARTY_PATH, 'libsrtp') FFMPEG_PATH = os.path.join(THIRD_PARTY_PATH, 'ffmpeg') LIBVPX_PATH = os.path.join(THIRD_PARTY_PATH, 'libvpx') +LIBJPEG_TURBO_PATH = os.path.join(THIRD_PARTY_PATH, 'libjpeg_turbo') +NASM_PATH = os.path.join(THIRD_PARTY_PATH, 'nasm') LIBVPX_SOURCE_PATH = os.path.join(LIBVPX_PATH, 'source/libvpx') WEBRTC_OVERRIDES_PATH = os.path.join(THIRD_PARTY_PATH, 'webrtc_overrides') BUILD_PATH = os.path.join(HOME_PATH, 'build') @@ -45,7 +47,9 @@ ('0014-Fix-missing-ffmpeg-configure-item-for-msvc-build.patch', FFMPEG_PATH), ('0015-Remove-custom-d8-dependency.patch', BUILD_PATH), ('0016-Remove-deprecated-create_srcjar-property.patch', THIRD_PARTY_PATH), - ('0017-Build-libvpx-with-RTC-rate-control-impl-included.patch', THIRD_PARTY_PATH) + ('0017-Build-libvpx-with-RTC-rate-control-impl-included.patch', THIRD_PARTY_PATH), + ('0018-Support-wasm-build.patch', LIBJPEG_TURBO_PATH), + ('0019-Support-wasm-build.patch', NASM_PATH) ] def _patch(ignoreFailures=False): diff --git a/talk/owt/BUILD.gn b/talk/owt/BUILD.gn index da66d208d..4eb5cbb2c 100644 --- a/talk/owt/BUILD.gn +++ b/talk/owt/BUILD.gn @@ -3,23 +3,23 @@ # SPDX-License-Identifier: Apache-2.0 import("//build_overrides/webrtc.gni") +import("//talk/owt/sdk/wasm/gn/wasm.gni") import("//testing/test.gni") # Introduced for using libvpx config files. We only enable libvpx rate # controller for VP9 on Windows. if (is_win) { - - if (current_cpu == "x86") { - cpu_arch_full = "ia32" - } else if (current_cpu == "x64") { - cpu_arch_full = "x64" - } else if (current_cpu == "arm") { - if (arm_use_neon) { - cpu_arch_full = "arm-neon" - } else { - cpu_arch_full = "arm" - } - } + if (current_cpu == "x86") { + cpu_arch_full = "ia32" + } else if (current_cpu == "x64") { + cpu_arch_full = "x64" + } else if (current_cpu == "arm") { + if (arm_use_neon) { + cpu_arch_full = "arm-neon" + } else { + cpu_arch_full = "arm" + } + } } if (is_android) { @@ -181,10 +181,10 @@ static_library("owt_sdk_base") { sources += [ "sdk/base/win/base_allocator.cc", "sdk/base/win/base_allocator.h", - "sdk/base/win/d3d_allocator.cc", - "sdk/base/win/d3d_allocator.h", "sdk/base/win/d3d11_allocator.cc", "sdk/base/win/d3d11_allocator.h", + "sdk/base/win/d3d_allocator.cc", + "sdk/base/win/d3d_allocator.h", "sdk/base/win/d3dnativeframe.h", "sdk/base/win/mediacapabilities.cc", "sdk/base/win/mediacapabilities.h", @@ -200,9 +200,9 @@ static_library("owt_sdk_base") { "sdk/base/win/msdkvideoencoderfactory.h", "sdk/base/win/sysmem_allocator.cc", "sdk/base/win/sysmem_allocator.h", - "sdk/base/win/vpedefs.h", "sdk/base/win/vp9ratecontrol.cc", "sdk/base/win/vp9ratecontrol.h", + "sdk/base/win/vpedefs.h", ] public_deps += [ "//third_party/libvpx" ] include_dirs += [ @@ -224,7 +224,7 @@ static_library("owt_sdk_base") { } else { defines += [ "OWT_USE_H265" ] } - + if (owt_use_quic) { defines += [ "OWT_ENABLE_QUIC" ] if (owt_quic_header_root != "") { @@ -242,39 +242,38 @@ static_library("owt_sdk_base") { sources += [ "sdk/base/desktopcapturer.cc", "sdk/base/desktopcapturer.h", - "sdk/base/win/videorendererwin.cc", - "sdk/base/win/videorendererwin.h", - "sdk/base/win/videorendererd3d11.cc", - "sdk/base/win/videorendererd3d11.h", "sdk/base/win/d3d11_manager.h", - "sdk/base/win/device_info_mf.h", "sdk/base/win/device_info_mf.cc", - "sdk/base/win/video_capture_mf.h", + "sdk/base/win/device_info_mf.h", "sdk/base/win/video_capture_mf.cc", + "sdk/base/win/video_capture_mf.h", + "sdk/base/win/videorendererd3d11.cc", + "sdk/base/win/videorendererd3d11.h", + "sdk/base/win/videorendererwin.cc", + "sdk/base/win/videorendererwin.h", ] public_deps += [ "//third_party/webrtc/modules/audio_device:audio_device_module_from_input_and_output" ] - } if (is_linux) { if (owt_msdk_header_root != "") { include_dirs += [ owt_msdk_header_root ] defines += [ "OWT_USE_MSDK" ] sources += [ - "sdk/base/linux/displayutils.cc", - "sdk/base/linux/displayutils.h", "sdk/base/linux/base_allocator.cc", "sdk/base/linux/base_allocator.h", - "sdk/base/linux/vaapi_allocator.cc", - "sdk/base/linux/vaapi_allocator.h", - "sdk/base/linux/msdkvideosession.cc", - "sdk/base/linux/msdkvideosession.h", - "sdk/base/linux/msdkvideodecoderfactory.cc", - "sdk/base/linux/msdkvideodecoderfactory.h", + "sdk/base/linux/displayutils.cc", + "sdk/base/linux/displayutils.h", "sdk/base/linux/msdkvideodecoder.cc", "sdk/base/linux/msdkvideodecoder.h", - "sdk/base/linux/xwindownativeframe.h", + "sdk/base/linux/msdkvideodecoderfactory.cc", + "sdk/base/linux/msdkvideodecoderfactory.h", + "sdk/base/linux/msdkvideosession.cc", + "sdk/base/linux/msdkvideosession.h", + "sdk/base/linux/vaapi_allocator.cc", + "sdk/base/linux/vaapi_allocator.h", "sdk/base/linux/videorenderlinux.cc", "sdk/base/linux/videorenderlinux.h", + "sdk/base/linux/xwindownativeframe.h", ] } } @@ -286,7 +285,7 @@ static_library("owt_sdk_base") { "sdk/base/customizedvideodecoderproxy.h", ] } - if ((is_win || is_linux)) { + if (is_win || is_linux) { sources += [ "sdk/base/customizedaudiocapturer.cc", "sdk/base/customizedaudiocapturer.h", @@ -294,7 +293,7 @@ static_library("owt_sdk_base") { "sdk/base/customizedaudiodevicemodule.h", ] } - if (is_clang) { + if (is_clang && !is_wasm) { configs -= [ "//build/config/clang:find_bad_constructs" ] } if (is_win && is_clang) { @@ -328,10 +327,35 @@ static_library("owt_sdk_p2p") { "sdk/p2p/p2psignalingsenderimpl.cc", "sdk/p2p/p2psignalingsenderimpl.h", ] - if (is_clang) { + if (is_clang && !is_wasm) { configs -= [ "//build/config/clang:find_bad_constructs" ] } } + +if (is_wasm) { + wasm_lib("owt_wasm") { + name = "owt" + deps = [ + "//third_party/boringssl", + "//third_party/webrtc/call", + "//third_party/webrtc/modules/rtp_rtcp:rtp_rtcp", + "//third_party/webrtc/rtc_base:rtc_json", + "//third_party/webrtc/rtc_base:rtc_task_queue_stdlib", + "//third_party/webrtc/api/rtc_event_log:rtc_event_log_factory", + ] + sources = [ + "sdk/wasm/binding.h", + "sdk/wasm/main.cc", + "sdk/wasm/media_session.cc", + "sdk/wasm/media_session.h", + "sdk/wasm/rtp_video_receiver.cc", + "sdk/wasm/rtp_video_receiver.h", + "sdk/wasm/web_transport_session.cc", + "sdk/wasm/web_transport_session.h", + ] + } +} + static_library("owt_sdk_conf") { deps = [ ":owt_sdk_base", @@ -356,7 +380,7 @@ static_library("owt_sdk_conf") { "sdk/include/cpp/owt/conference/remotemixedstream.h", "sdk/include/cpp/owt/conference/user.h", ] - if (is_clang) { + if (is_clang && !is_wasm) { configs -= [ "//build/config/clang:find_bad_constructs" ] } diff --git a/talk/owt/docker/Dockerfile b/talk/owt/docker/Dockerfile new file mode 100644 index 000000000..a385d1a13 --- /dev/null +++ b/talk/owt/docker/Dockerfile @@ -0,0 +1,27 @@ +# Copyright (C) <2021> Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 + +FROM ubuntu:20.04 +SHELL ["/bin/bash", "-c"] +RUN apt-get update && apt-get install -y git curl wget python python3 +RUN mkdir workspace +WORKDIR /workspace +RUN git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git +ENV PATH="$PATH:/workspace/depot_tools" +RUN mkdir owt +WORKDIR /workspace/owt +RUN echo $'solutions = [\n\ + {\n\ + "name" : "src",\n\ + "url" : "https://github.com/open-webrtc-toolkit/owt-client-native.git",\n\ + "deps_file" : "DEPS",\n\ + "managed" : False,\n\ + "custom_deps" : {\n\ + },\n\ + "custom_vars": {},\n\ + },\n\ +]' > .gclient +RUN git config --global user.name owt-bot +RUN git config --global user.email 82484650+owt-bot@users.noreply.github.com +RUN gclient sync diff --git a/talk/owt/gn/toolchain/.emscripten b/talk/owt/gn/toolchain/.emscripten new file mode 100644 index 000000000..ac3a69291 --- /dev/null +++ b/talk/owt/gn/toolchain/.emscripten @@ -0,0 +1,39 @@ +# Copyright (C) 2021 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This is the init script that is passed to `emcc --em-config=.emscripten` +# (Wasm toolchain). It sets vars to locate the various emsdk / llvm dirs. + +import os +import sys +from platform import system + +# We cannot use __file__ here. +this_file = os.getenv('EM_CONFIG') +if this_file is None: + sys.stderr.write('Could not find EM_CONFIG in .emscripten\n') + sys.exit(1) + +root_dir = os.path.dirname(os.path.dirname(os.path.dirname(this_file))) +buildools_os = 'mac' if system().lower() == 'darwin' else 'linux64' +node_path = os.path.join(root_dir, 'buildtools', buildools_os, 'nodejs') +emsdk_path = os.path.join(root_dir, 'buildtools', buildools_os, 'emsdk') + +# Exported vars. +NODE_JS = os.path.join(node_path, 'bin', 'node') +LLVM_ROOT = os.path.join(emsdk_path, 'bin') +BINARYEN_ROOT = emsdk_path +EMSCRIPTEN_ROOT = os.path.join(emsdk_path, 'emscripten') +COMPILER_ENGINE = NODE_JS +JS_ENGINES = [NODE_JS] diff --git a/talk/owt/patches/0018-Support-wasm-build.patch b/talk/owt/patches/0018-Support-wasm-build.patch new file mode 100644 index 000000000..0db725a93 --- /dev/null +++ b/talk/owt/patches/0018-Support-wasm-build.patch @@ -0,0 +1,25 @@ +From e4850e5a4ae4eb73f7a3625b332241d7cd941c3a Mon Sep 17 00:00:00 2001 +From: Jianjun Zhu +Date: Thu, 9 Jun 2022 18:38:00 +0800 +Subject: [PATCH] Support wasm build. + +--- + BUILD.gn | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/BUILD.gn b/BUILD.gn +index f9c483e..e18123d 100644 +--- a/BUILD.gn ++++ b/BUILD.gn +@@ -277,7 +277,7 @@ static_library("libjpeg") { + + # MemorySanitizer doesn't support assembly code, so keep it disabled in + # MSan builds for now. +- if (is_msan) { ++ if (is_msan || is_wasm) { + sources += [ "jsimd_none.c" ] + } else { + public_deps += [ ":simd" ] +-- +2.36.1 + diff --git a/talk/owt/patches/0019-Support-wasm-build.patch b/talk/owt/patches/0019-Support-wasm-build.patch new file mode 100644 index 000000000..28e5cd857 --- /dev/null +++ b/talk/owt/patches/0019-Support-wasm-build.patch @@ -0,0 +1,25 @@ +From d723c37c9a9be8796036602df510fe1c16e68e92 Mon Sep 17 00:00:00 2001 +From: Jianjun Zhu +Date: Thu, 9 Jun 2022 18:50:34 +0800 +Subject: [PATCH] Support wasm build. + +--- + nasm_assemble.gni | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/nasm_assemble.gni b/nasm_assemble.gni +index 902a6320..d2724feb 100644 +--- a/nasm_assemble.gni ++++ b/nasm_assemble.gni +@@ -67,6 +67,8 @@ if (is_mac || is_ios) { + } else if (current_cpu == "x64") { + _nasm_flags = [ "-fwin64" ] + } ++} else { ++ _nasm_flags = [] + } + + if (is_win) { +-- +2.36.1 + diff --git a/talk/owt/sdk/wasm/binding.h b/talk/owt/sdk/wasm/binding.h new file mode 100644 index 000000000..cfc948863 --- /dev/null +++ b/talk/owt/sdk/wasm/binding.h @@ -0,0 +1,32 @@ +// Copyright (C) <2021> Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 + +#ifndef OWT_WASM_BINDING_H_ +#define OWT_WASM_BINDING_H_ + +#include "talk/owt/sdk/wasm/media_session.h" +#include "talk/owt/sdk/wasm/rtp_video_receiver.h" + +namespace owt { +namespace wasm { + +EMSCRIPTEN_BINDINGS(Owt) { + emscripten::class_("MediaSession") + .smart_ptr>("MediaSession") + .function("createRtpVideoReceiver", &MediaSession::CreateRtpVideoReceiver) + .function("setRtcpCallback", &MediaSession::SetRtcpCallback) + .class_function("get", &MediaSession::Get, + emscripten::allow_raw_pointers()); + emscripten::class_("RtpVideoReceiver") + .smart_ptr>("RtpVideoReceiver") + .function("onRtpPacket", &RtpVideoReceiver::OnRtpPacket, + emscripten::allow_raw_pointers()) + .function("setCompleteFrameCallback", + &RtpVideoReceiver::SetCompleteFrameCallback); +} + +} // namespace wasm +} // namespace owt + +#endif \ No newline at end of file diff --git a/talk/owt/sdk/wasm/gn/BUILD.gn b/talk/owt/sdk/wasm/gn/BUILD.gn new file mode 100644 index 000000000..e6b1fcfe9 --- /dev/null +++ b/talk/owt/sdk/wasm/gn/BUILD.gn @@ -0,0 +1,180 @@ +# Copyright (C) <2021> Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 + +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This file is copied from third_party/perfetto/gn/standalone/toolchain/BUILD.gn with modifications. + +import("//build/config/compiler/compiler.gni") +import("wasm.gni") + +template("gcc_like_toolchain") { + toolchain(target_name) { + ar = invoker.ar + cc = invoker.cc + cxx = invoker.cxx + lib_switch = "-l" + lib_dir_switch = "-L" + ld_arg = "" + external_cflags = "" + external_cxxflags = "" + external_ldflags = "" + strip = "" + if (defined(invoker.linker) && invoker.linker != "") { + _invoker_linker = invoker.linker + ld_arg = "-fuse-ld=$_invoker_linker" + } + if (defined(invoker.sysroot) && invoker.sysroot != "") { + _invoker_sysroot = invoker.sysroot + cc = "$cc --sysroot=$_invoker_sysroot" + cxx = "$cxx --sysroot=$_invoker_sysroot" + } + if (defined(invoker.gcc_toolchain) && invoker.gcc_toolchain != "") { + assert(is_clang, "gcc_toolchain can be used only when using clang") + _invoker_gcc_toolchain = invoker.gcc_toolchain + ld_arg = "$ld_arg --gcc-toolchain=$_invoker_gcc_toolchain" + } + if (defined(invoker.external_cflags)) { + external_cflags = invoker.external_cflags + } + if (defined(invoker.external_cxxflags)) { + external_cxxflags = invoker.external_cxxflags + } + if (defined(invoker.external_ldflags)) { + external_ldflags = invoker.external_ldflags + } + if (defined(invoker.strip)) { + strip = invoker.strip + } + + # Object files go in this directory. + object_subdir = "{{target_out_dir}}/{{label_name}}" + + tool("cc") { + depfile = "{{output}}.d" + command = "$cc_wrapper $cc -MMD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_c}} -DEMSCRIPTEN_HAS_UNBOUND_TYPE_NAMES=1 -fno-stack-protector ${external_cflags} -c {{source}} -o {{output}}" + depsformat = "gcc" + outputs = [ "$object_subdir/{{source_name_part}}.o" ] + description = "compile {{source}}" + } + + tool("cxx") { + depfile = "{{output}}.d" + command = "$cc_wrapper $cxx -MMD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_cc}} -DEMSCRIPTEN_HAS_UNBOUND_TYPE_NAMES=1 -fno-stack-protector ${external_cflags} ${external_cxxflags} -c {{source}} -o {{output}}" + depsformat = "gcc" + outputs = [ "$object_subdir/{{source_name_part}}.o" ] + description = "compile {{source}}" + } + + tool("asm") { + depfile = "{{output}}.d" + command = "$cc_wrapper $cc -MMD -MF $depfile {{defines}} {{include_dirs}} {{asmflags}} -DEMSCRIPTEN_HAS_UNBOUND_TYPE_NAMES=1 -fno-stack-protector -c {{source}} -o {{output}}" + depsformat = "gcc" + outputs = [ "$object_subdir/{{source_name_part}}.o" ] + description = "assemble {{source}}" + } + + tool("alink") { + if (current_os == "aix") { + # AIX does not support either -D (deterministic output) or response + # files. + command = "$ar -X64 {{arflags}} -r -c -s {{output}} {{inputs}}" + } else { + rspfile = "{{output}}.rsp" + rspfile_content = "{{inputs}}" + command = "$ar {{arflags}} -r -c -s -D {{output}} @\"$rspfile\"" + } + + # Remove the output file first so that ar doesn't try to modify the + # existing file. + if (host_os == "win") { + tool_wrapper_path = + rebase_path("//build/toolchain/win/tool_wrapper.py", root_build_dir) + command = "cmd /c $python_path $tool_wrapper_path delete-file {{output}} && $command" + } else { + command = "rm -f {{output}} && $command" + } + + # Almost all targets build with //build/config/compiler:thin_archive which + # adds -T to arflags. + description = "AR {{output}}" + outputs = [ "{{output_dir}}/{{target_output_name}}{{output_extension}}" ] + + # Shared libraries go in the target out directory by default so we can + # generate different targets with the same name and not have them collide. + default_output_dir = "{{target_out_dir}}" + default_output_extension = ".a" + output_prefix = "lib" + } + + tool("solink") { + soname = "{{target_output_name}}{{output_extension}}" + unstripped_so = "{{root_out_dir}}/$soname" + rpath = "-Wl,-soname,$soname" + if (is_mac) { + rpath = "-Wl,-install_name,@rpath/$soname" + } + command = "$cc_wrapper $cxx $ld_arg -shared ${external_ldflags} -fstack-protector {{inputs}} {{solibs}} {{libs}} $rpath -o $unstripped_so" + outputs = [ unstripped_so ] + output_prefix = "lib" + default_output_extension = ".so" + description = "link $unstripped_so" + if (strip != "") { + stripped_so = "{{root_out_dir}}/stripped/$soname" + outputs += [ stripped_so ] + command += " && $strip -o $stripped_so $unstripped_so" + } + } + + tool("link") { + unstripped_exe = + "{{root_out_dir}}/{{target_output_name}}{{output_extension}}" + command = "$cc_wrapper $cxx $ld_arg {{ldflags}} ${external_ldflags} {{inputs}} {{solibs}} {{libs}} -o $unstripped_exe" + outputs = [ unstripped_exe ] + description = "link $unstripped_exe" + if (strip != "") { + stripped_exe = "{{root_out_dir}}/stripped/{{target_output_name}}{{output_extension}}" + outputs += [ stripped_exe ] + command += " && $strip -o $stripped_exe $unstripped_exe" + } + } + + tool("stamp") { + command = "touch {{output}}" + description = "stamp {{output}}" + } + + tool("copy") { + command = "cp -af {{source}} {{output}}" + description = "COPY {{source}} {{output}}" + } + + toolchain_args = { + current_cpu = invoker.cpu + current_os = invoker.os + } + } +} + +gcc_like_toolchain("wasm") { + # emsdk_dir and em_config are defined in wasm.gni. + cpu = host_cpu + os = host_os + ar = "$emsdk_dir/emscripten/emar --em-config $em_config" + cc = "$emsdk_dir/emscripten/emcc --em-config $em_config -pthread" + cxx = "$emsdk_dir/emscripten/em++ --em-config $em_config -pthread" + strip = "" +} diff --git a/talk/owt/sdk/wasm/gn/build_tool_wrapper.py b/talk/owt/sdk/wasm/gn/build_tool_wrapper.py new file mode 100644 index 000000000..e1abe197f --- /dev/null +++ b/talk/owt/sdk/wasm/gn/build_tool_wrapper.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python +# Copyright (C) 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" Wrapper to invoke compiled build tools from the build system. + +This is just a workaround for GN assuming that all external scripts are +python sources. It is used to invoke tools like the protoc compiler. +""" + +from __future__ import print_function + +import argparse +import os +import subprocess +import sys + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('--chdir', default=None) + parser.add_argument('--stamp', default=None) + parser.add_argument('--path', default=None) + parser.add_argument('--noop', default=False, action='store_true') + parser.add_argument('--suppress_stdout', default=False, action='store_true') + parser.add_argument('--suppress_stderr', default=False, action='store_true') + parser.add_argument('cmd', nargs=argparse.REMAINDER) + args = parser.parse_args() + + if args.noop: + return 0 + + if args.chdir and not os.path.exists(args.chdir): + print( + 'Cannot chdir to %s from %s' % (workdir, os.getcwd()), file=sys.stderr) + return 1 + + exe = os.path.abspath(args.cmd[0]) if os.sep in args.cmd[0] else args.cmd[0] + env = os.environ.copy() + if args.path: + env['PATH'] = os.path.abspath(args.path) + os.pathsep + env['PATH'] + + devnull = open(os.devnull, 'wb') + stdout = devnull if args.suppress_stdout else None + stderr = devnull if args.suppress_stderr else None + + try: + proc = subprocess.Popen( + [exe] + args.cmd[1:], + cwd=args.chdir, + env=env, + stderr=stderr, + stdout=stdout) + ret = proc.wait() + if ret == 0 and args.stamp: + with open(args.stamp, 'w'): + os.utime(args.stamp, None) + return ret + except OSError as e: + print('Error running: "%s" (%s)' % (args.cmd[0], e.strerror)) + print('PATH=%s' % env.get('PATH')) + return 127 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/talk/owt/sdk/wasm/gn/wasm.gni b/talk/owt/sdk/wasm/gn/wasm.gni new file mode 100644 index 000000000..a68f817da --- /dev/null +++ b/talk/owt/sdk/wasm/gn/wasm.gni @@ -0,0 +1,175 @@ +# Copyright (C) <2021> Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 + +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This file is copied from https://source.chromium.org/chromium/chromium/src/+/main:third_party/perfetto/gn/standalone/wasm.gni. + +import("//build_overrides/build.gni") +import("wasm_vars.gni") + +# Used by //gn/standalone/toolchain/BUILD.gn . +em_config = rebase_path("/home/jianjunz/code/emsdk/.emscripten", "") +emsdk_dir = rebase_path("/home/jianjunz/code/emsdk/upstream", "") + +# Defines a WASM library target. +# Args: +# generate_js: when true generates a .wasm file and a .js file that wraps it +# and provides the boilerplate to initialize the module. +# generate_html: when true generates also an example .html file which contains +# a minimal console to interact with the module (useful for testing). +template("wasm_lib") { + assert(defined(invoker.name)) + + # If the name is foo the target_name must be foo_wasm. + assert(invoker.name + "_wasm" == target_name) + _lib_name = invoker.name + if (is_wasm) { + _exports = "['writeArrayToMemory']" + _target_ldflags = [ + "-s", + "WASM=1", + # "-s", + # "DISABLE_EXCEPTION_CATCHING=1", + "-s", + "INITIAL_MEMORY=33554432", + "-s", + "ALLOW_TABLE_GROWTH=1", + # "-s", + # "WASM_ASYNC_COMPILATION=0", + "-s", + "USE_PTHREADS=1", + "-s", + "PROXY_TO_PTHREAD=1", + "-s", + "LLD_REPORT_UNDEFINED", + "-s", + "EXPORTED_RUNTIME_METHODS=" + _exports, + + # This forces the MEMFS filesystem library to always use typed arrays + # instead of building strings/arrays when appending to a file. This allows + # to deal with pseudo-files larger than 128 MB when calling trace_to_text. + "-s", + "MEMFS_APPEND_TO_TYPED_ARRAYS=1", + + # Reduces global namespace pollution. + "-s", + "MODULARIZE=1", + + "-s", + "EXPORT_ES6=1", + + "--bind", + + #"-fno-stack-protector", + + # This is to prevent that two different wasm modules end up generating + # JS that overrides the same global variable (var Module = ...) + # "-s", + # "EXPORT_NAME=${target_name}", + + # "-lworkerfs.js", # For FS.filesystems.WORKERFS + + "-s", + "PTHREAD_POOL_SIZE=8", + "-s", + "ALLOW_BLOCKING_ON_MAIN_THREAD=0", + + "-pthread", + ] + if (is_debug) { + _target_ldflags += [ + # "-s", + # "ASSERTIONS=2", + # "-s", + # "SAFE_HEAP=1", + # "-s", + # "STACK_OVERFLOW_CHECK=1", + "-O0", + "-gsource-map", + "--source-map-base", + "http://jianjunz-nuc-ubuntu.sh.intel.com:9900/out/wasm/", + ] + } else { + _target_ldflags += [ + "-s", + "ASSERTIONS=1", + "-g2", # Required for getting C++ symbol names. + "-O3", + ] + } + + if (defined(invoker.js_library)) { + _target_ldflags += [ + "--js-library", + invoker.js_library, + ] + } + + _target_cflags = [ + "-pthread", + ] + if (defined(invoker.cflags)) { + _target_cflags += invoker.cflags + } + + _vars_to_forward = [ + #"cflags", + "defines", + "deps", + "includes", + "sources", + "include_dirs", + "public_configs", + "testonly", + "visibility", + ] + + executable("${_lib_name}.js") { + forward_variables_from(invoker, _vars_to_forward) + + # Fix ldflags. + ldflags = _target_ldflags + cflags = _target_cflags + output_extension = "" + } + + # This is just a workaround to deal with the fact that GN doesn't allow + # spcifying extra outputs for an executable() target. In reality the .wasm + # file here is generated by the executable() target above, together with the + # .js file. This dummy target is here to tell GN "there is a target that + # outputs also the .wasm file", so we can depend on that in copy() targets. + action("${_lib_name}.wasm") { + inputs = [] + deps = [ ":${_lib_name}.js" ] + outputs = [ "$root_out_dir/$_lib_name.wasm" ] + if (is_debug) { + outputs += [ "$root_out_dir/$_lib_name.wasm.map" ] + } + args = [ "--noop" ] + script = "//talk/owt/sdk/wasm/gn/build_tool_wrapper.py" + } + } else { # is_wasm + not_needed(invoker, "*") + } + + group(target_name) { + deps = [ + ":${_lib_name}.js($wasm_toolchain)", + ":${_lib_name}.wasm($wasm_toolchain)", + ] + } +} # template diff --git a/talk/owt/sdk/wasm/gn/wasm_vars.gni b/talk/owt/sdk/wasm/gn/wasm_vars.gni new file mode 100644 index 000000000..d3ee3ed29 --- /dev/null +++ b/talk/owt/sdk/wasm/gn/wasm_vars.gni @@ -0,0 +1,20 @@ +# Copyright (C) <2021> Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 + +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +wasm_toolchain = "//talk/owt/sdk/wasm/gn:wasm" +# is_wasm = current_toolchain == wasm_toolchain \ No newline at end of file diff --git a/talk/owt/sdk/wasm/main.cc b/talk/owt/sdk/wasm/main.cc new file mode 100644 index 000000000..2c8821a58 --- /dev/null +++ b/talk/owt/sdk/wasm/main.cc @@ -0,0 +1,11 @@ +// Copyright (C) <2021> Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 + +#include +#include "talk/owt/sdk/wasm/binding.h" + +int main(int argc, char** argv) { + owt::wasm::MediaSession::Get(); + return 0; +} \ No newline at end of file diff --git a/talk/owt/sdk/wasm/media_session.cc b/talk/owt/sdk/wasm/media_session.cc new file mode 100644 index 000000000..5861b1d0f --- /dev/null +++ b/talk/owt/sdk/wasm/media_session.cc @@ -0,0 +1,84 @@ +// Copyright (C) <2021> Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 + +#include "talk/owt/sdk/wasm/media_session.h" +#include "third_party/webrtc/api/rtc_event_log/rtc_event_log_factory.h" +#include "third_party/webrtc/api/transport/field_trial_based_config.h" +#include "third_party/webrtc/rtc_base/task_queue_stdlib.h" + +namespace owt { +namespace wasm { + +MediaSession::MediaSession() + : task_queue_factory_(webrtc::CreateTaskQueueStdlibFactory()), + event_log_factory_(std::make_unique( + task_queue_factory_.get())), + event_log_(event_log_factory_->CreateRtcEventLog( + webrtc::RtcEventLog::EncodingType::NewFormat)), + web_transport_session_(std::make_unique()), + call_(nullptr), + receive_statistics_( + webrtc::ReceiveStatistics::Create(webrtc::Clock::GetRealTimeClock())), + receiver_process_thread_(nullptr), + video_receiver_(nullptr), + rtcp_callback_(emscripten::val::null()) { + rtc::ThreadManager::Instance()->WrapCurrentThread(); + receiver_process_thread_ = + webrtc::ProcessThread::Create("ReceiverProcessThread"); + webrtc::Call::Config config(event_log_.get()); + config.task_queue_factory = task_queue_factory_.get(); + webrtc::FieldTrialBasedConfig trials; + config.trials = &trials; + call_ = std::unique_ptr(webrtc::Call::Create(config)); +} + +std::shared_ptr MediaSession::CreateRtpVideoReceiver( + uint32_t remote_ssrc) { + webrtc::VideoReceiveStream::Config config(this); + config.rtp.remote_ssrc = remote_ssrc; + config.rtp.local_ssrc = 1; + // Same as `kNackRtpHistoryMs` in third_party/webrtc/media/engine/webrtc_voice_engine.cc. + config.rtp.nack.rtp_history_ms = 5000; + // TODO: Use call_->worker_thread() when libwebrtc is rolled to a newer version. + webrtc::TaskQueueBase* current = webrtc::TaskQueueBase::Current(); + if (!current) + current = rtc::ThreadManager::Instance()->CurrentThread(); + RTC_DCHECK(current); + video_receiver_ = std::make_shared( + current, this, &config, receive_statistics_.get(), + receiver_process_thread_.get()); + return video_receiver_; +} + +std::once_flag create_media_session_flag; +std::shared_ptr media_session; + +MediaSession* MediaSession::Get() { + std::call_once(create_media_session_flag, + [] { media_session = std::make_shared(); }); + return media_session.get(); +} + + +void MediaSession::SetRtcpCallback(emscripten::val callback) { + rtcp_callback_ = callback; +} + +bool MediaSession::SendRtp(const uint8_t* packet, + size_t length, + const webrtc::PacketOptions& options) { + return false; +} + +bool MediaSession::SendRtcp(const uint8_t* packet, size_t length) { + if (!rtcp_callback_) { + return false; + } + emscripten::val buffer(emscripten::typed_memory_view(length, packet)); + rtcp_callback_(buffer); + return true; +} + +} // namespace wasm +} // namespace owt \ No newline at end of file diff --git a/talk/owt/sdk/wasm/media_session.h b/talk/owt/sdk/wasm/media_session.h new file mode 100644 index 000000000..f21bd81b6 --- /dev/null +++ b/talk/owt/sdk/wasm/media_session.h @@ -0,0 +1,49 @@ +// Copyright (C) <2021> Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 + +#ifndef OWT_WASM_MEDIASESSION_H_ +#define OWT_WASM_MEDIASESSION_H_ + +#include +#include "call/call.h" +#include "talk/owt/sdk/wasm/rtp_video_receiver.h" +#include "talk/owt/sdk/wasm/web_transport_session.h" +#include "third_party/webrtc/api/rtc_event_log/rtc_event_log_factory_interface.h" + +namespace owt { +namespace wasm { +// Manages a media session between a web client and OWT server QUIC agent. +class MediaSession : public webrtc::Transport { + public: + explicit MediaSession(); + virtual ~MediaSession() = default; + + static MediaSession* Get(); + + std::shared_ptr CreateRtpVideoReceiver( + uint32_t remote_ssrc); + void SetRtcpCallback(emscripten::val callback); + + // Overrides webrtc::Transport. + bool SendRtp(const uint8_t* packet, + size_t length, + const webrtc::PacketOptions& options) override; + bool SendRtcp(const uint8_t* packet, size_t length) override; + + private: + std::unique_ptr task_queue_factory_; + std::unique_ptr event_log_factory_; + std::unique_ptr event_log_; + std::unique_ptr web_transport_session_; + std::unique_ptr call_; + std::unique_ptr receive_statistics_; + std::unique_ptr receiver_process_thread_; + std::shared_ptr video_receiver_; + emscripten::val rtcp_callback_; +}; + +} // namespace wasm +} // namespace owt + +#endif \ No newline at end of file diff --git a/talk/owt/sdk/wasm/rtp_video_receiver.cc b/talk/owt/sdk/wasm/rtp_video_receiver.cc new file mode 100644 index 000000000..1f4e9b839 --- /dev/null +++ b/talk/owt/sdk/wasm/rtp_video_receiver.cc @@ -0,0 +1,66 @@ +// Copyright (C) <2021> Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 + +#include "talk/owt/sdk/wasm/rtp_video_receiver.h" +#include +#include "third_party/webrtc/rtc_base/logging.h" + +namespace owt { +namespace wasm { + +RtpVideoReceiver::RtpVideoReceiver( + webrtc::TaskQueueBase* current_queue, + webrtc::Transport* transport, + const webrtc::VideoReceiveStream::Config* config, + webrtc::ReceiveStatistics* rtp_receive_statistics, + webrtc::ProcessThread* process_thread) + : receiver_(nullptr), complete_frame_callback_(emscripten::val::null()) { + receiver_ = std::make_unique( + current_queue, webrtc::Clock::GetRealTimeClock(), transport, nullptr, + nullptr, config, rtp_receive_statistics, nullptr, nullptr, process_thread, + this, nullptr, this, nullptr, nullptr); + webrtc::VideoCodec codec; + codec.codecType = webrtc::VideoCodecType::kVideoCodecH264; + // TODO: Assign payload value dynamically according to server side + // configuration. + receiver_->AddReceiveCodec(127, codec, std::map(), + false); + receiver_->StartReceive(); +} + +bool RtpVideoReceiver::OnRtpPacket(uintptr_t packet_ptr, size_t packet_size) { + const uint8_t* packet = reinterpret_cast(packet_ptr); + // TODO: Create `rtp_packet_received` from `packet`. + webrtc::RtpPacketReceived rtp_packet_received; + rtp_packet_received.Parse(packet, packet_size); + receiver_->OnRtpPacket(rtp_packet_received); + return false; +} + +void RtpVideoReceiver::OnCompleteFrame( + std::unique_ptr frame) { + if(complete_frame_callback_.isNull()){ + RTC_LOG(LS_WARNING) << "No callback registered for complete frames."; + return; + } + emscripten::val buffer(emscripten::typed_memory_view( + frame->EncodedImage().size(), frame->EncodedImage().data())); + complete_frame_callback_(buffer); +} + +void RtpVideoReceiver::SetCompleteFrameCallback(emscripten::val callback) { + complete_frame_callback_ = callback; +} + +void RtpVideoReceiver::SendNack(const std::vector& sequence_numbers, + bool buffering_allowed) { + // Only buffering_allowed == true is supported. Same as + // https://source.chromium.org/chromium/chromium/src/+/main:third_party/webrtc/video/video_receive_stream2.h;l=157;bpv=1;bpt=1 + RTC_DCHECK(buffering_allowed); + RTC_LOG(LS_INFO)<<"Request packet rtx"; + receiver_->RequestPacketRetransmit(sequence_numbers); +} + +} // namespace wasm +} // namespace owt \ No newline at end of file diff --git a/talk/owt/sdk/wasm/rtp_video_receiver.h b/talk/owt/sdk/wasm/rtp_video_receiver.h new file mode 100644 index 000000000..59e78eca0 --- /dev/null +++ b/talk/owt/sdk/wasm/rtp_video_receiver.h @@ -0,0 +1,43 @@ +// Copyright (C) <2021> Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 + +#ifndef OWT_WASM_RTPVIDEORECEIVER_H_ +#define OWT_WASM_RTPVIDEORECEIVER_H_ + +#include +#include "third_party/webrtc/video/rtp_video_stream_receiver2.h" + +namespace owt { +namespace wasm { +class RtpVideoReceiver + : public webrtc::RtpVideoStreamReceiver2::OnCompleteFrameCallback, + public webrtc::NackSender { + public: + explicit RtpVideoReceiver(webrtc::TaskQueueBase* current_queue, + webrtc::Transport* transport, + const webrtc::VideoReceiveStream::Config* config, + webrtc::ReceiveStatistics* rtp_receive_statistics, + webrtc::ProcessThread* process_thread); + virtual ~RtpVideoReceiver() {} + virtual bool OnRtpPacket(uintptr_t packet_ptr, size_t packet_size); + + // Overrides webrtc::RtpVideoStreamReceiver2::OnCompleteFrameCallback. + void OnCompleteFrame( + std::unique_ptr frame) override; + + void SetCompleteFrameCallback(emscripten::val callback); + + // Overrides webrtc::NackSender. + void SendNack(const std::vector& sequence_numbers, + bool buffering_allowed) override; + + private: + std::unique_ptr receiver_; + emscripten::val complete_frame_callback_; +}; + +} // namespace wasm +} // namespace owt + +#endif \ No newline at end of file diff --git a/talk/owt/sdk/wasm/web_transport_session.cc b/talk/owt/sdk/wasm/web_transport_session.cc new file mode 100644 index 000000000..e5ea596ff --- /dev/null +++ b/talk/owt/sdk/wasm/web_transport_session.cc @@ -0,0 +1,24 @@ +// Copyright (C) <2021> Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 + +#include "talk/owt/sdk/wasm/web_transport_session.h" + +namespace owt { +namespace wasm { + +bool WebTransportSession::SendRtp(const uint8_t* packet, + size_t length, + const webrtc::PacketOptions& options) { + return false; +} +bool WebTransportSession::SendRtcp(const uint8_t* packet, size_t length) { + return false; +} + +void WebTransportSession::SendNack( + const std::vector& sequence_numbers, + bool buffering_allowed) {} + +} // namespace wasm +} // namespace owt \ No newline at end of file diff --git a/talk/owt/sdk/wasm/web_transport_session.h b/talk/owt/sdk/wasm/web_transport_session.h new file mode 100644 index 000000000..365b91617 --- /dev/null +++ b/talk/owt/sdk/wasm/web_transport_session.h @@ -0,0 +1,34 @@ +// Copyright (C) <2021> Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 + +#ifndef OWT_WASM_WEBTRANSPORTSESSION_H_ +#define OWT_WASM_WEBTRANSPORTSESSION_H_ + +#include "third_party/webrtc/api/call/transport.h" +#include "third_party/webrtc/modules/include/module_common_types.h" + +namespace owt { +namespace wasm { + +class WebTransportSession : public webrtc::Transport, + public webrtc::NackSender { + public: + explicit WebTransportSession() {} + virtual ~WebTransportSession() {} + + // Overrides webrtc::Transport. + bool SendRtp(const uint8_t* packet, + size_t length, + const webrtc::PacketOptions& options) override; + bool SendRtcp(const uint8_t* packet, size_t length) override; + + // Overrides webrtc::NackSender. + void SendNack(const std::vector& sequence_numbers, + bool buffering_allowed) override; +}; + +} // namespace wasm +} // namespace owt + +#endif \ No newline at end of file