diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 000000000..5008ddfcf Binary files /dev/null and b/.DS_Store differ diff --git a/.appveyor.yml b/.appveyor.yml index 96b785d93..1dc7787d9 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -3,29 +3,29 @@ configuration: - Debug image: + - Ubuntu - Visual Studio 2019 - Visual Studio 2015 - - Visual Studio 2013 platform: - x64 - x86 build_script: - - ps: $VSIMG = $Env:APPVEYOR_BUILD_WORKER_IMAGE; $CNFG = $Env:CONFIGURATION + - ps: $VSIMG = $Env:APPVEYOR_BUILD_WORKER_IMAGE; $CNFG = $Env:CONFIGURATION # use a few differing arguments depending on VS version to exercise different options during builds - - ps: if ($VSIMG -match '2019' -and $CNFG -eq "Release") { .\scripts\build-windows.ps1 -STATIC_LINK_SSL ON -BUILD_APPS ON -UNIT_TESTS ON -BONDING ON } - - ps: if ($VSIMG -match '2019' -and $CNFG -eq "Debug") { .\scripts\build-windows.ps1 -STATIC_LINK_SSL ON -BUILD_APPS ON } + - ps: if ($VSIMG -match '2019' -and $CNFG -eq "Release") { .\scripts\build-windows.ps1 -STATIC_LINK_SSL ON -BUILD_APPS ON -UNIT_TESTS ON -BONDING ON -ENABLE_SWIG ON} + - ps: if ($VSIMG -match '2019' -and $CNFG -eq "Debug") { .\scripts\build-windows.ps1 -STATIC_LINK_SSL ON -BUILD_APPS ON -ENABLE_SWIG ON} - ps: if ($VSIMG -match '2015' -and $CNFG -eq "Release") { .\scripts\build-windows.ps1 -STATIC_LINK_SSL ON -BUILD_APPS ON -UNIT_TESTS ON -BONDING ON} - ps: if ($VSIMG -match '2015' -and $CNFG -eq "Debug") { .\scripts\build-windows.ps1 -STATIC_LINK_SSL ON -BUILD_APPS OFF } - - ps: if ($VSIMG -match '2013' -and $CNFG -eq "Release") { .\scripts\build-windows.ps1 -CXX11 OFF -BUILD_APPS ON } - - ps: if ($VSIMG -match '2013' -and $CNFG -eq "Debug") { Exit-AppveyorBuild } # just skip 2013 debug build for speed + - sh: pwsh ./scripts/build-lin-docker.ps1 -ENABLE_SWIG ON test_script: - ps: if ( $Env:RUN_UNIT_TESTS ) { cd ./_build; ctest -E "TestIPv6.v6_calls_v4" --extra-verbose -C $Env:CONFIGURATION; cd ../ } - + after_build: -- cmd: >- - scripts/gather-package.bat - 7z a SRT-%APPVEYOR_REPO_BRANCH%-%CONFIGURATION%-Win%PLATFORM%-%VS_VERSION%-%APPVEYOR_BUILD_VERSION%.zip %APPVEYOR_BUILD_FOLDER%\package\* - appveyor PushArtifact SRT-%APPVEYOR_REPO_BRANCH%-%CONFIGURATION%-Win%PLATFORM%-%VS_VERSION%-%APPVEYOR_BUILD_VERSION%.zip + - cmd: cd %APPVEYOR_BUILD_FOLDER% + - cmd: scripts/gather-package.bat + - cmd: scripts/create-win64-nuget.bat + - cmd: 7z a SRT-%APPVEYOR_REPO_BRANCH%-%CONFIGURATION%-Win%PLATFORM%-%VS_VERSION%-%APPVEYOR_BUILD_VERSION%.zip %APPVEYOR_BUILD_FOLDER%\package\* + - cmd: appveyor PushArtifact SRT-%APPVEYOR_REPO_BRANCH%-%CONFIGURATION%-Win%PLATFORM%-%VS_VERSION%-%APPVEYOR_BUILD_VERSION%.zip diff --git a/.gitignore b/.gitignore index 699d0e1b4..3bdec2300 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,11 @@ vcpkg/ # LSP compile_commands.json + +# Nuget packages +packages/ +package/ +*.nupkg + +# Swig bindings +/srtcore/swig_bindings diff --git a/CMakeLists.txt b/CMakeLists.txt index 4611f0ca2..7946d35ac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -167,6 +167,8 @@ option(USE_BUSY_WAITING "Enable more accurate sending times at a cost of potenti option(USE_GNUSTL "Get c++ library/headers from the gnustl.pc" OFF) option(ENABLE_SOCK_CLOEXEC "Enable setting SOCK_CLOEXEC on a socket" ON) option(ENABLE_SHOW_PROJECT_CONFIG "Enable show Project Configuration" OFF) +option(ENABLE_SWIG "Enable SWIG, used to generate alternative language bindings" OFF) +option(ENABLE_SWIG_CSHARP "Enable SWIG binding generation for CSharp language (only effective if ENABLE_SWIG is ON)" ON) option(ENABLE_CLANG_TSA "Enable Clang Thread Safety Analysis" OFF) @@ -1462,6 +1464,40 @@ endif() endif() +#SWIG support - adding bindings for other languages into a new library that statically include the main SRT lib +if(ENABLE_SWIG) + + #older cmakes do not understand SWIG in a trusted manner (e.g. VS2015 AppVeyor cmake is too old, and Travis Xenial) + if (${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} GREATER 3.21) + cmake_policy(SET CMP0078 NEW) + cmake_policy(SET CMP0086 NEW) + cmake_policy(SET CMP0122 NEW) + else() + message(FATAL_ERROR "ENABLE_SWIG is set, but cmake version is not at least 3.21 and SWIG support may be broken on earlier editions") + endif() + + #on windows, there is no swig just in the path - nuget / scripted downloads must be pointed at (linux it just works) + if(MICROSOFT) + #if nuget has been used to install swig in the project packages, bind to that + if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/packages/swig/swigwin-4.1.1/swig.exe") + set(SWIG_EXECUTABLE "${CMAKE_CURRENT_SOURCE_DIR}/packages/swig/swigwin-4.1.1/swig.exe") + endif() + endif() + + FIND_PACKAGE(SWIG REQUIRED) + INCLUDE(${SWIG_USE_FILE}) + + if(UNIX AND NOT APPLE) + list(APPEND CMAKE_SWIG_FLAGS "-DSWIGWORDSIZE64") + endif() + + if(ENABLE_SWIG_CSHARP) + swig_add_library(srt_swig_csharp LANGUAGE csharp SOURCES srtcore/srt.i OUTPUT_DIR ${CMAKE_BINARY_DIR}/swig_bindings/csharp) + swig_link_libraries(srt_swig_csharp ${srt_link_library} ${DEPENDS_srt}) + set_property(TARGET srt_swig_csharp PROPERTY SWIG_COMPILE_OPTIONS -namespace SrtSharp) + endif() +endif() + if (ENABLE_UNITTESTS AND ENABLE_CXX11) if (${CMAKE_VERSION} VERSION_LESS "3.10.0") diff --git a/googletest/googletest-src b/googletest/googletest-src new file mode 160000 index 000000000..703bd9caa --- /dev/null +++ b/googletest/googletest-src @@ -0,0 +1 @@ +Subproject commit 703bd9caab50b139428cea1aaff9974ebee5742e diff --git a/scripts/Dockerfile.linux b/scripts/Dockerfile.linux new file mode 100644 index 000000000..98ff3a333 --- /dev/null +++ b/scripts/Dockerfile.linux @@ -0,0 +1,39 @@ +FROM debian:bullseye + +LABEL maintainer="Lewis Kirkaldie " + +ENV TZ=Europe/Berlin +ENV APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=1 +ENV LANG=C.UTF-8 LC_ALL=C.UTF-8 +ENV DEBIAN_FRONTEND=noninteractive +ENV DOTNET_CLI_TELEMETRY_OPTOUT=1 + +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + +#base dev packages and OS tools, including mono and boost other library dependencies +RUN apt-get update && apt-get upgrade -y && apt-get install -y \ + build-essential git nano gnupg apt-utils unzip sudo bzip2 \ + apt-transport-https curl wget ca-certificates cpio \ + libssl-dev software-properties-common \ + p7zip-full libpcre2-dev bison automake && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +# add powershell core & dotnet 6.0 +# RUN curl https://packages.microsoft.com/keys/microsoft.asc | apt-key add - && \ +# sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-debian-bullseye-prod bullseye main" > /etc/apt/sources.list.d/microsoft.list' && \ +# apt update && sudo apt install -y powershell && \ +# apt-get install -y dotnet-sdk-6.0 && \ +# apt-get clean && \ +# rm -rf /var/lib/apt/lists/* + +#add cmake 3.24.1 +RUN mkdir -p /usr/src/cmake \ +&& curl -L https://github.com/Kitware/CMake/releases/download/v3.24.1/cmake-3.24.1-linux-x86_64.tar.gz -o /usr/src/cmake/cmake.tar.gz -sS \ +&& tar -C /usr/src/cmake -xzf /usr/src/cmake/cmake.tar.gz \ +&& ln -s /usr/src/cmake/cmake-3.24.1-linux-x86_64/bin/cmake /usr/bin/cmake \ +&& rm /usr/src/cmake/cmake.tar.gz + +#add swig 4.1 +RUN cd /usr/src && git clone https://github.com/swig/swig.git && cd swig && git checkout release-4.1 \ +&& ./autogen.sh && ./configure && make && make install && cd ../ && rm -rf /usr/src/swig \ No newline at end of file diff --git a/scripts/build-lin-docker.bat b/scripts/build-lin-docker.bat new file mode 100644 index 000000000..78a926cb5 --- /dev/null +++ b/scripts/build-lin-docker.bat @@ -0,0 +1,3 @@ +@ECHO OFF +%SYSTEMROOT%\System32\WindowsPowerShell\v1.0\PowerShell.exe -Command "& '%~dpn0.ps1'" +pause diff --git a/scripts/build-lin-docker.ps1 b/scripts/build-lin-docker.ps1 new file mode 100755 index 000000000..3cacc7e36 --- /dev/null +++ b/scripts/build-lin-docker.ps1 @@ -0,0 +1,62 @@ +#! /usr/bin/pwsh + +################################################################################ +# Linux SRT Build Script +#============================ +# Usable on a Linux machine (or container), or called by CI systems like AppVeyor +# +# By default produces a 64-bit Release binary using C++11 threads, without +# encryption or unit tests enabled, but including test apps. +################################################################################ + +param ( + [Parameter()][String]$CONFIGURATION = "Release", + [Parameter()][String]$BUILDCONTAINER = "srtselfbuildcontainer", + [Parameter()][String]$DEVENV_PLATFORM = "x64", + [Parameter()][String]$ENABLE_ENCRYPTION = "OFF", + [Parameter()][String]$STATIC_LINK_SSL = "OFF", + [Parameter()][String]$CXX11 = "ON", + [Parameter()][String]$BUILD_APPS = "ON", + [Parameter()][String]$UNIT_TESTS = "OFF", + [Parameter()][String]$BUILD_DIR = "_build-linux", + [Parameter()][String]$BONDING = "ON", + [Parameter()][String]$ENABLE_SWIG = "ON", + [Parameter()][String]$ENABLE_SWIG_CSHARP = "ON" +) + +$projectRoot = Join-Path $PSScriptRoot "/.." -Resolve + +if($BUILDCONTAINER -eq "srtselfbuildcontainer") { + # the default build container name was passed - so rebuild this self-contained build environment first + # on a CI system, there may be a known-suitable build container already existing, so this can speed up builds + # but if this is on default, we can just self-serve + $execVar = "docker build -t $BUILDCONTAINER -f $($projectRoot)/scripts/Dockerfile.linux ." + Write-Output $execVar + Invoke-Expression "& $execVar" +} + +# clear any previous build and create & enter the build directory +$buildDir = Join-Path "$projectRoot" "$BUILD_DIR" +Write-Output "Creating (or cleaning if already existing) the folder $buildDir for project files and outputs" +Remove-Item -Path $buildDir -Recurse -Force -ErrorAction SilentlyContinue | Out-Null +New-Item -ItemType Directory -Path $buildDir -ErrorAction SilentlyContinue | Out-Null + +# build the cmake command flags from arguments +$cmakeFlags = "-DCMAKE_BUILD_TYPE=$CONFIGURATION " + + "-DENABLE_STDCXX_SYNC=$CXX11 " + + "-DENABLE_APPS=$BUILD_APPS " + + "-DENABLE_ENCRYPTION=$ENABLE_ENCRYPTION " + + "-DENABLE_UNITTESTS=$UNIT_TESTS " + + "-DENABLE_BONDING=$BONDING " + + "-DENABLE_SWIG=$ENABLE_SWIG " + + "-DENABLE_SWIG_CSHARP=$ENABLE_SWIG_CSHARP" + +# generate command for docker run of cmake generation step +$execVar = "docker run --rm -v $($projectRoot):/srt -w /srt/$BUILD_DIR $BUILDCONTAINER cmake -S ../ $cmakeFlags" +Write-Output $execVar +Invoke-Expression "& $execVar" + +# generate command for docker run of cmake build +$execVar = "docker run --rm -v $($projectRoot):/srt -w /srt/$BUILD_DIR $BUILDCONTAINER cmake --build ./" +Write-Output $execVar +Invoke-Expression "& $execVar" diff --git a/scripts/build-linux.sh b/scripts/build-linux.sh new file mode 100755 index 000000000..af483e05c --- /dev/null +++ b/scripts/build-linux.sh @@ -0,0 +1,14 @@ +#! /usr/bin/bash + +set -e +set -o pipefail + +if [ -d Release.linux ]; then + rm -rf ./Release.linux +fi + +printf "\nBuilding Release\n" +mkdir Release.linux +cd Release.linux +cmake -S ../ -DENABLE_STDCXX_SYNC=ON -DENABLE_UNITTESTS=ON -DENABLE_EXPERIMENTAL_BONDING=ON -DENABLE_SWIG=ON +cmake --build ./ \ No newline at end of file diff --git a/scripts/build-windows.ps1 b/scripts/build-windows.ps1 index 4db3fe627..d5d874cca 100644 --- a/scripts/build-windows.ps1 +++ b/scripts/build-windows.ps1 @@ -4,13 +4,13 @@ # Usable on a Windows PC with Powershell and Visual studio, # or called by CI systems like AppVeyor # -# By default produces a VS2019 64-bit Release binary using C++11 threads, without +# By default produces a VS2022 64-bit Release binary using C++11 threads, without # encryption or unit tests enabled, but including test apps. # Before enabling any encryption options, install OpenSSL or set VCKPG flag to build ################################################################################ param ( - [Parameter()][String]$VS_VERSION = "2019", + [Parameter()][String]$VS_VERSION = "2022", [Parameter()][String]$CONFIGURATION = "Release", [Parameter()][String]$DEVENV_PLATFORM = "x64", [Parameter()][String]$ENABLE_ENCRYPTION = "OFF", @@ -20,13 +20,15 @@ param ( [Parameter()][String]$UNIT_TESTS = "OFF", [Parameter()][String]$BUILD_DIR = "_build", [Parameter()][String]$VCPKG_OPENSSL = "OFF", - [Parameter()][String]$BONDING = "OFF" + [Parameter()][String]$BONDING = "OFF", + [Parameter()][String]$ENABLE_SWIG = "OFF", + [Parameter()][String]$ENABLE_SWIG_CSHARP = "ON" ) # cmake can be optionally installed (useful when running interactively on a developer station). # The URL for automatic download is defined later in the script, but it should be possible to just vary the # specific version set below and the URL should be stable enough to still work - you have been warned. -$cmakeVersion = "3.23.2" +$cmakeVersion = "3.25.3" # make all errors trigger a script stop, rather than just carry on $ErrorActionPreference = "Stop" @@ -36,9 +38,9 @@ $projectRoot = Join-Path $PSScriptRoot "/.." -Resolve # if running within AppVeyor, use environment variables to set params instead of passed-in values if ( $Env:APPVEYOR ) { if ( $Env:PLATFORM -eq 'x86' ) { $DEVENV_PLATFORM = 'Win32' } else { $DEVENV_PLATFORM = 'x64' } + if ( $Env:APPVEYOR_BUILD_WORKER_IMAGE -eq 'Visual Studio 2022' ) { $VS_VERSION='2022' } if ( $Env:APPVEYOR_BUILD_WORKER_IMAGE -eq 'Visual Studio 2019' ) { $VS_VERSION='2019' } if ( $Env:APPVEYOR_BUILD_WORKER_IMAGE -eq 'Visual Studio 2015' ) { $VS_VERSION='2015' } - if ( $Env:APPVEYOR_BUILD_WORKER_IMAGE -eq 'Visual Studio 2013' ) { $VS_VERSION='2013' } #if not statically linking OpenSSL, set flag to gather the specific openssl package from the build server into package if ( $STATIC_LINK_SSL -eq 'OFF' ) { $Env:GATHER_SSL_INTO_PACKAGE = $true } @@ -48,24 +50,28 @@ if ( $Env:APPVEYOR ) { $CONFIGURATION = $Env:CONFIGURATION - #appveyor has many openssl installations - place the latest one in the default location unless VS2013 - if( $VS_VERSION -ne '2013' ) { - Remove-Item -Path "C:\OpenSSL-Win32" -Recurse -Force -ErrorAction SilentlyContinue | Out-Null - Remove-Item -Path "C:\OpenSSL-Win64" -Recurse -Force -ErrorAction SilentlyContinue | Out-Null - Copy-Item -Path "C:\OpenSSL-v111-Win32" "C:\OpenSSL-Win32" -Recurse | Out-Null - Copy-Item -Path "C:\OpenSSL-v111-Win64" "C:\OpenSSL-Win64" -Recurse | Out-Null - } + #appveyor has many openssl installations - place the latest v111 edition in the default location + Remove-Item -Path "C:\OpenSSL-Win32" -Recurse -Force -ErrorAction SilentlyContinue | Out-Null + Remove-Item -Path "C:\OpenSSL-Win64" -Recurse -Force -ErrorAction SilentlyContinue | Out-Null + Copy-Item -Path "C:\OpenSSL-v111-Win32" "C:\OpenSSL-Win32" -Recurse | Out-Null + Copy-Item -Path "C:\OpenSSL-v111-Win64" "C:\OpenSSL-Win64" -Recurse | Out-Null +} + +# if running within TeamCity and VS2022 compilation, force SWIG and BONDING to ON (to avoid changing default behaviour on pre-existing CI systems) +# this should be removable ones master branch is merged to always have these params available (so CI can set them reliably itself) +if($Env:TEAMCITY_VERSION -and ($VS_VERSION -eq "2022")){ + $ENABLE_SWIG = "ON" + $BONDING = "ON" } # persist VS_VERSION so it can be used in an artifact name later $Env:VS_VERSION = $VS_VERSION # select the appropriate cmake generator string given the environment +if ( $VS_VERSION -eq '2022' ) { $CMAKE_GENERATOR = 'Visual Studio 17 2022'; $MSBUILDVER = "17.0"; } if ( $VS_VERSION -eq '2019' ) { $CMAKE_GENERATOR = 'Visual Studio 16 2019'; $MSBUILDVER = "16.0"; } if ( $VS_VERSION -eq '2015' -and $DEVENV_PLATFORM -eq 'Win32' ) { $CMAKE_GENERATOR = 'Visual Studio 14 2015'; $MSBUILDVER = "14.0"; } if ( $VS_VERSION -eq '2015' -and $DEVENV_PLATFORM -eq 'x64' ) { $CMAKE_GENERATOR = 'Visual Studio 14 2015 Win64'; $MSBUILDVER = "14.0"; } -if ( $VS_VERSION -eq '2013' -and $DEVENV_PLATFORM -eq 'Win32' ) { $CMAKE_GENERATOR = 'Visual Studio 12 2013'; $MSBUILDVER = "12.0"; } -if ( $VS_VERSION -eq '2013' -and $DEVENV_PLATFORM -eq 'x64' ) { $CMAKE_GENERATOR = 'Visual Studio 12 2013 Win64'; $MSBUILDVER = "12.0"; } # clear any previous build and create & enter the build directory $buildDir = Join-Path "$projectRoot" "$BUILD_DIR" @@ -132,13 +138,32 @@ if ( $VCPKG_OPENSSL -eq "ON" ) { } } +# check to see if SWIG is marked to be used - if so, download swig into packages folder so cmake can find it +if ( $ENABLE_SWIG -eq "ON" ) { + if ( Test-Path "$projectRoot/packages/swig/swigwin-4.1.1" ) { + Write-Output "Found pre-existing copy of Swigwin 4.1.1 - using this binary" + } + else { + #originally, script downloaded from sourceforge from link below, but this was flakey + #and therefore was copied to a Cinegy-controlled S3 mirror bucket + #original source URL: https://deac-fra.dl.sourceforge.net/project/swig/swigwin/swigwin-4.1.1/swigwin-4.1.1.zip + Write-Output "Swigwin 4.1.1 not found - downloading and unpacking..." + Remove-Item -LiteralPath $projectRoot/packages/swig/ -Force -Recurse -ErrorAction Ignore + Invoke-WebRequest 'https://caas-deploy.s3.eu-central-1.amazonaws.com/v1/Thirdparty-Swig-v4.x/swigwin-4.1.1.zip' -OutFile swig.zip + Expand-Archive swig.zip -DestinationPath $projectRoot/packages/swig + Remove-Item swig.zip + } +} + # build the cmake command flags from arguments $cmakeFlags = "-DCMAKE_BUILD_TYPE=$CONFIGURATION " + - "-DENABLE_STDCXX_SYNC=$CXX11 " + - "-DENABLE_APPS=$BUILD_APPS " + - "-DENABLE_ENCRYPTION=$ENABLE_ENCRYPTION " + - "-DENABLE_BONDING=$BONDING " + - "-DENABLE_UNITTESTS=$UNIT_TESTS" + "-DENABLE_STDCXX_SYNC=$CXX11 " + + "-DENABLE_APPS=$BUILD_APPS " + + "-DENABLE_ENCRYPTION=$ENABLE_ENCRYPTION " + + "-DENABLE_BONDING=$BONDING " + + "-DENABLE_UNITTESTS=$UNIT_TESTS " + + "-DENABLE_SWIG=$ENABLE_SWIG " + + "-DENABLE_SWIG_CSHARP=$ENABLE_SWIG_CSHARP" # if VCPKG is flagged to provide OpenSSL, checkout VCPKG and install package if ( $VCPKG_OPENSSL -eq 'ON' ) { @@ -181,8 +206,8 @@ else { $cmakeFlags += " -DOPENSSL_USE_STATIC_LIBS=$STATIC_LINK_SSL " } -# cmake uses a flag for architecture from vs2019, so add that as a suffix -if ( $VS_VERSION -eq '2019' ) { +# cmake uses a flag for architecture from vs2019 onwards, so add that as a suffix +if ( $VS_VERSION -eq '2019' -or $VS_VERSION -eq '2022') { $cmakeFlags += " -A `"$DEVENV_PLATFORM`"" } @@ -229,6 +254,15 @@ if ( $null -eq $msBuildPath ) { & $msBuildPath SRT.sln -m /p:Configuration=$CONFIGURATION /p:Platform=$DEVENV_PLATFORM +# if CSharp SWIG is on, now trigger compilation of these elements (cmake for dotnet is very new, and not available in older versions) +if(($ENABLE_SWIG -eq "ON") -and ($ENABLE_SWIG_CSHARP -eq "ON")){ + Push-Location "$buildDir/swig_bindings/csharp" + Copy-Item "$projectRoot/srtcore/swig_bindings/csharp/*.csproj" . + & dotnet build -c $CONFIGURATION + Copy-Item "bin/$CONFIGURATION/netstandard2.0/*.dll" "$buildDir/$CONFIGURATION" + Pop-Location +} + # return to the directory previously occupied before running the script Pop-Location diff --git a/scripts/create-nuget.ps1 b/scripts/create-nuget.ps1 new file mode 100644 index 000000000..39cea0242 --- /dev/null +++ b/scripts/create-nuget.ps1 @@ -0,0 +1,27 @@ +################################################################################ +# Windows SRT NuGet Package Creation Script +#============================ +# Usable on a Windows PC with Powershell and nuget available, +# or called by CI systems like AppVeyor +# +################################################################################ + +param ( + [Parameter()][String]$VERSION = "1.5.1.10199", + [Parameter()][String]$BUILD_DIR = "_nuget" +) + +# make all errors trigger a script stop, rather than just carry on +$ErrorActionPreference = "Stop" + +$projectRoot = Join-Path $PSScriptRoot "/.." -Resolve +$sourceDir = Join-Path "$projectRoot" "package" +$targetDir = Join-Path "$projectRoot" "$BUILD_DIR" +$nuspecPath = Join-Path "$projectRoot" "scripts/nuget/SrtSharp/SrtSharp.nuspec" + +nuget pack $nuspecPath -version $VERSION-alpha -OutputDirectory $targetDir + +# if antyhing returned non-zero, throw to cause failure in CI +if( $LASTEXITCODE -ne 0 ) { + throw +} diff --git a/scripts/create-win64-nuget.bat b/scripts/create-win64-nuget.bat new file mode 100644 index 000000000..de295bc1a --- /dev/null +++ b/scripts/create-win64-nuget.bat @@ -0,0 +1,29 @@ +rem Bundle CI NuGet package for VS2015 Win64 Release only build (true package should contain multi-arch composite output) +@echo off + +rem If running in AppVeyor, only create a NuGet package on VS2015 x64 Release +IF DEFINED APPVEYOR_BUILD_VERSION ( + IF "%PLATFORM%"=="x64" ( + IF "%VS_VERSION%"=="2015" ( + IF "%CONFIGURATION%"=="Release" ( + echo "Building NuPkg for this build (VS2015, x64 Release)" + nuget pack .\scripts\nuget\SrtSharp\SrtSharp.nuspec -version %APPVEYOR_BUILD_VERSION%-2015-beta + appveyor PushArtifact SrtSharp.%APPVEYOR_BUILD_VERSION%-2015-beta.nupkg + exit 0 + ) + ) + IF "%VS_VERSION%"=="2019" ( + IF "%CONFIGURATION%"=="Release" ( + echo "Building NuPkg for this build (VS2019, x64 Release)" + nuget pack .\scripts\nuget\SrtSharp\SrtSharp.nuspec -version %APPVEYOR_BUILD_VERSION%-beta + appveyor PushArtifact SrtSharp.%APPVEYOR_BUILD_VERSION%-beta.nupkg + exit 0 + ) + ) + ) + echo "Skipping NuPkg build (not VS2015/2019, x64 Release)" + exit 0 +) ELSE ( + rem probably running on a local workstation, so use the first given argument parameter as the version number and don't push or exit + nuget pack .\scripts\nuget\SrtSharp\SrtSharp.nuspec -version %1 +) \ No newline at end of file diff --git a/scripts/gather-package.bat b/scripts/gather-package.bat index a0221cc05..defce9d0d 100644 --- a/scripts/gather-package.bat +++ b/scripts/gather-package.bat @@ -18,9 +18,10 @@ md %APPVEYOR_BUILD_FOLDER%\package\lib IF "%GATHER_SSL_INTO_PACKAGE%"=="True" ( md %APPVEYOR_BUILD_FOLDER%\package\openssl-win%FOLDER_PLATFORM% ) +md %APPVEYOR_BUILD_FOLDER%\package\swig_bindings rem Gather SRT includes, binaries and libs -copy %APPVEYOR_BUILD_FOLDER%\version.h %APPVEYOR_BUILD_FOLDER%\package\include\ +copy %APPVEYOR_BUILD_FOLDER%\_build\version.h %APPVEYOR_BUILD_FOLDER%\package\include\ copy %APPVEYOR_BUILD_FOLDER%\srtcore\*.h %APPVEYOR_BUILD_FOLDER%\package\include\ copy %APPVEYOR_BUILD_FOLDER%\haicrypt\*.h %APPVEYOR_BUILD_FOLDER%\package\include\ copy %APPVEYOR_BUILD_FOLDER%\common\*.h %APPVEYOR_BUILD_FOLDER%\package\include\ @@ -28,10 +29,16 @@ copy %APPVEYOR_BUILD_FOLDER%\common\win\*.h %APPVEYOR_BUILD_FOLDER%\package\incl copy %APPVEYOR_BUILD_FOLDER%\_build\%CONFIGURATION%\*.exe %APPVEYOR_BUILD_FOLDER%\package\bin\ copy %APPVEYOR_BUILD_FOLDER%\_build\%CONFIGURATION%\*.dll %APPVEYOR_BUILD_FOLDER%\package\bin\ copy %APPVEYOR_BUILD_FOLDER%\_build\%CONFIGURATION%\*.lib %APPVEYOR_BUILD_FOLDER%\package\lib\ -copy %APPVEYOR_BUILD_FOLDER%\_build\%CONFIGURATION%\*.pdb %APPVEYOR_BUILD_FOLDER%\package\bin\ rem Gather 3rd party openssl elements IF "%GATHER_SSL_INTO_PACKAGE%"=="True" ( (robocopy c:\openssl-win%FOLDER_PLATFORM%\ %APPVEYOR_BUILD_FOLDER%\package\openssl-win%FOLDER_PLATFORM% /s /e /np) ^& IF %ERRORLEVEL% GTR 1 exit %ERRORLEVEL% ) +IF "%CONFIGURATION%"=="Debug" ( + copy %APPVEYOR_BUILD_FOLDER%\_build\%CONFIGURATION%\*.pdb %APPVEYOR_BUILD_FOLDER%\package\bin\ +) + +rem gather any generated bindings for languages +(robocopy %APPVEYOR_BUILD_FOLDER%\srtcore\swig_bindings\ %APPVEYOR_BUILD_FOLDER%\package\swig_bindings /s /e /np) ^& IF %ERRORLEVEL% GTR 1 exit %ERRORLEVEL% + exit 0 diff --git a/scripts/get-windows-build-packages.bat b/scripts/get-windows-build-packages.bat new file mode 100644 index 000000000..8685119a9 --- /dev/null +++ b/scripts/get-windows-build-packages.bat @@ -0,0 +1,2 @@ +@ECHO OFF +PowerShell.exe -ExecutionPolicy Bypass -Command "& '%~dpn0.ps1'" \ No newline at end of file diff --git a/scripts/get-windows-build-packages.ps1 b/scripts/get-windows-build-packages.ps1 new file mode 100644 index 000000000..94717794a --- /dev/null +++ b/scripts/get-windows-build-packages.ps1 @@ -0,0 +1,6 @@ +#Script to grab external libraries for building these samples + +# prep shared variables +$DEVENV_PLATFORM = 'x64' # alternative is x86 +$FOLDER_PLATFORM = 'win64' # alternative is win32 +nuget install swigwintools -version 4.0.0 \ No newline at end of file diff --git a/scripts/nuget/SrtSharp/SrtSharp.nuspec b/scripts/nuget/SrtSharp/SrtSharp.nuspec new file mode 100644 index 000000000..458c64551 --- /dev/null +++ b/scripts/nuget/SrtSharp/SrtSharp.nuspec @@ -0,0 +1,23 @@ + + + + SrtSharp + SRT Library wrapped for C# + $version$-beta + Cinegy GmbH + Cinegy GmbH + MPL-2.0 + false + Secure, Reliable Transport Library with C# bindings, published by Cinegy GmbH + 2019 SRT Haivision Systems Inc (packaged by Cinegy GmbH). All rights reserved. + readme.md + + + + + + + + + + \ No newline at end of file diff --git a/scripts/nuget/SrtSharp/readme.md b/scripts/nuget/SrtSharp/readme.md new file mode 100644 index 000000000..70ee46061 --- /dev/null +++ b/scripts/nuget/SrtSharp/readme.md @@ -0,0 +1,19 @@ +# SrtSharp + +## Overview +SrtSharp is a .NET wrapper for the official [Haivision SRT](https://github.com/Haivision/srt) developed by [Cinegy](https://www.cinegy.com/). This package utilizes [SWIG](https://www.swig.org/) for interfacing with the SRT library, along with custom adaptations to fit .NET standards. + +## Features +- **.NET Standard 2.0 Library**: Ensures compatibility with a wide range of .NET applications. +- **Cross-Platform Compatibility**: Supports both x64 Linux and Windows runtimes. +- **Efficient SRT Handling**: Leverages Haivision's SRT for low-latency video streaming. + +## Installation +You can install SrtSharp via NuGet Package Manager or the dotnet CLI: + +```shell +dotnet add package SrtSharp +``` + +## Examples +For practical examples of how to use SrtSharp, please refer to the [Cinegy.SRT GitHub repository](https://github.com/Cinegy/Cinegy.SRT). diff --git a/scripts/set-version-metadata.ps1 b/scripts/set-version-metadata.ps1 index 050838e5e..d1d5003a4 100644 --- a/scripts/set-version-metadata.ps1 +++ b/scripts/set-version-metadata.ps1 @@ -40,7 +40,7 @@ if($Env:TEAMCITY_VERSION){ #find C++ resource files and update file description with branch / commit details $FileDescriptionStringRegex = '(\bVALUE\s+\"FileDescription\"\s*\,\s*\")([^\"]*\\\")*[^\"]*(\")' -Get-ChildItem -Path "../srtcore/srt_shared.rc" | ForEach-Object { +Get-ChildItem -Path "$PSScriptRoot/../srtcore/srt_shared.rc" | ForEach-Object { $fileName = $_ Write-Output "Processing metadata changes for file: $fileName" diff --git a/srtcore/srt.h b/srtcore/srt.h index 53b6fd274..c97b01a2c 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -60,6 +60,11 @@ written by #define SRT_API __attribute__ ((visibility("default"))) #endif +#ifdef SWIG + // Remove SRT_API definition when using SWIG + #undef SRT_API + #define SRT_API +#endif // For feature tests if you need. // You can use these constants with SRTO_MINVERSION option. diff --git a/srtcore/srt.i b/srtcore/srt.i new file mode 100644 index 000000000..495f51ef5 --- /dev/null +++ b/srtcore/srt.i @@ -0,0 +1,1424 @@ +/* + * SRT - Secure, Reliable, Transport + * Copyright (c) 2018 Haivision Systems Inc. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +/***************************************************************************** +written by + Lewis Kirkaldie - Cinegy GmbH + Volodymyr Shkolka - Cinegy GmbH + *****************************************************************************/ + +/* +Automatic generatation of bindings via SWIG (http://www.swig.org) +Install swig via the following (or use instructions from the link above): + sudo apt install swig / nuget install swigwintools / download latest from swig.org +Generate the bindings using: + mkdir srtcore/bindings/csharp -p + swig -v -csharp -namespace SrtSharp -outdir ./srtcore/bindings/csharp/ ./srtcore/srt.i +You can now reference the SrtSharp lib in your .Net Core projects. Ensure the srtlib.so (or srt.dll / srt_swig_csharp.dll) is in the binary path of your .NetCore project. +*/ + +%module srt +%{ + #include "srt.h" +%} + +%include +%include +%include + +#if defined(SWIGCSHARP) +// Push anything with an argument name 'buf' back to being an array (e.g. csharp defaults this type to string, which is not ideal here) +%apply unsigned char INOUT[] { + char* buf, + const char* buf + } + +%apply int INOUT[] { const SRTSOCKET listeners[]} +%apply int INOUT[] { const int* fara} + +// ---- Marshal Pointers to IntPtr +%apply void *VOID_INT_PTR { + void *, + SRT_LOG_HANDLER_FN *, // Delegate + srt_listen_callback_fn*, // Delegate + srt_connect_callback_fn*, // Delegate + int *, + byte *, + size_t *, + int64_t *, + SRT_SOCKOPT_CONFIG* + } + +// ---- REF int mapping +%typemap(cstype) int * "ref int" +%typemap(csin, + pre="var pin_$csinput = GCHandle.Alloc($csinput, GCHandleType.Pinned);", + post=" + $csinput = (int)pin_$csinput.Target; + pin_$csinput.Free();" + ) int * + "pin_$csinput.AddrOfPinnedObject()" + +// ---- REF size_t mapping +%typemap(cstype) size_t * "ref ulong" +%typemap(csin, + pre="var pin_$csinput = GCHandle.Alloc($csinput, GCHandleType.Pinned);", + post=" + $csinput = (ulong)pin_$csinput.Target; + pin_$csinput.Free();" + ) size_t * + "pin_$csinput.AddrOfPinnedObject()" + +// ---- OVERRIDE SIGNATURE (struct sockaddr* addr, int* addrlen) +%typemap(in) (struct sockaddr*, int*) { + TransitiveArguments * args = (TransitiveArguments *)$input; + $1 = (struct sockaddr *)args->Arg1; + $2 = (int *)args->Arg2; +} +%typemap(cstype) (struct sockaddr*, int*) "out IPEndPoint" +%typemap(imtype) (struct sockaddr*, int*) "global::System.Runtime.InteropServices.HandleRef" +%typemap(csin, + pre=" + var sock_$csinput = new SocketAddress(AddressFamily.InterNetwork); + var content_$csinput = sock_$csinput.GetInternalBuffer(); + int content_len_$csinput = sock_$csinput.Size; + var pin_content_$csinput = GCHandle.Alloc(content_$csinput, GCHandleType.Pinned); + var pin_content_len_$csinput = GCHandle.Alloc(content_len_$csinput, GCHandleType.Pinned); + + var transitive_$csinput = new TransitiveArguments + { + Arg1 = pin_content_$csinput.AddrOfPinnedObject(), + Arg2 = pin_content_len_$csinput.AddrOfPinnedObject() + }; + var input_$csinput = TransitiveArguments.getCPtr(transitive_$csinput);", + post=" + sock_$csinput.SetInternalSize(content_len_$csinput); + $csinput = sock_$csinput.ToIPEndPoint(); + pin_content_$csinput.Free(); + pin_content_len_$csinput.Free(); + transitive_$csinput.Dispose();" + ) (struct sockaddr*, int*) + "input_$csinput" + +// Apply mappings for different functions with the same signature (struct sockaddr*, int*) +%apply (struct sockaddr*, int*) +{ + (struct sockaddr* addr, int* addrlen), + (struct sockaddr* name, int* namelen) +} + +// ---- OVERRIDE SIGNATURE (const struct sockaddr addr, int addrlen) +%typemap(in) (const struct sockaddr*, int) { + TransitiveArguments * args = (TransitiveArguments *)$input; + $1 = (struct sockaddr *)args->Arg1; + $2 = *args->Arg2; +} +%typemap(cstype) (const struct sockaddr*, int) "IPEndPoint" +%typemap(imtype) (const struct sockaddr*, int) "global::System.Runtime.InteropServices.HandleRef" +%typemap(csin, + pre=" + var sock_$csinput = $csinput.Serialize(); + var content_$csinput = sock_$csinput.GetInternalBuffer(); + int content_len_$csinput = sock_$csinput.Size; + var pin_content_$csinput = GCHandle.Alloc(content_$csinput, GCHandleType.Pinned); + var pin_content_len_$csinput = GCHandle.Alloc(content_len_$csinput, GCHandleType.Pinned); + + var transitive_$csinput = new TransitiveArguments + { + Arg1 = pin_content_$csinput.AddrOfPinnedObject(), + Arg2 = pin_content_len_$csinput.AddrOfPinnedObject() + }; + var input_$csinput = TransitiveArguments.getCPtr(transitive_$csinput);", + post=" + pin_content_$csinput.Free(); + pin_content_len_$csinput.Free(); + transitive_$csinput.Dispose();" + ) (const struct sockaddr*, int) + "input_$csinput" + +// Apply mappings for different functions with the same signature (const struct sockaddr*, int) +%apply (const struct sockaddr*, int) +{ + (const struct sockaddr* name, int namelen), + (const struct sockaddr* local_name, int local_namelen), + (const struct sockaddr* remote_name, int remote_namelen), + (const struct sockaddr* target, int len), + (const struct sockaddr* adr, int namelen) +} + +// ---- OVERRIDE SIGNATURE (const struct sockaddr* /*nullable*/) +%typemap(cstype) (const struct sockaddr*) "IPEndPoint" +%typemap(imtype) (const struct sockaddr*) "global::System.IntPtr" +%typemap(csin, + pre=" + var input_$csinput = IntPtr.Zero; + GCHandle? pin_content_$csinput = null; + if($csinput != null) + { + var sock_$csinput = $csinput.Serialize(); + var content_$csinput = sock_$csinput.GetInternalBuffer(); + pin_content_$csinput = GCHandle.Alloc(content_$csinput, GCHandleType.Pinned); + input_$csinput = pin_content_$csinput.Value.AddrOfPinnedObject(); + }", + post="pin_content_$csinput?.Free();" + ) (const struct sockaddr*) + "input_$csinput" + +// ---- REF int64_t mapping +%typemap(cstype) (int64_t *) "ref long" +%typemap(csin, + pre="var pin_$csinput = GCHandle.Alloc($csinput, GCHandleType.Pinned);", + post=" + $csinput = (long)pin_$csinput.Target; + pin_$csinput.Free();" + ) int64_t * + "pin_$csinput.AddrOfPinnedObject()" + +// ---- SRT_LOG_HANDLER_FN to Delegate mapping +%typemap(cstype) (SRT_LOG_HANDLER_FN *) "SrtLogHandlerDelegate" +%typemap(csin, + pre=" + GCKeeper.Keep(nameof(SrtLogHandlerDelegate), $csinput); + var delegatePtr_$csinput = Marshal.GetFunctionPointerForDelegate($csinput);" + ) SRT_LOG_HANDLER_FN* + "delegatePtr_$csinput" + +// ---- srt_listen_callback_fn to Delegate mapping +%typemap(cstype) (srt_listen_callback_fn *) "SrtListenCallbackDelegate" +%typemap(csin, + pre=" + GCKeeper.Keep($\"{nameof(SrtListenCallbackDelegate)}-{lsn}\", hook_fn); + var delegatePtr_$csinput = Marshal.GetFunctionPointerForDelegate($csinput);" + ) srt_listen_callback_fn* + "delegatePtr_$csinput" + +%typemap(cstype) (srt_connect_callback_fn *) "SrtConnectCallbackDelegate" +%typemap(csin, + pre=" + GCKeeper.Keep($\"{nameof(SrtConnectCallbackDelegate)}-{clr}\", hook_fn); + var delegatePtr_$csinput = Marshal.GetFunctionPointerForDelegate($csinput);" + ) srt_connect_callback_fn* + "delegatePtr_$csinput" + +// ---- Nullable SRT_EPOLL_OPT handling +%typemap(cstype) (const int* events) "SRT_EPOLL_OPT?" +%typemap(csin, + pre=" + GCHandle? pin_$csinput = null; + IntPtr pinAddr_$csinput = IntPtr.Zero; + if($csinput.HasValue) + { + uint opt_$csinput = (uint)$csinput.Value; + pin_$csinput = GCHandle.Alloc(opt_$csinput, GCHandleType.Pinned); + pinAddr_$csinput = pin_$csinput.Value.AddrOfPinnedObject(); + } + ", + post="pin_$csinput?.Free();" + ) const int* events + "pinAddr_$csinput" + +// --- Map srt_getlasterror return type to SRT_ERRNO +%typemap(csbase) SRT_ERRNO "int" +%typemap(cstype) int srt_getlasterror "SRT_ERRNO" +%typemap(csout) int srt_getlasterror { return (SRT_ERRNO)$imcall; } + +// ------------------------------------------------------------ +// ------------------------- SRTSOCKET ------------------------ +// ------------------------------------------------------------ + +// --- Map all SRTSOCKET to artificial SRTSOCKET structure +%typemap(cstype) SRTSOCKET "SRTSOCKET" + +// --- Map srt_create_socket return type to SRTSOCKET +%typemap(cstype) int srt_create_socket "SRTSOCKET" + +// --- Map srt_accept return type to SRTSOCKET +%typemap(cstype) int srt_accept "SRTSOCKET" + +// ------------------------------------------------------------ +// ------------------------- SYSSOCKET ------------------------ +// ------------------------------------------------------------ + +// --- Map all SYSSOCKET to artificial SYSSOCKET structure +%typemap(cstype) SYSSOCKET "SYSSOCKET" + +// ------------------------------------------------------------ +// ------------------------- EPOLL ---------------------------- +// ------------------------------------------------------------ +// --- Map all int eid to artificial EPOLL structure +%typemap(cstype) int eid "EPOLL" + +// --- Map srt_epoll_create return type to EPOLL +%typemap(cstype) int srt_epoll_create "EPOLL" + +// srt_epoll_wait method parameters transformations +%typemap(in) (SRTSOCKET* , int*) { + TransitiveArguments * args = (TransitiveArguments *)$input; + $1 = args->Arg1; + $2 = (int*)args->Arg2; +} +%typemap(cstype) (SRTSOCKET* , int*) "SRTSOCKET[]" +%typemap(imtype) (SRTSOCKET* , int*) "global::System.Runtime.InteropServices.HandleRef" +%typemap(csin, + pre=" + GCHandle? pin_array_$csinput = null; + GCHandle? pin_array_len_$csinput = null; + if($csinput != null) + { + Array.Clear($csinput, 0, $csinput.Length); + var array_len_$csinput = $csinput.Length; + pin_array_$csinput = GCHandle.Alloc($csinput, GCHandleType.Pinned); + pin_array_len_$csinput = GCHandle.Alloc(array_len_$csinput, GCHandleType.Pinned); + } + + var transitive_$csinput = new TransitiveArguments + { + Arg1 = pin_array_$csinput?.AddrOfPinnedObject() ?? IntPtr.Zero, + Arg2 = pin_array_len_$csinput?.AddrOfPinnedObject() ?? IntPtr.Zero + }; + var input_$csinput = TransitiveArguments.getCPtr(transitive_$csinput);", + post=" + pin_array_$csinput?.Free(); + pin_array_len_$csinput?.Free(); + transitive_$csinput.Dispose();" + ) (SRTSOCKET* , int*) + "input_$csinput" + +// Apply mappings for different functions with the same signature (SRTSOCKET* , int*) +%apply (SRTSOCKET* , int*) +{ + (SRTSOCKET* readfds, int* rnum), + (SRTSOCKET* writefds, int* wnum) +} + +%typemap(in) (SYSSOCKET* , int*) { + TransitiveArguments * args = (TransitiveArguments *)$input; + $1 = args->Arg1; + $2 = (int*)args->Arg2; +} +%typemap(cstype) (SYSSOCKET* , int*) "SYSSOCKET[]" +%typemap(imtype) (SYSSOCKET* , int*) "global::System.Runtime.InteropServices.HandleRef" +%typemap(csin, + pre=" + GCHandle? pin_array_$csinput = null; + GCHandle? pin_array_len_$csinput = null; + if($csinput != null) + { + Array.Clear($csinput, 0, $csinput.Length); + var array_len_$csinput = $csinput.Length; + pin_array_$csinput = GCHandle.Alloc($csinput, GCHandleType.Pinned); + pin_array_len_$csinput = GCHandle.Alloc(array_len_$csinput, GCHandleType.Pinned); + } + + var transitive_$csinput = new TransitiveArguments + { + Arg1 = pin_array_$csinput?.AddrOfPinnedObject() ?? IntPtr.Zero, + Arg2 = pin_array_len_$csinput?.AddrOfPinnedObject() ?? IntPtr.Zero + }; + var input_$csinput = TransitiveArguments.getCPtr(transitive_$csinput);", + post=" + pin_array_$csinput?.Free(); + pin_array_len_$csinput?.Free(); + transitive_$csinput.Dispose();" + ) (SYSSOCKET* , int*) + "input_$csinput" + +// Apply mappings for different functions with the same signature (SYSSOCKET* , int*) +%apply (SYSSOCKET* , int*) +{ + (SYSSOCKET* lrfds, int* lrnum), + (SYSSOCKET* lwfds, int* lwnum) +} + +// srt_epoll_uwait method parameters transformations +/* +%inline +{ + int srt_getepoll_event_size() + { + return sizeof(SRT_EPOLL_EVENT); + } +} +*/ + +//%ignore srt_epoll_uwait(int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut); +%ignore SRT_EPOLL_EVENT_STR; +//int srt_epoll_uwait(int eid, SRT_EPOLL_EVENT1* fdsSet, int fdsSize, int64_t msTimeOut); + +%typemap(in) (SRT_EPOLL_EVENT* , int) { + TransitiveArguments * args = (TransitiveArguments *)$input; + $1 = args->Arg1; + $2 = (int)args->Arg2; +} +%typemap(cstype) (SRT_EPOLL_EVENT* , int) "SRT_EPOLL_EVENT[]" +%typemap(imtype) (SRT_EPOLL_EVENT* , int) "global::System.Runtime.InteropServices.HandleRef" +%typemap(csin, + pre=" + GCHandle? pin_array_$csinput = null; + int array_len_$csinput = 0; + if($csinput != null) + { + array_len_$csinput = $csinput.Length; + pin_array_$csinput = GCHandle.Alloc($csinput, GCHandleType.Pinned); + } + + var transitive_$csinput = new TransitiveArguments + { + Arg1 = pin_array_$csinput?.AddrOfPinnedObject() ?? IntPtr.Zero, + Arg2 = new IntPtr(array_len_$csinput) + }; + var input_$csinput = TransitiveArguments.getCPtr(transitive_$csinput);", + post=" + pin_array_$csinput?.Free(); + transitive_$csinput.Dispose();" + ) (SRT_EPOLL_EVENT* , int) + "input_$csinput" + +// Apply mappings for different functions with the same signature (SRT_EPOLL_EVENT* , int) +%apply (SRT_EPOLL_EVENT* , int) +{ + (SRT_EPOLL_EVENT* fdsSet, int fdsSize) +} + +// Rework srt_epoll_set flags type +%inline{ typedef enum SRT_EPOLL_FLAGS SRT_EPOLL_FLAGS_TRANSIENT; } +%ignore srt_epoll_set(int eid, int32_t flags); +int32_t srt_epoll_set(int eid, SRT_EPOLL_FLAGS_TRANSIENT flags); + +// ------------------------------------------------------------ +// ------------------------- LOGGING -------------------------- +// ------------------------------------------------------------ +// --- Map srt_setlogflags member to artificial LogFlag structure +// --- structure itself defined in csharp module imports below +// Type mappings for IM wrapper INT -> managed struct +%inline{ typedef int LogFlag; } + +// Force C# to use LogFlag structure instead original INT +%typemap(cstype) LogFlag "LogFlag" + +// Redefine C# method representation +%ignore srt_setlogflags(int flags); +void srt_setlogflags(LogFlag flags); + +// Forward constants from C side to C# srt module +const LogFlag SRT_LOGF_DISABLE_TIME = SRT_LOGF_DISABLE_TIME; +const LogFlag SRT_LOGF_DISABLE_THREADNAME = SRT_LOGF_DISABLE_THREADNAME; +const LogFlag SRT_LOGF_DISABLE_SEVERITY = SRT_LOGF_DISABLE_SEVERITY; +const LogFlag SRT_LOGF_DISABLE_EOL = SRT_LOGF_DISABLE_EOL; + +// --- Map srt_setloglevel member to artificial LogLevel structure +// --- structure itself defined in csharp module imports below +// Type mappings for IM wrapper INT -> managed struct +%inline{ typedef int LogLevel; } + +// Force C# to use LogLevel structure instead original INT +%typemap(cstype) LogLevel "LogLevel" + +// Redefine C# method representation +%ignore srt_setloglevel(int ll); +void srt_setloglevel(LogLevel logLevel); + +// Forward constants from C side to C# srt module +const LogLevel LOG_DEBUG = LOG_DEBUG; +const LogLevel LOG_NOTICE = LOG_NOTICE; +const LogLevel LOG_WARNING = LOG_WARNING; +const LogLevel LOG_ERR = LOG_ERR; +const LogLevel LOG_CRIT = LOG_CRIT; + +// --- Map srt_addlogfa/srt_dellogfa/srt_resetlogfa members to artificial LogFunctionalArea structure +// --- structure itself defined in csharp module imports below +// Type mappings for IM wrapper INT -> managed struct +%inline{ typedef int LogFunctionalArea; } + +// Force C# to use LogFunctionalArea structure instead original INT +%typemap(cstype) LogFunctionalArea "LogFunctionalArea" + +// Redefine C# method representation +%ignore srt_addlogfa(int fa); +void srt_addlogfa(LogFunctionalArea functionalArea); + +%ignore srt_dellogfa(int fa); +void srt_dellogfa(LogFunctionalArea functionalArea); + +// OVERRIDE SIGNATURE (const int* fara, size_t fara_size) +%typemap(in) (const int* fara, size_t fara_size) { + TransitiveArguments * args = (TransitiveArguments *)$input; + $1 = (int const *)args->Arg1; + $2 = (size_t)*args->Arg2; +} + +%typemap(cstype) (const int* fara, size_t fara_size) "params LogFunctionalArea[]" +%typemap(imtype) (const int* fara, size_t fara_size) "global::System.Runtime.InteropServices.HandleRef" +%typemap(csin, + pre=" + var array_$csinput = $csinput.Select(x => (int)x).ToArray(); + int array_len_$csinput = array_$csinput.Length; + + var pin_array_$csinput = GCHandle.Alloc(array_$csinput, GCHandleType.Pinned); + var pin_array_len_$csinput = GCHandle.Alloc(array_len_$csinput, GCHandleType.Pinned); + + var transitive_$csinput = new TransitiveArguments + { + Arg1 = pin_array_$csinput.AddrOfPinnedObject(), + Arg2 = pin_array_len_$csinput.AddrOfPinnedObject() + }; + var input_$csinput = TransitiveArguments.getCPtr(transitive_$csinput);", + post=" + pin_array_$csinput.Free(); + pin_array_len_$csinput.Free(); + transitive_$csinput.Dispose();" + ) (const int* fara, size_t fara_size) + "input_$csinput" + +// ------------------------------------------------------------ +// ------------------------- FLAGS ---------------------------- +// ------------------------------------------------------------ +// ---- ADD ADDITIONAL method for string flags manipulation +// -- srt_getsockflag_string method +%typemap(in) (void* stringOptVal, int* stringOptLen) { + TransitiveArguments * args = (TransitiveArguments *)$input; + $1 = args->Arg1; + $2 = (int*)args->Arg2; +} +%typemap(cstype) (void* stringOptVal, int* stringOptLen) "out string" +%typemap(imtype) (void* stringOptVal, int* stringOptLen) "global::System.Runtime.InteropServices.HandleRef" +%typemap(csin, + pre=" + var value_len_$csinput = 512; + var value_$csinput = new byte[value_len_$csinput]; + var pin_value_$csinput = GCHandle.Alloc(value_$csinput, GCHandleType.Pinned); + var pin_value_len_$csinput = GCHandle.Alloc(value_len_$csinput, GCHandleType.Pinned); + + var transitive_$csinput = new TransitiveArguments + { + Arg1 = pin_value_$csinput.AddrOfPinnedObject(), + Arg2 = pin_value_len_$csinput.AddrOfPinnedObject() + }; + var input_$csinput = TransitiveArguments.getCPtr(transitive_$csinput);", + post=" + $csinput = Encoding.UTF8.GetString(value_$csinput,0, (int)pin_value_len_$csinput.Target); + pin_value_$csinput.Free(); + pin_value_len_$csinput.Free(); + transitive_$csinput.Dispose();" + ) (void* stringOptVal, int* stringOptLen) + "input_$csinput" + +// -- srt_setsockflag_string method +%typemap(in) (void* stringOptVal, int stringOptLen) { + TransitiveArguments * args = (TransitiveArguments *)$input; + $1 = args->Arg1; + $2 = *(int*)args->Arg2; +} +%typemap(cstype) (void* stringOptVal, int stringOptLen) "string" +%typemap(imtype) (void* stringOptVal, int stringOptLen) "global::System.Runtime.InteropServices.HandleRef" +%typemap(csin, + pre=" + var value_$csinput = Encoding.UTF8.GetBytes($csinput); + var value_len_$csinput = value_$csinput.Length; + var pin_value_$csinput = GCHandle.Alloc(value_$csinput, GCHandleType.Pinned); + var pin_value_len_$csinput = GCHandle.Alloc(value_len_$csinput, GCHandleType.Pinned); + + var transitive_$csinput = new TransitiveArguments + { + Arg1 = pin_value_$csinput.AddrOfPinnedObject(), + Arg2 = pin_value_len_$csinput.AddrOfPinnedObject() + }; + var input_$csinput = TransitiveArguments.getCPtr(transitive_$csinput);", + post=" + pin_value_$csinput.Free(); + pin_value_len_$csinput.Free(); + transitive_$csinput.Dispose();" + ) (void* stringOptVal, int stringOptLen) + "input_$csinput" + +%typemap(csimports) SRT_STRING_SOCKOPT %{ + using static SRT_SOCKOPT; +%} +%inline { + typedef enum + { + SRTO_STRING_BINDTODEVICE = SRTO_BINDTODEVICE, + SRTO_STRING_CONGESTION = SRTO_CONGESTION, + SRTO_STRING_PACKETFILTER = SRTO_PACKETFILTER, + SRTO_STRING_PASSPHRASE = SRTO_PASSPHRASE, + SRTO_STRING_STREAMID = SRTO_STREAMID, + }SRT_STRING_SOCKOPT; + + int srt_getsockflag_string(SRTSOCKET u, SRT_STRING_SOCKOPT opt, void* stringOptVal, int* stringOptLen) + { + return srt_getsockflag(u, (SRT_SOCKOPT)opt, stringOptVal, stringOptLen); + } + + int srt_setsockflag_string(SRTSOCKET u, SRT_STRING_SOCKOPT opt, void* stringOptVal, int stringOptLen) + { + return srt_setsockflag(u, (SRT_SOCKOPT)opt, stringOptVal, stringOptLen); + } +} + +// ---- ADD ADDITIONAL method for bool flags manipulation +// -- srt_getsockflag_bool method +%typemap(in) (void* boolOptVal, int* boolOptLen) { + TransitiveArguments * args = (TransitiveArguments *)$input; + $1 = args->Arg1; + $2 = (int*)args->Arg2; +} +%typemap(cstype) (void* boolOptVal, int* boolOptLen) "out bool" +%typemap(imtype) (void* boolOptVal, int* boolOptLen) "global::System.Runtime.InteropServices.HandleRef" +%typemap(csin, + pre=" + byte value_$csinput = default(byte); + var value_len_$csinput = Marshal.SizeOf(value_$csinput.GetType()); + + var pin_value_$csinput = GCHandle.Alloc(value_$csinput, GCHandleType.Pinned); + var pin_value_len_$csinput = GCHandle.Alloc(value_len_$csinput, GCHandleType.Pinned); + + var transitive_$csinput = new TransitiveArguments + { + Arg1 = pin_value_$csinput.AddrOfPinnedObject(), + Arg2 = pin_value_len_$csinput.AddrOfPinnedObject() + }; + var input_$csinput = TransitiveArguments.getCPtr(transitive_$csinput);", + post=" + $csinput = ((byte)pin_value_$csinput.Target) == 1 ? true : false; + pin_value_$csinput.Free(); + pin_value_len_$csinput.Free(); + transitive_$csinput.Dispose();" + ) (void* boolOptVal, int* boolOptLen) + "input_$csinput" + +// -- srt_setsockflag_bool method +%typemap(in) (void* boolOptVal, int boolOptLen) { + TransitiveArguments * args = (TransitiveArguments *)$input; + $1 = args->Arg1; + $2 = *(int*)args->Arg2; +} +%typemap(cstype) (void* boolOptVal, int boolOptLen) "bool" +%typemap(imtype) (void* boolOptVal, int boolOptLen) "global::System.Runtime.InteropServices.HandleRef" +%typemap(csin, + pre=" + var value_$csinput = $csinput ? (byte)1 : (byte)0; + var value_len_$csinput = Marshal.SizeOf(value_$csinput.GetType()); + var pin_value_$csinput = GCHandle.Alloc(value_$csinput, GCHandleType.Pinned); + var pin_value_len_$csinput = GCHandle.Alloc(value_len_$csinput, GCHandleType.Pinned); + + var transitive_$csinput = new TransitiveArguments + { + Arg1 = pin_value_$csinput.AddrOfPinnedObject(), + Arg2 = pin_value_len_$csinput.AddrOfPinnedObject() + }; + var input_$csinput = TransitiveArguments.getCPtr(transitive_$csinput);", + post=" + pin_value_$csinput.Free(); + pin_value_len_$csinput.Free(); + transitive_$csinput.Dispose();" + ) (void* boolOptVal, int boolOptLen) + "input_$csinput" + +%typemap(csimports) SRT_BOOL_SOCKOPT %{ + using static SRT_SOCKOPT; +%} + +%inline { + typedef enum + { + SRTO_BOOL_DRIFTTRACER = SRTO_DRIFTTRACER, + SRTO_BOOL_ENFORCEDENCRYPTION = SRTO_ENFORCEDENCRYPTION, + SRTO_BOOL_MESSAGEAPI = SRTO_MESSAGEAPI, + SRTO_BOOL_NAKREPORT = SRTO_NAKREPORT, + SRTO_BOOL_RCVSYN = SRTO_RCVSYN, + SRTO_BOOL_RENDEZVOUS = SRTO_RENDEZVOUS, + SRTO_BOOL_REUSEADDR = SRTO_REUSEADDR, + SRTO_BOOL_SENDER = SRTO_SENDER, + SRTO_BOOL_SNDSYN = SRTO_SNDSYN, + SRTO_BOOL_TLPKTDROP = SRTO_TLPKTDROP, + SRTO_BOOL_TSBPDMODE = SRTO_TSBPDMODE + }SRT_BOOL_SOCKOPT; + + int srt_getsockflag_bool(SRTSOCKET u, SRT_BOOL_SOCKOPT opt, void* boolOptVal, int* boolOptLen) + { + return srt_getsockflag(u, (SRT_SOCKOPT)opt, boolOptVal, boolOptLen); + } + + int srt_setsockflag_bool(SRTSOCKET u, SRT_BOOL_SOCKOPT opt, void* boolOptVal, int boolOptLen) + { + return srt_setsockflag(u, (SRT_SOCKOPT)opt, boolOptVal, boolOptLen); + } +} + +// ---- ADD ADDITIONAL method for long flags manipulation +// -- srt_getsockflag_long method +%typemap(in) (void* longOptVal, int* longOptLen) { + TransitiveArguments * args = (TransitiveArguments *)$input; + $1 = args->Arg1; + $2 = (int*)args->Arg2; +} +%typemap(cstype) (void* longOptVal, int* longOptLen) "out long" +%typemap(imtype) (void* longOptVal, int* longOptLen) "global::System.Runtime.InteropServices.HandleRef" +%typemap(csin, + pre=" + long value_$csinput = default(long); + var value_len_$csinput = Marshal.SizeOf(value_$csinput.GetType()); + + var pin_value_$csinput = GCHandle.Alloc(value_$csinput, GCHandleType.Pinned); + var pin_value_len_$csinput = GCHandle.Alloc(value_len_$csinput, GCHandleType.Pinned); + + var transitive_$csinput = new TransitiveArguments + { + Arg1 = pin_value_$csinput.AddrOfPinnedObject(), + Arg2 = pin_value_len_$csinput.AddrOfPinnedObject() + }; + var input_$csinput = TransitiveArguments.getCPtr(transitive_$csinput);", + post=" + $csinput = (long)pin_value_$csinput.Target; + pin_value_$csinput.Free(); + pin_value_len_$csinput.Free(); + transitive_$csinput.Dispose();" + ) (void* longOptVal, int* longOptLen) + "input_$csinput" + +// -- srt_setsockflag_long method +%typemap(in) (void* longOptVal, int longOptLen) { + TransitiveArguments * args = (TransitiveArguments *)$input; + $1 = args->Arg1; + $2 = *(int*)args->Arg2; +} +%typemap(cstype) (void* longOptVal, int longOptLen) "long" +%typemap(imtype) (void* longOptVal, int longOptLen) "global::System.Runtime.InteropServices.HandleRef" +%typemap(csin, + pre=" + var value_$csinput = $csinput; + var value_len_$csinput = Marshal.SizeOf(value_$csinput.GetType()); + var pin_value_$csinput = GCHandle.Alloc(value_$csinput, GCHandleType.Pinned); + var pin_value_len_$csinput = GCHandle.Alloc(value_len_$csinput, GCHandleType.Pinned); + + var transitive_$csinput = new TransitiveArguments + { + Arg1 = pin_value_$csinput.AddrOfPinnedObject(), + Arg2 = pin_value_len_$csinput.AddrOfPinnedObject() + }; + var input_$csinput = TransitiveArguments.getCPtr(transitive_$csinput);", + post=" + pin_value_$csinput.Free(); + pin_value_len_$csinput.Free(); + transitive_$csinput.Dispose();" + ) (void* longOptVal, int longOptLen) + "input_$csinput" + +%typemap(csimports) SRT_LONG_SOCKOPT %{ + using static SRT_SOCKOPT; +%} + +%inline { + typedef enum + { + SRTO_LONG_INPUTBW = SRTO_INPUTBW, + SRTO_LONG_MAXBW = SRTO_MAXBW, + SRTO_LONG_MININPUTBW = SRTO_MININPUTBW, + }SRT_LONG_SOCKOPT; + + int srt_getsockflag_long(SRTSOCKET u, SRT_LONG_SOCKOPT opt, void* longOptVal, int* longOptLen) + { + return srt_getsockflag(u, (SRT_SOCKOPT)opt, longOptVal, longOptLen); + } + + int srt_setsockflag_long(SRTSOCKET u, SRT_LONG_SOCKOPT opt, void* longOptVal, int longOptLen) + { + return srt_setsockflag(u, (SRT_SOCKOPT)opt, longOptVal, longOptLen); + } +} + +// ---- ADD ADDITIONAL method for int flags manipulation +// -- srt_getsockflag_int method +%typemap(in) (void* intOptVal, int* intOptLen) { + TransitiveArguments * args = (TransitiveArguments *)$input; + $1 = args->Arg1; + $2 = (int*)args->Arg2; +} +%typemap(cstype) (void* intOptVal, int* intOptLen) "out int" +%typemap(imtype) (void* intOptVal, int* intOptLen) "global::System.Runtime.InteropServices.HandleRef" +%typemap(csin, + pre=" + int value_$csinput = default(int); + var value_len_$csinput = Marshal.SizeOf(value_$csinput.GetType()); + + var pin_value_$csinput = GCHandle.Alloc(value_$csinput, GCHandleType.Pinned); + var pin_value_len_$csinput = GCHandle.Alloc(value_len_$csinput, GCHandleType.Pinned); + + var transitive_$csinput = new TransitiveArguments + { + Arg1 = pin_value_$csinput.AddrOfPinnedObject(), + Arg2 = pin_value_len_$csinput.AddrOfPinnedObject() + }; + var input_$csinput = TransitiveArguments.getCPtr(transitive_$csinput);", + post=" + $csinput = (int)pin_value_$csinput.Target; + pin_value_$csinput.Free(); + pin_value_len_$csinput.Free(); + transitive_$csinput.Dispose();" + ) (void* intOptVal, int* intOptLen) + "input_$csinput" + +// -- srt_setsockflag_int method +%typemap(in) (void* intOptVal, int intOptLen) { + TransitiveArguments * args = (TransitiveArguments *)$input; + $1 = args->Arg1; + $2 = *(int*)args->Arg2; +} +%typemap(cstype) (void* intOptVal, int intOptLen) "int" +%typemap(imtype) (void* intOptVal, int intOptLen) "global::System.Runtime.InteropServices.HandleRef" +%typemap(csin, + pre=" + var value_$csinput = $csinput; + var value_len_$csinput = Marshal.SizeOf(value_$csinput.GetType()); + var pin_value_$csinput = GCHandle.Alloc(value_$csinput, GCHandleType.Pinned); + var pin_value_len_$csinput = GCHandle.Alloc(value_len_$csinput, GCHandleType.Pinned); + + var transitive_$csinput = new TransitiveArguments + { + Arg1 = pin_value_$csinput.AddrOfPinnedObject(), + Arg2 = pin_value_len_$csinput.AddrOfPinnedObject() + }; + var input_$csinput = TransitiveArguments.getCPtr(transitive_$csinput);", + post=" + pin_value_$csinput.Free(); + pin_value_len_$csinput.Free(); + transitive_$csinput.Dispose();" + ) (void* intOptVal, int intOptLen) + "input_$csinput" + +%typemap(csimports) SRT_INT_SOCKOPT %{ + using static SRT_SOCKOPT; +%} + +%inline { + typedef enum + { + SRTO_INT_CONNTIMEO = SRTO_CONNTIMEO, + //SRTO_INT_CRYPTOMODE = SRTO_CRYPTOMODE, //TODO: Remove when AEAD option is no longer conditional + SRTO_INT_EVENT = SRTO_EVENT, + SRTO_INT_FC = SRTO_FC, + SRTO_INT_GROUPCONNECT = SRTO_GROUPCONNECT, + SRTO_INT_GROUPMINSTABLETIMEO = SRTO_GROUPMINSTABLETIMEO, + SRTO_INT_GROUPTYPE = SRTO_GROUPTYPE, + SRTO_INT_IPTOS = SRTO_IPTOS, + SRTO_INT_IPTTL = SRTO_IPTTL, + SRTO_INT_IPV6ONLY = SRTO_IPV6ONLY, + SRTO_INT_ISN = SRTO_ISN, + SRTO_INT_KMPREANNOUNCE = SRTO_KMPREANNOUNCE, + SRTO_INT_KMREFRESHRATE = SRTO_KMREFRESHRATE, + SRTO_INT_KMSTATE = SRTO_KMSTATE, + SRTO_INT_LATENCY = SRTO_LATENCY, + SRTO_INT_LOSSMAXTTL = SRTO_LOSSMAXTTL, + SRTO_INT_MINVERSION = SRTO_MINVERSION, + SRTO_INT_MSS = SRTO_MSS, + SRTO_INT_OHEADBW = SRTO_OHEADBW, + SRTO_INT_PAYLOADSIZE = SRTO_PAYLOADSIZE, + SRTO_INT_PBKEYLEN = SRTO_PBKEYLEN, + SRTO_INT_PEERIDLETIMEO = SRTO_PEERIDLETIMEO, + SRTO_INT_PEERLATENCY = SRTO_PEERLATENCY, + SRTO_INT_PEERVERSION = SRTO_PEERVERSION, + SRTO_INT_RCVBUF = SRTO_RCVBUF, + SRTO_INT_RCVDATA = SRTO_RCVDATA, + SRTO_INT_RCVKMSTATE = SRTO_RCVKMSTATE, + SRTO_INT_RCVLATENCY = SRTO_RCVLATENCY, + SRTO_INT_RCVTIMEO = SRTO_RCVTIMEO, + SRTO_INT_RETRANSMITALGO = SRTO_RETRANSMITALGO, + SRTO_INT_SNDBUF = SRTO_SNDBUF, + SRTO_INT_SNDDATA = SRTO_SNDDATA, + SRTO_INT_SNDDROPDELAY = SRTO_SNDDROPDELAY, + SRTO_INT_SNDKMSTATE = SRTO_SNDKMSTATE, + SRTO_INT_SNDTIMEO = SRTO_SNDTIMEO, + SRTO_INT_STATE = SRTO_STATE, + SRTO_INT_TRANSTYPE = SRTO_TRANSTYPE, + SRTO_INT_UDP_RCVBUF = SRTO_UDP_RCVBUF, + SRTO_INT_UDP_SNDBUF = SRTO_UDP_SNDBUF, + SRTO_INT_VERSION = SRTO_VERSION, + }SRT_INT_SOCKOPT; + + int srt_getsockflag_int(SRTSOCKET u, SRT_INT_SOCKOPT opt, void* intOptVal, int* intOptLen) + { + return srt_getsockflag(u, (SRT_SOCKOPT)opt, intOptVal, intOptLen); + } + + int srt_setsockflag_int(SRTSOCKET u, SRT_INT_SOCKOPT opt, void* intOptVal, int intOptLen) + { + return srt_setsockflag(u, (SRT_SOCKOPT)opt, intOptVal, intOptLen); + } +} + +#if defined(SWIGWORDSIZE64) +%define PRIMITIVE_TYPEMAP(NEW_TYPE, TYPE) +%clear NEW_TYPE; +%clear NEW_TYPE *; +%clear NEW_TYPE &; +%clear const NEW_TYPE &; +%apply TYPE { NEW_TYPE }; +%apply TYPE * { NEW_TYPE * }; +%apply TYPE & { NEW_TYPE & }; +%apply const TYPE & { const NEW_TYPE & }; +%enddef // PRIMITIVE_TYPEMAP +PRIMITIVE_TYPEMAP(long int, long long); +PRIMITIVE_TYPEMAP(unsigned long int, unsigned long long); +#undef PRIMITIVE_TYPEMAP +#endif // defined(SWIGWORDSIZE64) +#endif // defined(SWIGCSHARP) + +// +// C# related configuration section, customizing binding for this language +// +// Rebind objects from the default mappings for types and objects that are optimized for C# +//enums in C# are int by default, this override pushes this enum to the require uint format +%typemap(csbase) SRT_EPOLL_OPT "uint" +%typemap(csattributes) SRT_EPOLL_OPT "[System.Flags]" + +//the SRT_ERRNO enum references itself another enum - we must import this other enum into the class file for resolution +%typemap(csimports) SRT_ERRNO %{ + using static CodeMajor; + using static CodeMinor; +%} + +%typemap(csimports) SRT_INT_SOCKOPT %{ + using static SRT_SOCKOPT; +%} + +%rename(SRT_TRACEBSTATS) CBytePerfMon; + +// Ignore deprecated methods +%ignore srt_rejectreason_msg; +%ignore srt_setsockopt; + +// General interface definition of wrapper - due to above typemaps and code, we can now just reference the main srt.h file +%include "srt.h"; + +// --- C additional definitions +%inline{ + #ifndef _WIN32 + typedef unsigned char byte; + #endif + + // Structure used for arguments transit + typedef struct + { + byte *Arg1; + byte *Arg2; + byte *Arg3; + byte *Arg4; + byte *Arg5; + byte *Arg6; + } TransitiveArguments; +} + +// add top-level code to module file +// which allows C# bindings of specific objects to be injected for easier use in C# +%pragma(csharp) moduleimports=%{ + +using System; +using System.Linq; +using System.Text; +using System.Net; +using System.Net.Sockets; +using System.Runtime.InteropServices; +using System.Reflection; +using System.Collections.Concurrent; +using static SrtSharp.SRT_SOCKOPT; + +/// +/// Delegate which is used for logger callbacks +/// +/// Custom parameter +/// Log level +/// File name +/// Line number +/// Log area name +/// Message +public delegate void SrtLogHandlerDelegate(IntPtr opaque, int level, string file, int line, string area, string message); + +/// +/// Callback hook delegate, which will be executed on a socket that is automatically created to handle the incoming connection on the listening socket (and is about to be returned by srt_accept), but before the connection has been accepted. +/// +/// The pointer passed as hook_opaque when registering +/// The freshly created socket to handle the incoming connection +/// The handshake version (usually 5, pre-1.3 versions of SRT use 4) +/// The address of the incoming connection +/// The value set to SRTO_STREAMID option set on the peer side +public delegate void SrtListenCallbackDelegate( + IntPtr opaque, + int ns, + int hsVersion, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(SockAddrMarshaler))] IPEndPoint peerAddress, + string streamId); + +/// +/// Callback hook delegate, which will be executed on a given u socket or all member sockets of a u group, just after a pending connection in the background has been resolved and the connection has failed. Note that this function is not guaranteed to be called if the u socket is set to blocking mode (SRTO_RCVSYN option set to true). It is guaranteed to be called when a socket is in non-blocking mode, or when you use a group. +/// +/// The pointer passed as hook_opaque when registering +/// The socket for which the connection process was resolved +/// The error code, same as for srt_connect for blocking mode +/// The target address passed to srt_connect call +/// The token value, if it was used for group connection, otherwise -1 +public delegate void SrtConnectCallbackDelegate( + IntPtr opaque, + int ns, + SRT_ERRNO errorCode, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(SockAddrMarshaler))]IPEndPoint peerAddress, + int token); + +public static class MarshalExtensions +{ + private static readonly FieldInfo SocketAddressBufferField; + private static readonly FieldInfo SocketAddressInternalSizeField; + + static MarshalExtensions() + { + // Internal fields of SocketAddress class are used to access the internal buffer and size + // But starting from NET 8 internal fields were renamed, so we need to find them in a different way + var socketAddressFields = typeof(SocketAddress).GetFields(BindingFlags.Instance | BindingFlags.NonPublic); + SocketAddressBufferField = socketAddressFields.FirstOrDefault(f => f.FieldType == typeof(byte[])); + SocketAddressInternalSizeField = socketAddressFields.FirstOrDefault(f => f.FieldType == typeof(int)); + } + + internal static byte[] GetInternalBuffer(this SocketAddress socketAddress) + { + if (SocketAddressBufferField == null) + { + var message = $"Unable to find the internal buffer field in the {nameof(SocketAddress)} class."; + throw new PlatformNotSupportedException(message); + } + + return (byte[])SocketAddressBufferField.GetValue(socketAddress); + } + + internal static void SetInternalSize(this SocketAddress socketAddress, int size) + { + if (SocketAddressInternalSizeField == null) + { + var message = $"Unable to find the internal buffer size field in the {nameof(SocketAddress)} class."; + throw new PlatformNotSupportedException(message); + } + + SocketAddressInternalSizeField.SetValue(socketAddress, size); + } + + internal static IPEndPoint ToIPEndPoint(this SocketAddress socketAddress) + { + IPEndPoint endPoint = new IPEndPoint(IPAddress.Any, 0); + return (IPEndPoint)endPoint.Create(socketAddress); + } + + public static void Validate(this int responseCode, string message) + { + if(responseCode != srt.SRT_ERROR) return; + throw new InvalidOperationException($"{message} failed: {srt.srt_getlasterror_str()}"); + } + + public static SRTSOCKET Validate(this SRTSOCKET socket) + { + if(socket.IsValid) return socket; + throw new InvalidOperationException($"Socket was not created: {srt.srt_getlasterror_str()}"); + } +} + +internal class SockAddrMarshaler : ICustomMarshaler +{ + private SockAddrMarshaler(string cookies) => Cookies = cookies; + public string Cookies { get; } + + public static ICustomMarshaler GetInstance(string cookies) => new SockAddrMarshaler(cookies); + + public void CleanUpManagedData(object managedObj) { /* Nothing GC will clean up managed object */ } + public void CleanUpNativeData(IntPtr pNativeData) { /* Nothing */ } + public int GetNativeDataSize() => -1; + public IntPtr MarshalManagedToNative(object managedObj) => throw new NotSupportedException(); + public object MarshalNativeToManaged(IntPtr pNativeData) + { + const int DATA_OFFSET = 2; + var family = Marshal.ReadInt16(pNativeData); + var dataPointer = pNativeData + DATA_OFFSET; + var socketAddress = new SocketAddress((AddressFamily) family, 16) + { + // Swap port bytes + [DATA_OFFSET + 0] = Marshal.ReadByte(dataPointer + 1), + [DATA_OFFSET + 1] = Marshal.ReadByte(dataPointer), + // Copy address bytes + [DATA_OFFSET + 2] = Marshal.ReadByte(dataPointer + 2), + [DATA_OFFSET + 3] = Marshal.ReadByte(dataPointer + 3), + [DATA_OFFSET + 4] = Marshal.ReadByte(dataPointer + 4), + [DATA_OFFSET + 5] = Marshal.ReadByte(dataPointer + 5), + [DATA_OFFSET + 6] = Marshal.ReadByte(dataPointer + 6), + [DATA_OFFSET + 7] = Marshal.ReadByte(dataPointer + 7), + [DATA_OFFSET + 8] = Marshal.ReadByte(dataPointer + 8), + [DATA_OFFSET + 9] = Marshal.ReadByte(dataPointer + 9), + [DATA_OFFSET + 10] = Marshal.ReadByte(dataPointer + 10), + [DATA_OFFSET + 11] = Marshal.ReadByte(dataPointer + 11), + [DATA_OFFSET + 12] = Marshal.ReadByte(dataPointer + 12), + [DATA_OFFSET + 13] = Marshal.ReadByte(dataPointer + 13), + }; + + var endPoint = new IPEndPoint(IPAddress.Any, 0); + return (IPEndPoint)endPoint.Create(socketAddress); + } +} + +static class GCKeeper +{ + private static readonly ConcurrentDictionary _registry = new ConcurrentDictionary(); + public static void Keep(string name, object value) + { + _registry.AddOrUpdate(name, value, (key, existen) => value); + } + public static void Forget(string name) + { + _registry.TryRemove(name, out _); + } +} + +/// +/// Artificial structure that represents SRTSOCKET +/// +public readonly struct SRTSOCKET : IDisposable +{ + /// + /// Not initialized socket structure + /// + public static readonly SRTSOCKET NotInitialized = 0; + + /// + /// Check socket is created + /// + public bool IsCreated { get { return _value > 0; } } + + /// + /// Check socket is valid + /// + public bool IsValid { get { return _value != -1; } } + + /// + /// Get socket status + /// + public SRT_SOCKSTATUS Status { get { return srt.srt_getsockstate(_value); } } + + private readonly int _value; + SRTSOCKET(int value) => _value = value; + public void Dispose() + { + if(!IsCreated) return; + + srt.srt_close(_value); + // Remove any kept delegate pointers associated with an socket + GCKeeper.Forget($"{nameof(SrtConnectCallbackDelegate)}-{_value}"); + GCKeeper.Forget($"{nameof(SrtListenCallbackDelegate)}-{_value}"); + } + public override string ToString() => $"{_value}"; + public static implicit operator SRTSOCKET(int b) => new SRTSOCKET(b); + public static implicit operator int(SRTSOCKET d) => d._value; +} + +/// +/// Artificial structure that represents SYSSOCKET +/// +public readonly struct SYSSOCKET : IDisposable +{ + /// + /// Not initialized socket structure + /// + public static readonly SYSSOCKET NotInitialized = 0; + + /// + /// Check socket is created + /// + public bool IsCreated { get { return _value > 0; } } + + /// + /// Check socket is valid + /// + public bool IsValid { get { return _value != -1; } } + + private readonly int _value; + SYSSOCKET(int value) => _value = value; + public void Dispose() + { + if(!IsCreated) return; + } + public override string ToString() => $"{_value}"; + public static implicit operator SYSSOCKET(int b) => new SYSSOCKET(b); + public static implicit operator int(SYSSOCKET d) => d._value; +} + +/// +/// Artificial structure that represents EPOLL +/// +public readonly struct EPOLL : IDisposable +{ + /// + /// Not initialized epoll structure + /// + public static readonly EPOLL NotInitialized = 0; + + /// + /// Check epoll is created + /// + public bool IsCreated { get { return _value > 0; } } + + /// + /// Check epoll is valid + /// + public bool IsValid { get { return _value != -1; } } + + private readonly int _value; + EPOLL(int value) => _value = value; + public void Dispose() + { + if(!IsCreated) return; + + srt.srt_epoll_release(_value); + } + public override string ToString() => $"{_value}"; + public static implicit operator EPOLL(int b) => new EPOLL(b); + public static implicit operator int(EPOLL d) => d._value; +} + +/// +/// Artificial structure that represents arguments for srt_setlogflags(LogFlag) +/// +public readonly struct LogFlag +{ + /// + /// Do not provide the time in the header + /// + public static readonly LogFlag DisableTime = srt.SRT_LOGF_DISABLE_TIME; + + /// + /// Do not provide the thread name in the header + /// + public static readonly LogFlag DisableThreadName = srt.SRT_LOGF_DISABLE_THREADNAME; + + /// + /// Do not provide severity information in the header + /// + public static readonly LogFlag DisableSeverity = srt.SRT_LOGF_DISABLE_SEVERITY; + + /// + /// Do not add the end-of-line character to the log line + /// + public static readonly LogFlag DisableEOL = srt.SRT_LOGF_DISABLE_EOL; + + private readonly int _value; + LogFlag(int value) => _value = value; + public override string ToString() => $"{_value}"; + public static implicit operator LogFlag(int b) => new LogFlag(b); + public static implicit operator int(LogFlag d) => d._value; +} + +/// +/// Artificial structure that represents arguments for srt_setloglevel(LogLevel) +/// +public readonly struct LogLevel +{ + /// + /// Highly detailed and very frequent messages + /// + public static readonly LogLevel Debug = srt.LOG_DEBUG; + + /// + /// Occasionally displayed information + /// + public static readonly LogLevel Notice = srt.LOG_NOTICE; + + /// + /// Unusual behavior + /// + public static readonly LogLevel Warning = srt.LOG_WARNING; + + /// + /// Abnormal behavior + /// + public static readonly LogLevel Error = srt.LOG_ERR; + + /// + /// Error that makes the current socket unusable + /// + public static readonly LogLevel Critical = srt.LOG_CRIT; + + private readonly int _value; + LogLevel(int value) => _value = value; + public override string ToString() => $"{_value}"; + public static implicit operator LogLevel(int b) => new LogLevel(b); + public static implicit operator int(LogLevel d) => d._value; +} + +/// +/// Artificial structure that represents arguments for srt_addlogfa/srt_dellogfa/srt_resetlogfa +/// +public readonly struct LogFunctionalArea +{ + /// + /// gglog: General uncategorized log; for serious issues only + /// + public static readonly LogFunctionalArea General = srt.SRT_LOGFA_GENERAL; + + /// + /// smlog: Socket create/open/close/configure activities + /// + public static readonly LogFunctionalArea SocketManagement = srt.SRT_LOGFA_SOCKMGMT; + + /// + /// cnlog: Connection establishment and handshake + /// + public static readonly LogFunctionalArea Connection = srt.SRT_LOGFA_CONN; + + /// + /// xtlog: The checkTimer and around activities + /// + public static readonly LogFunctionalArea XTimer = srt.SRT_LOGFA_XTIMER; + + /// + /// tslog: The TsBPD thread + /// + public static readonly LogFunctionalArea TsBPD = srt.SRT_LOGFA_TSBPD; + + /// + /// rslog: System resource allocation and management + /// + public static readonly LogFunctionalArea ResourceManagement = srt.SRT_LOGFA_RSRC; + + /// + /// cclog: Congestion control module + /// + public static readonly LogFunctionalArea Congestion = srt.SRT_LOGFA_CONGEST; + + /// + /// pflog: Packet filter module + /// + public static readonly LogFunctionalArea PacketFilter = srt.SRT_LOGFA_PFILTER; + + /// + /// aclog: API part for socket and library management + /// + public static readonly LogFunctionalArea SocketApi = srt.SRT_LOGFA_API_CTRL; + + /// + /// qclog: Queue control activities + /// + public static readonly LogFunctionalArea QueueControl = srt.SRT_LOGFA_QUE_CTRL; + + /// + /// eilog: EPoll; internal update activities + /// + public static readonly LogFunctionalArea EPollActivities = srt.SRT_LOGFA_EPOLL_UPD; + + /// + /// arlog: API part for receiving + /// + public static readonly LogFunctionalArea ReceivingApi = srt.SRT_LOGFA_API_RECV; + + /// + /// brlog: Buffer; receiving side + /// + public static readonly LogFunctionalArea ReceivingBuffer = srt.SRT_LOGFA_BUF_RECV; + + /// + /// qrlog: Queue; receiving side + /// + public static readonly LogFunctionalArea ReceivingQueue = srt.SRT_LOGFA_QUE_RECV; + + /// + /// krlog: CChannel; receiving side + /// + public static readonly LogFunctionalArea ReceivingChannel = srt.SRT_LOGFA_CHN_RECV; + + /// + /// grlog: Group; receiving side + /// + public static readonly LogFunctionalArea ReceivingGroup = srt.SRT_LOGFA_GRP_RECV; + + /// + /// aslog: API part for sending + /// + public static readonly LogFunctionalArea SendingApi = srt.SRT_LOGFA_API_SEND; + + /// + /// bslog: Buffer; sending side + /// + public static readonly LogFunctionalArea SendingBuffer = srt.SRT_LOGFA_BUF_SEND; + + /// + /// qslog: Queue; sending side + /// + public static readonly LogFunctionalArea SendingQueue = srt.SRT_LOGFA_QUE_SEND; + + /// + /// kslog: CChannel; sending side + /// + public static readonly LogFunctionalArea SendingChannel = srt.SRT_LOGFA_CHN_SEND; + + /// + /// gslog: Group; sending side + /// + public static readonly LogFunctionalArea SendingGroup = srt.SRT_LOGFA_GRP_SEND; + + /// + /// inlog: Internal activities not connected directly to a socket + /// + public static readonly LogFunctionalArea Internal = srt.SRT_LOGFA_INTERNAL; + + /// + /// qmlog: Queue; management part + /// + public static readonly LogFunctionalArea ManagementQueue = srt.SRT_LOGFA_QUE_MGMT; + + /// + /// kmlog: CChannel; management part + /// + public static readonly LogFunctionalArea ManagementChannel = srt.SRT_LOGFA_CHN_MGMT; + + /// + /// gmlog: Group; management part + /// + public static readonly LogFunctionalArea ManagementGroup = srt.SRT_LOGFA_GRP_MGMT; + + /// + /// ealog: EPoll; API part + /// + public static readonly LogFunctionalArea EPollApi = srt.SRT_LOGFA_EPOLL_API; + + /// + /// hclog: Haicrypt module area + /// + public static readonly LogFunctionalArea HaiCrypt = srt.SRT_LOGFA_HAICRYPT; + + /// + /// aplog: Applications + /// + public static readonly LogFunctionalArea Applications = srt.SRT_LOGFA_APPLOG; + + private readonly int _value; + LogFunctionalArea(int value) => _value = value; + public override string ToString() => $"{_value}"; + public static implicit operator LogFunctionalArea(int b) => new LogFunctionalArea(b); + public static implicit operator int(LogFunctionalArea d) => d._value; +} + +/// +/// SRT_EPOLL_EVENT structure definition +/// +[StructLayout(LayoutKind.Sequential)] +public struct SRT_EPOLL_EVENT +{ + /// + /// The user socket (SRT socket) + /// + public SRTSOCKET Socket; + + /// + /// Event flags that report readiness of this socket + /// + public SRT_EPOLL_OPT Events; +} +%} \ No newline at end of file diff --git a/srtcore/swig_bindings/csharp/srtsharp.csproj b/srtcore/swig_bindings/csharp/srtsharp.csproj new file mode 100644 index 000000000..7ea99cbfe --- /dev/null +++ b/srtcore/swig_bindings/csharp/srtsharp.csproj @@ -0,0 +1,9 @@ + + + + netstandard2.0 + AnyCPU + true + + +