Compare commits

..

43 Commits
0.1 ... 3.2

Author SHA1 Message Date
Fabio Alessandrelli
eeabf0a844 Merge pull request #38 from Faless/refactor/signals
Refactor signals and obeserver, fixes answer creation.
2021-07-09 16:24:35 +02:00
Fabio Alessandrelli
04fbae6ce3 Properly wait success callback before creating answers. 2021-07-09 03:46:25 +02:00
Fabio Alessandrelli
ce3f086ec4 Move observers implementations into PeerConnection. 2021-07-09 03:45:45 +02:00
Fabio Alessandrelli
00ac03c8e7 Use a class instead of lambdas for signals. 2021-07-09 00:53:15 +02:00
Fabio Alessandrelli
fa5296a4e4 Merge pull request #37 from Faless/ci/auto
[CI] Use organization's repository for automated builds.
2021-07-08 23:56:42 +02:00
Fabio Alessandrelli
189c353264 [CI] Use organization's repository for automated builds.
Update README.md to reflect new build sources.
2021-07-08 23:04:16 +02:00
Fabio Alessandrelli
e44c42fde8 Merge pull request #36 from Faless/ci/single
Setup CI for Android, iOS, Linux, macOS, Windows.
2021-07-06 16:27:59 +02:00
Fabio Alessandrelli
524fdde8f2 Setup CI for Android, iOS, Linux, macOS, Windows.
Includes all supported architectures:

* Android:
  - arm (neon)
  - arm64
  - x86
  - x64

* iOS:
  - arm
  - arm64
  - x64 (simulator)

* Linux
  - x86
  - x64

* macOS
  - x64

* windows
  - x86
  - x64

Use a single matrix for builds, then an extra step will package for
release.

Artefacts are generated for each platform/arch combination, along for
the 2 zip containing the full `webrtc` and `webrtc_debug` plugin.
2021-07-06 15:57:55 +02:00
Fabio Alessandrelli
673a4c1a1c Merge pull request #34 from Faless/build/optimization_flags
Fix osx, android optimization flags.
2021-07-05 03:17:27 +02:00
Fabio Alessandrelli
cf98eb2e7a Fix osx, android optimization flags.
Will need to be also fixed in upstream godot-cpp.
2021-07-02 21:58:44 +02:00
Fabio Alessandrelli
7b92ab2a7b Add missing iOS armv7 entry. 2021-07-02 18:11:35 +02:00
Fabio Alessandrelli
e120cc7ed6 Merge pull request #33 from Faless/bump/webrtc_4472
Bump webrtc to branch-heads/4472. Fixes signalling.
2021-07-02 15:50:08 +02:00
Fabio Alessandrelli
da20ebf397 Unified signalling thread. 2021-07-02 15:12:41 +02:00
Fabio Alessandrelli
3711ad23f6 Bump webrtc to branch-heads/4472. 2021-07-02 15:12:41 +02:00
Fabio Alessandrelli
001be78500 Merge pull request #32 from Faless/release/3.2_fix
Fix API breakage after godot cpp 3.2 bump.
2021-07-02 00:39:52 +02:00
Fabio Alessandrelli
416ea27305 Fix API breakage after godot cpp 3.2 bump. 2021-07-02 00:36:41 +02:00
Fabio Alessandrelli
65bb4cb9ae Merge pull request #31 from Faless/release/3.2
Update godot-cpp to tip of 3.2 branch.
2021-07-02 00:29:11 +02:00
Fabio Alessandrelli
3014c3099f Update godot-cpp to tip of 3.2 branch. 2021-07-02 00:26:10 +02:00
Fabio Alessandrelli
c1c7ae5081 Merge pull request #30 from Faless/scons/mac_ios
Add mac and ios platforms. Fix GDNLibrary.
2021-07-02 00:13:48 +02:00
Fabio Alessandrelli
9e287f7cee Add mac and ios platforms. Fix GDNLibrary. 2021-07-01 23:58:01 +02:00
Fabio Alessandrelli
a374cf4510 Merge pull request #24 from rondao/add_android_build_support
Add build support for Android
2021-07-01 07:34:03 +02:00
Fabio Alessandrelli
9ee98ff0e9 Merge pull request #28 from Faless/fix/python3
Fix gdns generation with python3.
2021-05-05 16:42:21 +02:00
Fabio Alessandrelli
f26a1d8e83 Fix gdns generation with python3.
Python2 has reached end-of-life.
2021-05-05 16:40:22 +02:00
Rafael Rondao
7a62fe467b Add build support for Android
To build for Android, use platform=android.
ANDROID_NDK_ROOT env variable needs to be defined.

By default, it builds for armv7 with Api 18.
To build for arm64v8 (or x86, x86_64), use android_arch=arm64v8.
64 bits requires at least Api level 21.

If needed, you can change it with android_api_level=XX.

Signed-off-by: Rafael Rondao <rafael.rondao@gmail.com>
2020-11-07 23:01:34 -03:00
Fabio Alessandrelli
74f2c78db5 Merge pull request #18 from MarkSort/support-server-platform
set library paths for Server platform
2020-08-02 12:07:13 +02:00
Mark Achée
db6d416eba set library paths for Server platform 2020-06-20 12:22:29 -05:00
Fabio Alessandrelli
5f9e74f2fb Merge pull request #11 from Faless/fix/close_crash
Fix crash when closing an invalid peerconnection.
2020-01-13 18:18:20 +01:00
Fabio Alessandrelli
bd8e139ccc Fix crash when closing an invalid peerconnection. 2020-01-03 19:50:48 +01:00
Fabio Alessandrelli
61e45cb083 Merge pull request #10 from Faless/refactor/better-scons
Better build script, readme
2020-01-03 19:43:01 +01:00
Fabio Alessandrelli
6983019cab Initial README 2020-01-03 19:41:21 +01:00
Fabio Alessandrelli
54cbe83c56 Simplify webrtc lib folder structure 2020-01-03 19:41:21 +01:00
Fabio Alessandrelli
20f8f89c2c Use submodule for godot-cpp 2020-01-03 19:41:21 +01:00
Fabio Alessandrelli
4b22741255 Build script also create gdnlib 2020-01-03 19:41:07 +01:00
Fabio Alessandrelli
5769d8b2de Merge observers 2020-01-03 19:35:20 +01:00
Fabio Alessandrelli
423454086e Merge pull request #3 from Faless/channels_pr
Update to new DataChannel API, singleton load.
2019-05-21 14:08:30 +02:00
Fabio Alessandrelli
599ed98f6c Small build system improvement 2019-05-16 12:08:39 +02:00
Fabio Alessandrelli
f5ebac4519 Working singleton load 2019-05-16 10:32:51 +02:00
Fabio Alessandrelli
3e64a42b14 Update to new DataChannel API.
New WebRTCLibDataChannel class act as PacketPeer.
Old WebRTCPeer (now WebRTCPeerConnection) now allows you to set
configuration (STUN/TURN) and creating multiple data channels.
Fixed many bugs and implemented most of the missing API.
2019-05-16 10:32:51 +02:00
Fabio Alessandrelli
f2cf2e5340 Merge pull request #2 from Faless/build/refactor
Refactor Scons build script
2019-04-17 16:46:17 +02:00
Fabio Alessandrelli
1763d278e0 Better flags 2019-04-17 16:41:45 +02:00
Fabio Alessandrelli
8e46eed9e8 Better gitignores 2019-04-17 16:41:45 +02:00
Fabio Alessandrelli
6c0831b6c6 Refactor SCons build script 2019-04-17 15:47:51 +02:00
Fabio Alessandrelli
c30ddd9b22 Update GDNative bind.
The GDNative bind was moved to the "next" version of the extention for
compatibility reasons.
2019-04-12 01:34:33 +02:00
27 changed files with 1664 additions and 625 deletions

View File

@@ -0,0 +1,51 @@
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 *

180
.github/workflows/build_release.yml vendored Normal file
View File

@@ -0,0 +1,180 @@
name: 🔧 Build -> Package 📦
on: [push, pull_request]
jobs:
build:
runs-on: ${{ matrix.os }}
name: 🔧 Build
strategy:
matrix:
include:
# Android
- platform: android
arch: 'x86'
sconsflags: 'android_arch=x86'
os: 'ubuntu-20.04'
- 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'
- platform: android
arch: 'arm64'
sconsflags: 'android_arch=arm64v8'
os: 'ubuntu-20.04'
# iOS
- platform: ios
arch: 'x64'
sconsflags: 'ios_arch=x86_64 ios_simulator=true'
os: 'macos-latest'
- platform: ios
arch: 'arm'
sconsflags: 'ios_arch=armv7'
os: 'macos-latest'
- platform: ios
arch: 'arm64'
sconsflags: 'ios_arch=arm64'
os: 'macos-latest'
# Linux
- platform: linux
arch: 'x86'
sconsflags: 'bits=32'
os: 'ubuntu-20.04'
- platform: linux
arch: 'x64'
sconsflags: 'bits=64'
os: 'ubuntu-20.04'
# macOS
- platform: osx
arch: 'x64'
sconsflags: 'bits=64'
os: 'macos-latest'
# Windows
- platform: windows
arch: 'x86'
sconsflags: 'bits=32'
os: 'windows-latest'
msvc_arch: amd64_x86
- platform: windows
arch: 'x64'
sconsflags: 'bits=64'
os: 'windows-latest'
msvc_arch: amd64
env:
SCONSFLAGS: ${{ matrix.sconsflags }} platform=${{ matrix.platform }} --jobs=2
NDK_VERSION: 22b
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:
run:
shell: bash
steps:
- uses: actions/checkout@v2
with:
submodules: recursive
- name: Cache NDK
id: cache-ndk
if: ${{ matrix.platform == 'android' }}
uses: actions/cache@v2
with:
path: ${{ env.ANDROID_NDK_ROOT }}
key: ndk-${{ env.NDK_VERSION }}
- name: Download NDK
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' }}
run: "'${{ env.MSVC_VARS }}' ${{ matrix.msvc_arch }}"
- name: Install Linux build dependencies
if: ${{ matrix.platform == 'linux' }}
run: |
sudo apt-get install build-essential gcc-multilib wget g++-multilib
- name: Set up Python 3.x
uses: actions/setup-python@v2
with:
python-version: '3.x'
architecture: 'x64'
- name: Configuring Python packages
run: |
python -c "import sys; print(sys.version)"
python -m pip install scons
python --version
scons --version
- name: Get WebRTC package for ${{ matrix.platform }} - ${{ matrix.arch }}
uses: ./.github/actions/webrtc-download
with:
platform: ${{ matrix.platform }}
archs: ${{ matrix.arch }}
- name: Compilation ${{ matrix.platform }} - ${{ matrix.arch }} - godot-cpp
run: |
scons -C godot-cpp target=debug generate_bindings=yes
scons -C godot-cpp target=release
- name: Compilation ${{ matrix.platform }} - ${{ matrix.arch }} - webrtc-native
run: |
scons target=debug
scons target=release
- uses: actions/upload-artifact@v2
with:
name: ${{ github.job }}-${{ matrix.platform }}-${{ matrix.arch }}
path: bin/*
package:
name: 📦 Package
needs: build
runs-on: "ubuntu-latest"
steps:
- uses: actions/checkout@v2
- uses: actions/download-artifact@v2
with:
path: artifacts
- name: Package artifacts for release
run: |
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
zip -r godot-webrtc-native-debug.zip webrtc_debug
ls -R
- uses: actions/upload-artifact@v2
with:
name: godot-webrtc-native-debug.zip
path: release/godot-webrtc-native-debug.zip
- uses: actions/upload-artifact@v2
with:
name: godot-webrtc-native-release.zip
path: release/godot-webrtc-native-release.zip

1
.gitignore vendored
View File

@@ -4,3 +4,4 @@ bin/*
*.lib
.sconsign.dblite
*.obj
*.swp

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "godot-cpp"]
path = godot-cpp
url = https://github.com/GodotNativeTools/godot-cpp

View File

@@ -0,0 +1,71 @@
# GDNative WebRTC plugin for Godot
## Getting Started
| **Download latest binary version** | [**GitHub**](https://github.com/godotengine/webrtc-native/releases) |
| --- | --- |
### 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.
```
$ 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:
```
$ git submodule update --init --recursive
```
Right now our directory structure should look like this:
```
webrtc-native/
├─bin/
├─godot-cpp/
| └─godot-headers/
├─src/
└─webrtc/
```
### Compiling the cpp bindings library
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>
```
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.

View File

@@ -1,6 +1,14 @@
#!python
import os, sys, platform, json
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):
@@ -9,35 +17,17 @@ def add_sources(sources, dirpath, extension):
sources.append(dirpath + '/' + f)
def gen_gdnative_lib(target, source, env):
for t in target:
with open(t.srcnode().path, 'w') as w:
w.write(decode_utf8(source[0].get_contents()).replace('{GDNATIVE_PATH}', os.path.splitext(t.name)[0]).replace('{TARGET}', env['target']))
env = Environment()
host_platform = platform.system()
target_arch = ARGUMENTS.get('b', ARGUMENTS.get('bits', '64'))
target_platform = ARGUMENTS.get('p', ARGUMENTS.get('platform', 'linux'))
target_arch = ARGUMENTS.get('a', ARGUMENTS.get('arch', '64'))
# default to debug build, must be same setting as used for cpp_bindings
target = ARGUMENTS.get('target', 'debug')
# Local dependency paths, adapt them to your setup
godot_headers = ARGUMENTS.get('headers', '../godot_headers')
godot_cpp = ARGUMENTS.get('godot-cpp', '../godot-cpp')
result_path = 'bin'
result_name = 'webrtc_native'
if target_platform == 'linux':
result_name += '.linux.' + target + '.' + target_arch
env['CXX']='g++'
if ARGUMENTS.get('use_llvm', 'no') == 'yes':
env['CXX'] = 'clang++'
env.Append(CCFLAGS = [ '-fPIC', '-g', '-O3', '-std=c++14', '-Wwrite-strings' ])
if target_arch == '32':
env.Append(CCFLAGS = [ '-m32' ])
env.Append(LINKFLAGS = [ '-m32' ])
elif target_arch == '64':
env.Append(CCFLAGS = [ '-m64' ])
env.Append(LINKFLAGS = [ '-m64' ])
elif target_platform == 'windows':
if target_platform == 'windows':
# 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'):
@@ -45,11 +35,101 @@ elif target_platform == 'windows':
else:
env = Environment(ENV = os.environ, TARGET_ARCH='x86')
result_name += '.windows.' + target + '.' + target_arch
env.Append(BUILDERS={'GDNativeLibBuilder': Builder(action=gen_gdnative_lib)})
customs = ['custom.py']
opts = Variables(customs, ARGUMENTS)
opts.Add(BoolVariable('use_llvm', 'Use the LLVM compiler', False))
opts.Add(EnumVariable('target', "Compilation target", 'debug', ('debug', 'release')))
opts.Add(EnumVariable(
'android_arch',
'Target Android architecture',
'armv7',
['armv7','arm64v8', 'x86', 'x86_64']
))
opts.Add(
'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']
))
opts.Add(BoolVariable(
'ios_simulator',
'Target iOS Simulator',
False
))
opts.Add(
'IPHONEPATH',
'Path to iPhone toolchain',
'/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain',
)
# Update environment (parse options)
opts.Update(env)
target = env['target']
if target_platform == 'android':
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':
#result_name += '.lib'
lib_prefix = "lib"
env.Append(LINKFLAGS = [ '/WX' ])
if target == 'debug':
env.Append(CCFLAGS = ['/EHsc', '/D_DEBUG', '/MDd' ])
@@ -61,50 +141,155 @@ elif target_platform == 'windows':
elif target_arch == '64':
env['CXX']='x86_64-w64-mingw32-g++'
env.Append(CCFLAGS = [ '-g', '-O3', '-std=c++14', '-Wwrite-strings' ])
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 ARGUMENTS.get('use_llvm', 'no') == 'yes':
if env['use_llvm']:
env['CXX'] = 'clang++'
# Only 64-bits is supported for OS X
target_arch = '64'
result_name += '.osx.' + target + '.' + target_arch
env.Append(CCFLAGS = [ '-g','-O3', '-std=c++14', '-arch', 'x86_64' ])
env.Append(CCFLAGS = [ '-std=c++14', '-arch', 'x86_64' ])
env.Append(LINKFLAGS = [ '-arch', 'x86_64', '-framework', 'Cocoa', '-Wl,-undefined,dynamic_lookup' ])
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:
print("No valid target platform selected.")
sys.exit(1)
# Godot CPP bindings
env.Append(CPPPATH=[godot_headers])
env.Append(CPPPATH=[godot_cpp + '/include', godot_cpp + '/include/core', godot_cpp + '/include/gen'])
env.Append(LIBPATH=[godot_cpp + '/bin'])
env.Append(LIBS=['godot-cpp'])
env.Append(CPPPATH=[godot_cpp_headers, godot_cpp_headers + '/core', godot_cpp_headers + '/gen'])
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
webrtc_dir = "webrtc"
lib_name = 'libwebrtc_full'
lib_path = webrtc_dir + '/lib'
lib_path = os.path.join(webrtc_dir, target_platform)
if target_arch == '64':
lib_path += '/x64'
elif target_arch == '32':
lib_path += '/x86'
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"])
#env.Append(CPPPATH=[lib_path])
env.Append(CPPPATH=[webrtc_dir + "/include", webrtc_dir + "/include/third_party/abseil-cpp"])
if target_platform == "linux":
env.Append(LIBS=[lib_name])
env.Append(LIBS=[lib_name, "atomic"])
env.Append(LIBPATH=[lib_path])
#env.Append(CCFLAGS=["-std=c++11"])
env.Append(CCFLAGS=["-DWEBRTC_POSIX", "-DWEBRTC_LINUX"])
@@ -125,12 +310,26 @@ elif target_platform == "windows":
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=''"])
# Godot CPP bindings
env.Append(CPPPATH=[godot_headers])
env.Append(CPPPATH=[godot_cpp + '/include', godot_cpp + '/include/core', godot_cpp + '/include/gen'])
env.Append(LIBPATH=[godot_cpp + '/bin'])
env.Append(LIBS=['godot-cpp'])
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
env.Append(CPPPATH=['src/'])
@@ -138,6 +337,18 @@ sources = []
add_sources(sources, 'src/', 'cpp')
add_sources(sources, 'src/net/', 'cpp')
# Suffix
suffix = '.%s.%s' % (target, target_arch)
env["SHOBJSUFFIX"] = suffix + env["SHOBJSUFFIX"]
# Make the shared library
result_name = 'webrtc_native.%s.%s.%s' % (target_platform, target, target_arch) + env["SHLIBSUFFIX"]
library = env.SharedLibrary(target=os.path.join(result_path, result_name), source=sources)
Default(library)
# GDNativeLibrary
gdnlib = 'webrtc'
if target != 'release':
gdnlib += '_debug'
Default(env.GDNativeLibBuilder([os.path.join('bin', gdnlib, gdnlib + '.tres')], ['misc/gdnlib.tres']))

1
godot-cpp Submodule

Submodule godot-cpp added at 77d41fa179

28
misc/gdnlib.tres Normal file
View File

@@ -0,0 +1,28 @@
[gd_resource type="GDNativeLibrary" format=2]
[resource]
singleton = true
reloadable = false
entry/OSX.64 = "res://{GDNATIVE_PATH}/lib/libwebrtc_native.osx.{TARGET}.64.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.32 = "res://{GDNATIVE_PATH}/lib/webrtc_native.windows.{TARGET}.32.dll"
entry/X11.64 = "res://{GDNATIVE_PATH}/lib/libwebrtc_native.linux.{TARGET}.64.so"
entry/X11.32 = "res://{GDNATIVE_PATH}/lib/libwebrtc_native.linux.{TARGET}.32.so"
entry/Server.64 = "res://{GDNATIVE_PATH}/lib/libwebrtc_native.linux.{TARGET}.64.so"
entry/Server.32 = "res://{GDNATIVE_PATH}/lib/libwebrtc_native.linux.{TARGET}.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}.arm64v8.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}.armv7.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"
dependency/Windows.64 = [ ]
dependency/Windows.32 = [ ]
dependency/X11.64 = [ ]
dependency/X11.32 = [ ]
dependency/Server.64 = [ ]
dependency/Server.32 = [ ]
dependency/Android.armeabi-v7a = [ ]
dependency/Android.arm64-v8a = [ ]

View File

@@ -1,16 +0,0 @@
#include "WebRTCLibPeer.hpp"
using namespace godot_webrtc;
WebRTCLibPeer::GodotCSDO::GodotCSDO(WebRTCLibPeer *parent) {
this->parent = parent;
}
void WebRTCLibPeer::GodotCSDO::OnSuccess(webrtc::SessionDescriptionInterface *desc) {
// serialize this offer and send it to the remote peer:
std::string sdp; // sdp = session description protocol
desc->ToString(&sdp);
parent->queue_signal("offer_created", 2, desc->type().c_str(), sdp.c_str());
};
void WebRTCLibPeer::GodotCSDO::OnFailure(const std::string &error){};

View File

@@ -1,19 +0,0 @@
#include "WebRTCLibPeer.hpp"
using namespace godot_webrtc;
WebRTCLibPeer::GodotDCO::GodotDCO(WebRTCLibPeer *parent) {
this->parent = parent;
}
void WebRTCLibPeer::GodotDCO::OnMessage(const webrtc::DataBuffer &buffer) {
const uint8_t *data = buffer.data.data<uint8_t>();
uint8_t *memory_controlled_buffer = new uint8_t[buffer.data.size()];
std::copy(data, data + buffer.data.size(), memory_controlled_buffer);
parent->queue_packet(memory_controlled_buffer, buffer.data.size());
};
void WebRTCLibPeer::GodotDCO::OnStateChange(){};
void WebRTCLibPeer::GodotDCO::OnBufferedAmountChange(uint64_t previous_amount){};

View File

@@ -1,46 +0,0 @@
#include "WebRTCLibPeer.hpp"
using namespace godot_webrtc;
WebRTCLibPeer::GodotPCO::GodotPCO(WebRTCLibPeer *parent) {
this->parent = parent;
}
void WebRTCLibPeer::GodotPCO::OnSignalingChange(webrtc::PeerConnectionInterface::SignalingState new_state) {
}
void WebRTCLibPeer::GodotPCO::OnAddStream(rtc::scoped_refptr<webrtc::MediaStreamInterface> stream) {
}
void WebRTCLibPeer::GodotPCO::OnRemoveStream(rtc::scoped_refptr<webrtc::MediaStreamInterface> stream) {
}
void WebRTCLibPeer::GodotPCO::OnDataChannel(rtc::scoped_refptr<webrtc::DataChannelInterface> data_channel) {
}
void WebRTCLibPeer::GodotPCO::OnRenegotiationNeeded() {
}
void WebRTCLibPeer::GodotPCO::OnIceConnectionChange(webrtc::PeerConnectionInterface::IceConnectionState new_state) {
}
void WebRTCLibPeer::GodotPCO::OnIceGatheringChange(webrtc::PeerConnectionInterface::IceGatheringState new_state) {
}
void WebRTCLibPeer::GodotPCO::OnIceCandidate(const webrtc::IceCandidateInterface *candidate) {
// Serialize the candidate and send it to the remote peer:
godot::Dictionary candidateSDP;
godot::String candidateSdpMidName = candidate->sdp_mid().c_str();
int candidateSdpMlineIndexName = candidate->sdp_mline_index();
std::string sdp;
candidate->ToString(&sdp);
godot::String candidateSdpName = sdp.c_str();
parent->queue_signal("new_ice_candidate",
3,
candidateSdpMidName,
candidateSdpMlineIndexName,
candidateSdpName);
}

View File

@@ -1,11 +0,0 @@
#include "WebRTCLibPeer.hpp"
using namespace godot_webrtc;
WebRTCLibPeer::GodotSSDO::GodotSSDO(WebRTCLibPeer *parent) {
this->parent = parent;
}
void WebRTCLibPeer::GodotSSDO::OnSuccess(){};
void WebRTCLibPeer::GodotSSDO::OnFailure(const std::string &error){};

View File

@@ -0,0 +1,180 @@
#include "WebRTCLibDataChannel.hpp"
#include "GDNativeLibrary.hpp"
#include "NativeScript.hpp"
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
WebRTCLibDataChannel *WebRTCLibDataChannel::new_data_channel(rtc::scoped_refptr<webrtc::DataChannelInterface> p_channel) {
// Invalid channel result in NULL return
ERR_FAIL_COND_V(p_channel.get() == nullptr, NULL);
// Instance a WebRTCDataChannelGDNative object
godot::WebRTCDataChannelGDNative *out = godot::WebRTCDataChannelGDNative::_new();
// Set our implementation as it's script
godot::NativeScript *script = godot::NativeScript::_new();
script->set_library(godot::detail::get_wrapper<godot::GDNativeLibrary>((godot_object *)godot::gdnlib));
script->set_class_name("WebRTCLibDataChannel");
out->set_script(script);
// Bind the data channel to the ScriptInstance userdata (our script)
WebRTCLibDataChannel *tmp = out->cast_to<WebRTCLibDataChannel>(out);
tmp->bind_channel(p_channel);
return tmp;
}
void WebRTCLibDataChannel::bind_channel(rtc::scoped_refptr<webrtc::DataChannelInterface> p_channel) {
ERR_FAIL_COND(p_channel.get() == nullptr);
channel = p_channel;
label = p_channel->label();
protocol = p_channel->protocol();
channel->RegisterObserver(&observer);
}
void WebRTCLibDataChannel::queue_packet(const uint8_t *data, uint32_t size) {
mutex->lock();
godot::PoolByteArray packet;
packet.resize(size);
{
godot::PoolByteArray::Write w = packet.write();
memcpy(w.ptr(), data, size);
}
packet_queue.push(packet);
mutex->unlock();
}
void WebRTCLibDataChannel::set_write_mode(godot_int mode) {
}
godot_int WebRTCLibDataChannel::get_write_mode() const {
return 0;
}
bool WebRTCLibDataChannel::was_string_packet() const {
return false;
}
WebRTCLibDataChannel::ChannelState WebRTCLibDataChannel::get_ready_state() const {
ERR_FAIL_COND_V(channel.get() == nullptr, STATE_CLOSED);
return (ChannelState)channel->state();
}
const char *WebRTCLibDataChannel::get_label() const {
ERR_FAIL_COND_V(channel.get() == nullptr, "");
return label.c_str();
}
bool WebRTCLibDataChannel::is_ordered() const {
ERR_FAIL_COND_V(channel.get() == nullptr, false);
return channel->ordered();
}
int WebRTCLibDataChannel::get_id() const {
ERR_FAIL_COND_V(channel.get() == nullptr, -1);
return channel->id();
}
int WebRTCLibDataChannel::get_max_packet_life_time() const {
ERR_FAIL_COND_V(channel.get() == nullptr, 0);
return channel->maxRetransmitTime();
}
int WebRTCLibDataChannel::get_max_retransmits() const {
ERR_FAIL_COND_V(channel.get() == nullptr, 0);
return channel->maxRetransmits();
}
const char *WebRTCLibDataChannel::get_protocol() const {
ERR_FAIL_COND_V(channel.get() == nullptr, "");
return protocol.c_str();
}
bool WebRTCLibDataChannel::is_negotiated() const {
ERR_FAIL_COND_V(channel.get() == nullptr, false);
return channel->negotiated();
}
godot_error WebRTCLibDataChannel::poll() {
return GODOT_OK;
}
void WebRTCLibDataChannel::close() {
if(channel.get() != nullptr) {
channel->Close();
channel->UnregisterObserver();
}
}
godot_error WebRTCLibDataChannel::get_packet(const uint8_t **r_buffer, int *r_len) {
ERR_FAIL_COND_V(packet_queue.empty(), GODOT_ERR_UNAVAILABLE);
mutex->lock();
// Update current packet and pop queue
current_packet = packet_queue.front();
packet_queue.pop();
// Set out buffer and size (buffer will be gone at next get_packet or close)
*r_buffer = current_packet.read().ptr();
*r_len = current_packet.size();
mutex->unlock();
return GODOT_OK;
}
godot_error WebRTCLibDataChannel::put_packet(const uint8_t *p_buffer, int p_len) {
ERR_FAIL_COND_V(channel.get() == nullptr, GODOT_ERR_UNAVAILABLE);
webrtc::DataBuffer webrtc_buffer(rtc::CopyOnWriteBuffer(p_buffer, p_len), true);
ERR_FAIL_COND_V(!channel->Send(webrtc_buffer), GODOT_FAILED);
return GODOT_OK;
}
godot_int WebRTCLibDataChannel::get_available_packet_count() const {
return packet_queue.size();
}
godot_int WebRTCLibDataChannel::get_max_packet_size() const {
return 1200;
}
void WebRTCLibDataChannel::_register_methods() {
}
void WebRTCLibDataChannel::_init() {
register_interface(&interface);
}
WebRTCLibDataChannel::WebRTCLibDataChannel() : observer(this) {
mutex = new std::mutex;
}
WebRTCLibDataChannel::~WebRTCLibDataChannel() {
close();
if (_owner) {
register_interface(NULL);
}
delete mutex;
}

View File

@@ -0,0 +1,76 @@
#ifndef WEBRTC_DATA_CHANNEL_H
#define WEBRTC_DATA_CHANNEL_H
#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 "net/WebRTCDataChannelNative.hpp"
#include "PoolArrays.hpp"
#include <mutex>
namespace godot_webrtc {
class WebRTCLibDataChannel : public WebRTCDataChannelNative {
GODOT_CLASS(WebRTCLibDataChannel, WebRTCDataChannelNative);
private:
class ChannelObserver : public webrtc::DataChannelObserver {
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::queue<godot::PoolByteArray> packet_queue;
godot::PoolByteArray current_packet;
std::string label;
std::string protocol;
public:
static WebRTCLibDataChannel *new_data_channel(rtc::scoped_refptr<webrtc::DataChannelInterface> p_channel);
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;
godot_error poll();
void close();
/* PacketPeer */
virtual godot_error get_packet(const uint8_t **r_buffer, int *r_len);
virtual godot_error put_packet(const uint8_t *p_buffer, int p_len);
virtual godot_int get_available_packet_count() const;
virtual godot_int get_max_packet_size() const;
WebRTCLibDataChannel();
~WebRTCLibDataChannel();
};
} // namespace godot_webrtc
#endif // WEBRTC_DATA_CHANNEL_H

View File

@@ -1,202 +0,0 @@
#include "WebRTCLibPeer.hpp"
using namespace godot_webrtc;
void WebRTCLibPeer::set_write_mode(godot_int mode) {
}
godot_int WebRTCLibPeer::get_write_mode() const {
return 0;
}
bool WebRTCLibPeer::was_string_packet() const {
return false;
}
godot_int WebRTCLibPeer::get_connection_state() const {
return 0;
}
godot_error WebRTCLibPeer::create_offer() {
peer_connection->CreateOffer(
ptr_csdo, // CreateSessionDescriptionObserver* observer,
nullptr // webrtc::PeerConnectionInterface::RTCOfferAnswerOptions() // const MediaConstraintsInterface* constraints
);
return GODOT_OK;
}
godot_error WebRTCLibPeer::set_remote_description(const char *type, const char *sdp) {
godot_error err = set_description(type, sdp, false); //false meaning !isLocal because it is remote
peer_connection->CreateAnswer(ptr_csdo, webrtc::PeerConnectionInterface::RTCOfferAnswerOptions());
return err;
}
godot_error WebRTCLibPeer::set_local_description(const char *type, const char *sdp) {
return set_description(type, sdp, true); // isLocal == true
}
godot_error WebRTCLibPeer::add_ice_candidate(const char *sdpMidName, int sdpMlineIndexName, const char *sdpName) {
webrtc::SdpParseError *error = nullptr;
webrtc::IceCandidateInterface *candidate = webrtc::CreateIceCandidate(
sdpMidName,
sdpMlineIndexName,
sdpName,
error);
// @TODO do something if there's an error (if error, or if !candidate)
if (error || !candidate)
std::cout << "ERROR with creating ICE candidate (" << error << ")\n";
if (!peer_connection->AddIceCandidate(candidate))
ERR_PRINT("Error with adding ICE candidate");
return GODOT_OK;
}
godot_error WebRTCLibPeer::poll() {
std::function<void()> signal;
while (!signal_queue.empty()) {
mutex_signal_queue->lock();
signal = signal_queue.front();
signal_queue.pop();
mutex_signal_queue->unlock();
signal();
}
return GODOT_OK;
}
godot_error WebRTCLibPeer::get_packet(const uint8_t **r_buffer, int *r_len) {
if (packet_queue_size == 0)
return GODOT_ERR_UNAVAILABLE;
mutex_packet_queue->lock();
uint8_t *current_packet = packet_queue.front();
*r_buffer = current_packet;
*r_len = packet_sizes_queue.front();
packet_queue.pop();
packet_sizes_queue.pop();
mutex_packet_queue->unlock();
--packet_queue_size;
return GODOT_OK;
}
godot_error WebRTCLibPeer::put_packet(const uint8_t *p_buffer, int p_len) {
webrtc::DataBuffer webrtc_buffer(rtc::CopyOnWriteBuffer(p_buffer, p_len), true);
data_channel->Send(webrtc_buffer);
return GODOT_OK; // @TODO properly return any Error we may get.
}
godot_int WebRTCLibPeer::get_available_packet_count() const {
return packet_queue_size;
}
godot_int WebRTCLibPeer::get_max_packet_size() const {
return 1200;
}
void WebRTCLibPeer::_register_methods() {
}
void WebRTCLibPeer::_init() {
register_interface(&interface);
// initialize variables:
mutex_signal_queue = new std::mutex;
mutex_packet_queue = new std::mutex;
packet_queue_size = 0;
// create a PeerConnectionFactoryInterface:
signaling_thread = new rtc::Thread;
signaling_thread->Start();
pc_factory = webrtc::CreateModularPeerConnectionFactory(
nullptr, // rtc::Thread* network_thread,
nullptr, // rtc::Thread* worker_thread,
signaling_thread,
nullptr, // std::unique_ptr<cricket::MediaEngineInterface> media_engine,
nullptr, // std::unique_ptr<CallFactoryInterface> call_factory,
nullptr // std::unique_ptr<RtcEventLogFactoryInterface> event_log_factory
);
if (pc_factory.get() == nullptr) { // PeerConnectionFactory couldn't be created. Fail the method call.
ERR_PRINT("PeerConnectionFactory could not be created");
// return GODOT_FAILED;
}
// create PeerConnection configuration and add the ice servers:
webrtc::PeerConnectionInterface::RTCConfiguration configuration;
//webrtc::PeerConnectionInterface::IceServer ice_server;
//ice_server.uri = "stun:stun.l.google.com:19302"; // @FIXME allow user to input ice servers
//configuration.servers.push_back(ice_server);
// create a PeerConnection object:
peer_connection = pc_factory->CreatePeerConnection(configuration, nullptr, nullptr, &pco);
if (peer_connection.get() == nullptr) { // PeerConnection couldn't be created. Fail the method call.
ERR_PRINT("PeerConnection could not be created");
// return GODOT_FAILED;
}
// create a DataChannel
webrtc::DataChannelInit data_channel_config;
data_channel_config.negotiated = true; // True if the channel has been externally negotiated
data_channel_config.id = 0;
data_channel = peer_connection->CreateDataChannel("channel", &data_channel_config);
// @TODO (NONESSENTIAL) create data_channel check. fail function call if data_channel isn't created
data_channel->RegisterObserver(&dco);
}
WebRTCLibPeer::WebRTCLibPeer() :
dco(this),
pco(this),
ptr_csdo(new rtc::RefCountedObject<GodotCSDO>(this)),
ptr_ssdo(new rtc::RefCountedObject<GodotSSDO>(this)) {
}
WebRTCLibPeer::~WebRTCLibPeer() {
if (_owner) {
register_interface(NULL);
}
delete mutex_signal_queue;
delete mutex_packet_queue;
}
void WebRTCLibPeer::queue_signal(godot::String p_name, int p_argc, const godot::Variant &p_arg1, const godot::Variant &p_arg2, const godot::Variant &p_arg3) {
mutex_signal_queue->lock();
signal_queue.push(
[this, p_name, p_argc, p_arg1, p_arg2, p_arg3] {
if (p_argc == 2)
emit_signal(p_name, p_arg1, p_arg2);
else
emit_signal(p_name, p_arg1, p_arg2, p_arg3);
});
mutex_signal_queue->unlock();
}
void WebRTCLibPeer::queue_packet(uint8_t *buffer, int buffer_size) {
mutex_packet_queue->lock();
packet_queue.push(buffer);
packet_sizes_queue.push(buffer_size);
++packet_queue_size;
mutex_packet_queue->unlock();
}
godot_error WebRTCLibPeer::set_description(const char *type, const char *sdp, bool isLocal) {
// webrtc::SdpType type = (isOffer) ? webrtc::SdpType::kOffer : webrtc::SdpType::kAnswer;
godot::String string_sdp = sdp;
webrtc::SdpType sdptype = (godot::String(type) == godot::String("offer")) ? webrtc::SdpType::kOffer : webrtc::SdpType::kAnswer;
std::unique_ptr<webrtc::SessionDescriptionInterface> desc =
webrtc::CreateSessionDescription(sdptype, sdp);
if (isLocal) {
peer_connection->SetLocalDescription(
ptr_ssdo, // @TODO (NONESSENTIAL, OPTIONAL) replace this with DummySetSessionDescriptionObserver::Create()
desc.release());
} else {
peer_connection->SetRemoteDescription(
ptr_ssdo, // @TODO (NONESSENTIAL, OPTIONAL) replace this with DummySetSessionDescriptionObserver::Create()
desc.release());
}
return GODOT_OK;
}

View File

@@ -1,117 +0,0 @@
#ifndef WEBRTC_PEER_H
#define WEBRTC_PEER_H
#include <Godot.hpp> // Godot.hpp must go first, or windows builds breaks
#include "api/peerconnectioninterface.h" // interface for all things needed from WebRTC
#include "media/base/mediaengine.h" // needed for CreateModularPeerConnectionFactory
#include <functional> // std::function
#include <mutex> // mutex @TODO replace std::mutex with Godot mutex
#include "net/WebRTCPeerNative.hpp"
namespace godot_webrtc {
class WebRTCLibPeer : public WebRTCPeerNative {
GODOT_CLASS(WebRTCLibPeer, WebRTCPeerNative);
public:
static void _register_methods();
void _init();
void set_write_mode(godot_int mode);
godot_int get_write_mode() const;
bool was_string_packet() const;
godot_int get_connection_state() const;
godot_error create_offer();
godot_error set_remote_description(const char *type, const char *sdp);
godot_error set_local_description(const char *type, const char *sdp);
godot_error add_ice_candidate(const char *sdpMidName, int sdpMlineIndexName, const char *sdpName);
godot_error poll();
/* WebRTCPeer */
virtual godot_error get_packet(const uint8_t **r_buffer, int *r_len);
virtual godot_error put_packet(const uint8_t *p_buffer, int p_len);
virtual godot_int get_available_packet_count() const;
virtual godot_int get_max_packet_size() const;
WebRTCLibPeer();
~WebRTCLibPeer();
/* helper functions */
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_signal(godot::StringName p_name, Variant_ARG_LIST);
void queue_packet(uint8_t *, int);
godot_error set_description(const char *type, const char *sdp, bool isLocal);
/** DataChannelObserver callback functions **/
class GodotDCO : public webrtc::DataChannelObserver {
public:
WebRTCLibPeer *parent;
GodotDCO(WebRTCLibPeer *parent);
void OnMessage(const webrtc::DataBuffer &buffer) override;
void OnStateChange() override; // UNUSED
void OnBufferedAmountChange(uint64_t previous_amount) override; // UNUSED
};
/** PeerConnectionObserver callback functions **/
class GodotPCO : public webrtc::PeerConnectionObserver {
public:
WebRTCLibPeer *parent;
GodotPCO(WebRTCLibPeer *parent);
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;
void OnIceCandidate(const webrtc::IceCandidateInterface *candidate) override;
};
/** CreateSessionDescriptionObserver callback functions **/
class GodotCSDO : public webrtc::CreateSessionDescriptionObserver {
public:
WebRTCLibPeer *parent;
GodotCSDO(WebRTCLibPeer *parent);
void OnSuccess(webrtc::SessionDescriptionInterface *desc) override;
void OnFailure(const std::string &error) override;
};
/** SetSessionDescriptionObserver callback functions **/
class GodotSSDO : public webrtc::SetSessionDescriptionObserver {
public:
WebRTCLibPeer *parent;
GodotSSDO(WebRTCLibPeer *parent);
void OnSuccess() override;
void OnFailure(const std::string &error) override;
};
GodotDCO dco;
GodotPCO pco;
rtc::scoped_refptr<GodotSSDO> ptr_ssdo;
rtc::scoped_refptr<GodotCSDO> ptr_csdo;
std::mutex *mutex_signal_queue;
std::mutex *mutex_packet_queue;
int packet_queue_size;
std::queue<uint8_t *> packet_queue;
std::queue<int> packet_sizes_queue;
std::queue<std::function<void()> > signal_queue;
rtc::Thread *signaling_thread;
rtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface> pc_factory;
rtc::scoped_refptr<webrtc::PeerConnectionInterface> peer_connection;
rtc::scoped_refptr<webrtc::DataChannelInterface> data_channel;
};
} // namespace godot_webrtc
#endif // WEBRTC_PEER_H

View File

@@ -0,0 +1,277 @@
#include "WebRTCDataChannel.hpp"
#include "WebRTCDataChannelGDNative.hpp"
#include "WebRTCLibPeerConnection.hpp"
#include "WebRTCLibDataChannel.hpp"
using namespace godot_webrtc;
std::unique_ptr<rtc::Thread> WebRTCLibPeerConnection::signaling_thread = nullptr;
// PeerConnectionObserver
void WebRTCLibPeerConnection::GodotPCO::OnIceCandidate(const webrtc::IceCandidateInterface *candidate) {
godot::Dictionary candidateSDP;
godot::String candidateSdpMidName = candidate->sdp_mid().c_str();
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() {
if (signaling_thread.get() == nullptr) {
signaling_thread = rtc::Thread::Create();
}
signaling_thread->Start();
}
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) {
godot::Variant v;
webrtc::PeerConnectionInterface::IceServer ice_server;
godot::String url;
ERR_FAIL_COND_V(!p_server.has("urls"), GODOT_ERR_INVALID_PARAMETER);
// Parse mandatory URL
v = p_server["urls"];
if (v.get_type() == godot::Variant::STRING) {
url = v;
ice_server.urls.push_back(url.utf8().get_data());
} else if (v.get_type() == godot::Variant::ARRAY) {
godot::Array names = v;
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 {
ERR_FAIL_V(GODOT_ERR_INVALID_PARAMETER);
}
// Parse credentials (only meaningful for TURN, only support password)
if (p_server.has("username") && (v = p_server["username"]) && v.get_type() == godot::Variant::STRING) {
ice_server.username = (v.operator godot::String()).utf8().get_data();
}
if (p_server.has("credential") && (v = p_server["credential"]) && v.get_type() == godot::Variant::STRING) {
ice_server.password = (v.operator godot::String()).utf8().get_data();
}
r_config.servers.push_back(ice_server);
return GODOT_OK;
}
godot_error _parse_channel_config(webrtc::DataChannelInit &r_config, godot::Dictionary p_dict) {
godot::Variant v;
#define _SET_N(PROP, PNAME, TYPE) if (p_dict.has(#PROP)) { v = p_dict[#PROP]; if(v.get_type() == godot::Variant::TYPE) r_config.PNAME = v; }
#define _SET(PROP, TYPE) _SET_N(PROP, PROP, TYPE)
_SET(negotiated, BOOL);
_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();
}
// 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 == -1 : r_config.id != -1, GODOT_ERR_INVALID_PARAMETER);
// Only one of maxRetransmits and maxRetransmitTime can be set on a channel.
ERR_FAIL_COND_V(r_config.maxRetransmits && r_config.maxRetransmitTime, GODOT_ERR_INVALID_PARAMETER);
return GODOT_OK;
}
WebRTCLibPeerConnection::ConnectionState WebRTCLibPeerConnection::get_connection_state() const {
ERR_FAIL_COND_V(peer_connection.get() == nullptr, STATE_CLOSED);
webrtc::PeerConnectionInterface::IceConnectionState state = peer_connection->ice_connection_state();
switch(state) {
case webrtc::PeerConnectionInterface::kIceConnectionNew:
return STATE_NEW;
case webrtc::PeerConnectionInterface::kIceConnectionChecking:
return STATE_CONNECTING;
case webrtc::PeerConnectionInterface::kIceConnectionConnected:
return STATE_CONNECTED;
case webrtc::PeerConnectionInterface::kIceConnectionCompleted:
return STATE_CONNECTED;
case webrtc::PeerConnectionInterface::kIceConnectionFailed:
return STATE_FAILED;
case webrtc::PeerConnectionInterface::kIceConnectionDisconnected:
return STATE_DISCONNECTED;
case webrtc::PeerConnectionInterface::kIceConnectionClosed:
return STATE_CLOSED;
default:
return STATE_CLOSED;
}
}
godot_error WebRTCLibPeerConnection::initialize(const godot_dictionary *p_config) {
webrtc::PeerConnectionInterface::RTCConfiguration config;
godot::Dictionary d = *(godot::Dictionary *)p_config;
godot::Variant v;
if (d.has("iceServers") && (v = d["iceServers"]) && v.get_type() == godot::Variant::ARRAY) {
godot::Array servers = v;
for (int i = 0; i < servers.size(); i++) {
v = servers[i];
ERR_FAIL_COND_V(v.get_type() != godot::Variant::DICTIONARY, GODOT_ERR_INVALID_PARAMETER);
godot_error err;
godot::Dictionary server = v;
err = _parse_ice_server(config, server);
ERR_FAIL_COND_V(err != GODOT_OK, err);
}
}
return _create_pc(config);
}
godot_object *WebRTCLibPeerConnection::create_data_channel(const char *p_channel, const godot_dictionary *p_channel_config) {
ERR_FAIL_COND_V(peer_connection.get() == nullptr, NULL);
// Read config from dictionary
webrtc::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));
ERR_FAIL_COND_V(wrapper == NULL, NULL);
return wrapper->_owner;
}
godot_error WebRTCLibPeerConnection::create_offer() {
ERR_FAIL_COND_V(peer_connection.get() == nullptr, GODOT_ERR_UNCONFIGURED);
peer_connection->CreateOffer(ptr_csdo, webrtc::PeerConnectionInterface::RTCOfferAnswerOptions());
return GODOT_OK;
}
#define _MAKE_DESC(TYPE, SDP) webrtc::CreateSessionDescription((godot::String(TYPE) == godot::String("offer") ? webrtc::SdpType::kOffer : webrtc::SdpType::kAnswer), SDP)
godot_error WebRTCLibPeerConnection::set_remote_description(const char *type, const char *sdp) {
ERR_FAIL_COND_V(peer_connection.get() == nullptr, GODOT_ERR_UNCONFIGURED);
std::unique_ptr<webrtc::SessionDescriptionInterface> desc = _MAKE_DESC(type, sdp);
if (desc->GetType() == webrtc::SdpType::kOffer) {
ptr_ssdo->make_offer = true;
}
peer_connection->SetRemoteDescription(ptr_ssdo, desc.release());
return GODOT_OK;
}
godot_error WebRTCLibPeerConnection::set_local_description(const char *type, const char *sdp) {
ERR_FAIL_COND_V(peer_connection.get() == nullptr, GODOT_ERR_UNCONFIGURED);
std::unique_ptr<webrtc::SessionDescriptionInterface> desc = _MAKE_DESC(type, sdp);
peer_connection->SetLocalDescription(ptr_ssdo, desc.release());
return GODOT_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() {
ERR_FAIL_COND_V(peer_connection.get() == nullptr, GODOT_ERR_UNCONFIGURED);
while (!signal_queue.empty()) {
mutex_signal_queue->lock();
Signal signal = signal_queue.front();
signal_queue.pop();
mutex_signal_queue->unlock();
signal.emit(this);
}
return GODOT_OK;
}
void WebRTCLibPeerConnection::close() {
if (peer_connection.get() != nullptr) {
peer_connection->Close();
}
peer_connection = nullptr;
while(!signal_queue.empty()) {
signal_queue.pop();
}
}
void WebRTCLibPeerConnection::_register_methods() {
}
void WebRTCLibPeerConnection::_init() {
register_interface(&interface);
// initialize variables:
mutex_signal_queue = new std::mutex;
// create a PeerConnectionFactoryInterface:
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) {
ERR_FAIL_COND_V(pc_factory.get() == nullptr, GODOT_ERR_BUG);
peer_connection = nullptr;
peer_connection = pc_factory->CreatePeerConnection(config, nullptr, nullptr, &pco);
if (peer_connection.get() == nullptr) { // PeerConnection couldn't be created. Fail the method call.
ERR_PRINT("PeerConnection could not be created");
return GODOT_FAILED;
}
return GODOT_OK;
}
WebRTCLibPeerConnection::WebRTCLibPeerConnection() :
pco(this),
ptr_csdo(new rtc::RefCountedObject<GodotCSDO>(this)),
ptr_ssdo(new rtc::RefCountedObject<GodotSSDO>(this)) {
}
WebRTCLibPeerConnection::~WebRTCLibPeerConnection() {
if (_owner) {
register_interface(NULL);
}
close();
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) {
mutex_signal_queue->lock();
const godot::Variant argv[3] = { p_arg1, p_arg2, p_arg3 };
signal_queue.push(Signal(p_name, p_argc, argv));
mutex_signal_queue->unlock();
}

View File

@@ -0,0 +1,135 @@
#ifndef WEBRTC_PEER_H
#define WEBRTC_PEER_H
#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"
namespace godot_webrtc {
class WebRTCLibPeerConnection : public WebRTCPeerConnectionNative {
GODOT_CLASS(WebRTCLibPeerConnection, WebRTCPeerConnectionNative);
private:
godot_error _create_pc(webrtc::PeerConnectionInterface::RTCConfiguration &config);
static std::unique_ptr<rtc::Thread> signaling_thread;
public:
static void _register_methods();
static void initialize_signaling();
static void deinitialize_signaling();
void _init();
ConnectionState get_connection_state() const;
godot_error initialize(const godot_dictionary *p_config);
godot_object *create_data_channel(const char *p_channel, const godot_dictionary *p_channel_config);
godot_error create_offer();
godot_error set_remote_description(const char *type, const char *sdp);
godot_error set_local_description(const char *type, const char *sdp);
godot_error add_ice_candidate(const char *sdpMidName, int sdpMlineIndexName, const char *sdpName);
godot_error poll();
void close();
WebRTCLibPeerConnection();
~WebRTCLibPeerConnection();
/* helper functions */
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 {
godot::String method;
godot::Variant argv[3];
int argc = 0;
public:
Signal(godot::String p_method, int p_argc, const godot::Variant *p_argv) {
method = p_method;
argc = p_argc;
for (int i = 0; i < argc; i++) {
argv[i] = p_argv[i];
}
}
void emit(godot::Object *p_object) {
if (argc == 0) {
p_object->emit_signal(method);
} else if (argc == 1) {
p_object->emit_signal(method, argv[0]);
} else if (argc == 2) {
p_object->emit_signal(method, argv[0], argv[1]);
} else if (argc == 3) {
p_object->emit_signal(method, argv[0], argv[1], argv[2]);
}
}
};
GodotPCO pco;
rtc::scoped_refptr<GodotSSDO> ptr_ssdo;
rtc::scoped_refptr<GodotCSDO> ptr_csdo;
std::mutex *mutex_signal_queue = nullptr;
std::queue<Signal> signal_queue;
rtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface> pc_factory;
rtc::scoped_refptr<webrtc::PeerConnectionInterface> peer_connection;
};
} // namespace godot_webrtc
#endif // WEBRTC_PEER_H

View File

@@ -1,25 +1,106 @@
#include "WebRTCLibPeer.hpp"
#include "net/WebRTCPeerNative.hpp"
#include "WebRTCLibDataChannel.hpp"
#include "WebRTCLibPeerConnection.hpp"
#include "net/WebRTCPeerConnectionNative.hpp"
#include <gdnative_api_struct.gen.h>
#include <net/godot_net.h>
/* Singleton */
static bool _singleton = false;
static const godot_object *_singleton_lib = NULL;
static const godot_gdnative_core_api_struct *_singleton_api = NULL;
static godot_class_constructor _create_ns_cb = NULL;
static godot_method_bind *_set_script_mb = NULL;
static godot_method_bind *_set_class_name_mb = NULL;
static godot_method_bind *_set_library_mb = NULL;
void unregistered() {
_singleton = false; // We are no longer the active singleton
}
godot_error create_peer_connection_wp(godot_object *out) {
ERR_FAIL_COND_V(!_singleton, GODOT_FAILED);
// Create Script
godot_object *script = _create_ns_cb();
ERR_FAIL_COND_V(!script, GODOT_FAILED);
const void *args[] = { (void *)_singleton_lib };
_singleton_api->godot_method_bind_ptrcall(_set_library_mb, script, args, nullptr);
godot_string s;
_singleton_api->godot_string_new(&s);
_singleton_api->godot_string_parse_utf8(&s, "WebRTCLibPeerConnection");
const void *args2[] = { (void *)&s };
_singleton_api->godot_method_bind_ptrcall(_set_class_name_mb, script, args2, nullptr);
_singleton_api->godot_string_destroy(&s);
// Bind script to Object
const void *args3[] = { (void *)script };
_singleton_api->godot_method_bind_ptrcall(_set_script_mb, out, args3, nullptr);
return GODOT_OK;
}
godot_net_webrtc_library library = {
{3, 2},
&unregistered,
&create_peer_connection_wp,
NULL,
};
extern "C" void GDN_EXPORT godot_gdnative_singleton() {
if (WebRTCPeerConnectionNative::_net_api) {
ERR_FAIL_COND(!godot::gdnlib);
_singleton_lib = godot::gdnlib;
ERR_FAIL_COND(!godot::api);
_singleton_api = godot::api;
_create_ns_cb = godot::api->godot_get_class_constructor("NativeScript");
ERR_FAIL_COND(!_create_ns_cb);
_set_script_mb = godot::api->godot_method_bind_get_method("Object", "set_script");
ERR_FAIL_COND(!_set_script_mb);
_set_class_name_mb = godot::api->godot_method_bind_get_method("NativeScript", "set_class_name");
ERR_FAIL_COND(!_set_class_name_mb);
_set_library_mb = godot::api->godot_method_bind_get_method("NativeScript", "set_library");
ERR_FAIL_COND(!_set_library_mb);
// If registration is successful _singleton will be set to true
_singleton = WebRTCPeerConnectionNative::_net_api->godot_net_set_webrtc_library(&library) == GODOT_OK;
if (!_singleton)
ERR_PRINT("Failed initializing webrtc singleton library");
}
}
/* Godot export stuff */
extern "C" void GDN_EXPORT godot_gdnative_init(godot_gdnative_init_options *o) {
const godot_gdnative_core_api_struct *api = o->api_struct;
for (int i = 0; i < api->num_extensions; i++) {
if (api->extensions[i]->type == GDNATIVE_EXT_NET) {
WebRTCPeerNative::_net_api = (godot_gdnative_ext_net_api_struct *)api->extensions[i];
if (api->extensions[i]->type != GDNATIVE_EXT_NET)
continue;
const godot_gdnative_ext_net_api_struct *net_api = (godot_gdnative_ext_net_api_struct *)api->extensions[i];
if (!net_api->next)
break;
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_webrtc::WebRTCLibPeerConnection::initialize_signaling();
godot::Godot::gdnative_init(o);
}
extern "C" void GDN_EXPORT godot_gdnative_terminate(godot_gdnative_terminate_options *o) {
if (_singleton) { // If we are the active singleton, unregister
WebRTCPeerConnectionNative::_net_api->godot_net_set_webrtc_library(NULL);
}
godot_webrtc::WebRTCLibPeerConnection::deinitialize_signaling();
godot::Godot::gdnative_terminate(o);
}
extern "C" void GDN_EXPORT godot_nativescript_init(void *handle) {
godot::Godot::nativescript_init(handle);
godot::register_class<godot_webrtc::WebRTCLibPeer>();
godot::register_class<godot_webrtc::WebRTCLibPeerConnection>();
godot::register_class<godot_webrtc::WebRTCLibDataChannel>();
}

View File

@@ -0,0 +1,92 @@
#include "WebRTCDataChannelNative.hpp"
#include "net/WebRTCPeerConnectionNative.hpp"
void WebRTCDataChannelNative::register_interface(const godot_net_webrtc_data_channel *p_interface) {
ERR_FAIL_COND(!WebRTCPeerConnectionNative::_net_api);
WebRTCPeerConnectionNative::_net_api->godot_net_bind_webrtc_data_channel(_owner, p_interface);
}
void WebRTCDataChannelNative::_register_methods() {}
void WebRTCDataChannelNative::_init() {
register_interface(&interface);
}
WebRTCDataChannelNative::~WebRTCDataChannelNative() {
if (_owner) {
register_interface(NULL);
}
}
/*
* The C interface that implements WebRTCDataChannel.
* In this case it forwards calls to our C++ class, but could be plain C,
* 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) {
return ((WebRTCDataChannelNative *)user)->get_packet(r_buffer, r_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);
}
godot_int get_available_packet_count_wdc(const void *user) {
return ((WebRTCDataChannelNative *)user)->get_available_packet_count();
}
godot_int get_max_packet_size_wdc(const void *user) {
return ((WebRTCDataChannelNative *)user)->get_max_packet_size();
}
void set_write_mode_wdc(void *user, godot_int write_mode) {
((WebRTCDataChannelNative *)user)->set_write_mode(write_mode);
}
godot_int get_write_mode_wdc(const void *user) {
return ((WebRTCDataChannelNative *)user)->get_write_mode();
}
bool was_string_packet_wdc(const void *user) {
return ((WebRTCDataChannelNative *)user)->was_string_packet();
}
godot_int get_ready_state_wdc(const void *user) {
return (godot_int)(((WebRTCDataChannelNative *)user)->get_ready_state());
}
const char *get_label_wdc(const void *user) {
return ((WebRTCDataChannelNative *)user)->get_label();
}
bool is_ordered_wdc(const void *user) {
return ((WebRTCDataChannelNative *)user)->is_ordered();
}
int get_id_wdc(const void *user) {
return ((WebRTCDataChannelNative *)user)->get_id();
}
int get_max_packet_life_time_wdc(const void *user) {
return ((WebRTCDataChannelNative *)user)->get_max_packet_life_time();
}
int get_max_retransmits_wdc(const void *user) {
return ((WebRTCDataChannelNative *)user)->get_max_retransmits();
}
const char *get_protocol_wdc(const void *user) {
return ((WebRTCDataChannelNative *)user)->get_protocol();
}
bool is_negotiated_wdc(const void *user) {
return ((WebRTCDataChannelNative *)user)->is_negotiated();
}
godot_error poll_wdc(void *user) {
return ((WebRTCDataChannelNative *)user)->poll();
}
void close_wdc(void *user) {
((WebRTCDataChannelNative *)user)->close();
}

View File

@@ -0,0 +1,91 @@
#ifndef WEBRTC_DATA_CHANNEL_NATIVE
#define WEBRTC_DATA_CHANNEL_NATIVE
#include <Godot.hpp>
#include <Reference.hpp>
#include <WebRTCDataChannelGDNative.hpp>
#include <net/godot_net.h>
/* Forward declare interface functions */
godot_error get_packet_wdc(void *, const uint8_t **, int *);
godot_error put_packet_wdc(void *, const uint8_t *, int);
godot_int get_available_packet_count_wdc(const void *);
godot_int get_max_packet_size_wdc(const void *);
void set_write_mode_wdc(void *, godot_int);
godot_int get_write_mode_wdc(const void *);
bool was_string_packet_wdc(const void *);
godot_int get_ready_state_wdc(const void *);
const char *get_label_wdc(const void *);
bool is_ordered_wdc(const void *);
int get_id_wdc(const void *);
int get_max_packet_life_time_wdc(const void *);
int get_max_retransmits_wdc(const void *);
const char *get_protocol_wdc(const void *);
bool is_negotiated_wdc(const void *);
godot_error poll_wdc(void *);
void close_wdc(void *);
class WebRTCDataChannelNative : public godot::WebRTCDataChannelGDNative {
GODOT_CLASS(WebRTCDataChannelNative, godot::WebRTCDataChannelGDNative);
protected:
godot_net_webrtc_data_channel interface = {
{ 3, 1 },
this,
&get_packet_wdc,
&put_packet_wdc,
&get_available_packet_count_wdc,
&get_max_packet_size_wdc,
&set_write_mode_wdc,
&get_write_mode_wdc,
&was_string_packet_wdc,
&get_ready_state_wdc,
&get_label_wdc,
&is_ordered_wdc,
&get_id_wdc,
&get_max_packet_life_time_wdc,
&get_max_retransmits_wdc,
&get_protocol_wdc,
&is_negotiated_wdc,
&poll_wdc,
&close_wdc,
NULL,
};
public:
static void _register_methods();
void _init();
void register_interface(const godot_net_webrtc_data_channel *interface);
virtual void set_write_mode(godot_int mode) = 0;
virtual godot_int get_write_mode() const = 0;
virtual bool was_string_packet() const = 0;
virtual ChannelState get_ready_state() const = 0;
virtual const char *get_label() const = 0;
virtual bool is_ordered() const = 0;
virtual int get_id() const = 0;
virtual int get_max_packet_life_time() const = 0;
virtual int get_max_retransmits() const = 0;
virtual const char *get_protocol() const = 0;
virtual bool is_negotiated() const = 0;
virtual godot_error poll() = 0;
virtual void close() = 0;
/* PacketPeer */
virtual godot_error get_packet(const uint8_t **r_buffer, int *r_len) = 0;
virtual godot_error put_packet(const uint8_t *p_buffer, int p_len) = 0;
virtual godot_int get_available_packet_count() const = 0;
virtual godot_int get_max_packet_size() const = 0;
~WebRTCDataChannelNative();
};
#endif // WEBRTC_DATA_CHANNEL_NATIVE

View File

@@ -0,0 +1,65 @@
#include "WebRTCPeerConnectionNative.hpp"
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) {
ERR_FAIL_COND(!_net_api);
_net_api->godot_net_bind_webrtc_peer_connection(_owner, p_interface);
}
void WebRTCPeerConnectionNative::_register_methods() {}
void WebRTCPeerConnectionNative::_init() {
register_interface(&interface);
}
WebRTCPeerConnectionNative::~WebRTCPeerConnectionNative() {
if (_owner) {
register_interface(NULL);
}
}
/*
* The C interface that implements WebRTCPeerConnection.
* In this case it forwards calls to our C++ class, but could be plain C,
* and you could use void *user for any kind of state struct pointer you have.
*/
godot_int get_connection_state_wp(const void *user) {
return (godot_int)((WebRTCPeerConnectionNative *)user)->get_connection_state();
}
godot_error initialize_wp(void *user, const godot_dictionary *p_config) {
return ((WebRTCPeerConnectionNative *)user)->initialize(p_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);
}
godot_error create_offer_wp(void *user) {
return ((WebRTCPeerConnectionNative *)user)->create_offer();
}
godot_error create_answer_wp(void *user) {
return GODOT_ERR_UNAVAILABLE; // Not implemented, not used yet.
}
godot_error set_remote_description_wp(void *user, const char *type, const char *sdp) {
return ((WebRTCPeerConnectionNative *)user)->set_remote_description(type, sdp);
}
godot_error set_local_description_wp(void *user, const char *type, const char *sdp) {
return ((WebRTCPeerConnectionNative *)user)->set_local_description(type, sdp);
}
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);
}
godot_error poll_wp(void *user) {
return ((WebRTCPeerConnectionNative *)user)->poll();
}
void close_wp(void *user) {
((WebRTCPeerConnectionNative *)user)->close();
}

View File

@@ -0,0 +1,66 @@
#ifndef WEBRTC_PEER_NATIVE
#define WEBRTC_PEER_NATIVE
#include <Godot.hpp>
#include <Reference.hpp>
#include <WebRTCPeerConnectionGDNative.hpp>
#include <net/godot_net.h>
/* Forward declare interface functions */
godot_int get_connection_state_wp(const void *);
godot_error initialize_wp(void *, const godot_dictionary *);
godot_object *create_data_channel_wp(void *, const char *, const godot_dictionary *);
godot_error create_offer_wp(void *);
godot_error create_answer_wp(void *);
godot_error set_remote_description_wp(void *, const char *, const char *);
godot_error set_local_description_wp(void *, const char *, const char *);
godot_error add_ice_candidate_wp(void *, const char *, int, const char *);
godot_error poll_wp(void *);
void close_wp(void *);
class WebRTCPeerConnectionNative : public godot::WebRTCPeerConnectionGDNative {
GODOT_CLASS(WebRTCPeerConnectionNative, godot::WebRTCPeerConnectionGDNative);
protected:
godot_net_webrtc_peer_connection interface = {
{ 3, 1 },
this,
&get_connection_state_wp,
&initialize_wp,
&create_data_channel_wp,
&create_offer_wp,
&create_answer_wp,
&set_remote_description_wp,
&set_local_description_wp,
&add_ice_candidate_wp,
&poll_wp,
&close_wp,
NULL,
};
public:
static void _register_methods();
static const godot_gdnative_ext_net_3_2_api_struct *_net_api;
void _init();
void register_interface(const godot_net_webrtc_peer_connection *interface);
virtual ConnectionState get_connection_state() const = 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_error create_offer() = 0;
virtual godot_error set_remote_description(const char *type, const char *sdp) = 0;
virtual godot_error set_local_description(const char *type, const char *sdp) = 0;
virtual godot_error add_ice_candidate(const char *sdpMidName, int sdpMlineIndexName, const char *sdpName) = 0;
virtual godot_error poll() = 0;
virtual void close() = 0;
~WebRTCPeerConnectionNative();
};
#endif // WEBRTC_PEER_NATIVE

View File

@@ -1,77 +0,0 @@
#include "WebRTCPeerNative.hpp"
const godot_gdnative_ext_net_api_struct *WebRTCPeerNative::_net_api = NULL;
void WebRTCPeerNative::register_interface(const godot_net_webrtc_peer *p_interface) {
ERR_FAIL_COND(!_net_api);
_net_api->godot_net_bind_webrtc_peer(_owner, p_interface);
}
void WebRTCPeerNative::_register_methods() {}
void WebRTCPeerNative::_init() {
register_interface(&interface);
}
WebRTCPeerNative::~WebRTCPeerNative() {
if (_owner) {
register_interface(NULL);
}
}
/*
* The C interface that implements WebRTCPeer.
* In this case it forwards calls to our C++ class, but could be plain C,
* and you could use void *user for any kind of state struct pointer you have.
*/
godot_error get_packet_wp(void *user, const uint8_t **r_buffer, int *r_len) {
return ((WebRTCPeerNative *)user)->get_packet(r_buffer, r_len);
}
godot_error put_packet_wp(void *user, const uint8_t *p_buffer, int p_len) {
return ((WebRTCPeerNative *)user)->put_packet(p_buffer, p_len);
}
godot_int get_available_packet_count_wp(const void *user) {
return ((WebRTCPeerNative *)user)->get_available_packet_count();
}
godot_int get_max_packet_size_wp(const void *user) {
return ((WebRTCPeerNative *)user)->get_max_packet_size();
}
void set_write_mode_wp(void *user, godot_int write_mode) {
((WebRTCPeerNative *)user)->set_write_mode(write_mode);
}
godot_int get_write_mode_wp(const void *user) {
return ((WebRTCPeerNative *)user)->get_write_mode();
}
bool was_string_packet_wp(const void *user) {
return ((WebRTCPeerNative *)user)->was_string_packet();
}
godot_int get_connection_state_wp(const void *user) {
return ((WebRTCPeerNative *)user)->get_connection_state();
}
godot_error create_offer_wp(void *user) {
return ((WebRTCPeerNative *)user)->create_offer();
}
godot_error set_remote_description_wp(void *user, const char *type, const char *sdp) {
return ((WebRTCPeerNative *)user)->set_remote_description(type, sdp);
}
godot_error set_local_description_wp(void *user, const char *type, const char *sdp) {
return ((WebRTCPeerNative *)user)->set_local_description(type, sdp);
}
godot_error add_ice_candidate_wp(void *user, const char *sdpMidName, int sdpMlineIndexName, const char *sdpName) {
return ((WebRTCPeerNative *)user)->add_ice_candidate(sdpMidName, sdpMlineIndexName, sdpName);
}
godot_error poll_wp(void *user) {
return ((WebRTCPeerNative *)user)->poll();
}

View File

@@ -1,80 +0,0 @@
#ifndef WEBRTC_PEER_NATIVE
#define WEBRTC_PEER_NATIVE
#include <Godot.hpp>
#include <Reference.hpp>
#include <WebRTCPeerGDNative.hpp>
#include <net/godot_net.h>
/* Forward declare interface functions */
godot_error get_packet_wp(void *, const uint8_t **, int *);
godot_error put_packet_wp(void *, const uint8_t *, int);
godot_int get_available_packet_count_wp(const void *);
godot_int get_max_packet_size_wp(const void *);
void set_write_mode_wp(void *, godot_int);
godot_int get_write_mode_wp(const void *);
bool was_string_packet_wp(const void *);
godot_int get_connection_state_wp(const void *);
godot_error create_offer_wp(void *);
godot_error set_remote_description_wp(void *, const char *, const char *);
godot_error set_local_description_wp(void *, const char *, const char *);
godot_error add_ice_candidate_wp(void *, const char *, int, const char *);
godot_error poll_wp(void *);
class WebRTCPeerNative : public godot::WebRTCPeerGDNative {
GODOT_CLASS(WebRTCPeerNative, godot::WebRTCPeerGDNative);
protected:
godot_net_webrtc_peer interface = {
{ 3, 1 },
this,
&get_packet_wp,
&put_packet_wp,
&get_available_packet_count_wp,
&get_max_packet_size_wp,
&set_write_mode_wp,
&get_write_mode_wp,
&was_string_packet_wp,
&get_connection_state_wp,
&create_offer_wp,
&set_remote_description_wp,
&set_local_description_wp,
&add_ice_candidate_wp,
&poll_wp,
NULL,
};
public:
static void _register_methods();
static const godot_gdnative_ext_net_api_struct *_net_api;
void _init();
void register_interface(const godot_net_webrtc_peer *interface);
virtual void set_write_mode(godot_int mode) = 0;
virtual godot_int get_write_mode() const = 0;
virtual bool was_string_packet() const = 0;
virtual godot_int get_connection_state() const = 0;
virtual godot_error create_offer() = 0;
virtual godot_error set_remote_description(const char *type, const char *sdp) = 0;
virtual godot_error set_local_description(const char *type, const char *sdp) = 0;
virtual godot_error add_ice_candidate(const char *sdpMidName, int sdpMlineIndexName, const char *sdpName) = 0;
virtual godot_error poll() = 0;
/* PacketPeer */
virtual godot_error get_packet(const uint8_t **r_buffer, int *r_len) = 0;
virtual godot_error put_packet(const uint8_t *p_buffer, int p_len) = 0;
virtual godot_int get_available_packet_count() const = 0;
virtual godot_int get_max_packet_size() const = 0;
~WebRTCPeerNative();
};
#endif // WEBRTC_PEER_NATIVE

View File

@@ -1,2 +0,0 @@
*
!.gitignore