Compare commits

...

55 Commits

Author SHA1 Message Date
Fabio Alessandrelli
5bc4b79435 Merge pull request #83 from Faless/bump/beta14
Update to Godot 4.0 beta14.
2023-01-21 16:07:16 +01:00
Fabio Alessandrelli
b7b76dddab Update to Godot 4.0 beta14. 2023-01-21 10:00:01 +01:00
Fabio Alessandrelli
0b1c55ed83 Merge pull request #82 from Faless/bump/beta13
Update to Godot 4.0 beta13.
2023-01-20 12:27:43 +01:00
Fabio Alessandrelli
093ac7d489 Update to Godot 4.0 beta13. 2023-01-18 08:57:07 +01:00
Fabio Alessandrelli
84e9d6dd37 Merge pull request #81 from Faless/bump/beta12
Update to Godot 4.0 beta12.
2023-01-14 04:15:34 +01:00
Fabio Alessandrelli
a49a2b03e1 Revert "[Extension] Fix library resource waiting for Godot auto prefix."
This reverts commit 0c10a7f3e3.
2023-01-13 18:09:17 +01:00
Fabio Alessandrelli
656d3a9964 Update to Godot 4.0 beta12.
Also update thirdparty README with correct libdatachannel version.
2023-01-13 18:06:10 +01:00
Fabio Alessandrelli
2bc2a5d162 Merge pull request #80 from Faless/bump/beta11
Update to Godot 4.0-beta11, libdatachannel v0.18.1
2023-01-12 17:08:36 +01:00
Fabio Alessandrelli
7176344056 Update to Godot 4.0-beta11, libdatachannel v0.18.1
Also update godot-cpp-3.x for better cache support.
2023-01-12 15:59:48 +01:00
Fabio Alessandrelli
7d260b1b32 Merge pull request #79 from Faless/fix/template_debug
[Extension] Fix library resource waiting for Godot auto prefix.
2023-01-12 15:57:10 +01:00
Fabio Alessandrelli
0c10a7f3e3 [Extension] Fix library resource waiting for Godot auto prefix. 2023-01-12 15:12:09 +01:00
Fabio Alessandrelli
40962dbe19 Merge pull request #78 from Faless/build/split_deps
[SCons] Refactor build tools.
2023-01-09 21:48:09 +01:00
Fabio Alessandrelli
bdd9d50745 [SCons] Refactor build tools.
Split dependency builders into separate tools, optimize OpenSSL builds.
2022-12-30 14:34:15 +01:00
Fabio Alessandrelli
5348407de9 Merge pull request #77 from Faless/bump/beta10
Update to Godot 4.0-beta10.
2022-12-29 17:30:51 +01:00
Fabio Alessandrelli
5002991511 Update to Godot 4.0-beta10. 2022-12-29 15:38:48 +01:00
Fabio Alessandrelli
0fc992e0d5 Merge pull request #76 from Faless/bump/beta9
Update to Godot 4.0 beta9
2022-12-19 22:11:33 +01:00
Fabio Alessandrelli
fbadff454c Update to Godot 4.0 beta9 2022-12-19 20:42:56 +01:00
Fabio Alessandrelli
61a6d25c47 Merge pull request #75 from Faless/bump/beta8
Bump godot-cpp to beta8, libdatachannel to v0.18.0
2022-12-10 18:26:25 +01:00
Fabio Alessandrelli
dfaad6f5ba Bump godot-cpp to beta8, libdatachannel to v0.18.0
libdatachannel and libjuice are now released under MPL 2.0.

Dependencies has been refactored under a `thirdparty` folder similar to
what we have in Godot, with a dedicated `thirdparty/README.md`
containing details information on dependencies upstreams, versions, and
licenses.
2022-12-10 17:13:59 +01:00
Fabio Alessandrelli
35bdd5efa7 Merge pull request #73 from Faless/bump/beta6
Update to Godot 4.0 beta6, and bump dependencies.
2022-11-28 20:19:47 +01:00
Fabio Alessandrelli
90aaa0b82e Update to libdatachannel v0.17.12+git. 2022-11-28 17:54:19 +01:00
Fabio Alessandrelli
16f311844b Update to OpenSSL 3.0.7. 2022-11-28 17:54:19 +01:00
Fabio Alessandrelli
dfb34df36e Update to Godot 4.0-beta6 2022-11-28 17:54:19 +01:00
Fabio Alessandrelli
9715e73ae4 Merge pull request #74 from Faless/build/cache_and_paths
[SCons] Fix caching and path detection.
2022-11-28 17:53:10 +01:00
Fabio Alessandrelli
2e9a25bb39 [SCons] Fix caching and path detection.
Add version file depenencies to SSL and RTC targets.

Disable OpenSSL caching since it causes issues as it doesn't properly
cache generated header files.

Add hack to prepend PATH and few other vars (should probably be added
to upstream godot-cpp), and clone scons envs when building ssl/rtc (so
that PATHs are properly setup).
2022-11-28 03:10:20 +01:00
Fabio Alessandrelli
6067addd96 Merge pull request #72 from JonathanPicques/patch-1
Rename osx to macos in webrtc.gdextension
2022-11-26 15:50:07 +01:00
Jonathan Picques
a3fbd33464 Rename osx to macos in webrtc.gdextension
The plugin wouldn't work with Godot beta 5 because it expects a macos key instead of an osx
2022-11-26 13:56:06 +01:00
Fabio Alessandrelli
d697901f81 Merge pull request #70 from Faless/bump/beta5
Update to Godot 4.0-beta5
2022-11-19 11:08:34 +01:00
Fabio Alessandrelli
3eb7d0aa3b Update to Godot 4.0-beta5 2022-11-19 01:43:24 +01:00
Fabio Alessandrelli
ecec40f59e Merge pull request #68 from Faless/bump/beta4
Update to Godot 4.0-beta4.
2022-11-15 00:41:01 +01:00
Fabio Alessandrelli
37a289addf Update to Godot 4.0-beta4. 2022-11-14 22:48:46 +01:00
Fabio Alessandrelli
1436024f59 Merge pull request #65 from Faless/bump/beta3
[Upstream] Update to Godot Beta 3
2022-10-17 22:42:33 +02:00
Fabio Alessandrelli
690b31e7dd [Upstream] Update to Godot beta 3.
Update build targets.
Move release script out of CI yaml.
Disable debug CI builds for Godot 3.x since they are mostly used for
debugging the extension itself.
2022-10-17 20:59:33 +02:00
Fabio Alessandrelli
9b0a5d8b46 [CI] Update actions, add cache for faster builds. 2022-10-16 16:15:42 +02:00
Fabio Alessandrelli
1769730ef0 Merge pull request #63 from Faless/bump/beta2
[CPP] Bump godot-cpp to beta2 headers.
2022-10-10 11:49:36 +02:00
Fabio Alessandrelli
04898f2714 [CPP] Bump godot-cpp to beta2 headers. 2022-10-09 19:42:31 +02:00
Fabio Alessandrelli
d7fc6cde3b Merge pull request #61 from Faless/bump/beta1
[Extension] Update to Godot 4.0 beta 1
2022-09-16 14:39:41 +02:00
Fabio Alessandrelli
1009e8bb1c Add methods to get gathering and signaling state. 2022-09-16 13:07:06 +02:00
Fabio Alessandrelli
5fd359f674 Bump godot-cpp to Godot 4.0 beta1. 2022-09-16 13:07:06 +02:00
Fabio Alessandrelli
822e0532c2 Merge pull request #60 from Faless/bump/alpha16
Update to Godot alpha16
2022-09-11 18:26:08 +02:00
Fabio Alessandrelli
d8cbd14a28 Disable android 32 bits builds. 2022-09-11 15:34:08 +02:00
Fabio Alessandrelli
6e52fce986 Update sources to alpha16 2022-09-11 15:34:08 +02:00
Fabio Alessandrelli
a93af67d75 [godot-cpp] Bump to alpha16. 2022-09-11 14:48:51 +02:00
Fabio Alessandrelli
22ee6435a1 Update to libdatachannel 0.17.10+git. 2022-09-11 14:48:51 +02:00
Fabio Alessandrelli
b5d8a72ef8 Update to OpenSSL 3.0.5 2022-09-11 14:48:51 +02:00
Fabio Alessandrelli
2ad1bf1716 Merge pull request #57 from Faless/bump/4.0_alpha11
Bump godot-cpp to alpha11.
2022-07-06 13:24:04 +02:00
Fabio Alessandrelli
e683c79938 Bump godot-cpp to alpha11. 2022-07-06 11:45:03 +02:00
Fabio Alessandrelli
426ca6cc8e Merge pull request #56 from Faless/fix/write_mode
Fix default data channel write mode to binary.
2022-06-19 13:48:20 +02:00
Fabio Alessandrelli
9b53ef3325 Fix default data channel write mode to binary.
It's the engine expected default, and the least surprising for the users
since it allows any data to be sent, while text mode requires valid UTF.
2022-06-19 12:18:49 +02:00
Fabio Alessandrelli
e41d2903ad Merge pull request #54 from Faless/fix/win32_mingw
[Windows] Fix windows build with mingw.
2022-06-18 16:46:06 +02:00
Fabio Alessandrelli
f315912d85 [Windows] Fix windows build with mingw.
Forces ".dll" extension for library when building with mingw.

Override GDN_EXPORT which is incorrectly defined in the upstream
gdnative headers (3.x) and godot-cpp include (4.0) when building for
windows with mingw.
2022-06-18 14:35:51 +02:00
Fabio Alessandrelli
62ab2e59f8 Merge pull request #53 from Faless/fix/build_deps_target
[SCons] Apply "target" to dependencies.
2022-06-18 03:00:48 +02:00
Fabio Alessandrelli
a2fc27f646 [SCons] Apply "target" to dependencies.
Dependencies used to be built with the default option (release for
OpenSSL, debug for libdachannel).
They now follow the desired target (producing smaller binaries in
release, and bigger in debug).
2022-06-18 02:00:18 +02:00
Fabio Alessandrelli
b2654cc2bd Merge pull request #51 from Faless/libdatachannel_pr
Switch to libdatachannel as RTC library. Add GDExtension support.
2022-06-16 11:32:10 +02:00
Fabio Alessandrelli
e19b370126 Use libdatachannel library, add Godot 4 support. 2022-06-15 21:38:12 +02:00
30 changed files with 1254 additions and 1050 deletions

View File

@@ -1,51 +0,0 @@
name: 'Get WebRTC Library'
description: 'Get pre-build statically linked WebRTC library from Faless/webrtc-builds'
inputs:
repo:
description: 'Base repository'
required: true
default: "godotengine/webrtc-actions"
release:
description: 'Release tag'
required: true
default: '4472-33644-92ba70c'
webrtc-base-name:
description: 'The WebRTC version'
required: true
default: "webrtc-33644-92ba70c"
out-dir:
description: 'Directory where to extract the library'
required: true
default: "webrtc"
platform:
description: 'Platform to download'
required: true
archs:
description: 'Space separated list of architecture to fetch'
required: true
runs:
using: "composite"
steps:
- shell: bash
env:
RTC_BASE_URL: https://github.com/${{ inputs.repo }}/releases/download/${{ inputs.release }}/${{ inputs.webrtc-base-name }}
run: |
cd ${{ inputs.out-dir }}
libplat=${{ inputs.platform }}
if [ "${{ inputs.platform }}" = "windows" ]; then
libplat=win
elif [ "${{ inputs.platform }}" = "osx" ]; then
libplat=mac
fi
for arch in ${{ inputs.archs }}
do
echo "Downloading ${{ env.RTC_BASE_URL }}-${{ inputs.platform }}-${arch}.tar.gz"
curl -L ${{ env.RTC_BASE_URL }}-${libplat}-${arch}.tar.gz -o ${arch}.tar.gz
tar -xzf ${arch}.tar.gz
done
mv lib ${{ inputs.platform }}
ls -l
ls -l *

View File

@@ -1,13 +1,17 @@
name: 🔧 Build -> Package 📦 name: 🔧 Build -> Package 📦
on: [push, pull_request] on: [push, pull_request]
env:
# Only used for the cache key. Increment version to force clean build.
GODOT_BASE_BRANCH: master
jobs: jobs:
static-checks: static-checks:
name: 📊 Static Checks (clang-format, black format, file format) name: 📊 Static Checks (clang-format, black format, file format)
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v3
- name: Install dependencies - name: Install dependencies
run: | run: |
@@ -33,115 +37,119 @@ jobs:
name: 🔧 Build name: 🔧 Build
needs: static-checks needs: static-checks
strategy: strategy:
fail-fast: false
matrix: matrix:
include: include:
# Android # Android
- platform: android - platform: android
arch: 'x86' arch: 'x86_64'
sconsflags: 'android_arch=x86' gdnative_flags: 'android_arch=x86_64'
os: 'ubuntu-20.04' sconsflags: ''
- platform: android
arch: 'x64'
sconsflags: 'android_arch=x86_64'
os: 'ubuntu-20.04'
- platform: android
arch: 'arm'
sconsflags: 'android_arch=armv7'
os: 'ubuntu-20.04' os: 'ubuntu-20.04'
cache-name: android-x86_64
- platform: android - platform: android
arch: 'arm64' arch: 'arm64'
sconsflags: 'android_arch=arm64v8' gdnative_flags: 'android_arch=arm64v8'
sconsflags: ''
os: 'ubuntu-20.04' os: 'ubuntu-20.04'
cache-name: android-arm64
# iOS # iOS
- platform: ios - platform: ios
arch: 'x64' arch: 'x86_64'
sconsflags: 'ios_arch=x86_64 ios_simulator=true' gdnative_flags: 'ios_arch=x86_64'
os: 'macos-latest' sconsflags: 'ios_simulator=true'
- platform: ios os: 'macos-11'
arch: 'arm' cache-name: ios-x86_64-simulator
sconsflags: 'ios_arch=armv7'
os: 'macos-latest'
- platform: ios - platform: ios
arch: 'arm64' arch: 'arm64'
sconsflags: 'ios_arch=arm64' gdnative_flags: 'ios_arch=arm64'
os: 'macos-latest' sconsflags: ''
os: 'macos-11'
cache-name: ios-arm64
# Linux # Linux
- platform: linux - platform: linux
arch: 'x86' arch: 'x86_32'
sconsflags: 'bits=32' gdnative_flags: 'bits=32'
sconsflags: ''
os: 'ubuntu-20.04' os: 'ubuntu-20.04'
cache-name: linux-x86_32
- platform: linux - platform: linux
arch: 'x64' arch: 'x86_64'
sconsflags: 'bits=64' gdnative_flags: 'bits=64'
sconsflags: ''
os: 'ubuntu-20.04' os: 'ubuntu-20.04'
cache-name: linux-x86_64
# macOS # macOS
- platform: osx - platform: macos
arch: 'x64' arch: 'x86_64'
sconsflags: 'bits=64' gdnative_flags: 'macos_arch=x86_64 bits=64'
os: 'macos-latest' sconsflags: ''
- platform: osx os: 'macos-11'
cache-name: macos-x86_64
- platform: macos
gdnative_flags: 'macos_arch=arm64 bits=64'
arch: 'arm64' arch: 'arm64'
sconsflags: 'bits=64 macos_arch=arm64 macos_sdk_path=/Library/Developer/CommandLineTools/SDKs/MacOSX11.1.sdk/' sconsflags: ''
os: 'macos-10.15' os: 'macos-11'
cache-name: macos-arm64
# Windows # Windows
- platform: windows - platform: windows
arch: 'x86' arch: 'x86_32'
sconsflags: 'bits=32' gdnative_flags: 'bits=32'
os: 'windows-latest' sconsflags: 'use_mingw=yes'
os: 'ubuntu-20.04'
msvc_arch: amd64_x86 msvc_arch: amd64_x86
cache-name: win-x86_32
- platform: windows - platform: windows
arch: 'x64' arch: 'x86_64'
sconsflags: 'bits=64' gdnative_flags: 'bits=64'
os: 'windows-latest' sconsflags: 'use_mingw=yes'
os: 'ubuntu-20.04'
msvc_arch: amd64 msvc_arch: amd64
cache-name: win-x86_64
env: env:
SCONSFLAGS: ${{ matrix.sconsflags }} platform=${{ matrix.platform }} --jobs=2 SCONS_CACHE: ${{ github.workspace }}/.scons-cache/
NDK_VERSION: 22b SCONSFLAGS: ${{ matrix.sconsflags }} platform=${{ matrix.platform }} arch=${{ matrix.arch }} --jobs=2
ANDROID_NDK_ROOT: ${{github.workspace}}/android-ndk-r22b
MSVC_VARS: 'C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat'
defaults: defaults:
run: run:
shell: bash shell: bash
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
with: with:
submodules: recursive submodules: recursive
- name: Cache NDK - name: Setup Godot build cache
id: cache-ndk uses: ./godot-cpp/.github/actions/godot-cache
if: ${{ matrix.platform == 'android' }}
uses: actions/cache@v2
with: with:
path: ${{ env.ANDROID_NDK_ROOT }} cache-name: ${{ matrix.cache-name }}
key: ndk-${{ env.NDK_VERSION }} continue-on-error: true
- name: Download NDK - name: Install Windows build dependencies
if: ${{ matrix.platform == 'android' && steps.cache-ndk.outputs.cache-hit != 'true' }}
id: setup-ndk
run: |
cd ${{ github.workspace }}
curl -L https://dl.google.com/android/repository/android-ndk-r${{ env.NDK_VERSION }}-linux-x86_64.zip -o ndk.zip
unzip ndk.zip
ls
- name: Setup MSVC build environment for ${{ matrix.msvc_arch }}
if: ${{ matrix.platform == 'windows' }} if: ${{ matrix.platform == 'windows' }}
run: "'${{ env.MSVC_VARS }}' ${{ matrix.msvc_arch }}" run: |
sudo apt-get update
sudo apt-get install build-essential mingw-w64
sudo update-alternatives --set i686-w64-mingw32-gcc /usr/bin/i686-w64-mingw32-gcc-posix
sudo update-alternatives --set i686-w64-mingw32-g++ /usr/bin/i686-w64-mingw32-g++-posix
sudo update-alternatives --set x86_64-w64-mingw32-gcc /usr/bin/x86_64-w64-mingw32-gcc-posix
sudo update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix
dpkg -l | grep ii | grep mingw
update-alternatives --get-selections | grep mingw
- name: Install Linux build dependencies - name: Install Linux build dependencies
if: ${{ matrix.platform == 'linux' }} if: ${{ matrix.platform == 'linux' }}
run: | run: |
sudo apt-get install build-essential gcc-multilib wget g++-multilib sudo apt-get install build-essential gcc-multilib g++-multilib
- name: Set up Python 3.x - name: Set up Python 3.x
uses: actions/setup-python@v2 uses: actions/setup-python@v4
with: with:
python-version: '3.x' python-version: '3.x'
architecture: 'x64' architecture: 'x64'
@@ -152,71 +160,68 @@ jobs:
python -m pip install scons python -m pip install scons
python --version python --version
scons --version scons --version
cmake --version
- name: Get WebRTC package for ${{ matrix.platform }} - ${{ matrix.arch }} - name: Compile Extension - template_debug - ${{ matrix.platform }} - ${{ matrix.arch }}
uses: ./.github/actions/webrtc-download
with:
platform: ${{ matrix.platform }}
archs: ${{ matrix.arch }}
- name: Fix godot-cpp revision and file names for OSX arm64 build.
if: ${{ matrix.platform == 'osx' && matrix.arch == 'arm64' }}
run: | run: |
cd godot-cpp scons target=template_debug
git checkout e08ecdc28c5409cb5366027227e996c342dcee93
rm -rf src/gen/
rm -rf include/gen/
mkdir bin
ln -s libgodot-cpp.osx.debug.64.a bin/libgodot-cpp.osx.debug.arm64.a
ln -s libgodot-cpp.osx.release.64.a bin/libgodot-cpp.osx.release.arm64.a
- name: Compilation ${{ matrix.platform }} - ${{ matrix.arch }} - godot-cpp - name: Compile Extension - template_release - ${{ matrix.platform }} - ${{ matrix.arch }}
run: | run: |
scons -C godot-cpp target=debug generate_bindings=yes scons target=template_release
scons -C godot-cpp target=release
- name: Compilation ${{ matrix.platform }} - ${{ matrix.arch }} - webrtc-native - name: Compile GDNative - release ${{ matrix.platform }} - ${{ matrix.arch }}
run: | run: |
scons target=debug scons target=release generate_bindings=yes ${{ matrix.gdnative_flags }} godot_version=3
scons target=release
- uses: actions/upload-artifact@v2 - uses: actions/upload-artifact@v3
with: with:
name: ${{ github.job }}-${{ matrix.platform }}-${{ matrix.arch }} name: ${{ github.job }}-${{ matrix.platform }}-${{ matrix.arch }}
path: bin/* path: |
bin/
!bin/thirdparty/
package: package:
name: 📦 Package name: 📦 Package
needs: build needs: build
runs-on: "ubuntu-latest" runs-on: "ubuntu-latest"
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
with:
submodules: recursive
- uses: actions/download-artifact@v2 - uses: actions/download-artifact@v3
with: with:
path: artifacts path: artifacts
- name: Bundle licenses.
run: |
cp LICENSE artifacts/LICENSE.webrtc-native
cp thirdparty/libdatachannel/LICENSE artifacts/LICENSE.libdatachannel
cp thirdparty/openssl/LICENSE.txt artifacts/LICENSE.openssl
cp thirdparty/libdatachannel/deps/libjuice/LICENSE artifacts/LICENSE.libjuice
cp thirdparty/libdatachannel/deps/usrsctp/LICENSE.md artifacts/LICENSE.usrsctp
cp thirdparty/libdatachannel/deps/libsrtp/LICENSE artifacts/LICENSE.libsrtp
cp thirdparty/libdatachannel/deps/json/LICENSE.MIT artifacts/LICENSE.json
cp thirdparty/libdatachannel/deps/plog/LICENSE artifacts/LICENSE.plog
- name: Package artifacts for release - name: Package artifacts for release
env:
DESTINATION: "release"
run: | run: |
mkdir release mkdir release
cd release
for name in webrtc webrtc_debug
do
mkdir -p ${name}/lib/
find ../artifacts -wholename "*/${name}/lib/*" | xargs cp -t ${name}/lib/
find ../artifacts -wholename "*/${name}/${name}.tres" | head -n 1 | xargs cp -t ${name}/
done
zip -r godot-webrtc-native-release.zip webrtc VERSION="extension" TYPE="webrtc" ./misc/scripts/package_release.sh
zip -r godot-webrtc-native-debug.zip webrtc_debug VERSION="gdnative" TYPE="webrtc" ./misc/scripts/package_release.sh
ls -R
- uses: actions/upload-artifact@v2 ls -R release
- uses: actions/upload-artifact@v3
with: with:
name: godot-webrtc-native-debug.zip name: godot-webrtc-extension
path: release/godot-webrtc-native-debug.zip path: release/*-extension-*.zip
- uses: actions/upload-artifact@v2 - uses: actions/upload-artifact@v3
with: with:
name: godot-webrtc-native-release.zip name: godot-webrtc-gdnative
path: release/godot-webrtc-native-release.zip path: release/*-gdnative-*.zip

2
.gitignore vendored
View File

@@ -5,3 +5,5 @@ bin/*
.sconsign.dblite .sconsign.dblite
*.obj *.obj
*.swp *.swp
__pycache__/*
*.pyc

11
.gitmodules vendored
View File

@@ -1,3 +1,12 @@
[submodule "godot-cpp-3.x"]
path = godot-cpp-3.x
url = https://github.com/godotengine/godot-cpp.git
[submodule "godot-cpp"] [submodule "godot-cpp"]
path = godot-cpp path = godot-cpp
url = https://github.com/GodotNativeTools/godot-cpp url = https://github.com/godotengine/godot-cpp.git
[submodule "libdatachannel"]
path = thirdparty/libdatachannel
url = https://github.com/paullouisageneau/libdatachannel.git
[submodule "openssl"]
path = thirdparty/openssl
url = https://github.com/openssl/openssl.git

View File

@@ -7,65 +7,42 @@
### Compiling ### Compiling
Clone this repository with the following command to checkout both [godot-cpp](https://github.com/godotengine/godot-cpp) and [godot-headers](https://github.com/godotengine/godot-headers) dependencies. Clone this repository with the following command to checkout all the dependencies: [godot-cpp](https://github.com/godotengine/godot-cpp), [openssl](https://www.openssl.org/) and [libdatachannel](https://github.com/paullouisageneau/libdatachannel) (and sub-dependencies).
``` ```
$ git clone --recurse-submodules https://github.com/godotengine/webrtc-native.git $ git clone --recurse-submodules https://github.com/godotengine/webrtc-native.git
``` ```
Note that if you wish to use a specific branch, add the -b option to the clone command:
```
$ git clone --recurse-submodules -b 3.2 https://github.com/godotengine/webrtc-native.git
```
If you already checked out the branch use the following commands to update the dependencies: If you already checked out the branch use the following commands to update the dependencies:
``` ```
$ git submodule update --init --recursive $ git submodule update --init --recursive
``` ```
Right now our directory structure should look like this: ### Compiling the extension.
```
webrtc-native/
├─bin/
├─godot-cpp/
| └─godot-headers/
├─src/
└─webrtc/
```
### Compiling the cpp bindings library To build the GDExtension version of the plugin (Godot 4.0) run the following command from the `webrtc-native` folder:
First, we need to compile our cpp bindings library:
```
$ cd godot-cpp
$ scons platform=<your platform> generate_bindings=yes
$ cd ..
```
> Replace `<your platform>` with either `windows`, `linux` or `osx`.
> Include `use_llvm=yes` for using clang++
> Include `target=runtime` to build a runtime build (windows only at the moment)
> Include `target=release` or `target=debug` for release or debug build.
> The resulting library will be created in `godot-cpp/bin/`, take note of its name as it will be different depending on platform.
### Building WebRTC
Building WebRTC is quite a complex task, involves huge downloads and long build times, and produces multiple output libraries that needs to bundled together.
To make things easier, a set of [GitHub Actions](https://docs.github.com/en/actions) are used to generate the library for this plugin, [available in this repository](https://github.com/godotengine/webrtc-actions).
Alternatively, [**download the latest pre-compiled libraries**](https://github.com/godotengine/webrtc-actions/releases).
Extract content of `include` into `webrtc/include` and content of `bin` into `webrtc/<your platform>`
### Compiling the plugin.
``` ```
$ scons platform=<your platform> target=<your target> $ scons platform=<your platform>
``` ```
The generated library and associated `tres` will be placed in `bin/webrtc/` or `bin/webrtc_debug/` according to the desired target. You simply need to copy that folder to the root folder of your project. This will build all the required dependencies into a single shared library.
To build the GDNative version of the plugin (Godot 3.x) run the following command instead:
```
$ scons platform=<your platform> godot_version=3
```
> Replace `<your platform>` with either `windows`, `linux`, `osx`, `android`, or `ios`.
> Include `target=release` or `target=debug` for release or debug build (default is `debug`).
The resulting library and associated `tres` or `gdextension` will be created in `bin/[extension|gdnative]/webrtc[_debug]` depending on the `target` and `godot_version`.
You simply need to copy that folder to the root folder of your project. Note that you will have to build the library for all the desired export platforms.
### License
The `webrtc-native` plugin is licensed under the MIT license (see [LICENSE](https://github.com/godotengine/webrtc-native/blob/master/LICENSE)), while `libdatachannel` and its dependencies are licensed under other permissive open source licences. Please see [`thirdparty/README.md`](thirdparty/README.md) for more informations.

View File

@@ -2,18 +2,6 @@
import os, sys, platform, json, subprocess import os, sys, platform, json, subprocess
if sys.version_info < (3,):
def decode_utf8(x):
return x
else:
import codecs
def decode_utf8(x):
return codecs.utf_8_decode(x)[0]
def add_sources(sources, dirpath, extension): def add_sources(sources, dirpath, extension):
for f in os.listdir(dirpath): for f in os.listdir(dirpath):
@@ -21,367 +9,130 @@ def add_sources(sources, dirpath, extension):
sources.append(dirpath + "/" + f) sources.append(dirpath + "/" + f)
def gen_gdnative_lib(target, source, env): def replace_flags(flags, replaces):
for t in target: for k, v in replaces.items():
with open(t.srcnode().path, "w") as w: if k in flags:
w.write( flags[flags.index(k)] = v
decode_utf8(source[0].get_contents())
.replace("{GDNATIVE_PATH}", os.path.splitext(t.name)[0])
.replace("{TARGET}", env["target"])
)
env = Environment() env = Environment()
opts = Variables(["customs.py"], ARGUMENTS)
opts.Add(EnumVariable("godot_version", "The Godot target version", "4", ["3", "4"]))
opts.Update(env)
target_arch = ARGUMENTS.get("b", ARGUMENTS.get("bits", "64")) if env["godot_version"] == "3":
target_platform = ARGUMENTS.get("p", ARGUMENTS.get("platform", "linux")) if "platform" in ARGUMENTS and ARGUMENTS["platform"] == "macos":
if target_platform == "windows": ARGUMENTS["platform"] = "osx" # compatibility with old osx name
# This makes sure to keep the session environment variables on windows,
# that way you can run scons in a vs 2017 prompt and it will find all the required tools
if target_arch == "64":
env = Environment(ENV=os.environ, TARGET_ARCH="amd64")
else:
env = Environment(ENV=os.environ, TARGET_ARCH="x86")
env.Append(BUILDERS={"GDNativeLibBuilder": Builder(action=gen_gdnative_lib)}) env = SConscript("godot-cpp-3.x/SConstruct")
customs = ["custom.py"] # Patch base env
opts = Variables(customs, ARGUMENTS) replace_flags(env["CCFLAGS"], {
"-mios-simulator-version-min=10.0": "-mios-simulator-version-min=11.0",
"-miphoneos-version-min=10.0": "-miphoneos-version-min=11.0",
"/std:c++14": "/std:c++17",
"-std=c++14": "-std=c++17",
})
opts.Add(BoolVariable("use_llvm", "Use the LLVM compiler", False)) env = env.Clone()
opts.Add(EnumVariable("target", "Compilation target", "debug", ("debug", "release")))
opts.Add(EnumVariable("android_arch", "Target Android architecture", "armv7", ["armv7", "arm64v8", "x86", "x86_64"])) if env["target"] == "debug":
opts.Add( env.Append(CPPDEFINES=["DEBUG_ENABLED"])
"android_api_level",
"Target Android API level",
"18" if ARGUMENTS.get("android_arch", "armv7") in ["armv7", "x86"] else "21",
)
opts.Add(
"ANDROID_NDK_ROOT",
"Path to your Android NDK installation. By default, uses ANDROID_NDK_ROOT from your defined environment variables.",
os.environ.get("ANDROID_NDK_ROOT", None),
)
opts.Add(EnumVariable("ios_arch", "Target iOS architecture", "arm64", ["armv7", "arm64", "x86_64"])) if env["platform"] == "windows" and env["use_mingw"]:
opts.Add(BoolVariable("ios_simulator", "Target iOS Simulator", False)) env.Append(LINKFLAGS=["-static-libgcc"])
opts.Add(
"IPHONEPATH", if env["platform"] == "osx":
"Path to iPhone toolchain", env["platform"] = "macos" # compatibility with old osx name
"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain", ARGUMENTS["platform"] = "macos"
)
opts.Add("macos_sdk_path", "macOS SDK path", "") # Normalize suffix
opts.Add(EnumVariable("macos_arch", "Target macOS architecture", "x86_64", ["x86_64", "arm64"])) if env["platform"] in ["windows", "linux"]:
env["arch"] = "x86_32" if env["bits"] == "32" else "x86_64"
env["arch_suffix"] = env["arch"]
elif env["platform"] == "macos":
env["arch"] = env["macos_arch"]
env["arch_suffix"] = env["arch"]
elif env["platform"] == "ios":
env["arch"] = "arm32" if env["ios_arch"] == "armv7" else env["ios_arch"]
env["arch_suffix"] = env["ios_arch"] + (".simulator" if env["ios_simulator"] else "")
elif env["platform"] == "android":
env["arch"] = {
"armv7": "arm32",
"arm64v8": "arm64",
"x86": "x86_32",
"x86_64": "x86_64",
}[env["android_arch"]]
env["arch_suffix"] = env["arch"]
target_compat = "template_" + env["target"]
env["suffix"] = ".{}.{}.{}".format(env["platform"], target_compat, env["arch_suffix"])
env["debug_symbols"] = False
else:
ARGUMENTS["ios_min_version"] = "11.0"
env = SConscript("godot-cpp/SConstruct").Clone()
# Should probably go to upstream godot-cpp.
# We let SCons build its default ENV as it includes OS-specific things which we don't
# want to have to pull in manually.
# Then we prepend PATH to make it take precedence, while preserving SCons' own entries.
env.PrependENVPath("PATH", os.getenv("PATH"))
env.PrependENVPath("PKG_CONFIG_PATH", os.getenv("PKG_CONFIG_PATH"))
if "TERM" in os.environ: # Used for colored output.
env["ENV"]["TERM"] = os.environ["TERM"]
# Patch mingw SHLIBSUFFIX.
if env["platform"] == "windows" and env["use_mingw"]:
env["SHLIBSUFFIX"] = ".dll"
# Update environment (parse options)
opts.Update(env) opts.Update(env)
target = env["target"] target = env["target"]
if env["godot_version"] == "3":
if target_platform == "android": result_path = os.path.join("bin", "gdnative", "webrtc" if env["target"] == "release" else "webrtc_debug")
target_arch = env["android_arch"]
elif target_platform == "ios":
target_arch = env["ios_arch"]
host_platform = platform.system()
# Local dependency paths, adapt them to your setup
godot_headers = ARGUMENTS.get("headers", "godot-cpp/godot-headers")
godot_cpp_headers = ARGUMENTS.get("godot_cpp_headers", "godot-cpp/include")
godot_cpp_lib_dir = ARGUMENTS.get("godot_cpp_lib_dir", "godot-cpp/bin")
result_path = os.path.join("bin", "webrtc" if env["target"] == "release" else "webrtc_debug", "lib")
lib_prefix = ""
# Convenience check to enforce the use_llvm overrides when CXX is clang(++)
if "CXX" in env and "clang" in os.path.basename(env["CXX"]):
env["use_llvm"] = True
if target_platform == "linux":
env["CXX"] = "g++"
# LLVM
if env["use_llvm"]:
if "clang++" not in os.path.basename(env["CXX"]):
env["CC"] = "clang"
env["CXX"] = "clang++"
env["LINK"] = "clang++"
if env["target"] == "debug":
env.Prepend(CCFLAGS=["-g3"])
env.Append(LINKFLAGS=["-rdynamic"])
else:
env.Prepend(CCFLAGS=["-O3"])
env.Append(CCFLAGS=["-fPIC", "-std=c++14"])
if target_arch == "32":
env.Append(CCFLAGS=["-m32"])
env.Append(LINKFLAGS=["-m32"])
elif target_arch == "64":
env.Append(CCFLAGS=["-m64"])
env.Append(LINKFLAGS=["-m64"])
# i386 does not like static libstdc++
env.Append(LINKFLAGS=["-static-libgcc", "-static-libstdc++"])
elif target_platform == "windows":
if host_platform == "Windows":
lib_prefix = "lib"
env.Append(LINKFLAGS=["/WX"])
if target == "debug":
env.Append(CCFLAGS=["/EHsc", "/D_DEBUG", "/MDd"])
else:
env.Append(CCFLAGS=["/O2", "/EHsc", "/DNDEBUG", "/MD"])
else:
if target_arch == "32":
env["CXX"] = "i686-w64-mingw32-g++"
elif target_arch == "64":
env["CXX"] = "x86_64-w64-mingw32-g++"
if env["target"] == "debug":
env.Append(CCFLAGS=["-Og", "-g"])
elif env["target"] == "release":
env.Append(CCFLAGS=["-O3"])
env.Append(CCFLAGS=["-std=c++14", "-Wwrite-strings"])
env.Append(LINKFLAGS=["--static", "-Wl,--no-undefined", "-static-libgcc", "-static-libstdc++"])
elif target_platform == "osx":
if env["use_llvm"]:
env["CXX"] = "clang++"
# Only 64-bits is supported for OS X
target_arch = "64"
if env["macos_arch"] != "x86_64":
target_arch = "arm64"
env.Append(CCFLAGS=["-std=c++14", "-arch", env["macos_arch"]])
env.Append(LINKFLAGS=["-arch", env["macos_arch"], "-framework", "Cocoa", "-Wl,-undefined,dynamic_lookup"])
if env["macos_sdk_path"]:
env.Append(CCFLAGS=["-isysroot", env["macos_sdk_path"]])
env.Append(LINKFLAGS=["-isysroot", env["macos_sdk_path"]])
if env["target"] == "debug":
env.Append(CCFLAGS=["-Og", "-g"])
elif env["target"] == "release":
env.Append(CCFLAGS=["-O3"])
elif target_platform == "ios":
if env["ios_simulator"]:
sdk_name = "iphonesimulator"
env.Append(CCFLAGS=["-mios-simulator-version-min=10.0"])
env["LIBSUFFIX"] = ".simulator" + env["LIBSUFFIX"]
else:
sdk_name = "iphoneos"
env.Append(CCFLAGS=["-miphoneos-version-min=10.0"])
try:
sdk_path = decode_utf8(subprocess.check_output(["xcrun", "--sdk", sdk_name, "--show-sdk-path"]).strip())
except (subprocess.CalledProcessError, OSError):
raise ValueError("Failed to find SDK path while running xcrun --sdk {} --show-sdk-path.".format(sdk_name))
compiler_path = env["IPHONEPATH"] + "/usr/bin/"
env["ENV"]["PATH"] = env["IPHONEPATH"] + "/Developer/usr/bin/:" + env["ENV"]["PATH"]
env["CC"] = compiler_path + "clang"
env["CXX"] = compiler_path + "clang++"
env["AR"] = compiler_path + "ar"
env["RANLIB"] = compiler_path + "ranlib"
env.Append(CCFLAGS=["-std=c++14", "-arch", env["ios_arch"], "-isysroot", sdk_path])
env.Append(
LINKFLAGS=["-arch", env["ios_arch"], "-Wl,-undefined,dynamic_lookup", "-isysroot", sdk_path, "-F" + sdk_path]
)
if env["target"] == "debug":
env.Append(CCFLAGS=["-Og", "-g"])
elif env["target"] == "release":
env.Append(CCFLAGS=["-O3"])
elif target_platform == "android":
# Verify NDK root
if not "ANDROID_NDK_ROOT" in env:
raise ValueError(
"To build for Android, ANDROID_NDK_ROOT must be defined. Please set ANDROID_NDK_ROOT to the root folder of your Android NDK installation."
)
# Validate API level
api_level = int(env["android_api_level"])
if target_arch in ["arm64v8", "x86_64"] and api_level < 21:
print("WARN: 64-bit Android architectures require an API level of at least 21; setting android_api_level=21")
env["android_api_level"] = "21"
api_level = 21
# Setup toolchain
toolchain = env["ANDROID_NDK_ROOT"] + "/toolchains/llvm/prebuilt/"
if host_platform == "Windows":
toolchain += "windows"
import platform as pltfm
if pltfm.machine().endswith("64"):
toolchain += "-x86_64"
elif host_platform == "Linux":
toolchain += "linux-x86_64"
elif host_platform == "Darwin":
toolchain += "darwin-x86_64"
# Get architecture info
arch_info_table = {
"armv7": {
"march": "armv7-a",
"target": "armv7a-linux-androideabi",
"tool_path": "arm-linux-androideabi",
"compiler_path": "armv7a-linux-androideabi",
"ccflags": ["-mfpu=neon"],
},
"arm64v8": {
"march": "armv8-a",
"target": "aarch64-linux-android",
"tool_path": "aarch64-linux-android",
"compiler_path": "aarch64-linux-android",
"ccflags": [],
},
"x86": {
"march": "i686",
"target": "i686-linux-android",
"tool_path": "i686-linux-android",
"compiler_path": "i686-linux-android",
"ccflags": ["-mstackrealign"],
},
"x86_64": {
"march": "x86-64",
"target": "x86_64-linux-android",
"tool_path": "x86_64-linux-android",
"compiler_path": "x86_64-linux-android",
"ccflags": [],
},
}
arch_info = arch_info_table[target_arch]
# Setup tools
env["CC"] = toolchain + "/bin/clang"
env["CXX"] = toolchain + "/bin/clang++"
env.Append(
CCFLAGS=["--target=" + arch_info["target"] + env["android_api_level"], "-march=" + arch_info["march"], "-fPIC"]
)
env.Append(CCFLAGS=arch_info["ccflags"])
env.Append(LINKFLAGS=["--target=" + arch_info["target"] + env["android_api_level"], "-march=" + arch_info["march"]])
if env["target"] == "debug":
env.Append(CCFLAGS=["-Og", "-g"])
elif env["target"] == "release":
env.Append(CCFLAGS=["-O3"])
else: else:
print("No valid target platform selected.") result_path = os.path.join("bin", "extension", "webrtc")
sys.exit(1)
# Godot CPP bindings # Dependencies
env.Append(CPPPATH=[godot_headers]) for tool in ["cmake", "common", "ssl", "rtc"]:
env.Append(CPPPATH=[godot_cpp_headers, godot_cpp_headers + "/core", godot_cpp_headers + "/gen"]) env.Tool(tool, toolpath=["tools"])
env.Append(LIBPATH=[godot_cpp_lib_dir])
env.Append(
LIBS=[
"%sgodot-cpp.%s.%s.%s%s"
% (lib_prefix, target_platform, target, target_arch, ".simulator" if env["ios_simulator"] else "")
]
)
# WebRTC stuff ssl = env.BuildOpenSSL()
webrtc_dir = "webrtc" env.NoCache(ssl) # Needs refactoring to properly cache generated headers.
lib_name = "libwebrtc_full" rtc = env.BuildLibDataChannel()
lib_path = os.path.join(webrtc_dir, target_platform)
lib_path += {
"32": "/x86",
"64": "/x64",
"armv7": "/arm",
"arm64v8": "/arm64",
"arm64": "/arm64",
"x86": "/x86",
"x86_64": "/x64",
}[target_arch]
if target == "debug":
lib_path += "/Debug"
else:
lib_path += "/Release"
env.Append(CPPPATH=[webrtc_dir + "/include", webrtc_dir + "/include/third_party/abseil-cpp"])
if target_platform == "linux":
env.Append(LIBS=[lib_name, "atomic"])
env.Append(LIBPATH=[lib_path])
# env.Append(CCFLAGS=["-std=c++11"])
env.Append(CCFLAGS=["-DWEBRTC_POSIX", "-DWEBRTC_LINUX"])
env.Append(CCFLAGS=["-DRTC_UNUSED=''", "-DNO_RETURN=''"])
elif target_platform == "windows":
# Mostly VisualStudio
if env["CC"] == "cl":
env.Append(CCFLAGS=["/DWEBRTC_WIN", "/DWIN32_LEAN_AND_MEAN", "/DNOMINMAX", "/DRTC_UNUSED=", "/DNO_RETURN="])
env.Append(LINKFLAGS=[p + env["LIBSUFFIX"] for p in ["secur32", "advapi32", "winmm", lib_name]])
env.Append(LIBPATH=[lib_path])
# Mostly "gcc"
else:
env.Append(
CCFLAGS=[
"-DWINVER=0x0603",
"-D_WIN32_WINNT=0x0603",
"-DWEBRTC_WIN",
"-DWIN32_LEAN_AND_MEAN",
"-DNOMINMAX",
"-DRTC_UNUSED=",
"-DNO_RETURN=",
]
)
env.Append(LINKFLAGS=[p + env["LIBSUFFIX"] for p in ["secur32", "advapi32", "winmm", lib_name]])
env.Append(LIBPATH=[lib_path])
elif target_platform == "osx":
env.Append(LIBS=[lib_name])
env.Append(LIBPATH=[lib_path])
env.Append(CCFLAGS=["-DWEBRTC_POSIX", "-DWEBRTC_MAC"])
env.Append(CCFLAGS=["-DRTC_UNUSED=''", "-DNO_RETURN=''"])
elif target_platform == "ios":
env.Append(LIBS=[lib_name])
env.Append(LIBPATH=[lib_path])
env.Append(CCFLAGS=["-DWEBRTC_POSIX", "-DWEBRTC_MAC", "-DWEBRTC_IOS"])
env.Append(CCFLAGS=["-DRTC_UNUSED=''", "-DNO_RETURN=''"])
elif target_platform == "android":
env.Append(LIBS=["log"])
env.Append(LIBS=[lib_name])
env.Append(LIBPATH=[lib_path])
env.Append(CCFLAGS=["-DWEBRTC_POSIX", "-DWEBRTC_LINUX", "-DWEBRTC_ANDROID"])
env.Append(CCFLAGS=["-DRTC_UNUSED=''", "-DNO_RETURN=''"])
if target_arch == "arm64v8":
env.Append(CCFLAGS=["-DWEBRTC_ARCH_ARM64", "-DWEBRTC_HAS_NEON"])
elif target_arch == "armv7":
env.Append(CCFLAGS=["-DWEBRTC_ARCH_ARM", "-DWEBRTC_ARCH_ARM_V7", "-DWEBRTC_HAS_NEON"])
# Our includes and sources # Our includes and sources
env.Append(CPPPATH=["src/"]) env.Append(CPPPATH=["src/"])
env.Append(CPPDEFINES=["RTC_STATIC"])
sources = [] sources = []
add_sources(sources, "src/", "cpp") sources.append(
add_sources(sources, "src/net/", "cpp") [
"src/WebRTCLibDataChannel.cpp",
"src/WebRTCLibPeerConnection.cpp",
]
)
if env["godot_version"] == "4":
sources.append("src/init_gdextension.cpp")
else:
env.Append(CPPDEFINES=["GDNATIVE_WEBRTC"])
sources.append("src/init_gdnative.cpp")
add_sources(sources, "src/net/", "cpp")
# Suffix env.Depends(sources, [ssl, rtc])
suffix = ".%s.%s" % (target, target_arch)
env["SHOBJSUFFIX"] = suffix + env["SHOBJSUFFIX"]
# Make the shared library # Make the shared library
result_name = "webrtc_native.%s.%s.%s" % (target_platform, target, target_arch) + env["SHLIBSUFFIX"] result_name = "webrtc_native{}{}".format(env["suffix"], env["SHLIBSUFFIX"])
library = env.SharedLibrary(target=os.path.join(result_path, "lib", result_name), source=sources)
library = env.SharedLibrary(target=os.path.join(result_path, result_name), source=sources)
Default(library) Default(library)
# GDNativeLibrary # GDNativeLibrary
gdnlib = "webrtc" if env["godot_version"] == "3":
if target != "release": gdnlib = "webrtc" if target != "debug" else "webrtc_debug"
gdnlib += "_debug" ext = ".tres"
Default(env.GDNativeLibBuilder([os.path.join("bin", gdnlib, gdnlib + ".tres")], ["misc/gdnlib.tres"])) extfile = env.Substfile(os.path.join(result_path, gdnlib + ext), "misc/webrtc" + ext, SUBST_DICT={
"{GDNATIVE_PATH}": gdnlib,
"{TARGET}": "template_" + env["target"],
})
else:
extfile = env.InstallAs(os.path.join(result_path, "webrtc.gdextension"), "misc/webrtc.gdextension")
Default(extfile)

1
godot-cpp-3.x Submodule

Submodule godot-cpp-3.x added at 7c09b5484d

View File

@@ -20,6 +20,8 @@ while IFS= read -rd '' f; do
continue continue
elif [[ "$f" == "godot-cpp"* ]]; then elif [[ "$f" == "godot-cpp"* ]]; then
continue continue
elif [[ "$f" == "misc/patches"* ]]; then
continue
fi fi
# Ensure that files are UTF-8 formatted. # Ensure that files are UTF-8 formatted.
recode UTF-8 "$f" 2> /dev/null recode UTF-8 "$f" 2> /dev/null

32
misc/scripts/package_release.sh Executable file
View File

@@ -0,0 +1,32 @@
#!/bin/bash
set -e
set -x
ARTIFACTS=${ARTIFACTS:-"artifacts"}
DESTINATION=${DESTIONATION:-"release"}
VERSION=${VERSION:-"extension"}
TYPE=${TYPE:-"webrtc"}
mkdir -p ${DESTINATION}
ls -R ${DESTINATION}
DESTDIR="${DESTINATION}/${VERSION}/${TYPE}"
mkdir -p ${DESTDIR}/lib
find "${ARTIFACTS}" -wholename "*/${VERSION}/${TYPE}/lib/*" | xargs cp -t "${DESTDIR}/lib/"
find "${ARTIFACTS}" -wholename "*/LICENSE*" | xargs cp -t "${DESTDIR}/"
if [ $VERSION = "extension" ]; then
find "${ARTIFACTS}" -wholename "*/${VERSION}/${TYPE}/${TYPE}.gdextension" | head -n 1 | xargs cp -t "${DESTDIR}/"
else
find "${ARTIFACTS}" -wholename "*/${VERSION}/${TYPE}/${TYPE}.tres" | head -n 1 | xargs cp -t "${DESTDIR}/"
fi
CURDIR=$(pwd)
cd "${DESTINATION}/${VERSION}"
zip -r ../godot-${VERSION}-${TYPE}.zip ${TYPE}
cd "$CURDIR"
ls -R ${DESTINATION}

27
misc/webrtc.gdextension Normal file
View File

@@ -0,0 +1,27 @@
[configuration]
entry_symbol = "webrtc_extension_init"
[libraries]
linux.debug.x86_64 = "res://webrtc/lib/libwebrtc_native.linux.template_debug.x86_64.so"
linux.debug.x86_32 = "res://webrtc/lib/libwebrtc_native.linux.template_debug.x86_32.so"
macos.debug.x86_64 = "res://webrtc/lib/libwebrtc_native.macos.template_debug.x86_64.dylib"
macos.debug.arm64 = "res://webrtc/lib/libwebrtc_native.macos.template_debug.arm64.dylib"
windows.debug.x86_64 = "res://webrtc/lib/libwebrtc_native.windows.template_debug.x86_64.dll"
windows.debug.x86_32 = "res://webrtc/lib/libwebrtc_native.windows.template_debug.x86_32.dll"
android.debug.arm64 = "res://webrtc/lib/libwebrtc_native.android.template_debug.arm64.so"
android.debug.x86_64 = "res://webrtc/lib/libwebrtc_native.android.template_debug.x86_64.so"
ios.debug.arm64 = "res://webrtc/lib/libwebrtc_native.ios.template_debug.arm64.dylib"
ios.debug.x86_64 = "res://webrtc/lib/libwebrtc_native.ios.template_debug.x86_64.simulator.dylib"
linux.release.x86_64 = "res://webrtc/lib/libwebrtc_native.linux.template_release.x86_64.so"
linux.release.x86_32 = "res://webrtc/lib/libwebrtc_native.linux.template_release.x86_32.so"
macos.release.x86_64 = "res://webrtc/lib/libwebrtc_native.macos.template_release.x86_64.dylib"
macos.release.arm64 = "res://webrtc/lib/libwebrtc_native.macos.template_release.arm64.dylib"
windows.release.x86_64 = "res://webrtc/lib/libwebrtc_native.windows.template_release.x86_64.dll"
windows.release.x86_32 = "res://webrtc/lib/libwebrtc_native.windows.template_release.x86_32.dll"
android.release.arm64 = "res://webrtc/lib/libwebrtc_native.android.template_release.arm64.so"
android.release.x86_64 = "res://webrtc/lib/libwebrtc_native.android.template_release.x86_64.so"
ios.release.arm64 = "res://webrtc/lib/libwebrtc_native.ios.template_release.arm64.dylib"
ios.release.x86_64 = "res://webrtc/lib/libwebrtc_native.ios.template_release.x86_64.simulator.dylib"

View File

@@ -3,21 +3,19 @@
[resource] [resource]
singleton = true singleton = true
reloadable = false reloadable = false
entry/OSX.64 = "res://{GDNATIVE_PATH}/lib/libwebrtc_native.osx.{TARGET}.64.dylib" entry/OSX.64 = "res://{GDNATIVE_PATH}/lib/libwebrtc_native.osx.{TARGET}.x86_64.dylib"
entry/OSX.arm64 = "res://{GDNATIVE_PATH}/lib/libwebrtc_native.osx.{TARGET}.arm64.dylib" entry/OSX.arm64 = "res://{GDNATIVE_PATH}/lib/libwebrtc_native.osx.{TARGET}.arm64.dylib"
entry/Windows.64 = "res://{GDNATIVE_PATH}/lib/webrtc_native.windows.{TARGET}.64.dll" entry/Windows.64 = "res://{GDNATIVE_PATH}/lib/libwebrtc_native.windows.{TARGET}.x86_64.dll"
entry/Windows.32 = "res://{GDNATIVE_PATH}/lib/webrtc_native.windows.{TARGET}.32.dll" entry/Windows.32 = "res://{GDNATIVE_PATH}/lib/libwebrtc_native.windows.{TARGET}.x86_32.dll"
entry/X11.64 = "res://{GDNATIVE_PATH}/lib/libwebrtc_native.linux.{TARGET}.64.so" entry/X11.64 = "res://{GDNATIVE_PATH}/lib/libwebrtc_native.linux.{TARGET}.x86_64.so"
entry/X11.32 = "res://{GDNATIVE_PATH}/lib/libwebrtc_native.linux.{TARGET}.32.so" entry/X11.32 = "res://{GDNATIVE_PATH}/lib/libwebrtc_native.linux.{TARGET}.x86_32.so"
entry/Server.64 = "res://{GDNATIVE_PATH}/lib/libwebrtc_native.linux.{TARGET}.64.so" entry/Server.64 = "res://{GDNATIVE_PATH}/lib/libwebrtc_native.linux.{TARGET}.x86_64.so"
entry/Server.32 = "res://{GDNATIVE_PATH}/lib/libwebrtc_native.linux.{TARGET}.32.so" entry/Server.32 = "res://{GDNATIVE_PATH}/lib/libwebrtc_native.linux.{TARGET}.x86_32.so"
entry/Android.armeabi-v7a = "res://{GDNATIVE_PATH}/lib/libwebrtc_native.android.{TARGET}.armv7.so" entry/Android.arm64-v8a = "res://{GDNATIVE_PATH}/lib/libwebrtc_native.android.{TARGET}.arm64.so"
entry/Android.arm64-v8a = "res://{GDNATIVE_PATH}/lib/libwebrtc_native.android.{TARGET}.arm64v8.so"
entry/Android.x64 = "res://{GDNATIVE_PATH}/lib/libwebrtc_native.android.{TARGET}.x86_64.so" entry/Android.x64 = "res://{GDNATIVE_PATH}/lib/libwebrtc_native.android.{TARGET}.x86_64.so"
entry/Android.x86 = "res://{GDNATIVE_PATH}/lib/libwebrtc_native.android.{TARGET}.x86.so" entry/iOS.armv7 = "res://{GDNATIVE_PATH}/lib/libwebrtc_native.ios.{TARGET}.armv32.dylib"
entry/iOS.armv7 = "res://{GDNATIVE_PATH}/lib/libwebrtc_native.ios.{TARGET}.armv7.dylib"
entry/iOS.arm64 = "res://{GDNATIVE_PATH}/lib/libwebrtc_native.ios.{TARGET}.arm64.dylib" entry/iOS.arm64 = "res://{GDNATIVE_PATH}/lib/libwebrtc_native.ios.{TARGET}.arm64.dylib"
entry/iOS.x86_64 = "res://{GDNATIVE_PATH}/lib/libwebrtc_native.ios.{TARGET}.x86_64.dylib" entry/iOS.x86_64 = "res://{GDNATIVE_PATH}/lib/libwebrtc_native.ios.{TARGET}.x86_64.simulator.dylib"
dependency/Windows.64 = [ ] dependency/Windows.64 = [ ]
dependency/Windows.32 = [ ] dependency/Windows.32 = [ ]
dependency/X11.64 = [ ] dependency/X11.64 = [ ]

View File

@@ -30,138 +30,155 @@
#include "WebRTCLibDataChannel.hpp" #include "WebRTCLibDataChannel.hpp"
#ifdef GDNATIVE_WEBRTC
#include "GDNativeLibrary.hpp" #include "GDNativeLibrary.hpp"
#include "NativeScript.hpp" #include "NativeScript.hpp"
#define ERR_UNAVAILABLE Error::ERR_UNAVAILABLE
#define FAILED Error::FAILED
#define ERR_INVALID_PARAMETER Error::ERR_INVALID_PARAMETER
#define OK Error::OK
#endif
#include <stdio.h>
#include <string.h>
#include <cstring>
using namespace godot;
using namespace godot_webrtc; using namespace godot_webrtc;
// Channel observer
WebRTCLibDataChannel::ChannelObserver::ChannelObserver(WebRTCLibDataChannel *parent) {
this->parent = parent;
}
void WebRTCLibDataChannel::ChannelObserver::OnMessage(const webrtc::DataBuffer &buffer) {
parent->queue_packet(buffer.data.data<uint8_t>(), buffer.data.size());
}
void WebRTCLibDataChannel::ChannelObserver::OnStateChange() {
}
void WebRTCLibDataChannel::ChannelObserver::OnBufferedAmountChange(uint64_t previous_amount) {
}
// DataChannel // DataChannel
WebRTCLibDataChannel *WebRTCLibDataChannel::new_data_channel(rtc::scoped_refptr<webrtc::DataChannelInterface> p_channel) { WebRTCLibDataChannel *WebRTCLibDataChannel::new_data_channel(std::shared_ptr<rtc::DataChannel> p_channel, bool p_negotiated) {
// Invalid channel result in NULL return // Invalid channel result in NULL return
ERR_FAIL_COND_V(p_channel.get() == nullptr, NULL); ERR_FAIL_COND_V(!p_channel, nullptr);
#ifdef GDNATIVE_WEBRTC
// Instance a WebRTCDataChannelGDNative object // Instance a WebRTCDataChannelGDNative object
godot::WebRTCDataChannelGDNative *out = godot::WebRTCDataChannelGDNative::_new(); WebRTCDataChannelGDNative *native = WebRTCDataChannelGDNative::_new();
// Set our implementation as it's script // Set our implementation as its script
godot::NativeScript *script = godot::NativeScript::_new(); NativeScript *script = NativeScript::_new();
script->set_library(godot::detail::get_wrapper<godot::GDNativeLibrary>((godot_object *)godot::gdnlib)); script->set_library(detail::get_wrapper<GDNativeLibrary>((godot_object *)gdnlib));
script->set_class_name("WebRTCLibDataChannel"); script->set_class_name("WebRTCLibDataChannel");
out->set_script(script); native->set_script(script);
WebRTCLibDataChannel *out = native->cast_to<WebRTCLibDataChannel>(native);
// Bind the data channel to the ScriptInstance userdata (our script) #else
WebRTCLibDataChannel *tmp = out->cast_to<WebRTCLibDataChannel>(out); WebRTCLibDataChannel *out = memnew(WebRTCLibDataChannel);
tmp->bind_channel(p_channel); #endif
// Bind the library data channel to our object.
return tmp; out->bind_channel(p_channel, p_negotiated);
return out;
} }
void WebRTCLibDataChannel::bind_channel(rtc::scoped_refptr<webrtc::DataChannelInterface> p_channel) { void WebRTCLibDataChannel::bind_channel(std::shared_ptr<rtc::DataChannel> p_channel, bool p_negotiated) {
ERR_FAIL_COND(p_channel.get() == nullptr); ERR_FAIL_COND(!p_channel);
channel = p_channel; channel = p_channel;
label = p_channel->label(); negotiated = p_negotiated;
protocol = p_channel->protocol();
channel->RegisterObserver(&observer); // Binding this should be fine as long as we call close when going out of scope.
p_channel->onMessage([this](auto message) {
if (std::holds_alternative<rtc::string>(message)) {
rtc::string str = std::get<rtc::string>(message);
queue_packet(reinterpret_cast<const uint8_t *>(str.c_str()), str.size(), true);
} else if (std::holds_alternative<rtc::binary>(message)) {
rtc::binary bin = std::get<rtc::binary>(message);
queue_packet(reinterpret_cast<const uint8_t *>(&bin[0]), bin.size(), false);
} else {
ERR_PRINT("Message parsing bug. Unknown message type.");
}
});
p_channel->onOpen([this]() {
channel_state = STATE_OPEN;
});
p_channel->onClosed([this]() {
channel_state = STATE_CLOSED;
});
p_channel->onError([](auto error) {
ERR_PRINT("Channel Error: " + String(std::string(error).c_str()));
});
} }
void WebRTCLibDataChannel::queue_packet(const uint8_t *data, uint32_t size) { void WebRTCLibDataChannel::queue_packet(const uint8_t *data, uint32_t size, bool p_is_string) {
mutex->lock(); mutex->lock();
godot::PoolByteArray packet; std::vector<uint8_t> packet;
packet.resize(size); packet.resize(size);
{ memcpy(&packet[0], data, size);
godot::PoolByteArray::Write w = packet.write(); packet_queue.push(QueuedPacket(packet, p_is_string));
memcpy(w.ptr(), data, size);
}
packet_queue.push(packet);
mutex->unlock(); mutex->unlock();
} }
void WebRTCLibDataChannel::set_write_mode(godot_int mode) { void WebRTCLibDataChannel::_set_write_mode(WriteMode p_mode) {
ERR_FAIL_COND(p_mode != WRITE_MODE_TEXT && p_mode != WRITE_MODE_BINARY);
write_mode = p_mode;
} }
godot_int WebRTCLibDataChannel::get_write_mode() const { WebRTCDataChannel::WriteMode WebRTCLibDataChannel::_get_write_mode() const {
return 0; return write_mode;
} }
bool WebRTCLibDataChannel::was_string_packet() const { bool WebRTCLibDataChannel::_was_string_packet() const {
return false; return current_packet.second;
} }
WebRTCLibDataChannel::ChannelState WebRTCLibDataChannel::get_ready_state() const { WebRTCDataChannel::ChannelState WebRTCLibDataChannel::_get_ready_state() const {
ERR_FAIL_COND_V(channel.get() == nullptr, STATE_CLOSED); ERR_FAIL_COND_V(!channel, STATE_CLOSED);
return (ChannelState)channel->state(); return channel_state;
} }
const char *WebRTCLibDataChannel::get_label() const { String WebRTCLibDataChannel::_get_label() const {
ERR_FAIL_COND_V(channel.get() == nullptr, ""); ERR_FAIL_COND_V(!channel, "");
return label.c_str(); return channel->label().c_str();
} }
bool WebRTCLibDataChannel::is_ordered() const { bool WebRTCLibDataChannel::_is_ordered() const {
ERR_FAIL_COND_V(channel.get() == nullptr, false); ERR_FAIL_COND_V(!channel, false);
return channel->ordered(); return channel->reliability().unordered == false;
} }
int WebRTCLibDataChannel::get_id() const { int64_t WebRTCLibDataChannel::_get_id() const {
ERR_FAIL_COND_V(channel.get() == nullptr, -1); ERR_FAIL_COND_V(!channel, -1);
return channel->id(); return channel->id().value_or(-1);
} }
int WebRTCLibDataChannel::get_max_packet_life_time() const { int64_t WebRTCLibDataChannel::_get_max_packet_life_time() const {
ERR_FAIL_COND_V(channel.get() == nullptr, 0); ERR_FAIL_COND_V(!channel, 0);
return channel->maxRetransmitTime(); return channel->reliability().type == rtc::Reliability::Type::Timed ? std::get<std::chrono::milliseconds>(channel->reliability().rexmit).count() : -1;
} }
int WebRTCLibDataChannel::get_max_retransmits() const { int64_t WebRTCLibDataChannel::_get_max_retransmits() const {
ERR_FAIL_COND_V(channel.get() == nullptr, 0); ERR_FAIL_COND_V(!channel, 0);
return channel->maxRetransmits(); return channel->reliability().type == rtc::Reliability::Type::Rexmit ? std::get<int>(channel->reliability().rexmit) : -1;
} }
const char *WebRTCLibDataChannel::get_protocol() const { String WebRTCLibDataChannel::_get_protocol() const {
ERR_FAIL_COND_V(channel.get() == nullptr, ""); ERR_FAIL_COND_V(!channel, "");
return protocol.c_str(); return channel->protocol().c_str();
} }
bool WebRTCLibDataChannel::is_negotiated() const { bool WebRTCLibDataChannel::_is_negotiated() const {
ERR_FAIL_COND_V(channel.get() == nullptr, false); ERR_FAIL_COND_V(!channel, false);
return channel->negotiated(); return negotiated;
} }
int WebRTCLibDataChannel::get_buffered_amount() const { int64_t WebRTCLibDataChannel::_get_buffered_amount() const {
ERR_FAIL_COND_V(channel.get() == nullptr, 0); ERR_FAIL_COND_V(!channel, 0);
return channel->buffered_amount(); return channel->bufferedAmount();
} }
godot_error WebRTCLibDataChannel::poll() { Error WebRTCLibDataChannel::_poll() {
return GODOT_OK; return OK;
} }
void WebRTCLibDataChannel::close() { void WebRTCLibDataChannel::_close() try {
if (channel.get() != nullptr) { if (channel) {
channel->Close(); channel->close();
channel->UnregisterObserver();
} }
} catch (...) {
} }
godot_error WebRTCLibDataChannel::get_packet(const uint8_t **r_buffer, int *r_len) { Error WebRTCLibDataChannel::_get_packet(const uint8_t **r_buffer, int32_t *r_len) {
ERR_FAIL_COND_V(packet_queue.empty(), GODOT_ERR_UNAVAILABLE); ERR_FAIL_COND_V(packet_queue.empty(), ERR_UNAVAILABLE);
mutex->lock(); mutex->lock();
@@ -169,47 +186,46 @@ godot_error WebRTCLibDataChannel::get_packet(const uint8_t **r_buffer, int *r_le
current_packet = packet_queue.front(); current_packet = packet_queue.front();
packet_queue.pop(); packet_queue.pop();
// Set out buffer and size (buffer will be gone at next get_packet or close) // Set out buffer and size (buffer will be gone at next get_packet or close)
*r_buffer = current_packet.read().ptr(); *r_buffer = &current_packet.first[0];
*r_len = current_packet.size(); *r_len = current_packet.first.size();
mutex->unlock(); mutex->unlock();
return GODOT_OK; return OK;
} }
godot_error WebRTCLibDataChannel::put_packet(const uint8_t *p_buffer, int p_len) { Error WebRTCLibDataChannel::_put_packet(const uint8_t *p_buffer, int64_t p_len) try {
ERR_FAIL_COND_V(channel.get() == nullptr, GODOT_ERR_UNAVAILABLE); ERR_FAIL_COND_V(!channel, FAILED);
ERR_FAIL_COND_V(channel->isClosed(), FAILED);
webrtc::DataBuffer webrtc_buffer(rtc::CopyOnWriteBuffer(p_buffer, p_len), true); if (write_mode == WRITE_MODE_TEXT) {
ERR_FAIL_COND_V(!channel->Send(webrtc_buffer), GODOT_FAILED); std::string str(p_len, '\x00');
std::strncpy(str.data(), (const char *)p_buffer, p_len);
return GODOT_OK; channel->send(str);
} else if (write_mode == WRITE_MODE_BINARY) {
channel->send(reinterpret_cast<const std::byte *>(p_buffer), p_len);
} else {
ERR_FAIL_V(ERR_INVALID_PARAMETER);
}
return OK;
} catch (const std::exception &e) {
ERR_PRINT(e.what());
ERR_FAIL_V(FAILED);
} }
godot_int WebRTCLibDataChannel::get_available_packet_count() const { int64_t WebRTCLibDataChannel::_get_available_packet_count() const {
return packet_queue.size(); return packet_queue.size();
} }
godot_int WebRTCLibDataChannel::get_max_packet_size() const { int64_t WebRTCLibDataChannel::_get_max_packet_size() const {
return 1200; return 16384; // See RFC-8831 section 6.6: https://datatracker.ietf.org/doc/rfc8831/
} }
void WebRTCLibDataChannel::_register_methods() { WebRTCLibDataChannel::WebRTCLibDataChannel() {
}
void WebRTCLibDataChannel::_init() {
register_interface(&interface);
}
WebRTCLibDataChannel::WebRTCLibDataChannel() :
observer(this) {
mutex = new std::mutex; mutex = new std::mutex;
} }
WebRTCLibDataChannel::~WebRTCLibDataChannel() { WebRTCLibDataChannel::~WebRTCLibDataChannel() {
close(); _close();
if (_owner) { channel = nullptr;
register_interface(NULL);
}
delete mutex; delete mutex;
} }

View File

@@ -31,72 +31,75 @@
#ifndef WEBRTC_DATA_CHANNEL_H #ifndef WEBRTC_DATA_CHANNEL_H
#define WEBRTC_DATA_CHANNEL_H #define WEBRTC_DATA_CHANNEL_H
#ifdef GDNATIVE_WEBRTC
#include <Godot.hpp> // Godot.hpp must go first, or windows builds breaks #include <Godot.hpp> // Godot.hpp must go first, or windows builds breaks
#include "api/peer_connection_interface.h" // interface for all things needed from WebRTC
#include "media/base/media_engine.h" // needed for CreateModularPeerConnectionFactory
#include "PoolArrays.hpp"
#include "net/WebRTCDataChannelNative.hpp" #include "net/WebRTCDataChannelNative.hpp"
#define WebRTCDataChannelExtension WebRTCDataChannelNative
#if !defined(GDCLASS)
#define GDCLASS(arg1, arg2) GODOT_CLASS(arg1, arg2)
#endif
#else
#include <godot_cpp/core/binder_common.hpp>
#include <godot_cpp/classes/global_constants_binds.hpp>
#include <godot_cpp/classes/web_rtc_data_channel_extension.hpp>
#endif
#include <mutex> #include <mutex>
#include <queue>
#include <utility>
#include "rtc/rtc.hpp"
namespace godot_webrtc { namespace godot_webrtc {
class WebRTCLibDataChannel : public WebRTCDataChannelNative { class WebRTCLibDataChannel : public godot::WebRTCDataChannelExtension {
GODOT_CLASS(WebRTCLibDataChannel, WebRTCDataChannelNative); GDCLASS(WebRTCLibDataChannel, WebRTCDataChannelExtension);
private: private:
class ChannelObserver : public webrtc::DataChannelObserver { using QueuedPacket = std::pair<std::vector<uint8_t>, bool>;
public:
WebRTCLibDataChannel *parent;
ChannelObserver(WebRTCLibDataChannel *parent);
void OnMessage(const webrtc::DataBuffer &buffer) override;
void OnStateChange() override; // UNUSED
void OnBufferedAmountChange(uint64_t previous_amount) override; // UNUSED
};
ChannelObserver observer;
rtc::scoped_refptr<webrtc::DataChannelInterface> channel;
std::mutex *mutex; std::mutex *mutex;
std::queue<godot::PoolByteArray> packet_queue; std::queue<QueuedPacket> packet_queue;
godot::PoolByteArray current_packet; QueuedPacket current_packet;
std::string label; std::shared_ptr<rtc::DataChannel> channel = nullptr;
std::string protocol;
WriteMode write_mode = WRITE_MODE_BINARY;
ChannelState channel_state = STATE_CONNECTING;
bool negotiated = false;
void queue_packet(const uint8_t *data, uint32_t size, bool p_is_string);
void bind_channel(std::shared_ptr<rtc::DataChannel> p_channel, bool p_negotiated);
protected:
static void _bind_methods() {}
public: public:
static WebRTCLibDataChannel *new_data_channel(rtc::scoped_refptr<webrtc::DataChannelInterface> p_channel); static WebRTCLibDataChannel *new_data_channel(std::shared_ptr<rtc::DataChannel> p_channel, bool p_negotiated);
static void _register_methods();
void _init();
void bind_channel(rtc::scoped_refptr<webrtc::DataChannelInterface> p_channel);
void queue_packet(const uint8_t *data, uint32_t size);
/* WebRTCDataChannel */
void set_write_mode(godot_int mode);
godot_int get_write_mode() const;
bool was_string_packet() const;
ChannelState get_ready_state() const;
const char *get_label() const;
bool is_ordered() const;
int get_id() const;
int get_max_packet_life_time() const;
int get_max_retransmits() const;
const char *get_protocol() const;
bool is_negotiated() const;
int get_buffered_amount() const;
godot_error poll();
void close();
/* PacketPeer */ /* PacketPeer */
virtual godot_error get_packet(const uint8_t **r_buffer, int *r_len); virtual godot::Error _get_packet(const uint8_t **r_buffer, int32_t *r_len) override;
virtual godot_error put_packet(const uint8_t *p_buffer, int p_len); virtual godot::Error _put_packet(const uint8_t *p_buffer, int64_t p_len) override;
virtual godot_int get_available_packet_count() const; virtual int64_t _get_available_packet_count() const override;
virtual godot_int get_max_packet_size() const; virtual int64_t _get_max_packet_size() const override;
/* WebRTCDataChannel */
godot::Error _poll() override;
void _close() override;
void _set_write_mode(WriteMode p_mode) override;
WriteMode _get_write_mode() const override;
bool _was_string_packet() const override;
ChannelState _get_ready_state() const override;
godot::String _get_label() const override;
bool _is_ordered() const override;
int64_t _get_id() const override;
int64_t _get_max_packet_life_time() const override;
int64_t _get_max_retransmits() const override;
godot::String _get_protocol() const override;
bool _is_negotiated() const override;
int64_t _get_buffered_amount() const override;
WebRTCLibDataChannel(); WebRTCLibDataChannel();
~WebRTCLibDataChannel(); ~WebRTCLibDataChannel();

View File

@@ -29,214 +29,223 @@
/*************************************************************************/ /*************************************************************************/
#include "WebRTCLibPeerConnection.hpp" #include "WebRTCLibPeerConnection.hpp"
#include "WebRTCDataChannel.hpp"
#include "WebRTCDataChannelGDNative.hpp"
#include "WebRTCLibDataChannel.hpp" #include "WebRTCLibDataChannel.hpp"
using namespace godot;
using namespace godot_webrtc; using namespace godot_webrtc;
std::unique_ptr<rtc::Thread> WebRTCLibPeerConnection::signaling_thread = nullptr; #ifdef GDNATIVE_WEBRTC
#define OK Error::OK
// PeerConnectionObserver #define FAILED Error::FAILED
void WebRTCLibPeerConnection::GodotPCO::OnIceCandidate(const webrtc::IceCandidateInterface *candidate) { #define ERR_UNCONFIGURED Error::ERR_UNCONFIGURED
godot::Dictionary candidateSDP; #define ERR_INVALID_PARAMETER Error::ERR_INVALID_PARAMETER
godot::String candidateSdpMidName = candidate->sdp_mid().c_str(); #endif
int candidateSdpMlineIndexName = candidate->sdp_mline_index();
std::string sdp;
candidate->ToString(&sdp);
godot::String candidateSdpName = sdp.c_str();
parent->queue_signal("ice_candidate_created", 3, candidateSdpMidName, candidateSdpMlineIndexName, candidateSdpName);
}
// SetSessionDescriptionObserver
void WebRTCLibPeerConnection::GodotSSDO::OnSuccess() {
if (make_offer) {
make_offer = false;
parent->peer_connection->CreateAnswer(parent->ptr_csdo, webrtc::PeerConnectionInterface::RTCOfferAnswerOptions());
}
}
// CreateSessionDescriptionObserver
void WebRTCLibPeerConnection::GodotCSDO::OnSuccess(webrtc::SessionDescriptionInterface *desc) {
// serialize this offer and send it to the remote peer:
std::string sdp;
desc->ToString(&sdp);
parent->queue_signal("session_description_created", 2, desc->type().c_str(), sdp.c_str());
}
void WebRTCLibPeerConnection::initialize_signaling() { void WebRTCLibPeerConnection::initialize_signaling() {
if (signaling_thread.get() == nullptr) { #ifdef DEBUG_ENABLED
signaling_thread = rtc::Thread::Create(); rtc::InitLogger(rtc::LogLevel::Debug);
} #endif
signaling_thread->Start();
} }
void WebRTCLibPeerConnection::deinitialize_signaling() { void WebRTCLibPeerConnection::deinitialize_signaling() {
if (signaling_thread.get() != nullptr) {
signaling_thread->Stop();
}
} }
godot_error _parse_ice_server(webrtc::PeerConnectionInterface::RTCConfiguration &r_config, godot::Dictionary p_server) { Error WebRTCLibPeerConnection::_parse_ice_server(rtc::Configuration &r_config, Dictionary p_server) {
godot::Variant v; ERR_FAIL_COND_V(!p_server.has("urls"), ERR_INVALID_PARAMETER);
webrtc::PeerConnectionInterface::IceServer ice_server;
godot::String url;
ERR_FAIL_COND_V(!p_server.has("urls"), GODOT_ERR_INVALID_PARAMETER);
// Parse mandatory URL // Parse mandatory URL
v = p_server["urls"]; Array urls;
if (v.get_type() == godot::Variant::STRING) { Variant urls_var = p_server["urls"];
url = v; if (urls_var.get_type() == Variant::STRING) {
ice_server.urls.push_back(url.utf8().get_data()); urls.push_back(urls_var);
} else if (v.get_type() == godot::Variant::ARRAY) { } else if (urls_var.get_type() == Variant::ARRAY) {
godot::Array names = v; urls = urls_var;
for (int j = 0; j < names.size(); j++) {
v = names[j];
ERR_FAIL_COND_V(v.get_type() != godot::Variant::STRING, GODOT_ERR_INVALID_PARAMETER);
url = v;
ice_server.urls.push_back(url.utf8().get_data());
}
} else { } else {
ERR_FAIL_V(GODOT_ERR_INVALID_PARAMETER); ERR_FAIL_V(ERR_INVALID_PARAMETER);
} }
// Parse credentials (only meaningful for TURN, only support password) // Parse credentials (only meaningful for TURN, only support password)
if (p_server.has("username") && (v = p_server["username"]) && v.get_type() == godot::Variant::STRING) { String username;
ice_server.username = (v.operator godot::String()).utf8().get_data(); String credential;
if (p_server.has("username") && p_server["username"].get_type() == Variant::STRING) {
username = p_server["username"];
} }
if (p_server.has("credential") && (v = p_server["credential"]) && v.get_type() == godot::Variant::STRING) { if (p_server.has("credential") && p_server["credential"].get_type() == Variant::STRING) {
ice_server.password = (v.operator godot::String()).utf8().get_data(); credential = p_server["credential"];
} }
for (int i = 0; i < urls.size(); i++) {
r_config.servers.push_back(ice_server); rtc::IceServer srv(urls[i].operator String().utf8().get_data());
return GODOT_OK; srv.username = username.utf8().get_data();
srv.password = credential.utf8().get_data();
r_config.iceServers.push_back(srv);
}
return OK;
} }
godot_error _parse_channel_config(webrtc::DataChannelInit &r_config, godot::Dictionary p_dict) { Error WebRTCLibPeerConnection::_parse_channel_config(rtc::DataChannelInit &r_config, const Dictionary &p_dict) {
godot::Variant v; Variant nil;
#define _SET_N(PROP, PNAME, TYPE) \ Variant v;
if (p_dict.has(#PROP)) { \ if (p_dict.has("negotiated")) {
v = p_dict[#PROP]; \ r_config.negotiated = p_dict["negotiated"].operator bool();
if (v.get_type() == godot::Variant::TYPE) \
r_config.PNAME = v; \
} }
#define _SET(PROP, TYPE) _SET_N(PROP, PROP, TYPE) if (p_dict.has("id")) {
_SET(negotiated, BOOL); r_config.id = uint16_t(p_dict["id"].operator int32_t());
_SET(id, INT);
_SET_N(maxPacketLifeTime, maxRetransmitTime, INT);
_SET(maxRetransmits, INT);
_SET(ordered, BOOL);
#undef _SET
if (p_dict.has("protocol") && (v = p_dict["protocol"]) && v.get_type() == godot::Variant::STRING) {
r_config.protocol = v.operator godot::String().utf8().get_data();
} }
// If negotiated it must have an ID, and ID only makes sense when negotiated.
// ID makes sense only when negotiated is true (and must be set in that case) ERR_FAIL_COND_V(r_config.negotiated != r_config.id.has_value(), ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V(r_config.negotiated ? r_config.id == -1 : r_config.id != -1, GODOT_ERR_INVALID_PARAMETER); // Channels cannot be both time-constrained and retry-constrained.
// Only one of maxRetransmits and maxRetransmitTime can be set on a channel. ERR_FAIL_COND_V(p_dict.has("maxPacketLifeTime") && p_dict.has("maxRetransmits"), ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V(r_config.maxRetransmits && r_config.maxRetransmitTime, GODOT_ERR_INVALID_PARAMETER); if (p_dict.has("maxPacketLifeTime")) {
return GODOT_OK; r_config.reliability.type = rtc::Reliability::Type::Timed;
r_config.reliability.rexmit = std::chrono::milliseconds(p_dict["maxPacketLifeTime"].operator int32_t());
} else if (p_dict.has("maxRetransmits")) {
r_config.reliability.type = rtc::Reliability::Type::Rexmit;
r_config.reliability.rexmit = p_dict["maxRetransmits"].operator int32_t();
}
if (p_dict.has("ordered") && p_dict["ordered"].operator bool() == false) {
r_config.reliability.unordered = true;
}
if (p_dict.has("protocol")) {
r_config.protocol = p_dict["protocol"].operator String().utf8().get_data();
}
return OK;
} }
WebRTCLibPeerConnection::ConnectionState WebRTCLibPeerConnection::get_connection_state() const { WebRTCPeerConnection::ConnectionState WebRTCLibPeerConnection::_get_connection_state() const {
ERR_FAIL_COND_V(peer_connection.get() == nullptr, STATE_CLOSED); ERR_FAIL_COND_V(peer_connection == nullptr, STATE_CLOSED);
webrtc::PeerConnectionInterface::IceConnectionState state = peer_connection->ice_connection_state(); rtc::PeerConnection::State state = peer_connection->state();
switch (state) { switch (state) {
case webrtc::PeerConnectionInterface::kIceConnectionNew: case rtc::PeerConnection::State::New:
return STATE_NEW; return STATE_NEW;
case webrtc::PeerConnectionInterface::kIceConnectionChecking: case rtc::PeerConnection::State::Connecting:
return STATE_CONNECTING; return STATE_CONNECTING;
case webrtc::PeerConnectionInterface::kIceConnectionConnected: case rtc::PeerConnection::State::Connected:
return STATE_CONNECTED; return STATE_CONNECTED;
case webrtc::PeerConnectionInterface::kIceConnectionCompleted: case rtc::PeerConnection::State::Disconnected:
return STATE_CONNECTED;
case webrtc::PeerConnectionInterface::kIceConnectionFailed:
return STATE_FAILED;
case webrtc::PeerConnectionInterface::kIceConnectionDisconnected:
return STATE_DISCONNECTED; return STATE_DISCONNECTED;
case webrtc::PeerConnectionInterface::kIceConnectionClosed: case rtc::PeerConnection::State::Failed:
return STATE_CLOSED; return STATE_FAILED;
default: default:
return STATE_CLOSED; return STATE_CLOSED;
} }
} }
godot_error WebRTCLibPeerConnection::initialize(const godot_dictionary *p_config) { WebRTCLibPeerConnection::GatheringState WebRTCLibPeerConnection::_get_gathering_state() const {
webrtc::PeerConnectionInterface::RTCConfiguration config; ERR_FAIL_COND_V(peer_connection == nullptr, GATHERING_STATE_NEW);
godot::Dictionary d = *(godot::Dictionary *)p_config;
godot::Variant v; rtc::PeerConnection::GatheringState state = peer_connection->gatheringState();
if (d.has("iceServers") && (v = d["iceServers"]) && v.get_type() == godot::Variant::ARRAY) { switch (state) {
godot::Array servers = v; case rtc::PeerConnection::GatheringState::New:
return GATHERING_STATE_NEW;
case rtc::PeerConnection::GatheringState::InProgress:
return GATHERING_STATE_GATHERING;
case rtc::PeerConnection::GatheringState::Complete:
return GATHERING_STATE_COMPLETE;
default:
return GATHERING_STATE_NEW;
}
}
WebRTCLibPeerConnection::SignalingState WebRTCLibPeerConnection::_get_signaling_state() const {
ERR_FAIL_COND_V(peer_connection == nullptr, SIGNALING_STATE_CLOSED);
rtc::PeerConnection::SignalingState state = peer_connection->signalingState();
switch (state) {
case rtc::PeerConnection::SignalingState::Stable:
return SIGNALING_STATE_STABLE;
case rtc::PeerConnection::SignalingState::HaveLocalOffer:
return SIGNALING_STATE_HAVE_LOCAL_OFFER;
case rtc::PeerConnection::SignalingState::HaveRemoteOffer:
return SIGNALING_STATE_HAVE_REMOTE_OFFER;
case rtc::PeerConnection::SignalingState::HaveLocalPranswer:
return SIGNALING_STATE_HAVE_LOCAL_PRANSWER;
case rtc::PeerConnection::SignalingState::HaveRemotePranswer:
return SIGNALING_STATE_HAVE_REMOTE_PRANSWER;
default:
return SIGNALING_STATE_CLOSED;
}
}
Error WebRTCLibPeerConnection::_initialize(const Dictionary &p_config) {
rtc::Configuration config = {};
if (p_config.has("iceServers") && p_config["iceServers"].get_type() == Variant::ARRAY) {
Array servers = p_config["iceServers"];
for (int i = 0; i < servers.size(); i++) { for (int i = 0; i < servers.size(); i++) {
v = servers[i]; ERR_FAIL_COND_V(servers[i].get_type() != Variant::DICTIONARY, ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V(v.get_type() != godot::Variant::DICTIONARY, GODOT_ERR_INVALID_PARAMETER); Dictionary server = servers[i];
godot_error err; Error err = _parse_ice_server(config, server);
godot::Dictionary server = v; ERR_FAIL_COND_V(err != OK, FAILED);
err = _parse_ice_server(config, server);
ERR_FAIL_COND_V(err != GODOT_OK, err);
} }
} }
return _create_pc(config); return _create_pc(config);
} }
godot_object *WebRTCLibPeerConnection::create_data_channel(const char *p_channel, const godot_dictionary *p_channel_config) { Object *WebRTCLibPeerConnection::_create_data_channel(const String &p_channel, const Dictionary &p_channel_config) try {
ERR_FAIL_COND_V(peer_connection.get() == nullptr, NULL); ERR_FAIL_COND_V(!peer_connection, nullptr);
// Read config from dictionary // Read config from dictionary
webrtc::DataChannelInit config; rtc::DataChannelInit config;
godot::Dictionary d = *(godot::Dictionary *)p_channel_config;
godot_error err = _parse_channel_config(config, d);
ERR_FAIL_COND_V(err != GODOT_OK, NULL);
WebRTCLibDataChannel *wrapper = WebRTCLibDataChannel::new_data_channel(peer_connection->CreateDataChannel(p_channel, &config)); Error err = _parse_channel_config(config, p_channel_config);
ERR_FAIL_COND_V(wrapper == NULL, NULL); ERR_FAIL_COND_V(err != OK, nullptr);
return wrapper->_owner;
std::shared_ptr<rtc::DataChannel> ch = peer_connection->createDataChannel(p_channel.utf8().get_data(), config);
ERR_FAIL_COND_V(ch == nullptr, nullptr);
WebRTCLibDataChannel *wrapper = WebRTCLibDataChannel::new_data_channel(ch, ch->id().has_value());
ERR_FAIL_COND_V(wrapper == nullptr, nullptr);
return wrapper;
} catch (const std::exception &e) {
ERR_PRINT(e.what());
ERR_FAIL_V(nullptr);
} }
godot_error WebRTCLibPeerConnection::create_offer() { Error WebRTCLibPeerConnection::_create_offer() try {
ERR_FAIL_COND_V(peer_connection.get() == nullptr, GODOT_ERR_UNCONFIGURED); ERR_FAIL_COND_V(!peer_connection, ERR_UNCONFIGURED);
peer_connection->CreateOffer(ptr_csdo, webrtc::PeerConnectionInterface::RTCOfferAnswerOptions()); ERR_FAIL_COND_V(_get_connection_state() != STATE_NEW, FAILED);
return GODOT_OK; peer_connection->setLocalDescription(rtc::Description::Type::Offer);
return OK;
} catch (const std::exception &e) {
ERR_PRINT(e.what());
ERR_FAIL_V(FAILED);
} }
#define _MAKE_DESC(TYPE, SDP) webrtc::CreateSessionDescription((godot::String(TYPE) == godot::String("offer") ? webrtc::SdpType::kOffer : webrtc::SdpType::kAnswer), SDP) Error WebRTCLibPeerConnection::_set_remote_description(const String &p_type, const String &p_sdp) try {
godot_error WebRTCLibPeerConnection::set_remote_description(const char *type, const char *sdp) { ERR_FAIL_COND_V(!peer_connection, ERR_UNCONFIGURED);
ERR_FAIL_COND_V(peer_connection.get() == nullptr, GODOT_ERR_UNCONFIGURED); std::string sdp(p_sdp.utf8().get_data());
std::unique_ptr<webrtc::SessionDescriptionInterface> desc = _MAKE_DESC(type, sdp); std::string type(p_type.utf8().get_data());
if (desc->GetType() == webrtc::SdpType::kOffer) { rtc::Description desc(sdp, type);
ptr_ssdo->make_offer = true; peer_connection->setRemoteDescription(desc);
// Automatically create the answer.
if (p_type == String("offer")) {
peer_connection->setLocalDescription(rtc::Description::Type::Answer);
} }
peer_connection->SetRemoteDescription(ptr_ssdo, desc.release()); return OK;
return GODOT_OK; } catch (const std::exception &e) {
ERR_PRINT(e.what());
ERR_FAIL_V(FAILED);
} }
godot_error WebRTCLibPeerConnection::set_local_description(const char *type, const char *sdp) { Error WebRTCLibPeerConnection::_set_local_description(const String &p_type, const String &p_sdp) {
ERR_FAIL_COND_V(peer_connection.get() == nullptr, GODOT_ERR_UNCONFIGURED); ERR_FAIL_COND_V(!peer_connection, ERR_UNCONFIGURED);
std::unique_ptr<webrtc::SessionDescriptionInterface> desc = _MAKE_DESC(type, sdp); // XXX Library quirk. It doesn't seem possible to create offers/answers without setting the local description.
peer_connection->SetLocalDescription(ptr_ssdo, desc.release()); // Ignore this call for now to avoid crash (it's already set automatically!).
return GODOT_OK; //peer_connection->setLocalDescription(p_type == String("offer") ? rtc::Description::Type::Offer : rtc::Description::Type::Answer);
} return OK;
#undef _MAKE_DESC
godot_error WebRTCLibPeerConnection::add_ice_candidate(const char *sdpMidName, int sdpMlineIndexName, const char *sdpName) {
ERR_FAIL_COND_V(peer_connection.get() == nullptr, GODOT_ERR_UNCONFIGURED);
webrtc::SdpParseError *error = nullptr;
webrtc::IceCandidateInterface *candidate = webrtc::CreateIceCandidate(
sdpMidName,
sdpMlineIndexName,
sdpName,
error);
ERR_FAIL_COND_V(error || !candidate, GODOT_ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V(!peer_connection->AddIceCandidate(candidate), GODOT_FAILED);
return GODOT_OK;
} }
godot_error WebRTCLibPeerConnection::poll() { Error WebRTCLibPeerConnection::_add_ice_candidate(const String &sdpMidName, int64_t sdpMlineIndexName, const String &sdpName) try {
ERR_FAIL_COND_V(peer_connection.get() == nullptr, GODOT_ERR_UNCONFIGURED); ERR_FAIL_COND_V(!peer_connection, ERR_UNCONFIGURED);
rtc::Candidate candidate(sdpName.utf8().get_data(), sdpMidName.utf8().get_data());
peer_connection->addRemoteCandidate(candidate);
return OK;
} catch (const std::exception &e) {
ERR_PRINT(e.what());
ERR_FAIL_V(FAILED);
}
Error WebRTCLibPeerConnection::_poll() {
ERR_FAIL_COND_V(!peer_connection, ERR_UNCONFIGURED);
while (!signal_queue.empty()) { while (!signal_queue.empty()) {
mutex_signal_queue->lock(); mutex_signal_queue->lock();
@@ -245,68 +254,83 @@ godot_error WebRTCLibPeerConnection::poll() {
mutex_signal_queue->unlock(); mutex_signal_queue->unlock();
signal.emit(this); signal.emit(this);
} }
return GODOT_OK; return OK;
} }
void WebRTCLibPeerConnection::close() { void WebRTCLibPeerConnection::_close() {
if (peer_connection.get() != nullptr) { if (peer_connection != nullptr) {
peer_connection->Close(); try {
peer_connection->close();
} catch (...) {
}
} }
peer_connection = nullptr;
while (!signal_queue.empty()) { while (!signal_queue.empty()) {
signal_queue.pop(); signal_queue.pop();
} }
} }
void WebRTCLibPeerConnection::_register_methods() {
}
void WebRTCLibPeerConnection::_init() { void WebRTCLibPeerConnection::_init() {
#ifdef GDNATIVE_WEBRTC
register_interface(&interface); register_interface(&interface);
#endif
// initialize variables:
mutex_signal_queue = new std::mutex; mutex_signal_queue = new std::mutex;
// create a PeerConnectionFactoryInterface: _initialize(Dictionary());
webrtc::PeerConnectionFactoryDependencies deps;
ERR_FAIL_COND(signaling_thread.get() == nullptr);
deps.signaling_thread = signaling_thread.get();
pc_factory = webrtc::CreateModularPeerConnectionFactory(std::move(deps));
// Create peer connection with default configuration.
webrtc::PeerConnectionInterface::RTCConfiguration config;
_create_pc(config);
} }
godot_error WebRTCLibPeerConnection::_create_pc(webrtc::PeerConnectionInterface::RTCConfiguration &config) { Error WebRTCLibPeerConnection::_create_pc(rtc::Configuration &r_config) try {
ERR_FAIL_COND_V(pc_factory.get() == nullptr, GODOT_ERR_BUG); // Prevents libdatachannel from automatically creating offers.
peer_connection = nullptr; r_config.disableAutoNegotiation = true;
peer_connection = pc_factory->CreatePeerConnection(config, nullptr, nullptr, &pco);
if (peer_connection.get() == nullptr) { // PeerConnection couldn't be created. Fail the method call. peer_connection = std::make_shared<rtc::PeerConnection>(r_config);
ERR_PRINT("PeerConnection could not be created"); ERR_FAIL_COND_V(!peer_connection, FAILED);
return GODOT_FAILED;
} // Binding this should be fine as long as we call close when going out of scope.
return GODOT_OK; peer_connection->onLocalDescription([this](rtc::Description description) {
String type = description.type() == rtc::Description::Type::Offer ? "offer" : "answer";
queue_signal("session_description_created", 2, type, String(std::string(description).c_str()));
});
peer_connection->onLocalCandidate([this](rtc::Candidate candidate) {
queue_signal("ice_candidate_created", 3, String(candidate.mid().c_str()), 0, String(candidate.candidate().c_str()));
});
peer_connection->onDataChannel([this](std::shared_ptr<rtc::DataChannel> channel) {
queue_signal("data_channel_received", 1, WebRTCLibDataChannel::new_data_channel(channel, false));
});
/*
peer_connection->onStateChange([](rtc::PeerConnection::State state) {
std::cout << "[State: " << state << "]" << std::endl;
});
peer_connection->onGatheringStateChange([](rtc::PeerConnection::GatheringState state) {
std::cout << "[Gathering State: " << state << "]" << std::endl;
});
*/
return OK;
} catch (const std::exception &e) {
ERR_PRINT(e.what());
ERR_FAIL_V(FAILED);
} }
WebRTCLibPeerConnection::WebRTCLibPeerConnection() : WebRTCLibPeerConnection::WebRTCLibPeerConnection() {
pco(this), #ifndef GDNATIVE_WEBRTC
ptr_csdo(new rtc::RefCountedObject<GodotCSDO>(this)), _init();
ptr_ssdo(new rtc::RefCountedObject<GodotSSDO>(this)) { #endif
} }
WebRTCLibPeerConnection::~WebRTCLibPeerConnection() { WebRTCLibPeerConnection::~WebRTCLibPeerConnection() {
#ifdef GDNATIVE_WEBRTC
if (_owner) { if (_owner) {
register_interface(NULL); register_interface(nullptr);
} }
close(); #endif
_close();
delete mutex_signal_queue; delete mutex_signal_queue;
} }
void WebRTCLibPeerConnection::queue_signal(godot::String p_name, int p_argc, const godot::Variant &p_arg1, const godot::Variant &p_arg2, const godot::Variant &p_arg3) { void WebRTCLibPeerConnection::queue_signal(String p_name, int p_argc, const Variant &p_arg1, const Variant &p_arg2, const Variant &p_arg3) {
mutex_signal_queue->lock(); mutex_signal_queue->lock();
const godot::Variant argv[3] = { p_arg1, p_arg2, p_arg3 }; const Variant argv[3] = { p_arg1, p_arg2, p_arg3 };
signal_queue.push(Signal(p_name, p_argc, argv)); signal_queue.push(Signal(p_name, p_argc, argv));
mutex_signal_queue->unlock(); mutex_signal_queue->unlock();
} }

View File

@@ -31,99 +31,66 @@
#ifndef WEBRTC_PEER_H #ifndef WEBRTC_PEER_H
#define WEBRTC_PEER_H #define WEBRTC_PEER_H
#ifdef GDNATIVE_WEBRTC
#include <Godot.hpp> // Godot.hpp must go first, or windows builds breaks #include <Godot.hpp> // Godot.hpp must go first, or windows builds breaks
#include "api/peer_connection_interface.h" // interface for all things needed from WebRTC
#include "media/base/media_engine.h" // needed for CreateModularPeerConnectionFactory
#include <mutex>
#include "net/WebRTCPeerConnectionNative.hpp" #include "net/WebRTCPeerConnectionNative.hpp"
#define WebRTCPeerConnectionExtension WebRTCPeerConnectionNative
#if !defined(GDCLASS)
#define GDCLASS(arg1, arg2) GODOT_CLASS(arg1, arg2)
#endif
#else
#include <godot_cpp/core/binder_common.hpp>
#include <godot_cpp/classes/global_constants_binds.hpp>
#include <godot_cpp/classes/web_rtc_peer_connection_extension.hpp>
#endif
#include "rtc/rtc.hpp"
#include <mutex>
#include <queue>
namespace godot_webrtc { namespace godot_webrtc {
class WebRTCLibPeerConnection : public WebRTCPeerConnectionNative { class WebRTCLibPeerConnection : public godot::WebRTCPeerConnectionExtension {
GODOT_CLASS(WebRTCLibPeerConnection, WebRTCPeerConnectionNative); GDCLASS(WebRTCLibPeerConnection, WebRTCPeerConnectionExtension);
private: private:
godot_error _create_pc(webrtc::PeerConnectionInterface::RTCConfiguration &config); std::shared_ptr<rtc::PeerConnection> peer_connection = nullptr;
godot::Array candidates;
static std::unique_ptr<rtc::Thread> signaling_thread; godot::Error _create_pc(rtc::Configuration &r_config);
godot::Error _parse_ice_server(rtc::Configuration &r_config, godot::Dictionary p_server);
godot::Error _parse_channel_config(rtc::DataChannelInit &r_config, const godot::Dictionary &p_dict);
protected:
static void _bind_methods() {}
public: public:
static void _register_methods(); static void _register_methods() {}
static void initialize_signaling(); static void initialize_signaling();
static void deinitialize_signaling(); static void deinitialize_signaling();
void _init(); void _init();
ConnectionState get_connection_state() const; ConnectionState _get_connection_state() const override;
GatheringState _get_gathering_state() const override;
SignalingState _get_signaling_state() const override;
godot_error initialize(const godot_dictionary *p_config); godot::Error _initialize(const godot::Dictionary &p_config) override;
godot_object *create_data_channel(const char *p_channel, const godot_dictionary *p_channel_config); godot::Object *_create_data_channel(const godot::String &p_channel, const godot::Dictionary &p_channel_config) override;
godot_error create_offer(); godot::Error _create_offer() override;
godot_error set_remote_description(const char *type, const char *sdp); godot::Error _set_remote_description(const godot::String &type, const godot::String &sdp) override;
godot_error set_local_description(const char *type, const char *sdp); godot::Error _set_local_description(const godot::String &type, const godot::String &sdp) override;
godot_error add_ice_candidate(const char *sdpMidName, int sdpMlineIndexName, const char *sdpName); godot::Error _add_ice_candidate(const godot::String &sdpMidName, int64_t sdpMlineIndexName, const godot::String &sdpName) override;
godot_error poll(); godot::Error _poll() override;
void close(); void _close() override;
WebRTCLibPeerConnection(); WebRTCLibPeerConnection();
~WebRTCLibPeerConnection(); ~WebRTCLibPeerConnection();
/* helper functions */
private: private:
void queue_signal(godot::String p_name, int p_argc, const godot::Variant &p_arg1 = godot::Variant(), const godot::Variant &p_arg2 = godot::Variant(), const godot::Variant &p_arg3 = godot::Variant());
void queue_packet(uint8_t *, int);
/** PeerConnectionObserver callback functions **/
class GodotPCO : public webrtc::PeerConnectionObserver {
public:
WebRTCLibPeerConnection *parent;
GodotPCO(WebRTCLibPeerConnection *p_parent) {
parent = p_parent;
}
void OnIceCandidate(const webrtc::IceCandidateInterface *candidate) override;
void OnSignalingChange(webrtc::PeerConnectionInterface::SignalingState new_state) override {}
void OnAddStream(rtc::scoped_refptr<webrtc::MediaStreamInterface> stream) override {}
void OnRemoveStream(rtc::scoped_refptr<webrtc::MediaStreamInterface> stream) override {}
void OnDataChannel(rtc::scoped_refptr<webrtc::DataChannelInterface> data_channel) override {}
void OnRenegotiationNeeded() override {}
void OnIceConnectionChange(webrtc::PeerConnectionInterface::IceConnectionState new_state) override {}
void OnIceGatheringChange(webrtc::PeerConnectionInterface::IceGatheringState new_state) override {}
};
/** CreateSessionDescriptionObserver callback functions **/
class GodotCSDO : public webrtc::CreateSessionDescriptionObserver {
public:
WebRTCLibPeerConnection *parent = nullptr;
GodotCSDO(WebRTCLibPeerConnection *p_parent) {
parent = p_parent;
}
void OnSuccess(webrtc::SessionDescriptionInterface *desc) override;
void OnFailure(webrtc::RTCError error) override {
ERR_PRINT(godot::String(error.message()));
}
};
/** SetSessionDescriptionObserver callback functions **/
class GodotSSDO : public webrtc::SetSessionDescriptionObserver {
public:
WebRTCLibPeerConnection *parent = nullptr;
bool make_offer = false;
GodotSSDO(WebRTCLibPeerConnection *p_parent) {
parent = p_parent;
}
void OnSuccess() override;
void OnFailure(webrtc::RTCError error) override {
make_offer = false;
ERR_PRINT(godot::String(error.message()));
}
};
class Signal { class Signal {
godot::String method; godot::String method;
godot::Variant argv[3]; godot::Variant argv[3];
@@ -151,15 +118,10 @@ private:
} }
}; };
GodotPCO pco;
rtc::scoped_refptr<GodotSSDO> ptr_ssdo;
rtc::scoped_refptr<GodotCSDO> ptr_csdo;
std::mutex *mutex_signal_queue = nullptr; std::mutex *mutex_signal_queue = nullptr;
std::queue<Signal> signal_queue; std::queue<Signal> signal_queue;
rtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface> pc_factory; void queue_signal(godot::String p_name, int p_argc, const godot::Variant &p_arg1 = godot::Variant(), const godot::Variant &p_arg2 = godot::Variant(), const godot::Variant &p_arg3 = godot::Variant());
rtc::scoped_refptr<webrtc::PeerConnectionInterface> peer_connection;
}; };
} // namespace godot_webrtc } // namespace godot_webrtc

78
src/init_gdextension.cpp Normal file
View File

@@ -0,0 +1,78 @@
/*************************************************************************/
/* init_gdextension.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include <gdextension_interface.h>
#include <godot_cpp/core/class_db.hpp>
#include <godot_cpp/core/defs.hpp>
#include <godot_cpp/godot.hpp>
#include "WebRTCLibDataChannel.hpp"
#include "WebRTCLibPeerConnection.hpp"
#ifdef _WIN32
// See upstream godot-cpp GH-771.
#undef GDN_EXPORT
#define GDN_EXPORT __declspec(dllexport)
#endif
using namespace godot;
using namespace godot_webrtc;
void register_webrtc_extension_types(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
return;
}
WebRTCLibPeerConnection::initialize_signaling();
godot::ClassDB::register_class<WebRTCLibDataChannel>();
godot::ClassDB::register_class<WebRTCLibPeerConnection>();
WebRTCPeerConnection::set_default_extension("WebRTCLibPeerConnection");
}
void unregister_webrtc_extension_types(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
return;
}
WebRTCLibPeerConnection::deinitialize_signaling();
}
extern "C" {
GDExtensionBool GDE_EXPORT webrtc_extension_init(const GDExtensionInterface *p_interface, const GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization) {
GDExtensionBinding::InitObject init_obj(p_interface, p_library, r_initialization);
init_obj.register_initializer(register_webrtc_extension_types);
init_obj.register_terminator(unregister_webrtc_extension_types);
init_obj.set_minimum_library_initialization_level(MODULE_INITIALIZATION_LEVEL_SCENE);
return init_obj.init();
}
}

View File

@@ -1,5 +1,5 @@
/*************************************************************************/ /*************************************************************************/
/* init.cpp */ /* init_gdnative.cpp */
/*************************************************************************/ /*************************************************************************/
/* This file is part of: */ /* This file is part of: */
/* GODOT ENGINE */ /* GODOT ENGINE */
@@ -34,6 +34,12 @@
#include <gdnative_api_struct.gen.h> #include <gdnative_api_struct.gen.h>
#include <net/godot_net.h> #include <net/godot_net.h>
#ifdef _WIN32
// See upstream godot GH-62173.
#undef GDN_EXPORT
#define GDN_EXPORT __declspec(dllexport)
#endif
/* Singleton */ /* Singleton */
static bool _singleton = false; static bool _singleton = false;
static const godot_object *_singleton_lib = NULL; static const godot_object *_singleton_lib = NULL;
@@ -78,7 +84,7 @@ godot_net_webrtc_library library = {
}; };
extern "C" void GDN_EXPORT godot_gdnative_singleton() { extern "C" void GDN_EXPORT godot_gdnative_singleton() {
if (WebRTCPeerConnectionNative::_net_api) { if (godot::WebRTCPeerConnectionNative::_net_api) {
ERR_FAIL_COND(!godot::gdnlib); ERR_FAIL_COND(!godot::gdnlib);
_singleton_lib = godot::gdnlib; _singleton_lib = godot::gdnlib;
ERR_FAIL_COND(!godot::api); ERR_FAIL_COND(!godot::api);
@@ -92,7 +98,7 @@ extern "C" void GDN_EXPORT godot_gdnative_singleton() {
_set_library_mb = godot::api->godot_method_bind_get_method("NativeScript", "set_library"); _set_library_mb = godot::api->godot_method_bind_get_method("NativeScript", "set_library");
ERR_FAIL_COND(!_set_library_mb); ERR_FAIL_COND(!_set_library_mb);
// If registration is successful _singleton will be set to true // If registration is successful _singleton will be set to true
_singleton = WebRTCPeerConnectionNative::_net_api->godot_net_set_webrtc_library(&library) == GODOT_OK; _singleton = godot::WebRTCPeerConnectionNative::_net_api->godot_net_set_webrtc_library(&library) == GODOT_OK;
if (!_singleton) if (!_singleton)
ERR_PRINT("Failed initializing webrtc singleton library"); ERR_PRINT("Failed initializing webrtc singleton library");
} }
@@ -111,7 +117,7 @@ extern "C" void GDN_EXPORT godot_gdnative_init(godot_gdnative_init_options *o) {
break; break;
if (net_api->next->version.major == 3 && net_api->next->version.minor == 2) { if (net_api->next->version.major == 3 && net_api->next->version.minor == 2) {
WebRTCPeerConnectionNative::_net_api = (const godot_gdnative_ext_net_3_2_api_struct *)net_api->next; godot::WebRTCPeerConnectionNative::_net_api = (const godot_gdnative_ext_net_3_2_api_struct *)net_api->next;
} }
} }
@@ -121,7 +127,7 @@ extern "C" void GDN_EXPORT godot_gdnative_init(godot_gdnative_init_options *o) {
extern "C" void GDN_EXPORT godot_gdnative_terminate(godot_gdnative_terminate_options *o) { extern "C" void GDN_EXPORT godot_gdnative_terminate(godot_gdnative_terminate_options *o) {
if (_singleton) { // If we are the active singleton, unregister if (_singleton) { // If we are the active singleton, unregister
WebRTCPeerConnectionNative::_net_api->godot_net_set_webrtc_library(NULL); godot::WebRTCPeerConnectionNative::_net_api->godot_net_set_webrtc_library(NULL);
} }
godot_webrtc::WebRTCLibPeerConnection::deinitialize_signaling(); godot_webrtc::WebRTCLibPeerConnection::deinitialize_signaling();
godot::Godot::gdnative_terminate(o); godot::Godot::gdnative_terminate(o);

View File

@@ -31,6 +31,8 @@
#include "WebRTCDataChannelNative.hpp" #include "WebRTCDataChannelNative.hpp"
#include "net/WebRTCPeerConnectionNative.hpp" #include "net/WebRTCPeerConnectionNative.hpp"
using namespace godot;
void WebRTCDataChannelNative::register_interface(const godot_net_webrtc_data_channel *p_interface) { void WebRTCDataChannelNative::register_interface(const godot_net_webrtc_data_channel *p_interface) {
ERR_FAIL_COND(!WebRTCPeerConnectionNative::_net_api); ERR_FAIL_COND(!WebRTCPeerConnectionNative::_net_api);
WebRTCPeerConnectionNative::_net_api->godot_net_bind_webrtc_data_channel(_owner, p_interface); WebRTCPeerConnectionNative::_net_api->godot_net_bind_webrtc_data_channel(_owner, p_interface);
@@ -54,73 +56,73 @@ WebRTCDataChannelNative::~WebRTCDataChannelNative() {
* and you could use void *user for any kind of state struct pointer you have. * and you could use void *user for any kind of state struct pointer you have.
*/ */
godot_error get_packet_wdc(void *user, const uint8_t **r_buffer, int *r_len) { godot_error get_packet_wdc(void *user, const uint8_t **r_buffer, int *r_len) {
return ((WebRTCDataChannelNative *)user)->get_packet(r_buffer, r_len); return (godot_error)(((WebRTCDataChannelNative *)user)->_get_packet(r_buffer, r_len));
} }
godot_error put_packet_wdc(void *user, const uint8_t *p_buffer, int p_len) { godot_error put_packet_wdc(void *user, const uint8_t *p_buffer, int p_len) {
return ((WebRTCDataChannelNative *)user)->put_packet(p_buffer, p_len); return (godot_error)(((WebRTCDataChannelNative *)user)->_put_packet(p_buffer, p_len));
} }
godot_int get_available_packet_count_wdc(const void *user) { godot_int get_available_packet_count_wdc(const void *user) {
return ((WebRTCDataChannelNative *)user)->get_available_packet_count(); return ((WebRTCDataChannelNative *)user)->_get_available_packet_count();
} }
godot_int get_max_packet_size_wdc(const void *user) { godot_int get_max_packet_size_wdc(const void *user) {
return ((WebRTCDataChannelNative *)user)->get_max_packet_size(); return ((WebRTCDataChannelNative *)user)->_get_max_packet_size();
} }
void set_write_mode_wdc(void *user, godot_int write_mode) { void set_write_mode_wdc(void *user, godot_int write_mode) {
((WebRTCDataChannelNative *)user)->set_write_mode(write_mode); ((WebRTCDataChannelNative *)user)->_set_write_mode((godot::WebRTCDataChannel::WriteMode)write_mode);
} }
godot_int get_write_mode_wdc(const void *user) { godot_int get_write_mode_wdc(const void *user) {
return ((WebRTCDataChannelNative *)user)->get_write_mode(); return ((WebRTCDataChannelNative *)user)->_get_write_mode();
} }
bool was_string_packet_wdc(const void *user) { bool was_string_packet_wdc(const void *user) {
return ((WebRTCDataChannelNative *)user)->was_string_packet(); return ((WebRTCDataChannelNative *)user)->_was_string_packet();
} }
godot_int get_ready_state_wdc(const void *user) { godot_int get_ready_state_wdc(const void *user) {
return (godot_int)(((WebRTCDataChannelNative *)user)->get_ready_state()); return (godot_int)(((WebRTCDataChannelNative *)user)->_get_ready_state());
} }
const char *get_label_wdc(const void *user) { const char *get_label_wdc(const void *user) {
return ((WebRTCDataChannelNative *)user)->get_label(); return ((WebRTCDataChannelNative *)user)->_get_label().utf8().get_data();
} }
bool is_ordered_wdc(const void *user) { bool is_ordered_wdc(const void *user) {
return ((WebRTCDataChannelNative *)user)->is_ordered(); return ((WebRTCDataChannelNative *)user)->_is_ordered();
} }
int get_id_wdc(const void *user) { int get_id_wdc(const void *user) {
return ((WebRTCDataChannelNative *)user)->get_id(); return ((WebRTCDataChannelNative *)user)->_get_id();
} }
int get_max_packet_life_time_wdc(const void *user) { int get_max_packet_life_time_wdc(const void *user) {
return ((WebRTCDataChannelNative *)user)->get_max_packet_life_time(); return ((WebRTCDataChannelNative *)user)->_get_max_packet_life_time();
} }
int get_max_retransmits_wdc(const void *user) { int get_max_retransmits_wdc(const void *user) {
return ((WebRTCDataChannelNative *)user)->get_max_retransmits(); return ((WebRTCDataChannelNative *)user)->_get_max_retransmits();
} }
const char *get_protocol_wdc(const void *user) { const char *get_protocol_wdc(const void *user) {
return ((WebRTCDataChannelNative *)user)->get_protocol(); return ((WebRTCDataChannelNative *)user)->_get_protocol().utf8().get_data();
} }
bool is_negotiated_wdc(const void *user) { bool is_negotiated_wdc(const void *user) {
return ((WebRTCDataChannelNative *)user)->is_negotiated(); return ((WebRTCDataChannelNative *)user)->_is_negotiated();
} }
int get_buffered_amount_wdc(const void *user) { int get_buffered_amount_wdc(const void *user) {
return ((WebRTCDataChannelNative *)user)->get_buffered_amount(); return ((WebRTCDataChannelNative *)user)->_get_buffered_amount();
} }
godot_error poll_wdc(void *user) { godot_error poll_wdc(void *user) {
return ((WebRTCDataChannelNative *)user)->poll(); return (godot_error)(((WebRTCDataChannelNative *)user)->_poll());
} }
void close_wdc(void *user) { void close_wdc(void *user) {
((WebRTCDataChannelNative *)user)->close(); ((WebRTCDataChannelNative *)user)->_close();
} }

View File

@@ -69,6 +69,8 @@ typedef struct {
} }
#endif #endif
namespace godot {
class WebRTCDataChannelNative : public godot::WebRTCDataChannelGDNative { class WebRTCDataChannelNative : public godot::WebRTCDataChannelGDNative {
GODOT_CLASS(WebRTCDataChannelNative, godot::WebRTCDataChannelGDNative); GODOT_CLASS(WebRTCDataChannelNative, godot::WebRTCDataChannelGDNative);
@@ -110,30 +112,32 @@ public:
void _init(); void _init();
void register_interface(const godot_net_webrtc_data_channel *interface); void register_interface(const godot_net_webrtc_data_channel *interface);
virtual void set_write_mode(godot_int mode) = 0; virtual void _set_write_mode(WriteMode mode) = 0;
virtual godot_int get_write_mode() const = 0; virtual WriteMode _get_write_mode() const = 0;
virtual bool was_string_packet() const = 0; virtual bool _was_string_packet() const = 0;
virtual ChannelState get_ready_state() const = 0; virtual ChannelState _get_ready_state() const = 0;
virtual const char *get_label() const = 0; virtual godot::String _get_label() const = 0;
virtual bool is_ordered() const = 0; virtual bool _is_ordered() const = 0;
virtual int get_id() const = 0; virtual int64_t _get_id() const = 0;
virtual int get_max_packet_life_time() const = 0; virtual int64_t _get_max_packet_life_time() const = 0;
virtual int get_max_retransmits() const = 0; virtual int64_t _get_max_retransmits() const = 0;
virtual const char *get_protocol() const = 0; virtual godot::String _get_protocol() const = 0;
virtual bool is_negotiated() const = 0; virtual bool _is_negotiated() const = 0;
virtual int get_buffered_amount() const = 0; virtual int64_t _get_buffered_amount() const = 0;
virtual godot_error poll() = 0; virtual godot::Error _poll() = 0;
virtual void close() = 0; virtual void _close() = 0;
/* PacketPeer */ /* PacketPeer */
virtual godot_error get_packet(const uint8_t **r_buffer, int *r_len) = 0; virtual godot::Error _get_packet(const uint8_t **r_buffer, int32_t *r_len) = 0;
virtual godot_error put_packet(const uint8_t *p_buffer, int p_len) = 0; virtual godot::Error _put_packet(const uint8_t *p_buffer, int64_t p_len) = 0;
virtual godot_int get_available_packet_count() const = 0; virtual int64_t _get_available_packet_count() const = 0;
virtual godot_int get_max_packet_size() const = 0; virtual int64_t _get_max_packet_size() const = 0;
~WebRTCDataChannelNative(); ~WebRTCDataChannelNative();
}; };
}; // namespace godot
#endif // WEBRTC_DATA_CHANNEL_NATIVE #endif // WEBRTC_DATA_CHANNEL_NATIVE

View File

@@ -30,6 +30,8 @@
#include "WebRTCPeerConnectionNative.hpp" #include "WebRTCPeerConnectionNative.hpp"
using namespace godot;
const godot_gdnative_ext_net_3_2_api_struct *WebRTCPeerConnectionNative::_net_api = NULL; const godot_gdnative_ext_net_3_2_api_struct *WebRTCPeerConnectionNative::_net_api = NULL;
void WebRTCPeerConnectionNative::register_interface(const godot_net_webrtc_peer_connection *p_interface) { void WebRTCPeerConnectionNative::register_interface(const godot_net_webrtc_peer_connection *p_interface) {
@@ -55,19 +57,23 @@ WebRTCPeerConnectionNative::~WebRTCPeerConnectionNative() {
* and you could use void *user for any kind of state struct pointer you have. * and you could use void *user for any kind of state struct pointer you have.
*/ */
godot_int get_connection_state_wp(const void *user) { godot_int get_connection_state_wp(const void *user) {
return (godot_int)((WebRTCPeerConnectionNative *)user)->get_connection_state(); return (godot_int)((WebRTCPeerConnectionNative *)user)->_get_connection_state();
} }
godot_error initialize_wp(void *user, const godot_dictionary *p_config) { godot_error initialize_wp(void *user, const godot_dictionary *p_config) {
return ((WebRTCPeerConnectionNative *)user)->initialize(p_config); return (godot_error)(((WebRTCPeerConnectionNative *)user)->_initialize(*(Dictionary *)p_config));
} }
godot_object *create_data_channel_wp(void *user, const char *p_channel, const godot_dictionary *p_channel_config) { godot_object *create_data_channel_wp(void *user, const char *p_channel, const godot_dictionary *p_channel_config) {
return ((WebRTCPeerConnectionNative *)user)->create_data_channel(p_channel, p_channel_config); Object *ptr = ((WebRTCPeerConnectionNative *)user)->_create_data_channel(p_channel, *(Dictionary *)p_channel_config);
if (ptr) {
return ptr->_owner;
}
return nullptr;
} }
godot_error create_offer_wp(void *user) { godot_error create_offer_wp(void *user) {
return ((WebRTCPeerConnectionNative *)user)->create_offer(); return (godot_error)(((WebRTCPeerConnectionNative *)user)->_create_offer());
} }
godot_error create_answer_wp(void *user) { godot_error create_answer_wp(void *user) {
@@ -75,21 +81,21 @@ godot_error create_answer_wp(void *user) {
} }
godot_error set_remote_description_wp(void *user, const char *type, const char *sdp) { godot_error set_remote_description_wp(void *user, const char *type, const char *sdp) {
return ((WebRTCPeerConnectionNative *)user)->set_remote_description(type, sdp); return (godot_error)(((WebRTCPeerConnectionNative *)user)->_set_remote_description(type, sdp));
} }
godot_error set_local_description_wp(void *user, const char *type, const char *sdp) { godot_error set_local_description_wp(void *user, const char *type, const char *sdp) {
return ((WebRTCPeerConnectionNative *)user)->set_local_description(type, sdp); return (godot_error)(((WebRTCPeerConnectionNative *)user)->_set_local_description(type, sdp));
} }
godot_error add_ice_candidate_wp(void *user, const char *sdpMidName, int sdpMlineIndexName, const char *sdpName) { godot_error add_ice_candidate_wp(void *user, const char *sdpMidName, int sdpMlineIndexName, const char *sdpName) {
return ((WebRTCPeerConnectionNative *)user)->add_ice_candidate(sdpMidName, sdpMlineIndexName, sdpName); return (godot_error)(((WebRTCPeerConnectionNative *)user)->_add_ice_candidate(sdpMidName, sdpMlineIndexName, sdpName));
} }
godot_error poll_wp(void *user) { godot_error poll_wp(void *user) {
return ((WebRTCPeerConnectionNative *)user)->poll(); return (godot_error)((WebRTCPeerConnectionNative *)user)->_poll();
} }
void close_wp(void *user) { void close_wp(void *user) {
((WebRTCPeerConnectionNative *)user)->close(); ((WebRTCPeerConnectionNative *)user)->_close();
} }

View File

@@ -50,6 +50,8 @@ godot_error add_ice_candidate_wp(void *, const char *, int, const char *);
godot_error poll_wp(void *); godot_error poll_wp(void *);
void close_wp(void *); void close_wp(void *);
namespace godot {
class WebRTCPeerConnectionNative : public godot::WebRTCPeerConnectionGDNative { class WebRTCPeerConnectionNative : public godot::WebRTCPeerConnectionGDNative {
GODOT_CLASS(WebRTCPeerConnectionNative, godot::WebRTCPeerConnectionGDNative); GODOT_CLASS(WebRTCPeerConnectionNative, godot::WebRTCPeerConnectionGDNative);
@@ -73,24 +75,43 @@ protected:
}; };
public: public:
enum GatheringState {
GATHERING_STATE_NEW,
GATHERING_STATE_GATHERING,
GATHERING_STATE_COMPLETE,
};
enum SignalingState {
SIGNALING_STATE_STABLE,
SIGNALING_STATE_HAVE_LOCAL_OFFER,
SIGNALING_STATE_HAVE_REMOTE_OFFER,
SIGNALING_STATE_HAVE_LOCAL_PRANSWER,
SIGNALING_STATE_HAVE_REMOTE_PRANSWER,
SIGNALING_STATE_CLOSED,
};
static void _register_methods(); static void _register_methods();
static const godot_gdnative_ext_net_3_2_api_struct *_net_api; static const godot_gdnative_ext_net_3_2_api_struct *_net_api;
void _init(); void _init();
void register_interface(const godot_net_webrtc_peer_connection *interface); void register_interface(const godot_net_webrtc_peer_connection *interface);
virtual ConnectionState get_connection_state() const = 0; virtual ConnectionState _get_connection_state() const = 0;
virtual GatheringState _get_gathering_state() const = 0;
virtual SignalingState _get_signaling_state() const = 0;
virtual godot_error initialize(const godot_dictionary *p_config) = 0; virtual godot::Error _initialize(const godot::Dictionary &p_config) = 0;
virtual godot_object *create_data_channel(const char *p_channel, const godot_dictionary *p_channel_config) = 0; virtual godot::Object *_create_data_channel(const godot::String &p_channel, const godot::Dictionary &p_channel_config) = 0;
virtual godot_error create_offer() = 0; virtual godot::Error _create_offer() = 0;
virtual godot_error set_remote_description(const char *type, const char *sdp) = 0; virtual godot::Error _set_remote_description(const godot::String &type, const godot::String &sdp) = 0;
virtual godot_error set_local_description(const char *type, const char *sdp) = 0; virtual godot::Error _set_local_description(const godot::String &type, const godot::String &sdp) = 0;
virtual godot_error add_ice_candidate(const char *sdpMidName, int sdpMlineIndexName, const char *sdpName) = 0; virtual godot::Error _add_ice_candidate(const godot::String &sdpMidName, int64_t sdpMlineIndexName, const godot::String &sdpName) = 0;
virtual godot_error poll() = 0; virtual godot::Error _poll() = 0;
virtual void close() = 0; virtual void _close() = 0;
~WebRTCPeerConnectionNative(); ~WebRTCPeerConnectionNative();
}; };
}; // namespace godot
#endif // WEBRTC_PEER_NATIVE #endif // WEBRTC_PEER_NATIVE

82
thirdparty/README.md vendored Normal file
View File

@@ -0,0 +1,82 @@
# Third party libraries
Please keep categories (`##` level) listed alphabetically and matching their
respective folder names. Use two empty lines to separate categories for
readability.
## json
- Upstream: https://github.com/nlohmann/json
- Version: 3.10.5 (4f8fba14066156b73f1189a2b8bd568bde5284c5, 2022)
- License: MIT
Module location:
- thirdparty/libdatachannel/deps/json
## libdatachannel
- Upstream: https://github.com/paullouisageneau/libdatachannel
- Version: 0.18.1 (595f0ebaac3974f17e5a5c63e7e7dc0c5edd163d, 2022)
- License: MPL 2.0
Module location:
- thirdparty/libdatachannel
# libjuice
- Upstream: https://github.com/paullouisageneau/libjuice
- Version: 1.1.0 (0dabc046cd23da6908749e4c6add834ec29a7c49, 2022)
- License: MPL 2.0
Module location:
- thirdparty/libdatachannel/deps/libjuice
## libsrtp
- Upstream: https://github.com/cisco/libsrtp
- Version: 2.4.2 (90d05bf8980d16e4ac3f16c19b77e296c4bc207b, 2021)
- License: BSD-3-Clause
Module location:
- thirdparty/libdatachannel/deps/libsrtp
## openssl
- Upstream: git://git.openssl.org/openssl.git
- Version: 3.0.7 (19cc035b6c6f2283573d29c7ea7f7d675cf750ce, 2022)
- License: Apache 2.0
Module location:
- thirdparty/openssl
## plog
- Upstream: https://github.com/SergiusTheBest/plog
- Version: git (d8461e9d473e59fbcc1f79eee021550dcf81e618, 2021)
- License: MPL 2.0
Module location:
- thirdparty/libdatachannel/deps/plog
## usrsctp
- Upstream: https://github.com/sctplab/usrsctp
- Version: git (7c31bd35c79ba67084ce029511193a19ceb97447, 2021)
- License: BSD-3-Clause
Module location:
- thirdparty/libdatachannel/deps/usrsctp

1
thirdparty/libdatachannel vendored Submodule

1
thirdparty/openssl vendored Submodule

Submodule thirdparty/openssl added at 19cc035b6c

27
tools/cmake.py Normal file
View File

@@ -0,0 +1,27 @@
def exists(env):
return True
def generate(env):
env.AddMethod(cmake_configure, "CMakeConfigure")
env.AddMethod(cmake_build, "CMakeBuild")
def cmake_configure(env, source, target, opt_args):
args = [
"-B",
target,
]
if env["platform"] == "windows" and env["use_mingw"]:
args.extend(["-G", "Unix Makefiles"])
for arg in opt_args:
args.append(arg)
args.append(source)
return env.Execute("cmake " + " ".join(['"%s"' % a for a in args]))
def cmake_build(env, source, target=""):
jobs = env.GetOption("num_jobs")
env = env.Clone()
return env.Execute("cmake --build %s %s -j%s" % (source, "-t %s" % target if target else "", jobs))

8
tools/common.py Normal file
View File

@@ -0,0 +1,8 @@
def exists(env):
return True
def generate(env):
env["DEPS_SOURCE"] = env.Dir("#thirdparty").abspath
env["DEPS_BUILD"] = env.Dir("#bin/thirdparty").abspath + "/{}.{}.dir".format(env["suffix"][1:], "RelWithDebInfo" if env["debug_symbols"] else "Release")

95
tools/rtc.py Normal file
View File

@@ -0,0 +1,95 @@
import os
def rtc_cmake_config(env):
config = {
"USE_NICE": 0,
"NO_WEBSOCKET": 1,
"NO_EXAMPLES": 1,
"NO_TESTS": 1,
"OPENSSL_USE_STATIC_LIBS": 1,
"OPENSSL_INCLUDE_DIR": env["SSL_INCLUDE"],
"OPENSSL_SSL_LIBRARY": env["SSL_LIBRARY"],
"OPENSSL_CRYPTO_LIBRARY": env["SSL_CRYPTO_LIBRARY"],
"OPENSSL_ROOT_DIR": env["SSL_BUILD"],
"CMAKE_BUILD_TYPE": "%s" % ("RelWithDebInfo" if env["debug_symbols"] else "Release"),
}
if "CC" in env:
config["CMAKE_C_COMPILER"] = env["CC"]
if "CXX" in env:
config["CMAKE_CXX_COMPILER"] = env["CXX"]
if env["platform"] == "android":
api = env["android_api_level"] if int(env["android_api_level"]) > 28 else "28"
abi = {
"arm64": "arm64-v8a",
"arm32": "armeabi-v7a",
"x86_32": "x86",
"x86_64": "x86_64",
}[env["arch"]]
config["CMAKE_SYSTEM_NAME"] = "Android"
config["CMAKE_SYSTEM_VERSION"] = api
config["CMAKE_ANDROID_ARCH_ABI"] = abi
config["ANDROID_ABI"] = abi
config["CMAKE_TOOLCHAIN_FILE"] = "%s/build/cmake/android.toolchain.cmake" % os.environ.get("ANDROID_NDK_ROOT", "")
config["CMAKE_ANDROID_STL_TYPE"] = "c++_static"
elif env["platform"] == "linux":
march = "-m32" if env["arch"] == "x86_32" else "-m64"
config["CMAKE_C_FLAGS"] = march
config["CMAKE_CXX_FLAGS"] = march
elif env["platform"] == "macos":
if env["arch"] == "universal":
raise ValueError("OSX architecture not supported: %s" % env["arch"])
config["CMAKE_OSX_ARCHITECTURES"] = env["arch"]
if env["macos_deployment_target"] != "default":
config["CMAKE_OSX_DEPLOYMENT_TARGET"] = env["macos_deployment_target"]
elif env["platform"] == "ios":
if env["arch"] == "universal":
raise ValueError("iOS architecture not supported: %s" % env["arch"])
config["CMAKE_SYSTEM_NAME"] = "iOS"
config["CMAKE_OSX_DEPLOYMENT_TARGET"] = "11.0"
config["CMAKE_OSX_ARCHITECTURES"] = env["arch"]
if env["ios_simulator"]:
config["CMAKE_OSX_SYSROOT"] = "iphonesimulator"
elif env["platform"] == "windows":
config["CMAKE_SYSTEM_NAME"] = "Windows"
return config
def rtc_emitter(target, source, env):
env.Depends(env["RTC_LIBS"], env["SSL_LIBS"])
env.Depends(env["RTC_LIBS"], [env.File(__file__), env.Dir(env["RTC_SOURCE"]), env.File(env["RTC_SOURCE"] + "/CMakeLists.txt")])
return env["RTC_LIBS"], env.Dir(env["RTC_SOURCE"])
def rtc_action(target, source, env):
rtc_env = env.Clone()
build_dir = env["RTC_BUILD"]
source_dir = env["RTC_SOURCE"]
opts = rtc_cmake_config(rtc_env)
rtc_env.CMakeConfigure(source_dir, build_dir, ["-D%s=%s" % it for it in opts.items()])
rtc_env.CMakeBuild(build_dir, "datachannel-static")
return None
def exists(env):
return "CMakeConfigure" in env and "CMakeBuild" in env
def generate(env):
env["RTC_SOURCE"] = env["DEPS_SOURCE"] + "/libdatachannel"
env["RTC_BUILD"] = env["DEPS_BUILD"] + "/libdatachannel"
env["RTC_INCLUDE"] = env["RTC_SOURCE"] + "/include"
env["RTC_LIBS"] = [env.File(env["RTC_BUILD"] + "/" + lib) for lib in [
"libdatachannel-static.a",
"deps/libjuice/libjuice-static.a",
"deps/libsrtp/libsrtp2.a",
"deps/usrsctp/usrsctplib/libusrsctp.a"
]]
env.Append(BUILDERS={
"BuildLibDataChannel": env.Builder(action=rtc_action, emitter=rtc_emitter)
})
env.Append(LIBPATH=[env["RTC_BUILD"]])
env.Append(CPPPATH=[env["RTC_INCLUDE"]])
env.Prepend(LIBS=env["RTC_LIBS"])
if env["platform"] == "windows":
env.AppendUnique(LIBS=["iphlpapi", "bcrypt"])

115
tools/ssl.py Normal file
View File

@@ -0,0 +1,115 @@
import os
from SCons.Defaults import Mkdir
def ssl_emitter(target, source, env):
env.Depends(env["SSL_LIBS"], env.File(__file__))
return env["SSL_LIBS"], [env.Dir(env["SSL_SOURCE"]), env.File(env["SSL_SOURCE"] + "/VERSION.dat")]
def ssl_action(target, source, env):
build_dir = env["SSL_BUILD"]
source_dir = env["SSL_SOURCE"]
install_dir = env["SSL_INSTALL"]
ssl_env = env.Clone()
args = [
"no-ssl2",
"no-ssl3",
"no-weak-ssl-ciphers",
"no-legacy",
"no-shared",
"no-tests",
"--prefix=%s" % install_dir,
"--openssldir=%s" % install_dir,
]
if env["debug_symbols"]:
args.append("-d")
if env["platform"] == "linux":
if env["arch"] == "x86_32":
args.extend(["linux-x86"])
else:
args.extend(["linux-x86_64"])
elif env["platform"] == "android":
api = env["android_api_level"] if int(env["android_api_level"]) > 28 else "28"
args.extend([
{
"arm64": "android-arm64",
"arm32": "android-arm",
"x86_32": "android-x86",
"x86_64": "android-x86_64",
}[env["arch"]],
"-D__ANDROID_API__=%s" % api,
])
# Setup toolchain path.
ssl_env.PrependENVPath("PATH", os.path.dirname(env["CC"]))
ssl_env["ENV"]["ANDROID_NDK_ROOT"] = os.environ.get("ANDROID_NDK_ROOT", "")
elif env["platform"] == "macos":
if env["arch"] == "x86_64":
args.extend(["darwin64-x86_64"])
elif env["arch"] == "arm64":
args.extend(["darwin64-arm64"])
else:
raise ValueError("macOS architecture not supported: %s" % env["arch"])
elif env["platform"] == "ios":
if env["ios_simulator"]:
args.extend(["iossimulator-xcrun"])
elif env["arch"] == "arm32":
args.extend(["ios-xcrun"])
elif env["arch"] == "arm64":
args.extend(["ios64-xcrun"])
else:
raise ValueError("iOS architecture not supported: %s" % env["arch"])
elif env["platform"] == "windows":
if env["arch"] == "x86_32":
if env["use_mingw"]:
args.extend([
"mingw",
"--cross-compile-prefix=i686-w64-mingw32-",
])
else:
args.extend(["VC-WIN32"])
else:
if env["use_mingw"]:
args.extend([
"mingw64",
"--cross-compile-prefix=x86_64-w64-mingw32-",
])
else:
args.extend(["VC-WIN64A"])
jobs = env.GetOption("num_jobs")
ssl_env.Execute([
Mkdir(build_dir),
"cd %s && perl %s/Configure %s" % (build_dir, source_dir, " ".join(['"%s"' % a for a in args])),
"make -C %s -j%s" % (build_dir, jobs),
"make -C %s install_sw install_ssldirs -j%s" % (build_dir, jobs),
]
)
return None
def exists(env):
return True
def generate(env):
env["SSL_SOURCE"] = env["DEPS_SOURCE"] + "/openssl"
env["SSL_BUILD"] = env["DEPS_BUILD"] + "/openssl"
env["SSL_INSTALL"] = env["SSL_BUILD"] + "/dest"
env["SSL_INCLUDE"] = env["SSL_INSTALL"] + "/include"
env["SSL_LIBRARY"] = env.File(env["SSL_BUILD"] + "/libssl.a")
env["SSL_CRYPTO_LIBRARY"] = env.File(env["SSL_BUILD"] + "/libcrypto.a")
env["SSL_LIBS"] = [env["SSL_LIBRARY"], env["SSL_CRYPTO_LIBRARY"]]
env.Append(BUILDERS={
"BuildOpenSSL": env.Builder(action=ssl_action, emitter=ssl_emitter)
})
env.Prepend(CPPPATH=[env["SSL_INCLUDE"]])
env.Prepend(LIBPATH=[env["SSL_BUILD"]])
env.Append(LIBS=env["SSL_LIBS"])
if env["platform"] == "windows":
env.AppendUnique(LIBS=["ws2_32", "gdi32", "advapi32", "crypt32", "user32"])