remove 3rd party stuff again, decided to use submodules afterall
This commit is contained in:
63
3rd/libomt/.gitattributes
vendored
63
3rd/libomt/.gitattributes
vendored
@@ -1,63 +0,0 @@
|
|||||||
###############################################################################
|
|
||||||
# Set default behavior to automatically normalize line endings.
|
|
||||||
###############################################################################
|
|
||||||
* text=auto
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# Set default behavior for command prompt diff.
|
|
||||||
#
|
|
||||||
# This is need for earlier builds of msysgit that does not have it on by
|
|
||||||
# default for csharp files.
|
|
||||||
# Note: This is only used by command line
|
|
||||||
###############################################################################
|
|
||||||
#*.cs diff=csharp
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# Set the merge driver for project and solution files
|
|
||||||
#
|
|
||||||
# Merging from the command prompt will add diff markers to the files if there
|
|
||||||
# are conflicts (Merging from VS is not affected by the settings below, in VS
|
|
||||||
# the diff markers are never inserted). Diff markers may cause the following
|
|
||||||
# file extensions to fail to load in VS. An alternative would be to treat
|
|
||||||
# these files as binary and thus will always conflict and require user
|
|
||||||
# intervention with every merge. To do so, just uncomment the entries below
|
|
||||||
###############################################################################
|
|
||||||
#*.sln merge=binary
|
|
||||||
#*.csproj merge=binary
|
|
||||||
#*.vbproj merge=binary
|
|
||||||
#*.vcxproj merge=binary
|
|
||||||
#*.vcproj merge=binary
|
|
||||||
#*.dbproj merge=binary
|
|
||||||
#*.fsproj merge=binary
|
|
||||||
#*.lsproj merge=binary
|
|
||||||
#*.wixproj merge=binary
|
|
||||||
#*.modelproj merge=binary
|
|
||||||
#*.sqlproj merge=binary
|
|
||||||
#*.wwaproj merge=binary
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# behavior for image files
|
|
||||||
#
|
|
||||||
# image files are treated as binary by default.
|
|
||||||
###############################################################################
|
|
||||||
#*.jpg binary
|
|
||||||
#*.png binary
|
|
||||||
#*.gif binary
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# diff behavior for common document formats
|
|
||||||
#
|
|
||||||
# Convert binary document formats to text before diffing them. This feature
|
|
||||||
# is only available from the command line. Turn it on by uncommenting the
|
|
||||||
# entries below.
|
|
||||||
###############################################################################
|
|
||||||
#*.doc diff=astextplain
|
|
||||||
#*.DOC diff=astextplain
|
|
||||||
#*.docx diff=astextplain
|
|
||||||
#*.DOCX diff=astextplain
|
|
||||||
#*.dot diff=astextplain
|
|
||||||
#*.DOT diff=astextplain
|
|
||||||
#*.pdf diff=astextplain
|
|
||||||
#*.PDF diff=astextplain
|
|
||||||
#*.rtf diff=astextplain
|
|
||||||
#*.RTF diff=astextplain
|
|
||||||
363
3rd/libomt/.gitignore
vendored
363
3rd/libomt/.gitignore
vendored
@@ -1,363 +0,0 @@
|
|||||||
## Ignore Visual Studio temporary files, build results, and
|
|
||||||
## files generated by popular Visual Studio add-ons.
|
|
||||||
##
|
|
||||||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
|
||||||
|
|
||||||
# User-specific files
|
|
||||||
*.rsuser
|
|
||||||
*.suo
|
|
||||||
*.user
|
|
||||||
*.userosscache
|
|
||||||
*.sln.docstates
|
|
||||||
|
|
||||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
|
||||||
*.userprefs
|
|
||||||
|
|
||||||
# Mono auto generated files
|
|
||||||
mono_crash.*
|
|
||||||
|
|
||||||
# Build results
|
|
||||||
[Dd]ebug/
|
|
||||||
[Dd]ebugPublic/
|
|
||||||
[Rr]elease/
|
|
||||||
[Rr]eleases/
|
|
||||||
x64/
|
|
||||||
x86/
|
|
||||||
[Ww][Ii][Nn]32/
|
|
||||||
[Aa][Rr][Mm]/
|
|
||||||
[Aa][Rr][Mm]64/
|
|
||||||
bld/
|
|
||||||
[Bb]in/
|
|
||||||
[Oo]bj/
|
|
||||||
[Oo]ut/
|
|
||||||
[Ll]og/
|
|
||||||
[Ll]ogs/
|
|
||||||
|
|
||||||
# Visual Studio 2015/2017 cache/options directory
|
|
||||||
.vs/
|
|
||||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
|
||||||
#wwwroot/
|
|
||||||
|
|
||||||
# Visual Studio 2017 auto generated files
|
|
||||||
Generated\ Files/
|
|
||||||
|
|
||||||
# MSTest test Results
|
|
||||||
[Tt]est[Rr]esult*/
|
|
||||||
[Bb]uild[Ll]og.*
|
|
||||||
|
|
||||||
# NUnit
|
|
||||||
*.VisualState.xml
|
|
||||||
TestResult.xml
|
|
||||||
nunit-*.xml
|
|
||||||
|
|
||||||
# Build Results of an ATL Project
|
|
||||||
[Dd]ebugPS/
|
|
||||||
[Rr]eleasePS/
|
|
||||||
dlldata.c
|
|
||||||
|
|
||||||
# Benchmark Results
|
|
||||||
BenchmarkDotNet.Artifacts/
|
|
||||||
|
|
||||||
# .NET Core
|
|
||||||
project.lock.json
|
|
||||||
project.fragment.lock.json
|
|
||||||
artifacts/
|
|
||||||
|
|
||||||
# ASP.NET Scaffolding
|
|
||||||
ScaffoldingReadMe.txt
|
|
||||||
|
|
||||||
# StyleCop
|
|
||||||
StyleCopReport.xml
|
|
||||||
|
|
||||||
# Files built by Visual Studio
|
|
||||||
*_i.c
|
|
||||||
*_p.c
|
|
||||||
*_h.h
|
|
||||||
*.ilk
|
|
||||||
*.meta
|
|
||||||
*.obj
|
|
||||||
*.iobj
|
|
||||||
*.pch
|
|
||||||
*.pdb
|
|
||||||
*.ipdb
|
|
||||||
*.pgc
|
|
||||||
*.pgd
|
|
||||||
*.rsp
|
|
||||||
*.sbr
|
|
||||||
*.tlb
|
|
||||||
*.tli
|
|
||||||
*.tlh
|
|
||||||
*.tmp
|
|
||||||
*.tmp_proj
|
|
||||||
*_wpftmp.csproj
|
|
||||||
*.log
|
|
||||||
*.vspscc
|
|
||||||
*.vssscc
|
|
||||||
.builds
|
|
||||||
*.pidb
|
|
||||||
*.svclog
|
|
||||||
*.scc
|
|
||||||
|
|
||||||
# Chutzpah Test files
|
|
||||||
_Chutzpah*
|
|
||||||
|
|
||||||
# Visual C++ cache files
|
|
||||||
ipch/
|
|
||||||
*.aps
|
|
||||||
*.ncb
|
|
||||||
*.opendb
|
|
||||||
*.opensdf
|
|
||||||
*.sdf
|
|
||||||
*.cachefile
|
|
||||||
*.VC.db
|
|
||||||
*.VC.VC.opendb
|
|
||||||
|
|
||||||
# Visual Studio profiler
|
|
||||||
*.psess
|
|
||||||
*.vsp
|
|
||||||
*.vspx
|
|
||||||
*.sap
|
|
||||||
|
|
||||||
# Visual Studio Trace Files
|
|
||||||
*.e2e
|
|
||||||
|
|
||||||
# TFS 2012 Local Workspace
|
|
||||||
$tf/
|
|
||||||
|
|
||||||
# Guidance Automation Toolkit
|
|
||||||
*.gpState
|
|
||||||
|
|
||||||
# ReSharper is a .NET coding add-in
|
|
||||||
_ReSharper*/
|
|
||||||
*.[Rr]e[Ss]harper
|
|
||||||
*.DotSettings.user
|
|
||||||
|
|
||||||
# TeamCity is a build add-in
|
|
||||||
_TeamCity*
|
|
||||||
|
|
||||||
# DotCover is a Code Coverage Tool
|
|
||||||
*.dotCover
|
|
||||||
|
|
||||||
# AxoCover is a Code Coverage Tool
|
|
||||||
.axoCover/*
|
|
||||||
!.axoCover/settings.json
|
|
||||||
|
|
||||||
# Coverlet is a free, cross platform Code Coverage Tool
|
|
||||||
coverage*.json
|
|
||||||
coverage*.xml
|
|
||||||
coverage*.info
|
|
||||||
|
|
||||||
# Visual Studio code coverage results
|
|
||||||
*.coverage
|
|
||||||
*.coveragexml
|
|
||||||
|
|
||||||
# NCrunch
|
|
||||||
_NCrunch_*
|
|
||||||
.*crunch*.local.xml
|
|
||||||
nCrunchTemp_*
|
|
||||||
|
|
||||||
# MightyMoose
|
|
||||||
*.mm.*
|
|
||||||
AutoTest.Net/
|
|
||||||
|
|
||||||
# Web workbench (sass)
|
|
||||||
.sass-cache/
|
|
||||||
|
|
||||||
# Installshield output folder
|
|
||||||
[Ee]xpress/
|
|
||||||
|
|
||||||
# DocProject is a documentation generator add-in
|
|
||||||
DocProject/buildhelp/
|
|
||||||
DocProject/Help/*.HxT
|
|
||||||
DocProject/Help/*.HxC
|
|
||||||
DocProject/Help/*.hhc
|
|
||||||
DocProject/Help/*.hhk
|
|
||||||
DocProject/Help/*.hhp
|
|
||||||
DocProject/Help/Html2
|
|
||||||
DocProject/Help/html
|
|
||||||
|
|
||||||
# Click-Once directory
|
|
||||||
publish/
|
|
||||||
|
|
||||||
# Publish Web Output
|
|
||||||
*.[Pp]ublish.xml
|
|
||||||
*.azurePubxml
|
|
||||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
|
||||||
# but database connection strings (with potential passwords) will be unencrypted
|
|
||||||
*.pubxml
|
|
||||||
*.publishproj
|
|
||||||
|
|
||||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
|
||||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
|
||||||
# in these scripts will be unencrypted
|
|
||||||
PublishScripts/
|
|
||||||
|
|
||||||
# NuGet Packages
|
|
||||||
*.nupkg
|
|
||||||
# NuGet Symbol Packages
|
|
||||||
*.snupkg
|
|
||||||
# The packages folder can be ignored because of Package Restore
|
|
||||||
**/[Pp]ackages/*
|
|
||||||
# except build/, which is used as an MSBuild target.
|
|
||||||
!**/[Pp]ackages/build/
|
|
||||||
# Uncomment if necessary however generally it will be regenerated when needed
|
|
||||||
#!**/[Pp]ackages/repositories.config
|
|
||||||
# NuGet v3's project.json files produces more ignorable files
|
|
||||||
*.nuget.props
|
|
||||||
*.nuget.targets
|
|
||||||
|
|
||||||
# Microsoft Azure Build Output
|
|
||||||
csx/
|
|
||||||
*.build.csdef
|
|
||||||
|
|
||||||
# Microsoft Azure Emulator
|
|
||||||
ecf/
|
|
||||||
rcf/
|
|
||||||
|
|
||||||
# Windows Store app package directories and files
|
|
||||||
AppPackages/
|
|
||||||
BundleArtifacts/
|
|
||||||
Package.StoreAssociation.xml
|
|
||||||
_pkginfo.txt
|
|
||||||
*.appx
|
|
||||||
*.appxbundle
|
|
||||||
*.appxupload
|
|
||||||
|
|
||||||
# Visual Studio cache files
|
|
||||||
# files ending in .cache can be ignored
|
|
||||||
*.[Cc]ache
|
|
||||||
# but keep track of directories ending in .cache
|
|
||||||
!?*.[Cc]ache/
|
|
||||||
|
|
||||||
# Others
|
|
||||||
ClientBin/
|
|
||||||
~$*
|
|
||||||
*~
|
|
||||||
*.dbmdl
|
|
||||||
*.dbproj.schemaview
|
|
||||||
*.jfm
|
|
||||||
*.pfx
|
|
||||||
*.publishsettings
|
|
||||||
orleans.codegen.cs
|
|
||||||
|
|
||||||
# Including strong name files can present a security risk
|
|
||||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
|
||||||
#*.snk
|
|
||||||
|
|
||||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
|
||||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
|
||||||
#bower_components/
|
|
||||||
|
|
||||||
# RIA/Silverlight projects
|
|
||||||
Generated_Code/
|
|
||||||
|
|
||||||
# Backup & report files from converting an old project file
|
|
||||||
# to a newer Visual Studio version. Backup files are not needed,
|
|
||||||
# because we have git ;-)
|
|
||||||
_UpgradeReport_Files/
|
|
||||||
Backup*/
|
|
||||||
UpgradeLog*.XML
|
|
||||||
UpgradeLog*.htm
|
|
||||||
ServiceFabricBackup/
|
|
||||||
*.rptproj.bak
|
|
||||||
|
|
||||||
# SQL Server files
|
|
||||||
*.mdf
|
|
||||||
*.ldf
|
|
||||||
*.ndf
|
|
||||||
|
|
||||||
# Business Intelligence projects
|
|
||||||
*.rdl.data
|
|
||||||
*.bim.layout
|
|
||||||
*.bim_*.settings
|
|
||||||
*.rptproj.rsuser
|
|
||||||
*- [Bb]ackup.rdl
|
|
||||||
*- [Bb]ackup ([0-9]).rdl
|
|
||||||
*- [Bb]ackup ([0-9][0-9]).rdl
|
|
||||||
|
|
||||||
# Microsoft Fakes
|
|
||||||
FakesAssemblies/
|
|
||||||
|
|
||||||
# GhostDoc plugin setting file
|
|
||||||
*.GhostDoc.xml
|
|
||||||
|
|
||||||
# Node.js Tools for Visual Studio
|
|
||||||
.ntvs_analysis.dat
|
|
||||||
node_modules/
|
|
||||||
|
|
||||||
# Visual Studio 6 build log
|
|
||||||
*.plg
|
|
||||||
|
|
||||||
# Visual Studio 6 workspace options file
|
|
||||||
*.opt
|
|
||||||
|
|
||||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
|
||||||
*.vbw
|
|
||||||
|
|
||||||
# Visual Studio LightSwitch build output
|
|
||||||
**/*.HTMLClient/GeneratedArtifacts
|
|
||||||
**/*.DesktopClient/GeneratedArtifacts
|
|
||||||
**/*.DesktopClient/ModelManifest.xml
|
|
||||||
**/*.Server/GeneratedArtifacts
|
|
||||||
**/*.Server/ModelManifest.xml
|
|
||||||
_Pvt_Extensions
|
|
||||||
|
|
||||||
# Paket dependency manager
|
|
||||||
.paket/paket.exe
|
|
||||||
paket-files/
|
|
||||||
|
|
||||||
# FAKE - F# Make
|
|
||||||
.fake/
|
|
||||||
|
|
||||||
# CodeRush personal settings
|
|
||||||
.cr/personal
|
|
||||||
|
|
||||||
# Python Tools for Visual Studio (PTVS)
|
|
||||||
__pycache__/
|
|
||||||
*.pyc
|
|
||||||
|
|
||||||
# Cake - Uncomment if you are using it
|
|
||||||
# tools/**
|
|
||||||
# !tools/packages.config
|
|
||||||
|
|
||||||
# Tabs Studio
|
|
||||||
*.tss
|
|
||||||
|
|
||||||
# Telerik's JustMock configuration file
|
|
||||||
*.jmconfig
|
|
||||||
|
|
||||||
# BizTalk build output
|
|
||||||
*.btp.cs
|
|
||||||
*.btm.cs
|
|
||||||
*.odx.cs
|
|
||||||
*.xsd.cs
|
|
||||||
|
|
||||||
# OpenCover UI analysis results
|
|
||||||
OpenCover/
|
|
||||||
|
|
||||||
# Azure Stream Analytics local run output
|
|
||||||
ASALocalRun/
|
|
||||||
|
|
||||||
# MSBuild Binary and Structured Log
|
|
||||||
*.binlog
|
|
||||||
|
|
||||||
# NVidia Nsight GPU debugger configuration file
|
|
||||||
*.nvuser
|
|
||||||
|
|
||||||
# MFractors (Xamarin productivity tool) working folder
|
|
||||||
.mfractor/
|
|
||||||
|
|
||||||
# Local History for Visual Studio
|
|
||||||
.localhistory/
|
|
||||||
|
|
||||||
# BeatPulse healthcheck temp database
|
|
||||||
healthchecksdb
|
|
||||||
|
|
||||||
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
|
||||||
MigrationBackup/
|
|
||||||
|
|
||||||
# Ionide (cross platform F# VS Code tools) working folder
|
|
||||||
.ionide/
|
|
||||||
|
|
||||||
# Fody - auto-generated XML schema
|
|
||||||
FodyWeavers.xsd
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2025 Open Media Transport Contribitors
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
# libomt
|
|
||||||
|
|
||||||
libomt is a C wrapper for the libomnet library.
|
|
||||||
|
|
||||||
libomt uses the .NET Native AOT Deployment to build a native shared library with C exports
|
|
||||||
|
|
||||||
## Getting Started
|
|
||||||
|
|
||||||
Prebuilt binaries of libomt for both Windows and MacOS is available in the Releases page for libomtnet here:
|
|
||||||
https://github.com/openmediatransport/libomtnet/releases
|
|
||||||
|
|
||||||
## Requirements
|
|
||||||
|
|
||||||
libvmx shared library (included in the prebuilt binaries)
|
|
||||||
|
|
||||||
.NET 8.0 for Windows, Linux or MacOS (.NET not required unless compiling from source)
|
|
||||||
|
|
||||||
## Compiling
|
|
||||||
|
|
||||||
1. Setup a folder structure with both libomtnet and libomt as follows:
|
|
||||||
|
|
||||||
[root]
|
|
||||||
|
|
||||||
/libomtnet
|
|
||||||
|
|
||||||
/libomt
|
|
||||||
|
|
||||||
2. The build process will reference and build the .NET Standard library in the ../libomtnet folder
|
|
||||||
3. Run the build script for the desired platform from the build directory.
|
|
||||||
4. A shared library and a .lib file should be generated in the platform native directory.
|
|
||||||
On Windows this is usually bin\Release\net8.0\win-x64\native
|
|
||||||
|
|
||||||
## Using the library in your project
|
|
||||||
|
|
||||||
1. Include the libomt.h file in your C/C++ application
|
|
||||||
2. Add the libomt.lib library
|
|
||||||
|
|
||||||
Note: libomtnet.dll is not needed as it is automatically built into the libomt shared library.
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
dotnet publish ../libomt.sln -r linux-arm64 -c Release
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
dotnet publish ../libomt.sln -r linux-x64 -c Release
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
dotnet publish ../libomt.sln -r osx-arm64 -c Release
|
|
||||||
dotnet publish ../libomt.sln -r osx-x64 -c Release
|
|
||||||
install_name_tool -id @rpath/libomt.dylib ../bin/Release/net8.0/osx-arm64/publish/libomt.dylib
|
|
||||||
install_name_tool -id @rpath/libomt.dylib ../bin/Release/net8.0/osx-arm64/native/libomt.dylib
|
|
||||||
install_name_tool -id @rpath/libomt.dylib ../bin/Release/net8.0/osx-x64/publish/libomt.dylib
|
|
||||||
install_name_tool -id @rpath/libomt.dylib ../bin/Release/net8.0/osx-x64/native/libomt.dylib
|
|
||||||
lipo -create -output libomt.dylib ../bin/Release/net8.0/osx-x64/native/libomt.dylib ../bin/Release/net8.0/osx-arm64/native/libomt.dylib
|
|
||||||
install_name_tool -id @rpath/libomt.dylib libomt.dylib
|
|
||||||
install_name_tool -add_rpath @loader_path libomt.dylib
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
dotnet publish ../libomt.sln -r win-arm64 -c Release
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
dotnet publish ../libomt.sln -r win-x64 -c Release
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
<PublishAot>true</PublishAot>
|
|
||||||
<Version>1.0.0.13</Version>
|
|
||||||
<Configurations>Debug;Release</Configurations>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Reference Include="libomtnet">
|
|
||||||
<HintPath>..\libomtnet\bin\Release\netstandard2.0\libomtnet.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<LinkerArg Condition="$(RuntimeIdentifier.StartsWith('osx'))" Include="-mmacosx-version-min=10.15" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<None Update="libomt.h">
|
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
|
||||||
</None>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
@@ -1,598 +0,0 @@
|
|||||||
/*
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Open Media Transport Contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
#pragma comment(lib, "libomt.lib")
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define OMT_MAX_STRING_LENGTH 1024
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
#include <cstdint>
|
|
||||||
#else
|
|
||||||
#include <stdint.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef enum OMTFrameType
|
|
||||||
{
|
|
||||||
OMTFrameType_None = 0,
|
|
||||||
OMTFrameType_Metadata = 1,
|
|
||||||
OMTFrameType_Video = 2,
|
|
||||||
OMTFrameType_Audio = 4,
|
|
||||||
OMTFrameType_INT32 = 0x7fffffff //Ensure int type in C
|
|
||||||
} OMTFrameType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Supported Codecs:
|
|
||||||
*
|
|
||||||
* VMX1 = Fast video codec
|
|
||||||
*
|
|
||||||
* UYVY = 16bpp YUV format
|
|
||||||
*
|
|
||||||
* YUY2 = 16bpp YUV format YUYV pixel order
|
|
||||||
*
|
|
||||||
* UYVA = 16pp YUV format immediately followed by an alpha plane
|
|
||||||
*
|
|
||||||
* NV12 = Planar 4:2:0 YUV format. Y plane followed by interleaved half height U/V plane.
|
|
||||||
*
|
|
||||||
* YV12 = Planar 4:2:0 YUV format. Y plane followed by half height U and V planes.
|
|
||||||
*
|
|
||||||
* BGRA = 32bpp RGBA format (Same as ARGB32 on Win32)
|
|
||||||
*
|
|
||||||
* P216 = Planar 4:2:2 YUV format. 16bit Y plane followed by interlaved 16bit UV plane.
|
|
||||||
*
|
|
||||||
* PA16 = Same as P216 folowed by an additional 16bit alpha plane.
|
|
||||||
*
|
|
||||||
* FPA1 = Floating-point Planar Audio 32bit
|
|
||||||
*/
|
|
||||||
typedef enum OMTCodec
|
|
||||||
{
|
|
||||||
OMTCodec_VMX1 = 0x31584D56,
|
|
||||||
OMTCodec_FPA1 = 0x31415046, //Planar audio
|
|
||||||
OMTCodec_UYVY = 0x59565955,
|
|
||||||
OMTCodec_YUY2 = 0x32595559,
|
|
||||||
OMTCodec_BGRA = 0x41524742,
|
|
||||||
OMTCodec_NV12 = 0x3231564E,
|
|
||||||
OMTCodec_YV12 = 0x32315659,
|
|
||||||
OMTCodec_UYVA = 0x41565955,
|
|
||||||
OMTCodec_P216 = 0x36313250,
|
|
||||||
OMTCodec_PA16 = 0x36314150
|
|
||||||
|
|
||||||
} OMTCodec;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specify the video encoding quality.
|
|
||||||
*
|
|
||||||
* If set to Default, the Sender is configured to allow suggestions from all Receivers.
|
|
||||||
*
|
|
||||||
* The highest suggest amongst all receivers is then selected.
|
|
||||||
*
|
|
||||||
* If a Receiver is set to Default, then it will defer the quality to whatever is set amongst other Receivers.
|
|
||||||
*/
|
|
||||||
typedef enum OMTQuality
|
|
||||||
{
|
|
||||||
OMTQuality_Default = 0,
|
|
||||||
OMTQuality_Low = 1,
|
|
||||||
OMTQuality_Medium = 50,
|
|
||||||
OMTQuality_High = 100,
|
|
||||||
OMTQuality_INT32 = 0x7fffffff //Ensure int type in C
|
|
||||||
} OMTQuality;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specify the color space of the uncompressed Frame. This is used to determine the color space for YUV<>RGB conversions internally.
|
|
||||||
*
|
|
||||||
* If undefined, the codec will assume BT601 for heights < 720, BT709 for everything else.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
typedef enum OMTColorSpace
|
|
||||||
{
|
|
||||||
OMTColorSpace_Undefined = 0,
|
|
||||||
OMTColorSpace_BT601 = 601,
|
|
||||||
OMTColorSpace_BT709 = 709,
|
|
||||||
OMTColorSpace_INT32 = 0x7fffffff //Ensure int type in C
|
|
||||||
} OMTColorSpace;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Flags set on video frames:
|
|
||||||
*
|
|
||||||
* Interlaced: Frames are interlaced
|
|
||||||
*
|
|
||||||
* Alpha: Frames contain an alpha channel. If this is not set, BGRA will be encoded as BGRX and UYVA will be encoded as UYVY.
|
|
||||||
*
|
|
||||||
* PreMultiplied: When combined with Alpha, alpha channel is premultiplied, otherwise straight
|
|
||||||
*
|
|
||||||
* Preview: Frame is a special 1/8th preview frame
|
|
||||||
*
|
|
||||||
* HighBitDepth: Sender automatically adds this flag for frames encoded using P216 or PA16 pixel formats.
|
|
||||||
*
|
|
||||||
* Set this manually for VMX1 compressed data where the the frame was originally encoded using P216 or PA16.
|
|
||||||
* This determines which pixel format is selected on the decode side.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
typedef enum OMTVideoFlags
|
|
||||||
{
|
|
||||||
OMTVideoFlags_None = 0,
|
|
||||||
OMTVideoFlags_Interlaced = 1,
|
|
||||||
OMTVideoFlags_Alpha = 2,
|
|
||||||
OMTVideoFlags_PreMultiplied = 4,
|
|
||||||
OMTVideoFlags_Preview = 8,
|
|
||||||
OMTVideoFlags_HighBitDepth = 16,
|
|
||||||
OMTVideoFlags_INT32 = 0x7fffffff //Ensure int type in C
|
|
||||||
} OMTVideoFlags;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specify the preferred uncompressed video format of decoded frames.
|
|
||||||
*
|
|
||||||
* UYVY is always the fastest, if no alpha channel is required.
|
|
||||||
*
|
|
||||||
* UYVYorBGRA will provide BGRA only when alpha channel is present.
|
|
||||||
*
|
|
||||||
* BGRA will always convert back to BGRA
|
|
||||||
*
|
|
||||||
* UYVYorUYVA will provide UYVA only when alpha channel is present.
|
|
||||||
*
|
|
||||||
* UYVYorUYVAorP216orPA16 will provide P216 if sender encoded with high bit depth, or PA16 if sender encoded with high bit depth and alpha. Otherwise same as UYVYorUYVA.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
typedef enum OMTPreferredVideoFormat
|
|
||||||
{
|
|
||||||
OMTPreferredVideoFormat_UYVY = 0,
|
|
||||||
OMTPreferredVideoFormat_UYVYorBGRA = 1,
|
|
||||||
OMTPreferredVideoFormat_BGRA = 2,
|
|
||||||
OMTPreferredVideoFormat_UYVYorUYVA = 3,
|
|
||||||
OMTPreferredVideoFormat_UYVYorUYVAorP216orPA16 = 4,
|
|
||||||
OMTPreferredVideoFormat_P216 = 5,
|
|
||||||
OMTPreferredVideoFormat_INT32 = 0x7fffffff //Ensure int type in C
|
|
||||||
} OMTPreferredVideoFormat;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Flags to enable certain features on a Receiver:
|
|
||||||
*
|
|
||||||
* Preview: Receive only a 1/8th preview of the video.
|
|
||||||
*
|
|
||||||
* IncludeCompressed: Include a copy of the compressed VMX1 video frames for further processing or recording.
|
|
||||||
*
|
|
||||||
* CompressedOnly: Include only the compressed VMX1 video frame without decoding. In this instance DataLength will always be 0.
|
|
||||||
*/
|
|
||||||
typedef enum OMTReceiveFlags
|
|
||||||
{
|
|
||||||
OMTReceiveFlags_None = 0,
|
|
||||||
OMTReceiveFlags_Preview = 1,
|
|
||||||
OMTReceiveFlags_IncludeCompressed = 2,
|
|
||||||
OMTReceiveFlags_CompressedOnly = 4,
|
|
||||||
OMTReceiveFlags_INT32 = 0x7fffffff //Ensure int type in C
|
|
||||||
} OMTReceiveFlags;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Tally where 0 = off, 1 = on.
|
|
||||||
/// </summary>
|
|
||||||
typedef struct OMTTally
|
|
||||||
{
|
|
||||||
int preview;
|
|
||||||
int program;
|
|
||||||
} OMTTally;
|
|
||||||
|
|
||||||
typedef struct OMTSenderInfo
|
|
||||||
{
|
|
||||||
char ProductName[OMT_MAX_STRING_LENGTH];
|
|
||||||
char Manufacturer[OMT_MAX_STRING_LENGTH];
|
|
||||||
char Version[OMT_MAX_STRING_LENGTH];
|
|
||||||
char Reserved1[OMT_MAX_STRING_LENGTH];
|
|
||||||
char Reserved2[OMT_MAX_STRING_LENGTH];
|
|
||||||
char Reserved3[OMT_MAX_STRING_LENGTH];
|
|
||||||
} OMTSenderInfo;
|
|
||||||
|
|
||||||
typedef struct OMTStatistics
|
|
||||||
{
|
|
||||||
int64_t BytesSent;
|
|
||||||
int64_t BytesReceived;
|
|
||||||
int64_t BytesSentSinceLast;
|
|
||||||
int64_t BytesReceivedSinceLast;
|
|
||||||
|
|
||||||
int64_t Frames;
|
|
||||||
int64_t FramesSinceLast;
|
|
||||||
int64_t FramesDropped;
|
|
||||||
|
|
||||||
//Time in milliseconds spent encoding so far this instance. Can be divided by Frames to work out per frame times.
|
|
||||||
int64_t CodecTime;
|
|
||||||
//Time in milliseconds of the last frame encoded.
|
|
||||||
int64_t CodecTimeSinceLast;
|
|
||||||
|
|
||||||
int64_t Reserved1;
|
|
||||||
int64_t Reserved2;
|
|
||||||
int64_t Reserved3;
|
|
||||||
int64_t Reserved4;
|
|
||||||
int64_t Reserved5;
|
|
||||||
int64_t Reserved6;
|
|
||||||
int64_t Reserved7;
|
|
||||||
} OMTStatistics;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Media Frame struct for sending receiving.
|
|
||||||
*
|
|
||||||
* IMPORTANT: Zero this struct before use. OMTMediaFrame frame = {}; is sufficient.
|
|
||||||
*/
|
|
||||||
typedef struct OMTMediaFrame
|
|
||||||
{
|
|
||||||
//Specify the type of frame. This determines which values of this struct are valid/used.
|
|
||||||
enum OMTFrameType Type;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is a timestamp where 1 second = 10,000,000
|
|
||||||
*
|
|
||||||
* This should not be left 0 unless this is the very first frame.
|
|
||||||
*
|
|
||||||
* This should represent the accurate time the frame or audio sample was generated at the original source and be used on the receiving end to synchronize
|
|
||||||
* and record to file as a presentation timestamp (pts).
|
|
||||||
*
|
|
||||||
* A special value of -1 can be specified to tell the Sender to generate timestamps and throttle as required to maintain
|
|
||||||
* the specified FrameRate or SampleRate of the frame.
|
|
||||||
*/
|
|
||||||
int64_t Timestamp;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sending:
|
|
||||||
*
|
|
||||||
* Video: 'UYVY', 'YUY2', 'NV12', 'YV12, 'BGRA', 'UYVA', 'VMX1' are supported (BGRA will be treated as BGRX and UYVA as UYVY where alpha flags are not set)
|
|
||||||
*
|
|
||||||
* Audio: Only 'FPA1' is supported (32bit floating point planar audio)
|
|
||||||
*
|
|
||||||
* Receiving:
|
|
||||||
*
|
|
||||||
* Video: Only 'UYVY', 'UYVA', 'BGRA' and 'BGRX' are supported
|
|
||||||
*
|
|
||||||
* Audio: Only 'FPA1' is supported (32bit floating point planar audio)
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
enum OMTCodec Codec;
|
|
||||||
|
|
||||||
//Video Properties
|
|
||||||
int Width;
|
|
||||||
int Height;
|
|
||||||
|
|
||||||
//Stride in bytes of each row of pixels. Typically width*2 for UYVY, width*4 for BGRA and just width for planar formats.
|
|
||||||
int Stride;
|
|
||||||
|
|
||||||
enum OMTVideoFlags Flags;
|
|
||||||
|
|
||||||
// Frame Rate Numerator/Denominator in Frames Per Second, for example Numerator 60 and Denominator 1 is 60 frames per second.
|
|
||||||
int FrameRateN;
|
|
||||||
int FrameRateD;
|
|
||||||
|
|
||||||
// Display aspect ratio expressed as a ratio of width/height. For example 1.777777777777778 for 16/9
|
|
||||||
float AspectRatio;
|
|
||||||
|
|
||||||
enum OMTColorSpace ColorSpace;
|
|
||||||
|
|
||||||
//Audio Properties
|
|
||||||
// Sample rate, i.e 48000, 44100 etc
|
|
||||||
int SampleRate;
|
|
||||||
// Audio Channels. A maximum of 32 channels are supported.
|
|
||||||
int Channels;
|
|
||||||
// Number of 32bit floating point samples per channel/plane. Each plane should contain SamplesPerChannel*4 bytes.
|
|
||||||
int SamplesPerChannel;
|
|
||||||
|
|
||||||
//Data Properties
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Video: Uncompressed pixel data (or compressed VMX1 data when sending and Codec set to VMX1)
|
|
||||||
*
|
|
||||||
* Audio: Planar 32bit floating point audio
|
|
||||||
*
|
|
||||||
* Metadata: UTF-8 encoded XML string with terminating null character
|
|
||||||
*/
|
|
||||||
void* Data;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Video: Number of bytes total including stride
|
|
||||||
*
|
|
||||||
* Audio: Number of bytes (SamplesPerChannel * Channels * 4)
|
|
||||||
*
|
|
||||||
* Metadata: Number of bytes in UTF-8 encoded string + 1 for terminating null character.
|
|
||||||
*/
|
|
||||||
int DataLength;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Receive only. Use standard Data/DataLength if sending VMX1 frames with a Sender
|
|
||||||
*
|
|
||||||
* If IncludeCompressed or CompressedOnly OMTReceiveFlags is set, this will include the original compressed video frame in VMX1 format.
|
|
||||||
*
|
|
||||||
* This could then be muxed into an AVI or MOV file using FFmpeg or similar APIs
|
|
||||||
*/
|
|
||||||
void* CompressedData;
|
|
||||||
int CompressedLength;
|
|
||||||
|
|
||||||
//Frame MetaData Properties
|
|
||||||
// Per frame metadata as UTF-8 encoded string + 1 for null character. Up to 65536 bytes supported.
|
|
||||||
void* FrameMetadata;
|
|
||||||
|
|
||||||
// Length in bytes of per frame metadata including null character
|
|
||||||
int FrameMetadataLength;
|
|
||||||
|
|
||||||
} OMTMediaFrame;
|
|
||||||
|
|
||||||
typedef long long omt_receive_t;
|
|
||||||
typedef long long omt_send_t;
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
|
||||||
* =================================================
|
|
||||||
* Discovery
|
|
||||||
* =================================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a list of sources currently available on the network.
|
|
||||||
*
|
|
||||||
* Return value is an array of UTF-8 char pointers.
|
|
||||||
*
|
|
||||||
* This array is valid until the next call to getaddresses.
|
|
||||||
*
|
|
||||||
* @param[in] count Number of entries in the returned array.
|
|
||||||
*/
|
|
||||||
char** omt_discovery_getaddresses(int * count);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* =================================================
|
|
||||||
* Receive
|
|
||||||
* =================================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new Receiver and begin connecting to the Sender specified by address.
|
|
||||||
* @param[in] address Address to connect to, either the full name provided by OMTDiscovery or a URL in the format omt://hostname:port</param>
|
|
||||||
* @param[in] frameTypes Specify the types of frames to receive, for example to setup audio only or metadata only feeds
|
|
||||||
* @param[in] format Specify the preferred uncompressed video format to receive. UYVYorBGRA will only receive BGRA frames when an alpha channel is present.
|
|
||||||
* @param[in] flags Specify optional flags such as requesting a Preview feed only, or including the compressed (VMX) data with each frame for further processing (or recording).
|
|
||||||
*/
|
|
||||||
omt_receive_t* omt_receive_create(const char* address, OMTFrameType frameTypes, OMTPreferredVideoFormat format, OMTReceiveFlags flags);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroy instance created with omt_receive_create.
|
|
||||||
*
|
|
||||||
* Make sure any threads currently accessing the omt_receive_ functions with this instance are closed before calling.
|
|
||||||
*/
|
|
||||||
void omt_receive_destroy(omt_receive_t* instance);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Receive any available frames in the buffer, or wait for frames if empty
|
|
||||||
*
|
|
||||||
* Returns a valid OMTMediaFrame if a frame was found, null if timed out.
|
|
||||||
*
|
|
||||||
* The data in this struct is valid for until the next call to omt_receive for this instance and frameType.
|
|
||||||
*
|
|
||||||
* Pointers do not need to be freed by the caller.
|
|
||||||
*
|
|
||||||
* @param[in] frameTypes The frame types to receive. Set multiple types to receive them all in a single thread. Set individually if using separate threads for audio/video/metadata
|
|
||||||
* @param[in] timeoutMilliseconds The maximum time to wait for a new frame if empty
|
|
||||||
*/
|
|
||||||
OMTMediaFrame* omt_receive(omt_receive_t* instance, OMTFrameType frameTypes, int timeoutMilliseconds);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send a metadata frame to the sender. Does not support other frame types.
|
|
||||||
*/
|
|
||||||
int omt_receive_send(omt_receive_t* instance, OMTMediaFrame* frame);
|
|
||||||
|
|
||||||
void omt_receive_settally(omt_receive_t* instance, OMTTally* tally);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Receives the current tally state across all connections to a Sender, not just this Receiver.
|
|
||||||
*
|
|
||||||
* If this function times out, the last known tally state will be received.
|
|
||||||
*
|
|
||||||
* Returns 0 if timed out or tally didn't change. 1 otherwise.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
int omt_receive_gettally(omt_send_t* instance, int timeoutMilliseconds, OMTTally* tally);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Change the flags on the current receive instance. Will apply from the next frame received.
|
|
||||||
* This allows dynamic switching between preview mode.
|
|
||||||
*/
|
|
||||||
void omt_receive_setflags(omt_receive_t* instance, OMTReceiveFlags flags);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inform the sender of the quality preference for this receiver. See OMTQuality documentation for more information.
|
|
||||||
*/
|
|
||||||
void omt_receive_setsuggestedquality(omt_receive_t* instance, OMTQuality quality);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve optional information describing the sender. Valid only when connected. Returns null if disconnected or no sender information was provided by sender.
|
|
||||||
*/
|
|
||||||
void omt_receive_getsenderinformation(omt_receive_t* instance, OMTSenderInfo* info);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve statistics for video and audio.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
void omt_receive_getvideostatistics(omt_receive_t* instance, OMTStatistics* stats);
|
|
||||||
void omt_receive_getaudiostatistics(omt_receive_t* instance, OMTStatistics* stats);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* =================================================
|
|
||||||
* Send
|
|
||||||
* =================================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new instance of the OMT Sender
|
|
||||||
*
|
|
||||||
* @param[in] name Specify the name of the source not including hostname
|
|
||||||
* @param[in] quality Specify the quality to use for video encoding. If Default, this can be automatically adjusted based on Receiver requirements.
|
|
||||||
*/
|
|
||||||
omt_send_t* omt_send_create(const char* name, OMTQuality quality);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Optionally set information describing the sender.
|
|
||||||
*/
|
|
||||||
void omt_send_setsenderinformation(omt_send_t* instance, OMTSenderInfo* info);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add to the list of metadata that is sent immediately upon a new connection by a receiver.
|
|
||||||
*
|
|
||||||
* This metadata will also be immediately sent to any currently connected receivers.
|
|
||||||
*
|
|
||||||
* @param[in] metadata XML metadata in UTF-8 encoding followed by a null terminator.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
void omt_send_addconnectionmetadata(omt_send_t* instance, const char* metadata);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clears the list of metadata that is sent immediately upon a new connection by a receiver.
|
|
||||||
*/
|
|
||||||
void omt_send_clearconnectionmetadata(omt_send_t* instance);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use this to inform receivers to connect to a different address.
|
|
||||||
*
|
|
||||||
* This is used to create a "virtual source" that can be dynamically switched as needed.
|
|
||||||
*
|
|
||||||
* This is useful for scenarios where receiver needs to be changed remotely.
|
|
||||||
*
|
|
||||||
* @param[in] newAddress The new address. Set to null or empty to disable redirect.
|
|
||||||
*/
|
|
||||||
void omt_send_setredirect(omt_send_t* instance, const char* newAddress);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the Discovery address in the format HOSTNAME (NAME)
|
|
||||||
*
|
|
||||||
* Returns the length in bytes of the UTF-8 encoded value including null terminator.
|
|
||||||
*
|
|
||||||
* maxLength specifies the maximum amount of bytes allocated to value by the caller.
|
|
||||||
*/
|
|
||||||
int omt_send_getaddress(omt_send_t* instance, char* address, int maxLength);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroy instance created with omt_send_create.
|
|
||||||
*
|
|
||||||
* Make sure any threads currently accessing the omt_send_ functions with this instance are closed before calling.
|
|
||||||
*/
|
|
||||||
void omt_send_destroy(omt_send_t* instance);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send a frame to any receivers currently connected.
|
|
||||||
*
|
|
||||||
* Video: 'UYVY', 'YUY2', 'NV12', 'YV12, 'BGRA', 'UYVA', 'VMX1' are supported (BGRA will be treated as BGRX and UYVA as UYVY where alpha flags are not set)
|
|
||||||
*
|
|
||||||
* Audio: Supports planar 32bit floating point audio
|
|
||||||
*
|
|
||||||
* Metadata: Supports UTF8 encoded XML
|
|
||||||
*/
|
|
||||||
int omt_send(omt_send_t* instance, OMTMediaFrame* frame);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Total number of connections to this sender. Receivers establish one connection for video/metadata and a second for audio.
|
|
||||||
*/
|
|
||||||
int omt_send_connections(omt_send_t* instance);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Receive any available metadata in the buffer, or wait for metadata if empty
|
|
||||||
*
|
|
||||||
* Returns a valid OMTMediaFrame if found, null of timed out
|
|
||||||
*/
|
|
||||||
OMTMediaFrame* omt_send_receive(omt_send_t* instance, int timeoutMilliseconds);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Receives the current tally state across all connections to a Sender.
|
|
||||||
*
|
|
||||||
* If this function times out, the last known tally state will be received.
|
|
||||||
*
|
|
||||||
* Returns 0 if timed out or tally didn't change. 1 otherwise.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
int omt_send_gettally(omt_send_t* instance, int timeoutMilliseconds, OMTTally* tally);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve statistics for video and audio.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
void omt_send_getvideostatistics(omt_send_t* instance, OMTStatistics* stats);
|
|
||||||
void omt_send_getaudiostatistics(omt_send_t* instance, OMTStatistics* stats);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* =================================================
|
|
||||||
* Logging
|
|
||||||
* =================================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specify a log file filename, including the full path, or null to disable.
|
|
||||||
*
|
|
||||||
* If this function is not called, a log file is created in the ~/.OMT/logs folder for this process on Mac and Linux, C:\ProgramData\OMT\logs on Windows.
|
|
||||||
*
|
|
||||||
* To override the default folder used for for logs, set the OMT_STORAGE_PATH environment variable prior to calling any OMT functions.
|
|
||||||
*/
|
|
||||||
void omt_setloggingfilename(const char* filename);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* =================================================
|
|
||||||
* Settings
|
|
||||||
*
|
|
||||||
* These functions override the default settings which are stored in ~/.OMT/settings.xml on Mac and Linux and C:\ProgramData\OMT\settings.xml on Windows by default.
|
|
||||||
*
|
|
||||||
* To override the default folder used for for settings, set the OMT_STORAGE_PATH environment variable prior to calling any OMT functions.
|
|
||||||
*
|
|
||||||
* The following settings are currently supported:
|
|
||||||
*
|
|
||||||
* DiscoveryServer [string] specify a URL in the format omt://hostname:port to connect to for discovery. If left blank, default DNS-SD discovery behavior is enabled.
|
|
||||||
*
|
|
||||||
* NetworkPortStart [integer] specify the first port to create Send instances on. Defaults to 6400
|
|
||||||
*
|
|
||||||
* NetworkPortEnd [integer] specify the last port to create Send instances on. Defaults to 6600
|
|
||||||
*
|
|
||||||
* Settings changed here will persist only for the currently running process.
|
|
||||||
* =================================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the current value of a string setting.
|
|
||||||
* Returns the length in bytes of the UTF-8 encoded value including null terminator.
|
|
||||||
* maxLength specifies the maximum amount of bytes allocated to value by the caller.
|
|
||||||
*/
|
|
||||||
int omt_settings_get_string(const char* name, char* value, int maxLength);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set a string setting that will be used for the duration of this process.
|
|
||||||
* Value should be a null terminated UTF-8 encoded string.
|
|
||||||
*/
|
|
||||||
void omt_settings_set_string(const char* name, const char* value);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the current value of the integer setting.
|
|
||||||
*/
|
|
||||||
int omt_settings_get_integer(const char* name);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the specified setting to an integer value.
|
|
||||||
*/
|
|
||||||
void omt_settings_set_integer(const char* name, int value);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
|
||||||
# Visual Studio Version 17
|
|
||||||
VisualStudioVersion = 17.11.35431.28
|
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "libomt", "libomt.csproj", "{6D82AB51-3240-46DA-BAEF-F2DD8BB70037}"
|
|
||||||
EndProject
|
|
||||||
Global
|
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
|
||||||
Debug|Any CPU = Debug|Any CPU
|
|
||||||
Release|Any CPU = Release|Any CPU
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
|
||||||
{6D82AB51-3240-46DA-BAEF-F2DD8BB70037}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{6D82AB51-3240-46DA-BAEF-F2DD8BB70037}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{6D82AB51-3240-46DA-BAEF-F2DD8BB70037}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{6D82AB51-3240-46DA-BAEF-F2DD8BB70037}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
|
||||||
HideSolutionNode = FALSE
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
|
||||||
SolutionGuid = {AB7BCB17-6DF5-48E2-B1EE-BA18FCC50869}
|
|
||||||
EndGlobalSection
|
|
||||||
EndGlobal
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
/*
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Open Media Transport Contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
using libomtnet;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace libomt
|
|
||||||
{
|
|
||||||
internal class BaseInstance : OMTBase
|
|
||||||
{
|
|
||||||
protected OMTSendReceiveBase? instance;
|
|
||||||
protected IntPtr lastMetadata = IntPtr.Zero;
|
|
||||||
|
|
||||||
public bool GetTally(int millisecondsTimeout, IntPtr pTally)
|
|
||||||
{
|
|
||||||
bool result = false;
|
|
||||||
if (instance != null)
|
|
||||||
{
|
|
||||||
OMTTally tally = new OMTTally();
|
|
||||||
result = instance.GetTally(millisecondsTimeout, ref tally);
|
|
||||||
if (pTally != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
Marshal.WriteInt32(pTally, tally.Preview);
|
|
||||||
Marshal.WriteInt32(pTally, 4, tally.Program);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void GetAudioStatistics(IntPtr pStatistics)
|
|
||||||
{
|
|
||||||
if (instance != null)
|
|
||||||
{
|
|
||||||
OMTStatistics stats = instance.GetAudioStatistics();
|
|
||||||
stats.ToIntPtr(pStatistics);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public void GetVideoStatistics(IntPtr pStatistics)
|
|
||||||
{
|
|
||||||
if (instance != null)
|
|
||||||
{
|
|
||||||
OMTStatistics stats = instance.GetVideoStatistics();
|
|
||||||
stats.ToIntPtr(pStatistics);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
protected override void DisposeInternal()
|
|
||||||
{
|
|
||||||
if (lastMetadata != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
OMTMediaFrame.FreeIntPtr(lastMetadata);
|
|
||||||
lastMetadata = IntPtr.Zero;
|
|
||||||
}
|
|
||||||
if (instance != null)
|
|
||||||
{
|
|
||||||
instance.Dispose();
|
|
||||||
instance = null;
|
|
||||||
}
|
|
||||||
base.DisposeInternal();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
/*
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Open Media Transport Contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
using libomtnet;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace libomt
|
|
||||||
{
|
|
||||||
internal class DiscoveryInstance : OMTBase
|
|
||||||
{
|
|
||||||
private OMTDiscovery instance;
|
|
||||||
private static IntPtr lastAddresses = IntPtr.Zero;
|
|
||||||
private static int lastAddressesLength = 0;
|
|
||||||
public DiscoveryInstance()
|
|
||||||
{
|
|
||||||
instance = OMTDiscovery.GetInstance();
|
|
||||||
}
|
|
||||||
public OMTDiscovery Instance { get { return instance; } }
|
|
||||||
|
|
||||||
public IntPtr GetAddresses(IntPtr addressCount)
|
|
||||||
{
|
|
||||||
if (addressCount == IntPtr.Zero) return IntPtr.Zero;
|
|
||||||
string[] addresses = instance.GetAddresses();
|
|
||||||
if (addresses.Length > 0)
|
|
||||||
{
|
|
||||||
if (lastAddresses != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
InstanceHelper.FreeStringArray(lastAddresses, lastAddressesLength);
|
|
||||||
lastAddressesLength = 0;
|
|
||||||
lastAddresses = IntPtr.Zero;
|
|
||||||
}
|
|
||||||
Marshal.WriteInt32(addressCount, addresses.Length);
|
|
||||||
lastAddressesLength = addresses.Length;
|
|
||||||
lastAddresses = InstanceHelper.AllocStringArray(addresses);
|
|
||||||
return lastAddresses;
|
|
||||||
}
|
|
||||||
return IntPtr.Zero;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void DisposeInternal()
|
|
||||||
{
|
|
||||||
if (lastAddresses != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
InstanceHelper.FreeStringArray(lastAddresses, lastAddressesLength);
|
|
||||||
lastAddressesLength = 0;
|
|
||||||
lastAddresses = IntPtr.Zero;
|
|
||||||
}
|
|
||||||
base.DisposeInternal();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
/*
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Open Media Transport Contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace libomt
|
|
||||||
{
|
|
||||||
internal class InstanceHelper
|
|
||||||
{
|
|
||||||
public static Object? FromIntPtr(IntPtr handle)
|
|
||||||
{
|
|
||||||
if (handle != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
GCHandle gh = GCHandle.FromIntPtr(handle);
|
|
||||||
return gh.Target;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
public static IntPtr ToIntPtr(Object? obj)
|
|
||||||
{
|
|
||||||
if (obj == null) return IntPtr.Zero;
|
|
||||||
return GCHandle.ToIntPtr(GCHandle.Alloc(obj));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int WriteString(string value, IntPtr dst, int maxLength)
|
|
||||||
{
|
|
||||||
byte[] b = UTF8Encoding.UTF8.GetBytes(value);
|
|
||||||
if (b != null)
|
|
||||||
{
|
|
||||||
int len = b.Length + 1;
|
|
||||||
if (dst == IntPtr.Zero) return len;
|
|
||||||
if (maxLength > len)
|
|
||||||
{
|
|
||||||
Marshal.Copy(b, 0, dst, b.Length);
|
|
||||||
Marshal.WriteByte(dst, b.Length, 0);
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IntPtr AllocStringArray(string[] values)
|
|
||||||
{
|
|
||||||
IntPtr m = Marshal.AllocCoTaskMem(values.Length * IntPtr.Size);
|
|
||||||
for (int i = 0; i < values.Length; i++)
|
|
||||||
{
|
|
||||||
Marshal.WriteIntPtr(m, i * IntPtr.Size, Marshal.StringToCoTaskMemUTF8(values[i]));
|
|
||||||
}
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
public static void FreeStringArray(IntPtr ptr, int length)
|
|
||||||
{
|
|
||||||
if (ptr != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < length; i++)
|
|
||||||
{
|
|
||||||
Marshal.ZeroFreeCoTaskMemUTF8(Marshal.ReadIntPtr(ptr, i * IntPtr.Size));
|
|
||||||
}
|
|
||||||
Marshal.FreeCoTaskMem(ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,153 +0,0 @@
|
|||||||
/*
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Open Media Transport Contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
using libomtnet;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace libomt
|
|
||||||
{
|
|
||||||
internal class ReceiveInstance : BaseInstance
|
|
||||||
{
|
|
||||||
private OMTReceive? receive;
|
|
||||||
private IntPtr lastVideo = IntPtr.Zero;
|
|
||||||
private IntPtr lastAudio = IntPtr.Zero;
|
|
||||||
|
|
||||||
public ReceiveInstance(string name, OMTFrameType frameTypes, OMTPreferredVideoFormat format, OMTReceiveFlags flags) {
|
|
||||||
receive = new OMTReceive(name, frameTypes, format, flags);
|
|
||||||
instance = receive;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetFlags(int flags)
|
|
||||||
{
|
|
||||||
if (receive != null)
|
|
||||||
{
|
|
||||||
receive.SetFlags((OMTReceiveFlags)flags);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetSuggestedQuality(int quality)
|
|
||||||
{
|
|
||||||
if (receive != null)
|
|
||||||
{
|
|
||||||
receive.SetSuggestedQuality((OMTQuality)quality);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetTally(IntPtr pTally)
|
|
||||||
{
|
|
||||||
if (receive != null)
|
|
||||||
{
|
|
||||||
if (pTally != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
int preview = Marshal.ReadInt32(pTally);
|
|
||||||
int program = Marshal.ReadInt32(pTally, 4);
|
|
||||||
OMTTally t = new OMTTally(preview, program);
|
|
||||||
receive.SetTally(t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Send(OMTMediaFrame frame)
|
|
||||||
{
|
|
||||||
if (receive != null)
|
|
||||||
{
|
|
||||||
return receive.Send(frame);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void GetSenderInformation(IntPtr pInfo)
|
|
||||||
{
|
|
||||||
if (receive != null)
|
|
||||||
{
|
|
||||||
OMTSenderInfo info = receive.GetSenderInformation();
|
|
||||||
if (info != null)
|
|
||||||
{
|
|
||||||
if (pInfo != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
OMTUtils.WriteStringToPtrUTF8(info.ProductName, pInfo, UnmanagedExports.MAX_STRING_LENGTH);
|
|
||||||
OMTUtils.WriteStringToPtrUTF8(info.Manufacturer, pInfo + UnmanagedExports.MAX_STRING_LENGTH, UnmanagedExports.MAX_STRING_LENGTH);
|
|
||||||
OMTUtils.WriteStringToPtrUTF8(info.Version, pInfo + UnmanagedExports.MAX_STRING_LENGTH + UnmanagedExports.MAX_STRING_LENGTH, UnmanagedExports.MAX_STRING_LENGTH);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public IntPtr Receive(OMTFrameType frameTypes, int millisecondsTimeout)
|
|
||||||
{
|
|
||||||
if (receive != null)
|
|
||||||
{
|
|
||||||
OMTMediaFrame mediaFrame = new OMTMediaFrame();
|
|
||||||
if (receive.Receive(frameTypes, millisecondsTimeout, ref mediaFrame))
|
|
||||||
{
|
|
||||||
if (mediaFrame.Type == OMTFrameType.Video)
|
|
||||||
{
|
|
||||||
if (lastVideo != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
OMTMediaFrame.FreeIntPtr(lastVideo);
|
|
||||||
lastVideo = IntPtr.Zero;
|
|
||||||
}
|
|
||||||
lastVideo = OMTMediaFrame.ToIntPtr(mediaFrame);
|
|
||||||
return lastVideo;
|
|
||||||
} else if (mediaFrame.Type == OMTFrameType.Audio)
|
|
||||||
{
|
|
||||||
if (lastAudio != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
OMTMediaFrame.FreeIntPtr(lastAudio);
|
|
||||||
lastAudio = IntPtr.Zero;
|
|
||||||
}
|
|
||||||
lastAudio = OMTMediaFrame.ToIntPtr(mediaFrame);
|
|
||||||
return lastAudio;
|
|
||||||
} else if (mediaFrame.Type == OMTFrameType.Metadata)
|
|
||||||
{
|
|
||||||
if (lastMetadata != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
OMTMediaFrame.FreeIntPtr(lastMetadata);
|
|
||||||
lastMetadata = IntPtr.Zero;
|
|
||||||
}
|
|
||||||
lastMetadata = OMTMediaFrame.ToIntPtr(mediaFrame);
|
|
||||||
return lastMetadata;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return IntPtr.Zero;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void DisposeInternal()
|
|
||||||
{
|
|
||||||
if (lastVideo != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
OMTMediaFrame.FreeIntPtr(lastVideo);
|
|
||||||
lastVideo = IntPtr.Zero;
|
|
||||||
}
|
|
||||||
if (lastAudio != IntPtr.Zero) {
|
|
||||||
OMTMediaFrame.FreeIntPtr(lastAudio);
|
|
||||||
lastAudio = IntPtr.Zero;
|
|
||||||
}
|
|
||||||
receive = null;
|
|
||||||
base.DisposeInternal();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,143 +0,0 @@
|
|||||||
/*
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Open Media Transport Contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
using libomtnet;
|
|
||||||
using System.Net.NetworkInformation;
|
|
||||||
|
|
||||||
namespace libomt
|
|
||||||
{
|
|
||||||
internal class SendInstance : BaseInstance
|
|
||||||
{
|
|
||||||
private OMTSend? send;
|
|
||||||
|
|
||||||
public SendInstance( string name, int quality)
|
|
||||||
{
|
|
||||||
send = new OMTSend(name, (OMTQuality)quality);
|
|
||||||
instance = send;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Send(OMTMediaFrame frame)
|
|
||||||
{
|
|
||||||
if (send != null)
|
|
||||||
{
|
|
||||||
return send.Send(frame);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetAddress(IntPtr value, int maxLength)
|
|
||||||
{
|
|
||||||
if (send != null)
|
|
||||||
{
|
|
||||||
return InstanceHelper.WriteString(send.Address, value, maxLength);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetRedirect(IntPtr newAddress)
|
|
||||||
{
|
|
||||||
if (send != null)
|
|
||||||
{
|
|
||||||
if (newAddress != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
send.SetRedirect(OMTUtils.PtrToStringUTF8(newAddress));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public IntPtr Receive(int millisecondsTimeout)
|
|
||||||
{
|
|
||||||
if (send != null)
|
|
||||||
{
|
|
||||||
OMTMediaFrame mediaFrame = new OMTMediaFrame();
|
|
||||||
if (send.Receive(millisecondsTimeout, ref mediaFrame))
|
|
||||||
{
|
|
||||||
if (mediaFrame.Type == OMTFrameType.Metadata)
|
|
||||||
{
|
|
||||||
if (lastMetadata != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
OMTMediaFrame.FreeIntPtr(lastMetadata);
|
|
||||||
lastMetadata = IntPtr.Zero;
|
|
||||||
}
|
|
||||||
lastMetadata = OMTMediaFrame.ToIntPtr(mediaFrame);
|
|
||||||
return lastMetadata;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return IntPtr.Zero;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Connections { get
|
|
||||||
{
|
|
||||||
if (send != null)
|
|
||||||
{
|
|
||||||
return send.Connections;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddConnectionMetadata(IntPtr pMetadata)
|
|
||||||
{
|
|
||||||
if (send != null)
|
|
||||||
{
|
|
||||||
if (pMetadata != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
string metadata = OMTUtils.PtrToStringUTF8(pMetadata);
|
|
||||||
send.AddConnectionMetadata(metadata);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public void ClearConnectionMetadata()
|
|
||||||
{
|
|
||||||
if (send != null)
|
|
||||||
{
|
|
||||||
send.ClearConnectionMetadata();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetSenderInformation(IntPtr pInfo)
|
|
||||||
{
|
|
||||||
if (send != null)
|
|
||||||
{
|
|
||||||
if (pInfo != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
OMTSenderInfo info = new OMTSenderInfo();
|
|
||||||
info.ProductName = OMTUtils.PtrToStringUTF8(pInfo, UnmanagedExports.MAX_STRING_LENGTH);
|
|
||||||
info.Manufacturer = OMTUtils.PtrToStringUTF8(pInfo + UnmanagedExports.MAX_STRING_LENGTH, UnmanagedExports.MAX_STRING_LENGTH);
|
|
||||||
info.Version = OMTUtils.PtrToStringUTF8(pInfo + UnmanagedExports.MAX_STRING_LENGTH + UnmanagedExports.MAX_STRING_LENGTH, UnmanagedExports.MAX_STRING_LENGTH);
|
|
||||||
send.SetSenderInformation(info);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
protected override void DisposeInternal()
|
|
||||||
{
|
|
||||||
send = null;
|
|
||||||
base.DisposeInternal();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,636 +0,0 @@
|
|||||||
/*
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Open Media Transport Contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Text;
|
|
||||||
using libomtnet;
|
|
||||||
|
|
||||||
namespace libomt
|
|
||||||
{
|
|
||||||
public class UnmanagedExports
|
|
||||||
{
|
|
||||||
private static object lockSync = new object();
|
|
||||||
private static DiscoveryInstance? discoveryInstance = null;
|
|
||||||
internal const int MAX_STRING_LENGTH = 1024;
|
|
||||||
|
|
||||||
static UnmanagedExports()
|
|
||||||
{
|
|
||||||
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(e.ExceptionObject.ToString(), "UnhandledException");
|
|
||||||
}
|
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "omt_settings_set_string")]
|
|
||||||
public static void OMTSettingsSetString(IntPtr name, IntPtr value)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (name != IntPtr.Zero && value != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
string? szName = Marshal.PtrToStringUTF8(name);
|
|
||||||
string? szValue = Marshal.PtrToStringUTF8(value);
|
|
||||||
if (szName != null && szValue != null)
|
|
||||||
{
|
|
||||||
OMTSettings.GetInstance().SetString(szName, szValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "omt_settings_set_string");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "omt_settings_get_string")]
|
|
||||||
public static int OMTSettingsGetString(IntPtr name, IntPtr value, int maxLength)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (name != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
string? szName = Marshal.PtrToStringUTF8(name);
|
|
||||||
if (szName != null)
|
|
||||||
{
|
|
||||||
string szValue = OMTSettings.GetInstance().GetString(szName, null);
|
|
||||||
if (szValue != null)
|
|
||||||
{
|
|
||||||
return InstanceHelper.WriteString(szValue, value, maxLength);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "omt_settings_get_string");
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "omt_settings_set_integer")]
|
|
||||||
public static void OMTSettingsSetInteger(IntPtr name, int value)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (name != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
string? szName = Marshal.PtrToStringUTF8(name);
|
|
||||||
if (szName != null)
|
|
||||||
{
|
|
||||||
OMTSettings.GetInstance().SetInteger(szName, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "omt_settings_set_integer");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "omt_settings_get_integer")]
|
|
||||||
public static int OMTSettingsGetInteger(IntPtr name)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (name != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
string? szName = Marshal.PtrToStringUTF8(name);
|
|
||||||
if (szName != null)
|
|
||||||
{
|
|
||||||
return OMTSettings.GetInstance().GetInteger(szName, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "omt_settings_get_integer");
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "omt_send_create")]
|
|
||||||
public static IntPtr OMTSendCreate(IntPtr name, int profile)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (name != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
string? szName = Marshal.PtrToStringUTF8(name);
|
|
||||||
if (szName != null)
|
|
||||||
{
|
|
||||||
return InstanceHelper.ToIntPtr(new SendInstance(szName, profile));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "omt_send_create");
|
|
||||||
}
|
|
||||||
return IntPtr.Zero;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "omt_receive_create")]
|
|
||||||
public static IntPtr OMTReceiveCreate(IntPtr name, OMTFrameType frameTypes, OMTPreferredVideoFormat format, OMTReceiveFlags receiveFlags)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (name != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
string? szName = Marshal.PtrToStringUTF8(name);
|
|
||||||
if (szName != null)
|
|
||||||
{
|
|
||||||
return InstanceHelper.ToIntPtr(new ReceiveInstance(szName, frameTypes, format, receiveFlags));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "omt_receive_create");
|
|
||||||
}
|
|
||||||
return IntPtr.Zero;
|
|
||||||
}
|
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "omt_send_destroy")]
|
|
||||||
public static void OMTSendDestroy(IntPtr instance)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (instance != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
SendInstance? sendInstance = (SendInstance?)InstanceHelper.FromIntPtr(instance);
|
|
||||||
if (sendInstance != null)
|
|
||||||
{
|
|
||||||
sendInstance.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "omt_send_destroy");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "omt_send_getaddress")]
|
|
||||||
public static int OMTSendGetAddress(IntPtr instance, IntPtr address, int maxLength)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (instance != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
SendInstance? sendInstance = (SendInstance?)InstanceHelper.FromIntPtr(instance);
|
|
||||||
if (sendInstance != null)
|
|
||||||
{
|
|
||||||
return sendInstance.GetAddress(address, maxLength);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "omt_send_getaddress");
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "omt_receive_destroy")]
|
|
||||||
public static void OMTReceiveDestroy(IntPtr instance)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (instance != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
ReceiveInstance? receiveInstance = (ReceiveInstance?)InstanceHelper.FromIntPtr(instance);
|
|
||||||
if (receiveInstance != null)
|
|
||||||
{
|
|
||||||
receiveInstance.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "omt_receive_destroy");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "omt_send")]
|
|
||||||
public static int OMTSend(IntPtr instance, IntPtr frame)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
SendInstance? sendInstance = (SendInstance?)InstanceHelper.FromIntPtr(instance);
|
|
||||||
if (sendInstance != null)
|
|
||||||
{
|
|
||||||
if (frame != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
OMTMediaFrame v = OMTMediaFrame.FromIntPtr(frame);
|
|
||||||
return sendInstance.Send(v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "omt_send");
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "omt_send_connections")]
|
|
||||||
public static int OMTSendConnections(IntPtr instance)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
SendInstance? sendInstance = (SendInstance?)InstanceHelper.FromIntPtr(instance);
|
|
||||||
if (sendInstance != null)
|
|
||||||
{
|
|
||||||
return sendInstance.Connections;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "omt_send_connections");
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "omt_receive_send")]
|
|
||||||
public static int OMTReceiveSend(IntPtr instance, IntPtr frame)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
ReceiveInstance? receiveInstance = (ReceiveInstance?)InstanceHelper.FromIntPtr(instance);
|
|
||||||
if (receiveInstance != null)
|
|
||||||
{
|
|
||||||
if (frame != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
OMTMediaFrame v = OMTMediaFrame.FromIntPtr(frame);
|
|
||||||
return receiveInstance.Send(v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "omt_receive_sendmetadata");
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "omt_send_receive")]
|
|
||||||
public static IntPtr OMTSendReceive(IntPtr instance, int millisecondsTimeout)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
SendInstance? sendInstance = (SendInstance?)InstanceHelper.FromIntPtr(instance);
|
|
||||||
if (sendInstance != null)
|
|
||||||
{
|
|
||||||
return sendInstance.Receive(millisecondsTimeout);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "omt_send_receive");
|
|
||||||
}
|
|
||||||
return IntPtr.Zero;
|
|
||||||
}
|
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "omt_receive")]
|
|
||||||
public static IntPtr OMTReceive(IntPtr instance, int frameTypes, int millisecondsTimeout)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
ReceiveInstance? receiveInstance = (ReceiveInstance?)InstanceHelper.FromIntPtr(instance);
|
|
||||||
if (receiveInstance != null)
|
|
||||||
{
|
|
||||||
return receiveInstance.Receive((OMTFrameType)frameTypes, millisecondsTimeout);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "omt_receive");
|
|
||||||
}
|
|
||||||
return IntPtr.Zero;
|
|
||||||
}
|
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "omt_receive_settally")]
|
|
||||||
public static void OMTReceiveSetTally(IntPtr instance, IntPtr tally)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
ReceiveInstance? receiveInstance = (ReceiveInstance?)InstanceHelper.FromIntPtr(instance);
|
|
||||||
if (receiveInstance != null)
|
|
||||||
{
|
|
||||||
receiveInstance.SetTally(tally);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "omt_receive_settally");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "omt_receive_setsuggestedquality")]
|
|
||||||
public static void OMTReceiveSetSuggestedQuality(IntPtr instance, int quality)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
ReceiveInstance? receiveInstance = (ReceiveInstance?)InstanceHelper.FromIntPtr(instance);
|
|
||||||
if (receiveInstance != null)
|
|
||||||
{
|
|
||||||
receiveInstance.SetSuggestedQuality(quality);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "omt_receive_setsuggestedquality");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "omt_receive_setflags")]
|
|
||||||
public static void OMTReceiveSetFlags(IntPtr instance, int flags)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
ReceiveInstance? receiveInstance = (ReceiveInstance?)InstanceHelper.FromIntPtr(instance);
|
|
||||||
if (receiveInstance != null)
|
|
||||||
{
|
|
||||||
receiveInstance.SetFlags(flags);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "omt_receive_setflags");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "omt_send_getvideostatistics")]
|
|
||||||
public static void OMTSendGetVideoStatistics(IntPtr instance, IntPtr statistics)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
SendInstance? sendInstance = (SendInstance?)InstanceHelper.FromIntPtr(instance);
|
|
||||||
if (sendInstance != null)
|
|
||||||
{
|
|
||||||
sendInstance.GetVideoStatistics(statistics);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "omt_send_getvideostatistics");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "omt_send_getaudiostatistics")]
|
|
||||||
public static void OMTSendGetAudioStatistics(IntPtr instance, IntPtr statistics)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
SendInstance? sendInstance = (SendInstance?)InstanceHelper.FromIntPtr(instance);
|
|
||||||
if (sendInstance != null)
|
|
||||||
{
|
|
||||||
sendInstance.GetAudioStatistics(statistics);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "omt_send_getaudiostatistics");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "omt_receive_getvideostatistics")]
|
|
||||||
public static void OMTReceiveGetVideoStatistics(IntPtr instance, IntPtr statistics)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
ReceiveInstance? receiveInstance = (ReceiveInstance?)InstanceHelper.FromIntPtr(instance);
|
|
||||||
if (receiveInstance != null)
|
|
||||||
{
|
|
||||||
receiveInstance.GetVideoStatistics(statistics);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "omt_receive_getvideostatistics");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "omt_receive_getaudiostatistics")]
|
|
||||||
public static void OMTReceiveGetAudioStatistics(IntPtr instance, IntPtr statistics)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
ReceiveInstance? receiveInstance = (ReceiveInstance?)InstanceHelper.FromIntPtr(instance);
|
|
||||||
if (receiveInstance != null)
|
|
||||||
{
|
|
||||||
receiveInstance.GetAudioStatistics(statistics);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "omt_receive_getaudiostatistics");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "omt_send_setredirect")]
|
|
||||||
public static void OMTSendSetRedirect(IntPtr instance, IntPtr newAddress)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
SendInstance? sendInstance = (SendInstance?)InstanceHelper.FromIntPtr(instance);
|
|
||||||
if (sendInstance != null)
|
|
||||||
{
|
|
||||||
sendInstance.SetRedirect(newAddress);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "omt_send_setredirect");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "omt_send_setsenderinformation")]
|
|
||||||
public static void OMTSendSetSenderInformation(IntPtr instance, IntPtr info)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
SendInstance? sendInstance = (SendInstance?)InstanceHelper.FromIntPtr(instance);
|
|
||||||
if (sendInstance != null)
|
|
||||||
{
|
|
||||||
sendInstance.SetSenderInformation(info);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "omt_send_setsenderinformation");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "omt_send_addconnectionmetadata")]
|
|
||||||
public static void OMTSendAddConnectionMetadata(IntPtr instance, IntPtr metadata)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
SendInstance? sendInstance = (SendInstance?)InstanceHelper.FromIntPtr(instance);
|
|
||||||
if (sendInstance != null)
|
|
||||||
{
|
|
||||||
sendInstance.AddConnectionMetadata(metadata);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "omt_send_addconnectionmetadata");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "omt_send_clearconnectionmetadata")]
|
|
||||||
public static void OMTSendClearConnectionMetadata(IntPtr instance)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
SendInstance? sendInstance = (SendInstance?)InstanceHelper.FromIntPtr(instance);
|
|
||||||
if (sendInstance != null)
|
|
||||||
{
|
|
||||||
sendInstance.ClearConnectionMetadata();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "omt_send_clearconnectionmetadata");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "omt_receive_getsenderinformation")]
|
|
||||||
public static void OMTReceiveGetSenderInformation(IntPtr instance, IntPtr info)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
ReceiveInstance? receiveInstance = (ReceiveInstance?)InstanceHelper.FromIntPtr(instance);
|
|
||||||
if (receiveInstance != null)
|
|
||||||
{
|
|
||||||
receiveInstance.GetSenderInformation(info);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "omt_receive_getsenderinformation");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "omt_receive_gettally")]
|
|
||||||
public static bool OMTReceiveGetTally(IntPtr instance, int millisecondsTimeout, IntPtr tally)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (tally != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
ReceiveInstance? receiveInstance = (ReceiveInstance?)InstanceHelper.FromIntPtr(instance);
|
|
||||||
if (receiveInstance != null)
|
|
||||||
{
|
|
||||||
return receiveInstance.GetTally(millisecondsTimeout, tally);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "omt_receive_gettally");
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "omt_send_gettally")]
|
|
||||||
public static bool OMTSendGetTally(IntPtr instance, int millisecondsTimeout, IntPtr tally)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
SendInstance? sendInstance = (SendInstance?)InstanceHelper.FromIntPtr(instance);
|
|
||||||
if (sendInstance != null)
|
|
||||||
{
|
|
||||||
return sendInstance.GetTally(millisecondsTimeout, tally);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "omt_send_gettally");
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "omt_setloggingfilename")]
|
|
||||||
public static void OMTSetLoggingFilename(IntPtr filename)
|
|
||||||
{
|
|
||||||
if (filename != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
string? fn = Marshal.PtrToStringUTF8(filename);
|
|
||||||
if (fn != null)
|
|
||||||
{
|
|
||||||
OMTLogging.SetFilename(fn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "omt_discovery_getaddresses")]
|
|
||||||
private static IntPtr OMTDiscoveryGetAddresses(IntPtr addressCount)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
DiscoveryInstance? instance = CreateDiscovery();
|
|
||||||
if (instance != null)
|
|
||||||
{
|
|
||||||
return instance.GetAddresses(addressCount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "omt_discovery_getaddresses");
|
|
||||||
}
|
|
||||||
return IntPtr.Zero;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static DiscoveryInstance? CreateDiscovery()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
lock (lockSync)
|
|
||||||
{
|
|
||||||
if (discoveryInstance == null)
|
|
||||||
{
|
|
||||||
discoveryInstance = new DiscoveryInstance();
|
|
||||||
}
|
|
||||||
return discoveryInstance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "omt_creatediscovery");
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
65
3rd/libomtnet/.gitattributes
vendored
65
3rd/libomtnet/.gitattributes
vendored
@@ -1,65 +0,0 @@
|
|||||||
###############################################################################
|
|
||||||
# Set default behavior to automatically normalize line endings.
|
|
||||||
###############################################################################
|
|
||||||
* text=auto
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# Set default behavior for command prompt diff.
|
|
||||||
#
|
|
||||||
# This is need for earlier builds of msysgit that does not have it on by
|
|
||||||
# default for csharp files.
|
|
||||||
# Note: This is only used by command line
|
|
||||||
###############################################################################
|
|
||||||
#*.cs diff=csharp
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# Set the merge driver for project and solution files
|
|
||||||
#
|
|
||||||
# Merging from the command prompt will add diff markers to the files if there
|
|
||||||
# are conflicts (Merging from VS is not affected by the settings below, in VS
|
|
||||||
# the diff markers are never inserted). Diff markers may cause the following
|
|
||||||
# file extensions to fail to load in VS. An alternative would be to treat
|
|
||||||
# these files as binary and thus will always conflict and require user
|
|
||||||
# intervention with every merge. To do so, just uncomment the entries below
|
|
||||||
###############################################################################
|
|
||||||
#*.sln merge=binary
|
|
||||||
#*.csproj merge=binary
|
|
||||||
#*.vbproj merge=binary
|
|
||||||
#*.vcxproj merge=binary
|
|
||||||
#*.vcproj merge=binary
|
|
||||||
#*.dbproj merge=binary
|
|
||||||
#*.fsproj merge=binary
|
|
||||||
#*.lsproj merge=binary
|
|
||||||
#*.wixproj merge=binary
|
|
||||||
#*.modelproj merge=binary
|
|
||||||
#*.sqlproj merge=binary
|
|
||||||
#*.wwaproj merge=binary
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# behavior for image files
|
|
||||||
#
|
|
||||||
# image files are treated as binary by default.
|
|
||||||
###############################################################################
|
|
||||||
#*.jpg binary
|
|
||||||
#*.png binary
|
|
||||||
#*.gif binary
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# diff behavior for common document formats
|
|
||||||
#
|
|
||||||
# Convert binary document formats to text before diffing them. This feature
|
|
||||||
# is only available from the command line. Turn it on by uncommenting the
|
|
||||||
# entries below.
|
|
||||||
###############################################################################
|
|
||||||
#*.doc diff=astextplain
|
|
||||||
#*.DOC diff=astextplain
|
|
||||||
#*.docx diff=astextplain
|
|
||||||
#*.DOCX diff=astextplain
|
|
||||||
#*.dot diff=astextplain
|
|
||||||
#*.DOT diff=astextplain
|
|
||||||
#*.pdf diff=astextplain
|
|
||||||
#*.PDF diff=astextplain
|
|
||||||
#*.rtf diff=astextplain
|
|
||||||
#*.RTF diff=astextplain
|
|
||||||
|
|
||||||
*.html linguist-detectable=false
|
|
||||||
363
3rd/libomtnet/.gitignore
vendored
363
3rd/libomtnet/.gitignore
vendored
@@ -1,363 +0,0 @@
|
|||||||
## Ignore Visual Studio temporary files, build results, and
|
|
||||||
## files generated by popular Visual Studio add-ons.
|
|
||||||
##
|
|
||||||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
|
||||||
|
|
||||||
# User-specific files
|
|
||||||
*.rsuser
|
|
||||||
*.suo
|
|
||||||
*.user
|
|
||||||
*.userosscache
|
|
||||||
*.sln.docstates
|
|
||||||
|
|
||||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
|
||||||
*.userprefs
|
|
||||||
|
|
||||||
# Mono auto generated files
|
|
||||||
mono_crash.*
|
|
||||||
|
|
||||||
# Build results
|
|
||||||
[Dd]ebug/
|
|
||||||
[Dd]ebugPublic/
|
|
||||||
[Rr]elease/
|
|
||||||
[Rr]eleases/
|
|
||||||
x64/
|
|
||||||
x86/
|
|
||||||
[Ww][Ii][Nn]32/
|
|
||||||
[Aa][Rr][Mm]/
|
|
||||||
[Aa][Rr][Mm]64/
|
|
||||||
bld/
|
|
||||||
[Bb]in/
|
|
||||||
[Oo]bj/
|
|
||||||
[Oo]ut/
|
|
||||||
[Ll]og/
|
|
||||||
[Ll]ogs/
|
|
||||||
|
|
||||||
# Visual Studio 2015/2017 cache/options directory
|
|
||||||
.vs/
|
|
||||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
|
||||||
#wwwroot/
|
|
||||||
|
|
||||||
# Visual Studio 2017 auto generated files
|
|
||||||
Generated\ Files/
|
|
||||||
|
|
||||||
# MSTest test Results
|
|
||||||
[Tt]est[Rr]esult*/
|
|
||||||
[Bb]uild[Ll]og.*
|
|
||||||
|
|
||||||
# NUnit
|
|
||||||
*.VisualState.xml
|
|
||||||
TestResult.xml
|
|
||||||
nunit-*.xml
|
|
||||||
|
|
||||||
# Build Results of an ATL Project
|
|
||||||
[Dd]ebugPS/
|
|
||||||
[Rr]eleasePS/
|
|
||||||
dlldata.c
|
|
||||||
|
|
||||||
# Benchmark Results
|
|
||||||
BenchmarkDotNet.Artifacts/
|
|
||||||
|
|
||||||
# .NET Core
|
|
||||||
project.lock.json
|
|
||||||
project.fragment.lock.json
|
|
||||||
artifacts/
|
|
||||||
|
|
||||||
# ASP.NET Scaffolding
|
|
||||||
ScaffoldingReadMe.txt
|
|
||||||
|
|
||||||
# StyleCop
|
|
||||||
StyleCopReport.xml
|
|
||||||
|
|
||||||
# Files built by Visual Studio
|
|
||||||
*_i.c
|
|
||||||
*_p.c
|
|
||||||
*_h.h
|
|
||||||
*.ilk
|
|
||||||
*.meta
|
|
||||||
*.obj
|
|
||||||
*.iobj
|
|
||||||
*.pch
|
|
||||||
*.pdb
|
|
||||||
*.ipdb
|
|
||||||
*.pgc
|
|
||||||
*.pgd
|
|
||||||
*.rsp
|
|
||||||
*.sbr
|
|
||||||
*.tlb
|
|
||||||
*.tli
|
|
||||||
*.tlh
|
|
||||||
*.tmp
|
|
||||||
*.tmp_proj
|
|
||||||
*_wpftmp.csproj
|
|
||||||
*.log
|
|
||||||
*.vspscc
|
|
||||||
*.vssscc
|
|
||||||
.builds
|
|
||||||
*.pidb
|
|
||||||
*.svclog
|
|
||||||
*.scc
|
|
||||||
|
|
||||||
# Chutzpah Test files
|
|
||||||
_Chutzpah*
|
|
||||||
|
|
||||||
# Visual C++ cache files
|
|
||||||
ipch/
|
|
||||||
*.aps
|
|
||||||
*.ncb
|
|
||||||
*.opendb
|
|
||||||
*.opensdf
|
|
||||||
*.sdf
|
|
||||||
*.cachefile
|
|
||||||
*.VC.db
|
|
||||||
*.VC.VC.opendb
|
|
||||||
|
|
||||||
# Visual Studio profiler
|
|
||||||
*.psess
|
|
||||||
*.vsp
|
|
||||||
*.vspx
|
|
||||||
*.sap
|
|
||||||
|
|
||||||
# Visual Studio Trace Files
|
|
||||||
*.e2e
|
|
||||||
|
|
||||||
# TFS 2012 Local Workspace
|
|
||||||
$tf/
|
|
||||||
|
|
||||||
# Guidance Automation Toolkit
|
|
||||||
*.gpState
|
|
||||||
|
|
||||||
# ReSharper is a .NET coding add-in
|
|
||||||
_ReSharper*/
|
|
||||||
*.[Rr]e[Ss]harper
|
|
||||||
*.DotSettings.user
|
|
||||||
|
|
||||||
# TeamCity is a build add-in
|
|
||||||
_TeamCity*
|
|
||||||
|
|
||||||
# DotCover is a Code Coverage Tool
|
|
||||||
*.dotCover
|
|
||||||
|
|
||||||
# AxoCover is a Code Coverage Tool
|
|
||||||
.axoCover/*
|
|
||||||
!.axoCover/settings.json
|
|
||||||
|
|
||||||
# Coverlet is a free, cross platform Code Coverage Tool
|
|
||||||
coverage*.json
|
|
||||||
coverage*.xml
|
|
||||||
coverage*.info
|
|
||||||
|
|
||||||
# Visual Studio code coverage results
|
|
||||||
*.coverage
|
|
||||||
*.coveragexml
|
|
||||||
|
|
||||||
# NCrunch
|
|
||||||
_NCrunch_*
|
|
||||||
.*crunch*.local.xml
|
|
||||||
nCrunchTemp_*
|
|
||||||
|
|
||||||
# MightyMoose
|
|
||||||
*.mm.*
|
|
||||||
AutoTest.Net/
|
|
||||||
|
|
||||||
# Web workbench (sass)
|
|
||||||
.sass-cache/
|
|
||||||
|
|
||||||
# Installshield output folder
|
|
||||||
[Ee]xpress/
|
|
||||||
|
|
||||||
# DocProject is a documentation generator add-in
|
|
||||||
DocProject/buildhelp/
|
|
||||||
DocProject/Help/*.HxT
|
|
||||||
DocProject/Help/*.HxC
|
|
||||||
DocProject/Help/*.hhc
|
|
||||||
DocProject/Help/*.hhk
|
|
||||||
DocProject/Help/*.hhp
|
|
||||||
DocProject/Help/Html2
|
|
||||||
DocProject/Help/html
|
|
||||||
|
|
||||||
# Click-Once directory
|
|
||||||
publish/
|
|
||||||
|
|
||||||
# Publish Web Output
|
|
||||||
*.[Pp]ublish.xml
|
|
||||||
*.azurePubxml
|
|
||||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
|
||||||
# but database connection strings (with potential passwords) will be unencrypted
|
|
||||||
*.pubxml
|
|
||||||
*.publishproj
|
|
||||||
|
|
||||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
|
||||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
|
||||||
# in these scripts will be unencrypted
|
|
||||||
PublishScripts/
|
|
||||||
|
|
||||||
# NuGet Packages
|
|
||||||
*.nupkg
|
|
||||||
# NuGet Symbol Packages
|
|
||||||
*.snupkg
|
|
||||||
# The packages folder can be ignored because of Package Restore
|
|
||||||
**/[Pp]ackages/*
|
|
||||||
# except build/, which is used as an MSBuild target.
|
|
||||||
!**/[Pp]ackages/build/
|
|
||||||
# Uncomment if necessary however generally it will be regenerated when needed
|
|
||||||
#!**/[Pp]ackages/repositories.config
|
|
||||||
# NuGet v3's project.json files produces more ignorable files
|
|
||||||
*.nuget.props
|
|
||||||
*.nuget.targets
|
|
||||||
|
|
||||||
# Microsoft Azure Build Output
|
|
||||||
csx/
|
|
||||||
*.build.csdef
|
|
||||||
|
|
||||||
# Microsoft Azure Emulator
|
|
||||||
ecf/
|
|
||||||
rcf/
|
|
||||||
|
|
||||||
# Windows Store app package directories and files
|
|
||||||
AppPackages/
|
|
||||||
BundleArtifacts/
|
|
||||||
Package.StoreAssociation.xml
|
|
||||||
_pkginfo.txt
|
|
||||||
*.appx
|
|
||||||
*.appxbundle
|
|
||||||
*.appxupload
|
|
||||||
|
|
||||||
# Visual Studio cache files
|
|
||||||
# files ending in .cache can be ignored
|
|
||||||
*.[Cc]ache
|
|
||||||
# but keep track of directories ending in .cache
|
|
||||||
!?*.[Cc]ache/
|
|
||||||
|
|
||||||
# Others
|
|
||||||
ClientBin/
|
|
||||||
~$*
|
|
||||||
*~
|
|
||||||
*.dbmdl
|
|
||||||
*.dbproj.schemaview
|
|
||||||
*.jfm
|
|
||||||
*.pfx
|
|
||||||
*.publishsettings
|
|
||||||
orleans.codegen.cs
|
|
||||||
|
|
||||||
# Including strong name files can present a security risk
|
|
||||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
|
||||||
#*.snk
|
|
||||||
|
|
||||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
|
||||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
|
||||||
#bower_components/
|
|
||||||
|
|
||||||
# RIA/Silverlight projects
|
|
||||||
Generated_Code/
|
|
||||||
|
|
||||||
# Backup & report files from converting an old project file
|
|
||||||
# to a newer Visual Studio version. Backup files are not needed,
|
|
||||||
# because we have git ;-)
|
|
||||||
_UpgradeReport_Files/
|
|
||||||
Backup*/
|
|
||||||
UpgradeLog*.XML
|
|
||||||
UpgradeLog*.htm
|
|
||||||
ServiceFabricBackup/
|
|
||||||
*.rptproj.bak
|
|
||||||
|
|
||||||
# SQL Server files
|
|
||||||
*.mdf
|
|
||||||
*.ldf
|
|
||||||
*.ndf
|
|
||||||
|
|
||||||
# Business Intelligence projects
|
|
||||||
*.rdl.data
|
|
||||||
*.bim.layout
|
|
||||||
*.bim_*.settings
|
|
||||||
*.rptproj.rsuser
|
|
||||||
*- [Bb]ackup.rdl
|
|
||||||
*- [Bb]ackup ([0-9]).rdl
|
|
||||||
*- [Bb]ackup ([0-9][0-9]).rdl
|
|
||||||
|
|
||||||
# Microsoft Fakes
|
|
||||||
FakesAssemblies/
|
|
||||||
|
|
||||||
# GhostDoc plugin setting file
|
|
||||||
*.GhostDoc.xml
|
|
||||||
|
|
||||||
# Node.js Tools for Visual Studio
|
|
||||||
.ntvs_analysis.dat
|
|
||||||
node_modules/
|
|
||||||
|
|
||||||
# Visual Studio 6 build log
|
|
||||||
*.plg
|
|
||||||
|
|
||||||
# Visual Studio 6 workspace options file
|
|
||||||
*.opt
|
|
||||||
|
|
||||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
|
||||||
*.vbw
|
|
||||||
|
|
||||||
# Visual Studio LightSwitch build output
|
|
||||||
**/*.HTMLClient/GeneratedArtifacts
|
|
||||||
**/*.DesktopClient/GeneratedArtifacts
|
|
||||||
**/*.DesktopClient/ModelManifest.xml
|
|
||||||
**/*.Server/GeneratedArtifacts
|
|
||||||
**/*.Server/ModelManifest.xml
|
|
||||||
_Pvt_Extensions
|
|
||||||
|
|
||||||
# Paket dependency manager
|
|
||||||
.paket/paket.exe
|
|
||||||
paket-files/
|
|
||||||
|
|
||||||
# FAKE - F# Make
|
|
||||||
.fake/
|
|
||||||
|
|
||||||
# CodeRush personal settings
|
|
||||||
.cr/personal
|
|
||||||
|
|
||||||
# Python Tools for Visual Studio (PTVS)
|
|
||||||
__pycache__/
|
|
||||||
*.pyc
|
|
||||||
|
|
||||||
# Cake - Uncomment if you are using it
|
|
||||||
# tools/**
|
|
||||||
# !tools/packages.config
|
|
||||||
|
|
||||||
# Tabs Studio
|
|
||||||
*.tss
|
|
||||||
|
|
||||||
# Telerik's JustMock configuration file
|
|
||||||
*.jmconfig
|
|
||||||
|
|
||||||
# BizTalk build output
|
|
||||||
*.btp.cs
|
|
||||||
*.btm.cs
|
|
||||||
*.odx.cs
|
|
||||||
*.xsd.cs
|
|
||||||
|
|
||||||
# OpenCover UI analysis results
|
|
||||||
OpenCover/
|
|
||||||
|
|
||||||
# Azure Stream Analytics local run output
|
|
||||||
ASALocalRun/
|
|
||||||
|
|
||||||
# MSBuild Binary and Structured Log
|
|
||||||
*.binlog
|
|
||||||
|
|
||||||
# NVidia Nsight GPU debugger configuration file
|
|
||||||
*.nvuser
|
|
||||||
|
|
||||||
# MFractors (Xamarin productivity tool) working folder
|
|
||||||
.mfractor/
|
|
||||||
|
|
||||||
# Local History for Visual Studio
|
|
||||||
.localhistory/
|
|
||||||
|
|
||||||
# BeatPulse healthcheck temp database
|
|
||||||
healthchecksdb
|
|
||||||
|
|
||||||
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
|
||||||
MigrationBackup/
|
|
||||||
|
|
||||||
# Ionide (cross platform F# VS Code tools) working folder
|
|
||||||
.ionide/
|
|
||||||
|
|
||||||
# Fody - auto-generated XML schema
|
|
||||||
FodyWeavers.xsd
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2025 Open Media Transport Contributors
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
@@ -1,184 +0,0 @@
|
|||||||
# Open Media Transport (OMT) Protocol 1.0
|
|
||||||
|
|
||||||
## Introduction
|
|
||||||
|
|
||||||
This guide should serve as an overview of the protocol used by Open Media Transport.
|
|
||||||
|
|
||||||
Implementers should first look at using libomtnet or libomt as these are complete implementations covering the vast majority of platforms.
|
|
||||||
|
|
||||||
## Basics
|
|
||||||
|
|
||||||
Open Media Transport consists of three major components:
|
|
||||||
|
|
||||||
1. TCP Protocol for sending/receiving video, audio and metadata.
|
|
||||||
2. Special Metadata commands to control various aspects of the connection.
|
|
||||||
3. DNS-SD for discovery (RFC 6763).
|
|
||||||
|
|
||||||
## Data Types
|
|
||||||
|
|
||||||
All data types are stored in Little-Endian byte order.
|
|
||||||
This is in contrast to the often used Big-Endian network order.
|
|
||||||
|
|
||||||
## TCP Protocol
|
|
||||||
|
|
||||||
All data is encapsulated into a frame consisting of the following parts:
|
|
||||||
|
|
||||||
### HEADER (16 bytes)
|
|
||||||
|
|
||||||
BYTE Version // Must be 1
|
|
||||||
|
|
||||||
BYTE FrameType // Metadata = 1, Video = 2, Audio = 4
|
|
||||||
|
|
||||||
INT64 Timestamp // Timestamp where 1 second = 10,000,000
|
|
||||||
|
|
||||||
UINT16 MetadataLength // Length of XML UTF-8 per-frame metadata including null character.
|
|
||||||
|
|
||||||
INT32 DataLength //ExtendedHeader + Data length + MetadataLength, excluding this header
|
|
||||||
|
|
||||||
### VIDEO EXTENDED HEADER (32 bytes) (Mandatory for video frame type)
|
|
||||||
|
|
||||||
INT32 Codec //Video codec FourCC
|
|
||||||
|
|
||||||
INT32 Width //Video width in pixels
|
|
||||||
|
|
||||||
INT32 Height //Video height in pixels
|
|
||||||
|
|
||||||
INT32 FrameRateN //Frame rate numerator/denominator in frames per second, for example 60/1 is 60 frames per second.
|
|
||||||
|
|
||||||
INT32 FrameRateD
|
|
||||||
|
|
||||||
FLOAT32 AspectRatio //Display aspect ratio expressed as a ratio of width/height. For example 1.777777777777778 for 16/9
|
|
||||||
|
|
||||||
INT32 Flags //Interlaced=1, Alpha=2, PreMultiplied=4, Preview=8, HighBitDepth=16
|
|
||||||
|
|
||||||
INT32 ColorSpace //Color space flag. 601 for BT601, 709 for BT709, 0 for undefined (typically BT601 for SD, BT709 for HD)
|
|
||||||
|
|
||||||
### AUDIO EXTENDED HEADER (24 bytes) (Mandatory for audio frame type)
|
|
||||||
|
|
||||||
INT32 Codec //Audio codec FourCC, currently only 'FPA1' is supported which is 32bit floating point planar audio
|
|
||||||
|
|
||||||
INT32 SampleRate //Audio sample rate
|
|
||||||
|
|
||||||
INT32 SamplesPerChannel //Number of samples per channel stored in this frame
|
|
||||||
|
|
||||||
INT32 Channels //Number of channels of audio
|
|
||||||
|
|
||||||
UINT32 ActiveChannels //Bit field denoting the number of actual channels stored in this frames data out of the total Channels specified. This is so silent channels can be skipped, saving bandwidth.
|
|
||||||
|
|
||||||
INT32 Reserved1 //Reserved for future use
|
|
||||||
|
|
||||||
### DATA
|
|
||||||
|
|
||||||
The frame data followed by the per-frame metadata
|
|
||||||
|
|
||||||
### Latency considerations
|
|
||||||
|
|
||||||
To optimize latency and prevent network stalls, implementers should ensure the receiver never blocks when accepting data.
|
|
||||||
To achieve this one approach involves keeping a small frame queue with at least one frame reserved for the asynchronous network callback.
|
|
||||||
That reserved frame can the be reused repeatedly if the separate decode thread is taking too long to process frames.
|
|
||||||
|
|
||||||
## Metadata
|
|
||||||
|
|
||||||
Metadata is stored as UTF-8 encoded, null terminated XML data.
|
|
||||||
|
|
||||||
DataLength should always include the null character.
|
|
||||||
|
|
||||||
Special metadata commands are used to control various aspects of the connection.
|
|
||||||
These are fixed strings that must be specified exactly.
|
|
||||||
This is an optimization so that the end point can employ simple string matching rather than full XML parsing.
|
|
||||||
|
|
||||||
### Subscribe Commands
|
|
||||||
|
|
||||||
A sender should not send any data until a subscribe command is received.
|
|
||||||
|
|
||||||
\<OMTSubscribe Video="true" /\>
|
|
||||||
|
|
||||||
Sent by a receiver to request the sender start sending video frames.
|
|
||||||
|
|
||||||
\<OMTSubscribe Audio="true" /\>
|
|
||||||
|
|
||||||
Sent by a receiver to request the sender start sending audio frames.
|
|
||||||
|
|
||||||
\<OMTSubscribe Metadata="true" /\>
|
|
||||||
|
|
||||||
### Preview Commands
|
|
||||||
|
|
||||||
\<OMTSettings Preview="true" /\>
|
|
||||||
\<OMTSettings Preview="false" /\>
|
|
||||||
|
|
||||||
Enable/disable sending preview video data instead of the full resolution frame.
|
|
||||||
|
|
||||||
### Tally Commands
|
|
||||||
|
|
||||||
\<OMTTally Preview="true" Program="false" /\>
|
|
||||||
\<OMTTally Preview="false" Program="true" /\>
|
|
||||||
\<OMTTally Preview="true" Program="true" /\>
|
|
||||||
\<OMTTally Preview="false" Program="false" /\>
|
|
||||||
|
|
||||||
Sent by a receiver to indicate tally status.
|
|
||||||
|
|
||||||
The sender should then combine this tally status with those set by other receivers and then broadcast this new combined tally to all receivers.
|
|
||||||
|
|
||||||
This tally should also be sent to any new connections as well.
|
|
||||||
|
|
||||||
### Suggested Quality
|
|
||||||
|
|
||||||
\<OMTSettings Quality="Default" /\>
|
|
||||||
|
|
||||||
Sent by receivers to indicate the preferred compression quality:
|
|
||||||
|
|
||||||
Default,
|
|
||||||
Low,
|
|
||||||
Medium,
|
|
||||||
High
|
|
||||||
|
|
||||||
Senders may respond to this by gathering the preferred quality of all receivers, determining the highest quality requested and then adjusting the encoder to match.
|
|
||||||
|
|
||||||
Default is Medium quality.
|
|
||||||
|
|
||||||
### Sender Information
|
|
||||||
|
|
||||||
\<OMTInfo ProductName="MyProduct" Manufacturer="MyCompany" Version="1.0" /\>
|
|
||||||
|
|
||||||
Senders can optionally send information about the encoder to receivers when connected.
|
|
||||||
|
|
||||||
## DNS-SD
|
|
||||||
|
|
||||||
Open Media Transport uses the service type _omt._tcp
|
|
||||||
The port should be the tcp port that sender is listening on
|
|
||||||
The full service name should take the form HOSTNAME (Source Name)._omt._tcp.local
|
|
||||||
|
|
||||||
## Discovery Server
|
|
||||||
|
|
||||||
Discovery Server uses the same communication protocol and frame headers to send and receive XML data.
|
|
||||||
|
|
||||||
The server does the following:
|
|
||||||
|
|
||||||
1. Keep track of register/deregister XML requests from each client
|
|
||||||
2. Determine the IP address to use for each registered source based on the client's connection ip.
|
|
||||||
3. Repeat registered requests to all connected clients, including the client that submitted the request.
|
|
||||||
4. Repeat all current registration requests to new clients.
|
|
||||||
5. Remove all requests from a client that has disconnected, and repeat that removed request to all remaining clients.
|
|
||||||
|
|
||||||
### Register XML
|
|
||||||
|
|
||||||
\<OMTAddress>
|
|
||||||
\<Name>MYMACHINENAME (My Source Name)\</Name>
|
|
||||||
\<Port>1234\</Port>
|
|
||||||
\<Addresses>
|
|
||||||
\<Address>0.0.0.0\</Address>
|
|
||||||
\</Addresses>
|
|
||||||
\</OMTAddress>
|
|
||||||
|
|
||||||
### DeRegister XML
|
|
||||||
|
|
||||||
\<OMTAddress>
|
|
||||||
\<Name>MYMACHINENAME (My Source Name)\</Name>
|
|
||||||
\<Port>1234\</Port>
|
|
||||||
\<Removed>True\</Removed>
|
|
||||||
\</OMTAddress>
|
|
||||||
|
|
||||||
### IP Addresses
|
|
||||||
|
|
||||||
IP Addresses should be determined by the server to ensure only the address accessible to the server is used.
|
|
||||||
Therefore when registering a source, the client provided Addresses portion should be ignored.
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
# Open Media Transport (OMT) Libary for .NET
|
|
||||||
|
|
||||||
libomtnet is a .NET library that implements the Open Media Transport protocol for low latency, high performance Local Area Network
|
|
||||||
video/audio transmission.
|
|
||||||
|
|
||||||
It is built using a basic subset of .NET and as a result supports both .NET Framework 4+ and .NET Standard 2.0+ applications, covering all .NET versions from 4 onwards.
|
|
||||||
|
|
||||||
libomt is a native compiled version of the .NET library and is available separately.
|
|
||||||
|
|
||||||
## Getting Started
|
|
||||||
|
|
||||||
### Installation
|
|
||||||
|
|
||||||
Official binary releases for Windows and MacOS can be found in the Releases section of this repository.
|
|
||||||
|
|
||||||
There are only two dependencies when using this library in a .NET app:
|
|
||||||
|
|
||||||
**libomtnet.dll**
|
|
||||||
This is a cross platform file that will work on Windows, Mac and Linux
|
|
||||||
|
|
||||||
**libvmx.dll (Windows)**
|
|
||||||
**libvmx.dylib (MacOS)**
|
|
||||||
**libvmx.so (Linux)**
|
|
||||||
|
|
||||||
These are platform specific native shared libraries. The correct library for the CPU type and OS platform needs to be placed in the same directory as the application.
|
|
||||||
|
|
||||||
### Creating a Source
|
|
||||||
|
|
||||||
1. Create an instance of the OMTSend class specifying a name
|
|
||||||
2. Fill the struct OMTMediaFrame with the video data in either of the available YUV or RGBx formats
|
|
||||||
3. Send using OMTSend.Send
|
|
||||||
4. That's it, the source is now available on the network for receivers to connect to
|
|
||||||
|
|
||||||
### Creating a Receiver
|
|
||||||
|
|
||||||
1. Create an instance of the OMTReceive class specifying the full name of the source (including machine name)
|
|
||||||
The full name of all sources on the network can be found by using the OMTDiscovery class
|
|
||||||
2. In a loop, poll OMTReceive.Receive specifying the types of frames to receive and also a timeout
|
|
||||||
3. Process said frames as required
|
|
||||||
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
dotnet build ../libomtnet.sln -c Release
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
dotnet build ../libomtnet.sln -c Release
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFrameworks>netstandard2.0;net40</TargetFrameworks>
|
|
||||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
|
||||||
<AssemblyVersion>1.0.0.13</AssemblyVersion>
|
|
||||||
<FileVersion>1.0.0.13</FileVersion>
|
|
||||||
<Version>1.0.0.13</Version>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
|
||||||
# Visual Studio Version 17
|
|
||||||
VisualStudioVersion = 17.11.35431.28
|
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "libomtnet", "libomtnet.csproj", "{4103721D-84E5-4037-B682-4FCF14AFEEDA}"
|
|
||||||
EndProject
|
|
||||||
Global
|
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
|
||||||
Debug|Any CPU = Debug|Any CPU
|
|
||||||
Release|Any CPU = Release|Any CPU
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
|
||||||
{4103721D-84E5-4037-B682-4FCF14AFEEDA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{4103721D-84E5-4037-B682-4FCF14AFEEDA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{4103721D-84E5-4037-B682-4FCF14AFEEDA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{4103721D-84E5-4037-B682-4FCF14AFEEDA}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
|
||||||
HideSolutionNode = FALSE
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
|
||||||
SolutionGuid = {3BF6FC38-ECEA-48A0-8F36-0921E8C0D0CA}
|
|
||||||
EndGlobalSection
|
|
||||||
EndGlobal
|
|
||||||
@@ -1,338 +0,0 @@
|
|||||||
/*
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Open Media Transport Contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
using System.Net;
|
|
||||||
using System.Xml;
|
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace libomtnet
|
|
||||||
{
|
|
||||||
public class OMTAddress
|
|
||||||
{
|
|
||||||
private string name;
|
|
||||||
private readonly string machineName;
|
|
||||||
private int port;
|
|
||||||
private IPAddress[] addresses = { };
|
|
||||||
private const int MAX_FULLNAME_LENGTH = 63;
|
|
||||||
internal bool removed = false;
|
|
||||||
|
|
||||||
public OMTAddress(string name, int port)
|
|
||||||
{
|
|
||||||
this.name = SanitizeName(name);
|
|
||||||
this.port = port;
|
|
||||||
this.machineName = SanitizeName(OMTPlatform.GetInstance().GetMachineName());
|
|
||||||
this.addresses = new IPAddress[] { };
|
|
||||||
LimitNameLength();
|
|
||||||
}
|
|
||||||
public OMTAddress(string machineName, string name, int port)
|
|
||||||
{
|
|
||||||
this.name = SanitizeName(name);
|
|
||||||
this.port = port;
|
|
||||||
this.machineName = SanitizeName(machineName);
|
|
||||||
this.addresses = new IPAddress[] { };
|
|
||||||
LimitNameLength();
|
|
||||||
}
|
|
||||||
|
|
||||||
public string ToURL()
|
|
||||||
{
|
|
||||||
return OMTConstants.URL_PREFIX + this.machineName + ":" + port;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LimitNameLength()
|
|
||||||
{
|
|
||||||
int oversize = ToString().Length - MAX_FULLNAME_LENGTH;
|
|
||||||
if (oversize > 0)
|
|
||||||
{
|
|
||||||
if (oversize < this.name.Length)
|
|
||||||
{
|
|
||||||
this.name = this.name.Substring(0, this.name.Length - oversize).Trim();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ClearAddresses()
|
|
||||||
{
|
|
||||||
addresses = new IPAddress[]{ };
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool AddAddress(IPAddress address)
|
|
||||||
{
|
|
||||||
if (address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
|
|
||||||
{
|
|
||||||
byte[] b = address.GetAddressBytes();
|
|
||||||
byte[] b128 = new byte[16];
|
|
||||||
|
|
||||||
b128[10] = 0xFF;
|
|
||||||
b128[11] = 0xFF;
|
|
||||||
|
|
||||||
b128[12] = b[0];
|
|
||||||
b128[13] = b[1];
|
|
||||||
b128[14] = b[2];
|
|
||||||
b128[15] = b[3];
|
|
||||||
|
|
||||||
address = new IPAddress(b128);
|
|
||||||
} else if (address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6)
|
|
||||||
{
|
|
||||||
if (address.IsIPv6LinkLocal) return false;
|
|
||||||
}
|
|
||||||
if (!HasAddress(address))
|
|
||||||
{
|
|
||||||
List<IPAddress> list = new List<IPAddress>();
|
|
||||||
bool v4 = OMTUtils.IsIPv4(address);
|
|
||||||
foreach (IPAddress a in this.addresses)
|
|
||||||
{
|
|
||||||
if (OMTUtils.IsIPv4(a))
|
|
||||||
{
|
|
||||||
list.Add(a);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (v4) list.Add(address);
|
|
||||||
foreach (IPAddress a in this.addresses)
|
|
||||||
{
|
|
||||||
if (!OMTUtils.IsIPv4(a))
|
|
||||||
{
|
|
||||||
list.Add(a);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!v4) list.Add(address);
|
|
||||||
addresses = list.ToArray();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
internal bool HasAddress(IPAddress address)
|
|
||||||
{
|
|
||||||
foreach (IPAddress a in addresses)
|
|
||||||
{
|
|
||||||
if (a.Equals(address))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string EscapeFullName(string fullName)
|
|
||||||
{
|
|
||||||
return fullName.Replace("\\", "\\\\").Replace(".", "\\.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string SanitizeName(string name)
|
|
||||||
{
|
|
||||||
return name; // return name.Replace(".", " ");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string UnescapeFullName(string fullName)
|
|
||||||
{
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
bool beginEscape = false;
|
|
||||||
string num = "";
|
|
||||||
foreach (char c in fullName.ToCharArray())
|
|
||||||
{
|
|
||||||
if (beginEscape)
|
|
||||||
{
|
|
||||||
if (Char.IsDigit(c))
|
|
||||||
{
|
|
||||||
num = num + c.ToString();
|
|
||||||
if (num.Length == 3)
|
|
||||||
{
|
|
||||||
int n = 0;
|
|
||||||
if (int.TryParse(num, out n))
|
|
||||||
{
|
|
||||||
sb.Append(Convert.ToChar(n));
|
|
||||||
}
|
|
||||||
beginEscape = false;
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
sb.Append(c);
|
|
||||||
beginEscape = false;
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
if (c == '\\')
|
|
||||||
{
|
|
||||||
beginEscape = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sb.Append(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return sb.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public string MachineName { get { return machineName; } }
|
|
||||||
public string Name { get { return name; } }
|
|
||||||
public IPAddress[] Addresses { get { return addresses; } }
|
|
||||||
public int Port { get { return port; } set { port = value; } }
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return ToString(machineName, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string ToString(string machineName, string name)
|
|
||||||
{
|
|
||||||
return machineName + " (" + name + ")";
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool IsValid(string fullName)
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrEmpty(fullName))
|
|
||||||
{
|
|
||||||
if (fullName.Contains("("))
|
|
||||||
{
|
|
||||||
if (fullName.Contains(")"))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
public static OMTAddress Create(string fullName, int port)
|
|
||||||
{
|
|
||||||
if (!IsValid(fullName)) return null;
|
|
||||||
int index = fullName.IndexOf('(');
|
|
||||||
string machineName = fullName.Substring(0, index).Trim();
|
|
||||||
if (index > 0)
|
|
||||||
{
|
|
||||||
string name = fullName.Substring(index + 1);
|
|
||||||
name = name.Substring(0, name.Length - 1);
|
|
||||||
return new OMTAddress(machineName, name, port);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetMachineName(string fullName)
|
|
||||||
{
|
|
||||||
string[] s = fullName.Split('(');
|
|
||||||
return s[0].Trim();
|
|
||||||
}
|
|
||||||
public static string GetName(string fullName)
|
|
||||||
{
|
|
||||||
int index = fullName.IndexOf('(');
|
|
||||||
if (index > 0)
|
|
||||||
{
|
|
||||||
string name = fullName.Substring(index + 1);
|
|
||||||
name = name.Substring(0, name.Length - 1);
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
public string ToXML()
|
|
||||||
{
|
|
||||||
using (StringWriter sw = new StringWriter())
|
|
||||||
{
|
|
||||||
using (XmlTextWriter t = new XmlTextWriter(sw))
|
|
||||||
{
|
|
||||||
t.Formatting = Formatting.Indented;
|
|
||||||
t.WriteStartElement(OMTMetadataTemplates.ADDRESS_NAME);
|
|
||||||
t.WriteElementString("Name", ToString());
|
|
||||||
t.WriteElementString("Port", port.ToString());
|
|
||||||
if (removed)
|
|
||||||
{
|
|
||||||
t.WriteElementString("Removed", "True");
|
|
||||||
}
|
|
||||||
t.WriteStartElement("Addresses");
|
|
||||||
foreach (IPAddress ip in addresses)
|
|
||||||
{
|
|
||||||
t.WriteElementString("IPAddress", ip.ToString());
|
|
||||||
}
|
|
||||||
t.WriteEndElement();
|
|
||||||
t.WriteEndElement();
|
|
||||||
return sw.ToString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static OMTAddress FromXML(string xml)
|
|
||||||
{
|
|
||||||
XmlDocument doc = OMTMetadataUtils.TryParse(xml);
|
|
||||||
if (doc != null)
|
|
||||||
{
|
|
||||||
XmlNode e = doc.DocumentElement;
|
|
||||||
if (e != null)
|
|
||||||
{
|
|
||||||
if (e.Name == OMTMetadataTemplates.ADDRESS_NAME)
|
|
||||||
{
|
|
||||||
XmlNode nm = e.SelectSingleNode("Name");
|
|
||||||
if (nm != null)
|
|
||||||
{
|
|
||||||
XmlNode prt = e.SelectSingleNode("Port");
|
|
||||||
if (prt != null)
|
|
||||||
{
|
|
||||||
int port = int.Parse(prt.InnerText);
|
|
||||||
OMTAddress a = OMTAddress.Create(nm.InnerText, port);
|
|
||||||
if (a != null)
|
|
||||||
{
|
|
||||||
foreach (XmlNode ipn in e.SelectNodes("Addresses/IPAddress"))
|
|
||||||
{
|
|
||||||
IPAddress ip = null;
|
|
||||||
if (IPAddress.TryParse(ipn.InnerText, out ip))
|
|
||||||
{
|
|
||||||
a.AddAddress(ip);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
XmlNode del = e.SelectSingleNode("Removed");
|
|
||||||
if (del != null)
|
|
||||||
{
|
|
||||||
if (del.InnerText.ToLower() == "true")
|
|
||||||
{
|
|
||||||
a.removed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
internal class OMTAddressSorter : IComparer<OMTAddress>
|
|
||||||
{
|
|
||||||
public int Compare(OMTAddress x, OMTAddress y)
|
|
||||||
{
|
|
||||||
if (x != null && y != null)
|
|
||||||
{
|
|
||||||
return String.Compare(x.ToString(), y.ToString());
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
/*
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Open Media Transport Contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Drawing;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace libomtnet
|
|
||||||
{
|
|
||||||
public class OMTBase : IDisposable
|
|
||||||
{
|
|
||||||
private bool disposedValue;
|
|
||||||
private bool exiting;
|
|
||||||
protected virtual void DisposeInternal()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
protected bool Exiting { get { return exiting; } }
|
|
||||||
protected void SetExiting()
|
|
||||||
{
|
|
||||||
exiting = true;
|
|
||||||
}
|
|
||||||
protected virtual void Dispose(bool disposing)
|
|
||||||
{
|
|
||||||
if (!disposedValue)
|
|
||||||
{
|
|
||||||
exiting = true;
|
|
||||||
if (disposing)
|
|
||||||
{
|
|
||||||
DisposeInternal();
|
|
||||||
}
|
|
||||||
disposedValue = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Dispose(disposing: true);
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,140 +0,0 @@
|
|||||||
/*
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Open Media Transport Contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace libomtnet
|
|
||||||
{
|
|
||||||
internal class OMTBinary
|
|
||||||
{
|
|
||||||
private byte[] buffer;
|
|
||||||
private int offset;
|
|
||||||
public void SetBuffer(byte[] buffer, int offset)
|
|
||||||
{
|
|
||||||
this.buffer = buffer;
|
|
||||||
this.offset = offset;
|
|
||||||
}
|
|
||||||
public byte ReadByte()
|
|
||||||
{
|
|
||||||
byte value = buffer[offset];
|
|
||||||
offset += 1;
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
public Int32 ReadInt32()
|
|
||||||
{
|
|
||||||
Int32 result = (int)this.buffer[offset] | ((int)this.buffer[offset + 1] << 8) | ((int)this.buffer[offset + 2] << 16) | ((int)this.buffer[offset + 3] << 24);
|
|
||||||
offset += 4;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public UInt16 ReadUInt16()
|
|
||||||
{
|
|
||||||
UInt16 result = (ushort)((int)this.buffer[offset] | ((int)this.buffer[offset + 1] << 8));
|
|
||||||
offset += 2;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Int64 ReadInt64()
|
|
||||||
{
|
|
||||||
uint num = (uint)((int)this.buffer[offset] | ((int)this.buffer[offset+1] << 8) | ((int)this.buffer[offset + 2] << 16) | ((int)this.buffer[offset + 3] << 24));
|
|
||||||
uint num2 = (uint)((int)this.buffer[offset + 4] | ((int)this.buffer[offset + 5] << 8) | ((int)this.buffer[offset + 6] << 16) | ((int)this.buffer[offset + 7] << 24));
|
|
||||||
Int64 val = (long)(((ulong)num2 << 32) | (ulong)num);
|
|
||||||
offset += 8;
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|
||||||
public UInt32 ReadUInt32()
|
|
||||||
{
|
|
||||||
UInt32 val = (uint)((int)this.buffer[offset + 0] | ((int)this.buffer[offset + 1] << 8) | ((int)this.buffer[offset + 2] << 16) | ((int)this.buffer[offset + 3] << 24));
|
|
||||||
offset += 4;
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|
||||||
public unsafe Single ReadSingle()
|
|
||||||
{
|
|
||||||
uint num = (uint)((int)this.buffer[offset + 0] | ((int)this.buffer[offset + 1] << 8) | ((int)this.buffer[offset + 2] << 16) | ((int)this.buffer[offset + 3] << 24));
|
|
||||||
offset += 4;
|
|
||||||
return *(float*)(&num);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Write(byte value)
|
|
||||||
{
|
|
||||||
this.buffer[offset] = value;
|
|
||||||
offset++;
|
|
||||||
}
|
|
||||||
public void Write(short value)
|
|
||||||
{
|
|
||||||
this.buffer[offset] = (byte)value;
|
|
||||||
this.buffer[offset + 1] = (byte)(value >> 8);
|
|
||||||
offset += 2;
|
|
||||||
}
|
|
||||||
public void Write(ushort value)
|
|
||||||
{
|
|
||||||
this.buffer[offset] = (byte)value;
|
|
||||||
this.buffer[offset + 1] = (byte)(value >> 8);
|
|
||||||
offset += 2;
|
|
||||||
}
|
|
||||||
public void Write(int value)
|
|
||||||
{
|
|
||||||
this.buffer[offset+ 0] = (byte)value;
|
|
||||||
this.buffer[offset + 1] = (byte)(value >> 8);
|
|
||||||
this.buffer[offset + 2] = (byte)(value >> 16);
|
|
||||||
this.buffer[offset + 3] = (byte)(value >> 24);
|
|
||||||
offset += 4;
|
|
||||||
}
|
|
||||||
public void Write(uint value)
|
|
||||||
{
|
|
||||||
this.buffer[offset + 0] = (byte)value;
|
|
||||||
this.buffer[offset + 1] = (byte)(value >> 8);
|
|
||||||
this.buffer[offset + 2] = (byte)(value >> 16);
|
|
||||||
this.buffer[offset + 3] = (byte)(value >> 24);
|
|
||||||
offset += 4;
|
|
||||||
}
|
|
||||||
public void Write(long value)
|
|
||||||
{
|
|
||||||
this.buffer[offset + 0] = (byte)value;
|
|
||||||
this.buffer[offset + 1] = (byte)(value >> 8);
|
|
||||||
this.buffer[offset + 2] = (byte)(value >> 16);
|
|
||||||
this.buffer[offset + 3] = (byte)(value >> 24);
|
|
||||||
this.buffer[offset + 4] = (byte)(value >> 32);
|
|
||||||
this.buffer[offset + 5] = (byte)(value >> 40);
|
|
||||||
this.buffer[offset + 6] = (byte)(value >> 48);
|
|
||||||
this.buffer[offset + 7] = (byte)(value >> 56);
|
|
||||||
offset += 8;
|
|
||||||
}
|
|
||||||
public unsafe void Write(float value)
|
|
||||||
{
|
|
||||||
uint num = *(uint*)(&value);
|
|
||||||
this.buffer[offset + 0] = (byte)num;
|
|
||||||
this.buffer[offset + 1] = (byte)(num >> 8);
|
|
||||||
this.buffer[offset + 2] = (byte)(num >> 16);
|
|
||||||
this.buffer[offset + 3] = (byte)(num >> 24);
|
|
||||||
offset += 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,145 +0,0 @@
|
|||||||
/*
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Open Media Transport Contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace libomtnet
|
|
||||||
{
|
|
||||||
internal class OMTBuffer : OMTBase
|
|
||||||
{
|
|
||||||
private byte[] buffer;
|
|
||||||
private int offset;
|
|
||||||
private int length;
|
|
||||||
private int maximumLength;
|
|
||||||
private bool resizable = false;
|
|
||||||
public OMTBuffer(byte[] buffer, int offset, int maximumLength)
|
|
||||||
{
|
|
||||||
this.buffer = buffer;
|
|
||||||
this.offset = offset;
|
|
||||||
this.maximumLength = maximumLength;
|
|
||||||
this.length = maximumLength;
|
|
||||||
}
|
|
||||||
public OMTBuffer(int maximumLength, bool resizable)
|
|
||||||
{
|
|
||||||
this.buffer = new byte[maximumLength];
|
|
||||||
this.offset = 0;
|
|
||||||
this.length = maximumLength;
|
|
||||||
this.maximumLength = maximumLength;
|
|
||||||
this.resizable = resizable;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Resize(int newMaximumLength)
|
|
||||||
{
|
|
||||||
if (resizable)
|
|
||||||
{
|
|
||||||
if (newMaximumLength > this.maximumLength)
|
|
||||||
{
|
|
||||||
Debug.WriteLine("Resizing: " + this.maximumLength + " to " + newMaximumLength);
|
|
||||||
this.maximumLength = newMaximumLength;
|
|
||||||
this.buffer = new byte[maximumLength];
|
|
||||||
this.length = 0;
|
|
||||||
this.offset = 0;
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
throw new Exception("This buffer does not support resizing.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Append(byte[] buffer, int offset, int count)
|
|
||||||
{
|
|
||||||
System.Buffer.BlockCopy(buffer, offset, this.buffer, this.offset, count);
|
|
||||||
this.offset += count;
|
|
||||||
this.length += count;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Append(IntPtr buffer, int offset, int count)
|
|
||||||
{
|
|
||||||
Marshal.Copy(buffer + offset, this.buffer, this.offset, count);
|
|
||||||
this.offset += count;
|
|
||||||
this.length += count;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Set the buffer where length is the entire length of valid data, not just from offset
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="offset"></param>
|
|
||||||
/// <param name="length"></param>
|
|
||||||
public void SetBuffer(int offset, int length)
|
|
||||||
{
|
|
||||||
this.offset = offset;
|
|
||||||
this.length = length;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetBuffer(byte[] buffer, int offset, int length)
|
|
||||||
{
|
|
||||||
this.buffer = buffer;
|
|
||||||
SetBuffer(offset, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] Buffer { get { return buffer; } }
|
|
||||||
public int Offset { get { return offset; } }
|
|
||||||
public int Length { get { return length; } }
|
|
||||||
public int MaximumLength { get { return maximumLength; } }
|
|
||||||
public static OMTBuffer FromMetadata(string xml)
|
|
||||||
{
|
|
||||||
byte[] b = UTF8Encoding.UTF8.GetBytes(xml);
|
|
||||||
return new OMTBuffer(b, 0, b.Length);
|
|
||||||
}
|
|
||||||
|
|
||||||
public string ToMetadata()
|
|
||||||
{
|
|
||||||
return UTF8Encoding.UTF8.GetString(this.buffer, this.offset, this.length);
|
|
||||||
}
|
|
||||||
protected override void DisposeInternal()
|
|
||||||
{
|
|
||||||
buffer = null;
|
|
||||||
base.DisposeInternal();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal class OMTPinnedBuffer : OMTBuffer
|
|
||||||
{
|
|
||||||
private GCHandle handle;
|
|
||||||
public OMTPinnedBuffer(int length) : base(length,false)
|
|
||||||
{
|
|
||||||
handle = GCHandle.Alloc(this.Buffer, GCHandleType.Pinned);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IntPtr Pointer { get { return handle.AddrOfPinnedObject(); } }
|
|
||||||
|
|
||||||
protected override void DisposeInternal()
|
|
||||||
{
|
|
||||||
if (handle.IsAllocated)
|
|
||||||
{
|
|
||||||
handle.Free();
|
|
||||||
}
|
|
||||||
base.DisposeInternal();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,594 +0,0 @@
|
|||||||
/*
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Open Media Transport Contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
using libomtnet.codecs;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Net;
|
|
||||||
using System.Net.Sockets;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Xml;
|
|
||||||
|
|
||||||
namespace libomtnet
|
|
||||||
{
|
|
||||||
internal class OMTChannel : OMTBase
|
|
||||||
{
|
|
||||||
private Socket socket;
|
|
||||||
private SocketAsyncEventArgs receiveargs;
|
|
||||||
private OMTSocketAsyncPool sendpool;
|
|
||||||
private OMTSocketAsyncPool metapool;
|
|
||||||
private OMTBuffer tempReceiveBuffer;
|
|
||||||
private OMTFramePool framePool;
|
|
||||||
private OMTFrame pendingFrame;
|
|
||||||
private readonly Queue<OMTFrame> readyFrames;
|
|
||||||
private AutoResetEvent frameReadyEvent;
|
|
||||||
private OMTFrameType subscriptions = OMTFrameType.None;
|
|
||||||
private readonly Queue<OMTMetadata> metadatas;
|
|
||||||
private AutoResetEvent metadataReadyEvent;
|
|
||||||
private OMTTally tally;
|
|
||||||
private bool preview;
|
|
||||||
private object lockSync = new object();
|
|
||||||
private object sendSync = new object();
|
|
||||||
private OMTQuality suggestedQuality = OMTQuality.Default;
|
|
||||||
private OMTSenderInfo senderInfo = null;
|
|
||||||
private IPEndPoint endPoint = null;
|
|
||||||
private OMTStatistics statistics = new OMTStatistics();
|
|
||||||
|
|
||||||
public delegate void ChangedEventHandler(object sender, OMTEventArgs e);
|
|
||||||
public event ChangedEventHandler Changed;
|
|
||||||
private OMTEventArgs tempEvent = new OMTEventArgs(OMTEventType.None);
|
|
||||||
|
|
||||||
private string redirectAddress = null;
|
|
||||||
|
|
||||||
public string RedirectAddress { get { return redirectAddress; } }
|
|
||||||
public IPEndPoint RemoteEndPoint { get { return endPoint; } }
|
|
||||||
|
|
||||||
public OMTChannel(Socket sck, OMTFrameType receiveFrameType, AutoResetEvent frameReady, AutoResetEvent metadataReady, bool metadataServer)
|
|
||||||
{
|
|
||||||
socket = sck;
|
|
||||||
endPoint = (IPEndPoint)sck.RemoteEndPoint;
|
|
||||||
|
|
||||||
socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);
|
|
||||||
socket.SendBufferSize = OMTConstants.NETWORK_SEND_BUFFER;
|
|
||||||
if (receiveFrameType == OMTFrameType.Metadata)
|
|
||||||
{
|
|
||||||
socket.ReceiveBufferSize = OMTConstants.NETWORK_SEND_RECEIVE_BUFFER;
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
socket.ReceiveBufferSize = OMTConstants.NETWORK_RECEIVE_BUFFER;
|
|
||||||
}
|
|
||||||
tempReceiveBuffer = new OMTBuffer(OMTConstants.VIDEO_MIN_SIZE, true);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
|
|
||||||
socket.SetSocketOption(SocketOptionLevel.Tcp, (SocketOptionName)3, (int)5); //TCP_KEEPIDLE=3 in Win32 and is translated in .NET8+ to platform specific values
|
|
||||||
OMTLogging.Write("KeepAlive.Enabled", "OMTChannel");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "OMTChannel.KeepAlive");
|
|
||||||
}
|
|
||||||
|
|
||||||
receiveargs = new SocketAsyncEventArgs();
|
|
||||||
receiveargs.Completed += Receive_Completed;
|
|
||||||
|
|
||||||
int poolCount = 1;
|
|
||||||
int startingFrameSize = 0;
|
|
||||||
if (receiveFrameType == OMTFrameType.Video)
|
|
||||||
{
|
|
||||||
poolCount = OMTConstants.VIDEO_FRAME_POOL_COUNT;
|
|
||||||
startingFrameSize = OMTConstants.VIDEO_MIN_SIZE;
|
|
||||||
receiveargs.SetBuffer(new byte[OMTConstants.VIDEO_MAX_SIZE], 0, OMTConstants.VIDEO_MAX_SIZE);
|
|
||||||
} else if (receiveFrameType == OMTFrameType.Audio) {
|
|
||||||
poolCount = OMTConstants.AUDIO_FRAME_POOL_COUNT;
|
|
||||||
startingFrameSize = OMTConstants.AUDIO_MIN_SIZE;
|
|
||||||
receiveargs.SetBuffer(new byte[OMTConstants.AUDIO_MAX_SIZE], 0, OMTConstants.AUDIO_MAX_SIZE);
|
|
||||||
} else {
|
|
||||||
poolCount = 1;
|
|
||||||
startingFrameSize = OMTConstants.AUDIO_MIN_SIZE;
|
|
||||||
receiveargs.SetBuffer(new byte[OMTConstants.AUDIO_MAX_SIZE], 0, OMTConstants.AUDIO_MAX_SIZE);
|
|
||||||
}
|
|
||||||
int sendPoolCount = OMTConstants.NETWORK_ASYNC_COUNT;
|
|
||||||
int startingSendPoolSize = OMTConstants.AUDIO_MIN_SIZE;
|
|
||||||
if (metadataServer)
|
|
||||||
{
|
|
||||||
sendPoolCount = OMTConstants.NETWORK_ASYNC_COUNT_META_ONLY;
|
|
||||||
startingSendPoolSize = OMTConstants.NETWORK_ASYNC_BUFFER_META_ONLY;
|
|
||||||
}
|
|
||||||
sendpool = new OMTSocketAsyncPool(sendPoolCount, startingSendPoolSize);
|
|
||||||
metapool = new OMTSocketAsyncPool(OMTConstants.NETWORK_ASYNC_COUNT_META_ONLY, OMTConstants.NETWORK_ASYNC_BUFFER_META_ONLY);
|
|
||||||
framePool = new OMTFramePool(poolCount, startingFrameSize, true);
|
|
||||||
|
|
||||||
readyFrames = new Queue<OMTFrame>();
|
|
||||||
frameReadyEvent = frameReady;
|
|
||||||
metadatas = new Queue<OMTMetadata>();
|
|
||||||
metadataReadyEvent = metadataReady;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void OnEvent(OMTEventType type)
|
|
||||||
{
|
|
||||||
tempEvent.Type = type;
|
|
||||||
Changed?.Invoke(this, tempEvent);
|
|
||||||
}
|
|
||||||
|
|
||||||
public OMTQuality SuggestedQuality { get { return suggestedQuality; } }
|
|
||||||
public OMTSenderInfo SenderInformation { get { return senderInfo; } }
|
|
||||||
|
|
||||||
public bool Connected { get {
|
|
||||||
if (socket == null) return false;
|
|
||||||
return socket.Connected;
|
|
||||||
} }
|
|
||||||
|
|
||||||
private void CloseSocket()
|
|
||||||
{
|
|
||||||
lock (lockSync)
|
|
||||||
{
|
|
||||||
if (socket != null)
|
|
||||||
{
|
|
||||||
socket.Close();
|
|
||||||
socket = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Socket Socket { get { return socket; } }
|
|
||||||
|
|
||||||
public int Send(OMTMetadata metadata)
|
|
||||||
{
|
|
||||||
OMTBuffer m = OMTBuffer.FromMetadata(metadata.XML);
|
|
||||||
OMTFrame frame = new OMTFrame(OMTFrameType.Metadata, m);
|
|
||||||
frame.Timestamp = metadata.Timestamp;
|
|
||||||
return Send(frame);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsVideo()
|
|
||||||
{
|
|
||||||
if (subscriptions.HasFlag(OMTFrameType.Video)) return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsMetadata()
|
|
||||||
{
|
|
||||||
if (subscriptions.HasFlag(OMTFrameType.Metadata)) return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsAudio()
|
|
||||||
{
|
|
||||||
if (subscriptions.HasFlag(OMTFrameType.Audio)) return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Send(OMTFrame frame)
|
|
||||||
{
|
|
||||||
lock (sendSync)
|
|
||||||
{
|
|
||||||
if (Exiting) return 0;
|
|
||||||
int written = 0;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if ((frame.FrameType != OMTFrameType.Metadata) && (subscriptions & frame.FrameType) != frame.FrameType)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
frame.SetPreviewMode(preview);
|
|
||||||
int length = frame.Length;
|
|
||||||
if (length > OMTConstants.VIDEO_MAX_SIZE)
|
|
||||||
{
|
|
||||||
statistics.FramesDropped += 1;
|
|
||||||
Debug.WriteLine("OMTChannel.Send.DroppedOversizedFrame");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
OMTSocketAsyncPool pool = sendpool;
|
|
||||||
if (frame.FrameType == OMTFrameType.Metadata)
|
|
||||||
{
|
|
||||||
pool = metapool;
|
|
||||||
}
|
|
||||||
SocketAsyncEventArgs e = pool.GetEventArgs();
|
|
||||||
if (e == null)
|
|
||||||
{
|
|
||||||
statistics.FramesDropped += 1;
|
|
||||||
Debug.WriteLine("OMTChannel.Send.DroppedFrame");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
pool.Resize(e, length);
|
|
||||||
frame.WriteHeaderTo(e.Buffer, 0, e.Count);
|
|
||||||
int headerLength = frame.HeaderLength + frame.ExtendedHeaderLength;
|
|
||||||
frame.WriteDataTo(e.Buffer, 0, headerLength, length - headerLength);
|
|
||||||
e.SetBuffer(0, length);
|
|
||||||
pool.SendAsync(socket, e);
|
|
||||||
written = length;
|
|
||||||
if (frame.FrameType != OMTFrameType.Metadata)
|
|
||||||
{
|
|
||||||
statistics.Frames += 1;
|
|
||||||
statistics.FramesSinceLast += 1;
|
|
||||||
}
|
|
||||||
statistics.BytesSent += written;
|
|
||||||
statistics.BytesSentSinceLast += written;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "OMTChannel.Send");
|
|
||||||
}
|
|
||||||
return written;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int ReadyFrameCount { get
|
|
||||||
{
|
|
||||||
lock (readyFrames)
|
|
||||||
{
|
|
||||||
return readyFrames.Count;
|
|
||||||
}
|
|
||||||
} }
|
|
||||||
|
|
||||||
public int ReadyMetadataCount { get {
|
|
||||||
lock (metadatas)
|
|
||||||
{
|
|
||||||
return metadatas.Count;
|
|
||||||
}
|
|
||||||
} }
|
|
||||||
|
|
||||||
public OMTStatistics GetStatistics() {
|
|
||||||
OMTStatistics s = statistics;
|
|
||||||
statistics.FramesSinceLast = 0;
|
|
||||||
statistics.BytesSentSinceLast = 0;
|
|
||||||
statistics.BytesReceivedSinceLast = 0;
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
public OMTFrame ReceiveFrame()
|
|
||||||
{
|
|
||||||
lock (readyFrames)
|
|
||||||
{
|
|
||||||
if (Exiting) return null;
|
|
||||||
if (readyFrames.Count > 0)
|
|
||||||
{
|
|
||||||
return readyFrames.Dequeue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ReturnFrame(OMTFrame frame)
|
|
||||||
{
|
|
||||||
lock (readyFrames)
|
|
||||||
{
|
|
||||||
if (frame != null)
|
|
||||||
{
|
|
||||||
if (Exiting)
|
|
||||||
{
|
|
||||||
frame.Dispose();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
framePool.Return(frame);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public OMTMetadata ReceiveMetadata()
|
|
||||||
{
|
|
||||||
lock (metadatas)
|
|
||||||
{
|
|
||||||
if (Exiting) return null;
|
|
||||||
if (metadatas.Count > 0)
|
|
||||||
{
|
|
||||||
return metadatas.Dequeue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public OMTTally GetTally()
|
|
||||||
{
|
|
||||||
return tally;
|
|
||||||
}
|
|
||||||
private void UpdateTally(OMTTally t)
|
|
||||||
{
|
|
||||||
if (t.Preview != tally.Preview || t.Program != tally.Program)
|
|
||||||
{
|
|
||||||
tally = t;
|
|
||||||
OnEvent(OMTEventType.TallyChanged);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool ProcessMetadata(OMTFrame frame)
|
|
||||||
{
|
|
||||||
if (frame.FrameType == OMTFrameType.Metadata)
|
|
||||||
{
|
|
||||||
string xml = frame.Data.ToMetadata();
|
|
||||||
if (xml == OMTMetadataConstants.CHANNEL_SUBSCRIBE_VIDEO)
|
|
||||||
{
|
|
||||||
subscriptions |= OMTFrameType.Video;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if (xml == OMTMetadataConstants.CHANNEL_SUBSCRIBE_AUDIO)
|
|
||||||
{
|
|
||||||
subscriptions |= OMTFrameType.Audio;
|
|
||||||
return true;
|
|
||||||
} else if (xml == OMTMetadataConstants.CHANNEL_SUBSCRIBE_METADATA)
|
|
||||||
{
|
|
||||||
subscriptions |= OMTFrameType.Metadata;
|
|
||||||
return true;
|
|
||||||
} else if (xml == OMTMetadataConstants.TALLY_PREVIEWPROGRAM)
|
|
||||||
{
|
|
||||||
UpdateTally(new OMTTally(1, 1));
|
|
||||||
return true;
|
|
||||||
} else if (xml == OMTMetadataConstants.TALLY_PROGRAM)
|
|
||||||
{
|
|
||||||
UpdateTally(new OMTTally(0, 1));
|
|
||||||
return true;
|
|
||||||
} else if (xml == OMTMetadataConstants.TALLY_PREVIEW)
|
|
||||||
{
|
|
||||||
UpdateTally(new OMTTally(1, 0));
|
|
||||||
return true;
|
|
||||||
} else if (xml == OMTMetadataConstants.TALLY_NONE)
|
|
||||||
{
|
|
||||||
UpdateTally(new OMTTally(0, 0));
|
|
||||||
return true;
|
|
||||||
} else if (xml == OMTMetadataConstants.CHANNEL_PREVIEW_VIDEO_ON)
|
|
||||||
{
|
|
||||||
preview = true;
|
|
||||||
return true;
|
|
||||||
} else if (xml == OMTMetadataConstants.CHANNEL_PREVIEW_VIDEO_OFF)
|
|
||||||
{
|
|
||||||
preview = false;
|
|
||||||
return true;
|
|
||||||
} else if (xml.StartsWith(OMTMetadataTemplates.SUGGESTED_QUALITY_PREFIX))
|
|
||||||
{
|
|
||||||
XmlDocument doc = OMTMetadataUtils.TryParse(xml);
|
|
||||||
if (doc != null)
|
|
||||||
{
|
|
||||||
XmlNode n = doc.DocumentElement;
|
|
||||||
if (n != null)
|
|
||||||
{
|
|
||||||
XmlNode a = n.Attributes.GetNamedItem("Quality");
|
|
||||||
if (a != null)
|
|
||||||
{
|
|
||||||
if (a.InnerText!= null)
|
|
||||||
{
|
|
||||||
foreach (OMTQuality e in Enum.GetValues(typeof(OMTQuality)))
|
|
||||||
{
|
|
||||||
if (e.ToString() == a.InnerText)
|
|
||||||
{
|
|
||||||
suggestedQuality = e;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
} else if (xml.StartsWith(OMTMetadataTemplates.SENDER_INFO_PREFIX))
|
|
||||||
{
|
|
||||||
senderInfo = OMTSenderInfo.FromXML(xml);
|
|
||||||
//Don't return here, as this info should passthrough to receiver as well.
|
|
||||||
} else if (xml.StartsWith(OMTMetadataTemplates.REDIRECT_PREFIX))
|
|
||||||
{
|
|
||||||
this.redirectAddress = OMTRedirect.FromXML(xml);
|
|
||||||
OnEvent(OMTEventType.RedirectChanged);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
lock (metadatas)
|
|
||||||
{
|
|
||||||
if (metadatas.Count < OMTConstants.METADATA_MAX_COUNT)
|
|
||||||
{
|
|
||||||
metadatas.Enqueue(new OMTMetadata(frame.Timestamp, xml, endPoint));
|
|
||||||
}
|
|
||||||
if (metadataReadyEvent != null)
|
|
||||||
{
|
|
||||||
metadataReadyEvent.Set();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
private void ProtocolFailure(string reason)
|
|
||||||
{
|
|
||||||
CloseSocket();
|
|
||||||
OMTLogging.Write("ProtocolFailure: " + reason, "OMTReceive");
|
|
||||||
}
|
|
||||||
private void Receive_Completed(object sender, SocketAsyncEventArgs e)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
lock (lockSync)
|
|
||||||
{
|
|
||||||
if (Exiting) return;
|
|
||||||
if (socket == null) return;
|
|
||||||
if (e.SocketError == SocketError.Success && e.BytesTransferred > 0)
|
|
||||||
{
|
|
||||||
statistics.BytesReceived += e.BytesTransferred;
|
|
||||||
statistics.BytesReceivedSinceLast += e.BytesTransferred;
|
|
||||||
int len = e.Offset + e.BytesTransferred;
|
|
||||||
while (len > 0)
|
|
||||||
{
|
|
||||||
if (pendingFrame == null)
|
|
||||||
{
|
|
||||||
pendingFrame = framePool.Get();
|
|
||||||
}
|
|
||||||
if (pendingFrame.ReadHeaderFrom(e.Buffer, 0, len))
|
|
||||||
{
|
|
||||||
if (pendingFrame.FrameType != OMTFrameType.Video & pendingFrame.FrameType != OMTFrameType.Audio & pendingFrame.FrameType != OMTFrameType.Metadata)
|
|
||||||
{
|
|
||||||
ProtocolFailure("Invalid packet or unsupported frame type: " + pendingFrame.FrameType);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (pendingFrame.ReadExtendedHeaderFrom(e.Buffer, 0, len))
|
|
||||||
{
|
|
||||||
if (pendingFrame.ReadDataFrom(e.Buffer, 0, len))
|
|
||||||
{
|
|
||||||
int read = pendingFrame.Length;
|
|
||||||
int remaining = len - read;
|
|
||||||
if (remaining > 0)
|
|
||||||
{
|
|
||||||
tempReceiveBuffer.Resize(remaining);
|
|
||||||
Buffer.BlockCopy(e.Buffer, read, tempReceiveBuffer.Buffer, 0, remaining);
|
|
||||||
Buffer.BlockCopy(tempReceiveBuffer.Buffer, 0, e.Buffer, 0, remaining);
|
|
||||||
len = remaining;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
len = 0;
|
|
||||||
}
|
|
||||||
if (ProcessMetadata(pendingFrame))
|
|
||||||
{
|
|
||||||
framePool.Return(pendingFrame);
|
|
||||||
pendingFrame = null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (framePool.Count > 0)
|
|
||||||
{
|
|
||||||
lock (readyFrames)
|
|
||||||
{
|
|
||||||
readyFrames.Enqueue(pendingFrame);
|
|
||||||
}
|
|
||||||
pendingFrame = null;
|
|
||||||
if (frameReadyEvent != null)
|
|
||||||
{
|
|
||||||
frameReadyEvent.Set();
|
|
||||||
}
|
|
||||||
statistics.Frames += 1;
|
|
||||||
statistics.FramesSinceLast += 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
statistics.FramesDropped += 1;
|
|
||||||
Debug.WriteLine("Receive.DroppedFrame: Ready " + readyFrames.Count);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else { break; }
|
|
||||||
}
|
|
||||||
else { break; }
|
|
||||||
}
|
|
||||||
else { break; }
|
|
||||||
}
|
|
||||||
e.SetBuffer(len, e.Buffer.Length - len);
|
|
||||||
StartReceive(e);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
OMTLogging.Write("SocketClosing: " + e.SocketError.ToString() + "," + e.BytesTransferred,"OMTChannel.Receive");
|
|
||||||
CloseSocket();
|
|
||||||
OnEvent(OMTEventType.Disconnected);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "OMTChannel.Receive");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void StartReceive(SocketAsyncEventArgs e)
|
|
||||||
{
|
|
||||||
if (!Exiting)
|
|
||||||
{
|
|
||||||
if (socket != null)
|
|
||||||
{
|
|
||||||
if (socket.Connected)
|
|
||||||
{
|
|
||||||
if (socket.ReceiveAsync(e) == false)
|
|
||||||
{
|
|
||||||
Receive_Completed(this, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void StartReceive()
|
|
||||||
{
|
|
||||||
receiveargs.SetBuffer(0, receiveargs.Buffer.Length);
|
|
||||||
StartReceive(receiveargs);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void DisposeInternal()
|
|
||||||
{
|
|
||||||
lock (sendSync) { }
|
|
||||||
lock (readyFrames) { }
|
|
||||||
lock (metadatas)
|
|
||||||
{
|
|
||||||
metadatas.Clear();
|
|
||||||
}
|
|
||||||
CloseSocket();
|
|
||||||
if (receiveargs != null)
|
|
||||||
{
|
|
||||||
receiveargs.Completed -= Receive_Completed;
|
|
||||||
receiveargs.Dispose();
|
|
||||||
receiveargs = null;
|
|
||||||
}
|
|
||||||
if (sendpool != null)
|
|
||||||
{
|
|
||||||
sendpool.Dispose();
|
|
||||||
sendpool = null;
|
|
||||||
}
|
|
||||||
if (metapool != null)
|
|
||||||
{
|
|
||||||
metapool.Dispose();
|
|
||||||
metapool = null;
|
|
||||||
}
|
|
||||||
if (framePool != null)
|
|
||||||
{
|
|
||||||
framePool.Dispose();
|
|
||||||
framePool = null;
|
|
||||||
}
|
|
||||||
if (readyFrames != null)
|
|
||||||
{
|
|
||||||
lock (readyFrames)
|
|
||||||
{
|
|
||||||
foreach (OMTFrame frame in readyFrames)
|
|
||||||
{
|
|
||||||
if (frame != null)
|
|
||||||
{
|
|
||||||
frame.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
readyFrames.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (pendingFrame != null)
|
|
||||||
{
|
|
||||||
pendingFrame.Dispose();
|
|
||||||
pendingFrame = null;
|
|
||||||
}
|
|
||||||
if (tempReceiveBuffer != null)
|
|
||||||
{
|
|
||||||
tempReceiveBuffer.Dispose();
|
|
||||||
tempReceiveBuffer = null;
|
|
||||||
}
|
|
||||||
base.DisposeInternal();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,103 +0,0 @@
|
|||||||
/*
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Open Media Transport Contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Runtime.Serialization;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace libomtnet
|
|
||||||
{ internal class OMTClock : OMTBase
|
|
||||||
{
|
|
||||||
private long lastTimestamp = -1;
|
|
||||||
private Stopwatch clock = Stopwatch.StartNew();
|
|
||||||
private long clockTimestamp = -1;
|
|
||||||
private int frameRateN = -1;
|
|
||||||
private int frameRateD = -1;
|
|
||||||
private int sampleRate = -1;
|
|
||||||
private long frameInterval = -1;
|
|
||||||
private bool audio;
|
|
||||||
public OMTClock(bool audio)
|
|
||||||
{
|
|
||||||
this.audio = audio;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Process(ref OMTMediaFrame frame)
|
|
||||||
{
|
|
||||||
if (audio && frame.SampleRate != sampleRate)
|
|
||||||
{
|
|
||||||
Reset(frame);
|
|
||||||
} else if ((frame.FrameRateN != frameRateN) || frame.FrameRateD != frameRateD)
|
|
||||||
{
|
|
||||||
Reset(frame);
|
|
||||||
}
|
|
||||||
if (frame.Timestamp == -1)
|
|
||||||
{
|
|
||||||
if (lastTimestamp == -1)
|
|
||||||
{
|
|
||||||
Reset(frame);
|
|
||||||
frame.Timestamp = 0;
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
if (audio && sampleRate > 0 && frame.SamplesPerChannel > 0)
|
|
||||||
{
|
|
||||||
frameInterval = 10000000L * frame.SamplesPerChannel;
|
|
||||||
frameInterval /= sampleRate;
|
|
||||||
}
|
|
||||||
frame.Timestamp = lastTimestamp + frameInterval;
|
|
||||||
clockTimestamp += frameInterval;
|
|
||||||
|
|
||||||
long diff = clockTimestamp - (clock.ElapsedMilliseconds * 10000);
|
|
||||||
while (diff < -frameInterval)
|
|
||||||
{
|
|
||||||
frame.Timestamp += frameInterval;
|
|
||||||
clockTimestamp += frameInterval;
|
|
||||||
diff += frameInterval;
|
|
||||||
}
|
|
||||||
while (!Exiting && (clockTimestamp > clock.ElapsedMilliseconds * 10000))
|
|
||||||
{
|
|
||||||
Thread.Sleep(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lastTimestamp = frame.Timestamp;
|
|
||||||
}
|
|
||||||
private void Reset(OMTMediaFrame frame)
|
|
||||||
{
|
|
||||||
frameRateD = frame.FrameRateD;
|
|
||||||
frameRateN = frame.FrameRateN;
|
|
||||||
sampleRate = frame.SampleRate;
|
|
||||||
if (frame.FrameRate > 0)
|
|
||||||
{
|
|
||||||
frameInterval = (long)(10000000 / frame.FrameRate);
|
|
||||||
}
|
|
||||||
clock = Stopwatch.StartNew();
|
|
||||||
clockTimestamp = 0;
|
|
||||||
Debug.WriteLine("OMTClock.Reset");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
/*
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Open Media Transport Contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace libomtnet
|
|
||||||
{
|
|
||||||
public class OMTPublicConstants
|
|
||||||
{
|
|
||||||
public static int DISCOVERY_SERVER_DEFAULT_PORT = 6399;
|
|
||||||
}
|
|
||||||
internal class OMTConstants
|
|
||||||
{
|
|
||||||
public static int NETWORK_SEND_BUFFER = 65536;
|
|
||||||
public static int NETWORK_SEND_RECEIVE_BUFFER = 65536;
|
|
||||||
public static int NETWORK_RECEIVE_BUFFER = 1048576 * 8; //8MB is a safe maximum for MacOS platforms
|
|
||||||
|
|
||||||
public static int NETWORK_ASYNC_COUNT = 4;
|
|
||||||
public static int NETWORK_ASYNC_BUFFER_AV = 1048576;
|
|
||||||
public static int NETWORK_ASYNC_BUFFER_META = 65536;
|
|
||||||
|
|
||||||
//For OMTDiscoveryServer
|
|
||||||
public static int NETWORK_ASYNC_COUNT_META_ONLY = 64;
|
|
||||||
public static int NETWORK_ASYNC_BUFFER_META_ONLY = 1024;
|
|
||||||
|
|
||||||
public static int VIDEO_FRAME_POOL_COUNT = 4;
|
|
||||||
|
|
||||||
public static int VIDEO_MIN_SIZE = 65536;
|
|
||||||
public static int VIDEO_MAX_SIZE = 10485760;
|
|
||||||
|
|
||||||
public static int AUDIO_FRAME_POOL_COUNT = 10;
|
|
||||||
|
|
||||||
public static int AUDIO_MIN_SIZE = 65536;
|
|
||||||
public static int AUDIO_MAX_SIZE = 1048576;
|
|
||||||
|
|
||||||
public static int NETWORK_PORT_START = 6400;
|
|
||||||
public static int NETWORK_PORT_END = 6600;
|
|
||||||
|
|
||||||
public static int AUDIO_SAMPLE_SIZE = 4;
|
|
||||||
public static int METADATA_MAX_COUNT = 60;
|
|
||||||
|
|
||||||
public static int METADATA_FRAME_SIZE = 65536;
|
|
||||||
|
|
||||||
public static string URL_PREFIX = "omt://";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,656 +0,0 @@
|
|||||||
/*
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Open Media Transport Contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Drawing;
|
|
||||||
using System.Net;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Security.Cryptography;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace libomtnet
|
|
||||||
{
|
|
||||||
public class OMTDiscovery : OMTBase
|
|
||||||
{
|
|
||||||
private string[] addresses = { };
|
|
||||||
private OMTAddressSorter addressSorter = new OMTAddressSorter();
|
|
||||||
internal List<OMTDiscoveryEntry> entries = new List<OMTDiscoveryEntry>();
|
|
||||||
private List<IOMTDiscoveryNotify> notifications = new List<IOMTDiscoveryNotify>();
|
|
||||||
protected object lockSync = new object();
|
|
||||||
private static object sharedLockSync = new object();
|
|
||||||
private static OMTDiscovery instance = null;
|
|
||||||
private DateTime lastCleared;
|
|
||||||
|
|
||||||
private OMTDiscoveryClient discoveryClient = null;
|
|
||||||
|
|
||||||
protected OMTDiscovery()
|
|
||||||
{
|
|
||||||
entries = new List<OMTDiscoveryEntry>();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
OMTSettings settings = OMTSettings.GetInstance();
|
|
||||||
string server = settings.GetString("DiscoveryServer", "");
|
|
||||||
if (!String.IsNullOrEmpty(server))
|
|
||||||
{
|
|
||||||
discoveryClient = new OMTDiscoveryClient(server, this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "OMTDiscovery");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal bool IsUsingServer()
|
|
||||||
{
|
|
||||||
if (discoveryClient == null) return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
///<summary>
|
|
||||||
///Get the shared instance of OMTDiscovery used by all Senders and Receivers with a process.
|
|
||||||
///This should never be disposed, or only disposed only when last sender or receiver has been disposed and no further use of this library is expected.
|
|
||||||
/// </summary>
|
|
||||||
public static OMTDiscovery GetInstance()
|
|
||||||
{
|
|
||||||
lock (sharedLockSync)
|
|
||||||
{
|
|
||||||
if (instance == null)
|
|
||||||
{
|
|
||||||
switch (OMTPlatform.GetPlatformType())
|
|
||||||
{
|
|
||||||
case OMTPlatformType.Win32:
|
|
||||||
instance = new win32.OMTDiscoveryWin32();
|
|
||||||
break;
|
|
||||||
case OMTPlatformType.MacOS:
|
|
||||||
case OMTPlatformType.iOS:
|
|
||||||
instance = new mac.OMTDiscoveryMac();
|
|
||||||
break;
|
|
||||||
case OMTPlatformType.Linux:
|
|
||||||
instance = new linux.OMTDiscoveryAvahi();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
instance = new OMTDiscovery();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void Subscribe(IOMTDiscoveryNotify notify)
|
|
||||||
{
|
|
||||||
lock (lockSync)
|
|
||||||
{
|
|
||||||
if (notifications.Contains(notify) == false)
|
|
||||||
{
|
|
||||||
notifications.Add(notify);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void Unsubscribe(IOMTDiscoveryNotify notify)
|
|
||||||
{
|
|
||||||
lock (lockSync)
|
|
||||||
{
|
|
||||||
if (notifications.Contains(notify))
|
|
||||||
{
|
|
||||||
notifications.Remove(notify);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
internal void OnNewAddress(OMTAddress address)
|
|
||||||
{
|
|
||||||
if (address.Addresses.Length > 0)
|
|
||||||
{
|
|
||||||
IOMTDiscoveryNotify[] n = null;
|
|
||||||
lock (lockSync)
|
|
||||||
{
|
|
||||||
n = notifications.ToArray();
|
|
||||||
}
|
|
||||||
if (n != null)
|
|
||||||
{
|
|
||||||
foreach (IOMTDiscoveryNotify notify in n)
|
|
||||||
{
|
|
||||||
notify.Notify(address);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal OMTDiscoveryEntry GetEntry(OMTAddress address)
|
|
||||||
{
|
|
||||||
lock (lockSync)
|
|
||||||
{
|
|
||||||
foreach (OMTDiscoveryEntry entry in entries)
|
|
||||||
{
|
|
||||||
if (entry.Address.ToString() == address.ToString())
|
|
||||||
{
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal OMTDiscoveryEntry GetEntry(string fullName)
|
|
||||||
{
|
|
||||||
lock (lockSync)
|
|
||||||
{
|
|
||||||
foreach (OMTDiscoveryEntry entry in entries)
|
|
||||||
{
|
|
||||||
if (entry.Address.ToString() == fullName)
|
|
||||||
{
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal bool RemoveDiscoveredEntry(string fullName)
|
|
||||||
{
|
|
||||||
lock (lockSync)
|
|
||||||
{
|
|
||||||
OMTDiscoveryEntry entry = GetEntry(fullName);
|
|
||||||
if (entry != null)
|
|
||||||
{
|
|
||||||
if (entry.Status == OMTDiscoveryEntryStatus.Discovered)
|
|
||||||
{
|
|
||||||
RemoveEntry(entry.Address, true);
|
|
||||||
OMTLogging.Write("Remove: " + entry.Address.ToString() + ":" + entry.Address.Port, "OMTDiscovery");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal OMTDiscoveryEntry UpdateDiscoveredEntry(string fullName, int port, IPAddress[] addresses)
|
|
||||||
{
|
|
||||||
lock (lockSync)
|
|
||||||
{
|
|
||||||
OMTDiscoveryEntry entry = GetEntry(fullName);
|
|
||||||
if (entry == null)
|
|
||||||
{
|
|
||||||
OMTAddress address = OMTAddress.Create(fullName, port);
|
|
||||||
entry = new OMTDiscoveryEntry(address);
|
|
||||||
entry.ChangeStatus(OMTDiscoveryEntryStatus.Discovered);
|
|
||||||
if (AddEntry(entry))
|
|
||||||
{
|
|
||||||
OMTLogging.Write("New: " + fullName + ":" + port, "OMTDiscovery");
|
|
||||||
if (addresses != null)
|
|
||||||
{
|
|
||||||
foreach (IPAddress ip in addresses)
|
|
||||||
{
|
|
||||||
if (address.AddAddress(ip))
|
|
||||||
{
|
|
||||||
OMTLogging.Write("NewIP: " + fullName + ":" + port + "," + ip.ToString(), "OMTDiscovery");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
bool newPort = false;
|
|
||||||
if (entry.Address.Port != port)
|
|
||||||
{
|
|
||||||
entry.Address.Port = port;
|
|
||||||
newPort = true;
|
|
||||||
OMTLogging.Write("ChangePort: " + fullName + ":" + port, "OMTDiscovery");
|
|
||||||
}
|
|
||||||
if (addresses != null)
|
|
||||||
{
|
|
||||||
bool newIp = false;
|
|
||||||
foreach (IPAddress ip in addresses)
|
|
||||||
{
|
|
||||||
if (entry.Address.AddAddress(ip))
|
|
||||||
{
|
|
||||||
OMTLogging.Write("AddIP: " + fullName + ":" + port + "," + ip.ToString(), "OMTDiscovery");
|
|
||||||
newIp = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (newIp || newPort)
|
|
||||||
{
|
|
||||||
OnNewAddress(entry.Address);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal bool AddEntry(OMTDiscoveryEntry entry)
|
|
||||||
{
|
|
||||||
lock (lockSync)
|
|
||||||
{
|
|
||||||
if (entries.Contains(entry) == false)
|
|
||||||
{
|
|
||||||
entries.Add(entry);
|
|
||||||
RefreshAddresses();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RefreshAddresses ()
|
|
||||||
{
|
|
||||||
List<string> addresses = new List<string>();
|
|
||||||
foreach (OMTDiscoveryEntry entry in entries)
|
|
||||||
{
|
|
||||||
addresses.Add(entry.Address.ToString());
|
|
||||||
}
|
|
||||||
addresses.Sort();
|
|
||||||
this.addresses = addresses.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal bool RemoveEntry(OMTAddress address, bool dispose)
|
|
||||||
{
|
|
||||||
lock (lockSync)
|
|
||||||
{
|
|
||||||
foreach (OMTDiscoveryEntry entry in entries)
|
|
||||||
{
|
|
||||||
if (entry.Address.ToString() == address.ToString())
|
|
||||||
{
|
|
||||||
entries.Remove(entry);
|
|
||||||
if (dispose)
|
|
||||||
{
|
|
||||||
entry.Dispose();
|
|
||||||
}
|
|
||||||
RefreshAddresses();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal virtual bool DeregisterAddressInternal(OMTAddress address)
|
|
||||||
{
|
|
||||||
return DeregisterAddressDefault(address);
|
|
||||||
}
|
|
||||||
internal virtual bool RegisterAddressInternal(OMTAddress address)
|
|
||||||
{
|
|
||||||
return RegisterAddressDefault(address);
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool RegisterAddressDefault(OMTAddress address)
|
|
||||||
{
|
|
||||||
lock (lockSync)
|
|
||||||
{
|
|
||||||
OMTDiscoveryEntry entry = GetEntry(address);
|
|
||||||
if (entry != null)
|
|
||||||
{
|
|
||||||
if (entry.Status == OMTDiscoveryEntryStatus.Discovered)
|
|
||||||
{
|
|
||||||
RemoveEntry(address, true);
|
|
||||||
entry = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (entry == null)
|
|
||||||
{
|
|
||||||
entry = new OMTDiscoveryEntry(address);
|
|
||||||
AddEntry(entry);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool DeregisterAddressDefault(OMTAddress address)
|
|
||||||
{
|
|
||||||
return RemoveEntry(address, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal bool RegisterAddress(OMTAddress address)
|
|
||||||
{
|
|
||||||
if (IsUsingServer())
|
|
||||||
{
|
|
||||||
if (RegisterAddressDefault(address))
|
|
||||||
{
|
|
||||||
address.removed = false;
|
|
||||||
discoveryClient.SendAddress(address);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return RegisterAddressInternal(address);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal bool DeregisterAddress(OMTAddress address)
|
|
||||||
{
|
|
||||||
if (IsUsingServer())
|
|
||||||
{
|
|
||||||
if (DeregisterAddressDefault(address))
|
|
||||||
{
|
|
||||||
address.removed = true;
|
|
||||||
discoveryClient.SendAddress(address);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return DeregisterAddressInternal(address);
|
|
||||||
}
|
|
||||||
|
|
||||||
static internal OMTAddress CreateFromUrl(string address, int defaultPort)
|
|
||||||
{
|
|
||||||
Uri u = null;
|
|
||||||
if (Uri.TryCreate(address, UriKind.Absolute, out u))
|
|
||||||
{
|
|
||||||
int port = u.Port;
|
|
||||||
if (port <= 0)
|
|
||||||
{
|
|
||||||
port = defaultPort;
|
|
||||||
}
|
|
||||||
if (port > 0)
|
|
||||||
{
|
|
||||||
OMTAddress a = new OMTAddress(u.Host, port.ToString(), port);
|
|
||||||
IPAddress[] ips = OMTUtils.ResolveHostname(u.Host);
|
|
||||||
if (ips != null && ips.Length > 0)
|
|
||||||
{
|
|
||||||
foreach (IPAddress ip in ips)
|
|
||||||
{
|
|
||||||
a.AddAddress(ip);
|
|
||||||
}
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal OMTAddress FindByFullNameOrUrl(string address)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(address)) { return null; }
|
|
||||||
if (address.ToLower().StartsWith(OMTConstants.URL_PREFIX))
|
|
||||||
{
|
|
||||||
return CreateFromUrl(address, 0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return FindByFullName(address);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
internal OMTAddress FindByFullName(string fullName)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(fullName)) { return null; }
|
|
||||||
lock (lockSync)
|
|
||||||
{
|
|
||||||
foreach (OMTDiscoveryEntry entry in entries)
|
|
||||||
{
|
|
||||||
if (entry.Address.ToString().Equals(OMTAddress.SanitizeName(fullName)))
|
|
||||||
{
|
|
||||||
return entry.Address;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal string ParseAddressName(string name)
|
|
||||||
{
|
|
||||||
int pos = name.IndexOf("._omt.");
|
|
||||||
if (pos > 0)
|
|
||||||
{
|
|
||||||
return name.Substring(0, pos);
|
|
||||||
}
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void RemoveServerAddresses()
|
|
||||||
{
|
|
||||||
lock (lockSync)
|
|
||||||
{
|
|
||||||
for (int i = entries.Count - 1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
OMTDiscoveryEntry entry = entries[i];
|
|
||||||
if (entry.FromServer)
|
|
||||||
{
|
|
||||||
if (entry.Status == OMTDiscoveryEntryStatus.Discovered)
|
|
||||||
{
|
|
||||||
entries.RemoveAt(i);
|
|
||||||
OMTLogging.Write("RemovedAddressFromServer: " + entry.Address.ToString(), "OMTDiscovery");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
RefreshAddresses();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void RemoveExpiredAddresses()
|
|
||||||
{
|
|
||||||
lock (lockSync)
|
|
||||||
{
|
|
||||||
lastCleared = DateTime.Now;
|
|
||||||
for (int i = entries.Count - 1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
OMTDiscoveryEntry entry = entries[i];
|
|
||||||
if (entry.Status == OMTDiscoveryEntryStatus.Discovered)
|
|
||||||
{
|
|
||||||
if (entry.Expiry > DateTime.MinValue && entry.Expiry < DateTime.Now)
|
|
||||||
{
|
|
||||||
entries.RemoveAt(i);
|
|
||||||
OMTLogging.Write("ExpiredAddress: " + entry.Address.ToString(), "OMTDiscovery");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
RefreshAddresses();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected int GetRegisteredEntryCount()
|
|
||||||
{
|
|
||||||
int count = 0;
|
|
||||||
lock (lockSync)
|
|
||||||
{
|
|
||||||
foreach (OMTDiscoveryEntry entry in entries)
|
|
||||||
{
|
|
||||||
if (entry.Status == OMTDiscoveryEntryStatus.Registered)
|
|
||||||
{
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal OMTDiscoveryEntry GetEntryByPort(int port, bool discovered)
|
|
||||||
{
|
|
||||||
lock (lockSync)
|
|
||||||
{
|
|
||||||
foreach (OMTDiscoveryEntry entry in entries)
|
|
||||||
{
|
|
||||||
if (entry.Address.Port == port)
|
|
||||||
{
|
|
||||||
if (discovered)
|
|
||||||
{
|
|
||||||
if (entry.Status == OMTDiscoveryEntryStatus.Discovered)
|
|
||||||
{
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (entry.Status != OMTDiscoveryEntryStatus.Discovered)
|
|
||||||
{
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
///<summary>
|
|
||||||
/// Retrieve a list of Sources currently available on the network
|
|
||||||
/// </summary>
|
|
||||||
public string[] GetAddresses()
|
|
||||||
{
|
|
||||||
if (lastCleared < DateTime.Now.AddSeconds(-10))
|
|
||||||
{
|
|
||||||
RemoveExpiredAddresses();
|
|
||||||
}
|
|
||||||
return addresses;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal OMTAddress[] GetAddressesInternal()
|
|
||||||
{
|
|
||||||
List<OMTAddress> a = new List<OMTAddress>();
|
|
||||||
lock (lockSync)
|
|
||||||
{
|
|
||||||
foreach (OMTDiscoveryEntry rr in entries)
|
|
||||||
{
|
|
||||||
if (rr.Status != OMTDiscoveryEntryStatus.Discovered)
|
|
||||||
{
|
|
||||||
a.Add(rr.Address);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return a.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DisposeEntries()
|
|
||||||
{
|
|
||||||
OMTDiscoveryEntry[] e = null;
|
|
||||||
lock (lockSync)
|
|
||||||
{
|
|
||||||
e = entries.ToArray();
|
|
||||||
}
|
|
||||||
if (e != null)
|
|
||||||
{
|
|
||||||
foreach (OMTDiscoveryEntry rr in e)
|
|
||||||
{
|
|
||||||
if (rr != null)
|
|
||||||
{
|
|
||||||
rr.Dispose(); //The cancel request calls OnComplete, so run outside of lock here
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lock (lockSync)
|
|
||||||
{
|
|
||||||
entries.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void DisposeInternal()
|
|
||||||
{
|
|
||||||
if (discoveryClient != null)
|
|
||||||
{
|
|
||||||
discoveryClient.Dispose();
|
|
||||||
discoveryClient = null;
|
|
||||||
}
|
|
||||||
DisposeEntries();
|
|
||||||
base.DisposeInternal();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
internal interface IOMTDiscoveryNotify
|
|
||||||
{
|
|
||||||
void Notify(OMTAddress address);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal enum OMTDiscoveryEntryStatus
|
|
||||||
{
|
|
||||||
None = 0,
|
|
||||||
PendingRegister,
|
|
||||||
PendingDeRegister,
|
|
||||||
PendingRegisterAfterDeRegister,
|
|
||||||
PendingDeRegisterAfterRegister,
|
|
||||||
Registered,
|
|
||||||
Discovered
|
|
||||||
}
|
|
||||||
internal class OMTDiscoveryEntry : OMTBase
|
|
||||||
{
|
|
||||||
private OMTAddress address;
|
|
||||||
private OMTDiscoveryEntryStatus status;
|
|
||||||
private GCHandle handle;
|
|
||||||
private DateTime expiry;
|
|
||||||
private bool fromServer;
|
|
||||||
|
|
||||||
public OMTAddress Address { get { return address; } }
|
|
||||||
public OMTDiscoveryEntryStatus Status { get { return status; } }
|
|
||||||
|
|
||||||
public OMTDiscoveryEntry(OMTAddress address)
|
|
||||||
{
|
|
||||||
this.address = address;
|
|
||||||
this.status = OMTDiscoveryEntryStatus.None;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DateTime Expiry
|
|
||||||
{
|
|
||||||
get { return expiry; }
|
|
||||||
set { expiry = value; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool FromServer
|
|
||||||
{
|
|
||||||
get { return fromServer; }
|
|
||||||
set { fromServer = value; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ChangeStatus(OMTDiscoveryEntryStatus status)
|
|
||||||
{
|
|
||||||
this.status = status;
|
|
||||||
}
|
|
||||||
public static OMTDiscoveryEntry FromIntPtr(IntPtr p)
|
|
||||||
{
|
|
||||||
GCHandle h = GCHandle.FromIntPtr(p);
|
|
||||||
if (h.IsAllocated)
|
|
||||||
{
|
|
||||||
OMTDiscoveryEntry q = (OMTDiscoveryEntry)h.Target;
|
|
||||||
return q;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IntPtr ToIntPtr()
|
|
||||||
{
|
|
||||||
if (!handle.IsAllocated)
|
|
||||||
{
|
|
||||||
handle = GCHandle.Alloc(this);
|
|
||||||
}
|
|
||||||
return GCHandle.ToIntPtr(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void DisposeInternal()
|
|
||||||
{
|
|
||||||
if (handle.IsAllocated)
|
|
||||||
{
|
|
||||||
handle.Free();
|
|
||||||
}
|
|
||||||
base.DisposeInternal();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,397 +0,0 @@
|
|||||||
/*
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Open Media Transport Contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace libomtnet
|
|
||||||
{
|
|
||||||
internal enum OMTVersion
|
|
||||||
{
|
|
||||||
Version1 = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
[Flags]
|
|
||||||
internal enum OMTActiveAudioChannels : uint
|
|
||||||
{
|
|
||||||
C1 = 1,
|
|
||||||
C2 = 2,
|
|
||||||
C3 = 4,
|
|
||||||
C4 = 8,
|
|
||||||
C5 = 16,
|
|
||||||
C6 = 32,
|
|
||||||
C7 = 64,
|
|
||||||
C8 = 128,
|
|
||||||
C9 = 256,
|
|
||||||
C10 = 512,
|
|
||||||
C11 = 1024,
|
|
||||||
C12 = 2048,
|
|
||||||
C13 = 4096,
|
|
||||||
C14 = 8192,
|
|
||||||
C15 = 16384,
|
|
||||||
C16 = 32768,
|
|
||||||
C17 = 65536,
|
|
||||||
C18 = 131072,
|
|
||||||
C19 = 262144,
|
|
||||||
C20 = 524288,
|
|
||||||
C21 = 1048576,
|
|
||||||
C22 = 2097152,
|
|
||||||
C23 = 4194304,
|
|
||||||
C24 = 8388608,
|
|
||||||
C25 = 16777216,
|
|
||||||
C26 = 33554432,
|
|
||||||
C27 = 67108864,
|
|
||||||
C28 = 134217728,
|
|
||||||
C29 = 268435456,
|
|
||||||
C30 = 536870912,
|
|
||||||
C31 = 1073741824,
|
|
||||||
C32 = 2147483658
|
|
||||||
}
|
|
||||||
|
|
||||||
internal struct OMTFrameHeader
|
|
||||||
{
|
|
||||||
public byte Version; //=1
|
|
||||||
public byte FrameType;
|
|
||||||
public long Timestamp;
|
|
||||||
//public byte Reserved1;
|
|
||||||
//public byte Reserved2;
|
|
||||||
public ushort MetadataLength; //Length in bytes of UTF-8 metadata including null character
|
|
||||||
public int DataLength; //Including extended header and metadata
|
|
||||||
}
|
|
||||||
internal struct OMTVideoHeader
|
|
||||||
{
|
|
||||||
public int Codec;
|
|
||||||
public int Width;
|
|
||||||
public int Height;
|
|
||||||
public int FrameRateN;
|
|
||||||
public int FrameRateD;
|
|
||||||
public float AspectRatio;
|
|
||||||
public int Flags;
|
|
||||||
public int ColorSpace;
|
|
||||||
}
|
|
||||||
internal struct OMTAudioHeader
|
|
||||||
{
|
|
||||||
public int Codec;
|
|
||||||
public int SampleRate;
|
|
||||||
public int SamplesPerChannel;
|
|
||||||
public int Channels;
|
|
||||||
public uint ActiveChannels;
|
|
||||||
public int Reserved1;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal enum OMTFrameLength
|
|
||||||
{
|
|
||||||
None = 0,
|
|
||||||
Header = 16,
|
|
||||||
ExtendedHeaderVideo = 32,
|
|
||||||
ExtendedHeaderAudio = 24
|
|
||||||
}
|
|
||||||
|
|
||||||
internal class OMTFrameBase : OMTBase
|
|
||||||
{
|
|
||||||
public virtual OMTFrameType FrameType { get { return OMTFrameType.None; } }
|
|
||||||
public virtual long Timestamp
|
|
||||||
{ get { return 0; } set { } }
|
|
||||||
}
|
|
||||||
|
|
||||||
internal class OMTFrame : OMTFrameBase
|
|
||||||
{
|
|
||||||
protected OMTFrameHeader header;
|
|
||||||
protected OMTBuffer buffer;
|
|
||||||
protected OMTVideoHeader videoHeader;
|
|
||||||
protected OMTAudioHeader audioHeader;
|
|
||||||
protected int previewLength;
|
|
||||||
protected bool preview;
|
|
||||||
protected OMTBinary binary = new OMTBinary();
|
|
||||||
public OMTFrame(OMTFrameType frameType, int maxDataLength, bool resizable)
|
|
||||||
{
|
|
||||||
header.Version = (byte)OMTVersion.Version1;
|
|
||||||
header.FrameType = (byte)frameType;
|
|
||||||
buffer = new OMTBuffer(maxDataLength, resizable);
|
|
||||||
buffer.SetBuffer(0, 0);
|
|
||||||
UpdateDataLength();
|
|
||||||
}
|
|
||||||
public OMTFrame(int maxDataLength, bool resizable)
|
|
||||||
{
|
|
||||||
buffer = new OMTBuffer(maxDataLength, resizable);
|
|
||||||
buffer.SetBuffer(0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public OMTFrame(OMTFrameType frameType, OMTBuffer buff)
|
|
||||||
{
|
|
||||||
header.Version = (byte)OMTVersion.Version1;
|
|
||||||
header.FrameType = (byte)frameType;
|
|
||||||
buffer = buff;
|
|
||||||
UpdateDataLength();
|
|
||||||
}
|
|
||||||
public int HeaderLength
|
|
||||||
{
|
|
||||||
get { return (int)OMTFrameLength.Header; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public int MetadataLength
|
|
||||||
{
|
|
||||||
get { return (int)header.MetadataLength; }
|
|
||||||
}
|
|
||||||
public int ExtendedHeaderLength
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (header.FrameType == (byte)OMTFrameType.Video)
|
|
||||||
{
|
|
||||||
return (int)OMTFrameLength.ExtendedHeaderVideo;
|
|
||||||
}
|
|
||||||
else if (header.FrameType == (byte)OMTFrameType.Audio)
|
|
||||||
{
|
|
||||||
return (int)OMTFrameLength.ExtendedHeaderAudio;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public int Length
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (preview)
|
|
||||||
{
|
|
||||||
return HeaderLength + previewLength;
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
return HeaderLength + header.DataLength;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void WriteHeaderInternal()
|
|
||||||
{
|
|
||||||
OMTBinary b = binary;
|
|
||||||
b.Write(header.Version);
|
|
||||||
b.Write(header.FrameType);
|
|
||||||
b.Write(header.Timestamp);
|
|
||||||
b.Write(header.MetadataLength);
|
|
||||||
if (preview)
|
|
||||||
{
|
|
||||||
b.Write(previewLength);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
b.Write(header.DataLength);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override OMTFrameType FrameType
|
|
||||||
{
|
|
||||||
get { return (OMTFrameType)header.FrameType; }
|
|
||||||
}
|
|
||||||
public override long Timestamp
|
|
||||||
{ get { return header.Timestamp; } set { header.Timestamp = value; } }
|
|
||||||
|
|
||||||
protected void WriteExtendedHeaderInternal()
|
|
||||||
{
|
|
||||||
OMTBinary b = binary;
|
|
||||||
if (header.FrameType == (byte)OMTFrameType.Video)
|
|
||||||
{
|
|
||||||
b.Write(videoHeader.Codec);
|
|
||||||
b.Write(videoHeader.Width);
|
|
||||||
b.Write(videoHeader.Height);
|
|
||||||
b.Write(videoHeader.FrameRateN);
|
|
||||||
b.Write(videoHeader.FrameRateD);
|
|
||||||
b.Write(videoHeader.AspectRatio);
|
|
||||||
if (preview)
|
|
||||||
{
|
|
||||||
b.Write(videoHeader.Flags | (int)OMTVideoFlags.Preview);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
b.Write(videoHeader.Flags);
|
|
||||||
}
|
|
||||||
b.Write(videoHeader.ColorSpace);
|
|
||||||
}
|
|
||||||
else if (header.FrameType == (byte)OMTFrameType.Audio)
|
|
||||||
{
|
|
||||||
b.Write(audioHeader.Codec);
|
|
||||||
b.Write(audioHeader.SampleRate);
|
|
||||||
b.Write(audioHeader.SamplesPerChannel);
|
|
||||||
b.Write(audioHeader.Channels);
|
|
||||||
b.Write(audioHeader.ActiveChannels);
|
|
||||||
b.Write(audioHeader.Reserved1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
protected bool ReadHeaderInternal(byte[] data, int offset)
|
|
||||||
{
|
|
||||||
OMTBinary b = binary;
|
|
||||||
b.SetBuffer(data, offset);
|
|
||||||
header.Version = b.ReadByte();
|
|
||||||
if (header.Version == (byte)OMTVersion.Version1)
|
|
||||||
{
|
|
||||||
header.FrameType = b.ReadByte();
|
|
||||||
header.Timestamp = b.ReadInt64();
|
|
||||||
header.MetadataLength = b.ReadUInt16();
|
|
||||||
header.DataLength = b.ReadInt32();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected bool ReadExtendedHeaderInternal(byte[] data, int offset)
|
|
||||||
{
|
|
||||||
OMTBinary b = binary;
|
|
||||||
b.SetBuffer(data, offset);
|
|
||||||
if (header.Version == (byte)OMTVersion.Version1)
|
|
||||||
{
|
|
||||||
if (header.FrameType == (byte)OMTFrameType.Video)
|
|
||||||
{
|
|
||||||
videoHeader.Codec = b.ReadInt32();
|
|
||||||
videoHeader.Width = b.ReadInt32();
|
|
||||||
videoHeader.Height = b.ReadInt32();
|
|
||||||
videoHeader.FrameRateN = b.ReadInt32();
|
|
||||||
videoHeader.FrameRateD = b.ReadInt32();
|
|
||||||
videoHeader.AspectRatio = b.ReadSingle();
|
|
||||||
videoHeader.Flags = b.ReadInt32();
|
|
||||||
videoHeader.ColorSpace = b.ReadInt32();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if (header.FrameType == (byte)OMTFrameType.Audio)
|
|
||||||
{
|
|
||||||
audioHeader.Codec = b.ReadInt32();
|
|
||||||
audioHeader.SampleRate = b.ReadInt32();
|
|
||||||
audioHeader.SamplesPerChannel = b.ReadInt32();
|
|
||||||
audioHeader.Channels = b.ReadInt32();
|
|
||||||
audioHeader.ActiveChannels = b.ReadUInt32();
|
|
||||||
audioHeader.Reserved1 = b.ReadInt32();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public OMTBuffer Data
|
|
||||||
{
|
|
||||||
get { return buffer; }
|
|
||||||
}
|
|
||||||
private void UpdateDataLength()
|
|
||||||
{
|
|
||||||
header.DataLength = this.buffer.Length + ExtendedHeaderLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Includes MetadataLength
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="length"></param>
|
|
||||||
public void SetPreviewDataLength(int length)
|
|
||||||
{
|
|
||||||
previewLength = ExtendedHeaderLength + length;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Includes MetadataLength
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="length"></param>
|
|
||||||
public void SetDataLength(int length)
|
|
||||||
{
|
|
||||||
this.buffer.SetBuffer(0, length);
|
|
||||||
UpdateDataLength();
|
|
||||||
}
|
|
||||||
public void SetMetadataLength(int length)
|
|
||||||
{
|
|
||||||
header.MetadataLength = (ushort)length;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetPreviewMode(bool preview)
|
|
||||||
{
|
|
||||||
this.preview = preview;
|
|
||||||
}
|
|
||||||
public void WriteHeaderTo(byte[] buffer, int offset, int count)
|
|
||||||
{
|
|
||||||
binary.SetBuffer(buffer, offset);
|
|
||||||
WriteHeaderInternal();
|
|
||||||
WriteExtendedHeaderInternal();
|
|
||||||
}
|
|
||||||
public void WriteDataTo(byte[] buffer, int srcOffset, int dstOffset, int count)
|
|
||||||
{
|
|
||||||
Buffer.BlockCopy(this.buffer.Buffer, this.buffer.Offset + srcOffset, buffer, dstOffset, count);
|
|
||||||
}
|
|
||||||
public bool ReadHeaderFrom(byte[] buffer, int offset, int count)
|
|
||||||
{
|
|
||||||
if (count < HeaderLength) return false;
|
|
||||||
return ReadHeaderInternal(buffer, offset);
|
|
||||||
}
|
|
||||||
public bool ReadExtendedHeaderFrom(byte[] buffer, int offset, int count)
|
|
||||||
{
|
|
||||||
if (count < HeaderLength + ExtendedHeaderLength) return false;
|
|
||||||
if (ExtendedHeaderLength == 0) return true;
|
|
||||||
return ReadExtendedHeaderInternal(buffer, offset + HeaderLength);
|
|
||||||
}
|
|
||||||
public bool ReadDataFrom(byte[] buffer, int offset, int count)
|
|
||||||
{
|
|
||||||
if (count < HeaderLength + header.DataLength) return false;
|
|
||||||
int len = header.DataLength - ExtendedHeaderLength;
|
|
||||||
this.buffer.Resize(len);
|
|
||||||
this.buffer.SetBuffer(0, 0);
|
|
||||||
this.buffer.Append(buffer, offset + HeaderLength + ExtendedHeaderLength, len);
|
|
||||||
this.buffer.SetBuffer(0, len);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public OMTVideoHeader GetVideoHeader()
|
|
||||||
{
|
|
||||||
return videoHeader;
|
|
||||||
}
|
|
||||||
|
|
||||||
public OMTAudioHeader GetAudioHeader()
|
|
||||||
{
|
|
||||||
return audioHeader;
|
|
||||||
}
|
|
||||||
public void ConfigureVideo(int codec, int width, int height, int framerateN, int framerateD, float aspectRatio, OMTVideoFlags flags, OMTColorSpace colorSpace)
|
|
||||||
{
|
|
||||||
videoHeader.Codec = codec;
|
|
||||||
videoHeader.Width = width;
|
|
||||||
videoHeader.Height = height;
|
|
||||||
videoHeader.FrameRateN = framerateN;
|
|
||||||
videoHeader.FrameRateD = framerateD;
|
|
||||||
videoHeader.AspectRatio = aspectRatio;
|
|
||||||
videoHeader.Flags = (int)flags;
|
|
||||||
videoHeader.ColorSpace = (int)colorSpace;
|
|
||||||
}
|
|
||||||
public void ConfigureAudio(int sampleRate, int channels, int samplesPerChannel, OMTActiveAudioChannels activeAudioChannels)
|
|
||||||
{
|
|
||||||
audioHeader.SampleRate = sampleRate;
|
|
||||||
audioHeader.Channels = channels;
|
|
||||||
audioHeader.SamplesPerChannel = samplesPerChannel;
|
|
||||||
audioHeader.ActiveChannels = (uint)activeAudioChannels;
|
|
||||||
audioHeader.Codec = (int)OMTCodec.FPA1;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void DisposeInternal()
|
|
||||||
{
|
|
||||||
if (this.buffer != null)
|
|
||||||
{
|
|
||||||
this.buffer.Dispose();
|
|
||||||
}
|
|
||||||
base.DisposeInternal();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
/*
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Open Media Transport Contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace libomtnet
|
|
||||||
{
|
|
||||||
internal class OMTFramePool : OMTBase
|
|
||||||
{
|
|
||||||
Queue<OMTFrame> pool;
|
|
||||||
public OMTFramePool(int count, int maxDataLength, bool resizable)
|
|
||||||
{
|
|
||||||
pool = new Queue<OMTFrame>();
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
pool.Enqueue(new OMTFrame(maxDataLength, resizable));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void DisposeInternal()
|
|
||||||
{
|
|
||||||
if (pool != null)
|
|
||||||
{
|
|
||||||
foreach (OMTFrame frame in pool)
|
|
||||||
{
|
|
||||||
if (frame != null)
|
|
||||||
{
|
|
||||||
frame.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pool.Clear();
|
|
||||||
}
|
|
||||||
base.DisposeInternal();
|
|
||||||
}
|
|
||||||
|
|
||||||
public OMTFrame Get()
|
|
||||||
{
|
|
||||||
lock (pool)
|
|
||||||
{
|
|
||||||
if (pool.Count > 0)
|
|
||||||
{
|
|
||||||
return pool.Dequeue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Return(OMTFrame frame)
|
|
||||||
{
|
|
||||||
lock (pool)
|
|
||||||
{
|
|
||||||
pool.Enqueue(frame);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Count { get { lock (pool) { return pool.Count; } } }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
/*
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Open Media Transport Contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace libomtnet
|
|
||||||
{ internal enum OMTEventType
|
|
||||||
{
|
|
||||||
None = 0,
|
|
||||||
TallyChanged = 1,
|
|
||||||
Disconnected = 2,
|
|
||||||
RedirectChanged = 3
|
|
||||||
}
|
|
||||||
|
|
||||||
internal class OMTEventArgs : EventArgs
|
|
||||||
{
|
|
||||||
private OMTEventType eventType;
|
|
||||||
public OMTEventArgs(OMTEventType eventType)
|
|
||||||
{
|
|
||||||
this.eventType = eventType;
|
|
||||||
}
|
|
||||||
public OMTEventType Type { get { return eventType; } set { eventType = value; } }
|
|
||||||
}
|
|
||||||
|
|
||||||
internal class OMTRedirectChangedEventArgs : EventArgs
|
|
||||||
{
|
|
||||||
private string newAddress;
|
|
||||||
public OMTRedirectChangedEventArgs(string newAddress)
|
|
||||||
{
|
|
||||||
this.newAddress = newAddress;
|
|
||||||
}
|
|
||||||
public string NewAddress { get { return newAddress; } }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,170 +0,0 @@
|
|||||||
/*
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Open Media Transport Contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
using System.IO;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace libomtnet
|
|
||||||
{
|
|
||||||
public class OMTLogging
|
|
||||||
{
|
|
||||||
private static FileStream logStream;
|
|
||||||
private static StreamWriter logWriter;
|
|
||||||
private static object lockSync = new object();
|
|
||||||
private static Thread loggingThread;
|
|
||||||
private static bool threadRunning;
|
|
||||||
private static Queue<string> queue = new Queue<string>();
|
|
||||||
private static AutoResetEvent readyEvent = new AutoResetEvent(false);
|
|
||||||
private static bool initialized = false;
|
|
||||||
|
|
||||||
static OMTLogging()
|
|
||||||
{
|
|
||||||
loggingThread = new Thread(ProcessLog);
|
|
||||||
loggingThread.IsBackground = true;
|
|
||||||
threadRunning = true;
|
|
||||||
loggingThread.Start();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void SetDefaultLogFilename()
|
|
||||||
{
|
|
||||||
lock (lockSync)
|
|
||||||
{
|
|
||||||
initialized = true;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
string name = GetProcessNameAndId();
|
|
||||||
if (name != null)
|
|
||||||
{
|
|
||||||
string szPath = OMTPlatform.GetInstance().GetStoragePath();
|
|
||||||
if (Directory.Exists(szPath) == false)
|
|
||||||
{
|
|
||||||
Directory.CreateDirectory(szPath);
|
|
||||||
}
|
|
||||||
szPath = szPath + Path.DirectorySeparatorChar + "logs";
|
|
||||||
if (Directory.Exists(szPath) == false)
|
|
||||||
{
|
|
||||||
Directory.CreateDirectory(szPath);
|
|
||||||
}
|
|
||||||
SetFilename(szPath + Path.DirectorySeparatorChar + name + ".log");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Console.WriteLine(ex.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetProcessNameAndId()
|
|
||||||
{
|
|
||||||
Process process = Process.GetCurrentProcess();
|
|
||||||
if (process != null)
|
|
||||||
{
|
|
||||||
ProcessModule module = process.MainModule;
|
|
||||||
if (module != null) //Some platforms, notably iOS return null
|
|
||||||
{
|
|
||||||
return module.ModuleName + process.Id;
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
return process.Id.ToString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void ProcessLog()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
while (threadRunning)
|
|
||||||
{
|
|
||||||
readyEvent.WaitOne();
|
|
||||||
lock (lockSync)
|
|
||||||
{
|
|
||||||
if (logWriter != null)
|
|
||||||
{
|
|
||||||
while (queue.Count > 0)
|
|
||||||
{
|
|
||||||
logWriter.WriteLine(queue.Dequeue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Debug.WriteLine(ex.ToString(), "OMTLogging.ProcessLog");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SetFilename(string filename)
|
|
||||||
{
|
|
||||||
lock (lockSync)
|
|
||||||
{
|
|
||||||
initialized = true;
|
|
||||||
if (logStream != null)
|
|
||||||
{
|
|
||||||
logStream.Close();
|
|
||||||
}
|
|
||||||
logWriter = null;
|
|
||||||
if (!String.IsNullOrEmpty(filename)) {
|
|
||||||
logStream = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.Write);
|
|
||||||
logStream.Position = logStream.Length;
|
|
||||||
logWriter = new StreamWriter(logStream);
|
|
||||||
logWriter.AutoFlush = true;
|
|
||||||
OMTLogging.Write("Log Started", "OMTLogging");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public static void Write(string message, string source)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
string line = DateTime.Now.ToString() + ",[" + source + "]," + message;
|
|
||||||
Debug.WriteLine(line);
|
|
||||||
lock (lockSync)
|
|
||||||
{
|
|
||||||
if (!initialized)
|
|
||||||
{
|
|
||||||
SetDefaultLogFilename();
|
|
||||||
}
|
|
||||||
if (logWriter != null)
|
|
||||||
{
|
|
||||||
queue.Enqueue(line);
|
|
||||||
readyEvent.Set();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Debug.WriteLine(ex.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,140 +0,0 @@
|
|||||||
/*
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Open Media Transport Contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Net;
|
|
||||||
using System.Xml;
|
|
||||||
namespace libomtnet
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Fixed static XML commands for protocol use.
|
|
||||||
/// Receivers will check for these exact string matches and won't bother to parse the XML.
|
|
||||||
/// This means any changes to these, even slightly will result in the commands being ignored entirely.
|
|
||||||
/// </summary>
|
|
||||||
internal class OMTMetadataConstants
|
|
||||||
{
|
|
||||||
public const string CHANNEL_SUBSCRIBE_VIDEO = @"<OMTSubscribe Video=""true"" />";
|
|
||||||
public const string CHANNEL_SUBSCRIBE_AUDIO = @"<OMTSubscribe Audio=""true"" />";
|
|
||||||
public const string CHANNEL_SUBSCRIBE_METADATA = @"<OMTSubscribe Metadata=""true"" />";
|
|
||||||
public const string CHANNEL_PREVIEW_VIDEO_ON = @"<OMTSettings Preview=""true"" />";
|
|
||||||
public const string CHANNEL_PREVIEW_VIDEO_OFF = @"<OMTSettings Preview=""false"" />";
|
|
||||||
public const string TALLY_PREVIEW = @"<OMTTally Preview=""true"" Program==""false"" />";
|
|
||||||
public const string TALLY_PROGRAM = @"<OMTTally Preview=""false"" Program==""true"" />";
|
|
||||||
public const string TALLY_PREVIEWPROGRAM = @"<OMTTally Preview=""true"" Program==""true"" />";
|
|
||||||
public const string TALLY_NONE = @"<OMTTally Preview=""false"" Program==""false"" />";
|
|
||||||
}
|
|
||||||
internal class OMTMetadataTemplates
|
|
||||||
{
|
|
||||||
public const string SUGGESTED_QUALITY_PREFIX = @"<OMTSettings Quality=";
|
|
||||||
public const string SUGGESTED_QUALITY = @"<OMTSettings Quality=""Default"" />";
|
|
||||||
public const string SENDER_INFO_NAME = @"OMTInfo";
|
|
||||||
public const string SENDER_INFO_PREFIX = @"<OMTInfo";
|
|
||||||
public const string ADDRESS_NAME = @"OMTAddress";
|
|
||||||
public const string REDIRECT_NAME = @"OMTRedirect";
|
|
||||||
public const string REDIRECT_PREFIX = @"<OMTRedirect";
|
|
||||||
}
|
|
||||||
|
|
||||||
internal class OMTMetadataUtils
|
|
||||||
{
|
|
||||||
public static XmlDocument TryParse(string xml)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
XmlDocument doc = new XmlDocument();
|
|
||||||
doc.LoadXml(xml);
|
|
||||||
return doc;
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
OMTLogging.Write("Invalid XML: " + xml, "OMTMetadata");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
internal class OMTMetadata : OMTFrameBase
|
|
||||||
{
|
|
||||||
public long timestamp;
|
|
||||||
public string XML;
|
|
||||||
public IPEndPoint Endpoint;
|
|
||||||
public OMTMetadata(long timestamp, string xML)
|
|
||||||
{
|
|
||||||
Timestamp = timestamp;
|
|
||||||
XML = xML;
|
|
||||||
}
|
|
||||||
public OMTMetadata(long timestamp, string xML, IPEndPoint endpoint)
|
|
||||||
{
|
|
||||||
Timestamp = timestamp;
|
|
||||||
XML = xML;
|
|
||||||
Endpoint = endpoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override long Timestamp
|
|
||||||
{ get { return timestamp; } set { timestamp = value; } }
|
|
||||||
|
|
||||||
public override OMTFrameType FrameType
|
|
||||||
{ get { return OMTFrameType.Metadata; } }
|
|
||||||
|
|
||||||
public IntPtr ToIntPtr(ref int length)
|
|
||||||
{
|
|
||||||
return OMTUtils.XMLToIntPtr(XML, ref length);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static OMTMetadata FromTally(OMTTally tally)
|
|
||||||
{
|
|
||||||
if (tally.Preview == 0 && tally.Program == 0)
|
|
||||||
{
|
|
||||||
return new OMTMetadata(0, OMTMetadataConstants.TALLY_NONE);
|
|
||||||
}
|
|
||||||
else if (tally.Preview == 1 && tally.Program == 0)
|
|
||||||
{
|
|
||||||
return new OMTMetadata(0, OMTMetadataConstants.TALLY_PREVIEW);
|
|
||||||
}
|
|
||||||
else if (tally.Program == 1 && tally.Preview == 0)
|
|
||||||
{
|
|
||||||
return new OMTMetadata(0, OMTMetadataConstants.TALLY_PROGRAM);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return new OMTMetadata(0, OMTMetadataConstants.TALLY_PREVIEWPROGRAM);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static OMTMetadata FromMediaFrame(OMTMediaFrame metadata)
|
|
||||||
{
|
|
||||||
if (metadata.Data != IntPtr.Zero && metadata.DataLength > 0)
|
|
||||||
{
|
|
||||||
string xml = OMTUtils.IntPtrToXML(metadata.Data, metadata.DataLength);
|
|
||||||
return new OMTMetadata(metadata.Timestamp, xml);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void FreeIntPtr(IntPtr ptr)
|
|
||||||
{
|
|
||||||
OMTUtils.FreeXMLIntPtr(ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,110 +0,0 @@
|
|||||||
/*
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Open Media Transport Contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace libomtnet
|
|
||||||
{
|
|
||||||
public class OMTPlatform
|
|
||||||
{
|
|
||||||
private static OMTPlatform instance;
|
|
||||||
private static object globalLock = new object();
|
|
||||||
private static OMTPlatformType platformType;
|
|
||||||
|
|
||||||
static OMTPlatform()
|
|
||||||
{
|
|
||||||
if (Environment.OSVersion.Platform == PlatformID.Win32NT)
|
|
||||||
{
|
|
||||||
platformType = OMTPlatformType.Win32;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
platformType = OMTPlatformType.Linux;
|
|
||||||
string path = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
|
|
||||||
if (path.Contains("/Containers/Data/Application/"))
|
|
||||||
{
|
|
||||||
platformType = OMTPlatformType.iOS;
|
|
||||||
} else if (Directory.Exists("/System/Applications/Utilities/Terminal.app"))
|
|
||||||
{
|
|
||||||
platformType = OMTPlatformType.MacOS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual string GetLibraryExtension()
|
|
||||||
{
|
|
||||||
return ".dll";
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual string GetMachineName()
|
|
||||||
{
|
|
||||||
return Environment.MachineName.ToUpper();
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual IntPtr OpenLibrary(string filename)
|
|
||||||
{
|
|
||||||
return IntPtr.Zero;
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual string GetStoragePath()
|
|
||||||
{
|
|
||||||
string sz = Environment.GetEnvironmentVariable("OMT_STORAGE_PATH");
|
|
||||||
if (!String.IsNullOrEmpty(sz)) return sz;
|
|
||||||
return Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + Path.DirectorySeparatorChar + "OMT";
|
|
||||||
}
|
|
||||||
public static OMTPlatformType GetPlatformType()
|
|
||||||
{
|
|
||||||
return platformType;
|
|
||||||
}
|
|
||||||
public static OMTPlatform GetInstance()
|
|
||||||
{
|
|
||||||
lock (globalLock)
|
|
||||||
{
|
|
||||||
if (instance == null)
|
|
||||||
{
|
|
||||||
switch (GetPlatformType())
|
|
||||||
{
|
|
||||||
case OMTPlatformType.Win32:
|
|
||||||
instance = new win32.Win32Platform();
|
|
||||||
break;
|
|
||||||
case OMTPlatformType.MacOS:
|
|
||||||
case OMTPlatformType.iOS:
|
|
||||||
instance = new mac.MacPlatform();
|
|
||||||
break;
|
|
||||||
case OMTPlatformType.Linux:
|
|
||||||
instance = new linux.LinuxPlatform();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
instance = new OMTPlatform();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,452 +0,0 @@
|
|||||||
/*
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Open Media Transport Contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Xml;
|
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace libomtnet
|
|
||||||
{
|
|
||||||
[Flags]
|
|
||||||
public enum OMTFrameType
|
|
||||||
{
|
|
||||||
None = 0,
|
|
||||||
Metadata = 1,
|
|
||||||
Video = 2,
|
|
||||||
Audio = 4
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Flags set on video frames:
|
|
||||||
///
|
|
||||||
/// Interlaced: Frames are interlaced
|
|
||||||
///
|
|
||||||
/// Alpha: Frames contain an alpha channel. If this is not set, BGRA will be encoded as BGRX and UYVA will be encoded as UYVY.
|
|
||||||
///
|
|
||||||
/// PreMultiplied: When combined with Alpha, alpha channel is premultiplied, otherwise straight
|
|
||||||
///
|
|
||||||
/// Preview: Frame is a special 1/8th preview frame
|
|
||||||
///
|
|
||||||
/// HighBitDepth: Sender automatically adds this flag for frames encoded using P216 or PA16 pixel formats.
|
|
||||||
///
|
|
||||||
/// Set this manually for VMX1 compressed data where the the frame was originally encoded using P216 or PA16.
|
|
||||||
/// This determines which pixel format is selected on the decode side.
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[Flags]
|
|
||||||
public enum OMTVideoFlags
|
|
||||||
{
|
|
||||||
None = 0,
|
|
||||||
Interlaced = 1,
|
|
||||||
Alpha = 2,
|
|
||||||
PreMultiplied = 4,
|
|
||||||
Preview = 8,
|
|
||||||
HighBitDepth = 16
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Supported Codecs:
|
|
||||||
///
|
|
||||||
/// VMX1 = Fast video codec
|
|
||||||
///
|
|
||||||
/// UYVY = 8-bit YUV format
|
|
||||||
///
|
|
||||||
/// YUY2 = 8-bit YUV format with YUYV pixel order
|
|
||||||
///
|
|
||||||
/// UYVA = 8-bit YUV format immediately followed by an alpha plane
|
|
||||||
///
|
|
||||||
/// NV12 = Planar 4:2:0 YUV format. Y plane followed by interleaved half height U/V plane.
|
|
||||||
///
|
|
||||||
/// YV12 = Planar 4:2:0 YUV format. Y plane followed by half height U and V planes.
|
|
||||||
///
|
|
||||||
/// BGRA = 32bpp RGBA format (Same as ARGB32 on Win32)
|
|
||||||
///
|
|
||||||
/// P216 = Planar 4:2:2 YUV format. 16bit Y plane followed by interlaved 16bit UV plane.
|
|
||||||
///
|
|
||||||
/// PA16 = Same as P216 followed by an additional 16bit alpha plane.
|
|
||||||
///
|
|
||||||
/// FPA1 = Floating-point Planar Audio 32bit
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public enum OMTCodec
|
|
||||||
{
|
|
||||||
VMX1 = 0x31584D56,
|
|
||||||
FPA1 = 0x31415046, //Planar audio
|
|
||||||
UYVY = 0x59565955,
|
|
||||||
YUY2 = 0x32595559,
|
|
||||||
BGRA = 0x41524742,
|
|
||||||
NV12 = 0x3231564E,
|
|
||||||
YV12 = 0x32315659,
|
|
||||||
UYVA = 0x41565955,
|
|
||||||
P216 = 0x36313250,
|
|
||||||
PA16 = 0x36314150
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum OMTPlatformType
|
|
||||||
{
|
|
||||||
Unknown = 0,
|
|
||||||
Win32 = 1,
|
|
||||||
MacOS = 2,
|
|
||||||
Linux = 3,
|
|
||||||
iOS = 4
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Specify the color space of the uncompressed Frame. This is used to determine the color space for YUV<>RGB conversions internally.
|
|
||||||
///
|
|
||||||
/// If undefined, the codec will assume BT601 for heights < 720, BT709 for everything else.
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public enum OMTColorSpace
|
|
||||||
{
|
|
||||||
Undefined = 0,
|
|
||||||
BT601 = 601,
|
|
||||||
BT709 = 709
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Specify the preferred uncompressed video format of decoded frames.
|
|
||||||
///
|
|
||||||
/// UYVY is always the fastest, if no alpha channel is required.
|
|
||||||
///
|
|
||||||
/// UYVYorBGRA will provide BGRA only when alpha channel is present.
|
|
||||||
///
|
|
||||||
/// BGRA will always convert back to BGRA
|
|
||||||
///
|
|
||||||
/// UYVYorUYVA will provide UYVA only when alpha channel is present.
|
|
||||||
///
|
|
||||||
/// UYVYorUYVAorP216orPA16 will provide P216 if sender encoded with high bit depth, or PA16 if sender encoded with high bit depth and alpha. Otherwise same as UYVYorUYVA.
|
|
||||||
///
|
|
||||||
/// P216 To receive only P216 frames
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public enum OMTPreferredVideoFormat
|
|
||||||
{
|
|
||||||
UYVY = 0,
|
|
||||||
UYVYorBGRA = 1,
|
|
||||||
BGRA = 2,
|
|
||||||
UYVYorUYVA = 3,
|
|
||||||
UYVYorUYVAorP216orPA16 = 4,
|
|
||||||
P216 = 5
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Flags to enable certain features on a Receiver:
|
|
||||||
///
|
|
||||||
/// Preview: Receive only a 1/8th preview of the video.
|
|
||||||
///
|
|
||||||
/// IncludeCompressed: Include a copy of the compressed VMX video frames for further processing or recording.
|
|
||||||
///
|
|
||||||
/// CompressedOnly: Include only the compressed VMX video frame without decoding. In this instance DataLength will always be 0.
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[Flags]
|
|
||||||
public enum OMTReceiveFlags
|
|
||||||
{
|
|
||||||
None = 0,
|
|
||||||
Preview = 1,
|
|
||||||
IncludeCompressed = 2,
|
|
||||||
CompressedOnly = 4
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Specify the video encoding quality.
|
|
||||||
///
|
|
||||||
/// If set to Default, the Sender is configured to allow suggestions from all Receivers.
|
|
||||||
///
|
|
||||||
/// The highest suggestion amongst all receivers is then selected.
|
|
||||||
///
|
|
||||||
/// If a Receiver is set to Default, then it will defer the quality to whatever is set amongst other Receivers.
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public enum OMTQuality
|
|
||||||
{
|
|
||||||
Default = 0,
|
|
||||||
Low = 1,
|
|
||||||
Medium = 50,
|
|
||||||
High = 100
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct OMTStatistics
|
|
||||||
{
|
|
||||||
public long BytesSent;
|
|
||||||
public long BytesReceived;
|
|
||||||
public long BytesSentSinceLast;
|
|
||||||
public long BytesReceivedSinceLast;
|
|
||||||
|
|
||||||
public long Frames;
|
|
||||||
public long FramesSinceLast;
|
|
||||||
public long FramesDropped;
|
|
||||||
|
|
||||||
public long CodecTime;
|
|
||||||
public long CodecTimeSinceLast;
|
|
||||||
|
|
||||||
public void ToIntPtr(IntPtr ptr)
|
|
||||||
{
|
|
||||||
if (ptr != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
Marshal.WriteInt64(ptr, BytesSent);
|
|
||||||
Marshal.WriteInt64(ptr + 8, BytesReceived);
|
|
||||||
Marshal.WriteInt64(ptr + 16, BytesSentSinceLast);
|
|
||||||
Marshal.WriteInt64(ptr + 24, BytesReceivedSinceLast);
|
|
||||||
Marshal.WriteInt64(ptr + 32, Frames);
|
|
||||||
Marshal.WriteInt64(ptr + 40, FramesSinceLast);
|
|
||||||
Marshal.WriteInt64(ptr + 48, FramesDropped);
|
|
||||||
Marshal.WriteInt64(ptr + 56, CodecTime);
|
|
||||||
Marshal.WriteInt64(ptr + 64, CodecTimeSinceLast);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class OMTSenderInfo
|
|
||||||
{
|
|
||||||
public string ProductName;
|
|
||||||
public string Manufacturer;
|
|
||||||
public string Version;
|
|
||||||
|
|
||||||
public OMTSenderInfo() { }
|
|
||||||
public OMTSenderInfo(string productName, string manufacturer, string version)
|
|
||||||
{
|
|
||||||
ProductName = productName;
|
|
||||||
Manufacturer = manufacturer;
|
|
||||||
Version = version;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string ToXML()
|
|
||||||
{
|
|
||||||
StringWriter sw = new StringWriter();
|
|
||||||
XmlTextWriter t = new XmlTextWriter(sw);
|
|
||||||
t.Formatting = Formatting.Indented;
|
|
||||||
t.WriteStartElement(OMTMetadataTemplates.SENDER_INFO_NAME);
|
|
||||||
t.WriteAttributeString("ProductName", ProductName);
|
|
||||||
t.WriteAttributeString("Manufacturer", Manufacturer);
|
|
||||||
t.WriteAttributeString("Version", Version);
|
|
||||||
t.WriteEndElement();
|
|
||||||
t.Close();
|
|
||||||
return sw.ToString();
|
|
||||||
}
|
|
||||||
public static OMTSenderInfo FromXML(string xml)
|
|
||||||
{
|
|
||||||
XmlDocument doc = OMTMetadataUtils.TryParse(xml);
|
|
||||||
if (doc != null)
|
|
||||||
{
|
|
||||||
XmlNode e = doc.DocumentElement;
|
|
||||||
if (e != null)
|
|
||||||
{
|
|
||||||
OMTSenderInfo senderInfo = new OMTSenderInfo();
|
|
||||||
XmlNode a = e.Attributes.GetNamedItem("ProductName");
|
|
||||||
if (a != null) senderInfo.ProductName = a.InnerText;
|
|
||||||
a = e.Attributes.GetNamedItem("Manufacturer");
|
|
||||||
if (a != null) senderInfo.Manufacturer = a.InnerText;
|
|
||||||
a = e.Attributes.GetNamedItem("Version");
|
|
||||||
if (a != null) senderInfo.Version = a.InnerText;
|
|
||||||
return senderInfo;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Stores one frame of Video, Audio or Metadata
|
|
||||||
/// </summary>
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
public struct OMTMediaFrame
|
|
||||||
{
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Specify the type of frame. This determines which values of this struct are valid/used.
|
|
||||||
/// </summary>
|
|
||||||
public OMTFrameType Type;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// This is a timestamp where 1 second = 10,000,000
|
|
||||||
///
|
|
||||||
/// This should not be left 0 unless this is the very first frame.
|
|
||||||
///
|
|
||||||
/// This should represent the accurate time the frame or audio sample was generated at the original source and be used on the receiving end to synchronize
|
|
||||||
/// and record to file as a presentation timestamp (pts).
|
|
||||||
///
|
|
||||||
/// A special value of -1 can be specified to tell the Sender to generate timestamps and throttle as required to maintain
|
|
||||||
/// the specified FrameRate or SampleRate of the frame.
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public long Timestamp;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sending:
|
|
||||||
///
|
|
||||||
/// Video: 'UYVY', 'YUY2', 'NV12', 'YV12, 'BGRA', 'UYVA', 'VMX1' are supported (BGRA will be treated as BGRX and UYVA as UYVY where alpha flags are not set)
|
|
||||||
///
|
|
||||||
/// Audio: Only 'FPA1' is supported (32bit floating point planar audio)
|
|
||||||
///
|
|
||||||
/// Receiving:
|
|
||||||
///
|
|
||||||
/// Video: Only 'UYVY', 'UYVA', 'BGRA' and 'BGRX' are supported
|
|
||||||
///
|
|
||||||
/// Audio: Only 'FPA1' is supported (32bit floating point planar audio)
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public int Codec;
|
|
||||||
|
|
||||||
//Video Properties
|
|
||||||
public int Width;
|
|
||||||
public int Height;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Stride in bytes of each row of pixels. Typically width*2 for UYVY, width*4 for BGRA and just width for planar formats.
|
|
||||||
/// </summary>
|
|
||||||
public int Stride;
|
|
||||||
|
|
||||||
public OMTVideoFlags Flags;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Frame Rate Numerator/Denominator in Frames Per Second, for example Numerator 60 and Denominator 1 is 60 frames per second.
|
|
||||||
/// </summary>
|
|
||||||
public int FrameRateN;
|
|
||||||
public int FrameRateD;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Display aspect ratio expressed as a ratio of width/height. For example 1.777777777777778 for 16/9
|
|
||||||
/// </summary>
|
|
||||||
public float AspectRatio;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Color space of the frame. If undefined a height < 720 is BT601 and everything else BT709
|
|
||||||
/// </summary>
|
|
||||||
public OMTColorSpace ColorSpace;
|
|
||||||
|
|
||||||
//Audio Properties
|
|
||||||
// Sample rate, i.e 48000, 44100 etc
|
|
||||||
public int SampleRate;
|
|
||||||
// Audio Channels. A maximum of 32 channels are supported.
|
|
||||||
public int Channels;
|
|
||||||
// Number of 32bit floating point samples per channel/plane. Each plane should contain SamplesPerChannel*4 bytes.
|
|
||||||
public int SamplesPerChannel;
|
|
||||||
|
|
||||||
//Data Properties
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Video: Uncompressed pixel data (or compressed VMX1 data when sending and Codec set to VMX1)
|
|
||||||
///
|
|
||||||
/// Audio: Planar 32bit floating point audio
|
|
||||||
///
|
|
||||||
/// Metadata: UTF-8 encoded XML string with terminating null character
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public IntPtr Data;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Video: Number of bytes total including stride
|
|
||||||
///
|
|
||||||
/// Audio: Number of bytes (SamplesPerChannel * Channels * 4)
|
|
||||||
///
|
|
||||||
/// Metadata: Number of bytes in UTF-8 encoded string + 1 for terminating null character.
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public int DataLength;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Receive only. Use standard Data/DataLength if sending VMX1 frames with a Sender
|
|
||||||
///
|
|
||||||
/// If IncludeCompressed or CompressedOnly OMTReceiveFlags is set, this will include the original compressed video frame in VMX1 format.
|
|
||||||
///
|
|
||||||
/// This could then be muxed into an AVI or MOV file using FFmpeg or similar APIs
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public IntPtr CompressedData;
|
|
||||||
public int CompressedLength;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Per frame metadata as UTF-8 encoded string + 1 for null character. Up to 65536 bytes supported.
|
|
||||||
/// </summary>
|
|
||||||
public IntPtr FrameMetadata;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Length in bytes of per frame metadata including null character
|
|
||||||
/// </summary>
|
|
||||||
public int FrameMetadataLength;
|
|
||||||
|
|
||||||
public static IntPtr ToIntPtr(OMTMediaFrame frame)
|
|
||||||
{
|
|
||||||
IntPtr dst = Marshal.AllocHGlobal(Marshal.SizeOf(frame));
|
|
||||||
Marshal.StructureToPtr(frame, dst, false);
|
|
||||||
return dst;
|
|
||||||
}
|
|
||||||
public static void FreeIntPtr(IntPtr ptr)
|
|
||||||
{
|
|
||||||
if (ptr != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
Marshal.FreeHGlobal(ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public static OMTMediaFrame FromIntPtr(IntPtr ptr)
|
|
||||||
{
|
|
||||||
return (OMTMediaFrame)Marshal.PtrToStructure(ptr, typeof(OMTMediaFrame));
|
|
||||||
}
|
|
||||||
|
|
||||||
public float FrameRate { get {
|
|
||||||
return OMTUtils.ToFrameRate(FrameRateN, FrameRateD);
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
OMTUtils.FromFrameRate(value,ref FrameRateN,ref FrameRateD);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct OMTSize
|
|
||||||
{
|
|
||||||
public int Width;
|
|
||||||
public int Height;
|
|
||||||
public OMTSize(int width, int height)
|
|
||||||
{
|
|
||||||
Width = width;
|
|
||||||
Height = height;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Tally where 0 = 0 off, 1 = on.
|
|
||||||
/// </summary>
|
|
||||||
public struct OMTTally
|
|
||||||
{
|
|
||||||
public int Preview;
|
|
||||||
public int Program;
|
|
||||||
public OMTTally(int preview, int program)
|
|
||||||
{
|
|
||||||
this.Preview = preview;
|
|
||||||
this.Program = program;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return "Preview: " + Preview + " Program: " + Program;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,204 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Xml;
|
|
||||||
|
|
||||||
namespace libomtnet
|
|
||||||
{
|
|
||||||
internal class OMTRedirect : OMTBase
|
|
||||||
{
|
|
||||||
private string redirectAddress = null;
|
|
||||||
private string redirectAddressUpstream = null;
|
|
||||||
private OMTReceive redirectConnection = null;
|
|
||||||
private object redirectLock = new object();
|
|
||||||
|
|
||||||
private OMTSend sender = null;
|
|
||||||
private string originalAddress = null;
|
|
||||||
private OMTReceive receiver = null;
|
|
||||||
|
|
||||||
public OMTRedirect(OMTSend sender)
|
|
||||||
{
|
|
||||||
this.sender = sender;
|
|
||||||
this.originalAddress = sender.Address;
|
|
||||||
}
|
|
||||||
|
|
||||||
public OMTRedirect(OMTReceive receiver)
|
|
||||||
{
|
|
||||||
this.receiver = receiver;
|
|
||||||
this.originalAddress = receiver.Address;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ClearRedirectConnection()
|
|
||||||
{
|
|
||||||
if (redirectConnection != null)
|
|
||||||
{
|
|
||||||
redirectAddressUpstream = null;
|
|
||||||
redirectConnection.RedirectChanged -= OnRedirectChanged;
|
|
||||||
redirectConnection.Dispose();
|
|
||||||
redirectConnection = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private OMTMetadata CreateRedirectMetadata()
|
|
||||||
{
|
|
||||||
string address = this.redirectAddress;
|
|
||||||
if (!String.IsNullOrEmpty(this.redirectAddressUpstream))
|
|
||||||
{
|
|
||||||
address = this.redirectAddressUpstream;
|
|
||||||
}
|
|
||||||
string xml = OMTRedirect.ToXML(address);
|
|
||||||
return new OMTMetadata(0, xml);
|
|
||||||
}
|
|
||||||
private void SendRedirect()
|
|
||||||
{
|
|
||||||
if (sender != null)
|
|
||||||
{
|
|
||||||
OMTMetadata metadata = CreateRedirectMetadata();
|
|
||||||
sender.SendMetadata(metadata, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnNewConnection(OMTChannel ch)
|
|
||||||
{
|
|
||||||
OMTMetadata m = CreateRedirectMetadata();
|
|
||||||
int result = ch.Send(m);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnReceiveChanged()
|
|
||||||
{
|
|
||||||
lock (redirectLock)
|
|
||||||
{
|
|
||||||
if (Exiting) return;
|
|
||||||
string newAddress = receiver.RedirectAddress;
|
|
||||||
this.redirectAddress = newAddress;
|
|
||||||
CreateRedirectConnection(newAddress, originalAddress);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void CheckConnection()
|
|
||||||
{
|
|
||||||
if (redirectConnection != null)
|
|
||||||
{
|
|
||||||
redirectConnection.CheckConnection();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CreateRedirectConnection(string newAddress, string sideChannelAddress)
|
|
||||||
{
|
|
||||||
if (String.IsNullOrEmpty(newAddress))
|
|
||||||
{
|
|
||||||
OMTLogging.Write("Redirect stopped for " + originalAddress, "OMTRedirect");
|
|
||||||
ClearRedirectConnection();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
OMTLogging.Write("Redirecting " + originalAddress + " to " + newAddress + " and monitoring for updates from " + sideChannelAddress, "OMTRedirect");
|
|
||||||
if (redirectConnection != null)
|
|
||||||
{
|
|
||||||
if (redirectConnection.Address != sideChannelAddress)
|
|
||||||
{
|
|
||||||
ClearRedirectConnection();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (redirectConnection == null)
|
|
||||||
{
|
|
||||||
redirectConnection = new OMTReceive(sideChannelAddress, OMTFrameType.Metadata, OMTPreferredVideoFormat.UYVY, OMTReceiveFlags.None);
|
|
||||||
redirectConnection.redirectMetadataOnly = true;
|
|
||||||
redirectConnection.RedirectChanged += OnRedirectChanged;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetRedirect(string newAddress)
|
|
||||||
{
|
|
||||||
lock (redirectLock)
|
|
||||||
{
|
|
||||||
if (Exiting) return;
|
|
||||||
if (this.originalAddress == newAddress)
|
|
||||||
{
|
|
||||||
newAddress = null; //No redirect in case of loopback
|
|
||||||
}
|
|
||||||
if (this.redirectAddress != newAddress)
|
|
||||||
{
|
|
||||||
this.redirectAddressUpstream = null;
|
|
||||||
}
|
|
||||||
this.redirectAddress = newAddress;
|
|
||||||
SendRedirect();
|
|
||||||
CreateRedirectConnection(newAddress, newAddress);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private void OnRedirectChanged(object sender, OMTRedirectChangedEventArgs e)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
//This is called by the Receive_Completed on the channel own by this object's receiver,
|
|
||||||
//so care needs to be taken to ensure this does not go back into that receiver where that lock may be used.
|
|
||||||
if (Exiting) return;
|
|
||||||
if (redirectConnection != null)
|
|
||||||
{
|
|
||||||
string newAddress = e.NewAddress;
|
|
||||||
if (newAddress != originalAddress)
|
|
||||||
{
|
|
||||||
if (newAddress != redirectAddress)
|
|
||||||
{
|
|
||||||
if (this.sender != null)
|
|
||||||
{
|
|
||||||
if (newAddress != redirectAddressUpstream)
|
|
||||||
{
|
|
||||||
this.redirectAddressUpstream = newAddress;
|
|
||||||
OMTLogging.Write("Redirect changed upstream for " + originalAddress + " to " + newAddress, "OMTRedirect");
|
|
||||||
SendRedirect();
|
|
||||||
}
|
|
||||||
} else if (this.receiver != null)
|
|
||||||
{
|
|
||||||
this.redirectAddress = newAddress;
|
|
||||||
receiver.OnRedirectConnection(newAddress);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "OMTRedirect");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string FromXML(string xml)
|
|
||||||
{
|
|
||||||
XmlDocument doc = OMTMetadataUtils.TryParse(xml);
|
|
||||||
if (doc != null)
|
|
||||||
{
|
|
||||||
if (doc.DocumentElement != null)
|
|
||||||
{
|
|
||||||
XmlNode a = doc.DocumentElement.Attributes.GetNamedItem("NewAddress");
|
|
||||||
if (a != null)
|
|
||||||
{
|
|
||||||
return a.InnerText;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
public static string ToXML(string address)
|
|
||||||
{
|
|
||||||
using (StringWriter sw = new StringWriter())
|
|
||||||
{
|
|
||||||
using (XmlTextWriter t = new XmlTextWriter(sw))
|
|
||||||
{
|
|
||||||
t.Formatting = Formatting.Indented;
|
|
||||||
t.WriteStartElement(OMTMetadataTemplates.REDIRECT_NAME);
|
|
||||||
t.WriteAttributeString("NewAddress", address);
|
|
||||||
t.WriteEndElement();
|
|
||||||
return sw.ToString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void DisposeInternal()
|
|
||||||
{
|
|
||||||
lock (redirectLock) { }
|
|
||||||
sender = null;
|
|
||||||
ClearRedirectConnection();
|
|
||||||
base.DisposeInternal();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,829 +0,0 @@
|
|||||||
/*
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Open Media Transport Contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Net;
|
|
||||||
using System.Net.Sockets;
|
|
||||||
using libomtnet.codecs;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
|
||||||
using System.Xml;
|
|
||||||
|
|
||||||
namespace libomtnet
|
|
||||||
{
|
|
||||||
public class OMTSend : OMTSendReceiveBase
|
|
||||||
{
|
|
||||||
private readonly OMTAddress address;
|
|
||||||
private Socket listener;
|
|
||||||
private OMTChannel[] channels = { };
|
|
||||||
private object channelsLock = new object();
|
|
||||||
private OMTDiscovery discovery;
|
|
||||||
private OMTDiscoveryServer discoveryServer;
|
|
||||||
|
|
||||||
private OMTFrame tempVideo;
|
|
||||||
private OMTFrame tempAudio;
|
|
||||||
private OMTBuffer tempAudioBuffer;
|
|
||||||
private OMTVMX1Codec codec = null;
|
|
||||||
private OMTQuality quality = OMTQuality.Default;
|
|
||||||
private SocketAsyncEventArgs listenEvent;
|
|
||||||
|
|
||||||
private OMTQuality suggestedQuality = OMTQuality.Default;
|
|
||||||
private string senderInfoXml = null;
|
|
||||||
private List<string> connectionMetadata = new List<string>();
|
|
||||||
|
|
||||||
private OMTClock videoClock;
|
|
||||||
private OMTClock audioClock;
|
|
||||||
|
|
||||||
private bool metadataServer = false;
|
|
||||||
|
|
||||||
internal OMTSend(IPEndPoint endpoint, OMTDiscoveryServer discoveryServer)
|
|
||||||
{
|
|
||||||
this.metadataServer = true;
|
|
||||||
this.discoveryServer = discoveryServer;
|
|
||||||
metadataHandle = new AutoResetEvent(false);
|
|
||||||
listenEvent = new SocketAsyncEventArgs();
|
|
||||||
listenEvent.Completed += OnAccept;
|
|
||||||
this.listener = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp);
|
|
||||||
this.listener.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, false);
|
|
||||||
this.listener.Bind(endpoint);
|
|
||||||
this.listener.Listen(5);
|
|
||||||
BeginAccept();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create a new instance of the OMT Sender
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="name">Specify the name of the source not including hostname</param>
|
|
||||||
/// <param name="quality"> Specify the quality to use for video encoding. If Default, this can be automatically adjusted based on Receiver requirements.</param>
|
|
||||||
public OMTSend(string name, OMTQuality quality)
|
|
||||||
{
|
|
||||||
videoClock = new OMTClock(false);
|
|
||||||
audioClock = new OMTClock(true);
|
|
||||||
metadataHandle = new AutoResetEvent(false);
|
|
||||||
tallyHandle = new AutoResetEvent(false);
|
|
||||||
listenEvent = new SocketAsyncEventArgs();
|
|
||||||
listenEvent.Completed += OnAccept;
|
|
||||||
tempVideo = new OMTFrame(OMTFrameType.Video, new OMTBuffer(OMTConstants.VIDEO_MIN_SIZE, true));
|
|
||||||
tempAudio = new OMTFrame(OMTFrameType.Audio, new OMTBuffer(OMTConstants.AUDIO_MIN_SIZE, true));
|
|
||||||
tempAudioBuffer = new OMTBuffer(OMTConstants.AUDIO_MIN_SIZE, true);
|
|
||||||
this.discovery = OMTDiscovery.GetInstance();
|
|
||||||
this.quality = quality;
|
|
||||||
this.suggestedQuality = quality;
|
|
||||||
this.listener = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp);
|
|
||||||
|
|
||||||
OMTSettings settings = OMTSettings.GetInstance();
|
|
||||||
int startPort = settings.GetInteger("NetworkPortStart", OMTConstants.NETWORK_PORT_START);
|
|
||||||
int endPort = settings.GetInteger("NetworkPortEnd", OMTConstants.NETWORK_PORT_END);
|
|
||||||
|
|
||||||
for (int i = startPort; i <= endPort; i++)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
this.listener.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, false);
|
|
||||||
this.listener.Bind(new IPEndPoint(IPAddress.IPv6Any, i));
|
|
||||||
this.listener.Listen(5);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
catch (SocketException se)
|
|
||||||
{
|
|
||||||
if (se.SocketErrorCode != SocketError.AddressAlreadyInUse | i == OMTConstants.NETWORK_PORT_END)
|
|
||||||
{
|
|
||||||
throw se;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BeginAccept();
|
|
||||||
IPEndPoint ip = (IPEndPoint)this.listener.LocalEndPoint;
|
|
||||||
this.address = new OMTAddress(name, ip.Port);
|
|
||||||
this.address.AddAddress(IPAddress.Loopback);
|
|
||||||
this.discovery.RegisterAddress(address); }
|
|
||||||
|
|
||||||
public override OMTStatistics GetVideoStatistics()
|
|
||||||
{
|
|
||||||
OMTChannel[] ch = channels;
|
|
||||||
if (ch != null)
|
|
||||||
{
|
|
||||||
foreach (OMTChannel c in ch)
|
|
||||||
{
|
|
||||||
if (c.IsVideo() && c.Connected)
|
|
||||||
{
|
|
||||||
OMTStatistics s = c.GetStatistics();
|
|
||||||
UpdateCodecTimerStatistics(ref s);
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return base.GetVideoStatistics();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override OMTStatistics GetAudioStatistics()
|
|
||||||
{
|
|
||||||
OMTChannel[] ch = channels;
|
|
||||||
if (ch != null)
|
|
||||||
{
|
|
||||||
foreach (OMTChannel c in ch)
|
|
||||||
{
|
|
||||||
if (c.IsAudio() && c.Connected)
|
|
||||||
{
|
|
||||||
return c.GetStatistics();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return base.GetAudioStatistics();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Port { get { return this.address.Port; } }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Specify information to describe the Sender to any Receivers
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="senderInfo"></param>
|
|
||||||
public void SetSenderInformation(OMTSenderInfo senderInfo)
|
|
||||||
{
|
|
||||||
if (senderInfo == null)
|
|
||||||
{
|
|
||||||
this.senderInfoXml = null;
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
this.senderInfoXml = senderInfo.ToXML();
|
|
||||||
SendMetadata(new OMTMetadata(0, this.senderInfoXml), null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SendConnectionMetadata()
|
|
||||||
{
|
|
||||||
lock (connectionMetadata)
|
|
||||||
{
|
|
||||||
foreach (string metadata in connectionMetadata)
|
|
||||||
{
|
|
||||||
if (!String.IsNullOrEmpty(metadata))
|
|
||||||
{
|
|
||||||
SendMetadata(new OMTMetadata(0, metadata), null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private void SendConnectionMetadata(OMTChannel ch)
|
|
||||||
{
|
|
||||||
lock (connectionMetadata)
|
|
||||||
{
|
|
||||||
foreach (string metadata in connectionMetadata)
|
|
||||||
{
|
|
||||||
if (!String.IsNullOrEmpty(metadata))
|
|
||||||
{
|
|
||||||
ch.Send(new OMTMetadata(0, metadata));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddConnectionMetadata(string xml)
|
|
||||||
{
|
|
||||||
lock (connectionMetadata)
|
|
||||||
{
|
|
||||||
connectionMetadata.Add(xml);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ClearConnectionMetadata()
|
|
||||||
{
|
|
||||||
lock (connectionMetadata)
|
|
||||||
{
|
|
||||||
connectionMetadata.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Use this to inform receivers to connect to a different address.
|
|
||||||
///
|
|
||||||
/// This is used to create a "virtual source" that can be dynamically switched as needed.
|
|
||||||
///
|
|
||||||
/// This is useful for scenarios where receiver needs to be changed remotely.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="newAddress">The new address. Set to null or empty to disable redirect.</param>
|
|
||||||
public void SetRedirect(string newAddress)
|
|
||||||
{
|
|
||||||
if (redirect == null) redirect = new OMTRedirect(this);
|
|
||||||
redirect.SetRedirect(newAddress);
|
|
||||||
}
|
|
||||||
protected override void DisposeInternal()
|
|
||||||
{
|
|
||||||
if (tallyHandle != null)
|
|
||||||
{
|
|
||||||
tallyHandle.Set();
|
|
||||||
}
|
|
||||||
if (metadataHandle != null)
|
|
||||||
{
|
|
||||||
metadataHandle.Set();
|
|
||||||
}
|
|
||||||
if (videoClock != null)
|
|
||||||
{
|
|
||||||
videoClock.Dispose();
|
|
||||||
}
|
|
||||||
if (audioClock != null)
|
|
||||||
{
|
|
||||||
audioClock.Dispose();
|
|
||||||
}
|
|
||||||
lock (videoLock) { }
|
|
||||||
lock (audioLock) { }
|
|
||||||
lock (metaLock) { }
|
|
||||||
if (redirect != null)
|
|
||||||
{
|
|
||||||
redirect.Dispose();
|
|
||||||
redirect = null;
|
|
||||||
}
|
|
||||||
if (discovery != null)
|
|
||||||
{
|
|
||||||
discovery.DeregisterAddress(address);
|
|
||||||
discovery = null;
|
|
||||||
}
|
|
||||||
discoveryServer = null;
|
|
||||||
if (listener != null)
|
|
||||||
{
|
|
||||||
listener.Dispose();
|
|
||||||
listener = null;
|
|
||||||
}
|
|
||||||
if (listenEvent != null)
|
|
||||||
{
|
|
||||||
listenEvent.Completed -= OnAccept;
|
|
||||||
listenEvent.Dispose();
|
|
||||||
listenEvent = null;
|
|
||||||
}
|
|
||||||
lock (channelsLock)
|
|
||||||
{
|
|
||||||
if (channels != null)
|
|
||||||
{
|
|
||||||
foreach (OMTChannel channel in channels)
|
|
||||||
{
|
|
||||||
if (channel != null)
|
|
||||||
{
|
|
||||||
channel.Changed -= Channel_Changed;
|
|
||||||
channel.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
channels = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (codec != null)
|
|
||||||
{
|
|
||||||
codec.Dispose();
|
|
||||||
codec = null;
|
|
||||||
}
|
|
||||||
discovery = null;
|
|
||||||
OMTMetadata.FreeIntPtr(lastMetadata);
|
|
||||||
lastMetadata = IntPtr.Zero;
|
|
||||||
if (metadataHandle != null)
|
|
||||||
{
|
|
||||||
metadataHandle.Close();
|
|
||||||
metadataHandle = null;
|
|
||||||
}
|
|
||||||
if (tallyHandle != null)
|
|
||||||
{
|
|
||||||
tallyHandle.Close();
|
|
||||||
tallyHandle = null;
|
|
||||||
}
|
|
||||||
if (tempVideo != null)
|
|
||||||
{
|
|
||||||
tempVideo.Dispose();
|
|
||||||
tempVideo = null;
|
|
||||||
}
|
|
||||||
if (tempAudio != null)
|
|
||||||
{
|
|
||||||
tempAudio.Dispose();
|
|
||||||
tempAudio = null;
|
|
||||||
}
|
|
||||||
if (tempAudioBuffer != null)
|
|
||||||
{
|
|
||||||
tempAudioBuffer.Dispose();
|
|
||||||
tempAudioBuffer = null;
|
|
||||||
}
|
|
||||||
base.DisposeInternal();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Discovery address in the format HOSTNAME (NAME)
|
|
||||||
/// </summary>
|
|
||||||
public string Address { get { return address.ToString(); } }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Direct connection address in the format omt://hostname:port
|
|
||||||
/// </summary>
|
|
||||||
public string URL { get { return address.ToURL(); } }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Total number of connections to this sender. Receivers establish one connection for video/metadata and a second for audio.
|
|
||||||
/// </summary>
|
|
||||||
public int Connections { get {
|
|
||||||
|
|
||||||
OMTChannel[] ch = channels;
|
|
||||||
if (ch != null)
|
|
||||||
{
|
|
||||||
return ch.Length;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
} }
|
|
||||||
|
|
||||||
private void OnAccept(object sender, SocketAsyncEventArgs e)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (e.SocketError == SocketError.Success)
|
|
||||||
{
|
|
||||||
Socket socket = null;
|
|
||||||
OMTChannel channel = null;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
socket = e.AcceptSocket;
|
|
||||||
channel = new OMTChannel(socket, OMTFrameType.Metadata, null, metadataHandle, metadataServer);
|
|
||||||
channel.StartReceive();
|
|
||||||
if (senderInfoXml != null)
|
|
||||||
{
|
|
||||||
channel.Send(new OMTMetadata(0, senderInfoXml));
|
|
||||||
}
|
|
||||||
SendConnectionMetadata(channel);
|
|
||||||
channel.Send(OMTMetadata.FromTally(lastTally));
|
|
||||||
if (redirect != null)
|
|
||||||
{
|
|
||||||
redirect.OnNewConnection(channel);
|
|
||||||
}
|
|
||||||
OMTLogging.Write("AddConnection: " + socket.RemoteEndPoint.ToString(), "OMTSend.BeginAccept");
|
|
||||||
AddChannel(channel);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "OMTSend.BeginAccept");
|
|
||||||
if (channel != null)
|
|
||||||
{
|
|
||||||
channel.Changed -= Channel_Changed;
|
|
||||||
channel.Dispose();
|
|
||||||
}
|
|
||||||
if (socket != null)
|
|
||||||
{
|
|
||||||
socket.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!Exiting)
|
|
||||||
{
|
|
||||||
BeginAccept();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "OMTSend.OnAccept");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private void BeginAccept()
|
|
||||||
{
|
|
||||||
Socket listener = this.listener;
|
|
||||||
if (listener != null)
|
|
||||||
{
|
|
||||||
listenEvent.AcceptSocket = null;
|
|
||||||
if (this.listener.AcceptAsync(listenEvent) == false) {
|
|
||||||
OnAccept(this.listener, listenEvent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
internal void AddChannel(OMTChannel channel)
|
|
||||||
{
|
|
||||||
lock (channelsLock)
|
|
||||||
{
|
|
||||||
List<OMTChannel> list = new List<OMTChannel>();
|
|
||||||
list.AddRange(channels);
|
|
||||||
list.Add(channel);
|
|
||||||
channels = list.ToArray();
|
|
||||||
}
|
|
||||||
channel.Changed += Channel_Changed;
|
|
||||||
UpdateTally();
|
|
||||||
if (discoveryServer != null)
|
|
||||||
{
|
|
||||||
discoveryServer.Connected(channel.RemoteEndPoint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
internal bool RemoveChannel(OMTChannel channel)
|
|
||||||
{
|
|
||||||
lock (channelsLock)
|
|
||||||
{
|
|
||||||
if (channel != null)
|
|
||||||
{
|
|
||||||
List<OMTChannel> list = new List<OMTChannel>();
|
|
||||||
list.AddRange(channels);
|
|
||||||
if (list.Contains(channel))
|
|
||||||
{
|
|
||||||
list.Remove(channel);
|
|
||||||
channels = list.ToArray();
|
|
||||||
channel.Changed -= Channel_Changed;
|
|
||||||
channel.Dispose();
|
|
||||||
OMTLogging.Write("RemoveConnection", "OMTSend.RemoveChannel");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets the video encoding quality from the next frame. If set to Default will defer to the suggested quality amongst receivers. See OMTQuality for more details.
|
|
||||||
/// </summary>
|
|
||||||
public OMTQuality Quality { get { return quality; } set {
|
|
||||||
quality = value;
|
|
||||||
if (quality != OMTQuality.Default)
|
|
||||||
{
|
|
||||||
suggestedQuality = quality;
|
|
||||||
}
|
|
||||||
} }
|
|
||||||
|
|
||||||
internal override void OnTallyChanged(OMTTally tally)
|
|
||||||
{
|
|
||||||
SendMetadata(OMTMetadata.FromTally(tally),null);
|
|
||||||
}
|
|
||||||
internal override void OnDisconnected(OMTChannel ch)
|
|
||||||
{
|
|
||||||
if (ch != null)
|
|
||||||
{
|
|
||||||
if (RemoveChannel(ch))
|
|
||||||
{
|
|
||||||
if (discoveryServer != null)
|
|
||||||
{
|
|
||||||
discoveryServer.Disconnected(ch.RemoteEndPoint);
|
|
||||||
}
|
|
||||||
UpdateTally();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal int Send(OMTFrame frame)
|
|
||||||
{
|
|
||||||
int len = 0;
|
|
||||||
OMTQuality suggested = OMTQuality.Default;
|
|
||||||
OMTChannel[] channels = this.channels;
|
|
||||||
if (channels != null)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < channels.Length; i++)
|
|
||||||
{
|
|
||||||
if (channels[i].Connected)
|
|
||||||
{
|
|
||||||
len += channels[i].Send(frame);
|
|
||||||
if (channels[i].IsVideo())
|
|
||||||
{
|
|
||||||
if (channels[i].SuggestedQuality > suggested)
|
|
||||||
{
|
|
||||||
suggested = channels[i].SuggestedQuality;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (quality == OMTQuality.Default)
|
|
||||||
{
|
|
||||||
suggestedQuality = suggested;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
private void CreateCodec(int width, int height, int framesPerSecond, VMXColorSpace colorSpace)
|
|
||||||
{
|
|
||||||
VMXProfile prof = VMXProfile.Default;
|
|
||||||
if (suggestedQuality != OMTQuality.Default)
|
|
||||||
{
|
|
||||||
if (suggestedQuality >= OMTQuality.Low) prof = VMXProfile.OMT_LQ;
|
|
||||||
if (suggestedQuality >= OMTQuality.Medium) prof = VMXProfile.OMT_SQ;
|
|
||||||
if (suggestedQuality >= OMTQuality.High) prof = VMXProfile.OMT_HQ;
|
|
||||||
}
|
|
||||||
if (codec == null)
|
|
||||||
{
|
|
||||||
codec = new OMTVMX1Codec(width, height, framesPerSecond, prof, colorSpace);
|
|
||||||
}
|
|
||||||
else if (codec.Width != width || codec.Height != height || codec.Profile != prof || codec.ColorSpace != colorSpace || codec.FramesPerSecond != framesPerSecond)
|
|
||||||
{
|
|
||||||
int lastQuality = codec.GetQuality();
|
|
||||||
codec.Dispose();
|
|
||||||
codec = new OMTVMX1Codec(width, height, framesPerSecond, prof, colorSpace);
|
|
||||||
codec.SetQuality(lastQuality); //Preserve the last quality in cases of profile change, so there isn't a temporarily drop in quality.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Receive any available metadata in the buffer, or wait for metadata if empty
|
|
||||||
///
|
|
||||||
/// Returns true if metadata was found, false of timed out
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="millisecondsTimeout">The maximum time to wait for a new frame if empty</param>
|
|
||||||
/// <param name="outFrame">The frame struct to fill with the received data</param>
|
|
||||||
public bool Receive(int millisecondsTimeout, ref OMTMediaFrame outFrame)
|
|
||||||
{
|
|
||||||
OMTMetadata metadata = null;
|
|
||||||
if (Receive(millisecondsTimeout, ref metadata))
|
|
||||||
{
|
|
||||||
return ReceiveMetadata(metadata, ref outFrame);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
internal bool Receive(int millisecondsTimeout, ref OMTMetadata metadata)
|
|
||||||
{
|
|
||||||
lock (metaLock)
|
|
||||||
{
|
|
||||||
if (Exiting) return false;
|
|
||||||
if (ReceiveInternal(ref metadata)) return true;
|
|
||||||
for (int i = 0; i < 2; i++)
|
|
||||||
{
|
|
||||||
if (metadataHandle.WaitOne(millisecondsTimeout) == false) return false;
|
|
||||||
if (Exiting) return false;
|
|
||||||
if (ReceiveInternal(ref metadata)) return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool ReceiveInternal(ref OMTMetadata frame)
|
|
||||||
{
|
|
||||||
OMTChannel[] channels = this.channels;
|
|
||||||
for (int i = 0; i < channels.Length; i++)
|
|
||||||
{
|
|
||||||
OMTChannel ch = channels[i];
|
|
||||||
if (ch != null)
|
|
||||||
{
|
|
||||||
if (ch.ReadyMetadataCount > 0)
|
|
||||||
{
|
|
||||||
frame = ch.ReceiveMetadata();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal override OMTTally GetTallyInternal()
|
|
||||||
{
|
|
||||||
OMTTally tally = new OMTTally();
|
|
||||||
OMTChannel[] channels = this.channels;
|
|
||||||
if (channels != null)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < channels.Length; i++)
|
|
||||||
{
|
|
||||||
OMTTally t = channels[i].GetTally();
|
|
||||||
if (t.Program == 1) tally.Program = 1;
|
|
||||||
if (t.Preview == 1) tally.Preview = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return tally;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Send a frame to any receivers currently connected.
|
|
||||||
///
|
|
||||||
/// Video: 'UYVY', 'YUY2', 'NV12', 'YV12, 'BGRA', 'UYVA', 'VMX1' are supported (BGRA will be treated as BGRX and UYVA as UYVY where alpha flags are not set)
|
|
||||||
///
|
|
||||||
/// Audio: Supports planar 32bit floating point audio
|
|
||||||
///
|
|
||||||
/// Metadata: Supports UTF8 encoded XML
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="frame">The frame to send</param>
|
|
||||||
public int Send(OMTMediaFrame frame)
|
|
||||||
{
|
|
||||||
if (Exiting) return 0;
|
|
||||||
if (frame.Type == OMTFrameType.Video)
|
|
||||||
{
|
|
||||||
return SendVideo(frame);
|
|
||||||
}
|
|
||||||
else if (frame.Type == OMTFrameType.Audio)
|
|
||||||
{
|
|
||||||
return SendAudio(frame);
|
|
||||||
}
|
|
||||||
else if (frame.Type == OMTFrameType.Metadata)
|
|
||||||
{
|
|
||||||
return SendMetadata(frame);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int SendMetadata(OMTMediaFrame metadata)
|
|
||||||
{
|
|
||||||
OMTMetadata m = OMTMetadata.FromMediaFrame(metadata);
|
|
||||||
if (m != null)
|
|
||||||
{
|
|
||||||
return SendMetadata(m, null);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal int SendMetadata(OMTMetadata metadata, IPEndPoint endpoint)
|
|
||||||
{
|
|
||||||
lock (metaLock)
|
|
||||||
{
|
|
||||||
if (Exiting) return 0;
|
|
||||||
int len = 0;
|
|
||||||
OMTChannel[] channels = this.channels;
|
|
||||||
if (channels != null)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < channels.Length; i++)
|
|
||||||
{
|
|
||||||
OMTChannel ch = channels[i];
|
|
||||||
if (ch.IsMetadata())
|
|
||||||
{
|
|
||||||
if (endpoint == null || ch.RemoteEndPoint == endpoint)
|
|
||||||
{
|
|
||||||
len += channels[i].Send(metadata);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int SendVideo(OMTMediaFrame frame)
|
|
||||||
{
|
|
||||||
lock (videoLock)
|
|
||||||
{
|
|
||||||
if (Exiting) return 0;
|
|
||||||
if (frame.Data != IntPtr.Zero && frame.DataLength > 0)
|
|
||||||
{
|
|
||||||
tempVideo.Data.Resize(frame.DataLength + frame.FrameMetadataLength);
|
|
||||||
|
|
||||||
if ((frame.Codec == (int)OMTCodec.UYVY) || (frame.Codec == (int)OMTCodec.BGRA) ||
|
|
||||||
(frame.Codec == (int)OMTCodec.YUY2) || (frame.Codec == (int)OMTCodec.NV12) ||
|
|
||||||
(frame.Codec == (int)OMTCodec.YV12) || (frame.Codec == (int)OMTCodec.UYVA) ||
|
|
||||||
(frame.Codec == (int)OMTCodec.P216) || (frame.Codec == (int)OMTCodec.PA16)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (frame.Width >= 16 && frame.Height >= 16 && frame.Stride >= frame.Width)
|
|
||||||
{
|
|
||||||
bool interlaced = frame.Flags.HasFlag(OMTVideoFlags.Interlaced);
|
|
||||||
bool alpha = frame.Flags.HasFlag(OMTVideoFlags.Alpha);
|
|
||||||
|
|
||||||
CreateCodec(frame.Width, frame.Height, (int)frame.FrameRate, (VMXColorSpace)frame.ColorSpace);
|
|
||||||
byte[] buffer = tempVideo.Data.Buffer;
|
|
||||||
int len;
|
|
||||||
BeginCodecTimer();
|
|
||||||
VMXImageType itype = VMXImageType.None;
|
|
||||||
if (frame.Codec == (int)OMTCodec.UYVY)
|
|
||||||
{
|
|
||||||
itype = VMXImageType.UYVY;
|
|
||||||
}
|
|
||||||
else if (frame.Codec == (int)OMTCodec.YUY2)
|
|
||||||
{
|
|
||||||
itype = VMXImageType.YUY2;
|
|
||||||
}
|
|
||||||
else if (frame.Codec == (int)OMTCodec.NV12)
|
|
||||||
{
|
|
||||||
itype = VMXImageType.NV12;
|
|
||||||
} else if (frame.Codec == (int)OMTCodec.YV12)
|
|
||||||
{
|
|
||||||
itype = VMXImageType.YV12;
|
|
||||||
}
|
|
||||||
else if (frame.Codec == (int)OMTCodec.BGRA)
|
|
||||||
{
|
|
||||||
if (alpha)
|
|
||||||
{
|
|
||||||
itype = VMXImageType.BGRA;
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
itype = VMXImageType.BGRX;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (frame.Codec == (int)OMTCodec.UYVA)
|
|
||||||
{
|
|
||||||
if (alpha)
|
|
||||||
{
|
|
||||||
itype = VMXImageType.UYVA;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
itype = VMXImageType.UYVY;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (frame.Codec == (int)OMTCodec.PA16)
|
|
||||||
{
|
|
||||||
frame.Flags |= OMTVideoFlags.HighBitDepth;
|
|
||||||
if (alpha)
|
|
||||||
{
|
|
||||||
itype = VMXImageType.PA16;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
itype = VMXImageType.P216;
|
|
||||||
}
|
|
||||||
} else if (frame.Codec == (int)OMTCodec.P216)
|
|
||||||
{
|
|
||||||
frame.Flags |= OMTVideoFlags.HighBitDepth;
|
|
||||||
itype = VMXImageType.P216;
|
|
||||||
}
|
|
||||||
len = codec.Encode(itype, frame.Data, frame.Stride, buffer, interlaced);
|
|
||||||
EndCodecTimer();
|
|
||||||
if (len > 0)
|
|
||||||
{
|
|
||||||
if (frame.FrameMetadataLength > 0)
|
|
||||||
{
|
|
||||||
tempVideo.Data.SetBuffer(len, len);
|
|
||||||
tempVideo.Data.Append(frame.FrameMetadata,0,frame.FrameMetadataLength);
|
|
||||||
}
|
|
||||||
tempVideo.SetDataLength(len + frame.FrameMetadataLength);
|
|
||||||
tempVideo.SetMetadataLength(frame.FrameMetadataLength);
|
|
||||||
tempVideo.SetPreviewDataLength(codec.GetEncodedPreviewLength() + frame.FrameMetadataLength);
|
|
||||||
tempVideo.ConfigureVideo((int)OMTCodec.VMX1, frame.Width, frame.Height, frame.FrameRateN, frame.FrameRateD, frame.AspectRatio, frame.Flags, frame.ColorSpace);
|
|
||||||
videoClock.Process(ref frame);
|
|
||||||
tempVideo.Timestamp = frame.Timestamp;
|
|
||||||
return Send(tempVideo);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
OMTLogging.Write("Encoding failed at timestamp: " + frame.Timestamp, "OMTSend.SendVideo");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
OMTLogging.Write("Frame dimensions invalid: " + frame.Width + "x" + frame.Height + " Stride: " + frame.Stride, "OMTSend.SendVideo");
|
|
||||||
}
|
|
||||||
} else if (frame.Codec == (int)OMTCodec.VMX1)
|
|
||||||
{
|
|
||||||
if (frame.DataLength > 0)
|
|
||||||
{
|
|
||||||
tempVideo.SetDataLength(frame.DataLength + frame.FrameMetadataLength);
|
|
||||||
tempVideo.SetMetadataLength(frame.FrameMetadataLength);
|
|
||||||
tempVideo.SetPreviewDataLength(frame.DataLength + frame.FrameMetadataLength);
|
|
||||||
Marshal.Copy(frame.Data, tempVideo.Data.Buffer, 0, frame.DataLength);
|
|
||||||
if (frame.FrameMetadataLength > 0)
|
|
||||||
{
|
|
||||||
Marshal.Copy(frame.FrameMetadata, tempVideo.Data.Buffer,frame.DataLength, frame.FrameMetadataLength);
|
|
||||||
}
|
|
||||||
tempVideo.ConfigureVideo((int)OMTCodec.VMX1, frame.Width, frame.Height, frame.FrameRateN, frame.FrameRateD, frame.AspectRatio, frame.Flags, frame.ColorSpace);
|
|
||||||
videoClock.Process(ref frame);
|
|
||||||
tempVideo.Timestamp = frame.Timestamp;
|
|
||||||
return Send(tempVideo);
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
OMTLogging.Write("Frame DataLength invalid", "OMTSend.SendVideo");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
OMTLogging.Write("Codec not supported: " + frame.Codec, "OMTSend.SendVideo");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
private int SendAudio(OMTMediaFrame frame)
|
|
||||||
{
|
|
||||||
lock (audioLock)
|
|
||||||
{
|
|
||||||
if (Exiting) return 0;
|
|
||||||
if (frame.Data != IntPtr.Zero && frame.DataLength > 0 && frame.Channels > 0 && frame.SampleRate > 0 && frame.SamplesPerChannel > 0 && frame.Channels <= 32)
|
|
||||||
{
|
|
||||||
if (frame.DataLength > OMTConstants.AUDIO_MAX_SIZE)
|
|
||||||
{
|
|
||||||
OMTLogging.Write("Audio DataLength exceeded maximum: " + frame.DataLength, "OMTSend");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
tempAudioBuffer.Resize(frame.DataLength);
|
|
||||||
tempAudio.Data.Resize(frame.DataLength + frame.FrameMetadataLength);
|
|
||||||
Marshal.Copy(frame.Data, tempAudioBuffer.Buffer, 0, frame.DataLength);
|
|
||||||
tempAudioBuffer.SetBuffer(0, frame.DataLength);
|
|
||||||
tempAudio.Data.SetBuffer(0, 0);
|
|
||||||
OMTActiveAudioChannels ch = OMTFPA1Codec.Encode(tempAudioBuffer, frame.Channels, frame.SamplesPerChannel, tempAudio.Data);
|
|
||||||
if (frame.FrameMetadataLength > 0 && frame.FrameMetadata != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
tempAudio.Data.Append(frame.FrameMetadata,0, frame.FrameMetadataLength);
|
|
||||||
}
|
|
||||||
tempAudio.SetDataLength(tempAudio.Data.Length);
|
|
||||||
tempAudio.SetMetadataLength(frame.FrameMetadataLength);
|
|
||||||
tempAudio.ConfigureAudio(frame.SampleRate, frame.Channels, frame.SamplesPerChannel, ch);
|
|
||||||
audioClock.Process(ref frame);
|
|
||||||
tempAudio.Timestamp = frame.Timestamp;
|
|
||||||
return Send(tempAudio);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,177 +0,0 @@
|
|||||||
/*
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Open Media Transport Contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace libomtnet
|
|
||||||
{
|
|
||||||
public class OMTSendReceiveBase : OMTBase
|
|
||||||
{
|
|
||||||
protected object videoLock = new object();
|
|
||||||
protected object audioLock = new object();
|
|
||||||
protected object metaLock = new object();
|
|
||||||
|
|
||||||
protected AutoResetEvent metadataHandle;
|
|
||||||
protected AutoResetEvent tallyHandle;
|
|
||||||
protected IntPtr lastMetadata;
|
|
||||||
protected OMTTally lastTally = new OMTTally();
|
|
||||||
|
|
||||||
private Stopwatch timer = Stopwatch.StartNew();
|
|
||||||
private long codecTime = 0;
|
|
||||||
private long codecTimeSinceLast = 0;
|
|
||||||
private long codecStartTime = 0;
|
|
||||||
|
|
||||||
internal OMTRedirect redirect = null;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Receives the current tally state across all connections to a Sender.
|
|
||||||
/// If this function times out, the last known tally state will be received.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="millisecondsTimeout">milliseconds to wait for tally change. set to 0 to receive current tally</param>
|
|
||||||
/// <param name="tally"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public bool GetTally(int millisecondsTimeout, ref OMTTally tally)
|
|
||||||
{
|
|
||||||
if (Exiting) return false;
|
|
||||||
if (millisecondsTimeout > 0)
|
|
||||||
{
|
|
||||||
if (tallyHandle != null)
|
|
||||||
{
|
|
||||||
if (tallyHandle.WaitOne(millisecondsTimeout))
|
|
||||||
{
|
|
||||||
tally = lastTally;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tally = lastTally;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal virtual OMTTally GetTallyInternal()
|
|
||||||
{
|
|
||||||
return new OMTTally();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void Channel_Changed(object sender, OMTEventArgs e)
|
|
||||||
{
|
|
||||||
if (Exiting) return; //Avoid deadlock where Channel may call back into sender while dispose is in progress.
|
|
||||||
if (e.Type == OMTEventType.TallyChanged)
|
|
||||||
{
|
|
||||||
UpdateTally();
|
|
||||||
} else if (e.Type == OMTEventType.Disconnected)
|
|
||||||
{
|
|
||||||
if (sender != null)
|
|
||||||
{
|
|
||||||
OMTChannel ch = (OMTChannel)sender;
|
|
||||||
OnDisconnected(ch);
|
|
||||||
}
|
|
||||||
} else if (e.Type == OMTEventType.RedirectChanged)
|
|
||||||
{
|
|
||||||
OMTChannel ch = (OMTChannel)sender;
|
|
||||||
OnRedirectChanged(ch);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal virtual void OnRedirectChanged(OMTChannel ch)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
internal virtual void OnDisconnected(OMTChannel ch)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
internal virtual void OnTallyChanged( OMTTally tally)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void UpdateTally()
|
|
||||||
{
|
|
||||||
OMTTally tally = GetTallyInternal();
|
|
||||||
if (tally.Preview != lastTally.Preview || tally.Program != lastTally.Program)
|
|
||||||
{
|
|
||||||
lastTally = tally;
|
|
||||||
OnTallyChanged(lastTally);
|
|
||||||
if (tallyHandle != null)
|
|
||||||
{
|
|
||||||
tallyHandle.Set();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual OMTStatistics GetVideoStatistics()
|
|
||||||
{
|
|
||||||
return new OMTStatistics();
|
|
||||||
}
|
|
||||||
public virtual OMTStatistics GetAudioStatistics()
|
|
||||||
{
|
|
||||||
return new OMTStatistics();
|
|
||||||
}
|
|
||||||
internal void BeginCodecTimer()
|
|
||||||
{
|
|
||||||
codecStartTime = timer.ElapsedMilliseconds;
|
|
||||||
}
|
|
||||||
internal void EndCodecTimer()
|
|
||||||
{
|
|
||||||
long v = (timer.ElapsedMilliseconds - codecStartTime);
|
|
||||||
codecTime += v;
|
|
||||||
codecTimeSinceLast += v;
|
|
||||||
}
|
|
||||||
internal void UpdateCodecTimerStatistics(ref OMTStatistics v)
|
|
||||||
{
|
|
||||||
v.CodecTime = codecTime;
|
|
||||||
v.CodecTimeSinceLast = codecTimeSinceLast;
|
|
||||||
codecTimeSinceLast = 0;
|
|
||||||
}
|
|
||||||
internal bool ReceiveMetadata(OMTMetadata frame, ref OMTMediaFrame outFrame)
|
|
||||||
{
|
|
||||||
lock (metaLock)
|
|
||||||
{
|
|
||||||
if (Exiting) return false;
|
|
||||||
OMTMetadata.FreeIntPtr(lastMetadata);
|
|
||||||
lastMetadata = IntPtr.Zero;
|
|
||||||
outFrame.Type = OMTFrameType.Metadata;
|
|
||||||
outFrame.Timestamp = frame.Timestamp;
|
|
||||||
outFrame.Data = frame.ToIntPtr(ref outFrame.DataLength);
|
|
||||||
lastMetadata = outFrame.Data;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void DisposeInternal()
|
|
||||||
{
|
|
||||||
OMTMetadata.FreeIntPtr(lastMetadata);
|
|
||||||
lastMetadata = IntPtr.Zero;
|
|
||||||
base.DisposeInternal();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,154 +0,0 @@
|
|||||||
/*
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Open Media Transport Contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
using System.Xml;
|
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace libomtnet
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// These functions override the default settings which are stored in ~/.OMT/settings.xml on Mac and Linux and C:\ProgramData\OMT\settings.xml on WIndows
|
|
||||||
///
|
|
||||||
/// To override the default folder used for for settings, set the OMT_STORAGE_PATH environment variable prior to calling any OMT functions.
|
|
||||||
///
|
|
||||||
/// The following settings are currently supported:
|
|
||||||
///
|
|
||||||
/// DiscoveryServer [string] specify a URL in the format omt://hostname:port to connect to for discovery. If left blank, default DNS-SD discovery behavior is enabled.
|
|
||||||
///
|
|
||||||
/// NetworkPortStart[integer] specify the first port to create Send instances on.Defaults to 6400
|
|
||||||
///
|
|
||||||
/// NetworkPortEnd[integer] specify the last port to create Send instances on.Defaults to 6600
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public class OMTSettings
|
|
||||||
{
|
|
||||||
private static object globalLock = new object();
|
|
||||||
private object instanceLock = new object();
|
|
||||||
private string filename;
|
|
||||||
private XmlDocument document;
|
|
||||||
private XmlNode rootNode;
|
|
||||||
private static OMTSettings instance;
|
|
||||||
public static OMTSettings GetInstance()
|
|
||||||
{
|
|
||||||
lock (globalLock)
|
|
||||||
{
|
|
||||||
if (instance == null)
|
|
||||||
{
|
|
||||||
string sz = OMTPlatform.GetInstance().GetStoragePath() + Path.DirectorySeparatorChar + "settings.xml";
|
|
||||||
instance = new OMTSettings(sz);
|
|
||||||
}
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public OMTSettings(string filename)
|
|
||||||
{
|
|
||||||
this.filename = filename;
|
|
||||||
lock (globalLock)
|
|
||||||
{
|
|
||||||
document = new XmlDocument();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (File.Exists(filename))
|
|
||||||
{
|
|
||||||
document.Load(filename);
|
|
||||||
rootNode = document.DocumentElement;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "OMTSettings.New");
|
|
||||||
}
|
|
||||||
if (rootNode == null)
|
|
||||||
{
|
|
||||||
rootNode = document.CreateElement("Settings");
|
|
||||||
document.AppendChild(rootNode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public void Save()
|
|
||||||
{
|
|
||||||
lock (globalLock)
|
|
||||||
{
|
|
||||||
using (XmlTextWriter writer = new XmlTextWriter(filename, null))
|
|
||||||
{
|
|
||||||
writer.Formatting = Formatting.Indented;
|
|
||||||
document.Save(writer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public string GetString(string key, string defaultValue)
|
|
||||||
{
|
|
||||||
lock (instanceLock)
|
|
||||||
{
|
|
||||||
if (rootNode != null)
|
|
||||||
{
|
|
||||||
XmlNode node = rootNode.SelectSingleNode(key);
|
|
||||||
if (node != null)
|
|
||||||
{
|
|
||||||
return node.InnerText;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public void SetString(string key, string value)
|
|
||||||
{
|
|
||||||
lock (instanceLock)
|
|
||||||
{
|
|
||||||
if (rootNode != null)
|
|
||||||
{
|
|
||||||
XmlNode node = rootNode.SelectSingleNode(key);
|
|
||||||
if (node == null)
|
|
||||||
{
|
|
||||||
node = document.CreateElement(key);
|
|
||||||
rootNode.AppendChild(node);
|
|
||||||
}
|
|
||||||
node.InnerText = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetInteger(string key, int defaultValue)
|
|
||||||
{
|
|
||||||
string value = GetString(key, null);
|
|
||||||
if (!string.IsNullOrEmpty(value))
|
|
||||||
{
|
|
||||||
int v = 0;
|
|
||||||
if (int.TryParse(value, out v))
|
|
||||||
{
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
public void SetInteger(string key, int value)
|
|
||||||
{
|
|
||||||
SetString(key, value.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,148 +0,0 @@
|
|||||||
/*
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Open Media Transport Contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.ComponentModel;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Net.Sockets;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace libomtnet
|
|
||||||
{
|
|
||||||
internal class OMTSocketAsyncPool : OMTBase
|
|
||||||
{
|
|
||||||
private Queue<SocketAsyncEventArgs> pool;
|
|
||||||
private int bufferSize;
|
|
||||||
private object lockSync = new object();
|
|
||||||
|
|
||||||
protected virtual void OnCompleted(object sender, SocketAsyncEventArgs e)
|
|
||||||
{
|
|
||||||
if (e.SocketError != SocketError.Success)
|
|
||||||
{
|
|
||||||
OMTLogging.Write("Socket Pool Error: " + e.SocketError.ToString() + "," + e.BytesTransferred, "OMTSocketAsyncPool");
|
|
||||||
}
|
|
||||||
ReturnEventArgs(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
public object SyncObject { get { return lockSync; } }
|
|
||||||
|
|
||||||
public OMTSocketAsyncPool(int count, int bufferSize)
|
|
||||||
{
|
|
||||||
this.bufferSize = bufferSize;
|
|
||||||
pool = new Queue<SocketAsyncEventArgs>();
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
SocketAsyncEventArgs e = new SocketAsyncEventArgs();
|
|
||||||
if (bufferSize > 0)
|
|
||||||
{
|
|
||||||
byte[] buf = new byte[bufferSize];
|
|
||||||
e.SetBuffer(buf,0,buf.Length);
|
|
||||||
}
|
|
||||||
e.Completed += OnCompleted;
|
|
||||||
pool.Enqueue(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Resize(SocketAsyncEventArgs e, int length)
|
|
||||||
{
|
|
||||||
if (e != null)
|
|
||||||
{
|
|
||||||
if (e.Buffer.Length < length)
|
|
||||||
{
|
|
||||||
byte[] buf = new byte[length];
|
|
||||||
e.SetBuffer(buf, 0, buf.Length);
|
|
||||||
Debug.WriteLine("SocketPool.Resize: " + length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SendAsync(Socket socket, SocketAsyncEventArgs e)
|
|
||||||
{
|
|
||||||
lock (lockSync)
|
|
||||||
{
|
|
||||||
if (socket != null)
|
|
||||||
{
|
|
||||||
if (socket.SendAsync(e) == false)
|
|
||||||
{
|
|
||||||
OnCompleted(this, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal SocketAsyncEventArgs GetEventArgs()
|
|
||||||
{
|
|
||||||
lock (lockSync)
|
|
||||||
{
|
|
||||||
if (pool == null) return null;
|
|
||||||
if (pool.Count > 0) {
|
|
||||||
SocketAsyncEventArgs e = pool.Dequeue();
|
|
||||||
e.SetBuffer(0, e.Buffer.Length);
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Count { get { lock (pool) { return pool.Count; } } }
|
|
||||||
|
|
||||||
internal void ReturnEventArgs(SocketAsyncEventArgs e)
|
|
||||||
{
|
|
||||||
lock (lockSync)
|
|
||||||
{
|
|
||||||
if (pool == null)
|
|
||||||
{
|
|
||||||
e.Dispose();
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
pool.Enqueue(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void DisposeInternal()
|
|
||||||
{
|
|
||||||
lock (lockSync)
|
|
||||||
{
|
|
||||||
if (pool != null)
|
|
||||||
{
|
|
||||||
while (pool.Count > 0)
|
|
||||||
{
|
|
||||||
SocketAsyncEventArgs e = pool.Dequeue();
|
|
||||||
if (e != null)
|
|
||||||
{
|
|
||||||
e.Completed -= OnCompleted;
|
|
||||||
e.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pool.Clear();
|
|
||||||
pool = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
base.DisposeInternal();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,227 +0,0 @@
|
|||||||
/*
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Open Media Transport Contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Net;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace libomtnet
|
|
||||||
{
|
|
||||||
public class OMTUtils
|
|
||||||
{
|
|
||||||
internal static IPAddress[] ResolveHostname(string hostname)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return Dns.GetHostAddresses(hostname);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "OMTUtils.ResolveHostname");
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IntPtr StringToPtrUTF8(string s)
|
|
||||||
{
|
|
||||||
byte[] b = UTF8Encoding.UTF8.GetBytes(s);
|
|
||||||
IntPtr dst = Marshal.AllocHGlobal(b.Length + 1);
|
|
||||||
Marshal.Copy(b, 0, dst, b.Length);
|
|
||||||
Marshal.WriteByte(dst, b.Length, 0);
|
|
||||||
return dst;
|
|
||||||
}
|
|
||||||
public static IntPtr StringToPtrUTF8(string s, out int length)
|
|
||||||
{
|
|
||||||
byte[] b = UTF8Encoding.UTF8.GetBytes(s);
|
|
||||||
IntPtr dst = Marshal.AllocHGlobal(b.Length + 1);
|
|
||||||
Marshal.Copy(b, 0, dst, b.Length);
|
|
||||||
Marshal.WriteByte(dst, b.Length, 0);
|
|
||||||
length = b.Length + 1;
|
|
||||||
return dst;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void WriteStringToPtrUTF8(string s, IntPtr dst)
|
|
||||||
{
|
|
||||||
byte[] b = UTF8Encoding.UTF8.GetBytes(s);
|
|
||||||
Marshal.Copy(b, 0, dst, b.Length);
|
|
||||||
Marshal.WriteByte(dst, b.Length, 0);
|
|
||||||
}
|
|
||||||
public static void WriteStringToPtrUTF8(string s, IntPtr dst, int maxLength)
|
|
||||||
{
|
|
||||||
if (maxLength <= 0) return;
|
|
||||||
byte[] b = UTF8Encoding.UTF8.GetBytes(s);
|
|
||||||
int len = Math.Min(maxLength - 1, b.Length);
|
|
||||||
Marshal.Copy(b, 0, dst, len);
|
|
||||||
Marshal.WriteByte(dst, len, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string PtrToStringUTF8(IntPtr ptr)
|
|
||||||
{
|
|
||||||
using (MemoryStream m = new MemoryStream())
|
|
||||||
{
|
|
||||||
int offset = 0;
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
byte b = Marshal.ReadByte(ptr, offset);
|
|
||||||
if (b == 0) break;
|
|
||||||
m.WriteByte(b);
|
|
||||||
offset++;
|
|
||||||
}
|
|
||||||
return UTF8Encoding.UTF8.GetString(m.ToArray());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string PtrToStringUTF8(IntPtr ptr, int maxLength)
|
|
||||||
{
|
|
||||||
using (MemoryStream m = new MemoryStream())
|
|
||||||
{
|
|
||||||
for (int i = 0; i < maxLength; i++)
|
|
||||||
{
|
|
||||||
byte b = Marshal.ReadByte(ptr, i);
|
|
||||||
if (b == 0) break;
|
|
||||||
m.WriteByte(b);
|
|
||||||
}
|
|
||||||
return UTF8Encoding.UTF8.GetString(m.ToArray());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void InterleavedToPlanarAudio32F32F(int numSamples, int channels, int sampleStride, float[] src, float[] dst)
|
|
||||||
{
|
|
||||||
int offset = 0;
|
|
||||||
for (int i = 0; i < numSamples; i++)
|
|
||||||
{
|
|
||||||
for (int c = 0; c < channels; c++)
|
|
||||||
{
|
|
||||||
dst[(sampleStride * c) + i] = src[offset];
|
|
||||||
offset += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public static void InterleavedToPlanarAudio1632F(int numSamples, int channels, int sampleStride, short[] src, float[] dst)
|
|
||||||
{
|
|
||||||
int offset = 0;
|
|
||||||
for (int i = 0; i < numSamples; i++)
|
|
||||||
{
|
|
||||||
for (int c = 0; c < channels; c++)
|
|
||||||
{
|
|
||||||
float s = src[offset];
|
|
||||||
dst[(sampleStride * c) + i] = s / short.MaxValue;
|
|
||||||
offset += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IntPtr XMLToIntPtr(string xml, ref int length)
|
|
||||||
{
|
|
||||||
byte[] utf8 = UTF8Encoding.UTF8.GetBytes(xml);
|
|
||||||
length = utf8.Length + 1;
|
|
||||||
IntPtr data = Marshal.AllocHGlobal(length);
|
|
||||||
Marshal.Copy(utf8, 0, data, utf8.Length);
|
|
||||||
Marshal.WriteByte(data, utf8.Length, 0);
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string IntPtrToXML(IntPtr ptr, int length)
|
|
||||||
{
|
|
||||||
if (ptr != IntPtr.Zero && length > 0)
|
|
||||||
{
|
|
||||||
byte[] b = new byte[length];
|
|
||||||
Marshal.Copy(ptr, b, 0, length);
|
|
||||||
string xml = UTF8Encoding.UTF8.GetString(b);
|
|
||||||
return xml;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void FreeXMLIntPtr(IntPtr x)
|
|
||||||
{
|
|
||||||
if (x != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
Marshal.FreeHGlobal(x);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static float ToFrameRate(int frameRateN, int frameRateD)
|
|
||||||
{
|
|
||||||
if (frameRateD == 0) return 0;
|
|
||||||
double d = (double)frameRateN / (double)frameRateD;
|
|
||||||
d = Math.Round(d, 2);
|
|
||||||
return (float)d;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void FromFrameRate(float fps, ref int frameRateN, ref int frameRateD)
|
|
||||||
{
|
|
||||||
switch (Math.Round(fps, 2))
|
|
||||||
{
|
|
||||||
case 29.97:
|
|
||||||
frameRateN = 30000;
|
|
||||||
frameRateD = 1001;
|
|
||||||
break;
|
|
||||||
case 59.94:
|
|
||||||
frameRateN = 60000;
|
|
||||||
frameRateD = 1001;
|
|
||||||
break;
|
|
||||||
case 119.88:
|
|
||||||
frameRateN = 120000;
|
|
||||||
frameRateD = 1001;
|
|
||||||
break;
|
|
||||||
case 239.76:
|
|
||||||
frameRateN = 240000;
|
|
||||||
frameRateD = 1001;
|
|
||||||
break;
|
|
||||||
case 23.98:
|
|
||||||
case 23.976:
|
|
||||||
frameRateN = 24000;
|
|
||||||
frameRateD = 1001;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
frameRateN = (int)fps;
|
|
||||||
frameRateD = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool IsIPv4(IPAddress address)
|
|
||||||
{
|
|
||||||
if (address != null)
|
|
||||||
{
|
|
||||||
if (address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) return true;
|
|
||||||
if (address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6)
|
|
||||||
{
|
|
||||||
byte[] b = address.GetAddressBytes();
|
|
||||||
for (int i = 0; i < 10; i++)
|
|
||||||
{
|
|
||||||
if (b[i] != 0) return false;
|
|
||||||
}
|
|
||||||
if (b[10] == 0xFF && b[11] == 0xFF) return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
/*
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Open Media Transport Contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace libomtnet.codecs
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Interface for native VMX codec. Needed for platforms which require different library paths, such as iOS
|
|
||||||
/// </summary>
|
|
||||||
internal interface IVMXCodec
|
|
||||||
{
|
|
||||||
IntPtr VMX_Create(OMTSize dimensions, VMXProfile profile, VMXColorSpace colorSpace);
|
|
||||||
void VMX_Destroy(IntPtr instance);
|
|
||||||
void VMX_SetQuality(IntPtr instance, int q);
|
|
||||||
int VMX_GetQuality(IntPtr instance);
|
|
||||||
void VMX_SetThreads(IntPtr instance, int t);
|
|
||||||
int VMX_GetThreads(IntPtr instance);
|
|
||||||
int VMX_LoadFrom(IntPtr instance, byte[] data, int dataLen);
|
|
||||||
int VMX_SaveTo(IntPtr instance, byte[] data, int maxLen);
|
|
||||||
int VMX_EncodeBGRA(IntPtr Instance, IntPtr src, int stride, int interlaced);
|
|
||||||
int VMX_EncodeBGRX(IntPtr Instance, IntPtr src, int stride, int interlaced);
|
|
||||||
int VMX_EncodeUYVY(IntPtr Instance, IntPtr src, int stride, int interlaced);
|
|
||||||
int VMX_EncodeUYVA(IntPtr Instance, IntPtr src, int stride, int interlaced);
|
|
||||||
int VMX_EncodeP216(IntPtr Instance, IntPtr src, int stride, int interlaced);
|
|
||||||
int VMX_EncodePA16(IntPtr Instance, IntPtr src, int stride, int interlaced);
|
|
||||||
int VMX_EncodeYUY2(IntPtr Instance, IntPtr src, int stride, int interlaced);
|
|
||||||
int VMX_EncodeNV12(IntPtr Instance, IntPtr srcY, int strideY, IntPtr srcUV, int strideUV, int interlaced);
|
|
||||||
int VMX_EncodeYV12(IntPtr Instance, IntPtr srcY, int strideY, IntPtr srcU, int strideU, IntPtr srcV, int strideV, int interlaced);
|
|
||||||
int VMX_DecodeUYVY(IntPtr Instance, byte[] dst, int stride);
|
|
||||||
int VMX_DecodeUYVA(IntPtr Instance, byte[] dst, int stride);
|
|
||||||
int VMX_DecodeP216(IntPtr Instance, byte[] dst, int stride);
|
|
||||||
int VMX_DecodePA16(IntPtr Instance, byte[] dst, int stride);
|
|
||||||
int VMX_DecodeYUY2(IntPtr Instance, byte[] dst, int stride);
|
|
||||||
int VMX_DecodeBGRX(IntPtr Instance, byte[] dst, int stride);
|
|
||||||
int VMX_DecodeBGRA(IntPtr Instance, byte[] dst, int stride);
|
|
||||||
int VMX_DecodePreviewUYVY(IntPtr Instance, byte[] dst, int stride);
|
|
||||||
int VMX_DecodePreviewYUY2(IntPtr Instance, byte[] dst, int stride);
|
|
||||||
int VMX_DecodePreviewBGRA(IntPtr Instance, byte[] dst, int stride);
|
|
||||||
int VMX_DecodePreviewBGRX(IntPtr Instance, byte[] dst, int stride);
|
|
||||||
int VMX_DecodePreviewUYVA(IntPtr Instance, byte[] dst, int stride);
|
|
||||||
int VMX_GetEncodedPreviewLength(IntPtr Instance);
|
|
||||||
float VMX_CalculatePSNR(byte[] image1, byte[] image2, int stride, int bytesPerPixel, OMTSize sz);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
/*
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Open Media Transport Contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace libomtnet.codecs
|
|
||||||
{
|
|
||||||
internal class OMTFPA1Codec : OMTBase
|
|
||||||
{
|
|
||||||
byte[] zeroBuffer;
|
|
||||||
public OMTFPA1Codec(int maxLength)
|
|
||||||
{
|
|
||||||
zeroBuffer = new byte[maxLength];
|
|
||||||
}
|
|
||||||
public void Decode(OMTBuffer src, int srcChannels, int srcSamplesPerChannel, OMTActiveAudioChannels srcActiveChannels, OMTBuffer dst)
|
|
||||||
{
|
|
||||||
int offset = 0;
|
|
||||||
int dstoffset = 0;
|
|
||||||
for (int i = 0; i < srcChannels; i++)
|
|
||||||
{
|
|
||||||
OMTActiveAudioChannels chflag = (OMTActiveAudioChannels)(1 << i);
|
|
||||||
if ((srcActiveChannels & chflag) == chflag)
|
|
||||||
{
|
|
||||||
Buffer.BlockCopy(src.Buffer, src.Offset + offset, dst.Buffer, dst.Offset + dstoffset, srcSamplesPerChannel * OMTConstants.AUDIO_SAMPLE_SIZE);
|
|
||||||
offset += srcSamplesPerChannel * 4;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//>twice as fast as Array.Clear
|
|
||||||
Buffer.BlockCopy(zeroBuffer, 0, dst.Buffer, dst.Offset + dstoffset, srcSamplesPerChannel * OMTConstants.AUDIO_SAMPLE_SIZE);
|
|
||||||
}
|
|
||||||
dstoffset += srcSamplesPerChannel * OMTConstants.AUDIO_SAMPLE_SIZE;
|
|
||||||
}
|
|
||||||
dst.SetBuffer(0, srcChannels * srcSamplesPerChannel * OMTConstants.AUDIO_SAMPLE_SIZE);
|
|
||||||
}
|
|
||||||
private static bool IsEmpty(OMTBuffer buff, int offset, int length)
|
|
||||||
{
|
|
||||||
for (int i = buff.Offset + offset; i < buff.Offset + offset + length; i++)
|
|
||||||
{
|
|
||||||
if (buff.Buffer[i] != 0) return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
public static OMTActiveAudioChannels Encode(OMTBuffer src, int srcChannels, int srcSamplesPerChannel, OMTBuffer dst)
|
|
||||||
{
|
|
||||||
OMTActiveAudioChannels activeChannels = 0;
|
|
||||||
int offset = 0;
|
|
||||||
int dstoffset = 0;
|
|
||||||
for (int i = 0; i < srcChannels; i++)
|
|
||||||
{
|
|
||||||
if (!IsEmpty(src, src.Offset + offset, srcSamplesPerChannel * OMTConstants.AUDIO_SAMPLE_SIZE))
|
|
||||||
{
|
|
||||||
OMTActiveAudioChannels chflag = (OMTActiveAudioChannels)(1 << i);
|
|
||||||
Buffer.BlockCopy(src.Buffer, src.Offset + offset, dst.Buffer, dst.Offset + dstoffset, srcSamplesPerChannel * OMTConstants.AUDIO_SAMPLE_SIZE);
|
|
||||||
activeChannels = activeChannels | chflag;
|
|
||||||
dstoffset += srcSamplesPerChannel * OMTConstants.AUDIO_SAMPLE_SIZE;
|
|
||||||
}
|
|
||||||
offset += srcSamplesPerChannel * OMTConstants.AUDIO_SAMPLE_SIZE;
|
|
||||||
}
|
|
||||||
dst.SetBuffer(dst.Offset, dstoffset);
|
|
||||||
return activeChannels;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void DisposeInternal()
|
|
||||||
{
|
|
||||||
zeroBuffer = null;
|
|
||||||
base.DisposeInternal();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,286 +0,0 @@
|
|||||||
/*
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Open Media Transport Contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Security;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace libomtnet.codecs
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// VMX Profile sets bitrate targets and DC coding precision
|
|
||||||
/// HQ, SQ, LQ: VMX Legacy recording profiles
|
|
||||||
/// OMT Profiles (SQ Default):
|
|
||||||
/// 2160p60 HQ 600 SQ 300 LQ 200 Mbps
|
|
||||||
/// 1080p60 HQ 288 SQ 200 LQ 80 Mbps
|
|
||||||
/// SD HQ 72 SQ 36 LQ 24 Mbps
|
|
||||||
/// </summary>
|
|
||||||
public enum VMXProfile
|
|
||||||
{
|
|
||||||
None = 0,
|
|
||||||
Default = 1,
|
|
||||||
LQ = 33,
|
|
||||||
SQ = 66,
|
|
||||||
HQ = 99,
|
|
||||||
OMT_LQ = 133,
|
|
||||||
OMT_SQ = 166,
|
|
||||||
OMT_HQ = 199
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum VMXColorSpace
|
|
||||||
{
|
|
||||||
Undefined = 0,
|
|
||||||
BT601 = 601,
|
|
||||||
BT709 = 709
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum VMXImageType
|
|
||||||
{
|
|
||||||
None = 0,
|
|
||||||
UYVY = 1,
|
|
||||||
YUY2 = 2,
|
|
||||||
NV12 = 3,
|
|
||||||
YV12 = 4,
|
|
||||||
BGRA = 5,
|
|
||||||
BGRX = 6,
|
|
||||||
UYVA = 7,
|
|
||||||
P216 = 8,
|
|
||||||
PA16 = 9
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// VMX Encoder/Decoder for OMT
|
|
||||||
/// </summary>
|
|
||||||
public class OMTVMX1Codec : OMTBase
|
|
||||||
{
|
|
||||||
private const string DLLPATH = @"libvmx";
|
|
||||||
private readonly int width;
|
|
||||||
private readonly int height;
|
|
||||||
private readonly int framesPerSecond;
|
|
||||||
private readonly VMXProfile profile;
|
|
||||||
private readonly VMXColorSpace colorSpace;
|
|
||||||
private IntPtr instance;
|
|
||||||
private IVMXCodec codec;
|
|
||||||
|
|
||||||
public OMTVMX1Codec(int width, int height, int framesPerSecond, VMXProfile profile = VMXProfile.Default, VMXColorSpace colorSpace = VMXColorSpace.Undefined)
|
|
||||||
{
|
|
||||||
if (OMTPlatform.GetPlatformType() == OMTPlatformType.iOS)
|
|
||||||
{
|
|
||||||
codec = new VMXCodecIOS();
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
codec = new VMXCodec();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.width = width;
|
|
||||||
this.height = height;
|
|
||||||
this.profile = profile;
|
|
||||||
this.colorSpace = colorSpace;
|
|
||||||
this.framesPerSecond = framesPerSecond;
|
|
||||||
if (profile == VMXProfile.Default) { profile = VMXProfile.OMT_SQ; }
|
|
||||||
this.instance = codec.VMX_Create(new OMTSize(width, height), profile, colorSpace);
|
|
||||||
if (framesPerSecond > 60)
|
|
||||||
{
|
|
||||||
int threads = codec.VMX_GetThreads(this.instance);
|
|
||||||
threads *= 2;
|
|
||||||
codec.VMX_SetThreads(this.instance, threads);
|
|
||||||
Debug.WriteLine("Codec.SetThreads: " + threads);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public float CalculatePSNR(byte[] image1, byte[] image2, int stride, int bytesPerPixel, OMTSize sz)
|
|
||||||
{
|
|
||||||
return codec.VMX_CalculatePSNR(image1, image2, stride, bytesPerPixel, sz);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetQuality(int quality)
|
|
||||||
{
|
|
||||||
codec.VMX_SetQuality(this.instance, quality);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetQuality()
|
|
||||||
{
|
|
||||||
return codec.VMX_GetQuality(this.instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Encode(VMXImageType itype, IntPtr src, int srcStride, byte[] dst, bool interlaced)
|
|
||||||
{
|
|
||||||
int i = 0;
|
|
||||||
if (interlaced) i = 1;
|
|
||||||
int hr = 0;
|
|
||||||
switch (itype)
|
|
||||||
{
|
|
||||||
case VMXImageType.UYVY:
|
|
||||||
hr = codec.VMX_EncodeUYVY(instance, src, srcStride, i);
|
|
||||||
break;
|
|
||||||
case VMXImageType.UYVA:
|
|
||||||
hr = codec.VMX_EncodeUYVA(instance, src, srcStride, i);
|
|
||||||
break;
|
|
||||||
case VMXImageType.P216:
|
|
||||||
hr = codec.VMX_EncodeP216(instance, src, srcStride, i);
|
|
||||||
break;
|
|
||||||
case VMXImageType.PA16:
|
|
||||||
hr = codec.VMX_EncodePA16(instance, src, srcStride, i);
|
|
||||||
break;
|
|
||||||
case VMXImageType.YUY2:
|
|
||||||
hr = codec.VMX_EncodeYUY2(instance, src, srcStride, i);
|
|
||||||
break;
|
|
||||||
case VMXImageType.NV12:
|
|
||||||
IntPtr srcUV = src + (srcStride * height);
|
|
||||||
hr = codec.VMX_EncodeNV12(instance, src, srcStride, srcUV, srcStride, i);
|
|
||||||
break;
|
|
||||||
case VMXImageType.YV12:
|
|
||||||
IntPtr srcV = src + (srcStride * height);
|
|
||||||
int strideV = srcStride >> 1;
|
|
||||||
IntPtr srcU = srcV + (strideV * (height >> 1));
|
|
||||||
int strideU = srcStride >> 1;
|
|
||||||
hr = codec.VMX_EncodeYV12(instance, src, srcStride, srcU, strideU, srcV, strideV, i);
|
|
||||||
break;
|
|
||||||
case VMXImageType.BGRA:
|
|
||||||
hr = codec.VMX_EncodeBGRA(instance, src, srcStride, i);
|
|
||||||
break;
|
|
||||||
case VMXImageType.BGRX:
|
|
||||||
hr = codec.VMX_EncodeBGRA(instance, src, srcStride, i);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (hr == 0)
|
|
||||||
{
|
|
||||||
int len = codec.VMX_SaveTo(instance, dst, dst.Length);
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
public bool DecodePreview(VMXImageType itype, byte[] src, int srcLen, ref byte[] dst, int dstStride)
|
|
||||||
{
|
|
||||||
int hr = codec.VMX_LoadFrom(instance, src, srcLen);
|
|
||||||
if (hr == 0)
|
|
||||||
{
|
|
||||||
switch (itype)
|
|
||||||
{
|
|
||||||
case VMXImageType.BGRA:
|
|
||||||
hr = codec.VMX_DecodePreviewBGRA(instance, dst, dstStride);
|
|
||||||
break;
|
|
||||||
case VMXImageType.BGRX:
|
|
||||||
hr = codec.VMX_DecodePreviewBGRX(instance, dst, dstStride);
|
|
||||||
break;
|
|
||||||
case VMXImageType.UYVY:
|
|
||||||
hr = codec.VMX_DecodePreviewUYVY(instance, dst, dstStride);
|
|
||||||
break;
|
|
||||||
case VMXImageType.UYVA:
|
|
||||||
hr = codec.VMX_DecodePreviewUYVA(instance, dst, dstStride);
|
|
||||||
break;
|
|
||||||
case VMXImageType.YUY2:
|
|
||||||
hr = codec.VMX_DecodePreviewYUY2(instance, dst, dstStride);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (hr == 0) return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
public bool Decode(VMXImageType itype, byte[] src, int srcLen, ref byte[] dst, int dstStride)
|
|
||||||
{
|
|
||||||
int hr = codec.VMX_LoadFrom(instance, src, srcLen);
|
|
||||||
if (hr == 0)
|
|
||||||
{
|
|
||||||
switch (itype)
|
|
||||||
{
|
|
||||||
case VMXImageType.BGRA:
|
|
||||||
hr = codec.VMX_DecodeBGRA(instance, dst, dstStride);
|
|
||||||
break;
|
|
||||||
case VMXImageType.BGRX:
|
|
||||||
hr = codec.VMX_DecodeBGRX(instance, dst, dstStride);
|
|
||||||
break;
|
|
||||||
case VMXImageType.UYVY:
|
|
||||||
hr = codec.VMX_DecodeUYVY(instance, dst, dstStride);
|
|
||||||
break;
|
|
||||||
case VMXImageType.YUY2:
|
|
||||||
hr = codec.VMX_DecodeYUY2(instance, dst, dstStride);
|
|
||||||
break;
|
|
||||||
case VMXImageType.UYVA:
|
|
||||||
hr = codec.VMX_DecodeUYVA(instance, dst, dstStride);
|
|
||||||
break;
|
|
||||||
case VMXImageType.P216:
|
|
||||||
hr = codec.VMX_DecodeP216(instance, dst, dstStride);
|
|
||||||
break;
|
|
||||||
case VMXImageType.PA16:
|
|
||||||
hr = codec.VMX_DecodePA16(instance, dst, dstStride);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (hr == 0) return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public OMTSize GetPreviewSize(bool interlaced)
|
|
||||||
{
|
|
||||||
OMTSize size = new OMTSize();
|
|
||||||
size.Width = width >> 3;
|
|
||||||
size.Height = height >> 3;
|
|
||||||
if ((size.Width %2) != 0)
|
|
||||||
{
|
|
||||||
size.Width++;
|
|
||||||
}
|
|
||||||
if (interlaced)
|
|
||||||
{
|
|
||||||
if ((size.Height % 2) != 0)
|
|
||||||
{
|
|
||||||
size.Height--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
public int GetEncodedPreviewLength()
|
|
||||||
{
|
|
||||||
return codec.VMX_GetEncodedPreviewLength(instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void DisposeInternal()
|
|
||||||
{
|
|
||||||
if (instance != null)
|
|
||||||
{
|
|
||||||
codec.VMX_Destroy(instance);
|
|
||||||
instance = IntPtr.Zero;
|
|
||||||
}
|
|
||||||
base.DisposeInternal();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Width { get { return width; } }
|
|
||||||
public int Height { get { return height; } }
|
|
||||||
public int FramesPerSecond { get { return framesPerSecond; } }
|
|
||||||
public VMXProfile Profile { get { return profile; } }
|
|
||||||
public VMXColorSpace ColorSpace { get { return colorSpace; } }
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,188 +0,0 @@
|
|||||||
/*
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Open Media Transport Contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace libomtnet.codecs
|
|
||||||
{
|
|
||||||
internal class VMXCodec : IVMXCodec
|
|
||||||
{
|
|
||||||
public float VMX_CalculatePSNR(byte[] image1, byte[] image2, int stride, int bytesPerPixel, OMTSize sz)
|
|
||||||
{
|
|
||||||
return VMXUnmanaged.VMX_CalculatePSNR(image1, image2, stride, bytesPerPixel, sz);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IntPtr VMX_Create(OMTSize dimensions, VMXProfile profile, VMXColorSpace colorSpace)
|
|
||||||
{
|
|
||||||
return VMXUnmanaged.VMX_Create(dimensions, profile, colorSpace);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int VMX_DecodeBGRA(IntPtr Instance, byte[] dst, int stride)
|
|
||||||
{
|
|
||||||
return VMXUnmanaged.VMX_DecodeBGRA(Instance, dst, stride);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int VMX_DecodeBGRX(IntPtr Instance, byte[] dst, int stride)
|
|
||||||
{
|
|
||||||
return VMXUnmanaged.VMX_DecodeBGRX(Instance, dst, stride);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int VMX_DecodePreviewBGRA(IntPtr Instance, byte[] dst, int stride)
|
|
||||||
{
|
|
||||||
return VMXUnmanaged.VMX_DecodePreviewBGRA(Instance, dst, stride);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int VMX_DecodePreviewBGRX(IntPtr Instance, byte[] dst, int stride)
|
|
||||||
{
|
|
||||||
return VMXUnmanaged.VMX_DecodePreviewBGRX(Instance, dst, stride);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int VMX_DecodePreviewUYVY(IntPtr Instance, byte[] dst, int stride)
|
|
||||||
{
|
|
||||||
return VMXUnmanaged.VMX_DecodePreviewUYVY(Instance, dst, stride);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int VMX_DecodePreviewUYVA(IntPtr Instance, byte[] dst, int stride)
|
|
||||||
{
|
|
||||||
return VMXUnmanaged.VMX_DecodePreviewUYVA(Instance, dst, stride);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int VMX_DecodePreviewYUY2(IntPtr Instance, byte[] dst, int stride)
|
|
||||||
{
|
|
||||||
return VMXUnmanaged.VMX_DecodePreviewYUY2(Instance, dst, stride);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int VMX_DecodeUYVY(IntPtr Instance, byte[] dst, int stride)
|
|
||||||
{
|
|
||||||
return VMXUnmanaged.VMX_DecodeUYVY(Instance, dst, stride);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int VMX_DecodeUYVA(IntPtr Instance, byte[] dst, int stride)
|
|
||||||
{
|
|
||||||
return VMXUnmanaged.VMX_DecodeUYVA(Instance, dst, stride);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int VMX_DecodeP216(IntPtr Instance, byte[] dst, int stride)
|
|
||||||
{
|
|
||||||
return VMXUnmanaged.VMX_DecodeP216(Instance, dst, stride);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int VMX_DecodePA16(IntPtr Instance, byte[] dst, int stride)
|
|
||||||
{
|
|
||||||
return VMXUnmanaged.VMX_DecodePA16(Instance, dst, stride);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int VMX_DecodeYUY2(IntPtr Instance, byte[] dst, int stride)
|
|
||||||
{
|
|
||||||
return VMXUnmanaged.VMX_DecodeYUY2(Instance, dst, stride);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void VMX_Destroy(IntPtr instance)
|
|
||||||
{
|
|
||||||
VMXUnmanaged.VMX_Destroy(instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int VMX_EncodeBGRA(IntPtr Instance, IntPtr src, int stride, int interlaced)
|
|
||||||
{
|
|
||||||
return VMXUnmanaged.VMX_EncodeBGRA(Instance,src,stride, interlaced);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int VMX_EncodeBGRX(IntPtr Instance, IntPtr src, int stride, int interlaced)
|
|
||||||
{
|
|
||||||
return VMXUnmanaged.VMX_EncodeBGRX(Instance, src, stride, interlaced);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int VMX_EncodeNV12(IntPtr Instance, IntPtr srcY, int strideY, IntPtr srcUV, int strideUV, int interlaced)
|
|
||||||
{
|
|
||||||
return VMXUnmanaged.VMX_EncodeNV12(Instance,srcY,strideY,srcUV,strideUV,interlaced);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int VMX_EncodeUYVY(IntPtr Instance, IntPtr src, int stride, int interlaced)
|
|
||||||
{
|
|
||||||
return VMXUnmanaged.VMX_EncodeUYVY(Instance,src,stride,interlaced);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int VMX_EncodeUYVA(IntPtr Instance, IntPtr src, int stride, int interlaced)
|
|
||||||
{
|
|
||||||
return VMXUnmanaged.VMX_EncodeUYVA(Instance, src, stride, interlaced);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int VMX_EncodeP216(IntPtr Instance, IntPtr src, int stride, int interlaced)
|
|
||||||
{
|
|
||||||
return VMXUnmanaged.VMX_EncodeP216(Instance, src, stride, interlaced);
|
|
||||||
}
|
|
||||||
public int VMX_EncodePA16(IntPtr Instance, IntPtr src, int stride, int interlaced)
|
|
||||||
{
|
|
||||||
return VMXUnmanaged.VMX_EncodePA16(Instance, src, stride, interlaced);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int VMX_EncodeYUY2(IntPtr Instance, IntPtr src, int stride, int interlaced)
|
|
||||||
{
|
|
||||||
return VMXUnmanaged.VMX_EncodeYUY2(Instance, src, stride, interlaced);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int VMX_EncodeYV12(IntPtr Instance, IntPtr srcY, int strideY, IntPtr srcU, int strideU, IntPtr srcV, int strideV, int interlaced)
|
|
||||||
{
|
|
||||||
return VMXUnmanaged.VMX_EncodeYV12(Instance,srcY,strideY,srcU,strideU,srcV,strideV,interlaced);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int VMX_GetEncodedPreviewLength(IntPtr Instance)
|
|
||||||
{
|
|
||||||
return VMXUnmanaged.VMX_GetEncodedPreviewLength(Instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int VMX_GetThreads(IntPtr instance)
|
|
||||||
{
|
|
||||||
return VMXUnmanaged.VMX_GetThreads(instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int VMX_LoadFrom(IntPtr instance, byte[] data, int dataLen)
|
|
||||||
{
|
|
||||||
return VMXUnmanaged.VMX_LoadFrom(instance,data,dataLen);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int VMX_SaveTo(IntPtr instance, byte[] data, int maxLen)
|
|
||||||
{
|
|
||||||
return VMXUnmanaged.VMX_SaveTo(instance, data, maxLen);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void VMX_SetQuality(IntPtr instance, int q)
|
|
||||||
{
|
|
||||||
VMXUnmanaged.VMX_SetQuality(instance, q);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void VMX_SetThreads(IntPtr instance, int t)
|
|
||||||
{
|
|
||||||
VMXUnmanaged.VMX_SetThreads(instance, t);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int VMX_GetQuality(IntPtr instance)
|
|
||||||
{
|
|
||||||
return VMXUnmanaged.VMX_GetQuality(instance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,189 +0,0 @@
|
|||||||
/*
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Open Media Transport Contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace libomtnet.codecs
|
|
||||||
{
|
|
||||||
internal class VMXCodecIOS : IVMXCodec
|
|
||||||
{
|
|
||||||
public float VMX_CalculatePSNR(byte[] image1, byte[] image2, int stride, int bytesPerPixel, OMTSize sz)
|
|
||||||
{
|
|
||||||
return VMXUnmanagedIOS.VMX_CalculatePSNR(image1, image2, stride, bytesPerPixel, sz);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IntPtr VMX_Create(OMTSize dimensions, VMXProfile profile, VMXColorSpace colorSpace)
|
|
||||||
{
|
|
||||||
return VMXUnmanagedIOS.VMX_Create(dimensions, profile, colorSpace);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int VMX_DecodeBGRA(IntPtr Instance, byte[] dst, int stride)
|
|
||||||
{
|
|
||||||
return VMXUnmanagedIOS.VMX_DecodeBGRA(Instance, dst, stride);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int VMX_DecodeBGRX(IntPtr Instance, byte[] dst, int stride)
|
|
||||||
{
|
|
||||||
return VMXUnmanagedIOS.VMX_DecodeBGRX(Instance, dst, stride);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int VMX_DecodePreviewBGRA(IntPtr Instance, byte[] dst, int stride)
|
|
||||||
{
|
|
||||||
return VMXUnmanagedIOS.VMX_DecodePreviewBGRA(Instance, dst, stride);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int VMX_DecodePreviewBGRX(IntPtr Instance, byte[] dst, int stride)
|
|
||||||
{
|
|
||||||
return VMXUnmanagedIOS.VMX_DecodePreviewBGRX(Instance, dst, stride);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int VMX_DecodePreviewUYVY(IntPtr Instance, byte[] dst, int stride)
|
|
||||||
{
|
|
||||||
return VMXUnmanagedIOS.VMX_DecodePreviewUYVY(Instance, dst, stride);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int VMX_DecodePreviewUYVA(IntPtr Instance, byte[] dst, int stride)
|
|
||||||
{
|
|
||||||
return VMXUnmanagedIOS.VMX_DecodePreviewUYVA(Instance, dst, stride);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int VMX_DecodePreviewYUY2(IntPtr Instance, byte[] dst, int stride)
|
|
||||||
{
|
|
||||||
return VMXUnmanagedIOS.VMX_DecodePreviewYUY2(Instance, dst, stride);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int VMX_DecodeUYVY(IntPtr Instance, byte[] dst, int stride)
|
|
||||||
{
|
|
||||||
return VMXUnmanagedIOS.VMX_DecodeUYVY(Instance, dst, stride);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int VMX_DecodeUYVA(IntPtr Instance, byte[] dst, int stride)
|
|
||||||
{
|
|
||||||
return VMXUnmanagedIOS.VMX_DecodeUYVA(Instance, dst, stride);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int VMX_DecodeP216(IntPtr Instance, byte[] dst, int stride)
|
|
||||||
{
|
|
||||||
return VMXUnmanagedIOS.VMX_DecodeP216(Instance, dst, stride);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int VMX_DecodePA16(IntPtr Instance, byte[] dst, int stride)
|
|
||||||
{
|
|
||||||
return VMXUnmanagedIOS.VMX_DecodePA16(Instance, dst, stride);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int VMX_DecodeYUY2(IntPtr Instance, byte[] dst, int stride)
|
|
||||||
{
|
|
||||||
return VMXUnmanagedIOS.VMX_DecodeYUY2(Instance, dst, stride);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void VMX_Destroy(IntPtr instance)
|
|
||||||
{
|
|
||||||
VMXUnmanagedIOS.VMX_Destroy(instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int VMX_EncodeBGRA(IntPtr Instance, IntPtr src, int stride, int interlaced)
|
|
||||||
{
|
|
||||||
return VMXUnmanagedIOS.VMX_EncodeBGRA(Instance, src, stride, interlaced);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int VMX_EncodeBGRX(IntPtr Instance, IntPtr src, int stride, int interlaced)
|
|
||||||
{
|
|
||||||
return VMXUnmanagedIOS.VMX_EncodeBGRX(Instance, src, stride, interlaced);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int VMX_EncodeNV12(IntPtr Instance, IntPtr srcY, int strideY, IntPtr srcUV, int strideUV, int interlaced)
|
|
||||||
{
|
|
||||||
return VMXUnmanagedIOS.VMX_EncodeNV12(Instance, srcY, strideY, srcUV, strideUV, interlaced);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int VMX_EncodeP216(IntPtr Instance, IntPtr src, int stride, int interlaced)
|
|
||||||
{
|
|
||||||
return VMXUnmanagedIOS.VMX_EncodeP216(Instance, src, stride, interlaced);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int VMX_EncodePA16(IntPtr Instance, IntPtr src, int stride, int interlaced)
|
|
||||||
{
|
|
||||||
return VMXUnmanagedIOS.VMX_EncodePA16(Instance, src, stride, interlaced);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int VMX_EncodeUYVY(IntPtr Instance, IntPtr src, int stride, int interlaced)
|
|
||||||
{
|
|
||||||
return VMXUnmanagedIOS.VMX_EncodeUYVY(Instance, src, stride, interlaced);
|
|
||||||
}
|
|
||||||
public int VMX_EncodeUYVA(IntPtr Instance, IntPtr src, int stride, int interlaced)
|
|
||||||
{
|
|
||||||
return VMXUnmanagedIOS.VMX_EncodeUYVA(Instance, src, stride, interlaced);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int VMX_EncodeYUY2(IntPtr Instance, IntPtr src, int stride, int interlaced)
|
|
||||||
{
|
|
||||||
return VMXUnmanagedIOS.VMX_EncodeYUY2(Instance, src, stride, interlaced);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int VMX_EncodeYV12(IntPtr Instance, IntPtr srcY, int strideY, IntPtr srcU, int strideU, IntPtr srcV, int strideV, int interlaced)
|
|
||||||
{
|
|
||||||
return VMXUnmanagedIOS.VMX_EncodeYV12(Instance, srcY, strideY, srcU, strideU, srcV, strideV, interlaced);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int VMX_GetEncodedPreviewLength(IntPtr Instance)
|
|
||||||
{
|
|
||||||
return VMXUnmanagedIOS.VMX_GetEncodedPreviewLength(Instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int VMX_GetThreads(IntPtr instance)
|
|
||||||
{
|
|
||||||
return VMXUnmanagedIOS.VMX_GetThreads(instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int VMX_LoadFrom(IntPtr instance, byte[] data, int dataLen)
|
|
||||||
{
|
|
||||||
return VMXUnmanagedIOS.VMX_LoadFrom(instance, data, dataLen);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int VMX_SaveTo(IntPtr instance, byte[] data, int maxLen)
|
|
||||||
{
|
|
||||||
return VMXUnmanagedIOS.VMX_SaveTo(instance, data, maxLen);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void VMX_SetQuality(IntPtr instance, int q)
|
|
||||||
{
|
|
||||||
VMXUnmanagedIOS.VMX_SetQuality(instance, q);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void VMX_SetThreads(IntPtr instance, int t)
|
|
||||||
{
|
|
||||||
VMXUnmanagedIOS.VMX_SetThreads(instance, t);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int VMX_GetQuality(IntPtr instance)
|
|
||||||
{
|
|
||||||
return VMXUnmanagedIOS.VMX_GetQuality(instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
/*
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Open Media Transport Contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace libomtnet.codecs
|
|
||||||
{
|
|
||||||
internal class VMXUnmanaged
|
|
||||||
{
|
|
||||||
private const string DLLPATH = @"libvmx";
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern IntPtr VMX_Create(OMTSize dimensions, VMXProfile profile, VMXColorSpace colorSpace);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern void VMX_Destroy(IntPtr instance);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern void VMX_SetQuality(IntPtr instance, int q);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern int VMX_GetQuality(IntPtr instance);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern void VMX_SetThreads(IntPtr instance, int t);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern int VMX_GetThreads(IntPtr instance);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern int VMX_LoadFrom(IntPtr instance, byte[] data, int dataLen);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern int VMX_SaveTo(IntPtr instance, byte[] data, int maxLen);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern int VMX_EncodeBGRA(IntPtr Instance, IntPtr src, int stride, int interlaced);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern int VMX_EncodeBGRX(IntPtr Instance, IntPtr src, int stride, int interlaced);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern int VMX_EncodeP216(IntPtr Instance, IntPtr src, int stride, int interlaced);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern int VMX_EncodePA16(IntPtr Instance, IntPtr src, int stride, int interlaced);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern int VMX_EncodeUYVY(IntPtr Instance, IntPtr src, int stride, int interlaced);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern int VMX_EncodeUYVA(IntPtr Instance, IntPtr src, int stride, int interlaced);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern int VMX_EncodeYUY2(IntPtr Instance, IntPtr src, int stride, int interlaced);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern int VMX_EncodeNV12(IntPtr Instance, IntPtr srcY, int strideY, IntPtr srcUV, int strideUV, int interlaced);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern int VMX_EncodeYV12(IntPtr Instance, IntPtr srcY, int strideY, IntPtr srcU, int strideU, IntPtr srcV, int strideV, int interlaced);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern int VMX_DecodeUYVY(IntPtr Instance, byte[] dst, int stride);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern int VMX_DecodeUYVA(IntPtr Instance, byte[] dst, int stride);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern int VMX_DecodeP216(IntPtr Instance, byte[] dst, int stride);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern int VMX_DecodePA16(IntPtr Instance, byte[] dst, int stride);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern int VMX_DecodeYUY2(IntPtr Instance, byte[] dst, int stride);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern int VMX_DecodeBGRX(IntPtr Instance, byte[] dst, int stride);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern int VMX_DecodeBGRA(IntPtr Instance, byte[] dst, int stride);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern int VMX_DecodePreviewUYVY(IntPtr Instance, byte[] dst, int stride);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern int VMX_DecodePreviewYUY2(IntPtr Instance, byte[] dst, int stride);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern int VMX_DecodePreviewBGRA(IntPtr Instance, byte[] dst, int stride);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern int VMX_DecodePreviewBGRX(IntPtr Instance, byte[] dst, int stride);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern int VMX_DecodePreviewUYVA(IntPtr Instance, byte[] dst, int stride);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern int VMX_GetEncodedPreviewLength(IntPtr Instance);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern float VMX_CalculatePSNR(byte[] image1, byte[] image2, int stride, int bytesPerPixel, OMTSize sz);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
/*
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Open Media Transport Contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace libomtnet.codecs
|
|
||||||
{
|
|
||||||
internal class VMXUnmanagedIOS
|
|
||||||
{
|
|
||||||
private const string DLLPATH = @"@rpath/libvmx.framework/libvmx";
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern IntPtr VMX_Create(OMTSize dimensions, VMXProfile profile, VMXColorSpace colorSpace);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern void VMX_Destroy(IntPtr instance);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern void VMX_SetQuality(IntPtr instance, int q);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern int VMX_GetQuality(IntPtr instance);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern void VMX_SetThreads(IntPtr instance, int t);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern int VMX_GetThreads(IntPtr instance);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern int VMX_LoadFrom(IntPtr instance, byte[] data, int dataLen);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern int VMX_SaveTo(IntPtr instance, byte[] data, int maxLen);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern int VMX_EncodeBGRA(IntPtr Instance, IntPtr src, int stride, int interlaced);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern int VMX_EncodeBGRX(IntPtr Instance, IntPtr src, int stride, int interlaced);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern int VMX_EncodeP216(IntPtr Instance, IntPtr src, int stride, int interlaced);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern int VMX_EncodePA16(IntPtr Instance, IntPtr src, int stride, int interlaced);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern int VMX_EncodeUYVY(IntPtr Instance, IntPtr src, int stride, int interlaced);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern int VMX_EncodeUYVA(IntPtr Instance, IntPtr src, int stride, int interlaced);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern int VMX_EncodeYUY2(IntPtr Instance, IntPtr src, int stride, int interlaced);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern int VMX_EncodeNV12(IntPtr Instance, IntPtr srcY, int strideY, IntPtr srcUV, int strideUV, int interlaced);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern int VMX_EncodeYV12(IntPtr Instance, IntPtr srcY, int strideY, IntPtr srcU, int strideU, IntPtr srcV, int strideV, int interlaced);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern int VMX_DecodeUYVY(IntPtr Instance, byte[] dst, int stride);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern int VMX_DecodeUYVA(IntPtr Instance, byte[] dst, int stride);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern int VMX_DecodeP216(IntPtr Instance, byte[] dst, int stride);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern int VMX_DecodePA16(IntPtr Instance, byte[] dst, int stride);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern int VMX_DecodeYUY2(IntPtr Instance, byte[] dst, int stride);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern int VMX_DecodeBGRX(IntPtr Instance, byte[] dst, int stride);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern int VMX_DecodeBGRA(IntPtr Instance, byte[] dst, int stride);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern int VMX_DecodePreviewUYVY(IntPtr Instance, byte[] dst, int stride);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern int VMX_DecodePreviewYUY2(IntPtr Instance, byte[] dst, int stride);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern int VMX_DecodePreviewBGRA(IntPtr Instance, byte[] dst, int stride);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern int VMX_DecodePreviewBGRX(IntPtr Instance, byte[] dst, int stride);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern int VMX_DecodePreviewUYVA(IntPtr Instance, byte[] dst, int stride);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern int VMX_GetEncodedPreviewLength(IntPtr Instance);
|
|
||||||
[DllImport(DLLPATH)]
|
|
||||||
internal static extern float VMX_CalculatePSNR(byte[] image1, byte[] image2, int stride, int bytesPerPixel, OMTSize sz);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,106 +0,0 @@
|
|||||||
/*
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Open Media Transport Contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace libomtnet.linux
|
|
||||||
{
|
|
||||||
internal class AvahiClient
|
|
||||||
{
|
|
||||||
public const string DLL_PATH_CLIENT = "libavahi-client.so.3";
|
|
||||||
public const string DLL_PATH_COMMON = "libavahi-common.so.3";
|
|
||||||
|
|
||||||
public const int AVAHI_PROTO_UNSPEC = -1;
|
|
||||||
public const int AVAHI_PROTO_INET = 0;
|
|
||||||
public const int AVAHI_PROTO_INET6 = 1;
|
|
||||||
public const int AVAHI_IF_UNSPEC = -1;
|
|
||||||
|
|
||||||
public delegate void AvahiClientCallback(IntPtr s, int state, IntPtr userData);
|
|
||||||
public delegate void AvahiServiceBrowserCallback(IntPtr b, int iface, int protocol, AvahiBrowserEvent evt, IntPtr name, IntPtr type, IntPtr domain, int flags, IntPtr userData);
|
|
||||||
public delegate void AvahiServiceResolverCallback(IntPtr r, int iface, int protocol, AvahiResolverEvent evt, IntPtr name, IntPtr type, IntPtr domain,
|
|
||||||
IntPtr host_name, IntPtr a, UInt16 port, IntPtr txt, int flags, IntPtr userData);
|
|
||||||
public delegate void AvahiEntryGroupCallback(IntPtr group, int state, IntPtr userData);
|
|
||||||
|
|
||||||
public enum AvahiBrowserEvent
|
|
||||||
{
|
|
||||||
AVAHI_BROWSER_NEW,
|
|
||||||
AVAHI_BROWSER_REMOVE,
|
|
||||||
AVAHI_BROWSER_CACHE_EXHAUSTED,
|
|
||||||
AVAHI_BROWSER_ALL_FOR_NOW,
|
|
||||||
AVAHI_BROWSER_FAILURE
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum AvahiResolverEvent
|
|
||||||
{
|
|
||||||
AVAHI_RESOLVER_FOUND,
|
|
||||||
AVAHI_RESOLVER_FAILURE
|
|
||||||
}
|
|
||||||
|
|
||||||
[DllImport(DLL_PATH_COMMON)]
|
|
||||||
public static extern IntPtr avahi_simple_poll_new();
|
|
||||||
|
|
||||||
[DllImport(DLL_PATH_COMMON)]
|
|
||||||
public static extern void avahi_simple_poll_free(IntPtr sp);
|
|
||||||
|
|
||||||
[DllImport(DLL_PATH_COMMON)]
|
|
||||||
public static extern void avahi_simple_poll_quit(IntPtr sp);
|
|
||||||
|
|
||||||
[DllImport(DLL_PATH_COMMON)]
|
|
||||||
public static extern IntPtr avahi_simple_poll_get(IntPtr s);
|
|
||||||
|
|
||||||
[DllImport(DLL_PATH_COMMON)]
|
|
||||||
public static extern int avahi_simple_poll_iterate(IntPtr sp, int sleepTime);
|
|
||||||
|
|
||||||
[DllImport(DLL_PATH_CLIENT)]
|
|
||||||
public static extern IntPtr avahi_client_new(IntPtr poll, int flags, IntPtr callback, IntPtr userdata, ref int error);
|
|
||||||
[DllImport(DLL_PATH_CLIENT)]
|
|
||||||
public static extern IntPtr avahi_service_browser_new(IntPtr client, int iface, int protocol, IntPtr type, IntPtr domain, int flags, IntPtr callback, IntPtr userData);
|
|
||||||
|
|
||||||
[DllImport(DLL_PATH_CLIENT)]
|
|
||||||
public static extern void avahi_client_free(IntPtr client);
|
|
||||||
|
|
||||||
[DllImport(DLL_PATH_CLIENT)]
|
|
||||||
public static extern int avahi_service_browser_free(IntPtr browser);
|
|
||||||
|
|
||||||
[DllImport(DLL_PATH_CLIENT)]
|
|
||||||
public static extern IntPtr avahi_service_resolver_new(IntPtr client, int iface, int protocol, IntPtr name, IntPtr type, IntPtr domain, int aprotocol, int flags, IntPtr callback, IntPtr userData);
|
|
||||||
|
|
||||||
[DllImport(DLL_PATH_CLIENT)]
|
|
||||||
public static extern int avahi_service_resolver_free(IntPtr resolver);
|
|
||||||
|
|
||||||
[DllImport(DLL_PATH_CLIENT)]
|
|
||||||
public static extern IntPtr avahi_entry_group_new(IntPtr client, IntPtr callback, IntPtr userData);
|
|
||||||
[DllImport(DLL_PATH_CLIENT)]
|
|
||||||
public static extern int avahi_entry_group_free(IntPtr group);
|
|
||||||
|
|
||||||
[DllImport(DLL_PATH_CLIENT)]
|
|
||||||
public static extern int avahi_entry_group_add_service(IntPtr group, int iface, int protocol, int flags, IntPtr name,
|
|
||||||
IntPtr type, IntPtr domain, IntPtr host, UInt16 port, IntPtr args);
|
|
||||||
|
|
||||||
[DllImport(DLL_PATH_CLIENT)]
|
|
||||||
public static extern int avahi_entry_group_commit(IntPtr group);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
/*
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Open Media Transport Contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace libomtnet.linux
|
|
||||||
{ internal class LinuxPlatform : OMTPlatform
|
|
||||||
{
|
|
||||||
private const int RTLD_NOW = 2;
|
|
||||||
private const int RTLD_GLOBAL = 8;
|
|
||||||
|
|
||||||
[DllImport("libdl.so")]
|
|
||||||
static extern IntPtr dlopen(string filename, int flags);
|
|
||||||
|
|
||||||
[DllImport("libc")]
|
|
||||||
private static extern int gethostname(IntPtr name, IntPtr size);
|
|
||||||
public override string GetMachineName()
|
|
||||||
{
|
|
||||||
int len = 4096;
|
|
||||||
IntPtr buf = Marshal.AllocHGlobal(len);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
int result = gethostname(buf, (IntPtr)len);
|
|
||||||
if (result == 0)
|
|
||||||
{
|
|
||||||
string name = OMTUtils.PtrToStringUTF8(buf);
|
|
||||||
if (!String.IsNullOrEmpty(name))
|
|
||||||
{
|
|
||||||
return name.ToUpper();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
Marshal.FreeHGlobal(buf);
|
|
||||||
}
|
|
||||||
OMTLogging.Write("Unable to retrieve full hostname", "LinuxPlatform");
|
|
||||||
return base.GetMachineName();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string GetStoragePath()
|
|
||||||
{
|
|
||||||
string sz = Environment.GetEnvironmentVariable("OMT_STORAGE_PATH");
|
|
||||||
if (!String.IsNullOrEmpty(sz)) return sz;
|
|
||||||
return Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + Path.DirectorySeparatorChar + ".OMT";
|
|
||||||
}
|
|
||||||
|
|
||||||
public override IntPtr OpenLibrary(string filename)
|
|
||||||
{
|
|
||||||
return dlopen(filename, RTLD_GLOBAL | RTLD_NOW);
|
|
||||||
}
|
|
||||||
protected override string GetLibraryExtension()
|
|
||||||
{
|
|
||||||
return ".so";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,292 +0,0 @@
|
|||||||
/*
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Open Media Transport Contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Net;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using static libomtnet.linux.AvahiClient;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace libomtnet.linux
|
|
||||||
{
|
|
||||||
internal class OMTDiscoveryAvahi : OMTDiscovery
|
|
||||||
{
|
|
||||||
private IntPtr client = IntPtr.Zero;
|
|
||||||
private IntPtr simplePoll = IntPtr.Zero;
|
|
||||||
private IntPtr poll = IntPtr.Zero;
|
|
||||||
private IntPtr browser = IntPtr.Zero;
|
|
||||||
private AvahiClient.AvahiClientCallback clientCallback;
|
|
||||||
private AvahiClient.AvahiServiceBrowserCallback serviceBrowserCallback;
|
|
||||||
private AvahiClient.AvahiServiceResolverCallback serviceResolverCallback;
|
|
||||||
private AvahiClient.AvahiEntryGroupCallback entryGroupCallback;
|
|
||||||
private IntPtr serviceType;
|
|
||||||
|
|
||||||
private Thread eventThread;
|
|
||||||
private bool eventThreadRunning = false;
|
|
||||||
|
|
||||||
private class EntryAvahi : OMTDiscoveryEntry
|
|
||||||
{
|
|
||||||
public EntryAvahi(OMTAddress address) : base(address) { }
|
|
||||||
|
|
||||||
public IntPtr Group;
|
|
||||||
protected override void DisposeInternal()
|
|
||||||
{
|
|
||||||
if (Group != null)
|
|
||||||
{
|
|
||||||
AvahiClient.avahi_entry_group_free(Group);
|
|
||||||
Group = IntPtr.Zero;
|
|
||||||
}
|
|
||||||
base.DisposeInternal();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal OMTDiscoveryAvahi()
|
|
||||||
{
|
|
||||||
serviceType = OMTUtils.StringToPtrUTF8("_omt._tcp");
|
|
||||||
|
|
||||||
clientCallback = new AvahiClient.AvahiClientCallback(ClientCallback);
|
|
||||||
entryGroupCallback = new AvahiClient.AvahiEntryGroupCallback(EntryGroupCallback);
|
|
||||||
serviceBrowserCallback = new AvahiClient.AvahiServiceBrowserCallback(ServiceBrowserCallback);
|
|
||||||
serviceResolverCallback = new AvahiClient.AvahiServiceResolverCallback(ServiceResolverCallback);
|
|
||||||
|
|
||||||
simplePoll = AvahiClient.avahi_simple_poll_new();
|
|
||||||
if (simplePoll == IntPtr.Zero) {
|
|
||||||
OMTLogging.Write("Failure creating simple poll", "OMTDiscoveryAvahi");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
poll = AvahiClient.avahi_simple_poll_get(simplePoll);
|
|
||||||
if (poll == IntPtr.Zero) {
|
|
||||||
OMTLogging.Write("Failure retrieving poll", "OMTDiscoveryAvahi");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int hr = 0;
|
|
||||||
client = AvahiClient.avahi_client_new(poll, 0, Marshal.GetFunctionPointerForDelegate(clientCallback), IntPtr.Zero, ref hr);
|
|
||||||
if (client == IntPtr.Zero)
|
|
||||||
{
|
|
||||||
OMTLogging.Write("Failure creating client: " + hr, "OMTDiscoveryAvahi");
|
|
||||||
}
|
|
||||||
browser = AvahiClient.avahi_service_browser_new(client, AvahiClient.AVAHI_IF_UNSPEC, AvahiClient.AVAHI_PROTO_UNSPEC
|
|
||||||
, serviceType, IntPtr.Zero, 0, Marshal.GetFunctionPointerForDelegate(serviceBrowserCallback), IntPtr.Zero);
|
|
||||||
if (browser == IntPtr.Zero)
|
|
||||||
{
|
|
||||||
OMTLogging.Write("Failure creating browser: " + hr, "OMTDiscoveryAvahi");
|
|
||||||
}
|
|
||||||
eventThreadRunning = true;
|
|
||||||
eventThread = new Thread(EventThread);
|
|
||||||
eventThread.IsBackground = true;
|
|
||||||
eventThread.Start();
|
|
||||||
OMTLogging.Write("BrowserStarted", "OMTDiscoveryAvahi");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void EventThread()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
int hr = 0;
|
|
||||||
while (eventThreadRunning)
|
|
||||||
{
|
|
||||||
hr = AvahiClient.avahi_simple_poll_iterate(simplePoll, -1);
|
|
||||||
if (hr != 0)
|
|
||||||
{
|
|
||||||
OMTLogging.Write("EventThead exiting...", "OMTDiscoveryAvahi");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "OMTDiscoveryAvahi.EventThread");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void DisposeInternal()
|
|
||||||
{
|
|
||||||
eventThreadRunning = false;
|
|
||||||
if (simplePoll != null)
|
|
||||||
{
|
|
||||||
AvahiClient.avahi_simple_poll_quit(simplePoll);
|
|
||||||
}
|
|
||||||
if (eventThread != null)
|
|
||||||
{
|
|
||||||
if (eventThread.Join(5000) == false)
|
|
||||||
{
|
|
||||||
eventThread.Abort();
|
|
||||||
}
|
|
||||||
eventThread = null;
|
|
||||||
}
|
|
||||||
if (browser != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
AvahiClient.avahi_service_browser_free(browser);
|
|
||||||
browser = IntPtr.Zero;
|
|
||||||
}
|
|
||||||
if (client != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
AvahiClient.avahi_client_free(client);
|
|
||||||
client = IntPtr.Zero;
|
|
||||||
}
|
|
||||||
if (serviceType != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
Marshal.FreeHGlobal(serviceType);
|
|
||||||
serviceType = IntPtr.Zero;
|
|
||||||
}
|
|
||||||
if (simplePoll != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
AvahiClient.avahi_simple_poll_free(simplePoll);
|
|
||||||
simplePoll = IntPtr.Zero;
|
|
||||||
poll = IntPtr.Zero;
|
|
||||||
}
|
|
||||||
base.DisposeInternal();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal override bool DeregisterAddressInternal(OMTAddress address)
|
|
||||||
{
|
|
||||||
lock (lockSync)
|
|
||||||
{
|
|
||||||
OMTDiscoveryEntry entry = GetEntry(address);
|
|
||||||
if (entry != null)
|
|
||||||
{
|
|
||||||
RemoveEntry(entry.Address, true);
|
|
||||||
OMTLogging.Write("DeRegisterAddress: " + address.ToString(), "OMTDiscoveryAvahi");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
internal override bool RegisterAddressInternal(OMTAddress address)
|
|
||||||
{
|
|
||||||
lock (lockSync)
|
|
||||||
{
|
|
||||||
OMTDiscoveryEntry entry = GetEntry(address);
|
|
||||||
if (entry == null)
|
|
||||||
{
|
|
||||||
EntryAvahi ctx = new EntryAvahi(address);
|
|
||||||
ctx.Group = AvahiClient.avahi_entry_group_new(client, Marshal.GetFunctionPointerForDelegate(entryGroupCallback), IntPtr.Zero);
|
|
||||||
if (ctx.Group == IntPtr.Zero)
|
|
||||||
{
|
|
||||||
OMTLogging.Write("Could not create avahi group", "OMTDiscoveryAvahi");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
IntPtr pName = OMTUtils.StringToPtrUTF8(address.ToString());
|
|
||||||
ushort port = (ushort)address.Port;
|
|
||||||
int hr = AvahiClient.avahi_entry_group_add_service(ctx.Group, AvahiClient.AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, pName, serviceType, IntPtr.Zero, IntPtr.Zero, port, IntPtr.Zero);
|
|
||||||
Marshal.FreeHGlobal(pName);
|
|
||||||
if (hr != 0)
|
|
||||||
{
|
|
||||||
OMTLogging.Write("Could not add entry to avahi group", "OMTDiscoveryAvahi");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
hr = AvahiClient.avahi_entry_group_commit(ctx.Group);
|
|
||||||
if (hr != 0)
|
|
||||||
{
|
|
||||||
OMTLogging.Write("Could not commit new avahi group", "OMTDiscoveryAvahi");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
OMTLogging.Write("RegisterAddress.Success: " + address.ToString(), "OMTDiscoveryAvahi");
|
|
||||||
ctx.ChangeStatus(OMTDiscoveryEntryStatus.Registered);
|
|
||||||
AddEntry(ctx);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ClientCallback(IntPtr s, int state, IntPtr userData)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
private void EntryGroupCallback(IntPtr group, int state, IntPtr userData)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
private void ServiceBrowserCallback(IntPtr b, int iface, int protocol, AvahiBrowserEvent evt, IntPtr name, IntPtr type, IntPtr domain, int flags, IntPtr userData)
|
|
||||||
{
|
|
||||||
if (evt == AvahiBrowserEvent.AVAHI_BROWSER_NEW)
|
|
||||||
{
|
|
||||||
IntPtr resolver = AvahiClient.avahi_service_resolver_new(client, iface, protocol
|
|
||||||
, name, type, domain, AvahiClient.AVAHI_PROTO_UNSPEC, 0, Marshal.GetFunctionPointerForDelegate(serviceResolverCallback), IntPtr.Zero);
|
|
||||||
if (resolver == IntPtr.Zero)
|
|
||||||
{
|
|
||||||
OMTLogging.Write("Failure creating resolver", "OMTDiscoveryAvahi");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else if (evt == AvahiBrowserEvent.AVAHI_BROWSER_REMOVE)
|
|
||||||
{
|
|
||||||
if (name != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
string szName = OMTUtils.PtrToStringUTF8(name);
|
|
||||||
RemoveDiscoveredEntry(szName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private void ServiceResolverCallback(IntPtr r, int iface, int protocol, AvahiResolverEvent evt, IntPtr name, IntPtr type, IntPtr domain,
|
|
||||||
IntPtr host_name, IntPtr a, UInt16 port, IntPtr txt, int flags, IntPtr userData)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (evt == AvahiResolverEvent.AVAHI_RESOLVER_FOUND)
|
|
||||||
{
|
|
||||||
if (name != null)
|
|
||||||
{
|
|
||||||
IPAddress ip = null;
|
|
||||||
if (a != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
int proto = Marshal.ReadInt32(a);
|
|
||||||
if (proto == AVAHI_PROTO_INET)
|
|
||||||
{
|
|
||||||
byte[] addr = new byte[4];
|
|
||||||
Marshal.Copy(a + 4, addr, 0, 4);
|
|
||||||
ip = new IPAddress(addr);
|
|
||||||
}
|
|
||||||
else if (proto == AVAHI_PROTO_INET6)
|
|
||||||
{
|
|
||||||
byte[] addr = new byte[16];
|
|
||||||
Marshal.Copy(a + 4, addr, 0, 16);
|
|
||||||
ip = new IPAddress(addr, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
string addressName = OMTUtils.PtrToStringUTF8(name);
|
|
||||||
if (OMTAddress.IsValid(addressName))
|
|
||||||
{
|
|
||||||
UpdateDiscoveredEntry(addressName, port, new IPAddress[] { ip });
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
OMTLogging.Write("InvalidAddressReceived: " + addressName, "OMTDiscoveryAvahi");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (r != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
AvahiClient.avahi_service_resolver_free(r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "OMTDiscoveryAvahi.Resolver");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,431 +0,0 @@
|
|||||||
/*
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Open Media Transport Contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace libomtnet.mac
|
|
||||||
{
|
|
||||||
internal class DnsSd
|
|
||||||
{
|
|
||||||
public const string DLL_PATH = @"libSystem.dylib";
|
|
||||||
|
|
||||||
public enum DNSServiceFlags
|
|
||||||
{
|
|
||||||
kDNSServiceFlagsMoreComing = 0x1,
|
|
||||||
/* MoreComing indicates to a callback that at least one more result is
|
|
||||||
* queued and will be delivered following immediately after this one.
|
|
||||||
* When the MoreComing flag is set, applications should not immediately
|
|
||||||
* update their UI, because this can result in a great deal of ugly flickering
|
|
||||||
* on the screen, and can waste a great deal of CPU time repeatedly updating
|
|
||||||
* the screen with content that is then immediately erased, over and over.
|
|
||||||
* Applications should wait until MoreComing is not set, and then
|
|
||||||
* update their UI when no more changes are imminent.
|
|
||||||
* When MoreComing is not set, that doesn't mean there will be no more
|
|
||||||
* answers EVER, just that there are no more answers immediately
|
|
||||||
* available right now at this instant. If more answers become available
|
|
||||||
* in the future they will be delivered as usual.
|
|
||||||
*/
|
|
||||||
|
|
||||||
kDNSServiceFlagsAutoTrigger = 0x1,
|
|
||||||
/* Valid for browses using kDNSServiceInterfaceIndexAny.
|
|
||||||
* Will auto trigger the browse over AWDL as well once the service is discoveryed
|
|
||||||
* over BLE.
|
|
||||||
* This flag is an input value to DNSServiceBrowse(), which is why we can
|
|
||||||
* use the same value as kDNSServiceFlagsMoreComing, which is an output flag
|
|
||||||
* for various client callbacks.
|
|
||||||
*/
|
|
||||||
|
|
||||||
kDNSServiceFlagsAdd = 0x2,
|
|
||||||
kDNSServiceFlagsDefault = 0x4,
|
|
||||||
/* Flags for domain enumeration and browse/query reply callbacks.
|
|
||||||
* "Default" applies only to enumeration and is only valid in
|
|
||||||
* conjunction with "Add". An enumeration callback with the "Add"
|
|
||||||
* flag NOT set indicates a "Remove", i.e. the domain is no longer
|
|
||||||
* valid.
|
|
||||||
*/
|
|
||||||
|
|
||||||
kDNSServiceFlagsNoAutoRename = 0x8,
|
|
||||||
/* Flag for specifying renaming behavior on name conflict when registering
|
|
||||||
* non-shared records. By default, name conflicts are automatically handled
|
|
||||||
* by renaming the service. NoAutoRename overrides this behavior - with this
|
|
||||||
* flag set, name conflicts will result in a callback. The NoAutorename flag
|
|
||||||
* is only valid if a name is explicitly specified when registering a service
|
|
||||||
* (i.e. the default name is not used.)
|
|
||||||
*/
|
|
||||||
|
|
||||||
kDNSServiceFlagsShared = 0x10,
|
|
||||||
kDNSServiceFlagsUnique = 0x20,
|
|
||||||
/* Flag for registering individual records on a connected
|
|
||||||
* DNSServiceRef. Shared indicates that there may be multiple records
|
|
||||||
* with this name on the network (e.g. PTR records). Unique indicates that the
|
|
||||||
* record's name is to be unique on the network (e.g. SRV records).
|
|
||||||
*/
|
|
||||||
|
|
||||||
kDNSServiceFlagsBrowseDomains = 0x40,
|
|
||||||
kDNSServiceFlagsRegistrationDomains = 0x80,
|
|
||||||
/* Flags for specifying domain enumeration type in DNSServiceEnumerateDomains.
|
|
||||||
* BrowseDomains enumerates domains recommended for browsing, RegistrationDomains
|
|
||||||
* enumerates domains recommended for registration.
|
|
||||||
*/
|
|
||||||
|
|
||||||
kDNSServiceFlagsLongLivedQuery = 0x100,
|
|
||||||
/* Flag for creating a long-lived unicast query for the DNSServiceQueryRecord call. */
|
|
||||||
|
|
||||||
kDNSServiceFlagsAllowRemoteQuery = 0x200,
|
|
||||||
/* Flag for creating a record for which we will answer remote queries
|
|
||||||
* (queries from hosts more than one hop away; hosts not directly connected to the local link).
|
|
||||||
*/
|
|
||||||
|
|
||||||
kDNSServiceFlagsForceMulticast = 0x400,
|
|
||||||
/* Flag for signifying that a query or registration should be performed exclusively via multicast
|
|
||||||
* DNS, even for a name in a domain (e.g. foo.apple.com.) that would normally imply unicast DNS.
|
|
||||||
*/
|
|
||||||
|
|
||||||
kDNSServiceFlagsForce = 0x800, // This flag is deprecated.
|
|
||||||
|
|
||||||
kDNSServiceFlagsKnownUnique = 0x800,
|
|
||||||
/*
|
|
||||||
* Client guarantees that record names are unique, so we can skip sending out initial
|
|
||||||
* probe messages. Standard name conflict resolution is still done if a conflict is discovered.
|
|
||||||
* Currently only valid for a DNSServiceRegister call.
|
|
||||||
*/
|
|
||||||
|
|
||||||
kDNSServiceFlagsReturnIntermediates = 0x1000,
|
|
||||||
/* Flag for returning intermediate results.
|
|
||||||
* For example, if a query results in an authoritative NXDomain (name does not exist)
|
|
||||||
* then that result is returned to the client. However the query is not implicitly
|
|
||||||
* cancelled -- it remains active and if the answer subsequently changes
|
|
||||||
* (e.g. because a VPN tunnel is subsequently established) then that positive
|
|
||||||
* result will still be returned to the client.
|
|
||||||
* Similarly, if a query results in a CNAME record, then in addition to following
|
|
||||||
* the CNAME referral, the intermediate CNAME result is also returned to the client.
|
|
||||||
* When this flag is not set, NXDomain errors are not returned, and CNAME records
|
|
||||||
* are followed silently without informing the client of the intermediate steps.
|
|
||||||
* (In earlier builds this flag was briefly calledkDNSServiceFlagsReturnCNAME)
|
|
||||||
*/
|
|
||||||
|
|
||||||
kDNSServiceFlagsNonBrowsable = 0x2000,
|
|
||||||
/* A service registered with the NonBrowsable flag set can be resolved using
|
|
||||||
* DNSServiceResolve(), but will not be discoverable using DNSServiceBrowse().
|
|
||||||
* This is for cases where the name is actually a GUID; it is found by other means;
|
|
||||||
* there is no end-user benefit to browsing to find a long list of opaque GUIDs.
|
|
||||||
* Using the NonBrowsable flag creates SRV+TXT without the cost of also advertising
|
|
||||||
* an associated PTR record.
|
|
||||||
*/
|
|
||||||
|
|
||||||
kDNSServiceFlagsShareConnection = 0x4000,
|
|
||||||
/* For efficiency, clients that perform many concurrent operations may want to use a
|
|
||||||
* single Unix Domain Socket connection with the background daemon, instead of having a
|
|
||||||
* separate connection for each independent operation. To use this mode, clients first
|
|
||||||
* call DNSServiceCreateConnection(&MainRef) to initialize the main DNSServiceRef.
|
|
||||||
* For each subsequent operation that is to share that same connection, the client copies
|
|
||||||
* the MainRef, and then passes the address of that copy, setting the ShareConnection flag
|
|
||||||
* to tell the library that this DNSServiceRef is not a typical uninitialized DNSServiceRef;
|
|
||||||
* it's a copy of an existing DNSServiceRef whose connection information should be reused.
|
|
||||||
*
|
|
||||||
* For example:
|
|
||||||
*
|
|
||||||
* DNSServiceErrorType error;
|
|
||||||
* DNSServiceRef MainRef;
|
|
||||||
* error = DNSServiceCreateConnection(&MainRef);
|
|
||||||
* if (error) ...
|
|
||||||
* DNSServiceRef BrowseRef = MainRef; // Important: COPY the primary DNSServiceRef first...
|
|
||||||
* error = DNSServiceBrowse(&BrowseRef, kDNSServiceFlagsShareConnection, ...); // then use the copy
|
|
||||||
* if (error) ...
|
|
||||||
* ...
|
|
||||||
* DNSServiceRefDeallocate(BrowseRef); // Terminate the browse operation
|
|
||||||
* DNSServiceRefDeallocate(MainRef); // Terminate the shared connection
|
|
||||||
* Also see Point 4.(Don't Double-Deallocate if the MainRef has been Deallocated) in Notes below:
|
|
||||||
*
|
|
||||||
* Notes:
|
|
||||||
*
|
|
||||||
* 1. Collective kDNSServiceFlagsMoreComing flag
|
|
||||||
* When callbacks are invoked using a shared DNSServiceRef, the
|
|
||||||
* kDNSServiceFlagsMoreComing flag applies collectively to *all* active
|
|
||||||
* operations sharing the same parent DNSServiceRef. If the MoreComing flag is
|
|
||||||
* set it means that there are more results queued on this parent DNSServiceRef,
|
|
||||||
* but not necessarily more results for this particular callback function.
|
|
||||||
* The implication of this for client programmers is that when a callback
|
|
||||||
* is invoked with the MoreComing flag set, the code should update its
|
|
||||||
* internal data structures with the new result, and set a variable indicating
|
|
||||||
* that its UI needs to be updated. Then, later when a callback is eventually
|
|
||||||
* invoked with the MoreComing flag not set, the code should update *all*
|
|
||||||
* stale UI elements related to that shared parent DNSServiceRef that need
|
|
||||||
* updating, not just the UI elements related to the particular callback
|
|
||||||
* that happened to be the last one to be invoked.
|
|
||||||
*
|
|
||||||
* 2. Canceling operations and kDNSServiceFlagsMoreComing
|
|
||||||
* Whenever you cancel any operation for which you had deferred UI updates
|
|
||||||
* waiting because of a kDNSServiceFlagsMoreComing flag, you should perform
|
|
||||||
* those deferred UI updates. This is because, after cancelling the operation,
|
|
||||||
* you can no longer wait for a callback *without* MoreComing set, to tell
|
|
||||||
* you do perform your deferred UI updates (the operation has been canceled,
|
|
||||||
* so there will be no more callbacks). An implication of the collective
|
|
||||||
* kDNSServiceFlagsMoreComing flag for shared connections is that this
|
|
||||||
* guideline applies more broadly -- any time you cancel an operation on
|
|
||||||
* a shared connection, you should perform all deferred UI updates for all
|
|
||||||
* operations sharing that connection. This is because the MoreComing flag
|
|
||||||
* might have been referring to events coming for the operation you canceled,
|
|
||||||
* which will now not be coming because the operation has been canceled.
|
|
||||||
*
|
|
||||||
* 3. Only share DNSServiceRef's created with DNSServiceCreateConnection
|
|
||||||
* Calling DNSServiceCreateConnection(&ref) creates a special shareable DNSServiceRef.
|
|
||||||
* DNSServiceRef's created by other calls like DNSServiceBrowse() or DNSServiceResolve()
|
|
||||||
* cannot be shared by copying them and using kDNSServiceFlagsShareConnection.
|
|
||||||
*
|
|
||||||
* 4. Don't Double-Deallocate if the MainRef has been Deallocated
|
|
||||||
* Calling DNSServiceRefDeallocate(ref) for a particular operation's DNSServiceRef terminates
|
|
||||||
* just that operation. Calling DNSServiceRefDeallocate(ref) for the main shared DNSServiceRef
|
|
||||||
* (the parent DNSServiceRef, originally created by DNSServiceCreateConnection(&ref))
|
|
||||||
* automatically terminates the shared connection and all operations that were still using it.
|
|
||||||
* After doing this, DO NOT then attempt to deallocate any remaining subordinate DNSServiceRef's.
|
|
||||||
* The memory used by those subordinate DNSServiceRef's has already been freed, so any attempt
|
|
||||||
* to do a DNSServiceRefDeallocate (or any other operation) on them will result in accesses
|
|
||||||
* to freed memory, leading to crashes or other equally undesirable results.
|
|
||||||
*
|
|
||||||
* 5. Thread Safety
|
|
||||||
* The dns_sd.h API does not presuppose any particular threading model, and consequently
|
|
||||||
* does no locking internally (which would require linking with a specific threading library).
|
|
||||||
* If the client concurrently, from multiple threads (or contexts), calls API routines using
|
|
||||||
* the same DNSServiceRef, it is the client's responsibility to provide mutual exclusion for
|
|
||||||
* that DNSServiceRef.
|
|
||||||
|
|
||||||
* For example, use of DNSServiceRefDeallocate requires caution. A common mistake is as follows:
|
|
||||||
* Thread B calls DNSServiceRefDeallocate to deallocate sdRef while Thread A is processing events
|
|
||||||
* using sdRef. Doing this will lead to intermittent crashes on thread A if the sdRef is used after
|
|
||||||
* it was deallocated.
|
|
||||||
|
|
||||||
* A telltale sign of this crash type is to see DNSServiceProcessResult on the stack preceding the
|
|
||||||
* actual crash location.
|
|
||||||
|
|
||||||
* To state this more explicitly, mDNSResponder does not queue DNSServiceRefDeallocate so
|
|
||||||
* that it occurs discretely before or after an event is handled.
|
|
||||||
*/
|
|
||||||
|
|
||||||
kDNSServiceFlagsSuppressUnusable = 0x8000,
|
|
||||||
/*
|
|
||||||
* This flag is meaningful only in DNSServiceQueryRecord which suppresses unusable queries on the
|
|
||||||
* wire. If "hostname" is a wide-area unicast DNS hostname (i.e. not a ".local." name)
|
|
||||||
* but this host has no routable IPv6 address, then the call will not try to look up IPv6 addresses
|
|
||||||
* for "hostname", since any addresses it found would be unlikely to be of any use anyway. Similarly,
|
|
||||||
* if this host has no routable IPv4 address, the call will not try to look up IPv4 addresses for
|
|
||||||
* "hostname".
|
|
||||||
*/
|
|
||||||
|
|
||||||
kDNSServiceFlagsTimeout = 0x10000,
|
|
||||||
/*
|
|
||||||
* When kDNServiceFlagsTimeout is passed to DNSServiceQueryRecord or DNSServiceGetAddrInfo, the query is
|
|
||||||
* stopped after a certain number of seconds have elapsed. The time at which the query will be stopped
|
|
||||||
* is determined by the system and cannot be configured by the user. The query will be stopped irrespective
|
|
||||||
* of whether a response was given earlier or not. When the query is stopped, the callback will be called
|
|
||||||
* with an error code of kDNSServiceErr_Timeout and a NULL sockaddr will be returned for DNSServiceGetAddrInfo
|
|
||||||
* and zero length rdata will be returned for DNSServiceQueryRecord.
|
|
||||||
*/
|
|
||||||
|
|
||||||
kDNSServiceFlagsIncludeP2P = 0x20000,
|
|
||||||
/*
|
|
||||||
* Include P2P interfaces when kDNSServiceInterfaceIndexAny is specified.
|
|
||||||
* By default, specifying kDNSServiceInterfaceIndexAny does not include P2P interfaces.
|
|
||||||
*/
|
|
||||||
|
|
||||||
kDNSServiceFlagsWakeOnResolve = 0x40000,
|
|
||||||
/*
|
|
||||||
* This flag is meaningful only in DNSServiceResolve. When set, it tries to send a magic packet
|
|
||||||
* to wake up the client.
|
|
||||||
*/
|
|
||||||
|
|
||||||
kDNSServiceFlagsBackgroundTrafficClass = 0x80000,
|
|
||||||
/*
|
|
||||||
* This flag is meaningful for Unicast DNS queries. When set, it uses the background traffic
|
|
||||||
* class for packets that service the request.
|
|
||||||
*/
|
|
||||||
|
|
||||||
kDNSServiceFlagsIncludeAWDL = 0x100000,
|
|
||||||
/*
|
|
||||||
* Include AWDL interface when kDNSServiceInterfaceIndexAny is specified.
|
|
||||||
*/
|
|
||||||
|
|
||||||
kDNSServiceFlagsValidate = 0x200000,
|
|
||||||
/*
|
|
||||||
* This flag is meaningful in DNSServiceGetAddrInfo and DNSServiceQueryRecord. This is the ONLY flag to be valid
|
|
||||||
* as an input to the APIs and also an output through the callbacks in the APIs.
|
|
||||||
*
|
|
||||||
* When this flag is passed to DNSServiceQueryRecord and DNSServiceGetAddrInfo to resolve unicast names,
|
|
||||||
* the response will be validated using DNSSEC. The validation results are delivered using the flags field in
|
|
||||||
* the callback and kDNSServiceFlagsValidate is marked in the flags to indicate that DNSSEC status is also available.
|
|
||||||
* When the callback is called to deliver the query results, the validation results may or may not be available.
|
|
||||||
* If it is not delivered along with the results, the validation status is delivered when the validation completes.
|
|
||||||
*
|
|
||||||
* When the validation results are delivered in the callback, it is indicated by marking the flags with
|
|
||||||
* kDNSServiceFlagsValidate and kDNSServiceFlagsAdd along with the DNSSEC status flags (described below) and a NULL
|
|
||||||
* sockaddr will be returned for DNSServiceGetAddrInfo and zero length rdata will be returned for DNSServiceQueryRecord.
|
|
||||||
* DNSSEC validation results are for the whole RRSet and not just individual records delivered in the callback. When
|
|
||||||
* kDNSServiceFlagsAdd is not set in the flags, applications should implicitly assume that the DNSSEC status of the
|
|
||||||
* RRSet that has been delivered up until that point is not valid anymore, till another callback is called with
|
|
||||||
* kDNSServiceFlagsAdd and kDNSServiceFlagsValidate.
|
|
||||||
*
|
|
||||||
* The following four flags indicate the status of the DNSSEC validation and marked in the flags field of the callback.
|
|
||||||
* When any of the four flags is set, kDNSServiceFlagsValidate will also be set. To check the validation status, the
|
|
||||||
* other applicable output flags should be masked. See kDNSServiceOutputFlags below.
|
|
||||||
*/
|
|
||||||
|
|
||||||
kDNSServiceFlagsSecure = 0x200010,
|
|
||||||
/*
|
|
||||||
* The response has been validated by verifying all the signatures in the response and was able to
|
|
||||||
* build a successful authentication chain starting from a known trust anchor.
|
|
||||||
*/
|
|
||||||
|
|
||||||
kDNSServiceFlagsInsecure = 0x200020,
|
|
||||||
/*
|
|
||||||
* A chain of trust cannot be built starting from a known trust anchor to the response.
|
|
||||||
*/
|
|
||||||
|
|
||||||
kDNSServiceFlagsBogus = 0x200040,
|
|
||||||
/*
|
|
||||||
* If the response cannot be verified to be secure due to expired signatures, missing signatures etc.,
|
|
||||||
* then the results are considered to be bogus.
|
|
||||||
*/
|
|
||||||
|
|
||||||
kDNSServiceFlagsIndeterminate = 0x200080,
|
|
||||||
/*
|
|
||||||
* There is no valid trust anchor that can be used to determine whether a response is secure or not.
|
|
||||||
*/
|
|
||||||
|
|
||||||
kDNSServiceFlagsUnicastResponse = 0x400000,
|
|
||||||
/*
|
|
||||||
* Request unicast response to query.
|
|
||||||
*/
|
|
||||||
kDNSServiceFlagsValidateOptional = 0x800000,
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This flag is identical to kDNSServiceFlagsValidate except for the case where the response
|
|
||||||
* cannot be validated. If this flag is set in DNSServiceQueryRecord or DNSServiceGetAddrInfo,
|
|
||||||
* the DNSSEC records will be requested for validation. If they cannot be received for some reason
|
|
||||||
* during the validation (e.g., zone is not signed, zone is signed but cannot be traced back to
|
|
||||||
* root, recursive server does not understand DNSSEC etc.), then this will fallback to the default
|
|
||||||
* behavior where the validation will not be performed and no DNSSEC results will be provided.
|
|
||||||
*
|
|
||||||
* If the zone is signed and there is a valid path to a known trust anchor configured in the system
|
|
||||||
* and the application requires DNSSEC validation irrespective of the DNSSEC awareness in the current
|
|
||||||
* network, then this option MUST not be used. This is only intended to be used during the transition
|
|
||||||
* period where the different nodes participating in the DNS resolution may not understand DNSSEC or
|
|
||||||
* managed properly (e.g. missing DS record) but still want to be able to resolve DNS successfully.
|
|
||||||
*/
|
|
||||||
|
|
||||||
kDNSServiceFlagsWakeOnlyService = 0x1000000,
|
|
||||||
/*
|
|
||||||
* This flag is meaningful only in DNSServiceRegister. When set, the service will not be registered
|
|
||||||
* with sleep proxy server during sleep.
|
|
||||||
*/
|
|
||||||
|
|
||||||
kDNSServiceFlagsThresholdOne = 0x2000000,
|
|
||||||
kDNSServiceFlagsThresholdFinder = 0x4000000,
|
|
||||||
kDNSServiceFlagsThresholdReached = kDNSServiceFlagsThresholdOne,
|
|
||||||
/*
|
|
||||||
* kDNSServiceFlagsThresholdOne is meaningful only in DNSServiceBrowse. When set,
|
|
||||||
* the system will stop issuing browse queries on the network once the number
|
|
||||||
* of answers returned is one or more. It will issue queries on the network
|
|
||||||
* again if the number of answers drops to zero.
|
|
||||||
* This flag is for Apple internal use only. Third party developers
|
|
||||||
* should not rely on this behavior being supported in any given software release.
|
|
||||||
*
|
|
||||||
* kDNSServiceFlagsThresholdFinder is meaningful only in DNSServiceBrowse. When set,
|
|
||||||
* the system will stop issuing browse queries on the network once the number
|
|
||||||
* of answers has reached the threshold set for Finder.
|
|
||||||
* It will issue queries on the network again if the number of answers drops below
|
|
||||||
* this threshold.
|
|
||||||
* This flag is for Apple internal use only. Third party developers
|
|
||||||
* should not rely on this behavior being supported in any given software release.
|
|
||||||
*
|
|
||||||
* When kDNSServiceFlagsThresholdReached is set in the client callback add or remove event,
|
|
||||||
* it indicates that the browse answer threshold has been reached and no
|
|
||||||
* browse requests will be generated on the network until the number of answers falls
|
|
||||||
* below the threshold value. Add and remove events can still occur based
|
|
||||||
* on incoming Bonjour traffic observed by the system.
|
|
||||||
* The set of services return to the client is not guaranteed to represent the
|
|
||||||
* entire set of services present on the network once the threshold has been reached.
|
|
||||||
*
|
|
||||||
* Note, while kDNSServiceFlagsThresholdReached and kDNSServiceFlagsThresholdOne
|
|
||||||
* have the same value, there isn't a conflict because kDNSServiceFlagsThresholdReached
|
|
||||||
* is only set in the callbacks and kDNSServiceFlagsThresholdOne is only set on
|
|
||||||
* input to a DNSServiceBrowse call.
|
|
||||||
*/
|
|
||||||
kDNSServiceFlagsDenyCellular = 0x8000000,
|
|
||||||
/*
|
|
||||||
* This flag is meaningful only for Unicast DNS queries. When set, the kernel will restrict
|
|
||||||
* DNS resolutions on the cellular interface for that request.
|
|
||||||
*/
|
|
||||||
|
|
||||||
kDNSServiceFlagsServiceIndex = 0x10000000,
|
|
||||||
/*
|
|
||||||
* This flag is meaningful only for DNSServiceGetAddrInfo() for Unicast DNS queries.
|
|
||||||
* When set, DNSServiceGetAddrInfo() will interpret the "interfaceIndex" argument of the call
|
|
||||||
* as the "serviceIndex".
|
|
||||||
*/
|
|
||||||
|
|
||||||
kDNSServiceFlagsDenyExpensive = 0x20000000,
|
|
||||||
/*
|
|
||||||
* This flag is meaningful only for Unicast DNS queries. When set, the kernel will restrict
|
|
||||||
* DNS resolutions on interfaces defined as expensive for that request.
|
|
||||||
*/
|
|
||||||
|
|
||||||
kDNSServiceFlagsPathEvaluationDone = 0x40000000
|
|
||||||
/*
|
|
||||||
* This flag is meaningful for only Unicast DNS queries.
|
|
||||||
* When set, it indicates that Network PathEvaluation has already been performed.
|
|
||||||
*/
|
|
||||||
|
|
||||||
}
|
|
||||||
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
|
|
||||||
public delegate void DNSServiceBrowseReply(IntPtr sdRef, DNSServiceFlags flags, uint interfaceIndex, int errorCode, IntPtr serviceName, IntPtr regType, IntPtr replyDomain, IntPtr context);
|
|
||||||
|
|
||||||
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
|
|
||||||
public delegate void DNSServiceResolveReply(IntPtr sdRef, DNSServiceFlags flags, uint interfaceIndex, int errorCode, IntPtr fullName, IntPtr hostTarget, UInt16 port, UInt16 txtLen, IntPtr txtRecord, IntPtr context);
|
|
||||||
|
|
||||||
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
|
|
||||||
public delegate void DNSServiceRegisterReply(IntPtr sdRef, DNSServiceFlags flags, int errorCode, IntPtr name, IntPtr regtype, IntPtr domain, IntPtr context);
|
|
||||||
|
|
||||||
|
|
||||||
[DllImport(DLL_PATH)]
|
|
||||||
public static extern int DNSServiceBrowse(ref IntPtr sdRef, DNSServiceFlags flags, uint interfaceIndex, IntPtr regType, IntPtr domain, [MarshalAs(UnmanagedType.FunctionPtr)] DNSServiceBrowseReply callback, IntPtr context);
|
|
||||||
|
|
||||||
[DllImport(DLL_PATH)]
|
|
||||||
public static extern int DNSServiceProcessResult(IntPtr sdRef);
|
|
||||||
|
|
||||||
[DllImport(DLL_PATH)]
|
|
||||||
public static extern int DNSServiceResolve(ref IntPtr sdRef, DNSServiceFlags flags, uint interfaceIndex, IntPtr name, IntPtr regType, IntPtr domain, [MarshalAs(UnmanagedType.FunctionPtr)] DNSServiceResolveReply callback, IntPtr context);
|
|
||||||
|
|
||||||
[DllImport(DLL_PATH)]
|
|
||||||
public static extern void DNSServiceRefDeallocate(IntPtr sdRef);
|
|
||||||
|
|
||||||
[DllImport(DLL_PATH)]
|
|
||||||
public static extern int DNSServiceRegister(ref IntPtr sdRef, uint interfaceIndex, DNSServiceFlags flags,IntPtr name,IntPtr regType, IntPtr domain, IntPtr host, UInt16 port, UInt16 txtLen, IntPtr txtRecord, IntPtr callback, IntPtr context);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,121 +0,0 @@
|
|||||||
/*
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Open Media Transport Contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace libomtnet.mac
|
|
||||||
{
|
|
||||||
internal class MacPlatform : OMTPlatform
|
|
||||||
{
|
|
||||||
private const int RTLD_NOW = 2;
|
|
||||||
private const int RTLD_GLOBAL = 8;
|
|
||||||
|
|
||||||
[DllImport("libdl.dylib")]
|
|
||||||
static extern IntPtr dlopen(string filename, int flags);
|
|
||||||
|
|
||||||
[DllImport("libc")]
|
|
||||||
private static extern uint getuid();
|
|
||||||
|
|
||||||
[DllImport("libc")]
|
|
||||||
private static extern IntPtr getpwuid(uint uid);
|
|
||||||
|
|
||||||
[DllImport("libc")]
|
|
||||||
private static extern int gethostname(IntPtr name, IntPtr size);
|
|
||||||
|
|
||||||
public override string GetMachineName()
|
|
||||||
{
|
|
||||||
int len = 4096;
|
|
||||||
IntPtr buf = Marshal.AllocHGlobal(len);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
int result = gethostname(buf, (IntPtr)len);
|
|
||||||
if (result == 0)
|
|
||||||
{
|
|
||||||
string name = OMTUtils.PtrToStringUTF8(buf);
|
|
||||||
if (!String.IsNullOrEmpty(name))
|
|
||||||
{
|
|
||||||
return name.ToUpper();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
Marshal.FreeHGlobal(buf);
|
|
||||||
}
|
|
||||||
OMTLogging.Write("Unable to retrieve full hostname", "MacPlatform");
|
|
||||||
return base.GetMachineName();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string GetStoragePath()
|
|
||||||
{
|
|
||||||
string sz = Environment.GetEnvironmentVariable("OMT_STORAGE_PATH");
|
|
||||||
if (!String.IsNullOrEmpty(sz)) return sz;
|
|
||||||
sz = GetRealUserHome();
|
|
||||||
if (!String.IsNullOrEmpty(sz)) {
|
|
||||||
return Path.Combine(sz, ".OMT");
|
|
||||||
}
|
|
||||||
return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),".OMT");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Attempts to get the real user home directory by manually reading pointer offset into passwd struct.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Real user home directory path or null if unavailable.</returns>
|
|
||||||
private static string GetRealUserHome()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
uint uid = getuid();
|
|
||||||
IntPtr pwdPtr = getpwuid(uid);
|
|
||||||
if (pwdPtr == IntPtr.Zero) return null;
|
|
||||||
|
|
||||||
int offsetOfPwDir = IntPtr.Size * 6;
|
|
||||||
IntPtr pwDirPtr = Marshal.ReadIntPtr(pwdPtr, offsetOfPwDir);
|
|
||||||
if (pwDirPtr == IntPtr.Zero) return null;
|
|
||||||
|
|
||||||
string home = Marshal.PtrToStringAnsi(pwDirPtr);
|
|
||||||
return home;
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override IntPtr OpenLibrary(string filename)
|
|
||||||
{
|
|
||||||
return dlopen(filename, RTLD_NOW | RTLD_GLOBAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override string GetLibraryExtension()
|
|
||||||
{
|
|
||||||
return ".dylib";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,341 +0,0 @@
|
|||||||
/*
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Open Media Transport Contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
using libomtnet.linux;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Net;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
|
||||||
using static libomtnet.mac.DnsSd;
|
|
||||||
|
|
||||||
namespace libomtnet.mac
|
|
||||||
{
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// DnsSd/Bonjour based Discovery implementation for MacOS
|
|
||||||
/// Issues:
|
|
||||||
/// 1. Windows requests for service responses using unicast (QU), but there can be multiple services on a computer listening on port 5353 such as Chrome
|
|
||||||
/// that will result in these responses being missed by the desired client.
|
|
||||||
/// Solution: On all platforms ensure we refresh periodically so that multicast packets are sent.
|
|
||||||
/// </summary>
|
|
||||||
internal class OMTDiscoveryDnsSd :OMTDiscovery
|
|
||||||
{
|
|
||||||
private DnsSd.DNSServiceBrowseReply browseCallback;
|
|
||||||
private DnsSd.DNSServiceResolveReply resolveCallback;
|
|
||||||
|
|
||||||
private class EntryDnsSd : OMTDiscoveryEntry
|
|
||||||
{
|
|
||||||
public EntryDnsSd(OMTAddress address) : base(address) { }
|
|
||||||
public IntPtr sdRef;
|
|
||||||
public ushort RegisteredPort;
|
|
||||||
public string RegisteredName;
|
|
||||||
|
|
||||||
public void CancelRequest()
|
|
||||||
{
|
|
||||||
if (sdRef != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
DnsSd.DNSServiceRefDeallocate(sdRef);
|
|
||||||
sdRef = IntPtr.Zero;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void DisposeInternal()
|
|
||||||
{
|
|
||||||
CancelRequest();
|
|
||||||
base.DisposeInternal();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<OMTAddress> registeredAddresses = new List<OMTAddress>();
|
|
||||||
private IntPtr browseRef;
|
|
||||||
private Thread processingThread;
|
|
||||||
private Timer refreshTimer;
|
|
||||||
private bool processing;
|
|
||||||
|
|
||||||
internal OMTDiscoveryDnsSd()
|
|
||||||
{
|
|
||||||
BeginDNSBrowse();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void BeginDNSBrowse()
|
|
||||||
{
|
|
||||||
browseCallback = new DnsSd.DNSServiceBrowseReply(OnBrowse);
|
|
||||||
resolveCallback = new DnsSd.DNSServiceResolveReply(OnResolve);
|
|
||||||
|
|
||||||
IntPtr pType = OMTUtils.StringToPtrUTF8("_omt._tcp");
|
|
||||||
int hr = DnsSd.DNSServiceBrowse(ref browseRef, 0, 0,pType, IntPtr.Zero, browseCallback, IntPtr.Zero);
|
|
||||||
if (hr == 0)
|
|
||||||
{
|
|
||||||
OMTLogging.Write("BeginDNSBrowse.OK", "OMTDiscoveryDnsSd");
|
|
||||||
processingThread = new Thread(OnProcessThread);
|
|
||||||
processingThread.IsBackground = true;
|
|
||||||
processing = true;
|
|
||||||
processingThread.Start();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
OMTLogging.Write("BeginDNSBrowse.Error: " + hr, "OMTDiscoveryDnsSd");
|
|
||||||
}
|
|
||||||
Marshal.FreeHGlobal(pType);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnProcessThread(object state)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
while (processing)
|
|
||||||
{
|
|
||||||
if (browseRef == IntPtr.Zero) return;
|
|
||||||
int hr = DnsSd.DNSServiceProcessResult(browseRef);
|
|
||||||
if (hr != 0)
|
|
||||||
{
|
|
||||||
OMTLogging.Write("DNSBrowse.ProcessingError: " + hr, "OMTDiscoveryDnsSd");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "OMTDiscoveryDnsSd.ProcessThread");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void EndDnsBrowse()
|
|
||||||
{
|
|
||||||
if (browseRef != null)
|
|
||||||
{
|
|
||||||
processing = false;
|
|
||||||
DnsSd.DNSServiceRefDeallocate(browseRef);
|
|
||||||
browseRef = IntPtr.Zero;
|
|
||||||
processingThread.Join();
|
|
||||||
processingThread = null;
|
|
||||||
OMTLogging.Write("EndDNSBrowse", "OMTDiscoveryDnsSd");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal override bool DeregisterAddressInternal(OMTAddress address)
|
|
||||||
{
|
|
||||||
lock (lockSync)
|
|
||||||
{
|
|
||||||
OMTDiscoveryEntry entry = GetEntry(address);
|
|
||||||
if (entry != null)
|
|
||||||
{
|
|
||||||
RemoveEntry(entry.Address, true);
|
|
||||||
OMTLogging.Write("DeRegisterAddress: " + address.ToString(), "OMTDiscoveryDnsSd");
|
|
||||||
if (GetRegisteredEntryCount() == 0)
|
|
||||||
{
|
|
||||||
//StopRefreshTimer();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] CreateTXTRecord(string record)
|
|
||||||
{
|
|
||||||
byte[] data = UTF8Encoding.UTF8.GetBytes(" " + record);
|
|
||||||
data[0] = (byte)(data.Length - 1);
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal override bool RegisterAddressInternal(OMTAddress address)
|
|
||||||
{
|
|
||||||
lock (lockSync)
|
|
||||||
{
|
|
||||||
OMTDiscoveryEntry ctx = GetEntry(address);
|
|
||||||
if (ctx == null)
|
|
||||||
{
|
|
||||||
string addressName = address.ToString();
|
|
||||||
ushort port = (ushort)address.Port;
|
|
||||||
byte[] b = BitConverter.GetBytes(port);
|
|
||||||
Array.Reverse(b);
|
|
||||||
port = BitConverter.ToUInt16(b, 0);
|
|
||||||
IntPtr pType = OMTUtils.StringToPtrUTF8("_omt._tcp");
|
|
||||||
IntPtr pAddress = OMTUtils.StringToPtrUTF8(addressName);
|
|
||||||
IntPtr newRequest = IntPtr.Zero;
|
|
||||||
int hr = DnsSd.DNSServiceRegister(ref newRequest, 0, 0, pAddress, pType, IntPtr.Zero, IntPtr.Zero, port, 0, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
|
|
||||||
if (hr == 0)
|
|
||||||
{
|
|
||||||
EntryDnsSd sd = new EntryDnsSd(address);
|
|
||||||
sd.sdRef = newRequest;
|
|
||||||
sd.RegisteredPort = port;
|
|
||||||
sd.RegisteredName = addressName;
|
|
||||||
sd.ChangeStatus(OMTDiscoveryEntryStatus.Registered);
|
|
||||||
AddEntry(sd);
|
|
||||||
OMTLogging.Write("RegisterAddress: " + address.ToString(), "OMTDiscoveryDnsSd");
|
|
||||||
//StartRefreshTimer(); //No longer required since moving to QM requests on Win32
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
OMTLogging.Write("RegisterAddress.Error: " + hr, "OMTDiscoveryDnsSd");
|
|
||||||
}
|
|
||||||
Marshal.FreeHGlobal(pType);
|
|
||||||
Marshal.FreeHGlobal(pAddress);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void StartRefreshTimer()
|
|
||||||
{
|
|
||||||
if (refreshTimer == null)
|
|
||||||
{
|
|
||||||
refreshTimer = new Timer(RefreshTimerCallback, null, 10000, 10000);
|
|
||||||
OMTLogging.Write("StartRefreshTimer", "OMTDiscoveryDnsSd");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private void StopRefreshTimer()
|
|
||||||
{
|
|
||||||
if (refreshTimer != null)
|
|
||||||
{
|
|
||||||
refreshTimer.Dispose();
|
|
||||||
refreshTimer = null;
|
|
||||||
OMTLogging.Write("StopRefreshTimer", "OMTDiscoveryDnsSd");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private void RefreshTimerCallback(object state)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
lock (lockSync)
|
|
||||||
{
|
|
||||||
foreach (OMTDiscoveryEntry entry in entries)
|
|
||||||
{
|
|
||||||
if (entry.Status == OMTDiscoveryEntryStatus.Registered)
|
|
||||||
{
|
|
||||||
EntryDnsSd sd = (EntryDnsSd)entry;
|
|
||||||
if (sd.sdRef != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
IntPtr pType = OMTUtils.StringToPtrUTF8("_omt._tcp");
|
|
||||||
IntPtr pAddress = OMTUtils.StringToPtrUTF8(sd.RegisteredName);
|
|
||||||
DnsSd.DNSServiceRefDeallocate(sd.sdRef);
|
|
||||||
sd.sdRef = IntPtr.Zero;
|
|
||||||
int hr = DnsSd.DNSServiceRegister(ref sd.sdRef, 0, 0, pAddress, pType, IntPtr.Zero, IntPtr.Zero, sd.RegisteredPort, 0, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
|
|
||||||
if (hr != 0)
|
|
||||||
{
|
|
||||||
OMTLogging.Write("RefreshAddress.Error: " + hr, "OMTDiscoveryDnsSd");
|
|
||||||
}
|
|
||||||
Marshal.FreeHGlobal(pType);
|
|
||||||
Marshal.FreeHGlobal(pAddress);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "OMTDiscoveryDnsSd");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void DisposeInternal()
|
|
||||||
{
|
|
||||||
EndDnsBrowse();
|
|
||||||
//StopRefreshTimer();
|
|
||||||
base.DisposeInternal();
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnResolve(IntPtr sdRef, DNSServiceFlags flags, uint interfaceIndex, int errorCode, IntPtr fullName, IntPtr hostTarget, UInt16 port, UInt16 txtLen, IntPtr txtRecord, IntPtr context)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
string addressName = "";
|
|
||||||
if (context != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
addressName = OMTUtils.PtrToStringUTF8(context);
|
|
||||||
}
|
|
||||||
if (errorCode == 0)
|
|
||||||
{
|
|
||||||
string szHostTarget = OMTUtils.PtrToStringUTF8(hostTarget);
|
|
||||||
string szFullName = OMTUtils.PtrToStringUTF8(fullName);
|
|
||||||
|
|
||||||
if (OMTAddress.IsValid(addressName)) {
|
|
||||||
|
|
||||||
byte[] b = BitConverter.GetBytes(port);
|
|
||||||
Array.Reverse(b);
|
|
||||||
port = BitConverter.ToUInt16(b, 0);
|
|
||||||
IPHostEntry a = Dns.GetHostEntry(szHostTarget);
|
|
||||||
UpdateDiscoveredEntry(addressName, port, a.AddressList);
|
|
||||||
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
OMTLogging.Write("InvalidAddressReceived: " + addressName, "OMTDiscoveryDnsSd");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
OMTLogging.Write("OnResolve.Error: " + errorCode, "OMTDiscoveryDnsSd");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "OMTDiscoveryDnsSd");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnBrowse(IntPtr sdRef, DNSServiceFlags flags, uint interfaceIndex, int errorCode, IntPtr serviceName, IntPtr regType, IntPtr replyDomain, IntPtr context)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (errorCode == 0)
|
|
||||||
{
|
|
||||||
if (serviceName != IntPtr.Zero && regType != IntPtr.Zero && replyDomain != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
string szServiceName = OMTUtils.PtrToStringUTF8(serviceName);
|
|
||||||
if (flags.HasFlag(DnsSd.DNSServiceFlags.kDNSServiceFlagsAdd))
|
|
||||||
{
|
|
||||||
IntPtr sdRRef = IntPtr.Zero;
|
|
||||||
IntPtr ctx = OMTUtils.StringToPtrUTF8(szServiceName);
|
|
||||||
int hr = DnsSd.DNSServiceResolve(ref sdRRef, 0, 0, serviceName, regType, replyDomain, resolveCallback, ctx);
|
|
||||||
if (hr == 0)
|
|
||||||
{
|
|
||||||
hr = DnsSd.DNSServiceProcessResult(sdRRef);
|
|
||||||
}
|
|
||||||
Marshal.FreeHGlobal(ctx);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
RemoveDiscoveredEntry(szServiceName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
OMTLogging.Write("OnBrowse.Error: " + errorCode, "OMTDiscoveryDnsSd");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "OMTDiscoveryDnsSd");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
/*
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Open Media Transport Contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace libomtnet.mac
|
|
||||||
{
|
|
||||||
internal class OMTDiscoveryMac : OMTDiscoveryDnsSd
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,232 +0,0 @@
|
|||||||
/*
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Open Media Transport Contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
|
||||||
using System.Net.NetworkInformation;
|
|
||||||
using System.Net.Sockets;
|
|
||||||
using System.Net;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace libomtnet.src.mdns
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// This class periodically sends out a MDNS QM (multicast) query for the service type.
|
|
||||||
/// This overcomes a limitation of DNS-SD API on Windows, where it will stop sending out queries after some time.
|
|
||||||
/// </summary>
|
|
||||||
internal class MDNSClient : OMTBase
|
|
||||||
{
|
|
||||||
private const int DEFAULT_PORT = 5353;
|
|
||||||
private const string MULTICAST_ADDRESS = "224.0.0.251";
|
|
||||||
private const string MULTICAST_ADDRESS_V6 = "ff02::fb";
|
|
||||||
private const int SEND_INTERVAL_MILLISECONDS = 8000;
|
|
||||||
|
|
||||||
private Socket[] sockets;
|
|
||||||
private Timer refreshTimer;
|
|
||||||
private byte[] query;
|
|
||||||
|
|
||||||
private IPEndPoint mdns4;
|
|
||||||
private IPEndPoint mdns6;
|
|
||||||
|
|
||||||
private object lockSync = new object();
|
|
||||||
public MDNSClient(string serviceType)
|
|
||||||
{
|
|
||||||
query = CreateDNSQuery(serviceType);
|
|
||||||
mdns4 = new IPEndPoint(IPAddress.Parse(MULTICAST_ADDRESS), DEFAULT_PORT);
|
|
||||||
mdns6 = new IPEndPoint(IPAddress.Parse(MULTICAST_ADDRESS_V6), DEFAULT_PORT);
|
|
||||||
sockets = CreateMulticastSockets();
|
|
||||||
refreshTimer = new Timer(RefreshTimerCallback, null, 0, SEND_INTERVAL_MILLISECONDS);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SendQueryToSocket(Socket s)
|
|
||||||
{
|
|
||||||
if (s.AddressFamily == AddressFamily.InterNetworkV6)
|
|
||||||
{
|
|
||||||
s.SendTo(query, mdns6);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
s.SendTo(query, mdns4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Socket[] CreateMulticastSockets()
|
|
||||||
{
|
|
||||||
List<Socket> l = new List<Socket>();
|
|
||||||
Socket s;
|
|
||||||
NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces();
|
|
||||||
foreach (NetworkInterface n in nics)
|
|
||||||
{
|
|
||||||
if (n.NetworkInterfaceType != NetworkInterfaceType.Loopback)
|
|
||||||
{
|
|
||||||
if (n.SupportsMulticast)
|
|
||||||
{
|
|
||||||
IPInterfaceProperties ip = n.GetIPProperties();
|
|
||||||
if (ip != null)
|
|
||||||
{
|
|
||||||
IPv4InterfaceProperties ipv4 = ip.GetIPv4Properties();
|
|
||||||
if (ipv4 != null)
|
|
||||||
{
|
|
||||||
s = CreateMulticastSocket(AddressFamily.InterNetwork, ipv4.Index, DEFAULT_PORT);
|
|
||||||
if (s != null) l.Add(s);
|
|
||||||
}
|
|
||||||
IPv6InterfaceProperties ipv6 = ip.GetIPv6Properties();
|
|
||||||
if (ipv6 != null)
|
|
||||||
{
|
|
||||||
s = CreateMulticastSocket(AddressFamily.InterNetworkV6, ipv6.Index, DEFAULT_PORT);
|
|
||||||
if (s != null) l.Add(s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return l.ToArray();
|
|
||||||
}
|
|
||||||
private byte[] CreateDNSQuery(string serviceType)
|
|
||||||
{
|
|
||||||
byte[] sn = StringToDNS(serviceType);
|
|
||||||
int messageLength = sn.Length + 16;
|
|
||||||
byte[] query = new byte[messageLength];
|
|
||||||
int pos = 5;
|
|
||||||
query[pos] = 1;
|
|
||||||
pos = 12;
|
|
||||||
Buffer.BlockCopy(sn, 0, query, pos, sn.Length);
|
|
||||||
pos += sn.Length;
|
|
||||||
pos += 1;
|
|
||||||
query[pos] = 12;
|
|
||||||
pos += 2;
|
|
||||||
query[pos] = 1;
|
|
||||||
return query;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Socket CreateMulticastSocket(AddressFamily af, int interfaceIndex, int port)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Socket socket = new Socket(af, SocketType.Dgram, ProtocolType.Udp);
|
|
||||||
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
|
|
||||||
if (af == AddressFamily.InterNetworkV6)
|
|
||||||
{
|
|
||||||
socket.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.MulticastInterface, interfaceIndex);
|
|
||||||
socket.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.MulticastLoopback, false);
|
|
||||||
socket.Bind(new IPEndPoint(IPAddress.IPv6Any, DEFAULT_PORT));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastInterface, IPAddress.HostToNetworkOrder(interfaceIndex));
|
|
||||||
socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastLoopback, false);
|
|
||||||
socket.Bind(new IPEndPoint(IPAddress.Any, DEFAULT_PORT));
|
|
||||||
}
|
|
||||||
return socket;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "MDNSClient");
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] StringToDNS(string str)
|
|
||||||
{
|
|
||||||
using (MemoryStream m = new MemoryStream())
|
|
||||||
{
|
|
||||||
string[] strs = str.Split('.');
|
|
||||||
foreach (string s in strs)
|
|
||||||
{
|
|
||||||
byte[] b = ASCIIEncoding.ASCII.GetBytes(s);
|
|
||||||
m.WriteByte((byte)b.Length);
|
|
||||||
m.Write(b, 0, b.Length);
|
|
||||||
}
|
|
||||||
m.WriteByte(0);
|
|
||||||
return m.ToArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RefreshTimerCallback(object state)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (Exiting) return;
|
|
||||||
lock (lockSync)
|
|
||||||
{
|
|
||||||
if (sockets != null)
|
|
||||||
{
|
|
||||||
foreach (Socket s in sockets)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
SendQueryToSocket(s);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "MDNSClient");
|
|
||||||
List<Socket> list = new List<Socket>();
|
|
||||||
list.AddRange(sockets);
|
|
||||||
list.Remove(s);
|
|
||||||
sockets = list.ToArray();
|
|
||||||
OMTLogging.Write("Removed failed socket", "MDNSClient");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "MDNSClient");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void DisposeInternal()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (refreshTimer != null)
|
|
||||||
{
|
|
||||||
refreshTimer.Dispose();
|
|
||||||
refreshTimer = null;
|
|
||||||
}
|
|
||||||
if (sockets != null)
|
|
||||||
{
|
|
||||||
lock (lockSync)
|
|
||||||
{
|
|
||||||
foreach (Socket s in sockets)
|
|
||||||
{
|
|
||||||
s.Close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sockets = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "MDNSClient");
|
|
||||||
}
|
|
||||||
base.DisposeInternal();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,194 +0,0 @@
|
|||||||
/*
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Open Media Transport Contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Net;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Xml;
|
|
||||||
|
|
||||||
namespace libomtnet
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// This is an internal class used to manage connection to the OMT Discovery Server
|
|
||||||
/// This should not be used directly by client apps, and is declared public for internal testing purposes only.
|
|
||||||
/// </summary>
|
|
||||||
public class OMTDiscoveryClient : OMTBase
|
|
||||||
{
|
|
||||||
private OMTReceive client = null;
|
|
||||||
private OMTDiscovery discovery = null;
|
|
||||||
private Thread processingThread = null;
|
|
||||||
private bool threadExit = false;
|
|
||||||
|
|
||||||
public OMTDiscoveryClient(string address, OMTDiscovery discovery)
|
|
||||||
{
|
|
||||||
this.discovery = discovery;
|
|
||||||
this.client = new OMTReceive(address, this);
|
|
||||||
StartClient();
|
|
||||||
OMTLogging.Write("Started: " + address, "OMTDiscoveryClient");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void StartClient()
|
|
||||||
{
|
|
||||||
if (processingThread == null)
|
|
||||||
{
|
|
||||||
threadExit = false;
|
|
||||||
processingThread = new Thread(ProcessThread);
|
|
||||||
processingThread.IsBackground = true;
|
|
||||||
processingThread.Start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private void StopClient()
|
|
||||||
{
|
|
||||||
if (processingThread != null)
|
|
||||||
{
|
|
||||||
threadExit = true;
|
|
||||||
processingThread.Join();
|
|
||||||
processingThread = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void SendAddress(OMTAddress address)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
string xml = address.ToXML();
|
|
||||||
int bytes = client.SendMetadata(new OMTMetadata(0, xml));
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "OMTDiscoveryClient");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void SendAll()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
OMTAddress[] addresses = discovery.GetAddressesInternal();
|
|
||||||
if (addresses != null)
|
|
||||||
{
|
|
||||||
foreach (OMTAddress a in addresses)
|
|
||||||
{
|
|
||||||
SendAddress(a);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "OMTDiscoveryClient");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void Connected()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
OMTLogging.Write("Connected to server", "OMTDiscoveryClient");
|
|
||||||
SendAll();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "OMTDiscoveryClient");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void Disconnected()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
OMTLogging.Write("Disconnected from server", "OMTDiscoveryClient");
|
|
||||||
discovery.RemoveServerAddresses();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "OMTDiscoveryClient");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ProcessThread()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
OMTMetadata frame = null;
|
|
||||||
while (threadExit == false)
|
|
||||||
{
|
|
||||||
if (client.Receive(400, ref frame))
|
|
||||||
{
|
|
||||||
if (frame != null)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
OMTAddress a = OMTAddress.FromXML(frame.XML);
|
|
||||||
if (a != null)
|
|
||||||
{
|
|
||||||
if (a.removed)
|
|
||||||
{
|
|
||||||
discovery.RemoveEntry(a, true);
|
|
||||||
OMTLogging.Write("RemovedFromServer: " + a.ToString(), "OMTDiscoveryClient");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
OMTLogging.Write("NewFromServer: " + a.ToString(), "OMTDiscoveryClient");
|
|
||||||
OMTDiscoveryEntry e = discovery.UpdateDiscoveredEntry(a.ToString(), a.Port, a.Addresses);
|
|
||||||
if (e != null)
|
|
||||||
{
|
|
||||||
e.FromServer = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
OMTLogging.Write("Invalid XML Received: " + frame.XML, "OMTDiscoveryClient");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "OMTDiscoveryClient");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "OMTDiscoveryClient");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void DisposeInternal()
|
|
||||||
{
|
|
||||||
StopClient();
|
|
||||||
if (client != null)
|
|
||||||
{
|
|
||||||
client.Dispose();
|
|
||||||
client = null;
|
|
||||||
}
|
|
||||||
discovery = null;
|
|
||||||
base.DisposeInternal();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,243 +0,0 @@
|
|||||||
/*
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Open Media Transport Contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Net;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Xml;
|
|
||||||
|
|
||||||
namespace libomtnet
|
|
||||||
{
|
|
||||||
public class OMTDiscoveryServer : OMTBase
|
|
||||||
{
|
|
||||||
private OMTSend send;
|
|
||||||
private Thread processingThread;
|
|
||||||
private bool threadExit = false;
|
|
||||||
|
|
||||||
private List<AddressEntry> addresses;
|
|
||||||
|
|
||||||
private class AddressEntry
|
|
||||||
{
|
|
||||||
public OMTAddress Address;
|
|
||||||
public IPEndPoint EndPoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
public OMTDiscoveryServer(IPEndPoint endpoint)
|
|
||||||
{
|
|
||||||
addresses = new List<AddressEntry>();
|
|
||||||
send = new OMTSend(endpoint, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void StartServer()
|
|
||||||
{
|
|
||||||
if (processingThread == null)
|
|
||||||
{
|
|
||||||
threadExit = false;
|
|
||||||
processingThread = new Thread(ProcessThread);
|
|
||||||
processingThread.IsBackground = true;
|
|
||||||
processingThread.Start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void StopServer()
|
|
||||||
{
|
|
||||||
if (processingThread != null)
|
|
||||||
{
|
|
||||||
threadExit = true;
|
|
||||||
processingThread.Join();
|
|
||||||
processingThread = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private AddressEntry GetEntry(string fullName, int port)
|
|
||||||
{
|
|
||||||
lock (addresses)
|
|
||||||
{
|
|
||||||
foreach (AddressEntry address in addresses)
|
|
||||||
{
|
|
||||||
if (address.Address.ToString().Equals(fullName))
|
|
||||||
{
|
|
||||||
if (address.Address.Port == port)
|
|
||||||
{
|
|
||||||
return address;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RemoveEntriesByEndPoint(IPEndPoint endpoint)
|
|
||||||
{
|
|
||||||
lock (addresses)
|
|
||||||
{
|
|
||||||
List<AddressEntry> toRemove = new List<AddressEntry>();
|
|
||||||
string match = endpoint.ToString();
|
|
||||||
foreach (AddressEntry address in addresses)
|
|
||||||
{
|
|
||||||
if (address.EndPoint.ToString() == match)
|
|
||||||
{
|
|
||||||
toRemove.Add(address);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
foreach (AddressEntry address in toRemove)
|
|
||||||
{
|
|
||||||
RemoveEntry(address, endpoint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AddEntry(OMTAddress address, IPEndPoint endpoint)
|
|
||||||
{
|
|
||||||
lock (addresses)
|
|
||||||
{
|
|
||||||
AddressEntry entry = new AddressEntry();
|
|
||||||
entry.Address = address;
|
|
||||||
entry.EndPoint = endpoint;
|
|
||||||
addresses.Add(entry);
|
|
||||||
SendEntry(entry, null);
|
|
||||||
OMTLogging.Write("Added " + address.ToString() + " From " + endpoint.ToString(), "OMTDiscoveryServer");
|
|
||||||
Console.WriteLine(endpoint.ToString() + " ADDED " + address.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private void RemoveEntry(AddressEntry entry, IPEndPoint endpoint)
|
|
||||||
{
|
|
||||||
lock (addresses)
|
|
||||||
{
|
|
||||||
addresses.Remove(entry);
|
|
||||||
entry.Address.removed = true;
|
|
||||||
SendEntry(entry, null);
|
|
||||||
OMTLogging.Write("Removed " + entry.Address.ToString() + " From " + endpoint.ToString(), "OMTDiscoveryServer");
|
|
||||||
Console.WriteLine(endpoint.ToString() + " REMOVED " + entry.Address.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private void SendEntry(AddressEntry entry, IPEndPoint endpoint)
|
|
||||||
{
|
|
||||||
string xml = entry.Address.ToXML();
|
|
||||||
send.SendMetadata(new OMTMetadata(0, xml), endpoint);
|
|
||||||
}
|
|
||||||
private void SendAllToEndpoint(IPEndPoint endpoint)
|
|
||||||
{
|
|
||||||
lock (addresses)
|
|
||||||
{
|
|
||||||
foreach (AddressEntry entry in addresses)
|
|
||||||
{
|
|
||||||
SendEntry(entry, endpoint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
internal void Connected(IPEndPoint endpoint)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
SendAllToEndpoint(endpoint);
|
|
||||||
Console.WriteLine("Connected: " + endpoint.ToString());
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "OMTDiscoveryServer");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void Disconnected(IPEndPoint endpoint)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
RemoveEntriesByEndPoint(endpoint);
|
|
||||||
Console.WriteLine("Disconnected: " + endpoint.ToString());
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "OMTDiscoveryServer");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ProcessThread()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
OMTMetadata frame = null;
|
|
||||||
XmlDocument xml = new XmlDocument();
|
|
||||||
while (threadExit == false)
|
|
||||||
{
|
|
||||||
if (send.Receive(100, ref frame))
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (frame != null)
|
|
||||||
{
|
|
||||||
OMTAddress a = OMTAddress.FromXML(frame.XML);
|
|
||||||
if (a != null)
|
|
||||||
{
|
|
||||||
AddressEntry entry = GetEntry(a.ToString(), a.Port);
|
|
||||||
if (entry == null)
|
|
||||||
{
|
|
||||||
if (!a.removed)
|
|
||||||
{
|
|
||||||
a.ClearAddresses(); //Any IP addresses provided by client (typically loopback) are cleared so only detected IP is used.
|
|
||||||
a.AddAddress(frame.Endpoint.Address);
|
|
||||||
AddEntry(a, frame.Endpoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
if (a.removed)
|
|
||||||
{
|
|
||||||
RemoveEntry(entry, frame.Endpoint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
OMTLogging.Write("Invalid XML Received: " + frame.XML, "OMTDiscoveryServer");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "OMTDiscoveryServer");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
OMTLogging.Write(ex.ToString(), "OMTDiscoveryServer");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void DisposeInternal()
|
|
||||||
{
|
|
||||||
StopServer();
|
|
||||||
if (send != null)
|
|
||||||
{
|
|
||||||
send.Dispose();
|
|
||||||
send = null;
|
|
||||||
}
|
|
||||||
base.DisposeInternal();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
363
3rd/omt-examples/.gitignore
vendored
363
3rd/omt-examples/.gitignore
vendored
@@ -1,363 +0,0 @@
|
|||||||
## Ignore Visual Studio temporary files, build results, and
|
|
||||||
## files generated by popular Visual Studio add-ons.
|
|
||||||
##
|
|
||||||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
|
||||||
|
|
||||||
# User-specific files
|
|
||||||
*.rsuser
|
|
||||||
*.suo
|
|
||||||
*.user
|
|
||||||
*.userosscache
|
|
||||||
*.sln.docstates
|
|
||||||
|
|
||||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
|
||||||
*.userprefs
|
|
||||||
|
|
||||||
# Mono auto generated files
|
|
||||||
mono_crash.*
|
|
||||||
|
|
||||||
# Build results
|
|
||||||
[Dd]ebug/
|
|
||||||
[Dd]ebugPublic/
|
|
||||||
[Rr]elease/
|
|
||||||
[Rr]eleases/
|
|
||||||
x64/
|
|
||||||
x86/
|
|
||||||
[Ww][Ii][Nn]32/
|
|
||||||
[Aa][Rr][Mm]/
|
|
||||||
[Aa][Rr][Mm]64/
|
|
||||||
bld/
|
|
||||||
[Bb]in/
|
|
||||||
[Oo]bj/
|
|
||||||
[Oo]ut/
|
|
||||||
[Ll]og/
|
|
||||||
[Ll]ogs/
|
|
||||||
|
|
||||||
# Visual Studio 2015/2017 cache/options directory
|
|
||||||
.vs/
|
|
||||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
|
||||||
#wwwroot/
|
|
||||||
|
|
||||||
# Visual Studio 2017 auto generated files
|
|
||||||
Generated\ Files/
|
|
||||||
|
|
||||||
# MSTest test Results
|
|
||||||
[Tt]est[Rr]esult*/
|
|
||||||
[Bb]uild[Ll]og.*
|
|
||||||
|
|
||||||
# NUnit
|
|
||||||
*.VisualState.xml
|
|
||||||
TestResult.xml
|
|
||||||
nunit-*.xml
|
|
||||||
|
|
||||||
# Build Results of an ATL Project
|
|
||||||
[Dd]ebugPS/
|
|
||||||
[Rr]eleasePS/
|
|
||||||
dlldata.c
|
|
||||||
|
|
||||||
# Benchmark Results
|
|
||||||
BenchmarkDotNet.Artifacts/
|
|
||||||
|
|
||||||
# .NET Core
|
|
||||||
project.lock.json
|
|
||||||
project.fragment.lock.json
|
|
||||||
artifacts/
|
|
||||||
|
|
||||||
# ASP.NET Scaffolding
|
|
||||||
ScaffoldingReadMe.txt
|
|
||||||
|
|
||||||
# StyleCop
|
|
||||||
StyleCopReport.xml
|
|
||||||
|
|
||||||
# Files built by Visual Studio
|
|
||||||
*_i.c
|
|
||||||
*_p.c
|
|
||||||
*_h.h
|
|
||||||
*.ilk
|
|
||||||
*.meta
|
|
||||||
*.obj
|
|
||||||
*.iobj
|
|
||||||
*.pch
|
|
||||||
*.pdb
|
|
||||||
*.ipdb
|
|
||||||
*.pgc
|
|
||||||
*.pgd
|
|
||||||
*.rsp
|
|
||||||
*.sbr
|
|
||||||
*.tlb
|
|
||||||
*.tli
|
|
||||||
*.tlh
|
|
||||||
*.tmp
|
|
||||||
*.tmp_proj
|
|
||||||
*_wpftmp.csproj
|
|
||||||
*.log
|
|
||||||
*.vspscc
|
|
||||||
*.vssscc
|
|
||||||
.builds
|
|
||||||
*.pidb
|
|
||||||
*.svclog
|
|
||||||
*.scc
|
|
||||||
|
|
||||||
# Chutzpah Test files
|
|
||||||
_Chutzpah*
|
|
||||||
|
|
||||||
# Visual C++ cache files
|
|
||||||
ipch/
|
|
||||||
*.aps
|
|
||||||
*.ncb
|
|
||||||
*.opendb
|
|
||||||
*.opensdf
|
|
||||||
*.sdf
|
|
||||||
*.cachefile
|
|
||||||
*.VC.db
|
|
||||||
*.VC.VC.opendb
|
|
||||||
|
|
||||||
# Visual Studio profiler
|
|
||||||
*.psess
|
|
||||||
*.vsp
|
|
||||||
*.vspx
|
|
||||||
*.sap
|
|
||||||
|
|
||||||
# Visual Studio Trace Files
|
|
||||||
*.e2e
|
|
||||||
|
|
||||||
# TFS 2012 Local Workspace
|
|
||||||
$tf/
|
|
||||||
|
|
||||||
# Guidance Automation Toolkit
|
|
||||||
*.gpState
|
|
||||||
|
|
||||||
# ReSharper is a .NET coding add-in
|
|
||||||
_ReSharper*/
|
|
||||||
*.[Rr]e[Ss]harper
|
|
||||||
*.DotSettings.user
|
|
||||||
|
|
||||||
# TeamCity is a build add-in
|
|
||||||
_TeamCity*
|
|
||||||
|
|
||||||
# DotCover is a Code Coverage Tool
|
|
||||||
*.dotCover
|
|
||||||
|
|
||||||
# AxoCover is a Code Coverage Tool
|
|
||||||
.axoCover/*
|
|
||||||
!.axoCover/settings.json
|
|
||||||
|
|
||||||
# Coverlet is a free, cross platform Code Coverage Tool
|
|
||||||
coverage*.json
|
|
||||||
coverage*.xml
|
|
||||||
coverage*.info
|
|
||||||
|
|
||||||
# Visual Studio code coverage results
|
|
||||||
*.coverage
|
|
||||||
*.coveragexml
|
|
||||||
|
|
||||||
# NCrunch
|
|
||||||
_NCrunch_*
|
|
||||||
.*crunch*.local.xml
|
|
||||||
nCrunchTemp_*
|
|
||||||
|
|
||||||
# MightyMoose
|
|
||||||
*.mm.*
|
|
||||||
AutoTest.Net/
|
|
||||||
|
|
||||||
# Web workbench (sass)
|
|
||||||
.sass-cache/
|
|
||||||
|
|
||||||
# Installshield output folder
|
|
||||||
[Ee]xpress/
|
|
||||||
|
|
||||||
# DocProject is a documentation generator add-in
|
|
||||||
DocProject/buildhelp/
|
|
||||||
DocProject/Help/*.HxT
|
|
||||||
DocProject/Help/*.HxC
|
|
||||||
DocProject/Help/*.hhc
|
|
||||||
DocProject/Help/*.hhk
|
|
||||||
DocProject/Help/*.hhp
|
|
||||||
DocProject/Help/Html2
|
|
||||||
DocProject/Help/html
|
|
||||||
|
|
||||||
# Click-Once directory
|
|
||||||
publish/
|
|
||||||
|
|
||||||
# Publish Web Output
|
|
||||||
*.[Pp]ublish.xml
|
|
||||||
*.azurePubxml
|
|
||||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
|
||||||
# but database connection strings (with potential passwords) will be unencrypted
|
|
||||||
*.pubxml
|
|
||||||
*.publishproj
|
|
||||||
|
|
||||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
|
||||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
|
||||||
# in these scripts will be unencrypted
|
|
||||||
PublishScripts/
|
|
||||||
|
|
||||||
# NuGet Packages
|
|
||||||
*.nupkg
|
|
||||||
# NuGet Symbol Packages
|
|
||||||
*.snupkg
|
|
||||||
# The packages folder can be ignored because of Package Restore
|
|
||||||
**/[Pp]ackages/*
|
|
||||||
# except build/, which is used as an MSBuild target.
|
|
||||||
!**/[Pp]ackages/build/
|
|
||||||
# Uncomment if necessary however generally it will be regenerated when needed
|
|
||||||
#!**/[Pp]ackages/repositories.config
|
|
||||||
# NuGet v3's project.json files produces more ignorable files
|
|
||||||
*.nuget.props
|
|
||||||
*.nuget.targets
|
|
||||||
|
|
||||||
# Microsoft Azure Build Output
|
|
||||||
csx/
|
|
||||||
*.build.csdef
|
|
||||||
|
|
||||||
# Microsoft Azure Emulator
|
|
||||||
ecf/
|
|
||||||
rcf/
|
|
||||||
|
|
||||||
# Windows Store app package directories and files
|
|
||||||
AppPackages/
|
|
||||||
BundleArtifacts/
|
|
||||||
Package.StoreAssociation.xml
|
|
||||||
_pkginfo.txt
|
|
||||||
*.appx
|
|
||||||
*.appxbundle
|
|
||||||
*.appxupload
|
|
||||||
|
|
||||||
# Visual Studio cache files
|
|
||||||
# files ending in .cache can be ignored
|
|
||||||
*.[Cc]ache
|
|
||||||
# but keep track of directories ending in .cache
|
|
||||||
!?*.[Cc]ache/
|
|
||||||
|
|
||||||
# Others
|
|
||||||
ClientBin/
|
|
||||||
~$*
|
|
||||||
*~
|
|
||||||
*.dbmdl
|
|
||||||
*.dbproj.schemaview
|
|
||||||
*.jfm
|
|
||||||
*.pfx
|
|
||||||
*.publishsettings
|
|
||||||
orleans.codegen.cs
|
|
||||||
|
|
||||||
# Including strong name files can present a security risk
|
|
||||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
|
||||||
#*.snk
|
|
||||||
|
|
||||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
|
||||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
|
||||||
#bower_components/
|
|
||||||
|
|
||||||
# RIA/Silverlight projects
|
|
||||||
Generated_Code/
|
|
||||||
|
|
||||||
# Backup & report files from converting an old project file
|
|
||||||
# to a newer Visual Studio version. Backup files are not needed,
|
|
||||||
# because we have git ;-)
|
|
||||||
_UpgradeReport_Files/
|
|
||||||
Backup*/
|
|
||||||
UpgradeLog*.XML
|
|
||||||
UpgradeLog*.htm
|
|
||||||
ServiceFabricBackup/
|
|
||||||
*.rptproj.bak
|
|
||||||
|
|
||||||
# SQL Server files
|
|
||||||
*.mdf
|
|
||||||
*.ldf
|
|
||||||
*.ndf
|
|
||||||
|
|
||||||
# Business Intelligence projects
|
|
||||||
*.rdl.data
|
|
||||||
*.bim.layout
|
|
||||||
*.bim_*.settings
|
|
||||||
*.rptproj.rsuser
|
|
||||||
*- [Bb]ackup.rdl
|
|
||||||
*- [Bb]ackup ([0-9]).rdl
|
|
||||||
*- [Bb]ackup ([0-9][0-9]).rdl
|
|
||||||
|
|
||||||
# Microsoft Fakes
|
|
||||||
FakesAssemblies/
|
|
||||||
|
|
||||||
# GhostDoc plugin setting file
|
|
||||||
*.GhostDoc.xml
|
|
||||||
|
|
||||||
# Node.js Tools for Visual Studio
|
|
||||||
.ntvs_analysis.dat
|
|
||||||
node_modules/
|
|
||||||
|
|
||||||
# Visual Studio 6 build log
|
|
||||||
*.plg
|
|
||||||
|
|
||||||
# Visual Studio 6 workspace options file
|
|
||||||
*.opt
|
|
||||||
|
|
||||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
|
||||||
*.vbw
|
|
||||||
|
|
||||||
# Visual Studio LightSwitch build output
|
|
||||||
**/*.HTMLClient/GeneratedArtifacts
|
|
||||||
**/*.DesktopClient/GeneratedArtifacts
|
|
||||||
**/*.DesktopClient/ModelManifest.xml
|
|
||||||
**/*.Server/GeneratedArtifacts
|
|
||||||
**/*.Server/ModelManifest.xml
|
|
||||||
_Pvt_Extensions
|
|
||||||
|
|
||||||
# Paket dependency manager
|
|
||||||
.paket/paket.exe
|
|
||||||
paket-files/
|
|
||||||
|
|
||||||
# FAKE - F# Make
|
|
||||||
.fake/
|
|
||||||
|
|
||||||
# CodeRush personal settings
|
|
||||||
.cr/personal
|
|
||||||
|
|
||||||
# Python Tools for Visual Studio (PTVS)
|
|
||||||
__pycache__/
|
|
||||||
*.pyc
|
|
||||||
|
|
||||||
# Cake - Uncomment if you are using it
|
|
||||||
# tools/**
|
|
||||||
# !tools/packages.config
|
|
||||||
|
|
||||||
# Tabs Studio
|
|
||||||
*.tss
|
|
||||||
|
|
||||||
# Telerik's JustMock configuration file
|
|
||||||
*.jmconfig
|
|
||||||
|
|
||||||
# BizTalk build output
|
|
||||||
*.btp.cs
|
|
||||||
*.btm.cs
|
|
||||||
*.odx.cs
|
|
||||||
*.xsd.cs
|
|
||||||
|
|
||||||
# OpenCover UI analysis results
|
|
||||||
OpenCover/
|
|
||||||
|
|
||||||
# Azure Stream Analytics local run output
|
|
||||||
ASALocalRun/
|
|
||||||
|
|
||||||
# MSBuild Binary and Structured Log
|
|
||||||
*.binlog
|
|
||||||
|
|
||||||
# NVidia Nsight GPU debugger configuration file
|
|
||||||
*.nvuser
|
|
||||||
|
|
||||||
# MFractors (Xamarin productivity tool) working folder
|
|
||||||
.mfractor/
|
|
||||||
|
|
||||||
# Local History for Visual Studio
|
|
||||||
.localhistory/
|
|
||||||
|
|
||||||
# BeatPulse healthcheck temp database
|
|
||||||
healthchecksdb
|
|
||||||
|
|
||||||
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
|
||||||
MigrationBackup/
|
|
||||||
|
|
||||||
# Ionide (cross platform F# VS Code tools) working folder
|
|
||||||
.ionide/
|
|
||||||
|
|
||||||
# Fody - auto-generated XML schema
|
|
||||||
FodyWeavers.xsd
|
|
||||||
@@ -1,102 +0,0 @@
|
|||||||
using libomtnet;
|
|
||||||
using SkiaSharp;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* OMT Graphics Example
|
|
||||||
*
|
|
||||||
* This example generates a horizontal ticker graphic and sends it via an OMT Sender.
|
|
||||||
*
|
|
||||||
* This demonstrates the alpha channel functionality built into Open Media Transport.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace omtgraphicsexample
|
|
||||||
{
|
|
||||||
internal class Program
|
|
||||||
{
|
|
||||||
static void Main(string[] args)
|
|
||||||
{
|
|
||||||
Console.WriteLine("OMT Graphics Example");
|
|
||||||
|
|
||||||
//Create the graphics format we will be sending, in this example this is a standard 1080p video.
|
|
||||||
SKImageInfo imageInfo = new SKImageInfo(1920, 1080, SKColorType.Bgra8888, SKAlphaType.Unpremul);
|
|
||||||
|
|
||||||
//This is the surface we will be drawing to to create out frame
|
|
||||||
SKSurface image = SKSurface.Create(imageInfo);
|
|
||||||
|
|
||||||
//Canvas contains all the drawing functions we will be utilising
|
|
||||||
SKCanvas canvas = image.Canvas;
|
|
||||||
|
|
||||||
//Define a rectangle for our ticker
|
|
||||||
SKRect rect = new SKRect(0, 960, 1920, 1080);
|
|
||||||
SKRoundRect roundRect = new SKRoundRect(rect, 8);
|
|
||||||
|
|
||||||
//Create a simple gradient background for our ticker
|
|
||||||
SKShader gradient = SKShader.CreateLinearGradient(new SKPoint(rect.Left, rect.Top), new SKPoint(rect.Right, rect.Bottom), new[] {
|
|
||||||
new SKColor(0, 19, 69, 192), // semi-transparent deep blue
|
|
||||||
new SKColor(0, 89, 254, 192) // semi-transparent light blue
|
|
||||||
}, null, SKShaderTileMode.Clamp);
|
|
||||||
SKPaint gradientPaint = new SKPaint() { Shader = gradient };
|
|
||||||
|
|
||||||
//Create the text formatting to use
|
|
||||||
SKFont textFont = new SKFont(SKTypeface.FromFamilyName("Sans Serif", SKFontStyle.Bold), 48);
|
|
||||||
SKPaint textPaint = new SKPaint() { IsAntialias = true, Color = SKColors.White };
|
|
||||||
|
|
||||||
//Position text in the middle of the gradient rectangle
|
|
||||||
SKFontMetrics fontMetrics = new SKFontMetrics();
|
|
||||||
textFont.GetFontMetrics(out fontMetrics);
|
|
||||||
SKPoint textPosition =new SKPoint(rect.Right, rect.Top + ((rect.Height - (fontMetrics.Ascent + fontMetrics.Descent)) / 2f));
|
|
||||||
|
|
||||||
//This is the text we will be sending along with an estimate of its width to know when to repeat.
|
|
||||||
string tickerText = "This is an example of text rendering in SkiaSharp that is sent over Open Media Transport!";
|
|
||||||
float tickerTextWidth = textFont.MeasureText(tickerText);
|
|
||||||
|
|
||||||
//Create our sender
|
|
||||||
using (OMTSend send = new OMTSend("Graphics", OMTQuality.Default))
|
|
||||||
{
|
|
||||||
|
|
||||||
//Create the media frame that contains the final graphic ready for sending
|
|
||||||
OMTMediaFrame frame = new OMTMediaFrame();
|
|
||||||
frame.Type = OMTFrameType.Video;
|
|
||||||
frame.Codec = (int)OMTCodec.BGRA;
|
|
||||||
frame.Width = imageInfo.Width;
|
|
||||||
frame.Height = imageInfo.Height;
|
|
||||||
frame.FrameRate = 59.94F;
|
|
||||||
frame.Flags = OMTVideoFlags.Alpha; //Set premultiplied here also if necessary
|
|
||||||
frame.AspectRatio = (float)imageInfo.Width / (float)imageInfo.Height;
|
|
||||||
frame.ColorSpace = OMTColorSpace.BT709;
|
|
||||||
frame.Stride = imageInfo.Width * 4;
|
|
||||||
frame.DataLength = frame.Stride * frame.Height;
|
|
||||||
frame.Data = Marshal.AllocHGlobal(frame.DataLength);
|
|
||||||
frame.Timestamp = -1; //Important, informs OMT to automatically pace our frames to the frame rate.
|
|
||||||
|
|
||||||
Console.WriteLine("Sending graphics on: \"" + send.Address + "\"");
|
|
||||||
Console.WriteLine("");
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
canvas.Clear(); //Clear our canvas so it is fully transparent
|
|
||||||
canvas.DrawRoundRect(roundRect, gradientPaint);
|
|
||||||
canvas.DrawText(tickerText, textPosition, textFont, textPaint);
|
|
||||||
|
|
||||||
if (image.ReadPixels(imageInfo, frame.Data, frame.Stride, 0, 0))
|
|
||||||
{
|
|
||||||
send.Send(frame);
|
|
||||||
Console.Write(".");
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
Console.WriteLine("Failed to read pixels from SkiaSharp Surface, exiting....");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Update text position to scroll along the screen
|
|
||||||
textPosition.X -= 4;
|
|
||||||
if (textPosition.X < -tickerTextWidth) { textPosition.X = rect.Width; }
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Binary file not shown.
Binary file not shown.
@@ -1,31 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<OutputType>Exe</OutputType>
|
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
<PublishAot>true</PublishAot>
|
|
||||||
<InvariantGlobalization>true</InvariantGlobalization>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="SkiaSharp" Version="3.119.0" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Reference Include="libomtnet">
|
|
||||||
<HintPath>libomtnet.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<None Update="libomtnet.dll">
|
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
|
||||||
</None>
|
|
||||||
<None Update="libvmx.dll">
|
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
|
||||||
</None>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
|
||||||
# Visual Studio Version 17
|
|
||||||
VisualStudioVersion = 17.11.35431.28
|
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "omtgraphicsexample", "omtgraphicsexample.csproj", "{0E671018-770B-4F53-9199-30807405A368}"
|
|
||||||
EndProject
|
|
||||||
Global
|
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
|
||||||
Debug|Any CPU = Debug|Any CPU
|
|
||||||
Release|Any CPU = Release|Any CPU
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
|
||||||
{0E671018-770B-4F53-9199-30807405A368}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{0E671018-770B-4F53-9199-30807405A368}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{0E671018-770B-4F53-9199-30807405A368}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{0E671018-770B-4F53-9199-30807405A368}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
|
||||||
HideSolutionNode = FALSE
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
|
||||||
SolutionGuid = {232A60FB-F452-4930-B746-90D05F7646A6}
|
|
||||||
EndGlobalSection
|
|
||||||
EndGlobal
|
|
||||||
@@ -1,186 +0,0 @@
|
|||||||
#include <iostream>
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
#include "libomt.h"
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#define strcasecmp _stricmp
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// We will use this to dump info about the incoming OMT
|
|
||||||
static int dumpOMTMediaFrameInfo(OMTMediaFrame * video);
|
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, const char * argv[])
|
|
||||||
{
|
|
||||||
omt_send_t * sndloop;
|
|
||||||
int nativeReceiveMode = 0;
|
|
||||||
int sixteenBitReceiveMode = 0;
|
|
||||||
|
|
||||||
// optionally setup logging
|
|
||||||
string filename = "omtrecvtest.log";
|
|
||||||
omt_setloggingfilename(filename.c_str());
|
|
||||||
|
|
||||||
// this example can just take a Stream name, plus it can optionally also have either nativevmx or 16bit as a second parameter
|
|
||||||
// to request compressed VMX data instead of uncompressed video, or to request specifically 16-bit uncompressed video
|
|
||||||
if (argc<2)
|
|
||||||
{
|
|
||||||
printf("Usage : omtrecvtest \"HOST (OMTSOURCE)\" [nativevmx|16bit]");
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// this example receives OMT then sends it back out again through another stream.
|
|
||||||
// Create a loop out stream
|
|
||||||
sndloop = omt_send_create("OMLoopBack", OMTQuality_Default);
|
|
||||||
|
|
||||||
// the instance of an OMT receiver.
|
|
||||||
omt_receive_t* recv;
|
|
||||||
|
|
||||||
// check for parameters
|
|
||||||
if (argc>2)
|
|
||||||
{
|
|
||||||
if (!strcasecmp((char *)argv[2],"nativevmx"))
|
|
||||||
{
|
|
||||||
nativeReceiveMode = 1;
|
|
||||||
}
|
|
||||||
if (!strcasecmp((char *)argv[2],"16bit"))
|
|
||||||
{
|
|
||||||
sixteenBitReceiveMode = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// setup an OMT Receiver. We specify the types of data we are interested in and then the format, and an optional flag.
|
|
||||||
if (nativeReceiveMode)
|
|
||||||
{
|
|
||||||
// force receive of compressed data only
|
|
||||||
recv = omt_receive_create((const char *)argv[1], (OMTFrameType)(OMTFrameType_Video | OMTFrameType_Audio | OMTFrameType_Metadata), (OMTPreferredVideoFormat)OMTPreferredVideoFormat_UYVYorUYVAorP216orPA16, (OMTReceiveFlags)OMTReceiveFlags_CompressedOnly);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// optionally force 16bit receive
|
|
||||||
if (sixteenBitReceiveMode)
|
|
||||||
{
|
|
||||||
recv = omt_receive_create((const char *)argv[1], (OMTFrameType)(OMTFrameType_Video | OMTFrameType_Audio | OMTFrameType_Metadata), (OMTPreferredVideoFormat)OMTPreferredVideoFormat_P216, (OMTReceiveFlags)OMTReceiveFlags_None);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
recv = omt_receive_create((const char *)argv[1], (OMTFrameType)(OMTFrameType_Video | OMTFrameType_Audio | OMTFrameType_Metadata), (OMTPreferredVideoFormat)OMTPreferredVideoFormat_UYVYorUYVAorP216orPA16, (OMTReceiveFlags)OMTReceiveFlags_None);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while(1)
|
|
||||||
{
|
|
||||||
OMTMediaFrame frame = {}; // loop out frame
|
|
||||||
OMTMediaFrame * theOMTFrame;
|
|
||||||
OMTFrameType t = OMTFrameType_None;
|
|
||||||
|
|
||||||
// capture a frame of video, audio or metadata from the OMT Source
|
|
||||||
theOMTFrame = omt_receive(recv, (OMTFrameType)(OMTFrameType_Video | OMTFrameType_Audio | OMTFrameType_Metadata), 40);
|
|
||||||
if (theOMTFrame)
|
|
||||||
{
|
|
||||||
t = theOMTFrame->Type;
|
|
||||||
|
|
||||||
// dump what we got to the console
|
|
||||||
dumpOMTMediaFrameInfo(theOMTFrame);
|
|
||||||
|
|
||||||
|
|
||||||
// we are going to loop the OMT stream back out, so let's make a copy of the Frame
|
|
||||||
memcpy(&frame,theOMTFrame,sizeof(OMTMediaFrame));
|
|
||||||
switch (t)
|
|
||||||
{
|
|
||||||
case OMTFrameType_Video:
|
|
||||||
{
|
|
||||||
// send it back out.. If its native VMX we need to move the ComressedData into Data and CompressedLength into DataLength
|
|
||||||
if (nativeReceiveMode && theOMTFrame->Codec == OMTCodec_VMX1)
|
|
||||||
{
|
|
||||||
frame.Data = theOMTFrame->CompressedData;
|
|
||||||
frame.DataLength = theOMTFrame->CompressedLength;
|
|
||||||
frame.CompressedData = NULL;
|
|
||||||
frame.CompressedLength = 0;
|
|
||||||
}
|
|
||||||
omt_send(sndloop, &frame);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OMTFrameType_Audio: case OMTFrameType_Metadata:
|
|
||||||
{
|
|
||||||
omt_send(sndloop, &frame);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OMTFrameType_None: default:
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
omt_receive_destroy(recv);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#define FourCharCodeToString(code) (code) & 0xFF, ((code) >> 8) & 0xFF, ((code) >> 16) & 0xFF, ((code) >> 24) & 0xFF
|
|
||||||
|
|
||||||
static int dumpOMTMediaFrameInfo(OMTMediaFrame * video)
|
|
||||||
{
|
|
||||||
printf("----------------------------------------------\n");
|
|
||||||
if (video)
|
|
||||||
{
|
|
||||||
if (video->Type == OMTFrameType_Video)
|
|
||||||
{
|
|
||||||
printf("VIDEO FRAME:\n");
|
|
||||||
printf("Timestamp=%llu\n", (unsigned long long)(video->Timestamp));
|
|
||||||
printf("Codec=%c%c%c%c\n", FourCharCodeToString(video->Codec));
|
|
||||||
printf("Width=%d\n", video->Width);
|
|
||||||
printf("Height=%d\n", video->Height);
|
|
||||||
printf("Stride=%d\n", video->Stride);
|
|
||||||
printf("Flags=%d\n", video->Flags);
|
|
||||||
printf("FrameRateN=%d\n", video->FrameRateN);
|
|
||||||
printf("FrameRateD=%d\n", video->FrameRateD);
|
|
||||||
printf("AspectRatio=%.2f\n", video->AspectRatio);
|
|
||||||
printf("ColorSpace=%d\n", video->ColorSpace);
|
|
||||||
printf("Data=%llu\n", (unsigned long long)video->Data);
|
|
||||||
printf("DataLength=%d\n", video->DataLength);
|
|
||||||
printf("CompressedData=%llu\n", (unsigned long long)video->CompressedData);
|
|
||||||
printf("CompressedLength=%llu\n", (unsigned long long)video->CompressedLength);
|
|
||||||
printf("FrameMetadata=%llu\n", (unsigned long long)video->FrameMetadata);
|
|
||||||
printf("FrameMetadataLength=%llu\n", (unsigned long long)video->FrameMetadataLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (video->Type == OMTFrameType_Audio)
|
|
||||||
{
|
|
||||||
printf("AUDIO FRAME:\n");
|
|
||||||
printf("Timestamp=%llu\n", (unsigned long long)video->Timestamp);
|
|
||||||
printf("Codec=%c%c%c%c\n", FourCharCodeToString(video->Codec));
|
|
||||||
printf("Flags=%d\n", video->Flags);
|
|
||||||
printf("SampleRate=%d\n", video->SampleRate);
|
|
||||||
printf("Channels=%d\n", video->Channels);
|
|
||||||
printf("SamplesPerChannel=%d\n", video->SamplesPerChannel);
|
|
||||||
printf("Data=%llu\n", (unsigned long long)video->Data);
|
|
||||||
printf("DataLength=%d\n", video->DataLength);
|
|
||||||
printf("FrameMetadata=%llu\n", (unsigned long long)video->FrameMetadata);
|
|
||||||
printf("FrameMetadataLength=%llu\n", (unsigned long long)video->FrameMetadataLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (video->Type == OMTFrameType_Metadata)
|
|
||||||
{
|
|
||||||
printf("METADATA FRAME:\n");
|
|
||||||
printf("Timestamp=%llu\n", (unsigned long long)video->Timestamp);
|
|
||||||
printf("Flags=%d\n", video->Flags);
|
|
||||||
printf("Data=%llu\n", (unsigned long long)video->Data);
|
|
||||||
printf("DataLength=%d\n", video->DataLength);
|
|
||||||
printf("FrameMetadata=%llu\n", (unsigned long long)video->FrameMetadata);
|
|
||||||
printf("FrameMetadataLength=%llu\n", (unsigned long long)video->FrameMetadataLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
File diff suppressed because one or more lines are too long
@@ -1,239 +0,0 @@
|
|||||||
/*
|
|
||||||
* MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Open Media Transport Contributors
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
/* omtsendtest.cpp demonstrates the process of creating a named OMT output,
|
|
||||||
and emitting an 8-bit image repeatedly, with the frame rate controlled by OMT
|
|
||||||
it also demonstrates how to setup a log destination, attach vendor information
|
|
||||||
to the stream, retrieve OMT statistics on the output stream and also monitor tally */
|
|
||||||
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <chrono>
|
|
||||||
#include <thread>
|
|
||||||
#include <fstream>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
// The header for the C/C++ wrapper of OMT
|
|
||||||
#include "libomt.h"
|
|
||||||
// link this exe with libomt, and make sure libomt and libvpx are accessible to the exe, either in the same folder, or linked explicitly via rpath or otherwise.
|
|
||||||
// libomt will dynamically open libvpx at runtime
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
#include <random>
|
|
||||||
|
|
||||||
// Generate a random number between a and b
|
|
||||||
// used to generate audio noise.
|
|
||||||
float rand_FloatRange(float a, float b)
|
|
||||||
{
|
|
||||||
return ((b - a) * ((float)rand() / (float)RAND_MAX)) + a;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
std::cout << "OMTSendTest\n";
|
|
||||||
|
|
||||||
string filename = "omtsendtest.log";
|
|
||||||
omt_setloggingfilename(filename.c_str());
|
|
||||||
std::cout << "omt_setloggingfilename.success\n";
|
|
||||||
|
|
||||||
// this is the name of the generated OMT Stream in the form HOSTNAME (Test)
|
|
||||||
string name = "Test";
|
|
||||||
|
|
||||||
// Create the OMT output stream using the default (medium) quality.
|
|
||||||
omt_send_t * snd = omt_send_create(name.c_str(), OMTQuality_Default);
|
|
||||||
if (snd)
|
|
||||||
{
|
|
||||||
std::cout << "omt_send_create.success\n";
|
|
||||||
|
|
||||||
// Optionally attach some vendor specific information to the stream.
|
|
||||||
OMTSenderInfo info = {};
|
|
||||||
string ProductName = "SendTest";
|
|
||||||
string Manufacturer = "OMT";
|
|
||||||
string Version = "1.0";
|
|
||||||
ProductName.copy(&info.ProductName[0], OMT_MAX_STRING_LENGTH, 0);
|
|
||||||
Manufacturer.copy(&info.Manufacturer[0], OMT_MAX_STRING_LENGTH, 0);
|
|
||||||
Version.copy(&info.Version[0], OMT_MAX_STRING_LENGTH, 0);
|
|
||||||
omt_send_setsenderinformation(snd, &info);
|
|
||||||
|
|
||||||
std::cout << "omt_send_setsenderinformation.success\n";
|
|
||||||
|
|
||||||
// Prepare the OMTMediaFrame Video Frame structure. Be sure to zero any unused fields
|
|
||||||
// here the frame = {} will zero-initialise the structure in C++
|
|
||||||
OMTMediaFrame video_frame = {};
|
|
||||||
|
|
||||||
// OMTMediaFrames can be Video, Audio or Metadata
|
|
||||||
video_frame.Type = OMTFrameType_Video;
|
|
||||||
video_frame.Width = 1920;
|
|
||||||
video_frame.Height = 1080;
|
|
||||||
|
|
||||||
// Select the format of data you are passing to OMT
|
|
||||||
// This is typically UYVY or BGRA for 8bit or P216 for 10-bit (16 bit data).
|
|
||||||
video_frame.Codec = OMTCodec_UYVY;
|
|
||||||
|
|
||||||
// Setting Timestamp to -1 will force OMT to auto increment this from zero
|
|
||||||
// -1 also enables output clocking so OMT will hold the output on each send
|
|
||||||
// to enforce the correct frame rate if your code is able to send faster.
|
|
||||||
video_frame.Timestamp = -1;
|
|
||||||
|
|
||||||
// OMT uses BT709 for HD and UHD. Use BT601 for SD streams
|
|
||||||
video_frame.ColorSpace = OMTColorSpace_BT709;
|
|
||||||
|
|
||||||
// if the Video Frame was interleaved (interlaced), pass OMTVideoFlags_Interlaced
|
|
||||||
// OMT uses a single frame of data for Progressive and Interlaced sources.
|
|
||||||
video_frame.Flags = OMTVideoFlags_None;
|
|
||||||
|
|
||||||
// line stride in bytes, typically width*2 for UYVY and also P216 formats.
|
|
||||||
// Can be a custom value in case you are padding lines for byte alignment efficiency,
|
|
||||||
video_frame.Stride = video_frame.Width * 2;
|
|
||||||
|
|
||||||
// Total size of the data
|
|
||||||
video_frame.DataLength = video_frame.Stride * video_frame.Height;
|
|
||||||
|
|
||||||
// A pointer to the UYVY data which will be passed to OMT
|
|
||||||
video_frame.Data = malloc(video_frame.DataLength);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// The target frame rate expressed as numerator and denominator. In this case 60 fps
|
|
||||||
video_frame.FrameRateN = 60000;
|
|
||||||
video_frame.FrameRateD = 1000;
|
|
||||||
|
|
||||||
// we are passing uncompressed, rather than pre-compressed VMX codec data, so set these to zero
|
|
||||||
// video_frame.CompressedData = NULL;
|
|
||||||
// video_frame.CompressedLength = 0;
|
|
||||||
|
|
||||||
// its possible to attach frame specific XML metadata to each frame, otherwise zero these.
|
|
||||||
video_frame.FrameMetadata = NULL;
|
|
||||||
video_frame.FrameMetadataLength = 0;
|
|
||||||
|
|
||||||
|
|
||||||
// load sample UYVY data from the california-1080-uyvy.yuv file
|
|
||||||
// make sure its in the same folder with the built executable
|
|
||||||
void * uyvy = malloc(video_frame.DataLength);
|
|
||||||
std::ifstream file("california-1080-uyvy.yuv", std::ios::binary | std::ios::in | std::ios::ate);
|
|
||||||
if (file.is_open())
|
|
||||||
{
|
|
||||||
std::streamsize size = file.tellg();
|
|
||||||
file.seekg(0, std::ios::beg);
|
|
||||||
file.read((char*)uyvy, size);
|
|
||||||
file.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
// create some audio a stereo buffer exactly 1 frame long
|
|
||||||
float * audioBuffer = (float *)malloc(800 * sizeof(float) * 2 );
|
|
||||||
// fill the buffer with noise
|
|
||||||
srand((unsigned int)time(NULL));
|
|
||||||
for (int z=0;z<1600;z++)
|
|
||||||
{
|
|
||||||
audioBuffer[z] = rand_FloatRange(-1.0,+1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// prepare an OMTMediaFrame for the audio
|
|
||||||
OMTMediaFrame audio_frame = {};
|
|
||||||
memset(&audio_frame,0,sizeof(OMTMediaFrame));
|
|
||||||
audio_frame.Type = OMTFrameType_Audio;
|
|
||||||
audio_frame.Timestamp = -1;
|
|
||||||
audio_frame.Codec = OMTCodec_FPA1; // floating point planar data format
|
|
||||||
audio_frame.SampleRate = 48000;
|
|
||||||
audio_frame.Channels = 2;
|
|
||||||
audio_frame.SamplesPerChannel = 800; // we are sending exactly 1 frame of audio per 60fps video frame
|
|
||||||
audio_frame.Data = (void *)audioBuffer;
|
|
||||||
audio_frame.DataLength = (800 * sizeof(float) * 2);
|
|
||||||
audio_frame.FrameMetadata = NULL;
|
|
||||||
audio_frame.FrameMetadataLength = 0;
|
|
||||||
|
|
||||||
// used to create a dynamically changing image
|
|
||||||
void* twoLines = malloc(video_frame.Stride * 2);
|
|
||||||
memset(twoLines, 255, video_frame.Stride * 2);
|
|
||||||
int linePos = 0;
|
|
||||||
|
|
||||||
|
|
||||||
// structs ready to receive streaming statistics and tally
|
|
||||||
OMTTally tally = {};
|
|
||||||
OMTStatistics stats = {};
|
|
||||||
|
|
||||||
int frameCount = 0;
|
|
||||||
int bytes = 0;
|
|
||||||
for (int i = 0; i < 10000; i++)
|
|
||||||
{
|
|
||||||
|
|
||||||
//used to create a dynamically changing image by overwriting 2 lines moving down the image
|
|
||||||
memcpy(video_frame.Data, uyvy, video_frame.DataLength);
|
|
||||||
memcpy((char*)video_frame.Data + linePos, twoLines, video_frame.Stride * 2);
|
|
||||||
linePos += video_frame.Stride * 2;
|
|
||||||
if (linePos >= video_frame.DataLength)
|
|
||||||
{
|
|
||||||
linePos = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send out the prepared OMT Video Frame.
|
|
||||||
bytes += omt_send(snd, &video_frame);
|
|
||||||
|
|
||||||
// gather and output statistics once per second
|
|
||||||
frameCount += 1;
|
|
||||||
if (frameCount >= 60)
|
|
||||||
{
|
|
||||||
std::cout << "omt_send.ok: " << bytes << "\n";
|
|
||||||
omt_send_gettally(snd, 0, &tally);
|
|
||||||
std::cout << "omt_send.tally: " << tally.preview << " " << tally.program << "\n";
|
|
||||||
|
|
||||||
OMTMediaFrame* frame = omt_send_receive(snd, 0);
|
|
||||||
if (frame) {
|
|
||||||
char* sz = (char*)frame->Data;
|
|
||||||
std::cout << "omt_send.meta: " << sz << "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
int connections = omt_send_connections(snd);
|
|
||||||
std::cout << "omt_send.connections: " << connections << "\n";
|
|
||||||
|
|
||||||
omt_send_getvideostatistics(snd, &stats);
|
|
||||||
std::cout << "omt_send_getvideostatistics: Bytes: " << stats.BytesSent << " Frames: " << stats.Frames << "\n";
|
|
||||||
|
|
||||||
frameCount = 0;
|
|
||||||
bytes = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send out the prepared OMT Audio Frame.
|
|
||||||
omt_send(snd, &audio_frame);
|
|
||||||
// make some different noise for next frame
|
|
||||||
for (int z=0;z<1600;z++)
|
|
||||||
{
|
|
||||||
audioBuffer[z] = rand_FloatRange(-1.0,+1.0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// close and clean up the OMT output
|
|
||||||
omt_send_destroy(snd);
|
|
||||||
std::cout << "omt_send_destroy.success\n";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
std::cout << "omt_send_create.failed\n";
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2025 Open Media Transport Contributors
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
# Open Media Transport (OMT) Code Examples
|
|
||||||
|
|
||||||
This repository contains some basic C++ and C# code examples for common use cases.
|
|
||||||
|
|
||||||
## C#
|
|
||||||
|
|
||||||
```
|
|
||||||
omtgraphicsexample
|
|
||||||
```
|
|
||||||
|
|
||||||
This example generates a horizontal ticker graphic with alpha channel and sends it via an OMT Sender.
|
|
||||||
|
|
||||||
## C++
|
|
||||||
|
|
||||||
```
|
|
||||||
omtrecvtest
|
|
||||||
omtsendtest
|
|
||||||
```
|
|
||||||
|
|
||||||
These are cross platform examples that use libomt and libvmx to send and receive video frames over a network.
|
|
||||||
|
|
||||||
### Windows Compile Notes
|
|
||||||
|
|
||||||
These examples can be compiled in Visual C++ using a command line similar to the follwing (from the VS x64 Command Prompt)
|
|
||||||
|
|
||||||
```
|
|
||||||
cl omtrecvtest.cpp
|
|
||||||
```
|
|
||||||
|
|
||||||
### Linux Compilation Notes
|
|
||||||
|
|
||||||
These can be compiled from the command line using Clang on Linux with a command similar to the following:
|
|
||||||
|
|
||||||
```
|
|
||||||
clang++ -O3 -std=c++17 -o omtrecvtest omtrecvtest.cpp -L./ -lomt -Wl,-rpath,'$ORIGIN'
|
|
||||||
```
|
|
||||||
|
|
||||||
Note the above command adds the app directory as a search path for the library libomt.so which should be placed in the same directory alongside libvmx.so
|
|
||||||
Reference in New Issue
Block a user