diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 000000000..378eac25d
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1 @@
+build
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 000000000..579061a0a
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,40 @@
+#sources
+*.c text
+*.cc text
+*.cxx text
+*.cpp text
+*.c++ text
+*.hpp text
+*.h text
+*.h++ text
+*.hh text
+
+# Compiled Object files
+*.slo binary
+*.lo binary
+*.o binary
+*.obj binary
+
+
+# Precompiled Headers
+*.gch binary
+*.pch binary
+
+
+# Compiled Dynamic libraries
+*.so binary
+*.dylib binary
+*.dll binary
+
+
+# Compiled Static libraries
+*.lai binary
+*.la binary
+*.a binary
+*.lib binary
+
+
+# Executables
+*.exe binary
+*.out binary
+*.app binary
\ No newline at end of file
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
new file mode 100644
index 000000000..a46e89e52
--- /dev/null
+++ b/.github/workflows/codeql.yml
@@ -0,0 +1,88 @@
+# For most projects, this workflow file will not need changing; you simply need
+# to commit it to your repository.
+#
+# You may wish to alter this file to override the set of languages analyzed,
+# or to provide custom queries or build logic.
+#
+# ******** NOTE ********
+# We have attempted to detect the languages in your repository. Please check
+# the `language` matrix defined below to confirm you have the correct set of
+# supported CodeQL languages.
+#
+name: "CodeQL Analysis"
+
+on:
+ push:
+ branches: [ "main" ]
+ pull_request:
+ # The branches below must be a subset of the branches above
+ branches: [ "main" ]
+ schedule:
+ - cron: '40 9 * * 0'
+
+env:
+ # Path to the CMake build directory.
+ build: '${{ github.workspace }}/build'
+
+jobs:
+ analyze:
+ name: Analyze
+ runs-on: ubuntu-latest
+ permissions:
+ actions: read
+ contents: read
+ security-events: write
+
+ strategy:
+ fail-fast: false
+ matrix:
+ language: [ 'cpp' ]
+ # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
+ # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v3
+
+ - name: Setup VCPKG
+ uses: friendlyanon/setup-vcpkg@v1
+ with: { committish: 63aa65e65b9d2c08772ea15d25fb8fdb0d32e557 }
+
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v2
+ with:
+ languages: ${{ matrix.language }}
+ # If you wish to specify custom queries, you can do so here or in a config file.
+ # By default, queries listed here will override any specified in a config file.
+ # Prefix the list here with "+" to use these queries and those in the config file.
+
+ # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
+ # queries: security-extended,security-and-quality
+
+
+ # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java).
+ # If this step fails, then you should remove it and run the build manually (see below)
+ #- name: Autobuild
+ #uses: github/codeql-action/autobuild@v2
+
+ - name: Configure CMake
+ run: cmake -B ${{ env.build }} --preset linux-x64-release
+
+ # Build is not required for MSVC Code Analysis and will be used for Codecov
+ - name: Build CMake
+ run: cmake --build ${{ env.build }} --preset linux-x64-release
+
+ # ℹ️ Command-line programs to run using the OS shell.
+ # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
+
+ # If the Autobuild fails above, remove it and uncomment the following three lines.
+ # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
+
+ # - run: |
+ # echo "Run, Build Application using script"
+ # ./location_of_script_within_repo/buildscript.sh
+
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v2
+ with:
+ category: "/language:${{matrix.language}}"
diff --git a/.github/workflows/msvc.yml b/.github/workflows/msvc.yml
new file mode 100644
index 000000000..5dc37805f
--- /dev/null
+++ b/.github/workflows/msvc.yml
@@ -0,0 +1,97 @@
+# This workflow uses actions that are not certified by GitHub.
+# They are provided by a third-party and are governed by
+# separate terms of service, privacy policy, and support
+# documentation.
+#
+# Find more information at:
+# https://github.com/microsoft/msvc-code-analysis-action
+
+name: Microsoft C++ Code Analysis
+
+on:
+ push:
+ branches: ["main"]
+ pull_request:
+ branches: ["main"]
+ schedule:
+ - cron: "41 16 * * 1"
+
+env:
+ # Path to the CMake build directory.
+ build: "${{ github.workspace }}/build"
+
+permissions:
+ contents: read
+
+jobs:
+ analyze:
+ permissions:
+ contents: read # for actions/checkout to fetch code
+ security-events: write # for github/codeql-action/upload-sarif to upload SARIF results
+ actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status
+ name: Analyze
+ runs-on: windows-latest
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v3
+
+ - name: Setup VCPKG
+ uses: friendlyanon/setup-vcpkg@v1
+ with: { committish: 63aa65e65b9d2c08772ea15d25fb8fdb0d32e557 }
+
+ - name: Get SW
+ uses: egorpugin/sw-action@master
+
+ - name: SW setup and add to PATH
+ run: |
+ ./sw setup
+ echo "D:\a\WolfEngine\WolfEngine" >> $env:GITHUB_PATH
+
+ - name: Setup OpenCppCoverage and add to PATh
+ id: setup_opencppcoverage
+ run: |
+ choco install OpenCppCoverage -y
+ echo "C:\Program Files\OpenCppCoverage" >> $env:GITHUB_PATH
+
+ - name: Configure CMake
+ run: cmake -DCMAKE_BUILD_TYPE=Debug -B ${{ env.build }}
+
+
+ # Build is not required for MSVC Code Analysis and will be used for Codecov
+ - name: Build CMake
+ run: cmake --build ${{ env.build }}
+
+ - name: Run MSVC Code Analysis
+ uses: microsoft/msvc-code-analysis-action@04825f6d9e00f87422d6bf04e1a38b1f3ed60d99
+ # Provide a unique ID to access the sarif output path
+ id: run-analysis
+ with:
+ cmakeBuildDirectory: ${{ env.build }}
+ # Ruleset file that will determine what checks will be run
+ ruleset: NativeRecommendedRules.ruleset
+ ignoredTargetPaths: ${{ env.build }}/_deps/boost_chrono-src;${{ env.build }}/_deps/boost_context-src;${{ env.build }}/_deps/boost_coroutine-src;${{ env.build }}/_deps/boost_date_time-src;${{ env.build }}/_deps/boost_exception-src;${{ env.build }}/_deps/fmt-src;${{ env.build }}/_deps/boost_container-src;${{ env.build }}/_deps/opencv-src;${{ env.build }}/_deps/rapidjson-src;${{ env.build }}/_deps/tesseract-src
+
+ - name: Generate Codecov Report
+ id: generate_test_report
+ shell: cmd
+ run: OpenCppCoverage.exe --continue_after_cpp_exception --export_type cobertura:WolfCov.xml --sources %CD% --excluded_sources %CD%\build\_deps -- %CD%\build\wolf\Debug\wolf_tests.exe
+ - name: Upload Report to Codecov
+ uses: codecov/codecov-action@v2
+ with:
+ files: ./WolfCov.xml
+ fail_ci_if_error: true
+ functionalities: fix
+
+ # Upload SARIF file to GitHub Code Scanning Alerts
+ #- name: Upload SARIF to GitHub
+ # uses: github/codeql-action/upload-sarif@v2
+ # with:
+ # sarif_file: ${{ steps.run-analysis.outputs.sarif }}
+
+ # Upload SARIF file as an Artifact to download and view
+ - name: Upload SARIF as an Artifact
+ uses: actions/upload-artifact@v3
+ with:
+ name: sarif-file
+ path: ${{ steps.run-analysis.outputs.sarif }}
diff --git a/.gitignore b/.gitignore
index b8bd0267b..40c135a73 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,31 @@
+*.log
+*.tlog
+*.wLog
+*.idb
+*.pdb
+*.ipch
+*.ilk
+*.recipe
+*.res
+*.enc
+*.vscode
+*.advixeproj
+*.advixeexp
+*.dflgadvixe
+*.infoadvixe
+*.DS_Store
+*.db
+*.db-shm
+*.db-wal
+*.opendb
+*.DS_Store
+*.pem
+
+/bin/*
+/build/*
+/coverage/*
+/install/*
+
# Compiled Object files
*.slo
*.lo
@@ -8,21 +36,12 @@
*.gch
*.pch
-# Compiled Dynamic libraries
-*.so
-*.dylib
-*.dll
# Fortran module files
*.mod
-# Compiled Static libraries
-*.lai
-*.la
-*.a
-*.lib
# Executables
*.exe
*.out
-*.app
+*.app
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 000000000..e6f019818
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,85 @@
+{
+ "C_Cpp.default.configurationProvider": "vector-of-bool.cmake-tools",
+ "editor.formatOnSave": true,
+ "files.associations": {
+ "__locale": "cpp",
+ "regex": "cpp",
+ "*.ipp": "cpp",
+ "pointers": "cpp",
+ "any": "cpp",
+ "array": "cpp",
+ "atomic": "cpp",
+ "strstream": "cpp",
+ "bit": "cpp",
+ "*.tcc": "cpp",
+ "cctype": "cpp",
+ "charconv": "cpp",
+ "chrono": "cpp",
+ "clocale": "cpp",
+ "cmath": "cpp",
+ "codecvt": "cpp",
+ "compare": "cpp",
+ "complex": "cpp",
+ "concepts": "cpp",
+ "condition_variable": "cpp",
+ "coroutine": "cpp",
+ "csignal": "cpp",
+ "cstdarg": "cpp",
+ "cstddef": "cpp",
+ "cstdint": "cpp",
+ "cstdio": "cpp",
+ "cstdlib": "cpp",
+ "cstring": "cpp",
+ "ctime": "cpp",
+ "cwchar": "cpp",
+ "cwctype": "cpp",
+ "deque": "cpp",
+ "list": "cpp",
+ "map": "cpp",
+ "set": "cpp",
+ "string": "cpp",
+ "unordered_map": "cpp",
+ "vector": "cpp",
+ "exception": "cpp",
+ "algorithm": "cpp",
+ "functional": "cpp",
+ "iterator": "cpp",
+ "memory": "cpp",
+ "memory_resource": "cpp",
+ "numeric": "cpp",
+ "optional": "cpp",
+ "random": "cpp",
+ "ratio": "cpp",
+ "source_location": "cpp",
+ "string_view": "cpp",
+ "system_error": "cpp",
+ "tuple": "cpp",
+ "type_traits": "cpp",
+ "utility": "cpp",
+ "fstream": "cpp",
+ "future": "cpp",
+ "initializer_list": "cpp",
+ "iomanip": "cpp",
+ "iosfwd": "cpp",
+ "iostream": "cpp",
+ "istream": "cpp",
+ "limits": "cpp",
+ "mutex": "cpp",
+ "new": "cpp",
+ "numbers": "cpp",
+ "ostream": "cpp",
+ "semaphore": "cpp",
+ "shared_mutex": "cpp",
+ "span": "cpp",
+ "sstream": "cpp",
+ "stdexcept": "cpp",
+ "stop_token": "cpp",
+ "streambuf": "cpp",
+ "thread": "cpp",
+ "cfenv": "cpp",
+ "cinttypes": "cpp",
+ "typeindex": "cpp",
+ "typeinfo": "cpp",
+ "variant": "cpp"
+ }
+}
\ No newline at end of file
diff --git a/CHANGE_LOG.md b/CHANGE_LOG.md
new file mode 100644
index 000000000..98a392b70
--- /dev/null
+++ b/CHANGE_LOG.md
@@ -0,0 +1,46 @@
+# ToDos
+- Dynamic lod creator for lod sample
+- Forward+
+- DirectX 12
+- Realtime Raytracing
+- DEBUG, RELEASE, MinSizeRelease(does not have assimp and just use wscene files)
+
+# 2.1
+A major release, rewrite most of wolf.system with pure C
+ - fixed some bugs
+ - improved memory pool
+ - executing python from c has been added
+
+# 2.0.0.0
+A major release, rewrite most of wolf.system with pure C
+ - Build for Win64
+ - Build for OSX
+ - Build for iOS
+ - Build for Android-armv7
+ - Build for Linux64
+
+
+# 1.68.0.9 (2018-10-03)
+A minor release with many compatibility-breaking changes.
+
+New features:
+- CMAKE added for building wolf for linux platform
+
+# 1.65.0.0 (2018-06-04)
+A minor release with many compatibility-breaking changes.
+
+New features:
+- system::w_logger optimized and integrated with spdlog
+- framework::w_media_core optimized for streaming
+- gpu occlusion culling has been added
+
+
+# 1.63.1.0 (2018-04-19)
+
+A minor release with many compatibility-breaking changes.
+
+New features:
+- Integrated with Vulkan SDK version 1.1.73.0
+- Integrated with VulkanMemoryAllocator for better gpu memory managment
+- The new coordinate system is Left handed Y-Up
+- The function "contains" have been added to wolf::system::w_bounding_sphere
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 000000000..0714bbec8
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,5 @@
+cmake_minimum_required(VERSION 3.22)
+
+# set the name of the projects
+project(wolf)
+add_subdirectory(wolf)
diff --git a/CMakePresets.json b/CMakePresets.json
new file mode 100644
index 000000000..b0460e2a8
--- /dev/null
+++ b/CMakePresets.json
@@ -0,0 +1,258 @@
+{
+ "version": 3,
+ "cmakeMinimumRequired": {
+ "major": 3,
+ "minor": 20
+ },
+ "configurePresets": [
+ {
+ "name": "base",
+ "hidden": true,
+ "description": "base project for other configurations.",
+ "binaryDir": "${sourceDir}/build/${presetName}",
+ "installDir": "${sourceDir}/install/${presetName}",
+ "generator": "Ninja",
+ "cacheVariables": {
+ "CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake",
+ "ARCH": "win64"
+ }
+ },
+ {
+ "name": "wasm32-unknown-emscripten-debug",
+ "displayName": "wasm32-unknown-emscripten",
+ "description": "Configure debug mode based on Emscripten",
+ "inherits": "base",
+ "cacheVariables": {
+ "CMAKE_TOOLCHAIN_FILE": "$env{EMSDK}/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake",
+ "CMAKE_BUILD_TYPE": "Debug"
+ }
+ },
+ {
+ "name": "wasm32-unknown-emscripten-release",
+ "displayName": "Win x64 Release",
+ "description": "Configure release mode based on Emscripten",
+ "inherits": "wasm32-unknown-emscripten-debug",
+ "cacheVariables": {
+ "CMAKE_BUILD_TYPE": "Release"
+ }
+ },
+ {
+ "name": "win-x64-debug",
+ "displayName": "Win x64 Debug",
+ "description": "Sets windows platform and debug build type for x64 arch",
+ "inherits": "base",
+ "architecture": {
+ "value": "x64",
+ "strategy": "external"
+ },
+ "cacheVariables": {
+ "CMAKE_BUILD_TYPE": "Debug"
+ },
+ "condition": {
+ "type": "equals",
+ "lhs": "${hostSystemName}",
+ "rhs": "Windows"
+ }
+ },
+ {
+ "name": "win-x64-release",
+ "displayName": "Win x64 Release",
+ "description": "Sets windows platform and release build type for x64 arch",
+ "inherits": "win-x64-debug",
+ "cacheVariables": {
+ "CMAKE_BUILD_TYPE": "Release"
+ }
+ },
+ {
+ "name": "base-android",
+ "hidden": true,
+ "inherits": "base",
+ "environment": {
+ "ANDROID_NDK_HOME": "$env{NDK}",
+ "ANDROID_NDK": "$env{NDK}",
+ "ANDROID_NATIVE_API_LEVEL": "24",
+ "ANDROID_PLATFORM": "android-24"
+ },
+ "cacheVariables": {
+ "ANDROID": true,
+ "CMAKE_TOOLCHAIN_FILE": "$env{ANDROID_NDK_HOME}/build/cmake/android.toolchain.cmake",
+ "ANDROID_STL": "c++_shared",
+ "ANDROID_ABI": "arm64-v8a",
+ "CPU": "armv8",
+ "CMAKE_ANDROID_ARCH_ABI": "arm64-v8a",
+ "CMAKE_EXPORT_COMPILE_COMMANDS": "ON",
+ "CMAKE_SYSTEM_NAME": "Android"
+ }
+ },
+ {
+ "name": "android-arm64-debug",
+ "inherits": "base-android",
+ "displayName": "Android ARM64 v8a Debug",
+ "description": "Sets android platform and debug build type for arm64-v8a arch",
+ "architecture": {
+ "value": "arm64-v8a",
+ "strategy": "external"
+ },
+ "environment": {
+ "ANDROID_ABI": "arm64-v8a",
+ "CPU": "armv8",
+ "ARCH": "aarch64",
+ "CMAKE_ANDROID_ARCH_ABI": "arm64-v8a",
+ "CMAKE_BUILD_TYPE": "Debug"
+ },
+ "cacheVariables": {
+ "CMAKE_BUILD_TYPE": "Debug"
+ }
+ },
+ {
+ "name": "android-arm64-release",
+ "displayName": "Android ARM64 v8a Release",
+ "description": "Sets android platform and release build type for arm64-v8a arch",
+ "inherits": "android-arm64-debug",
+ "cacheVariables": {
+ "CMAKE_BUILD_TYPE": "Release"
+ },
+ "environment": {
+ "CMAKE_BUILD_TYPE": "Release"
+ }
+ },
+ {
+ "name": "android-arm-debug",
+ "inherits": "base-android",
+ "displayName": "Android ARM eabi v7a Debug",
+ "description": "Sets android platform and debug build type for armeabi-v7a arch",
+ "architecture": {
+ "value": "armeabi-v7a",
+ "strategy": "external"
+ },
+ "environment": {
+ "ANDROID_ABI": "armeabi-v7a",
+ "CPU": "arm",
+ "ARCH": "arm",
+ "CMAKE_ANDROID_ARCH_ABI": "armeabi-v7a",
+ "CMAKE_BUILD_TYPE": "Debug"
+ },
+ "cacheVariables": {
+ "CMAKE_BUILD_TYPE": "Debug",
+ "ANDROID_ABI": "armeabi-v7a",
+ "CMAKE_ANDROID_ARCH_ABI": "armeabi-v7a",
+ "ARCH": "arm",
+ "CPU": "arm"
+ }
+ },
+ {
+ "name": "android-arm-release",
+ "displayName": "Android ARM eabi v7a Release",
+ "description": "Sets android platform and release build type for armeabi-v7a arch",
+ "inherits": "android-arm-debug",
+ "cacheVariables": {
+ "CMAKE_BUILD_TYPE": "Release"
+ },
+ "environment": {
+ "CMAKE_BUILD_TYPE": "Release"
+ }
+ },
+ {
+ "name": "linux-x64-debug",
+ "displayName": "GCC x64 linux gnu debug",
+ "description": "Using compilers: C = /usr/bin/gcc, CXX = /usr/bin/g++",
+ "binaryDir": "${sourceDir}/build/${presetName}",
+ "cacheVariables": {
+ "CMAKE_INSTALL_PREFIX": "${sourceDir}/build/install/${presetName}",
+ "CMAKE_C_COMPILER": "gcc",
+ "CMAKE_CXX_COMPILER": "g++",
+ "CMAKE_BUILD_TYPE": "Debug"
+ }
+ },
+ {
+ "name": "linux-x64-release",
+ "displayName": "GCC x64 linux gnu release",
+ "inherits": "linux-x64-debug",
+ "cacheVariables": {
+ "CMAKE_BUILD_TYPE": "Release"
+ }
+ }
+ ],
+ "buildPresets": [
+ {
+ "name": "wasm32-unknown-emscripten-debug",
+ "displayName": "wasm32-unknown-emscripten-debug",
+ "description": "Build WebAssembly with Emscripten",
+ "configurePreset": "wasm32-unknown-emscripten-debug"
+ },
+ {
+ "name": "wasm32-unknown-emscripten-release",
+ "displayName": "wasm32-unknown-emscripten-release",
+ "description": "Build WebAssembly with Emscripten",
+ "configurePreset": "wasm32-unknown-emscripten-release"
+ },
+ {
+ "name": "win-x64-release",
+ "displayName": "Win x64 Release",
+ "description": "Sets windows platform and debug build type for x64 arch in release mode",
+ "configurePreset": "win-x64-release"
+ },
+ {
+ "name": "win-x64-debug",
+ "displayName": "Win x64 Debug",
+ "description": "Sets windows platform and debug build type for x64 arch in debug mode",
+ "configurePreset": "win-x64-debug"
+ },
+ {
+ "name": "android-arm64-debug",
+ "displayName": "Android ARM64 v8a Debug",
+ "description": "Build Android",
+ "configurePreset": "android-arm64-debug"
+ },
+ {
+ "name": "android-arm64-release",
+ "displayName": "Android ARM64 v8a Release",
+ "description": "Build Android",
+ "configurePreset": "android-arm64-release"
+ },
+ {
+ "name": "android-arm-debug",
+ "displayName": "Android ARM eabi v7a Debug",
+ "description": "Build Android",
+ "configurePreset": "android-arm-debug"
+ },
+ {
+ "name": "android-arm-release",
+ "displayName": "Android ARM eabi v7a Release",
+ "description": "Build Android",
+ "configurePreset": "android-arm-release"
+ },
+ {
+ "name": "linux-x64-debug",
+ "displayName": "Sets linux platform and debug build type for x64 arch in debug mode",
+ "description": "linux x64 Debug",
+ "configurePreset": "linux-x64-debug"
+ },
+ {
+ "name": "linux-x64-release",
+ "displayName": "Sets linux platform and release build type for x64 arch in release mode",
+ "description": "linux x64 Release",
+ "configurePreset": "linux-x64-release"
+ },
+ {
+ "name": "wasm",
+ "description": "",
+ "displayName": "",
+ "configurePreset": "wasm32-unknown-emscripten-debug"
+ }
+ ],
+ "testPresets": [
+ {
+ "name": "base-tests",
+ "hidden": true,
+ "output": {
+ "outputOnFailure": true
+ }
+ },
+ {
+ "name": "windows-tests",
+ "inherits": "base-tests",
+ "configurePreset": "win-x64-debug"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 000000000..7fbcdc34e
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,132 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+We as members, contributors, and leaders pledge to make participation in our
+community a harassment-free experience for everyone, regardless of age, body
+size, visible or invisible disability, ethnicity, sex characteristics, gender
+identity and expression, level of experience, education, socio-economic status,
+nationality, personal appearance, race, caste, color, religion, or sexual identity
+and orientation.
+
+We pledge to act and interact in ways that contribute to an open, welcoming,
+diverse, inclusive, and healthy community.
+
+## Our Standards
+
+Examples of behavior that contributes to a positive environment for our
+community include:
+
+* Demonstrating empathy and kindness toward other people
+* Being respectful of differing opinions, viewpoints, and experiences
+* Giving and gracefully accepting constructive feedback
+* Accepting responsibility and apologizing to those affected by our mistakes,
+ and learning from the experience
+* Focusing on what is best not just for us as individuals, but for the
+ overall community
+
+Examples of unacceptable behavior include:
+
+* The use of sexualized language or imagery, and sexual attention or
+ advances of any kind
+* Trolling, insulting or derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or email
+ address, without their explicit permission
+* Other conduct which could reasonably be considered inappropriate in a
+ professional setting
+
+## Enforcement Responsibilities
+
+Community leaders are responsible for clarifying and enforcing our standards of
+acceptable behavior and will take appropriate and fair corrective action in
+response to any behavior that they deem inappropriate, threatening, offensive,
+or harmful.
+
+Community leaders have the right and responsibility to remove, edit, or reject
+comments, commits, code, wiki edits, issues, and other contributions that are
+not aligned to this Code of Conduct, and will communicate reasons for moderation
+decisions when appropriate.
+
+## Scope
+
+This Code of Conduct applies within all community spaces, and also applies when
+an individual is officially representing the community in public spaces.
+Examples of representing our community include using an official e-mail address,
+posting via an official social media account, or acting as an appointed
+representative at an online or offline event.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported to the community leaders responsible for enforcement at
+pooya{dot}eimandar{at}gmail{dot}com.
+All complaints will be reviewed and investigated promptly and fairly.
+
+All community leaders are obligated to respect the privacy and security of the
+reporter of any incident.
+
+## Enforcement Guidelines
+
+Community leaders will follow these Community Impact Guidelines in determining
+the consequences for any action they deem in violation of this Code of Conduct:
+
+### 1. Correction
+
+**Community Impact**: Use of inappropriate language or other behavior deemed
+unprofessional or unwelcome in the community.
+
+**Consequence**: A private, written warning from community leaders, providing
+clarity around the nature of the violation and an explanation of why the
+behavior was inappropriate. A public apology may be requested.
+
+### 2. Warning
+
+**Community Impact**: A violation through a single incident or series
+of actions.
+
+**Consequence**: A warning with consequences for continued behavior. No
+interaction with the people involved, including unsolicited interaction with
+those enforcing the Code of Conduct, for a specified period of time. This
+includes avoiding interactions in community spaces as well as external channels
+like social media. Violating these terms may lead to a temporary or
+permanent ban.
+
+### 3. Temporary Ban
+
+**Community Impact**: A serious violation of community standards, including
+sustained inappropriate behavior.
+
+**Consequence**: A temporary ban from any sort of interaction or public
+communication with the community for a specified period of time. No public or
+private interaction with the people involved, including unsolicited interaction
+with those enforcing the Code of Conduct, is allowed during this period.
+Violating these terms may lead to a permanent ban.
+
+### 4. Permanent Ban
+
+**Community Impact**: Demonstrating a pattern of violation of community
+standards, including sustained inappropriate behavior, harassment of an
+individual, or aggression toward or disparagement of classes of individuals.
+
+**Consequence**: A permanent ban from any sort of public interaction within
+the community.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage],
+version 2.1, available at
+[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
+
+Community Impact Guidelines were inspired by
+[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
+
+For answers to common questions about this code of conduct, see the FAQ at
+[https://www.contributor-covenant.org/faq][FAQ]. Translations are available
+at [https://www.contributor-covenant.org/translations][translations].
+
+[homepage]: https://www.contributor-covenant.org
+[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
+[Mozilla CoC]: https://github.com/mozilla/diversity
+[FAQ]: https://www.contributor-covenant.org/faq
+[translations]: https://www.contributor-covenant.org/translations
diff --git a/LICENSE b/LICENSE
index 4d333c1c7..261eeb9e9 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,22 +1,201 @@
-The MIT License (MIT)
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
-Copyright (c) 2014 Pooya Eimandar
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-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:
+ 1. Definitions.
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
-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.
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/Logo.png b/Logo.png
new file mode 100644
index 000000000..261a58993
Binary files /dev/null and b/Logo.png differ
diff --git a/README.md b/README.md
index 843fd1364..076dabe2b 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,116 @@
-Wolf-Engine
-===========
-
-
-The Wolf Game Engine is a cross-platform game engine created by Pooya Eimandar for use in game titles.
-
-This is the next generation of Persian Game Engine which is mainly focused on mobile platforms. The engine will be use on Microsoft WinRT platforms, Android and it will be adapted for a range of video game genres. You can use it under this License .
-
-
-More information about product will be provided after first Milestone.
-
+# Wolf Engine [](https://github.com/WolfEngine/Wolf.Engine/blob/main/LICENSE.md) [](https://codecov.io/github/WolfEngine/WolfEngine) [](https://github.com/WolfEngine/WolfEngine/actions/workflows/codeql.yml) [](https://github.com/WolfEngine/WolfEngine/actions/workflows/msvc.yml)
+
+
+Welcome to the Wolf Engine source code.
+The Wolf Engine is the next
+generation of Persian Game Engine which is a
+cross-platform open source game engine created by Pooya Eimandar .
+The Wolf is a comprehensive set of C++ open source libraries for realtime rendering, realtime streaming and game developing, which is support Lua and WASM as an embedded scripting languages.
+
+# Build
+- Prerequisites
+ - For windows, make sure install the latest Windows 11/10 SDK
+ - [git](https://git-scm.com/downloads)
+ - [CMake](https://cmake.org/download/)
+ - [vcpkg](https://vcpkg.io/)
+ - [Meson](https://github.com/mesonbuild/meson/releases)
+ - [Ninja](https://ninja-build.org/). Alternatively, setup [Python3](https://www.python.org/downloads/) and use "pip3 install ninja"
+ - [QT6](https://www.qt.io/download) for demos and examples
+ - [NDK](https://developer.android.com/ndk/downloads) for android.
+
+then make sure get the main branch
+`git clone https://github.com/WolfEngine/WolfEngine.git --branch main --depth 1`
+
+## CMakePresets
+
+To list configure presets: `cmake . --list-presets`
+To list build presets: `cmake --build --list-presets`
+To install wolf: `cmake --install --prefix `
+
+For example for building wolf for android:
+```
+cmake . --preset android-arm64-release
+cmake --build --preset android-arm64-release
+```
+
+For example for building wolf for windows:
+```
+cmake . --preset win-x64-release
+cmake --build --preset win-x64-release
+cmake --install C:/WolfEngine/build/win-x64-release --prefix C:/wolf
+```
+
+## Recent Sample
+Dynamic LOD Generation using Simplygon
+
+
+## Supported platforms
+
+| Not Supported | Planned | In Progress | Done |
+|:-----------:|:-----------:|:-----------:|:-----------:|
+| :x: | :memo: | :construction: | :white_check_mark: |
+
+### Supported platforms and APIs for render module
+
+| API | Windows | Linux | macOS | iOS | Android | Wasm |
+|:-----------:|:-----------:|:--------------------------:|:--------------:|:-------------:|:--------------:|:-------------:|
+| GPU | Vulkan/OpenGL ES :construction: | Vulkan/OpenGL ES :memo: | MoltenVK :memo: | MoltenVK :memo: | Vulkan/OpenGL ES :memo: | WebGL/WebGPU :memo: |
+
+### Supported platforms and APIs for media module
+
+| API | Windows | Linux | macOS | iOS | Android | Wasm |
+|:-----------:|:-----------:|:--------------------------:|:--------------:|:-------------:|:--------------:|:-------------:|
+| [Bitmap](https://github.com/WolfEngine/WolfEngine/blob/main/wolf/media/test/ffmpeg.hpp) | :white_check_mark: | :white_check_mark: | :memo: | :memo: | :memo: | :x: |
+| [FFmpeg](https://github.com/WolfEngine/WolfEngine/blob/main/wolf/stream/test/ffmpeg_stream.hpp) | :white_check_mark: | :white_check_mark: | :memo: | :memo: | :memo: | :x: |
+| [JPEG](https://github.com/WolfEngine/WolfEngine/blob/main/wolf/media/test/ffmpeg.hpp) | :white_check_mark: | :white_check_mark: | :memo: | :memo: | :memo: | :x: |
+| [OpenAL](https://github.com/WolfEngine/WolfEngine/blob/main/wolf/media/test/openal.hpp) | :white_check_mark: | :white_check_mark: | :memo: | :memo: | :memo: | :x: |
+| [PNG](https://github.com/WolfEngine/WolfEngine/blob/main/wolf/media/test/ffmpeg.hpp) | :white_check_mark: | :white_check_mark: | :memo: | :memo: | :memo: | :x: |
+| WebP | :memo: | :memo: | :memo: | :memo: | :memo: | :x: |
+
+### Supported platforms and APIs for stream module
+
+| API | Windows | Linux | macOS | iOS | Android | Wasm |
+|:-----------:|:-----------:|:--------------------------:|:--------------:|:-------------:|:--------------:|:-------------:|
+| gRPC | :memo: | :x: | :x: | :x: | :x: | :x: |
+| [Janus](https://github.com/WolfEngine/WolfEngine/tree/main/wolf_demo/wasm) | :construction: | :x: | :x: | :x: | :x: | :white_check_mark: |
+| QUIC | :memo: | :memo: | :memo: | :memo: | :memo: | :x: |
+| [RIST](https://github.com/WolfEngine/WolfEngine/blob/main/wolf/stream/test/rist.hpp) | :white_check_mark: | :memo: | :memo: | :memo: | :white_check_mark: | :x: |
+| RTMP | :memo: | :x: | :x: | :x: | :x: | :x: |
+| [RTSP](https://github.com/WolfEngine/WolfEngine/blob/main/wolf/stream/test/ffmpeg_stream.hpp) | :white_check_mark: | :memo: | :memo: | :memo: | :memo: | :x: |
+| [SRT](https://github.com/WolfEngine/WolfEngine/blob/main/wolf/stream/test/ffmpeg_stream.hpp) | :white_check_mark: | :memo: | :memo: | :memo: | :memo: | :x: |
+| webRTC | :memo: | :memo: | :memo: | :memo: | :memo: | :memo: |
+| [WebSocket](https://github.com/WolfEngine/WolfEngine/blob/main/wolf/system/test/ws.hpp) | :white_check_mark: | :white_check_mark: | :memo: | :memo: | :memo: | :memo: |
+
+### Supported platforms and APIs for system module
+
+| API | Windows | Linux | macOS | iOS | Android | Wasm |
+|:-----------:|:-----------:|:--------------------------:|:--------------:|:-------------:|:--------------:|:-------------:|
+| [Coroutine](https://github.com/WolfEngine/WolfEngine/blob/main/wolf/system/test/coroutine.hpp) | :white_check_mark: | :white_check_mark: | :memo: | :x: | :x: | :x: |
+| [GameTime](https://github.com/WolfEngine/WolfEngine/blob/main/wolf/system/test/gametime.hpp) | :white_check_mark: | :white_check_mark: | :memo: | :memo: | :memo: | :white_check_mark: |
+| [Gamepad](https://github.com/WolfEngine/WolfEngine/blob/main/wolf/system/test/gamepad.hpp) | :white_check_mark: | :white_check_mark: | :memo: | :memo: | :memo: | :white_check_mark: |
+| [Virtual Gamepad](https://github.com/WolfEngine/WolfEngine/blob/main/wolf/system/test/gamepad.hpp) | :white_check_mark: | :x: | :x: | :x: | :x: | :x: |
+| [Log](https://github.com/WolfEngine/WolfEngine/blob/main/wolf/system/test/log.hpp) | :white_check_mark: | :white_check_mark: | :construction: | :construction: | :construction: | :construction: |
+| LuaJit | :memo: | :memo: | :memo: | :memo: | :memo: | :x: |
+| [LZ4](https://github.com/WolfEngine/WolfEngine/blob/main/wolf/system/test/compress.hpp) | :white_check_mark: | :white_check_mark: | :memo: | :memo: | :memo: | :x: |
+| [LZMA](https://github.com/WolfEngine/WolfEngine/blob/main/wolf/system/test/compress.hpp) | :white_check_mark: | :white_check_mark: | :memo: | :x: | :x: | :x: |
+| OpenTelemetry | :memo: | :memo: | :memo: | :x: | :x: | :x: |
+| RAFT | :memo: | :memo: | :memo: | :memo: | :memo: | :memo: |
+| Screen Capture | :memo: | :construction: | :construction: | :x: | :x: | :x: |
+| [Signal Slot](https://github.com/WolfEngine/WolfEngine/blob/main/wolf/system/test/signal_slot.hpp) | :white_check_mark: | :white_check_mark: | :construction: | :x: | :x: | :x: |
+| [Stacktrace](https://github.com/WolfEngine/WolfEngine/blob/main/wolf/tests.cpp) | :white_check_mark: | :white_check_mark: | :construction: | :construction: | :construction: | :x: |
+| Sycl | :memo: | :memo: | :memo: | :x: | :x: | :x: |
+| [TCP](https://github.com/WolfEngine/WolfEngine/blob/main/wolf/system/test/tcp.hpp) | :white_check_mark: | :white_check_mark: | :memo: | :memo: | :memo: | :x: |
+| [Trace](https://github.com/WolfEngine/WolfEngine/blob/main/wolf/system/test/trace.hpp) | :white_check_mark: | :white_check_mark: | :memo: | :memo: | :memo: | :x: |
+| UDP | :construction: | :memo: | :memo: | :memo: | :memo: | :x: |
+| Wasm3 | :memo: | :memo: | :memo: | :memo: | :memo: | :memo: |
+
+## Projects using Wolf
+* [Wolf.Playout](https://www.youtube.com/watch?v=EZSdEjBvuGY), a playout automation software
+* [Falcon](https://youtu.be/ygpz35ddZ_4), a real time 3D monitoring system
+* [PlayPod](https://playpod.ir), the first cloud gaming platform launched in Middle East
+* [RivalArium](https://rivalarium.com), play and rival other users via our leagues and duels from any device, any location and let your skills generate income
+
+## [Youtube](https://www.youtube.com/c/WolfEngine)
+## [Twitter](https://www.twitter.com/Wolf_Engine)
+
+Wolf Engine © 2014-2023 [Pooya Eimandar](https://www.linkedin.com/in/pooyaeimandar/)
diff --git a/TODO.md b/TODO.md
new file mode 100644
index 000000000..ab7368fbc
--- /dev/null
+++ b/TODO.md
@@ -0,0 +1,7 @@
+- font rendering
+- support android
+- support windows
+- support linux
+- support osx
+- support ios
+
diff --git a/content/audio/sine.wav b/content/audio/sine.wav
new file mode 100644
index 000000000..fb3acf663
Binary files /dev/null and b/content/audio/sine.wav differ
diff --git a/content/texture/rgb.png b/content/texture/rgb.png
new file mode 100644
index 000000000..ea177c855
Binary files /dev/null and b/content/texture/rgb.png differ
diff --git a/coverage.bat b/coverage.bat
new file mode 100644
index 000000000..ffff76a84
--- /dev/null
+++ b/coverage.bat
@@ -0,0 +1 @@
+OpenCppCoverage.exe --continue_after_cpp_exception --export_type=html:%CD%\coverage\ --sources %CD% --excluded_sources %CD%\build\_deps -- %CD%\build\wolf\Debug\wolf_tests.exe
\ No newline at end of file
diff --git a/coverage/index.html b/coverage/index.html
new file mode 100644
index 000000000..0a899a8da
--- /dev/null
+++ b/coverage/index.html
@@ -0,0 +1,105 @@
+
+
+
+
+ wolf_tests.exe
+
+
+
+
+
+
+
+
+
+
+ wolf_tests.exe
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docker/Dockerfile b/docker/Dockerfile
new file mode 100644
index 000000000..bf3b221ba
--- /dev/null
+++ b/docker/Dockerfile
@@ -0,0 +1,39 @@
+ARG UBUNTU_VERSION="22.04"
+FROM ubuntu:${UBUNTU_VERSION}
+
+ENV VCPKG_ROOT=/opt/vcpkg
+ARG VCPKG_GITHUB_REPOSITORY=https://github.com/microsoft/vcpkg/archive/master.tar.gz
+ARG VCPKG_COMPRESSED_FILE_NAME=vcpkg.tar.gz
+
+RUN set -eux \
+ && export DEBIAN_FRONTEND="noninteractive" \
+ && apt-get update \
+ && apt-get install --yes --no-install-recommends \
+ build-essential \
+ ca-certificates \
+ cmake \
+ cpp \
+ curl \
+ git \
+ make \ls
+ pkg-config \
+ tar \
+ unzip \
+ wget \
+ zip
+
+WORKDIR ${VCPKG_ROOT}
+
+RUN wget -qO ${VCPKG_COMPRESSED_FILE_NAME} ${VCPKG_GITHUB_REPOSITORY} \
+ && tar xf ${VCPKG_COMPRESSED_FILE_NAME} --strip-components=1 \
+ && rm ${VCPKG_COMPRESSED_FILE_NAME} \
+ && /opt/vcpkg/bootstrap-vcpkg.sh \
+ && ln -s /opt/vcpkg/vcpkg /usr/local/bin/vcpkg
+
+WORKDIR /workspace
+
+COPY . .
+
+RUN cmake -B build --preset linux-x64-release
+
+RUN rm -rf ./*
diff --git a/docs/GCC.txt b/docs/GCC.txt
new file mode 100644
index 000000000..c297154cb
--- /dev/null
+++ b/docs/GCC.txt
@@ -0,0 +1,15 @@
+cd /home/build
+GCC_VERSION=10.2.0
+wget https://ftp.gnu.org/gnu/gcc/gcc-${GCC_VERSION}/gcc-${GCC_VERSION}.tar.gz
+tar xzvf gcc-${GCC_VERSION}.tar.gz
+mkdir obj.gcc-${GCC_VERSION}
+cd gcc-${GCC_VERSION}
+./contrib/download_prerequisites
+cd ../obj.gcc-${GCC_VERSION}
+#RedHat base
+sudo yum groupinstall "Development Tools"
+#Debian
+sudo apt-get install build-essential
+../gcc-${GCC_VERSION}/configure --disable-multilib --enable-languages=c,c++
+make -j $(nproc)
+make install
diff --git a/docs/ProblemSolutions.txt b/docs/ProblemSolutions.txt
new file mode 100644
index 000000000..6935c45c9
--- /dev/null
+++ b/docs/ProblemSolutions.txt
@@ -0,0 +1,83 @@
+------------------------------------------------------- -------------------------------------------------------
+-------------------- Problem ----------------------- -------------------- Solution -----------------------
+------------------------------------------------------- -------------------------------------------------------
+initializePlugin function could not be found in Add following command to the Liker->command Line
+Maya plug-in "/export:initializePlugin /export:uninitializePlugin"
+
+
+------------------------------------------------------- -------------------------------------------------------
+No target architecture error at compile time use #include instead of #include
+
+------------------------------------------------------- -------------------------------------------------------
+
+Copy relative content to the execution directory $(OutDir)/%(RelativeDir)/%(filename).cso for more info
+ see following link
+ http://msdn.microsoft.com/en-us/library/ms164313.aspx
+
+------------------------------------------------------- -------------------------------------------------------
+
+Debug does not active, breakpoint disable Properties->Linker->debugging->GenerateDebugInfo->yes
+ Properties->Linker->debugging->DebuggingAssembly->yes
+ Properties->C/C++->Code Generation->Multi-threaded Debug DLL (/MDd)
+
+------------------------------------------------------- -------------------------------------------------------
+
+Could not find entry point First right click on
+ Solution->Properties->Configuration Manager
+ set the x64
+ Then check dllMain or main function
+
+------------------------------------------------------- -------------------------------------------------------
+
+error MSB6006: "icl.exe" exited with code 4 switch from intel c++ to microsoft visual c++ to check other errors,
+ alsoCheck precompiled headers
+
+------------------------------------------------------- -------------------------------------------------------
+error LNK2019: unresolved external symbol __imp___CrtDbgReportW Remove _DEBUG from preprocessor or somwhere in your code
+referenced in function, when you add std::map or std::vector
+
+------------------------------------------------------- -------------------------------------------------------
+Can link constructor or destructor ot method of a class make sure add API(see W_Object.h) before declaration of method in class
+from DLL
+
+
+------------------------------------------------------- -------------------------------------------------------
+eglGetPlatformDisplayEXT does not generate display device make sure copy libGLESv2.dll, libEGL.dll & d3dcompiler_47.dll in bin folder
+in Angle project
+
+------------------------------------------------------- -------------------------------------------------------
+Error MSB6006 "fxc.exe" exited with code 1. check all *.hlsl codes and shader type in Properties->HLSL Compiler->General in your project
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/git-commands.txt b/docs/git-commands.txt
new file mode 100644
index 000000000..ca3fdde51
--- /dev/null
+++ b/docs/git-commands.txt
@@ -0,0 +1,14 @@
+//subtrees
+
+git config commit.gpgsign false
+git fetch git@github.com:g-truc/glm.git master
+git subtree add --prefix=engine/dependencies/glm-master/ --squash git@github.com:g-truc/glm.git master
+git subtree pull --prefix=engine/dependencies/glm-master/ --squash git@github.com:g-truc/glm.git master
+
+git fetch git@github.com:dwd/rapidxml.git master
+git subtree add --prefix=engine/dependencies/rapidxml-master/ --squash git@github.com:dwd/rapidxml.git master
+git subtree pull --prefix=engine/dependencies/rapidxml-master/ --squash git@github.com:dwd/rapidxml.git master
+
+git fetch git@github.com:miloyip/rapidjson.git master
+git subtree add --prefix=engine/dependencies/rapidjson-master/ --squash git@github.com:miloyip/rapidjson.git master
+git subtree pull --prefix=engine/dependencies/rapidjson-master/ --squash git@github.com:miloyip/rapidjson.git master
diff --git a/docs/glslValidator.txt b/docs/glslValidator.txt
new file mode 100644
index 000000000..8daeef456
--- /dev/null
+++ b/docs/glslValidator.txt
@@ -0,0 +1 @@
+./glslangValidator.exe -V -t -o ~path/to/out.spv ~/path/to/in.vert
\ No newline at end of file
diff --git a/docs/glslValidator_MoltenVK.txt b/docs/glslValidator_MoltenVK.txt
new file mode 100644
index 000000000..696bbadf5
--- /dev/null
+++ b/docs/glslValidator_MoltenVK.txt
@@ -0,0 +1,6 @@
+cd /Users/pooyaeimandar/Documents/github/WolfSource/Wolf.Engine/engine/dependencies/vulkan/Mac/Molten/MoltenShaderConverter/Tools/
+
+./MoltenShaderConverter -gi /Users/pooyaeimandar/Documents/github/WolfSource/Wolf.Engine/samples/02_basics/02_shader/src/content/shaders/shader.frag -t f -so /Users/pooyaeimandar/Documents/github/WolfSource/Wolf.Engine/samples/02_basics/02_shader/src/content/shaders/shader.frag.spv
+
+
+./MoltenShaderConverter -gi /Users/pooyaeimandar/Documents/github/WolfSource/Wolf.Engine/samples/02_basics/02_shader/src/content/shaders/shader.vert -t v -so /Users/pooyaeimandar/Documents/github/WolfSource/Wolf.Engine/samples/02_basics/02_shader/src/content/shaders/shader.vert.spv
\ No newline at end of file
diff --git a/manifest.manifest b/manifest.manifest
new file mode 100644
index 000000000..1c1323f75
--- /dev/null
+++ b/manifest.manifest
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/wolf/.clang-format b/wolf/.clang-format
new file mode 100644
index 000000000..c8838e98f
--- /dev/null
+++ b/wolf/.clang-format
@@ -0,0 +1,33 @@
+# http://clang.llvm.org/docs/ClangFormatStyleOptions.html
+BasedOnStyle: Google
+# This defaults to 'Auto'. Explicitly set it for a while, so that
+# 'vector >' in existing files gets formatted to
+# 'vector>'. ('Auto' means that clang-format will only use
+# 'int>>' if the file already contains at least one such instance.)
+Standard: Cpp11
+SortIncludes: true
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
+BreakStringLiterals: false
+DerivePointerAlignment: true
+PointerAlignment: Left
+ColumnLimit: 100
+ForEachMacros: ['list_for_every_entry','list_for_every_entry_safe']
+IncludeBlocks: Regroup
+IncludeCategories:
+ # This specific header must come last in kernel source files. Its
+ # matching rule must come first so the lower-priority rules don't match.
+ - Regex: '^'
+ Priority: 1000
+ # C Header: , , etc
+ - Regex: '^(<((zircon/|lib/|fuchsia/|arpa/|net/|netinet/|sys/|fidl/)[a-zA-Z0-9_/\.-]+\.h|[a-zA-Z0-9_-]+\.h)>)'
+ Priority: 1
+ # Cpp Header: and
+ - Regex: '^(<(experimental/)*[a-zA-Z0-9_-]+>)'
+ Priority: 2
+ # Libraries:
+ - Regex: '^(<[a-zA-Z0-9_/-]+\.h>)'
+ Priority: 3
+ # Local headers: "foo/bar.h"
+ - Regex: '^("[.a-zA-Z0-9_/-]+\.h")'
+ Priority: 4
\ No newline at end of file
diff --git a/wolf/.clang-tidy b/wolf/.clang-tidy
new file mode 100644
index 000000000..2b2f09529
--- /dev/null
+++ b/wolf/.clang-tidy
@@ -0,0 +1,68 @@
+---
+Checks: >
+ -*,
+ bugprone-*,
+ clang-diagnostic-*,
+ -clang-diagnostic-unused-command-line-argument,
+ google-*,
+ -google-runtime-references,
+ misc-*,
+ -misc-noexcept*,
+ -misc-unused-parameters,
+ -misc-const-correctness,
+ modernize-*,
+ -modernize-avoid-c-arrays,
+ -modernize-deprecated-headers,
+ -modernize-raw-string-literal,
+ -modernize-return-braced-init-list,
+ -modernize-use-auto,
+ -modernize-use-equals-delete,
+ -modernize-use-nodiscard,
+ -modernize-use-trailing-return-type,
+ performance-*,
+ readability-*,
+ -readability-function-cognitive-complexity,
+ -readability-identifier-length,
+ -readability-implicit-bool-conversion,
+ -readability-isolate-declaration,
+ -readability-magic-numbers,
+ -readability-qualified-auto,
+ -readability-uppercase-literal-suffix,
+ -readability-identifier-length,
+ -readability-named-parameter,
+WarningsAsErrors: false
+AnalyzeTemporaryDtors: false
+FormatStyle: file
+CheckOptions:
+ - key: bugprone-signed-char-misuse.CharTypdefsToIgnore
+ value: 'int8_t'
+ - key: bugprone-assert-side-effect.AssertMacros
+ value: 'FXL_DCHECK'
+ - key: google-readability-braces-around-statements.ShortStatementLines
+ value: '2'
+ - key: google-readability-function-size.StatementThreshold
+ value: '800'
+ - key: google-readability-namespace-comments.ShortNamespaceLines
+ value: '10'
+ - key: google-readability-namespace-comments.SpacesBeforeComments
+ value: '2'
+ - key: misc-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic
+ value: '1'
+ - key: modernize-loop-convert.MaxCopySize
+ value: '16'
+ - key: modernize-loop-convert.MinConfidence
+ value: reasonable
+ - key: modernize-loop-convert.NamingStyle
+ value: CamelCase
+ - key: modernize-pass-by-value.IncludeStyle
+ value: llvm
+ - key: modernize-replace-auto-ptr.IncludeStyle
+ value: llvm
+ - key: modernize-use-default-member-init.UseAssignment
+ value: '1'
+ - key: modernize-use-emplace.IgnoreImplicitConstructors
+ value: '1'
+ - key: modernize-use-nullptr.NullMacros
+ value: 'NULL'
+ - key: readability-braces-around-statements.ShortStatementLines
+ value: '2'
\ No newline at end of file
diff --git a/wolf/.gitignore b/wolf/.gitignore
new file mode 100644
index 000000000..08b0e5705
--- /dev/null
+++ b/wolf/.gitignore
@@ -0,0 +1,5 @@
+*.DS_Store
+*.suo
+/build/*
+/shells/ffmpeg/build/*
+/rust/target/*
\ No newline at end of file
diff --git a/wolf/CMakeLists.txt b/wolf/CMakeLists.txt
new file mode 100644
index 000000000..9532b814a
--- /dev/null
+++ b/wolf/CMakeLists.txt
@@ -0,0 +1,386 @@
+#cmake . -B build -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake -DANDROID_ABI=armeabi-v7a -DANDROID_NDK=$ANDROID_NDK_HOME -DANDROID_PLATFORM=android-21 -DCMAKE_ANDROID_ARCH_ABI=armeabi-v7a -DCMAKE_ANDROID_NDK=$ANDROID_NDK_HOME -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_SYSTEM_NAME=Android -DCMAKE_SYSTEM_VERSION=21 -DCMAKE_BUILD_TYPE=Debug -GNinja
+#cd build
+#ninja
+
+cmake_minimum_required(VERSION 3.22...3.23)
+
+# set the name of the projects
+project(wolf
+ DESCRIPTION "Wolf Engine"
+ LANGUAGES CXX
+)
+
+set(TEST_PROJECT_NAME "${PROJECT_NAME}_tests")
+message("CXX Compiler ID is ${CMAKE_CXX_COMPILER_ID}")
+
+# set the options and enviroment variables
+#set(WEBRTC_SRC $ENV{WEBRTC_ROOT} CACHE STRING "path to the root folder of webrtc folder")
+
+#define includes, libs and srcs
+set(INCLUDES)
+set(LIBS PARENT_SCOPE)
+set(SRCS)
+set(TESTS_SRCS)
+
+# check the OS
+if(CMAKE_SIZEOF_VOID_P EQUAL 8)
+ if (WIN32)
+ set(WIN64 TRUE)
+ endif()
+endif()
+
+if(UNIX AND (NOT APPLE) AND (NOT EMSCRIPTEN))
+ set(LINUX TRUE)
+endif()
+
+if (MSVC AND NOT WIN64)
+ message( FATAL_ERROR "Only Window 64 bit is supported" )
+endif()
+
+# set target os
+if (WIN64)
+ set(TARGET_OS "win")
+ set(LIB_EXT "lib")
+ set(SHARED_EXT "dll")
+elseif(APPLE)
+ set(TARGET_OS "mac")
+ set(LIB_EXT "a")
+ set(SHARED_EXT "dylib")
+elseif(LINUX)
+ set(TARGET_OS "linux")
+ set(LIB_EXT "a")
+ set(SHARED_EXT "so")
+elseif (NOT EMSCRIPTEN)
+ message( FATAL_ERROR "Unsuported OS, please open an issue at https://github.com/WolfEngine/WolfEngine" )
+endif()
+
+# required packages
+find_package(Git REQUIRED)
+if (NOT EMSCRIPTEN)
+ find_package (Threads)
+endif()
+
+# use folders
+set_property(GLOBAL PROPERTY USE_FOLDERS ON)
+
+# FetchContent for cloning repositories, avaiable since CMAKE 3.11
+include(FetchContent)
+set(FETCHCONTENT_QUIET OFF)
+
+# set type of library
+if (LINUX OR ANDROID_ABI)
+ set(LIBRARY_TYPE "SHARED")
+else()
+ set(LIBRARY_TYPE "STATIC")
+endif()
+
+set(LIBRARY_TYPE "${LIBRARY_TYPE}" CACHE STRING "Library type")
+
+# Build options
+set(LIB_INSTALL_DIR lib CACHE STRING "Install location of libraries")
+set(BIN_INSTALL_DIR bin CACHE STRING "Install location of executables")
+set(INCLUDE_INSTALL_DIR include CACHE STRING "Install location of executables")
+
+# CMAKE GUI Options
+set(EMSDK "$ENV{EMSDK}" CACHE STRING "Emscripten SDK path")
+set(BOOST_VERSION "1.82.0" CACHE STRING "Boost version tag")
+
+# render module
+option(WOLF_RENDER "Enable cross platform render engine based on Vulkan / OpenGL ES" OFF)
+
+# media modules
+option(WOLF_MEDIA_FFMPEG "Enable ffmpeg encoding and decoding" OFF)
+option(WOLF_MEDIA_OPENAL "Enable OpenAL soft" OFF)
+option(WOLF_MEDIA_SCREEN_CAPTURE "Enable screen capture" OFF)
+option(WOLF_MEDIA_STB "Enable stb headers" OFF)
+option(WOLF_MEDIA_GSTREAMER "Enable gstreamer wrapper" OFF)
+
+# stream modules
+option(WOLF_STREAM_GRPC "Enable gRPC connection" ON)
+option(WOLF_STREAM_QUIC "Enable QUIC" OFF)
+option(WOLF_STREAM_RIST "Enable RIST streaming protocol" OFF)
+option(WOLF_STREAM_WEBRTC "Enable webRTC" OFF)
+
+# system modules
+option(WOLF_SYSTEM_GAMEPAD_CLIENT "Enable gamepad input handling" OFF)
+option(WOLF_SYSTEM_GAMEPAD_VIRTUAL "Enable virtual gamepad simulator" OFF)
+option(WOLF_SYSTEM_HTTP_WS "Enable http1.1 and websocket client/server based on boost beast or Emscripten" OFF)
+option(WOLF_SYSTEM_LOG "Enable log" OFF)
+option(WOLF_SYSTEM_LZ4 "Enable lz4 for compression" OFF)
+option(WOLF_SYSTEM_LZMA "Enable lzma for compression" OFF)
+option(WOLF_SYSTEM_LUA "Enable lua scripting language" OFF)
+option(WOLF_SYSTEM_MIMALLOC "Enable Microsoft's mimalloc memory allocator" OFF)
+option(WOLF_SYSTEM_POSTGRESQL "Enable postgresql database client" OFF)
+option(WOLF_SYSTEM_PYTHON "Enable embedded Python3 scripting" OFF)
+option(WOLF_SYSTEM_SIG_SLOT "Enable signal/slot based on boost signals2" OFF)
+option(WOLF_SYSTEM_SOCKET "Enable TCP/UDP protocol over socket" OFF)
+option(WOLF_SYSTEM_OPENSSL "Enable openSSL" OFF)
+option(WOLF_SYSTEM_STACKTRACE "Enable boost stacktrace" OFF)
+option(WOLF_SYSTEM_ZLIB "Enable Zlib compression library" OFF)
+
+# machine learing modules
+option(WOLF_ML_NUDITY_DETECTION "Enable machine learning nudity detection" OFF)
+option(WOLF_ML_OCR "Enable machine learning referee ocr" OFF)
+
+#option(WOLF_ENABLE_LTO "Enable cross language linking time optimization" OFF)
+option(WOLF_TEST "Enable tests" ON)
+if (NOT MSVC)
+ option(WOLF_ENABLE_ASAN "Enable ASAN" OFF)
+endif()
+
+if(ENABLE_LTO)
+ include(CheckIPOSupported)
+ check_ipo_supported(RESULT supported OUTPUT error)
+ if(supported)
+ message(STATUS "IPO / LTO enabled")
+ set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
+ add_link_options(-fuse-ld=lld)
+ else()
+ message(STATUS "IPO / LTO not supported: <${error}>")
+ endif()
+endif()
+
+# set C/CXX standards
+set(CMAKE_C_STANDARD 23)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+set(CMAKE_CXX_EXTENSIONS ON)
+set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
+set(THREADS_PREFER_PTHREAD_FLAG TRUE)
+
+#set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
+if ("${CMAKE_BUILD_TYPE}" STREQUAL "")
+ set(CMAKE_BUILD_TYPE "Debug")
+endif()
+
+# set C++ flags based on compiler
+if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
+ # using Clang or AppleClang
+ set(CMAKE_CXX_STANDARD 20)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++2b -fexceptions -fcoroutines-ts")
+ set(CMAKE_CXX_FLAGS_DEBUG "-g")
+ set(CMAKE_CXX_FLAGS_RELEASE "-O3")
+elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+ # using GCC
+ set(CMAKE_CXX_STANDARD 23)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++2b -fexceptions -fcoroutines")
+ set(CMAKE_CXX_FLAGS_DEBUG "-g")
+ set(CMAKE_CXX_FLAGS_RELEASE "-O3")
+elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
+ # using Microsoft Visual C++
+ # set C++23 as the primary standard
+ set(CMAKE_CXX_STANDARD 23)
+ set(CMAKE_CXX_FLAGS "/EHsc /W3 /bigobj")
+endif()
+
+set(GETOPT_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/system)
+
+# include cmakes
+include(cmake/vcpkg.cmake)
+include(cmake/system.cmake)
+include(cmake/stream.cmake)
+include(cmake/media.cmake)
+include(cmake/ml.cmake)
+
+if (EMSCRIPTEN)
+ message(WARNING "WOLF_TEST will be disabled for Emscripten")
+ set(WOLF_TEST OFF)
+else()
+ # currently threads was not supported with WASM
+ list(APPEND LIBS Threads::Threads)
+endif()
+
+# disable build testing
+set(BUILD_TESTING OFF CACHE BOOL "BUILD_TESTING")
+
+if (WOLF_ENABLE_ASAN)
+ set(ENABLE_ASAN TRUE)
+endif()
+
+#add_compile_options(-fsanitize=address)
+#add_link_options(-fsanitize=address)
+
+# enabling clang-tidy
+# can be enabled with .CLANG-TIDY from Visual Studio Code
+# https://devblogs.microsoft.com/cppblog/visual-studio-code-c-december-2021-update-clang-tidy/
+# can be enabled with .CLANG-TIDY from Visual Studio
+# https://devblogs.microsoft.com/cppblog/code-analysis-with-clang-tidy-in-visual-studio/
+#set(CMAKE_C_CLANG_TIDY
+# clang-tidy;
+# -format-style=file;)
+#set(CMAKE_CXX_CLANG_TIDY
+# clang-tidy;
+# -format-style=file;)
+
+# add definitions
+
+add_definitions(
+ -DBOOST_ASIO_NO_DEPRECATED
+ -DBOOST_ASIO_HAS_CO_AWAIT
+ -DBOOST_ASIO_HAS_STD_COROUTINE
+)
+if (MSVC)
+ add_definitions(
+ -EHsc
+ -DNOMINMAX
+ -DWIN32_LEAN_AND_MEAN
+ -DWIN64
+ -DWIN32
+ )
+elseif (EMSCRIPTEN)
+ add_definitions(
+ -DEMSCRIPTEN
+ )
+elseif(APPLE)
+ add_definitions(-DNEED_XLOCALE_H=1)
+endif()
+
+if(CMAKE_BUILD_TYPE MATCHES Debug)
+ add_definitions(-DDEBUG -D_DEBUG)
+else()
+ add_definitions(-DNDEBUG)
+endif()
+
+# setup Wolf definitions
+get_cmake_property(_vars VARIABLES)
+foreach (_var ${_vars})
+ string(FIND ${_var} "WOLF_" out)
+ if(("${out}" EQUAL 0) AND ("(${${_var}}" MATCHES ON))
+ add_definitions("-D${_var}")
+ endif()
+endforeach()
+
+# include sources
+file(GLOB_RECURSE WOLF_SRCS
+ "${CMAKE_CURRENT_SOURCE_DIR}/wolf.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/wolf.hpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/DISABLE_ANALYSIS_BEGIN"
+ "${CMAKE_CURRENT_SOURCE_DIR}/DISABLE_ANALYSIS_END"
+)
+
+file(GLOB_RECURSE WOLF_PROTOS
+ "${CMAKE_CURRENT_SOURCE_DIR}/protos/*"
+)
+
+file(GLOB_RECURSE WOLF_CMAKES
+ "${CMAKE_CURRENT_SOURCE_DIR}/cmake/*"
+)
+
+# includes
+include_directories(
+ ${INCLUDES}
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR}/../
+)
+
+# add source codes
+add_library(${PROJECT_NAME} ${LIBRARY_TYPE}
+ ${SRCS}
+ ${WOLF_SRCS}
+ ${WOLF_CMAKES}
+ ${WOLF_PROTOS}
+)
+
+if (WOLF_STREAM_RIST)
+ add_dependencies(${PROJECT_NAME} ${RIST_TARGET})
+endif()
+
+if (MSVC OR WIN32)
+ if (LIBRARY_TYPE STREQUAL "STATIC")
+ set_property(TARGET ${PROJECT_NAME} PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>")
+ else()
+ set_property(TARGET ${PROJECT_NAME} PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>DLL")
+ endif()
+endif()
+
+# link libraries
+target_link_libraries(${PROJECT_NAME} PUBLIC ${LIBS})
+
+# create source group
+source_group("wolf" FILES ${WOLF_SRCS})
+source_group("cmake" FILES ${WOLF_CMAKES})
+source_group("protos" FILES ${WOLF_PROTOS})
+source_group("stream/grpc" FILES ${WOLF_STREAM_GRPC_SRC})
+source_group("stream/janus" FILES ${WOLF_STREAM_JANUS_SRC})
+source_group("stream/test" FILES ${WOLF_STREAM_TEST_SRC})
+source_group("stream/quic" FILES ${WOLF_STREAM_QUIC_SRC})
+source_group("stream/rist" FILES ${WOLF_STREAM_RIST_SRC})
+source_group("stream/webrtc/capturer" FILES ${WOLF_STREAM_WEBRTC_CAPTURER_SRC})
+source_group("stream/webrtc/data" FILES ${WOLF_STREAM_WEBRTC_DATA_SRC})
+source_group("stream/webrtc/interceptor" FILES ${WOLF_STREAM_WEBRTC_INTERCEPTOR_SRC})
+source_group("stream/webrtc/media" FILES ${WOLF_STREAM_WEBRTC_MEDIA_SRC})
+source_group("stream/webrtc/peer" FILES ${WOLF_STREAM_WEBRTC_PEER_SRC})
+source_group("stream/test" FILES ${WOLF_STREAM_QUIC_SRC})
+source_group("stream" FILES ${WOLF_STREAM_SRC})
+source_group("system/gamepad" FILES ${WOLF_SYSTEM_GAMEPAD_CLIENT_SRC} ${WOLF_SYSTEM_GAMEPAD_VIRTUAL_SRCS})
+source_group("system/log" FILES ${WOLF_SYSTEM_LOG_SRC})
+source_group("system/compression" FILES ${WOLF_SYSTEM_LZ4_SRCS} ${WOLF_SYSTEM_LZMA_SRCS})
+source_group("system/script" FILES ${WOLF_SYSTEM_LUA_SRC})
+source_group("system/script" FILES ${WOLF_SYSTEM_PYTHON_SRC})
+source_group("system/socket" FILES ${WOLF_SYSTEM_SOCKET_SRC} ${WOLF_SYSTEM_HTTP_WS_SRC})
+source_group("system/test" FILES ${WOLF_SYSTEM_TEST_SRC})
+source_group("system" FILES ${WOLF_SYSTEM_SRC})
+source_group("media/test" FILES ${WOLF_MEDIA_TEST_SRC})
+source_group("media/ffmpeg" FILES ${WOLF_MEDIA_FFMPEG_SRC})
+source_group("media" FILES ${WOLF_MEDIA_OPENAL_SRC} ${WOLF_MEDIA_STB_SRC})
+source_group("ml/referee_ocr" FILES ${WOLF_ML_OCR_SRC})
+source_group("ml/nudity_detection" FILES ${WOLF_ML_NUD_DET_SRC})
+
+# add compile options
+if (NOT WIN32)
+ target_compile_options(${PROJECT_NAME} PRIVATE -std=c++2b -fPIC)
+endif()
+
+if (WOLF_ENABLE_ASAN)
+ target_compile_options(${PROJECT_NAME} PRIVATE -fsanitize=address)
+ target_link_options(${PROJECT_NAME} PRIVATE -fsanitize=address)
+endif()
+
+# build tests
+if (WOLF_TEST)
+ add_executable(${TEST_PROJECT_NAME}
+ tests.cpp
+ ${TESTS_SRCS}
+ )
+
+ if (MSVC OR WIN32)
+ if (LIBRARY_TYPE STREQUAL "STATIC")
+ set_property(TARGET ${TEST_PROJECT_NAME} PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>")
+ else()
+ set_property(TARGET ${TEST_PROJECT_NAME} PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>DLL")
+ endif()
+ endif()
+
+ if(WOLF_ML_OCR AND LINUX)
+ target_link_libraries(${TEST_PROJECT_NAME} PRIVATE ${PROJECT_NAME} ${leptonica_BINARY_DIR}/install/lib/libleptonica.so)
+ else()
+ target_link_libraries(${TEST_PROJECT_NAME} PRIVATE ${PROJECT_NAME})
+ endif()
+
+ if (NOT WIN32)
+ target_compile_options(${TEST_PROJECT_NAME} PRIVATE -std=c++2b)
+ endif()
+
+ include(CTest)
+ add_test(NAME ${TEST_PROJECT_NAME} COMMAND ${TEST_PROJECT_NAME})
+endif()
+
+if(WOLF_ML_OCR AND WIN64)
+ add_custom_command(TARGET ${TEST_PROJECT_NAME} POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_BINARY_DIR}/bin/${CMAKE_BUILD_TYPE} ${CMAKE_BINARY_DIR}/${PROJECT_NAME}/${CMAKE_BUILD_TYPE}
+ )
+endif()
+
+# install target
+install(TARGETS ${PROJECT_NAME}
+ LIBRARY DESTINATION ${LIB_INSTALL_DIR}
+ ARCHIVE DESTINATION ${LIB_INSTALL_DIR})
+
+foreach(ITEM ${INCLUDES})
+ install(DIRECTORY ${ITEM}/ DESTINATION ${INCLUDE_INSTALL_DIR})
+endforeach()
+
+install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/wolf.hpp DESTINATION ${INCLUDE_INSTALL_DIR})
+
+if (WOLF_TEST)
+ install(TARGETS ${TEST_PROJECT_NAME} DESTINATION ${BIN_INSTALL_DIR})
+endif()
diff --git a/wolf/DISABLE_ANALYSIS_BEGIN b/wolf/DISABLE_ANALYSIS_BEGIN
new file mode 100644
index 000000000..a17af7e5d
--- /dev/null
+++ b/wolf/DISABLE_ANALYSIS_BEGIN
@@ -0,0 +1,5 @@
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning (disable:ALL_CODE_ANALYSIS_WARNINGS)
+#endif
+// NOLINTBEGIN
diff --git a/wolf/DISABLE_ANALYSIS_END b/wolf/DISABLE_ANALYSIS_END
new file mode 100644
index 000000000..c528772b3
--- /dev/null
+++ b/wolf/DISABLE_ANALYSIS_END
@@ -0,0 +1,4 @@
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+// NOLINTEND
\ No newline at end of file
diff --git a/wolf/cmake/media.cmake b/wolf/cmake/media.cmake
new file mode 100644
index 000000000..ce5dd0c1d
--- /dev/null
+++ b/wolf/cmake/media.cmake
@@ -0,0 +1,111 @@
+ # link to ffmpeg
+if (WOLF_MEDIA_FFMPEG)
+ file(GLOB_RECURSE WOLF_MEDIA_FFMPEG_SRC
+ "${CMAKE_CURRENT_SOURCE_DIR}/media/ffmpeg/*"
+ )
+ list(APPEND SRCS ${WOLF_MEDIA_FFMPEG_SRC})
+ list(APPEND INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}/third_party/ffmpeg/include)
+
+ list(APPEND FFMPEG_LIBS
+ avcodec
+ avdevice
+ avfilter
+ avformat
+ avutil
+ swresample
+ swscale
+ )
+
+ foreach (lib_name ${FFMPEG_LIBS})
+ list(APPEND LIBS ${CMAKE_CURRENT_SOURCE_DIR}/third_party/ffmpeg/lib/${TARGET_OS}/${lib_name}.${LIB_EXT})
+ endforeach()
+endif()
+
+# link openAL
+if (WOLF_MEDIA_OPENAL)
+ message("fetching https://github.com/kcat/openal-soft.git")
+
+ FetchContent_Declare(
+ openal
+ GIT_REPOSITORY https://github.com/kcat/openal-soft.git
+ GIT_TAG master
+ )
+
+ set(ALSOFT_EXAMPLES OFF CACHE BOOL "ALSOFT_EXAMPLES")
+ set(ALSOFT_INSTALL_EXAMPLES OFF CACHE BOOL "ALSOFT_INSTALL_EXAMPLES")
+ set(LIBTYPE "STATIC" CACHE STRING "STATIC")
+
+ set(FETCHCONTENT_QUIET OFF)
+ FetchContent_MakeAvailable(openal)
+
+ file(GLOB_RECURSE WOLF_MEDIA_OPENAL_SRC
+ "${CMAKE_CURRENT_SOURCE_DIR}/media/w_openal.hpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/media/w_openal.cpp"
+ )
+
+ list(APPEND SRCS ${WOLF_MEDIA_OPENAL_SRC})
+ list(APPEND INCLUDES ${openal_SOURCE_DIR}/include)
+ list(APPEND LIBS OpenAL::OpenAL)
+
+ set_target_properties(
+ build_version
+ common
+ ex-common
+ OpenAL
+ openal-info
+ PROPERTIES FOLDER "openAL")
+
+endif()
+
+if (WOLF_MEDIA_STB)
+ message("fetching https://github.com/nothings/stb.git")
+
+ FetchContent_Declare(
+ stb
+ GIT_REPOSITORY https://github.com/nothings/stb.git
+ GIT_TAG master
+ )
+
+ FetchContent_Populate(stb)
+
+ file(GLOB_RECURSE WOLF_MEDIA_STB_SRC
+ "${CMAKE_CURRENT_SOURCE_DIR}/media/w_image.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/media/w_image.hpp"
+ )
+
+ list(APPEND SRCS ${WOLF_MEDIA_STB_SRC})
+ list(APPEND INCLUDES ${stb_SOURCE_DIR})
+
+endif()
+
+if (WOLF_MEDIA_GSTREAMER)
+ file(GLOB_RECURSE WOLF_MEDIA_GSTREAMER_SRC
+ "${CMAKE_CURRENT_SOURCE_DIR}/media/gst/*"
+ "${CMAKE_CURRENT_SOURCE_DIR}/media/gst/audio/*"
+ "${CMAKE_CURRENT_SOURCE_DIR}/media/gst/core/*"
+ "${CMAKE_CURRENT_SOURCE_DIR}/media/gst/elements/*"
+ "${CMAKE_CURRENT_SOURCE_DIR}/media/gst/video/*"
+ )
+
+ find_package(PkgConfig REQUIRED)
+
+ pkg_check_modules(gstreamer REQUIRED IMPORTED_TARGET
+ gstreamer-1.0
+ gstreamer-video-1.0
+ gstreamer-audio-1.0)
+
+ add_library(gstreamer-lib INTERFACE)
+
+ target_compile_options(gstreamer-lib INTERFACE ${gstreamer_CFLAGS})
+ target_include_directories(gstreamer-lib INTERFACE ${gstreamer_INCLUDE_DIRS})
+ target_link_directories(gstreamer-lib BEFORE INTERFACE ${gstreamer_LIBRARY_DIRS})
+ target_link_libraries(gstreamer-lib INTERFACE ${gstreamer_LIBRARIES})
+
+ list(APPEND SRCS ${WOLF_MEDIA_GSTREAMER_SRC})
+ list(APPEND LIBS gstreamer-lib)
+endif()
+
+file(GLOB_RECURSE WOLF_MEDIA_TEST_SRC
+"${CMAKE_CURRENT_SOURCE_DIR}/media/test/*"
+)
+list(APPEND SRCS ${WOLF_MEDIA_TEST_SRC})
diff --git a/wolf/cmake/ml.cmake b/wolf/cmake/ml.cmake
new file mode 100644
index 000000000..8ef6d3e97
--- /dev/null
+++ b/wolf/cmake/ml.cmake
@@ -0,0 +1,196 @@
+if(WOLF_ML_OCR)
+ if(LINUX)
+ # fetch leptonica
+ message("fetching https://github.com/DanBloomberg/leptonica.git")
+ FetchContent_Declare(
+ leptonica
+ GIT_REPOSITORY https://github.com/DanBloomberg/leptonica.git
+ GIT_TAG 1.80.0
+ GIT_SHALLOW TRUE
+ GIT_PROGRESS TRUE
+ )
+ FetchContent_Populate(leptonica)
+
+ add_custom_command(OUTPUT lept_config.out COMMAND cmake -B ${leptonica_BINARY_DIR} -DBUILD_SHARED_LIBS=1 -DCMAKE_INSTALL_PREFIX:PATH=${leptonica_BINARY_DIR}/install ${leptonica_SOURCE_DIR})
+ add_custom_target(lept_config ALL DEPENDS lept_config.out)
+ add_custom_command(OUTPUT lept_build.out COMMAND cmake --build ${leptonica_BINARY_DIR} --target install)
+ add_custom_target(lept_build ALL DEPENDS lept_build.out)
+ endif()
+
+ # fetch tesseract
+ message("fetching https://github.com/tesseract-ocr/tesseract.git")
+ FetchContent_Declare(
+ tesseract
+ GIT_REPOSITORY https://github.com/tesseract-ocr/tesseract.git
+ GIT_TAG main
+
+ GIT_SHALLOW TRUE
+ GIT_PROGRESS TRUE
+ )
+
+ if(WIN64)
+ set(FETCHCONTENT_QUIET OFF)
+
+ set(BUILD_TESTS OFF CACHE BOOL "BUILD_TESTS")
+ set(BUILD_TRAINING_TOOLS OFF CACHE BOOL "BUILD_TRAINING_TOOLS")
+ set(DISABLE_ARCHIVE ON CACHE BOOL "DISABLE_ARCHIVE")
+ set(DISABLE_CURL ON CACHE BOOL "DISABLE_CURL")
+ set(FAST_FLOAT ON CACHE BOOL "FAST_FLOAT")
+ set(GRAPHICS_DISABLED ON CACHE BOOL "GRAPHICS_DISABLED")
+ set(INSTALL_CONFIGS OFF CACHE BOOL "INSTALL_CONFIGS")
+ set(SW_BUILD ON CACHE BOOL "SW_BUILD")
+
+ FetchContent_MakeAvailable(tesseract)
+ list(APPEND INCLUDES
+ ${tesseract_SOURCE_DIR}/include
+ ${tesseract_BINARY_DIR}/include
+ )
+
+ if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
+ set(DEBUG_LIB_EXTENTION "d")
+ else()
+ set(DEBUG_LIB_EXTENTION "")
+ endif()
+
+ list(APPEND LIBS
+ ${tesseract_BINARY_DIR}/${CMAKE_BUILD_TYPE}/tesseract53${DEBUG_LIB_EXTENTION}.lib
+ )
+ elseif(LINUX)
+ FetchContent_Populate(tesseract)
+
+ list(APPEND INCLUDES
+ ${tesseract_SOURCE_DIR}/include
+ ${tesseract_BINARY_DIR}/install/include
+ )
+
+ link_directories(${tesseract_BINARY_DIR}/install/lib)
+ list(APPEND LIBS
+ tesseract
+ )
+
+ add_custom_command(OUTPUT tess_config.out COMMAND cmake -B ${tesseract_BINARY_DIR} -DBUILD_SHARED_LIBS=1 -DBUILD_TESTS=OFF -DBUILD_TRAINING_TOOLS=OFF -DDISABLE_ARCHIVE=ON -DDISABLE_CURL=ON -DFAST_FLOAT=ON -DGRAPHICS_DISABLED=ON -DINSTALL_CONFIGS=OFF -DLeptonica_DIR=${leptonica_BINARY_DIR} -DCMAKE_INSTALL_PREFIX:PATH=${tesseract_BINARY_DIR}/install ${tesseract_SOURCE_DIR})
+ add_custom_target(tess_config ALL DEPENDS tess_config.out)
+ add_custom_command(OUTPUT tess_build.out COMMAND cmake --build ${tesseract_BINARY_DIR} --target install)
+ add_custom_target(tess_build ALL DEPENDS tess_build.out)
+ endif()
+
+ # fetch opencv
+ message("fetching https://github.com/opencv/opencv.git")
+ FetchContent_Declare(
+ opencv
+ GIT_REPOSITORY https://github.com/opencv/opencv.git
+ GIT_TAG 4.5.4
+ GIT_SHALLOW TRUE
+ GIT_PROGRESS TRUE
+ )
+
+ if(WIN64)
+ FetchContent_GetProperties(opencv)
+
+ set(BUILD_LIST core,highgui,videoio CACHE STRING "BUILD_LIST")
+ set(WITH_IPP OFF CACHE BOOL "WITH_IPP")
+ set(BUILD_EXAMPLES OFF CACHE BOOL "BUILD_EXAMPLES")
+ set(OPENCV_GENERATE_PKGCONFIG ON CACHE BOOL "OPENCV_GENERATE_PKGCONFIG")
+
+ FetchContent_MakeAvailable(opencv)
+
+ list(APPEND INCLUDES
+ ${CMAKE_BINARY_DIR}
+ ${opencv_SOURCE_DIR}/include
+ ${opencv_SOURCE_DIR}/modules/core/include
+ ${opencv_SOURCE_DIR}/modules/highgui/include
+ ${opencv_SOURCE_DIR}/modules/imgcodecs/include
+ ${opencv_SOURCE_DIR}/modules/imgproc/include
+ ${opencv_SOURCE_DIR}/modules/videoio/include
+ )
+ list(APPEND LIBS
+ ${opencv_BINARY_DIR}/lib/${CMAKE_BUILD_TYPE}/opencv_core454${DEBUG_LIB_EXTENTION}.lib
+ ${opencv_BINARY_DIR}/lib/${CMAKE_BUILD_TYPE}/opencv_highgui454${DEBUG_LIB_EXTENTION}.lib
+ ${opencv_BINARY_DIR}/lib/${CMAKE_BUILD_TYPE}/opencv_imgcodecs454${DEBUG_LIB_EXTENTION}.lib
+ ${opencv_BINARY_DIR}/lib/${CMAKE_BUILD_TYPE}/opencv_imgproc454${DEBUG_LIB_EXTENTION}.lib
+ ${opencv_BINARY_DIR}/lib/${CMAKE_BUILD_TYPE}/opencv_videoio454${DEBUG_LIB_EXTENTION}.lib
+ )
+ elseif(LINUX)
+ FetchContent_Populate(opencv)
+
+ list(APPEND INCLUDES
+ ${opencv_BINARY_DIR}/install/include/opencv4
+ )
+ list(APPEND LIBS
+ ${opencv_BINARY_DIR}/install/lib/libopencv_core.so
+ ${opencv_BINARY_DIR}/install/lib/libopencv_highgui.so
+ ${opencv_BINARY_DIR}/install/lib/libopencv_imgcodecs.so
+ ${opencv_BINARY_DIR}/install/lib/libopencv_imgproc.so
+ ${opencv_BINARY_DIR}/install/lib/libopencv_videoio.so
+ )
+
+ add_custom_command(OUTPUT opencv_config.out COMMAND cmake -B ${opencv_BINARY_DIR} -DBUILD_LIST=core,highgui,videoio -DBUILD_opencv_python3=OFF -DWITH_IPP=OFF -DBUILD_EXAMPLES=OFF -DOPENCV_GENERATE_PKGCONFIG=ON -DCMAKE_INSTALL_PREFIX:PATH=${opencv_BINARY_DIR}/install ${opencv_SOURCE_DIR})
+ add_custom_target(opencv_config ALL DEPENDS opencv_config.out)
+ add_custom_command(OUTPUT opencv_build.out COMMAND cmake --build ${opencv_BINARY_DIR} --target install)
+ add_custom_target(opencv_build ALL DEPENDS opencv_build.out)
+ endif()
+
+ # fetch rapidjson
+ message("fetching https://github.com/Tencent/rapidjson.git")
+ FetchContent_Declare(
+ rapidjson
+ GIT_REPOSITORY https://github.com/Tencent/rapidjson.git
+
+ GIT_SHALLOW TRUE
+ GIT_PROGRESS TRUE
+ )
+
+ FetchContent_Populate(rapidjson)
+
+ list(APPEND INCLUDES
+ ${rapidjson_SOURCE_DIR}/include
+ )
+
+ file(GLOB_RECURSE WOLF_ML_OCR_SRC
+ "${CMAKE_CURRENT_SOURCE_DIR}/ml/referee_ocr/*"
+ )
+
+ list(APPEND SRCS
+ ${WOLF_ML_OCR_SRC}
+ )
+endif()
+
+if(WOLF_ML_NUDITY_DETECTION)
+ # Set the C++ standard for the rest of the project
+ set(CMAKE_CXX_STANDARD 17)
+ set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+ # Find Torch package
+ find_package(Torch REQUIRED)
+
+ # Set the C++ standard for the rest of the project
+ set(CMAKE_CXX_STANDARD 23)
+ set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+ find_package(OpenCV REQUIRED)
+
+ list(APPEND INCLUDES
+ ${OpenCV_INCLUDE_DIRS}
+ )
+
+ list(APPEND LIBS
+ ${TORCH_LIBRARIES}
+ ${OpenCV_LIBS}
+ )
+
+ file(GLOB_RECURSE WOLF_ML_SHARED_SRC
+ "${CMAKE_CURRENT_SOURCE_DIR}/ml/w_common.*"
+ )
+
+ list(APPEND SRCS
+ ${WOLF_ML_SHARED_SRC}
+ )
+
+ file(GLOB_RECURSE WOLF_ML_NUD_DET_SRC
+ "${CMAKE_CURRENT_SOURCE_DIR}/ml/nudity_detection/*"
+ )
+
+ list(APPEND SRCS
+ ${WOLF_ML_NUD_DET_SRC}
+ )
+endif()
\ No newline at end of file
diff --git a/wolf/cmake/stream.cmake b/wolf/cmake/stream.cmake
new file mode 100644
index 000000000..894abce79
--- /dev/null
+++ b/wolf/cmake/stream.cmake
@@ -0,0 +1,254 @@
+# fetch gRPC
+if (WOLF_STREAM_GRPC)
+ if (EMSCRIPTEN)
+ message(FATAL_ERROR "the wasm32 target is not supported for WOLF_STREAM_GRPC")
+ endif()
+
+ vcpkg_install(asio-grpc asio-grpc TRUE)
+ list(APPEND LIBS asio-grpc::asio-grpc)
+
+ file(GLOB_RECURSE WOLF_STREAM_GRPC_SRC
+ "${CMAKE_CURRENT_SOURCE_DIR}/stream/grpc/*"
+ )
+
+ list(APPEND SRCS
+ ${WOLF_STREAM_GRPC_SRC}
+ )
+
+ if(WOLF_TEST)
+ add_library(generate-protos OBJECT)
+
+ target_link_libraries(generate-protos PUBLIC protobuf::libprotobuf gRPC::grpc++_unsecure)
+
+ set(PROTO_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/protos")
+ set(PROTO_IMPORT_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/protos")
+
+ asio_grpc_protobuf_generate(
+ GENERATE_GRPC GENERATE_MOCK_CODE
+ TARGET generate-protos
+ USAGE_REQUIREMENT PUBLIC
+ IMPORT_DIRS ${PROTO_IMPORT_DIRS}
+ OUT_DIR "${PROTO_BINARY_DIR}"
+ PROTOS "${CMAKE_CURRENT_SOURCE_DIR}/protos/raft.proto")
+
+ list(APPEND INCLUDES "${PROTO_BINARY_DIR}")
+ list(APPEND TESTS_SRCS
+ "${PROTO_BINARY_DIR}/raft.grpc.pb.cc"
+ "${PROTO_BINARY_DIR}/raft.pb.cc"
+ )
+ endif()
+
+endif()
+
+if (WOLF_STREAM_JANUS)
+
+ file(GLOB_RECURSE WOLF_STREAM_JANUS_SRC
+ "${CMAKE_CURRENT_SOURCE_DIR}/stream/janus/*"
+ )
+
+ list(APPEND SRCS
+ ${WOLF_STREAM_JANUS_SRC}
+ )
+
+endif()
+
+# fetch msquic
+if (WOLF_STREAM_QUIC)
+ if (EMSCRIPTEN)
+ message(FATAL_ERROR "WOLF_STREAM_QUIC is not supported for wasm32 target")
+ endif()
+
+ if (NOT WIN32)
+ message(FATAL_ERROR "WOLF_STREAM_QUIC feature is not avilable on non-windows yet.")
+ endif()
+
+ file(GLOB_RECURSE WOLF_STREAM_QUIC_SRCS
+ "${CMAKE_CURRENT_SOURCE_DIR}/stream/quic/*"
+ )
+
+ if (WIN32 OR WIN64)
+ FetchContent_Declare(
+ msquic
+ URL https://github.com/microsoft/msquic/releases/download/v2.2.0/msquic_windows_x64_Release_schannel.zip
+ DOWNLOAD_EXTRACT_TIMESTAMP TRUE
+ )
+ FetchContent_Populate(msquic)
+ else()
+ message(FATAL_ERROR "WOLF_STREAM_QUIC feature is not supported on target platform.")
+ endif()
+
+ add_library(msquic-lib INTERFACE)
+ add_library(msquic::msquic ALIAS msquic-lib)
+ target_include_directories(msquic-lib INTERFACE ${msquic_SOURCE_DIR}/include)
+ target_link_directories(msquic-lib INTERFACE BEFORE ${msquic_SOURCE_DIR}/bin)
+ target_link_directories(msquic-lib INTERFACE BEFORE ${msquic_SOURCE_DIR}/lib)
+ target_link_libraries(msquic-lib INTERFACE msquic)
+
+ list(APPEND SRCS ${WOLF_STREAM_QUIC_SRCS})
+ list(APPEND LIBS msquic::msquic)
+endif()
+
+if (WOLF_STREAM_RIST)
+ if (EMSCRIPTEN)
+ message(FATAL_ERROR "the wasm32 target is not supported for WOLF_STREAM_RIST")
+ endif()
+
+ set(RIST_TARGET "rist")
+ message("fetching https://code.videolan.org/rist/librist.git")
+ FetchContent_Declare(
+ ${RIST_TARGET}
+ GIT_REPOSITORY https://code.videolan.org/rist/librist.git
+ GIT_TAG master
+ )
+
+ set(FETCHCONTENT_QUIET OFF)
+ FetchContent_MakeAvailable(${RIST_TARGET})
+
+ if (ANDROID)
+ add_custom_command(OUTPUT rist_command.out COMMAND
+ /bin/bash "${CMAKE_CURRENT_SOURCE_DIR}/third_party/shells/librist/librist-android.sh" --build_dir=${rist_BINARY_DIR}
+ WORKING_DIRECTORY ${rist_SOURCE_DIR})
+ add_custom_target(rist ALL DEPENDS rist_command.out)
+
+ list(APPEND LIBS
+ ${rist_BINARY_DIR}/librist.a)
+ else ()
+ STRING(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LOWER)
+
+ add_custom_command(OUTPUT rist_command.out COMMAND cmd /c "meson setup ${rist_BINARY_DIR} --backend vs2022 --default-library static --buildtype ${CMAKE_BUILD_TYPE_LOWER} & meson compile -C ${rist_BINARY_DIR}" WORKING_DIRECTORY ${rist_SOURCE_DIR})
+ add_custom_target(rist ALL DEPENDS rist_command.out)
+
+ list(APPEND LIBS
+ ws2_32
+ ${rist_BINARY_DIR}/librist.a)
+
+ endif()
+
+ list(APPEND INCLUDES
+ ${rist_BINARY_DIR}
+ ${rist_BINARY_DIR}/include
+ ${rist_BINARY_DIR}/include/librist
+ ${rist_SOURCE_DIR}/contrib
+ ${rist_SOURCE_DIR}/contrib/mbedtls/include
+ ${rist_SOURCE_DIR}/include
+ ${rist_SOURCE_DIR}/include/librist
+ ${rist_SOURCE_DIR}/src
+ )
+
+ file(GLOB_RECURSE WOLF_STREAM_RIST_SRC
+ "${CMAKE_CURRENT_SOURCE_DIR}/stream/rist/*"
+ )
+ list(APPEND SRCS ${WOLF_STREAM_RIST_SRC})
+endif()
+
+if (WOLF_STREAM_WEBRTC)
+ if (EMSCRIPTEN)
+ message(FATAL_ERROR "the wasm32 target is not supported for WOLF_STREAM_WEBRTC")
+ endif()
+
+ # we need http & json for webrtc
+
+ if (NOT WOLF_SYSTEM_JSON)
+ message( FATAL_ERROR "'WOLF_STREAM_WEBRTC' needs 'WOLF_SYSTEM_JSON' = ON" )
+ endif()
+
+ if (NOT WOLF_STREAM_HTTP)
+ message( FATAL_ERROR "'WOLF_STREAM_WEBRTC' needs 'WOLF_STREAM_HTTP' = ON" )
+ endif()
+
+ list(APPEND INCLUDES
+ ${WEBRTC_SRC}
+ ${WEBRTC_SRC}/third_party/abseil-cpp
+ ${WEBRTC_SRC}/third_party/libyuv/include
+ )
+ if (WIN32)
+ # enable/disable debug option
+ if(CMAKE_BUILD_TYPE MATCHES Debug)
+ add_definitions(
+ -D_HAS_ITERATOR_DEBUGGING=1
+ -D_ITERATOR_DEBUG_LEVEL=2
+ )
+ else()
+ add_definitions(
+ -D_HAS_ITERATOR_DEBUGGING=0
+ -D_ITERATOR_DEBUG_LEVEL=0
+ )
+ endif()
+
+ add_definitions(
+ -DWEBRTC_WIN
+ -D__PRETTY_FUNCTION__=__FUNCTION__
+ #-DUSE_X11
+ #-D_WINSOCKAPI_
+ -DHAVE_SOUND)
+
+ list(APPEND LIBS
+ d3d11
+ dmoguids
+ dwmapi
+ dxgi
+ iphlpapi
+ msdmo
+ secur32
+ strmiids
+ winmm
+ wmcodecdspuuid
+ )
+ elseif (APPLE)
+
+ add_definitions(
+ -DHAVE_SOUND
+ -DWEBRTC_MAC
+ -DWEBRTC_POSIX
+ -fno-rtti)
+
+ find_library(APPLICATION_SERVICES ApplicationServices)
+ find_library(AUDIO_TOOLBOX AudioToolBox)
+ find_library(CORE_AUDIO CoreAudio)
+ find_library(CORE_FOUNDATION CoreFoundation)
+ find_library(CORE_SERVICES CoreServices)
+ find_library(FOUNDATION Foundation)
+
+ list(APPEND LIBS
+ ${APPLICATION_SERVICES}
+ ${AUDIO_TOOLBOX}
+ ${CORE_AUDIO}
+ ${CORE_FOUNDATION}
+ ${CORE_SERVICES}
+ ${FOUNDATION}
+ )
+ endif()
+ add_definitions(-DHAVE_JPEG)
+ link_directories(${WEBRTC_SRC}/out/${TARGET_OS}/${CMAKE_BUILD_TYPE}/obj/)
+ list(APPEND LIBS webrtc)
+
+ file(GLOB_RECURSE WOLF_STREAM_WEBRTC_CAPTURER_SRC
+ "${CMAKE_CURRENT_SOURCE_DIR}/stream/webrtc/capturer/*"
+ )
+ file(GLOB_RECURSE WOLF_STREAM_WEBRTC_DATA_SRC
+ "${CMAKE_CURRENT_SOURCE_DIR}/stream/webrtc/data/*"
+ )
+ file(GLOB_RECURSE WOLF_STREAM_WEBRTC_INTERCEPTOR_SRC
+ "${CMAKE_CURRENT_SOURCE_DIR}/stream/webrtc/interceptor/*"
+ )
+ file(GLOB_RECURSE WOLF_STREAM_WEBRTC_MEDIA_SRC
+ "${CMAKE_CURRENT_SOURCE_DIR}/stream/webrtc/media/*"
+ )
+ file(GLOB_RECURSE WOLF_STREAM_WEBRTC_PEER_SRC
+ "${CMAKE_CURRENT_SOURCE_DIR}/stream/webrtc/peer/*"
+ )
+
+ list(APPEND SRCS
+ ${WOLF_STREAM_WEBRTC_CAPTURER_SRC}
+ ${WOLF_STREAM_WEBRTC_DATA_SRC}
+ ${WOLF_STREAM_WEBRTC_INTERCEPTOR_SRC}
+ ${WOLF_STREAM_WEBRTC_MEDIA_SRC}
+ ${WOLF_STREAM_WEBRTC_PEER_SRC}
+ )
+endif()
+
+file(GLOB_RECURSE WOLF_STREAM_TEST_SRC
+ "${CMAKE_CURRENT_SOURCE_DIR}/stream/test/*"
+)
+list(APPEND SRCS ${WOLF_STREAM_TEST_SRC})
+
diff --git a/wolf/cmake/system.cmake b/wolf/cmake/system.cmake
new file mode 100644
index 000000000..521525cbb
--- /dev/null
+++ b/wolf/cmake/system.cmake
@@ -0,0 +1,306 @@
+if (WOLF_SYSTEM_STACKTRACE)
+ if (EMSCRIPTEN)
+ message(FATAL_ERROR "the wasm32 target is not supported for WOLF_SYSTEM_STACKTRACE")
+ elseif(NOT MSVC)
+ message(FATAL_ERROR "WOLF_SYSTEM_STACKTRACE is only supported on Visual C++")
+ endif()
+endif()
+
+
+# fetch mimalloc
+if (WOLF_SYSTEM_MIMALLOC)
+ if (EMSCRIPTEN)
+ message(FATAL_ERROR "the wasm32 target is not supported for WOLF_SYSTEM_MIMALLOC")
+ endif()
+ vcpkg_install(mimalloc mimalloc TRUE)
+ list(APPEND LIBS mimalloc)
+endif()
+
+# fetch boost components via vcpkg
+if (EMSCRIPTEN)
+ execute_process(COMMAND vcpkg install
+ boost-leaf
+ boost-signals2 --triplet=${VCPKG_TARGET_TRIPLET})
+elseif(WOLF_SYSTEM_PYTHON)
+ execute_process(COMMAND vcpkg install
+ boost-asio
+ boost-beast
+ boost-leaf
+ boost-python
+ boost-signals2
+ boost-test --triplet=${VCPKG_TARGET_TRIPLET})
+else()
+ execute_process(COMMAND vcpkg install
+ boost-asio
+ boost-beast
+ boost-leaf
+ boost-signals2
+ boost-test --triplet=${VCPKG_TARGET_TRIPLET})
+endif()
+
+set(Boost_INCLUDE_DIR $ENV{VCPKG_ROOT}/installed/${VCPKG_TARGET_TRIPLET}/include CACHE STRING "boost include directory" FORCE)
+list(APPEND INCLUDES ${Boost_INCLUDE_DIR})
+find_package(Boost ${Boost_VERSION} REQUIRED)
+
+# install gsl
+vcpkg_install(Microsoft.GSL ms-gsl TRUE)
+list(APPEND LIBS Microsoft.GSL::GSL)
+
+if (NOT CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" AND NOT EMSCRIPTEN)
+ vcpkg_install(fmt fmt FALSE)
+ list(APPEND LIBS fmt::fmt-header-only)
+endif()
+
+if (WOLF_SYSTEM_GAMEPAD_CLIENT)
+ file(GLOB_RECURSE WOLF_SYSTEM_GAMEPAD_CLIENT_SRC
+ "${CMAKE_CURRENT_SOURCE_DIR}/system/gamepad/w_gamepad_client_emc.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/system/gamepad/w_gamepad_client_keymap.hpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/system/gamepad/w_gamepad_client_sdl.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/system/gamepad/w_gamepad_client_types.hpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/system/gamepad/w_gamepad_client.hpp"
+ )
+ list(APPEND SRCS ${WOLF_SYSTEM_GAMEPAD_CLIENT_SRC})
+
+ if (NOT EMSCRIPTEN)
+ message("fetching https://github.com/libsdl-org/SDL")
+ FetchContent_Declare(
+ SDL3-static
+ GIT_REPOSITORY https://github.com/libsdl-org/SDL
+ GIT_TAG main
+ )
+
+ set(SDL_AUDIO OFF CACHE BOOL "SDL_AUDIO")
+ set(SDL_DIRECTX OFF CACHE BOOL "SDL_DIRECTX")
+ set(SDL_DISKAUDIO OFF CACHE BOOL "SDL_DISKAUDIO")
+ set(SDL_DUMMYAUDIO OFF CACHE BOOL "SDL_DUMMYAUDIO")
+ set(SDL_DUMMYVIDEO OFF CACHE BOOL "SDL_DUMMYVIDEO")
+ set(SDL_FILE OFF CACHE BOOL "SDL_FILE")
+ set(SDL_FILESYSTEM OFF CACHE BOOL "SDL_FILESYSTEM")
+ set(SDL_METAL OFF CACHE BOOL "SDL_METAL")
+ set(SDL_OFFSCREEN OFF CACHE BOOL "SDL_OFFSCREEN")
+ set(SDL_OPENGL OFF CACHE BOOL "SDL_OPENGL")
+ set(SDL_OPENGLES OFF CACHE BOOL "SDL_OPENGLES")
+ set(SDL_RENDER OFF CACHE BOOL "SDL_RENDER")
+ set(SDL_RENDER_D3D OFF CACHE BOOL "SDL_RENDER_D3D")
+ set(SDL_RENDER_METAL OFF CACHE BOOL "SDL_RENDER_METAL")
+ set(SDL_SHARED OFF CACHE BOOL "SDL_SHARED")
+ set(SDL_TEST OFF CACHE BOOL "SDL_TEST")
+ set(SDL_TESTS OFF CACHE BOOL "SDL_TESTS")
+ set(SDL_VIDEO OFF CACHE BOOL "SDL_VIDEO")
+ set(SDL_VULKAN OFF CACHE BOOL "SDL_VULKAN")
+ set(SDL_WASAPI OFF CACHE BOOL "SDL_WASAPI")
+
+ set(SDL_HIGHDAPI_JOYSTICK ON CACHE BOOL "SDL_HIGHDAPI_JOYSTICK")
+ set(SDL_JOYSTICK ON CACHE BOOL "SDL_JOYSTICK")
+ set(SDL_STATIC ON CACHE BOOL "SDL_STATIC")
+ set(SDL_XINPUT ON CACHE BOOL "SDL_XINPUT")
+
+ set(FETCHCONTENT_QUIET OFF)
+ FetchContent_MakeAvailable(SDL3-static)
+
+ list(APPEND INCLUDES
+ ${SDL3-static_SOURCE_DIR}/include
+ )
+ list(APPEND LIBS SDL3-static)
+
+ set_target_properties(
+ SDL3-static
+ uninstall
+ PROPERTIES FOLDER "SDL")
+ endif()
+endif()
+
+if (WOLF_SYSTEM_GAMEPAD_VIRTUAL)
+ if (NOT WIN32)
+ message(SEND_ERROR "WOLF_SYSTEM_GAMEPAD_VIRTUAL can only build for Windows")
+ else()
+ message("fetching https://github.com/ViGEm/ViGEmClient.git")
+ FetchContent_Declare(
+ ViGEmClient
+ GIT_REPOSITORY https://github.com/ViGEm/ViGEmClient.git
+ GIT_TAG master
+ )
+ FetchContent_MakeAvailable(ViGEmClient)
+
+ file(GLOB_RECURSE WOLF_SYSTEM_GAMEPAD_VIRTUAL_SRCS
+ "${CMAKE_CURRENT_SOURCE_DIR}/system/gamepad/w_gamepad_virtual_pool.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/system/gamepad/w_gamepad_virtual_pool.hpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/system/gamepad/w_gamepad_virtual.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/system/gamepad/w_gamepad_virtual.hpp"
+ )
+ list(APPEND SRCS ${WOLF_SYSTEM_GAMEPAD_VIRTUAL_SRCS})
+ list(APPEND INCLUDES ${ViGEmClient_SOURCE_DIR}/include)
+ list(APPEND LIBS
+ ViGEmClient
+ Xinput.lib
+ SetupAPI.lib)
+ endif()
+endif()
+
+if (WOLF_SYSTEM_LOG)
+ if (EMSCRIPTEN)
+ message(FATAL_ERROR "the wasm32 target is not supported for WOLF_SYSTEM_LOG")
+ endif()
+
+ vcpkg_install(spdlog spdlog TRUE)
+ list(APPEND LIBS spdlog::spdlog spdlog::spdlog_header_only)
+
+ file(GLOB_RECURSE WOLF_SYSTEM_LOG_SRC
+ "${CMAKE_CURRENT_SOURCE_DIR}/system/log/*"
+ )
+ list(APPEND SRCS ${WOLF_SYSTEM_LOG_SRC})
+endif()
+
+if (WOLF_SYSTEM_LZ4)
+ if (EMSCRIPTEN)
+ message(FATAL_ERROR "the wasm32 target is not supported for WOLF_SYSTEM_LZ4")
+ endif()
+ vcpkg_install(lz4 lz4 TRUE)
+ list(APPEND LIBS lz4::lz4)
+
+ file(GLOB_RECURSE WOLF_SYSTEM_LZ4_SRCS
+ "${CMAKE_CURRENT_SOURCE_DIR}/system/compression/w_lz4.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/system/compression/w_lz4.hpp"
+ )
+ list(APPEND SRCS ${WOLF_SYSTEM_LZ4_SRCS})
+endif()
+
+if (WOLF_SYSTEM_LZMA)
+ if (EMSCRIPTEN)
+ message(FATAL_ERROR "the wasm32 target is not supported for WOLF_SYSTEM_LZMA")
+ endif()
+ message("fetching https://github.com/WolfEngine/lzma.git")
+ FetchContent_Declare(
+ lzma
+ GIT_REPOSITORY https://github.com/WolfEngine/lzma.git
+ GIT_TAG main
+ )
+ set(FETCHCONTENT_QUIET OFF)
+ FetchContent_MakeAvailable(lzma)
+
+ file(GLOB_RECURSE WOLF_SYSTEM_LZMA_SRCS
+ "${CMAKE_CURRENT_SOURCE_DIR}/system/compression/w_lzma.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/system/compression/w_lzma.hpp"
+ )
+ list(APPEND SRCS ${WOLF_SYSTEM_LZMA_SRCS})
+ list(APPEND INCLUDES ${lzma_SOURCE_DIR}/src)
+ list(APPEND LIBS lzma)
+endif()
+
+# include openSSL
+if (WOLF_SYSTEM_OPENSSL AND NOT EMSCRIPTEN)
+ vcpkg_install(OpenSSL openssl FALSE)
+ list(APPEND LIBS OpenSSL::SSL OpenSSL::Crypto)
+endif()
+
+# include socket/websocket sources
+if (WOLF_SYSTEM_SOCKET AND NOT EMSCRIPTEN)
+ file(GLOB_RECURSE WOLF_SYSTEM_SOCKET_SRC
+ "${CMAKE_CURRENT_SOURCE_DIR}/system/socket/w_socket_options.hpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/system/socket/w_tcp_client.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/system/socket/w_tcp_client.hpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/system/socket/w_tcp_server.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/system/socket/w_tcp_server.hpp"
+ )
+ list(APPEND SRCS ${WOLF_SYSTEM_SOCKET_SRC})
+endif()
+
+if (WOLF_SYSTEM_HTTP_WS)
+ if (EMSCRIPTEN)
+ file(GLOB_RECURSE WOLF_SYSTEM_HTTP_WS_SRC
+ "${CMAKE_CURRENT_SOURCE_DIR}/system/socket/w_ws_client_emc.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/system/socket/w_ws_client_emc.hpp"
+ )
+ else()
+ file(GLOB_RECURSE WOLF_SYSTEM_HTTP_WS_SRC
+ "${CMAKE_CURRENT_SOURCE_DIR}/system/socket/w_ws_client.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/system/socket/w_ws_client.hpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/system/socket/w_ws_server.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/system/socket/w_ws_server.hpp"
+ )
+ endif()
+ list(APPEND SRCS ${WOLF_SYSTEM_HTTP_WS_SRC})
+endif()
+
+if (WOLF_SYSTEM_ZLIB)
+ vcpkg_install(ZLIB zlib FALSE)
+ list(APPEND LIBS ZLIB::ZLIB)
+endif()
+
+if (WOLF_SYSTEM_POSTGRESQL)
+ vcpkg_install(libpq libpq TRUE)
+ list(APPEND LIBS libpq::libpq)
+
+ file(GLOB_RECURSE WOLF_SYSTEM_POSTGRESQL_SRC
+ "${CMAKE_CURRENT_SOURCE_DIR}/system/db/w_postgresql.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/system/db/w_postgresql.hpp"
+ )
+ list(APPEND LIBS PostgreSQL::PostgreSQL)
+endif()
+
+if (WOLF_SYSTEM_LUA)
+ vcpkg_install(Lua lua FALSE)
+ vcpkg_install(sol2 sol2 TRUE)
+
+ list(APPEND LIBS lua sol2)
+
+ file(GLOB_RECURSE WOLF_SYSTEM_LUA_SRC
+ "${CMAKE_CURRENT_SOURCE_DIR}/system/script/w_lua.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/system/script/w_lua.hpp"
+ )
+
+ list(APPEND SRCS
+ ${WOLF_SYSTEM_LUA_SRC}
+ )
+endif()
+
+if (WOLF_SYSTEM_PYTHON)
+ find_package(Python3 REQUIRED COMPONENTS Development)
+ find_package(Boost REQUIRED COMPONENTS python${Python3_VERSION_MAJOR}${Python3_VERSION_MINOR})
+
+ file(GLOB_RECURSE WOLF_SYSTEM_PYTHON_SRC
+ "${CMAKE_CURRENT_SOURCE_DIR}/system/script/w_python.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/system/script/w_python.hpp"
+ )
+ list(APPEND SRCS
+ ${WOLF_SYSTEM_PYTHON_SRC}
+ )
+ list(APPEND INCLUDES ${Python3_INCLUDE_DIRS})
+ list(APPEND LIBS Python3::Python Boost::python${Python3_VERSION_MAJOR}${Python3_VERSION_MINOR})
+
+ get_filename_component(PYTHON_HOME ${Python3_EXECUTABLE} DIRECTORY)
+ add_definitions(
+ -DBOOST_PYTHON_STATIC_LIB
+ -DPYTHON_HOME="${PYTHON_HOME}"
+ )
+endif()
+
+if (EMSCRIPTEN)
+ file (GLOB_RECURSE WOLF_SYSTEM_SRC
+ "${CMAKE_CURRENT_SOURCE_DIR}/system/w_gametime.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/system/w_gametime.hpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/system/w_trace.hpp"
+ )
+else()
+ file (GLOB_RECURSE WOLF_SYSTEM_SRC
+ "${CMAKE_CURRENT_SOURCE_DIR}/system/getopt.h"
+ "${CMAKE_CURRENT_SOURCE_DIR}/system/w_gametime.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/system/w_gametime.hpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/system/w_leak_detector.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/system/w_leak_detector.hpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/system/w_process.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/system/w_process.hpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/system/w_time.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/system/w_time.hpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/system/w_trace.hpp"
+ )
+endif()
+
+file(GLOB_RECURSE WOLF_SYSTEM_TEST_SRC
+ "${CMAKE_CURRENT_SOURCE_DIR}/system/test/*"
+)
+
+list(APPEND SRCS
+ ${WOLF_SYSTEM_SRC}
+ ${WOLF_SYSTEM_TEST_SRC}
+)
diff --git a/wolf/cmake/vcpkg.cmake b/wolf/cmake/vcpkg.cmake
new file mode 100644
index 000000000..4e0e3b4a1
--- /dev/null
+++ b/wolf/cmake/vcpkg.cmake
@@ -0,0 +1,62 @@
+if(NOT DEFINED ENV{VCPKG_ROOT})
+ message(FATAL_ERROR "VCPKG_ROOT environment variable is not set.")
+endif()
+
+if(QT_IS_ANDROID_MULTI_ABI_EXTERNAL_PROJECT)
+ if(CMAKE_TOOLCHAIN_FILE MATCHES "android_x86_64")
+ set(vcpkg_triplet "x64-android")
+ elseif(CMAKE_TOOLCHAIN_FILE MATCHES "android_x86")
+ set(vcpkg_triplet "x86-android")
+ elseif(CMAKE_TOOLCHAIN_FILE MATCHES "android_arm64_v8a")
+ set(vcpkg_triplet "arm64-android")
+ elseif(CMAKE_TOOLCHAIN_FILE MATCHES "android_armv7")
+ set(vcpkg_triplet "arm-neon-android")
+ endif()
+elseif(ANDROID_ABI)
+ if(ANDROID_ABI STREQUAL "x86_64")
+ set(vcpkg_triplet "x64-android")
+ elseif(ANDROID_ABI STREQUAL "x86")
+ set(vcpkg_triplet "x86-android")
+ elseif(ANDROID_ABI STREQUAL "arm64-v8a")
+ set(vcpkg_triplet "arm64-android")
+ elseif(ANDROID_ABI STREQUAL "armeabi-v7a")
+ set(vcpkg_triplet "arm-neon-android")
+ endif()
+elseif(EMSCRIPTEN)
+ set(vcpkg_triplet "wasm32-emscripten")
+else() # desktop
+ if(WIN32)
+ set(vcpkg_triplet "x64-windows")
+ elseif(APPLE)
+ set(vcpkg_triplet "x64-osx")
+ elseif(UNIX)
+ set(vcpkg_triplet "x64-linux")
+ endif()
+endif()
+
+set(CMAKE_TOOLCHAIN_FILE "$ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake")
+if (LIBRARY_TYPE STREQUAL "STATIC" AND NOT EMSCRIPTEN)
+ set(VCPKG_TARGET_TRIPLET ${vcpkg_triplet}-static CACHE STRING "vcpkg target triplet" FORCE)
+else()
+ set(VCPKG_TARGET_TRIPLET ${vcpkg_triplet} CACHE STRING "vcpkg target triplet" FORCE)
+endif()
+
+include("$ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake")
+
+function(vcpkg_install PACKAGE PACKAGE_NAME WITH_CONFIG)
+ message(STATUS "finding ${PACKAGE}")
+ if (WITH_CONFIG)
+ find_package(${PACKAGE} CONFIG)
+ else()
+ find_package(${PACKAGE})
+ endif()
+ if(NOT ${PACKAGE}_FOUND)
+ message(STATUS "installing ${PACKAGE} via vcpkg")
+ execute_process(COMMAND vcpkg install ${PACKAGE_NAME} --triplet=${VCPKG_TARGET_TRIPLET})
+ if (WITH_CONFIG)
+ find_package(${PACKAGE} CONFIG REQUIRED)
+ else()
+ find_package(${PACKAGE} REQUIRED)
+ endif()
+ endif()
+endfunction()
diff --git a/wolf/media/ffmpeg/w_av_config.cpp b/wolf/media/ffmpeg/w_av_config.cpp
new file mode 100644
index 000000000..9f972cebe
--- /dev/null
+++ b/wolf/media/ffmpeg/w_av_config.cpp
@@ -0,0 +1,15 @@
+#include "w_av_config.hpp"
+
+using w_av_config = wolf::media::ffmpeg::w_av_config;
+
+w_av_config::w_av_config(_In_ AVPixelFormat p_format, _In_ int p_width, _In_ int p_height,
+ _In_ int p_alignment) noexcept
+ : format(p_format), width(p_width), height(p_height), alignment(p_alignment) {}
+
+w_av_config::w_av_config(_In_ int p_nb_channels, _In_ AVSampleFormat p_sample_fmts,
+ _In_ int p_sample_rate) noexcept
+ : sample_rate(p_sample_rate), sample_fmts(p_sample_fmts), nb_channels(p_nb_channels) {}
+
+int w_av_config::get_required_video_buffer_size() const noexcept {
+ return av_image_get_buffer_size(this->format, this->width, this->height, this->alignment);
+}
diff --git a/wolf/media/ffmpeg/w_av_config.hpp b/wolf/media/ffmpeg/w_av_config.hpp
new file mode 100644
index 000000000..177acff65
--- /dev/null
+++ b/wolf/media/ffmpeg/w_av_config.hpp
@@ -0,0 +1,68 @@
+/*
+ Project: Wolf Engine. Copyright 2014-2023 Pooya Eimandar
+ https://github.com/WolfEngine/WolfEngine
+*/
+
+#ifdef WOLF_MEDIA_FFMPEG
+
+#pragma once
+
+#include
+
+extern "C" {
+#include
+#include
+}
+
+namespace wolf::media::ffmpeg {
+class w_av_config {
+ public:
+#pragma region Constructors /Destructor
+ // default constructor
+ W_API w_av_config() noexcept = default;
+
+ // constructor for av video format
+ W_API w_av_config(_In_ AVPixelFormat p_format, _In_ int p_width, _In_ int p_height,
+ _In_ int p_alignment = 1) noexcept;
+
+ // constructor for av audio format
+ W_API w_av_config(_In_ int p_nb_channels, _In_ AVSampleFormat p_sample_fmts,
+ _In_ int p_sample_rate) noexcept;
+
+ // destructor
+ W_API virtual ~w_av_config() noexcept = default;
+
+ // move constructor.
+ W_API w_av_config(w_av_config &&p_other) noexcept = default;
+ // move assignment operator.
+ W_API w_av_config &operator=(w_av_config &&p_other) noexcept = default;
+
+ // copy constructor
+ w_av_config(const w_av_config &p_other) noexcept = default;
+ // copy assignment operator
+ w_av_config &operator=(const w_av_config &p_other) noexcept = default;
+#pragma endregion
+
+ /**
+ * @returns the required buffer size for video frame
+ */
+ W_API int get_required_video_buffer_size() const noexcept;
+
+ // the format of av frame
+ AVPixelFormat format = AVPixelFormat::AV_PIX_FMT_NONE;
+ // the width of av frame
+ int width = 0;
+ // the height of av frame
+ int height = 0;
+ // data alignment
+ int alignment = 0;
+ // the sample rate of the audio
+ int sample_rate = 0;
+ // the sample format of the audio
+ AVSampleFormat sample_fmts = AVSampleFormat::AV_SAMPLE_FMT_NONE;
+ // number of channels
+ int nb_channels = 0;
+};
+} // namespace wolf::media::ffmpeg
+
+#endif // WOLF_MEDIA_FFMPEG
\ No newline at end of file
diff --git a/wolf/media/ffmpeg/w_av_format.cpp b/wolf/media/ffmpeg/w_av_format.cpp
new file mode 100644
index 000000000..17e37e89d
--- /dev/null
+++ b/wolf/media/ffmpeg/w_av_format.cpp
@@ -0,0 +1,117 @@
+#ifdef WOLF_MEDIA_FFMPEG
+
+#include "w_av_format.hpp"
+#include "w_av_frame.hpp"
+
+using w_av_format = wolf::media::ffmpeg::w_av_format;
+
+w_av_format::w_av_format() noexcept
+ : _stream_buffer(nullptr), _fmt_ctx(nullptr), _io_ctx(nullptr) {}
+
+void w_av_format::_release() noexcept {
+ if (this->_stream_buffer != nullptr) {
+ auto _ptr = this->_stream_buffer.get();
+ free(_ptr);
+ this->_stream_buffer = nullptr;
+ }
+ if (this->_fmt_ctx != nullptr) {
+ auto _ptr = this->_fmt_ctx.get();
+ avformat_close_input(&_ptr);
+ this->_stream_buffer = nullptr;
+ }
+ if (this->_io_ctx != nullptr) {
+ auto _ptr = this->_io_ctx.get();
+ av_free(_ptr);
+ this->_stream_buffer = nullptr;
+ }
+}
+
+static int s_read_packet(void *p_opaque, _Inout_ uint8_t *p_buf, _In_ int p_buf_size) {
+ auto _av_fmt = gsl::narrow_cast(p_opaque);
+ if (_av_fmt) {
+ if (_av_fmt->on_read_callback) {
+ return _av_fmt->on_read_callback(p_buf, p_buf_size);
+ }
+ }
+ return -1; // failed
+}
+
+boost::leaf::result w_av_format::init(_In_ int p_stream_buf_size) noexcept {
+ _release();
+
+ // alloc a buffer for the stream
+ auto _ptr = gsl::narrow_cast(malloc(p_stream_buf_size));
+ if (_ptr == nullptr) {
+ // out of memory
+ return W_FAILURE(std::errc::not_enough_memory, "could not allocate memory for stream buffer");
+ }
+ this->_stream_buffer.reset(_ptr);
+
+ // get a AVContext stream
+ auto _io_ctx_ptr =
+ avio_alloc_context(this->_stream_buffer.get(), // buffer
+ p_stream_buf_size, // buffer size
+ 0, // buffer is only readable - set to 1 for read/write
+ this, // use your specified data
+ s_read_packet, // function - reading Packets (see example)
+ nullptr, // function - write Packets
+ nullptr // function - seek to position in stream (see example)
+ );
+ if (_io_ctx_ptr == nullptr) {
+ // out of memory
+ return W_FAILURE(std::errc::not_enough_memory, "could not allocate io context");
+ }
+ this->_io_ctx.reset(_io_ctx_ptr);
+
+ // allocate a AVContext
+ auto _fmt_ctx_ptr = avformat_alloc_context();
+ if (_fmt_ctx_ptr == nullptr) {
+ // out of memory
+ return W_FAILURE(std::errc::not_enough_memory, "could not allocate avformat context");
+ }
+ this->_fmt_ctx.reset(_fmt_ctx_ptr);
+
+ // Set up the format context based on custom IO
+ this->_fmt_ctx->pb = _io_ctx_ptr;
+ this->_fmt_ctx->flags |= AVFMT_FLAG_CUSTOM_IO;
+
+ // open "file" (open our custom IO)
+ // empty string is where filename would go. doesn't matter since we aren't
+ // reading a file NULL params are format and demuxer settings, respectively
+ if (avformat_open_input(&_fmt_ctx_ptr, "", nullptr, nullptr) < 0) {
+ // error on opening input
+ return W_FAILURE(std::errc::not_enough_memory, "could not allocate io context");
+ }
+
+ // find the stream info
+ if (avformat_find_stream_info(_fmt_ctx_ptr, nullptr) < 0) {
+ // Error on finding stream info
+ return W_FAILURE(std::errc::operation_canceled, "could not get stream info");
+ }
+
+ // find best stream
+ const auto _ret =
+ av_find_best_stream(_fmt_ctx_ptr, AVMediaType::AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0);
+ if (_ret < 0) {
+ // Error on finding stream info
+ return W_FAILURE(std::errc::operation_canceled, "could not find best stream");
+ }
+
+ return 0;
+}
+
+uint8_t *w_av_format::get_io_ctx_buffer() const {
+ if (this->_io_ctx) {
+ return this->_io_ctx->buffer;
+ }
+ return nullptr;
+}
+
+int w_av_format::get_io_ctx_size() const {
+ if (this->_io_ctx) {
+ return this->_io_ctx->buffer_size;
+ }
+ return -1;
+}
+
+#endif // WOLF_MEDIA_FFMPEG
\ No newline at end of file
diff --git a/wolf/media/ffmpeg/w_av_format.hpp b/wolf/media/ffmpeg/w_av_format.hpp
new file mode 100644
index 000000000..b41a0a27e
--- /dev/null
+++ b/wolf/media/ffmpeg/w_av_format.hpp
@@ -0,0 +1,50 @@
+/*
+ Project: Wolf Engine. Copyright © 2014-2023 Pooya Eimandar
+ https://github.com/WolfEngine/WolfEngine
+*/
+
+#ifdef WOLF_MEDIA_FFMPEG
+
+#pragma once
+
+#include
+
+extern "C" {
+#include
+}
+
+namespace wolf::media::ffmpeg {
+class w_av_format {
+ public:
+#pragma region Constructors /Destructor
+ W_API w_av_format() noexcept;
+ W_API ~w_av_format() noexcept { _release(); };
+ // move constructor.
+ W_API w_av_format(w_av_format &&p_other) noexcept = default;
+ // move assignment operator.
+ W_API w_av_format &operator=(w_av_format &&p_other) noexcept = default;
+#pragma endregion
+
+ boost::leaf::result init(_In_ int p_stream_buf_size = 32'767) noexcept;
+
+ uint8_t *get_io_ctx_buffer() const;
+ int get_io_ctx_size() const;
+
+ std::function on_read_callback;
+
+ private:
+ // copy constructor.
+ w_av_format(const w_av_format &) = delete;
+ // copy assignment operator.
+ w_av_format &operator=(const w_av_format &) = delete;
+
+ // release
+ void _release() noexcept;
+
+ std::unique_ptr _stream_buffer = nullptr;
+ std::unique_ptr _fmt_ctx = nullptr;
+ std::unique_ptr _io_ctx = nullptr;
+};
+} // namespace wolf::media::ffmpeg
+
+#endif // WOLF_MEDIA_FFMPEG
diff --git a/wolf/media/ffmpeg/w_av_frame.cpp b/wolf/media/ffmpeg/w_av_frame.cpp
new file mode 100644
index 000000000..03d681d87
--- /dev/null
+++ b/wolf/media/ffmpeg/w_av_frame.cpp
@@ -0,0 +1,300 @@
+#ifdef WOLF_MEDIA_FFMPEG
+
+#include "w_av_frame.hpp"
+#include "w_ffmpeg_ctx.hpp"
+
+extern "C" {
+#include
+#include
+#include
+}
+
+#ifdef WOLF_MEDIA_STB
+#include
+#include
+#endif //WOLF_MEDIA_STB
+
+using w_av_frame = wolf::media::ffmpeg::w_av_frame;
+using w_av_config = wolf::media::ffmpeg::w_av_config;
+
+w_av_frame::w_av_frame(_In_ w_av_config &&p_config) noexcept : _config(std::move(p_config)) {}
+
+void w_av_frame::_release() noexcept {
+ if (this->_av_frame != nullptr) {
+ av_frame_free(&this->_av_frame);
+ if (this->_config.nb_channels > 0) {
+ av_channel_layout_uninit(&this->_av_frame->ch_layout);
+ }
+ }
+}
+
+void w_av_frame::_move(w_av_frame &&p_other) noexcept {
+ if (this == &p_other) {
+ return;
+ }
+ this->_av_frame = std::exchange(p_other._av_frame, nullptr);
+ this->_config = std::move(p_other._config);
+ this->_data = std::move(p_other._data);
+}
+
+boost::leaf::result w_av_frame::init() noexcept {
+ _release();
+
+ // allocate memory for AVFrame
+ this->_av_frame = av_frame_alloc();
+ if (this->_av_frame == nullptr) {
+ return W_FAILURE(std::errc::not_enough_memory, "could not allocate memory for AVFrame");
+ }
+
+ // set audio
+ this->_av_frame->sample_rate = this->_config.sample_rate;
+ if (this->_config.nb_channels > 0) {
+ av_channel_layout_default(&this->_av_frame->ch_layout, this->_config.nb_channels);
+ }
+
+ // set video
+ this->_av_frame->format = gsl::narrow_cast(this->_config.format);
+ this->_av_frame->width = this->_config.width;
+ this->_av_frame->height = this->_config.height;
+
+ return 0;
+}
+
+boost::leaf::result w_av_frame::set_video_frame(
+ _Inout_ std::vector &&p_data) noexcept {
+ const auto _width = this->_config.width;
+ const auto _height = this->_config.height;
+ const auto _alignment = this->_config.alignment;
+
+ // check for width and height
+ if (_width <= 0 || _height <= 0) {
+ return W_FAILURE(std::errc::invalid_argument, "width or height of w_av_frame is zero");
+ }
+
+ // move the owenership of data to buffer
+ this->_data = std::forward &&>(p_data);
+
+ const auto _buffer_size = this->_config.get_required_video_buffer_size();
+ if (this->_data.empty()) {
+ this->_data.resize(_buffer_size, 0);
+ }
+
+ // if size does not fit
+ if (this->_data.size() != _buffer_size) {
+ return W_FAILURE(std::errc::invalid_argument,
+ wolf::format("w_av_frame video buffer size is expected {} but is {}",
+ _buffer_size, this->_data.size()));
+ }
+
+ const auto _ret = av_image_fill_arrays(this->_av_frame->data, this->_av_frame->linesize,
+ gsl::narrow_cast(this->_data.data()),
+ this->_config.format, this->_av_frame->width,
+ this->_av_frame->height, _alignment);
+ if (_ret < 0) {
+ return W_FAILURE(std::errc::operation_canceled, "av_image_fill_arrays failed");
+ }
+ return _ret;
+}
+
+void w_av_frame::set_pts(_In_ int64_t p_pts) noexcept { this->_av_frame->pts = p_pts; }
+
+std::tuple w_av_frame::get() const noexcept {
+ if (this->_av_frame) {
+ auto _buffer_size =
+ av_image_get_buffer_size(gsl::narrow_cast(this->_av_frame->format),
+ this->_av_frame->width, this->_av_frame->height, 4);
+
+ return std::make_tuple(this->_av_frame->data, _buffer_size);
+ }
+ return std::make_tuple(nullptr, 0);
+}
+
+w_av_config w_av_frame::get_config() const noexcept { return this->_config; }
+
+boost::leaf::result w_av_frame::convert_audio(_In_ w_av_config &&p_dst_config) {
+ auto _ret = 0;
+
+ SwrContext *swr = nullptr;
+ auto _dst_frame = w_av_frame(std::move(p_dst_config));
+ _dst_frame.init();
+
+ DEFER {
+ if (_ret != S_OK) {
+ if (swr) {
+ if (swr_is_initialized(swr)) {
+ swr_close(swr);
+ }
+ swr_free(&swr);
+ }
+ }
+ });
+
+ swr_alloc_set_opts2(&swr, &this->_channel_layout, p_dst_config.sample_fmts,
+ p_dst_config.sample_rate, &this->_av_frame->ch_layout,
+ gsl::narrow_cast(this->_av_frame->format),
+ this->_av_frame->sample_rate, 0, nullptr);
+
+ if (swr == nullptr) {
+ _ret = -1;
+ return W_FAILURE(std::errc::operation_canceled, "could not create audio SwrContext");
+ }
+
+ _ret = swr_init(swr);
+
+ if (_ret < 0) {
+ return W_FAILURE(std::errc::operation_canceled, "could not initialize audio SwrContext");
+ }
+
+ // get number of samples
+ auto _sample_rate = gsl::narrow_cast(this->_av_frame->sample_rate);
+
+ auto _delay = swr_get_delay(swr, _sample_rate);
+
+ const auto _rescale_rnd =
+ av_rescale_rnd(_delay + _sample_rate, gsl::narrow_cast(p_dst_config.sample_rate),
+ _sample_rate, AV_ROUND_UP);
+
+ _dst_frame._av_frame->nb_samples = gsl::narrow_cast(_rescale_rnd);
+
+ auto size = av_samples_alloc(gsl::narrow_cast(&_dst_frame._av_frame->data[0]),
+ &_dst_frame._av_frame->linesize[0],
+ this->_channel_layout.nb_channels, _dst_frame._config.nb_channels,
+ gsl::narrow_cast(p_dst_config.sample_fmts), 1);
+
+ if (size < 0) {
+ _ret = -1;
+ return W_FAILURE(std::errc::operation_canceled,
+ "could not allocate memory for buffer of audio");
+ }
+
+ /* convert to destination format */
+ size = swr_convert(swr, gsl::narrow_cast(&_dst_frame._av_frame->data[0]),
+ _dst_frame._av_frame->nb_samples,
+ (const uint8_t **)(&this->_av_frame->data[0]), this->_av_frame->nb_samples);
+
+ if (size < 0) {
+ _ret = -1;
+ return W_FAILURE(std::errc::operation_canceled, "error while audio converting\n");
+ }
+
+ const auto _buffer_size = av_samples_get_buffer_size(&_dst_frame._av_frame->linesize[0],
+ _dst_frame._av_frame->ch_layout.nb_channels,
+ size, p_dst_config.sample_fmts, 1);
+
+ if (_buffer_size < 0) {
+ _ret = -1;
+ return W_FAILURE(std::errc::operation_canceled, "could not get sample buffer size\n");
+ }
+
+ return _dst_frame;
+}
+
+boost::leaf::result w_av_frame::convert_video(_In_ w_av_config &&p_dst_config) {
+ // create a buffer and dst frame
+ auto _video_buffer = std::vector();
+ auto _dst_frame = w_av_frame(std::move(p_dst_config));
+ BOOST_LEAF_CHECK(_dst_frame.init());
+ BOOST_LEAF_CHECK(_dst_frame.set_video_frame(std::move(_video_buffer)));
+
+ auto *_context = sws_getContext(
+ this->_config.width, this->_config.height, this->_config.format, _dst_frame._config.width,
+ _dst_frame._config.height, _dst_frame._config.format, SWS_BICUBIC, nullptr, nullptr, nullptr);
+ if (_context == nullptr) {
+ return W_FAILURE(std::errc::not_enough_memory, "could not create sws context");
+ }
+
+ auto _dst_frame_nn = gsl::not_null(_dst_frame._av_frame);
+ const auto _height =
+ sws_scale(_context, gsl::narrow_cast(this->_av_frame->data),
+ gsl::narrow_cast(this->_av_frame->linesize), 0, this->_config.height,
+ gsl::narrow_cast(_dst_frame_nn->data),
+ gsl::narrow_cast(_dst_frame_nn->linesize));
+
+ // free context
+ sws_freeContext(_context);
+
+ if (_height < 0) {
+ return W_FAILURE(
+ std::errc::invalid_argument,
+ "w_av_frame sws_scale failed because: \"" + w_ffmpeg_ctx::get_av_error_str(_height) + "\"");
+ }
+
+ return _dst_frame;
+}
+
+boost::leaf::result w_av_frame::load_video_frame_from_img_file(
+ _In_ const std::filesystem::path &p_path, _In_ AVPixelFormat p_pixel_fmt) {
+#ifdef WOLF_MEDIA_STB
+
+ // width, height, comp
+ int _width = 0;
+ int _height = 0;
+ int _comp = 0;
+
+ const auto _path = p_path.string();
+ if (!std::filesystem::exists(p_path)) {
+ return W_FAILURE(std::errc::invalid_argument, " path not exist for av_frame" + _path);
+ }
+
+ auto *_raw_img_data = stbi_load(_path.c_str(), &_width, &_height, &_comp, 0);
+
+ if (_raw_img_data == nullptr) {
+ return W_FAILURE(std::errc::invalid_argument, "could not load image file " + _path);
+ }
+
+ auto _len = gsl::narrow_cast(_width * _height * _comp);
+ const auto _raw_img_data_span = gsl::span(_raw_img_data, _raw_img_data + _len);
+ auto _raw_img_data_vec =
+ std::vector(_raw_img_data_span.begin(), _raw_img_data_span.end());
+
+ free(_raw_img_data);
+
+ auto _src_config = w_av_config(p_pixel_fmt, _width, _height);
+ // create an av_frame from image raw data
+ auto _src_frame = w_av_frame(std::move(_src_config));
+ BOOST_LEAF_CHECK(_src_frame.init());
+ BOOST_LEAF_CHECK(_src_frame.set_video_frame(std::move(_raw_img_data_vec)));
+
+ return _src_frame;
+#else
+ return W_FAILURE(std::errc::not_supported, "WOLF_MEDIA_STB not defined");
+#endif
+}
+
+boost::leaf::result w_av_frame::save_video_frame_to_img_file(
+ _In_ const std::filesystem::path &p_path, int p_quality) noexcept {
+ try {
+#ifdef WOLF_MEDIA_STB
+
+ if (this->_av_frame == nullptr || this->_av_frame->width == 0 || this->_av_frame->height == 0) {
+ return W_FAILURE(std::errc::invalid_argument, "bad parameters for avframe");
+ }
+
+ const auto _path = p_path.string();
+ auto _ext = p_path.extension().string();
+ std::transform(_ext.cbegin(), _ext.cend(), _ext.begin(), ::tolower);
+
+ const auto _comp = this->_av_frame->linesize[0] / this->_av_frame->width;
+ if (_ext == ".bmp") {
+ return stbi_write_bmp(_path.c_str(), this->_config.width, this->_config.height, _comp,
+ this->_av_frame->data[0]);
+ }
+ if (_ext == ".png") {
+ return stbi_write_png(_path.c_str(), this->_config.width, this->_config.height, _comp,
+ this->_av_frame->data[0], this->_av_frame->linesize[0]);
+ }
+ if (_ext == ".jpg" || _ext == ".jpeg") {
+ return stbi_write_jpg(_path.c_str(), this->_config.width, this->_config.height, _comp,
+ this->_av_frame->data[0], p_quality);
+ }
+ return W_FAILURE(std::errc::invalid_argument, "image format not supported for " + _path);
+ } catch (const std::exception &p_exc) {
+ return W_FAILURE(std::errc::invalid_argument,
+ "caught an esxception for " + std::string(p_exc.what()));
+ }
+#else
+ return W_FAILURE(std::errc::not_supported, "WOLF_MEDIA_STB not defined");
+#endif
+}
+
+#endif // WOLF_MEDIA_FFMPEG
\ No newline at end of file
diff --git a/wolf/media/ffmpeg/w_av_frame.hpp b/wolf/media/ffmpeg/w_av_frame.hpp
new file mode 100644
index 000000000..71155ae3e
--- /dev/null
+++ b/wolf/media/ffmpeg/w_av_frame.hpp
@@ -0,0 +1,136 @@
+/*
+ Project: Wolf Engine. Copyright © 2014-2023 Pooya Eimandar
+ https://github.com/WolfEngine/WolfEngine
+*/
+
+#ifdef WOLF_MEDIA_FFMPEG
+
+#pragma once
+
+#include
+#include "w_av_config.hpp"
+
+extern "C" {
+#include
+}
+
+namespace wolf::media::ffmpeg {
+
+class w_decoder;
+class w_encoder;
+
+class w_av_frame {
+ friend w_decoder;
+ friend w_encoder;
+
+ public:
+ /**
+ * constructor the av_frame with specific config
+ * @param p_config, the av audio config
+ */
+ W_API explicit w_av_frame(_In_ w_av_config &&p_config) noexcept;
+
+ // destructor
+ W_API virtual ~w_av_frame() noexcept { _release(); }
+
+ // move constructor.
+ W_API w_av_frame(w_av_frame &&p_other) noexcept { _move(std::forward(p_other)); }
+ // move assignment operator.
+ W_API w_av_frame &operator=(w_av_frame &&p_other) noexcept {
+ _move(std::forward(p_other));
+ return *this;
+ }
+
+ /**
+ * initialize the avframe
+ * @returns zero on success
+ */
+ W_API
+ boost::leaf::result init() noexcept;
+
+ /**
+ * set the AVFrame data
+ * @param p_data, the initial data of ffmpeg AVFrame
+ * @param p_alignment, the alignment
+ * @returns zero on success
+ */
+ W_API boost::leaf::result set_video_frame(_Inout_ std::vector &&p_data) noexcept;
+
+ /**
+ * set the AVFrame's pts
+ * @param p_pts, the pts data
+ * @returns void
+ */
+ W_API void set_pts(_In_ int64_t p_pts) noexcept;
+
+ /**
+ * get data and linesize as a tuple
+ * @returns tuple
+ */
+ W_API
+ std::tuple get() const noexcept;
+
+ /**
+ * convert the ffmpeg video AVFrame
+ * @returns the converted instance of AVFrame
+ */
+ W_API
+ boost::leaf::result convert_video(_In_ w_av_config &&p_dst_config);
+
+ /**
+ * convert the ffmpeg audio AVFrame
+ * @returns the converted instance of AVFrame
+ */
+ W_API
+ boost::leaf::result convert_audio(_In_ w_av_config &&p_dst_config);
+
+ /**
+ * @returns config
+ */
+ W_API w_av_config get_config() const noexcept;
+
+#ifdef WOLF_MEDIA_STB
+
+ /**
+ * create w_av_frame from image file path
+ * @returns the AVFrame
+ */
+ W_API
+ static boost::leaf::result load_video_frame_from_img_file(
+ _In_ const std::filesystem::path &p_path, _In_ AVPixelFormat p_pixel_fmt);
+
+ /**
+ * save to to the image file
+ * @param p_quality, quality will be used only for jpeg and is between 1 and
+ * 100
+ * @returns zero on success
+ */
+ W_API
+ boost::leaf::result save_video_frame_to_img_file(_In_ const std::filesystem::path &p_path,
+ int p_quality = 100) noexcept;
+
+#endif
+
+ private:
+ // copy constructor.
+ w_av_frame(const w_av_frame &) = delete;
+ // copy assignment operator.
+ w_av_frame &operator=(const w_av_frame &) = delete;
+
+ // release
+ void _release() noexcept;
+ // move
+ void _move(w_av_frame &&p_other) noexcept;
+
+ // the channel layout of the audio
+ AVChannelLayout _channel_layout = {};
+ // the AVFrame config
+ w_av_config _config = {};
+ // the ffmpeg AVFrame
+ gsl::owner _av_frame = nullptr;
+ // the ffmpeg AVFrame data
+ std::vector _data = {};
+};
+} // namespace wolf::media::ffmpeg
+
+#endif // WOLF_MEDIA_FFMPEG
diff --git a/wolf/media/ffmpeg/w_av_packet.cpp b/wolf/media/ffmpeg/w_av_packet.cpp
new file mode 100644
index 000000000..462e9d25c
--- /dev/null
+++ b/wolf/media/ffmpeg/w_av_packet.cpp
@@ -0,0 +1,64 @@
+#ifdef WOLF_MEDIA_FFMPEG
+
+#include "w_av_packet.hpp"
+
+using w_av_packet = wolf::media::ffmpeg::w_av_packet;
+
+w_av_packet::w_av_packet(_In_ AVPacket *p_av_packet) noexcept
+ : _packet(p_av_packet) {}
+
+boost::leaf::result w_av_packet::init() noexcept {
+ _release();
+ return init(nullptr, 0);
+}
+
+boost::leaf::result w_av_packet::init(_Inout_ std::vector &&p_data) noexcept {
+ _release();
+ this->_own_data = std::forward &&>(p_data);
+ return init(this->_own_data.data(), this->_own_data.size());
+}
+
+boost::leaf::result w_av_packet::init(_In_ uint8_t *p_data, _In_ size_t p_data_len) noexcept {
+
+ this->_packet = av_packet_alloc();
+ if (this->_packet == nullptr) {
+ return W_FAILURE(std::errc::not_enough_memory, "could not allocate memory for av packet");
+ }
+ if (p_data && p_data_len) {
+ this->_packet->data = p_data;
+ this->_packet->size = gsl::narrow_cast(p_data_len);
+ }
+
+ return 0;
+}
+
+void w_av_packet::unref() noexcept { av_packet_unref(this->_packet); }
+
+uint8_t *w_av_packet::get_data() const noexcept {
+ return this->_packet->data;
+}
+
+int w_av_packet::get_size() const noexcept {
+ return this->_packet->size;
+}
+
+int w_av_packet::get_stream_index() const noexcept {
+ return this->_packet->stream_index;
+}
+
+void w_av_packet::_release() noexcept {
+ if (this->_packet != nullptr) {
+ av_packet_free(&this->_packet);
+ this->_packet = nullptr;
+ }
+}
+
+void w_av_packet::_move(_Inout_ w_av_packet &&p_other) noexcept {
+ if (this == &p_other) {
+ return;
+ }
+ this->_packet = std::exchange(p_other._packet, nullptr);
+ this->_own_data = std::move(p_other._own_data);
+}
+
+#endif // WOLF_MEDIA_FFMPEG
diff --git a/wolf/media/ffmpeg/w_av_packet.hpp b/wolf/media/ffmpeg/w_av_packet.hpp
new file mode 100644
index 000000000..12287bd8e
--- /dev/null
+++ b/wolf/media/ffmpeg/w_av_packet.hpp
@@ -0,0 +1,96 @@
+/*
+ Project: Wolf Engine. Copyright © 2014-2023 Pooya Eimandar
+ https://github.com/WolfEngine/WolfEngine
+*/
+
+#ifdef WOLF_MEDIA_FFMPEG
+
+#pragma once
+
+#include
+
+extern "C" {
+#include
+}
+
+namespace wolf::media::ffmpeg {
+
+class w_decoder;
+class w_encoder;
+class w_ffmpeg;
+
+class w_av_packet {
+ friend w_decoder;
+ friend w_encoder;
+ friend w_ffmpeg;
+
+ public:
+ // default construct an av_packet
+ W_API w_av_packet() noexcept = default;
+
+ /**
+ * construct an av_packet
+ */
+ W_API explicit w_av_packet(_In_ AVPacket *p_av_packet) noexcept;
+
+ // move constructor.
+ W_API w_av_packet(w_av_packet &&p_other) noexcept {
+ _move(std::forward(p_other));
+ }
+ // move assignment operator.
+ W_API w_av_packet &operator=(w_av_packet &&p_other) noexcept {
+ _move(std::forward(p_other));
+ return *this;
+ }
+
+ // destructor
+ W_API virtual ~w_av_packet() noexcept { _release(); }
+
+ /**
+ * initialize the av_packet
+ * @returns zero on success
+ */
+ W_API boost::leaf::result init() noexcept;
+
+ /**
+ * initialize the av_packet from data
+ * @returns zero on success
+ */
+ W_API boost::leaf::result init(_In_ uint8_t *p_data, _In_ size_t p_data_len) noexcept;
+
+ /**
+ * initialize the av_packet
+ * @returns void
+ */
+ W_API boost::leaf::result init(_Inout_ std::vector &&p_data) noexcept;
+
+ /**
+ * unref av_packet
+ */
+ W_API void unref() noexcept;
+
+ // get packet data
+ W_API uint8_t *get_data() const noexcept;
+
+ // get packet size
+ W_API int get_size() const noexcept;
+
+ // get stream index
+ W_API int get_stream_index() const noexcept;
+
+ private:
+ // copy constructor.
+ w_av_packet(const w_av_packet &) = delete;
+ // copy assignment operator.
+ w_av_packet &operator=(const w_av_packet &) = delete;
+ // release the resources
+ void _release() noexcept;
+ // move the resources
+ void _move(_Inout_ w_av_packet && p_other) noexcept;
+
+ gsl::owner _packet = {};
+ std::vector _own_data;
+};
+} // namespace wolf::media::ffmpeg
+
+#endif
\ No newline at end of file
diff --git a/wolf/media/ffmpeg/w_decoder.cpp b/wolf/media/ffmpeg/w_decoder.cpp
new file mode 100644
index 000000000..4e0a9d7c6
--- /dev/null
+++ b/wolf/media/ffmpeg/w_decoder.cpp
@@ -0,0 +1,70 @@
+#ifdef WOLF_MEDIA_FFMPEG
+
+#include "w_decoder.hpp"
+
+using w_decoder = wolf::media::ffmpeg::w_decoder;
+
+boost::leaf::result w_decoder::decode_frame_from_packet(_In_ AVPacket *p_packet,
+ _Inout_ w_av_frame &p_frame) {
+ // start decoding
+ auto _ret = avcodec_send_packet(this->ctx.codec_ctx, p_packet);
+ if (_ret < 0) {
+ return W_FAILURE(std::errc::operation_canceled,
+ "could not parse packet for decoding because:\"" +
+ w_ffmpeg_ctx::get_av_error_str(_ret) + "\"");
+ }
+
+ for (;;) {
+ _ret = avcodec_receive_frame(this->ctx.codec_ctx, p_frame._av_frame);
+ if (_ret == 0 || _ret == AVERROR(EAGAIN) || _ret == AVERROR_EOF) {
+ break;
+ }
+ if (_ret < 0) {
+ return W_FAILURE(std::errc::operation_canceled,
+ "error happened during the encoding because:\"" +
+ w_ffmpeg_ctx::get_av_error_str(_ret) + "\"");
+ }
+ }
+ return 0;
+}
+
+boost::leaf::result w_decoder::decode(_In_ const w_av_packet &p_packet,
+ _Inout_ w_av_frame &p_frame,
+ _In_ bool p_flush) noexcept {
+ auto _dst_packet = w_av_packet();
+ _dst_packet.init();
+
+ for (;;) {
+ const auto _bytes =
+ av_parser_parse2(this->ctx.parser, this->ctx.codec_ctx, &_dst_packet._packet->data,
+ &_dst_packet._packet->size, p_packet._packet->data, p_packet._packet->size,
+ AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
+
+ if (_bytes == 0) {
+ break;
+ }
+
+ if (_dst_packet._packet->size == 0) {
+ // try decode the inputed packet
+ BOOST_LEAF_CHECK(decode_frame_from_packet(p_packet._packet, p_frame));
+ } else {
+ if (_bytes < 0) {
+ return W_FAILURE(std::errc::operation_canceled, "could not parse packet for decoding");
+ }
+ p_packet._packet->data += _bytes;
+ p_packet._packet->size -= _bytes;
+ if (_dst_packet._packet->size > 0) {
+ BOOST_LEAF_CHECK(decode_frame_from_packet(_dst_packet._packet, p_frame));
+ }
+ }
+ }
+
+ if (p_flush) {
+ // flush the decoder
+ BOOST_LEAF_CHECK(decode_frame_from_packet(nullptr, p_frame));
+ }
+
+ return 0;
+}
+
+#endif // WOLF_MEDIA_FFMPEG
\ No newline at end of file
diff --git a/wolf/media/ffmpeg/w_decoder.hpp b/wolf/media/ffmpeg/w_decoder.hpp
new file mode 100644
index 000000000..08bc925d6
--- /dev/null
+++ b/wolf/media/ffmpeg/w_decoder.hpp
@@ -0,0 +1,46 @@
+/*
+ Project: Wolf Engine. Copyright © 2014-2023 Pooya Eimandar
+ https://github.com/WolfEngine/WolfEngine
+*/
+
+#ifdef WOLF_MEDIA_FFMPEG
+
+#pragma once
+
+#include "w_av_frame.hpp"
+#include "w_av_packet.hpp"
+#include "w_ffmpeg_ctx.hpp"
+#include
+
+namespace wolf::media::ffmpeg {
+
+class w_decoder {
+public:
+ w_ffmpeg_ctx ctx = {};
+
+ // constructor
+ W_API w_decoder() = default;
+ // destructor
+ W_API virtual ~w_decoder() noexcept = default;
+
+ // move constructor.
+ W_API w_decoder(w_decoder &&p_other) noexcept = default;
+ // move assignment operator.
+ W_API w_decoder &operator=(w_decoder &&p_other) noexcept = default;
+
+ W_API boost::leaf::result decode(_In_ const w_av_packet &p_packet,
+ _Inout_ w_av_frame &p_frame,
+ _In_ bool p_flush = false) noexcept;
+
+private:
+ // copy constructor
+ w_decoder(const w_decoder &) = delete;
+ // copy operator
+ w_decoder &operator=(const w_decoder &) = delete;
+
+ boost::leaf::result decode_frame_from_packet(_In_ AVPacket *p_packet,
+ _Inout_ w_av_frame &p_frame);
+};
+} // namespace wolf::media::ffmpeg
+
+#endif // WOLF_MEDIA_FFMPEG
\ No newline at end of file
diff --git a/wolf/media/ffmpeg/w_encoder.cpp b/wolf/media/ffmpeg/w_encoder.cpp
new file mode 100644
index 000000000..f141a489d
--- /dev/null
+++ b/wolf/media/ffmpeg/w_encoder.cpp
@@ -0,0 +1,62 @@
+#ifdef WOLF_MEDIA_FFMPEG
+
+#include "w_encoder.hpp"
+
+using w_encoder = wolf::media::ffmpeg::w_encoder;
+using w_av_packet = wolf::media::ffmpeg::w_av_packet;
+
+boost::leaf::result w_encoder::_encode_frame_to_packet(
+ _In_ const AVFrame *p_frame, _Inout_ std::vector &p_packet_data) const noexcept {
+ auto _packet = w_av_packet();
+ _packet.init();
+
+ for (;;) {
+ auto _ret = avcodec_send_frame(this->ctx.codec_ctx, p_frame);
+ if (_ret < 0) {
+ return W_FAILURE(std::errc::operation_canceled,
+ "failed to send the avframe for encoding because:\"" +
+ w_ffmpeg_ctx::get_av_error_str(_ret) + "\"");
+ }
+
+ for (;;) {
+ _ret = avcodec_receive_packet(this->ctx.codec_ctx, _packet._packet);
+ if (_ret == 0 || _ret == AVERROR_EOF) {
+ if (_packet._packet->size) {
+ std::copy(_packet._packet->data, _packet._packet->data + _packet._packet->size,
+ std::back_inserter(p_packet_data));
+ }
+ return 0;
+ }
+
+ _packet.unref();
+
+ if (_ret == AVERROR(EAGAIN)) {
+ break;
+ }
+ return W_FAILURE(std::errc::operation_canceled,
+ "error happened during the encoding because:\"" +
+ w_ffmpeg_ctx::get_av_error_str(_ret) + "\"");
+ }
+ }
+ return 0;
+}
+
+boost::leaf::result w_encoder::encode(_In_ const w_av_frame &p_frame,
+ _Inout_ w_av_packet &p_packet,
+ _In_ bool p_flush) noexcept {
+ std::vector _packet_data;
+
+ // encode frame to packet
+ BOOST_LEAF_CHECK(_encode_frame_to_packet(p_frame._av_frame, _packet_data));
+ if (p_flush) {
+ // flush
+ BOOST_LEAF_CHECK(_encode_frame_to_packet(nullptr, _packet_data));
+ }
+
+ // init packet
+ p_packet.init(std::move(_packet_data));
+
+ return {};
+}
+
+#endif // WOLF_MEDIA_FFMPEG
\ No newline at end of file
diff --git a/wolf/media/ffmpeg/w_encoder.hpp b/wolf/media/ffmpeg/w_encoder.hpp
new file mode 100644
index 000000000..8b738daa2
--- /dev/null
+++ b/wolf/media/ffmpeg/w_encoder.hpp
@@ -0,0 +1,47 @@
+/*
+ Project: Wolf Engine. Copyright © 2014-2023 Pooya Eimandar
+ https://github.com/WolfEngine/WolfEngine
+*/
+
+#ifdef WOLF_MEDIA_FFMPEG
+
+#pragma once
+
+#include "w_av_frame.hpp"
+#include "w_av_packet.hpp"
+#include "w_ffmpeg_ctx.hpp"
+#include
+
+namespace wolf::media::ffmpeg {
+
+class w_encoder {
+public:
+ // constructor
+ W_API w_encoder() = default;
+ // destructor
+ W_API virtual ~w_encoder() noexcept = default;
+
+ // move constructor.
+ W_API w_encoder(w_encoder &&p_other) noexcept = default;
+ // move assignment operator.
+ W_API w_encoder &operator=(w_encoder &&p_other) noexcept = default;
+
+ W_API boost::leaf::result encode(_In_ const w_av_frame &p_frame,
+ _Inout_ w_av_packet &p_packet,
+ _In_ bool p_flush = true) noexcept;
+
+ w_ffmpeg_ctx ctx = {};
+
+private:
+ // copy constructor
+ w_encoder(const w_encoder &) = delete;
+ // copy operator
+ w_encoder &operator=(const w_encoder &) = delete;
+
+ boost::leaf::result
+ _encode_frame_to_packet(_In_ const AVFrame *p_frame,
+ _Inout_ std::vector &p_packet_data) const noexcept;
+};
+} // namespace wolf::media::ffmpeg
+
+#endif // WOLF_MEDIA_FFMPEG
\ No newline at end of file
diff --git a/wolf/media/ffmpeg/w_ffmpeg.cpp b/wolf/media/ffmpeg/w_ffmpeg.cpp
new file mode 100644
index 000000000..5773a87ea
--- /dev/null
+++ b/wolf/media/ffmpeg/w_ffmpeg.cpp
@@ -0,0 +1,345 @@
+#ifdef WOLF_MEDIA_FFMPEG
+
+#include "w_ffmpeg.hpp"
+
+extern "C" {
+#include
+}
+
+using w_av_codec_opt = wolf::media::ffmpeg::w_av_codec_opt;
+using w_av_config = wolf::media::ffmpeg::w_av_config;
+using w_av_set_opt = wolf::media::ffmpeg::w_av_set_opt;
+using w_decoder = wolf::media::ffmpeg::w_decoder;
+using w_encoder = wolf::media::ffmpeg::w_encoder;
+using w_ffmpeg = wolf::media::ffmpeg::w_ffmpeg;
+using w_ffmpeg_ctx = wolf::media::ffmpeg::w_ffmpeg_ctx;
+
+static boost::leaf::result s_set_dict(
+ _In_ const std::vector &p_opts) noexcept {
+ AVDictionary *_dict = nullptr;
+ if (p_opts.empty()) {
+ return _dict;
+ }
+
+ auto _ret = av_dict_set(&_dict, nullptr, nullptr, 0);
+ if (_ret < 0) {
+ return W_FAILURE(std::errc::operation_canceled,
+ "could not allocate memory for AVDictionary because: " +
+ w_ffmpeg_ctx::get_av_error_str(_ret));
+ }
+
+ try {
+ for (const auto &_opt : p_opts) {
+ if (_opt.name.empty()) {
+ continue;
+ }
+
+ auto _name_str = _opt.name.c_str();
+ if (std::holds_alternative(_opt.value)) {
+ // set an integer value
+ const auto _value = std::get(_opt.value);
+ const auto _ret = av_dict_set_int(&_dict, _name_str, _value, 0);
+ if (_ret < 0) {
+ return W_FAILURE(std::errc::invalid_argument, "could not set int value for " + _opt.name +
+ ":" + std::to_string(_value) +
+ " because " +
+ w_ffmpeg_ctx::get_av_error_str(_ret));
+ }
+ } else {
+ // set string value
+ const auto _value_str = &std::get(_opt.value);
+ if (_value_str && !_value_str->empty()) {
+ const auto _ret = av_dict_set(&_dict, _opt.name.c_str(), _value_str->c_str(), 0);
+ if (_ret < 0) {
+ return W_FAILURE(std::errc::invalid_argument,
+ "could not set string value for " + _opt.name + ":" + *_value_str +
+ " because " + w_ffmpeg_ctx::get_av_error_str(_ret));
+ }
+ }
+ }
+ }
+ } catch (const std::exception &p_exc) {
+ return W_FAILURE(std::errc::operation_canceled,
+ "s_set_dict failed because: " + std::string(p_exc.what()));
+ }
+ return _dict;
+}
+
+static boost::leaf::result s_create(_Inout_ w_ffmpeg_ctx &p_ctx,
+ _In_ const w_av_config &p_config,
+ _In_ const w_av_codec_opt &p_codec_opts,
+ _In_ const std::vector &p_opts) noexcept {
+ p_ctx.codec_ctx = avcodec_alloc_context3(p_ctx.codec);
+ if (p_ctx.codec_ctx == nullptr) {
+ return W_FAILURE(std::errc::not_enough_memory,
+ "could not allocate memory for avcodec context3");
+ }
+
+ bool _has_error = false;
+ DEFER {
+ if (_has_error && p_ctx.codec_ctx) {
+ auto _ptr = p_ctx.codec_ctx;
+ avcodec_free_context(&_ptr);
+ p_ctx.codec_ctx = nullptr;
+ }
+ });
+
+ p_ctx.codec_ctx->width = p_config.width;
+ p_ctx.codec_ctx->height = p_config.height;
+ p_ctx.codec_ctx->bit_rate = p_codec_opts.bitrate;
+ p_ctx.codec_ctx->time_base = AVRational{1, p_codec_opts.fps};
+ p_ctx.codec_ctx->framerate = AVRational{p_codec_opts.fps, 1};
+ p_ctx.codec_ctx->pix_fmt = p_config.format;
+
+ // set gop
+ if (p_codec_opts.gop >= 0) {
+ p_ctx.codec_ctx->gop_size = p_codec_opts.gop;
+ }
+ // set refs
+ if (p_codec_opts.refs >= 0) {
+ p_ctx.codec_ctx->refs = p_codec_opts.refs;
+ }
+ // set frames
+ if (p_codec_opts.max_b_frames >= 0) {
+ p_ctx.codec_ctx->max_b_frames = p_codec_opts.max_b_frames;
+ }
+ // set thread numbers
+ if (p_codec_opts.thread_count >= 0) {
+ p_ctx.codec_ctx->thread_count = p_codec_opts.thread_count;
+ }
+ // set level
+ if (p_codec_opts.level >= 0) {
+ p_ctx.codec_ctx->level = p_codec_opts.level;
+ }
+ // set flags
+ if (p_ctx.codec_ctx->flags & AVFMT_GLOBALHEADER) {
+ p_ctx.codec_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
+ }
+
+ try {
+ for (const auto &_opt : p_opts) {
+ if (_opt.name.empty()) {
+ continue;
+ }
+
+ auto _name_str = _opt.name.c_str();
+ if (std::holds_alternative(_opt.value)) {
+ // set an integer value
+ const auto _value = std::get(_opt.value);
+ const auto _ret = av_opt_set_int(p_ctx.codec_ctx->priv_data, _name_str, _value, 0);
+ if (_ret < 0) {
+ return W_FAILURE(std::errc::invalid_argument, "could not set int value for " + _opt.name +
+ ":" + std::to_string(_value) +
+ " because " +
+ w_ffmpeg_ctx::get_av_error_str(_ret));
+ }
+ } else if (std::holds_alternative(_opt.value)) {
+ // set double value
+ const auto _value = std::get(_opt.value);
+ const auto _ret = av_opt_set_double(p_ctx.codec_ctx->priv_data, _name_str, _value, 0);
+ if (_ret < 0) {
+ return W_FAILURE(std::errc::invalid_argument, "could not set double value for " +
+ _opt.name + ":" +
+ std::to_string(_value) + " because " +
+ w_ffmpeg_ctx::get_av_error_str(_ret));
+ }
+ } else {
+ // set string value
+ const auto _value_str = &std::get(_opt.value);
+ if (_value_str && !_value_str->empty()) {
+ const auto _ret =
+ av_opt_set(p_ctx.codec_ctx->priv_data, _opt.name.c_str(), _value_str->c_str(), 0);
+ if (_ret < 0) {
+ return W_FAILURE(std::errc::invalid_argument,
+ "could not set string value for " + _opt.name + ":" + *_value_str +
+ " because " + w_ffmpeg_ctx::get_av_error_str(_ret));
+ }
+ }
+ }
+ }
+ } catch (const std::exception &p_exc) {
+ return W_FAILURE(std::errc::operation_canceled,
+ "could not set av option because: " + std::string(p_exc.what()));
+ }
+
+ // open avcodec
+ const auto _ret = avcodec_open2(p_ctx.codec_ctx, p_ctx.codec_ctx->codec, nullptr);
+ if (_ret < 0) {
+ return W_FAILURE(std::errc::operation_canceled,
+ "could not open avcodec because " + w_ffmpeg_ctx::get_av_error_str(_ret));
+ }
+ return 0;
+}
+
+boost::leaf::result w_ffmpeg::create_encoder(
+ _In_ const w_av_config &p_config, _In_ AVCodecID p_id, _In_ const w_av_codec_opt &p_codec_opts,
+ _In_ const std::vector &p_opts) noexcept {
+ w_encoder _encoder = {};
+
+ _encoder.ctx.codec = avcodec_find_encoder(p_id);
+ if (_encoder.ctx.codec == nullptr) {
+ return W_FAILURE(std::errc::invalid_argument,
+ "could not find encoder codec id: " + std::to_string(p_id));
+ }
+
+ BOOST_LEAF_CHECK(s_create(_encoder.ctx, p_config, p_codec_opts, p_opts));
+
+ return _encoder;
+}
+
+boost::leaf::result w_ffmpeg::create_encoder(
+ _In_ const w_av_config &p_config, _In_ const std::string &p_id,
+ _In_ const w_av_codec_opt &p_codec_opts,
+ _In_ const std::vector &p_opts) noexcept {
+ w_encoder _encoder = {};
+
+ _encoder.ctx.codec = avcodec_find_encoder_by_name(p_id.c_str());
+ if (_encoder.ctx.codec == nullptr) {
+ return W_FAILURE(std::errc::invalid_argument, "could not find encoder codec id: " + p_id);
+ };
+
+ BOOST_LEAF_CHECK(s_create(_encoder.ctx, p_config, p_codec_opts, p_opts));
+
+ return _encoder;
+}
+
+boost::leaf::result w_ffmpeg::create_decoder(
+ _In_ const w_av_config &p_config, _In_ const AVCodecID p_id,
+ _In_ const w_av_codec_opt &p_codec_opts,
+ _In_ const std::vector &p_opts) noexcept {
+ w_decoder _decoder = {};
+
+ _decoder.ctx.codec = avcodec_find_decoder(p_id);
+ if (_decoder.ctx.codec == nullptr) {
+ return W_FAILURE(std::errc::invalid_argument,
+ "could not find decoder codec id: " + std::to_string(p_id));
+ }
+
+ _decoder.ctx.parser = av_parser_init(_decoder.ctx.codec->id);
+ if (_decoder.ctx.parser == nullptr) {
+ return W_FAILURE(std::errc::invalid_argument,
+ "could not initialize parser for codec id: " + std::to_string(p_id));
+ }
+
+ BOOST_LEAF_CHECK(s_create(_decoder.ctx, p_config, p_codec_opts, p_opts));
+
+ return _decoder;
+}
+
+boost::leaf::result w_ffmpeg::create_decoder(
+ _In_ const w_av_config &p_config, _In_ const std::string &p_id,
+ _In_ const w_av_codec_opt &p_codec_opts,
+ _In_ const std::vector &p_opts) noexcept {
+ w_decoder _decoder = {};
+
+ _decoder.ctx.codec = avcodec_find_decoder_by_name(p_id.c_str());
+ if (_decoder.ctx.codec == nullptr) {
+ return W_FAILURE(std::errc::invalid_argument, "could not find decoder codec id: " + p_id);
+ }
+
+ _decoder.ctx.parser = av_parser_init(_decoder.ctx.codec->id);
+ if (_decoder.ctx.parser == nullptr) {
+ return W_FAILURE(std::errc::invalid_argument,
+ "could not initialize parser for codec id: " + p_id);
+ }
+
+ BOOST_LEAF_CHECK(s_create(_decoder.ctx, p_config, p_codec_opts, p_opts));
+
+ return _decoder;
+}
+
+boost::leaf::result w_ffmpeg::open_stream(
+ _In_ const std::string &p_url, _In_ const std::vector &p_opts,
+ _In_ const
+ std::function &p_on_frame) noexcept {
+ try {
+ // url is invalid
+ if (p_url.empty()) {
+ return W_FAILURE(std::errc::invalid_argument,
+ "could not allocate memory for av format context");
+ }
+
+ // allocate memory for avformat context
+ auto _fmt_ctx = avformat_alloc_context();
+ if (_fmt_ctx == nullptr) {
+ return W_FAILURE(std::errc::not_enough_memory,
+ "could not allocate memory for av format context from the url: " + p_url);
+ }
+
+ DEFER {
+ if (_fmt_ctx != nullptr) {
+ // free av format context
+ avformat_free_context(_fmt_ctx);
+ _fmt_ctx = nullptr;
+ }
+ });
+
+ // allocate memory for packet
+ auto _packet = w_av_packet();
+ BOOST_LEAF_CHECK(_packet.init());
+
+ // set options to av format context
+ BOOST_LEAF_AUTO(_dict, s_set_dict(p_opts));
+
+ // open input url
+ int _ret = avformat_open_input(&_fmt_ctx, p_url.c_str(), nullptr, &_dict);
+ if (_ret < 0) {
+ return W_FAILURE(std::errc::operation_canceled,
+ "could not open input url: " + p_url +
+ " because: " + w_ffmpeg_ctx::get_av_error_str(_ret));
+ }
+
+ // find the stream info
+ _ret = avformat_find_stream_info(_fmt_ctx, nullptr);
+ if (_ret < 0) {
+ return W_FAILURE(std::errc::operation_canceled,
+ "could not find stream info from the url: " + p_url);
+ }
+
+ if (_fmt_ctx->nb_streams == 0) {
+ return W_FAILURE(std::errc::operation_canceled, "missing stream for the url: " + p_url);
+ }
+
+ // search for audio & video stream
+ const auto _video_stream_index =
+ av_find_best_stream(_fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0);
+ const auto _audio_stream_index =
+ av_find_best_stream(_fmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, nullptr, 0);
+
+ if (_audio_stream_index < 0 && _video_stream_index < 0) {
+ return W_FAILURE(std::errc::operation_canceled,
+ "could not find any video or audio stream from the url: " + p_url);
+ }
+
+ AVStream *_audio_stream = nullptr;
+ AVStream *_video_stream = nullptr;
+
+ if (_audio_stream_index >= 0) {
+ _audio_stream = _fmt_ctx->streams[_audio_stream_index];
+ }
+ if (_video_stream_index >= 0) {
+ _video_stream = _fmt_ctx->streams[_video_stream_index];
+ }
+
+ for (;;) {
+ // unref packet
+ _packet.unref();
+ // read packet
+ _ret = av_read_frame(_fmt_ctx, _packet._packet);
+ if (_ret < 0) {
+ break;
+ }
+
+ if (p_on_frame && !p_on_frame(_packet, _audio_stream, _video_stream)) {
+ break;
+ }
+ }
+ return 0;
+ } catch (const std::exception &p_exc) {
+ return W_FAILURE(std::errc::operation_canceled,
+ "caught an exception: " + std::string(p_exc.what()));
+ }
+}
+
+#endif // WOLF_MEDIA_FFMPEG
\ No newline at end of file
diff --git a/wolf/media/ffmpeg/w_ffmpeg.hpp b/wolf/media/ffmpeg/w_ffmpeg.hpp
new file mode 100644
index 000000000..f418536f1
--- /dev/null
+++ b/wolf/media/ffmpeg/w_ffmpeg.hpp
@@ -0,0 +1,105 @@
+/*
+ Project: Wolf Engine. Copyright © 2014-2023 Pooya Eimandar
+ https://github.com/WolfEngine/WolfEngine
+*/
+
+#ifdef WOLF_MEDIA_FFMPEG
+
+#pragma once
+
+#include "w_av_packet.hpp"
+#include "w_ffmpeg_ctx.hpp"
+#include "w_encoder.hpp"
+#include "w_decoder.hpp"
+#include
+#include
+
+namespace wolf::media::ffmpeg {
+
+struct w_av_codec_opt {
+ int64_t bitrate;
+ int fps;
+ int gop;
+ int level;
+ int max_b_frames;
+ int refs;
+ int thread_count;
+};
+
+struct w_av_set_opt {
+ // name of option
+ std::string name;
+ // value type
+ std::variant value;
+};
+
+class w_ffmpeg {
+ public:
+ /*
+ * create ffmpeg encoder
+ * @param p_config, the video config
+ * @param p_id, the avcodec id
+ * @param p_codec_opts, the codec settings
+ * @param p_opts, the codec options
+ * @returns encoder object on success
+ */
+ W_API static boost::leaf::result create_encoder(
+ _In_ const w_av_config &p_config, _In_ AVCodecID p_id,
+ _In_ const w_av_codec_opt &p_codec_opts,
+ _In_ const std::vector &p_opts = {}) noexcept;
+
+ /*
+ * create ffmpeg encoder
+ * @param p_config, the video config
+ * @param p_id, the avcodec id in string (e.g. "libsvtav1", "libvpx")
+ * @param p_codec_opts, the codec settings
+ * @param p_opts, the codec options
+ * @returns encoder object on success
+ */
+ W_API static boost::leaf::result create_encoder(
+ _In_ const w_av_config &p_config, _In_ const std::string &p_id,
+ _In_ const w_av_codec_opt &p_codec_opts,
+ _In_ const std::vector &p_opts = {}) noexcept;
+
+ /*
+ * create ffmpeg decoder
+ * @param p_config, the avconfig
+ * @param p_id, the avcodec id in string (e.g. "libsvtav1", "libvpx")
+ * @param p_codec_opts, the codec options
+ * @param p_opts, the codec options
+ * @returns encoder object on success
+ */
+ W_API static boost::leaf::result create_decoder(
+ _In_ const w_av_config &p_config, _In_ AVCodecID p_id,
+ _In_ const w_av_codec_opt &p_codec_opts,
+ _In_ const std::vector &p_opts = {}) noexcept;
+
+ /*
+ * create ffmpeg decoder
+ * @param p_config, the avconfig
+ * @param p_id, the avcodec id in string (e.g. "libsvtav1", "libvpx")
+ * @param p_codec_opts, the codec settings
+ * @param p_opts, the codec options
+ * @returns encoder object on success
+ */
+ W_API static boost::leaf::result create_decoder(
+ _In_ const w_av_config &p_config, _In_ const std::string &p_id,
+ _In_ const w_av_codec_opt &p_codec_opts,
+ _In_ const std::vector &p_opts = {}) noexcept;
+
+ /*
+ * open and receive stream from file or url
+ * @param p_url, the url
+ * @param p_opts, the codec options
+ * @param p_on_frame, on frame data recieved callback
+ * @returns encoder object on success
+ */
+ W_API static boost::leaf::result open_stream(
+ _In_ const std::string &p_url, _In_ const std::vector &p_opts,
+ _In_ const
+ std::function &p_on_frame) noexcept;
+};
+} // namespace wolf::media::ffmpeg
+
+#endif // WOLF_MEDIA_FFMPEG
\ No newline at end of file
diff --git a/wolf/media/ffmpeg/w_ffmpeg_ctx.cpp b/wolf/media/ffmpeg/w_ffmpeg_ctx.cpp
new file mode 100644
index 000000000..ba168bb09
--- /dev/null
+++ b/wolf/media/ffmpeg/w_ffmpeg_ctx.cpp
@@ -0,0 +1,40 @@
+#ifdef WOLF_MEDIA_FFMPEG
+
+#include "w_ffmpeg_ctx.hpp"
+
+using w_ffmpeg_ctx = wolf::media::ffmpeg::w_ffmpeg_ctx;
+
+void w_ffmpeg_ctx::_release() noexcept {
+ if (this->parser != nullptr) {
+ av_parser_close(this->parser);
+ this->parser = nullptr;
+ }
+ if (this->codec_ctx != nullptr) {
+ if (avcodec_is_open(this->codec_ctx) > 0) {
+ avcodec_close(this->codec_ctx);
+ }
+ avcodec_free_context(&this->codec_ctx);
+ this->codec_ctx = nullptr;
+ }
+}
+
+void w_ffmpeg_ctx::_move(w_ffmpeg_ctx &&p_other) noexcept {
+ if (this == &p_other) {
+ return;
+ }
+ this->codec_ctx = std::exchange(p_other.codec_ctx, nullptr);
+ this->codec = std::exchange(p_other.codec, nullptr);
+ this->parser = std::exchange(p_other.parser, nullptr);
+}
+
+std::string w_ffmpeg_ctx::get_av_error_str(_In_ int p_error_code) noexcept {
+ std::array _error[] = {'\0'};
+ try {
+ std::ignore = av_make_error_string(_error->data(), W_MAX_PATH, p_error_code);
+ return std::string(_error->data());
+ } catch (...) {
+ return std::string();
+ }
+}
+
+#endif // WOLF_MEDIA_FFMPEG
\ No newline at end of file
diff --git a/wolf/media/ffmpeg/w_ffmpeg_ctx.hpp b/wolf/media/ffmpeg/w_ffmpeg_ctx.hpp
new file mode 100644
index 000000000..b6dd9d9fe
--- /dev/null
+++ b/wolf/media/ffmpeg/w_ffmpeg_ctx.hpp
@@ -0,0 +1,53 @@
+/*
+ Project: Wolf Engine. Copyright © 2014-2023 Pooya Eimandar
+ https://github.com/WolfEngine/WolfEngine
+*/
+
+#ifdef WOLF_MEDIA_FFMPEG
+
+#pragma once
+
+#include
+
+extern "C" {
+#include
+}
+
+namespace wolf::media::ffmpeg {
+
+class w_ffmpeg_ctx {
+ public:
+ // constructor
+ W_API w_ffmpeg_ctx() = default;
+ // destructor
+ W_API virtual ~w_ffmpeg_ctx() noexcept { _release(); }
+
+ // move constructor.
+ W_API w_ffmpeg_ctx(w_ffmpeg_ctx &&p_other) noexcept {
+ _move(std::forward(p_other));
+ }
+ // move assignment operator.
+ W_API w_ffmpeg_ctx &operator=(w_ffmpeg_ctx &&p_other) noexcept {
+ _move(std::forward(p_other));
+ return *this;
+ }
+
+ W_API static std::string get_av_error_str(_In_ int p_error_code) noexcept;
+
+ gsl::owner codec_ctx = {};
+ gsl::owner codec = {};
+ gsl::owner parser = {};
+
+ private:
+ // copy constructor
+ w_ffmpeg_ctx(const w_ffmpeg_ctx &) = delete;
+ // copy operator
+ w_ffmpeg_ctx &operator=(const w_ffmpeg_ctx &) = delete;
+ // release all resources
+ void _release() noexcept;
+ // move all resources
+ void _move(w_ffmpeg_ctx &&p_other) noexcept;
+};
+} // namespace wolf::media::ffmpeg
+
+#endif // WOLF_MEDIA_FFMPEG
\ No newline at end of file
diff --git a/wolf/media/gst/audio/w_audio_format.cpp b/wolf/media/gst/audio/w_audio_format.cpp
new file mode 100644
index 000000000..86458e89b
--- /dev/null
+++ b/wolf/media/gst/audio/w_audio_format.cpp
@@ -0,0 +1 @@
+#include "media/gst/audio/w_audio_format.hpp"
diff --git a/wolf/media/gst/audio/w_audio_format.hpp b/wolf/media/gst/audio/w_audio_format.hpp
new file mode 100644
index 000000000..43bdfbb63
--- /dev/null
+++ b/wolf/media/gst/audio/w_audio_format.hpp
@@ -0,0 +1,39 @@
+#pragma once
+
+#include
+
+namespace wolf::media::gst {
+
+/**
+ * enum same as GstAudioFormat.
+ */
+enum class w_audio_format
+{
+ Unknonw = GST_AUDIO_FORMAT_UNKNOWN,
+ Encoded = GST_AUDIO_FORMAT_ENCODED,
+
+ S8 = GST_AUDIO_FORMAT_S8,
+ S16 = GST_AUDIO_FORMAT_S16,
+ S16LE = GST_AUDIO_FORMAT_S16LE,
+ S16BE = GST_AUDIO_FORMAT_S16BE,
+ S32 = GST_AUDIO_FORMAT_S32,
+ S32LE = GST_AUDIO_FORMAT_S32LE,
+ S32BE = GST_AUDIO_FORMAT_S32BE,
+
+ U8 = GST_AUDIO_FORMAT_U8,
+ U16 = GST_AUDIO_FORMAT_U16,
+ U16LE = GST_AUDIO_FORMAT_U16LE,
+ U16BE = GST_AUDIO_FORMAT_U16BE,
+ U32 = GST_AUDIO_FORMAT_U32,
+ U32LE = GST_AUDIO_FORMAT_U32LE,
+ U32BE = GST_AUDIO_FORMAT_U32BE,
+
+ F32 = GST_AUDIO_FORMAT_F32,
+ F32LE = GST_AUDIO_FORMAT_F32LE,
+ F32BE = GST_AUDIO_FORMAT_F32BE,
+ F64 = GST_AUDIO_FORMAT_F32,
+ F64LE = GST_AUDIO_FORMAT_F64LE,
+ F64BE = GST_AUDIO_FORMAT_F64BE
+};
+
+} // namespace wolf::media::gst
diff --git a/wolf/media/gst/audio/w_audio_info.cpp b/wolf/media/gst/audio/w_audio_info.cpp
new file mode 100644
index 000000000..5a5f5f09c
--- /dev/null
+++ b/wolf/media/gst/audio/w_audio_info.cpp
@@ -0,0 +1,25 @@
+#include "media/gst/audio/w_audio_info.hpp"
+
+namespace wolf::media::gst {
+
+auto w_audio_info::make(w_audio_format p_format, size_t p_channels, size_t p_samples)
+ -> boost::leaf::result
+{
+ auto audioinfo_raw = gst_audio_info_new();
+ if (!audioinfo_raw) {
+ return W_FAILURE(std::errc::operation_canceled, "couldn't create audio info.");
+ }
+
+ auto format_raw = static_cast(p_format);
+ gst_audio_info_set_format(
+ audioinfo_raw,
+ format_raw,
+ gsl::narrow_cast(p_samples),
+ gsl::narrow_cast(p_channels),
+ nullptr
+ );
+
+ return w_audio_info(internal::w_raw_tag{}, audioinfo_raw);
+}
+
+} // namespace wolf::media::gst
diff --git a/wolf/media/gst/audio/w_audio_info.hpp b/wolf/media/gst/audio/w_audio_info.hpp
new file mode 100644
index 000000000..ec2994223
--- /dev/null
+++ b/wolf/media/gst/audio/w_audio_info.hpp
@@ -0,0 +1,66 @@
+#pragma once
+
+#include "wolf.hpp"
+
+#include "media/gst/internal/w_common.hpp"
+#include "media/gst/internal/w_wrapper.hpp"
+#include "media/gst/core/w_caps.hpp"
+#include "media/gst/audio/w_audio_format.hpp"
+
+#include
+
+#include
+#include
+
+namespace wolf::media::gst {
+
+/**
+ * wrapper of GstAudioInfo.
+ */
+class w_audio_info : public w_wrapper
+{
+ friend class internal::w_raw_access;
+
+public:
+ /**
+ * @brief make a w_audio_info instance with given format and other info.
+ * @return a constructed audio info on success.
+ */
+ [[nodiscard]] static auto make(w_audio_format p_format,
+ std::size_t p_channels = 2,
+ std::size_t p_samples = 44100)
+ -> boost::leaf::result;
+
+ /**
+ * @brief make a w_caps with info values.
+ */
+ [[nodiscard]] w_caps to_caps()
+ {
+ auto caps_raw = gst_audio_info_to_caps(raw());
+ return internal::w_raw_access::from_raw(caps_raw);
+ }
+
+ /**
+ * @brief set sample rate.
+ * @param p_rate sample rate.
+ */
+ void set_rate(std::size_t p_rate)
+ {
+ raw()->rate = gsl::narrow_cast(p_rate);
+ }
+
+ /**
+ * @brief set channels.
+ */
+ void set_channels(std::size_t p_channels)
+ {
+ raw()->channels = gsl::narrow_cast(p_channels);
+ }
+
+private:
+ w_audio_info(internal::w_raw_tag, GstAudioInfo* p_rawptr) noexcept
+ : w_wrapper(p_rawptr)
+ {}
+};
+
+} // namespace wolf::media::gst
diff --git a/wolf/media/gst/core/w_buffer.cpp b/wolf/media/gst/core/w_buffer.cpp
new file mode 100644
index 000000000..75089a79b
--- /dev/null
+++ b/wolf/media/gst/core/w_buffer.cpp
@@ -0,0 +1,18 @@
+#include "media/gst/core/w_buffer.hpp"
+
+#include "media/gst/internal/w_common.hpp"
+
+namespace wolf::media::gst {
+
+auto w_buffer::make(size_t p_size) -> boost::leaf::result
+{
+ auto buffer_raw = gst_buffer_new_and_alloc(p_size);
+ if (!buffer_raw) {
+ return W_FAILURE(std::errc::operation_canceled,
+ "couldn't create and allocate Buffer.");
+ }
+
+ return w_buffer(internal::w_raw_tag{}, buffer_raw);
+}
+
+} // namespace wolf::media::gst
diff --git a/wolf/media/gst/core/w_buffer.hpp b/wolf/media/gst/core/w_buffer.hpp
new file mode 100644
index 000000000..4a264b032
--- /dev/null
+++ b/wolf/media/gst/core/w_buffer.hpp
@@ -0,0 +1,192 @@
+#pragma once
+
+#include "wolf.hpp"
+
+#include "media/gst/internal/w_wrapper.hpp"
+
+#include
+
+#include
+#include
+
+namespace wolf::media::gst {
+
+/**
+ * @brief read/write map flags.
+ */
+enum class w_buffer_map_flags
+{
+ Read = GST_MAP_READ,
+ Write = GST_MAP_WRITE,
+ ReadWrite = GST_MAP_READWRITE
+};
+
+class w_buffer;
+
+/**
+ * helper class to map underlying data of w_buffer
+ * to an accessible memory region and unmap on destruction.
+ */
+class w_buffer_mapped_data
+{
+ friend class w_buffer;
+
+public:
+ w_buffer_mapped_data(const w_buffer_mapped_data&) = delete;
+ w_buffer_mapped_data(w_buffer_mapped_data&& p_other) noexcept
+ : _buffer(std::exchange(p_other._buffer, nullptr))
+ , _map(std::exchange(p_other._map, GstMapInfo{}))
+ {}
+
+ w_buffer_mapped_data& operator=(const w_buffer_mapped_data&) = delete;
+ w_buffer_mapped_data& operator=(w_buffer_mapped_data&& p_other) noexcept
+ {
+ std::swap(_buffer, p_other._buffer);
+ std::swap(_map, p_other._map);
+ return *this;
+ }
+
+ ~w_buffer_mapped_data() noexcept
+ {
+ if (_buffer) {
+ gst_buffer_unmap(_buffer, &_map);
+ }
+ }
+
+ [[nodiscard]] std::size_t size() const noexcept { return _map.size; }
+
+ [[nodiscard]] guint8* begin() noexcept { return _map.data; }
+ [[nodiscard]] const guint8* begin() const noexcept { return _map.data; }
+
+ [[nodiscard]] guint8* end() noexcept { return _map.data + _map.size; }
+ [[nodiscard]] const guint8* end() const noexcept { return _map.data + _map.size; }
+
+ [[nodiscard]] guint8* data() noexcept { return _map.data; }
+ [[nodiscard]] const guint8* data() const noexcept { return _map.data; }
+
+ [[nodiscard]] guint8& operator[](std::size_t p_index) { return _map.data[p_index]; }
+ [[nodiscard]] const guint8& operator[](std::size_t p_index) const { return _map.data[p_index]; }
+
+private:
+ w_buffer_mapped_data() noexcept {}
+
+ /**
+ * @brief helper method to map raw buffer to a normal accessible data region.
+ * @param p_buffer raw gstreamer buffer.
+ * @param p_flags read/write flags. defaults to read-write.
+ */
+ [[nodiscard]] static auto make(internal::w_raw_tag,
+ GstBuffer* p_buffer,
+ w_buffer_map_flags p_flags = w_buffer_map_flags::ReadWrite)
+ -> boost::leaf::result
+ {
+ auto ret = w_buffer_mapped_data();
+
+ auto flags_raw = static_cast(p_flags);
+ if (!p_buffer || !gst_buffer_map(p_buffer, &ret._map, flags_raw)) {
+ return W_FAILURE(std::errc::operation_canceled, "couldn't map the buffer data.");
+ }
+
+ ret._buffer = p_buffer;
+
+ return ret;
+ }
+
+ GstBuffer* _buffer = nullptr;
+ GstMapInfo _map{};
+};
+
+/**
+ * wrapper of GstBuffer.
+ */
+class w_buffer : public w_wrapper
+{
+ friend class internal::w_raw_access;
+
+public:
+ /**
+ * @brief make a buffer with given size.
+ * @return buffer on success.
+ */
+ [[nodiscard]] static auto make(std::size_t p_size) -> boost::leaf::result;
+
+ /**
+ * @brief get timestamp in nanoseconds.
+ */
+ [[nodiscard]] auto get_timestamp() const noexcept
+ {
+ return GST_BUFFER_TIMESTAMP(raw());
+ }
+
+ /**
+ * @brief get duration of this buffer's playback in nanoseconds.
+ */
+ [[nodiscard]] auto get_duration() const noexcept
+ {
+ return raw()->duration;
+ }
+
+ /**
+ * @brief set timestamp in nanoseconds.
+ */
+ void set_timestamp(std::size_t p_nanoseconds) noexcept
+ {
+ GST_BUFFER_TIMESTAMP(raw()) = static_cast(p_nanoseconds);
+ }
+
+ /**
+ * @brief set duration of this buffer's playback in nanoseconds.
+ */
+ void set_duration(std::size_t p_nanoseconds) noexcept
+ {
+ raw()->duration = static_cast(p_nanoseconds);
+ }
+
+ /**
+ * @brief map to write-only data region.
+ */
+ [[nodiscard]] auto map_data_write()
+ {
+ return w_buffer_mapped_data::make(
+ internal::w_raw_tag{},
+ raw(),
+ w_buffer_map_flags::Write
+ );
+ }
+
+ /**
+ * @brief map to read-only data region.
+ */
+ [[nodiscard]] auto map_data_read() const
+ {
+ // NOTE unfortunately raw methods need pointer to non-const data, thus const_cast.
+ return w_buffer_mapped_data::make(
+ internal::w_raw_tag{},
+ const_cast(raw()),
+ w_buffer_map_flags::Read
+ );
+ }
+
+ /**
+ * @brief map to readable-writable data region.
+ */
+ [[nodiscard]] auto map_data()
+ {
+ return w_buffer_mapped_data::make(internal::w_raw_tag{}, raw());
+ }
+
+ /**
+ * @brief map to readable data region.
+ */
+ [[nodiscard]] auto map_data() const
+ {
+ return map_data_read();
+ }
+
+private:
+ explicit w_buffer(internal::w_raw_tag, GstBuffer* p_buffer) noexcept
+ : w_wrapper(p_buffer)
+ {}
+};
+
+} // namespace wolf::media::gst
diff --git a/wolf/media/gst/core/w_bus.cpp b/wolf/media/gst/core/w_bus.cpp
new file mode 100644
index 000000000..6dfb9a1b3
--- /dev/null
+++ b/wolf/media/gst/core/w_bus.cpp
@@ -0,0 +1 @@
+#include "media/gst/core/w_bus.hpp"
diff --git a/wolf/media/gst/core/w_bus.hpp b/wolf/media/gst/core/w_bus.hpp
new file mode 100644
index 000000000..00227882c
--- /dev/null
+++ b/wolf/media/gst/core/w_bus.hpp
@@ -0,0 +1,95 @@
+#pragma once
+
+#include "media/gst/core/w_signal_handler.hpp"
+#include "media/gst/core/w_message.hpp"
+#include "media/gst/internal/w_wrapper.hpp"
+
+#include
+
+namespace wolf::media::gst {
+
+/**
+ * @brief wrapper of GstBus. a bus to send messages to and notify listeners.
+ */
+class w_bus : public w_wrapper
+{
+ friend class internal::w_raw_access;
+
+public:
+ w_bus() = delete;
+
+ w_bus(const w_bus&) = delete;
+ w_bus(w_bus&&) noexcept = default;
+
+ w_bus& operator=(const w_bus&) = delete;
+ w_bus& operator=(w_bus&&) noexcept = default;
+
+ ~w_bus() noexcept
+ {
+ for (auto i = 0u; i < _watch_count; ++i) {
+ gst_bus_remove_signal_watch(raw());
+ }
+ }
+
+ /**
+ * @brief hook a listener handler on message signal.
+ * @param p_handler message listener handler. it will be passed `w_nonowning`.
+ */
+ template
+ void hook_message(F&& p_handler)
+ {
+ ensure_watch();
+ constexpr auto invoker = +[](GstBus* /* p_self */, GstMessage* p_message, gpointer p_callee) {
+ auto& func = (*static_cast(p_callee));
+ func(internal::w_raw_access::from_raw>(p_message));
+ };
+ _sighandlers.message.hook("message", invoker, std::forward(p_handler));
+ }
+
+ /**
+ * @brief unhook the message listener handler on message signal.
+ */
+ void unhook_message()
+ {
+ if (_sighandlers.message.unhook()) {
+ gst_bus_remove_signal_watch(raw());
+ --_watch_count;
+ }
+ }
+
+private:
+ explicit w_bus(internal::w_raw_tag, GstBus* p_bus_raw) noexcept
+ : w_wrapper(p_bus_raw)
+ , _sighandlers(G_OBJECT(p_bus_raw))
+ {}
+
+ /**
+ * @brief make sure there is at least one signal watch.
+ */
+ void ensure_watch() noexcept
+ {
+ if (_watch_count == 0) {
+ watch();
+ }
+ }
+
+ /**
+ * @brief add a signal watch so a listener can be added.
+ */
+ void watch() noexcept
+ {
+ gst_bus_add_signal_watch(raw());
+ ++_watch_count;
+ }
+
+ // signals
+ struct signal_handler_set {
+ w_signal_handler> message;
+
+ signal_handler_set(auto* p_rawptr) : message(p_rawptr) {}
+ } _sighandlers;
+
+ std::size_t _watch_count = 0;
+};
+
+} // namespace wolf::media::gst
diff --git a/wolf/media/gst/core/w_caps.cpp b/wolf/media/gst/core/w_caps.cpp
new file mode 100644
index 000000000..2fcfeef1a
--- /dev/null
+++ b/wolf/media/gst/core/w_caps.cpp
@@ -0,0 +1 @@
+#include "media/gst/core/w_caps.hpp"
diff --git a/wolf/media/gst/core/w_caps.hpp b/wolf/media/gst/core/w_caps.hpp
new file mode 100644
index 000000000..b560ea9a0
--- /dev/null
+++ b/wolf/media/gst/core/w_caps.hpp
@@ -0,0 +1,86 @@
+#pragma once
+
+#include "wolf.hpp"
+
+#include "media/gst/internal/w_common.hpp"
+#include "media/gst/internal/w_wrapper.hpp"
+#include "media/gst/core/w_structure.hpp"
+
+#include
+
+#include
+
+namespace wolf::media::gst {
+
+/**
+ * @brief wrapper of GstCaps, gstreamer capabilities concept.
+ */
+class w_caps : public w_wrapper
+{
+ friend class internal::w_raw_access;
+
+public:
+ /**
+ * @brief make an empty caps.
+ * @return an instance of w_caps on success.
+ */
+ [[nodiscard]] static auto make() -> boost::leaf::result
+ {
+ auto caps_raw = gst_caps_new_empty();
+ if (!caps_raw) {
+ return W_FAILURE(std::errc::operation_canceled,
+ "couldn't make caps.");
+ }
+
+ return w_caps(internal::w_raw_tag{}, caps_raw);
+ }
+
+ /**
+ * @brief make a caps that acceps any kind of media.
+ * @return an instance of w_caps on success.
+ */
+ [[nodiscard]] static auto make_any() -> boost::leaf::result
+ {
+ auto caps_raw = gst_caps_new_any();
+ if (!caps_raw) {
+ return W_FAILURE(std::errc::operation_canceled,
+ "couldn't make caps as any.");
+ }
+
+ return w_caps(internal::w_raw_tag{}, caps_raw);
+ }
+
+ /**
+ * @brief make an empty caps with media name.
+ * @return an instance of w_caps on success.
+ */
+ [[nodiscard]] static auto make_simple(const char* p_media_name)
+ -> boost::leaf::result
+ {
+ auto caps_raw = gst_caps_new_empty_simple(p_media_name);
+ if (!caps_raw) {
+ return W_FAILURE(
+ std::errc::operation_canceled,
+ "couldn't make caps with given media name."
+ );
+ }
+
+ return w_caps(internal::w_raw_tag{}, caps_raw);
+ }
+
+ /**
+ * @brief add a capability structure.
+ * @param p_structure a capability structure.
+ */
+ void add(w_structure&& p_structure)
+ {
+ gst_caps_append_structure(raw(), internal::w_raw_access::disown_raw(p_structure));
+ }
+
+private:
+ w_caps(internal::w_raw_tag, GstCaps* p_caps_raw) noexcept
+ : w_wrapper(p_caps_raw)
+ {}
+};
+
+} // namespace wolf::media::gst
diff --git a/wolf/media/gst/core/w_clock.cpp b/wolf/media/gst/core/w_clock.cpp
new file mode 100644
index 000000000..b11285840
--- /dev/null
+++ b/wolf/media/gst/core/w_clock.cpp
@@ -0,0 +1 @@
+#include "media/gst/core/w_clock.hpp"
diff --git a/wolf/media/gst/core/w_clock.hpp b/wolf/media/gst/core/w_clock.hpp
new file mode 100644
index 000000000..6b384c146
--- /dev/null
+++ b/wolf/media/gst/core/w_clock.hpp
@@ -0,0 +1,36 @@
+#pragma once
+
+#include "media/gst/internal/w_wrapper.hpp"
+
+#include
+
+namespace wolf::media::gst {
+
+/**
+ * @brief wrapper of GstClock.
+ *
+ * @note this class is not user constructible. it's nonowning by default.
+ */
+class w_clock : public w_wrapper
+{
+ friend class internal::w_raw_access;
+
+public:
+ w_clock() = delete;
+
+ /**
+ * @brief get time in nanoseconds.
+ * @return time in nanoseconds.
+ */
+ std::size_t get_time() noexcept
+ {
+ return gst_clock_get_time(raw());
+ }
+
+private:
+ explicit w_clock(internal::w_raw_tag, GstClock* p_clock_raw) noexcept
+ : w_wrapper(p_clock_raw)
+ {}
+};
+
+} // namespace wolf::media::gst
diff --git a/wolf/media/gst/core/w_element.cpp b/wolf/media/gst/core/w_element.cpp
new file mode 100644
index 000000000..3ae806735
--- /dev/null
+++ b/wolf/media/gst/core/w_element.cpp
@@ -0,0 +1,29 @@
+#include "media/gst/core/w_element.hpp"
+
+namespace wolf::media::gst {
+
+auto w_element::make(const char *p_factory_name) -> boost::leaf::result
+{
+ auto element_factory_raw = gst_element_factory_find(p_factory_name);
+ if (!element_factory_raw) {
+ auto err_msg = wolf::format("couldn't find factory name: {}", p_factory_name);
+ return W_FAILURE(std::errc::operation_canceled, err_msg);
+ }
+
+ auto element_raw = gst_element_factory_create(element_factory_raw, nullptr);
+ if (!element_raw) {
+ auto err_msg = wolf::format(
+ "couldn't create the element with given factory name: {}",
+ p_factory_name
+ );
+ return W_FAILURE(std::errc::operation_canceled, err_msg);
+ }
+
+ if (G_IS_INITIALLY_UNOWNED(element_raw)) {
+ gst_object_ref_sink(element_raw);
+ }
+
+ return w_element(internal::w_raw_tag{}, element_raw);
+}
+
+} // namespace wolf::media::gst
diff --git a/wolf/media/gst/core/w_element.hpp b/wolf/media/gst/core/w_element.hpp
new file mode 100644
index 000000000..ce80116ac
--- /dev/null
+++ b/wolf/media/gst/core/w_element.hpp
@@ -0,0 +1,85 @@
+#pragma once
+
+#include "wolf.hpp"
+
+#include "media/gst/internal/w_common.hpp"
+#include "media/gst/internal/w_wrapper.hpp"
+#include "media/gst/core/w_pad.hpp"
+#include "media/gst/core/w_clock.hpp"
+
+#include
+
+#include
+
+namespace wolf::media::gst {
+
+/**
+ * @brief wrapper of GstElement, gstreamer elements process stream data.
+ *
+ * elements combine to make dataflow pipeline graphs (DAG)
+ * to process one or more media stream(s).
+ */
+class w_element : public w_wrapper
+{
+ friend class internal::w_raw_access;
+
+public:
+ /**
+ * @brief make an element by given factory name.
+ * @param p_factory_name element's factory name.
+ * @return an instance of element of given name on success.
+ */
+ [[nodiscard]] static auto make(const char* p_factory_name)
+ -> boost::leaf::result;
+
+ w_element(const w_element&) = delete;
+ w_element(w_element&&) noexcept = default;
+
+ w_element& operator=(const w_element&) = delete;
+ w_element& operator=(w_element&&) noexcept = default;
+
+ virtual ~w_element() = default;
+
+ /**
+ * @brief get a request=pad with given name.
+ */
+ [[nodiscard]] auto request_pad(const char* p_pad_name)
+ -> boost::leaf::result
+ {
+ auto raw_pad = gst_element_request_pad_simple(raw(), p_pad_name);
+ if (!raw_pad) {
+ auto err_msg = wolf::format("request for pad `{}` failed.", p_pad_name);
+ return W_FAILURE(std::errc::operation_canceled, err_msg);
+ }
+ return internal::w_raw_access::from_raw(raw_pad);
+ }
+
+ /**
+ * @brief get an always=pad with given name.
+ */
+ [[nodiscard]] auto always_pad(const char* p_pad_name)
+ -> boost::leaf::result
+ {
+ auto raw_pad = gst_element_get_static_pad(raw(), p_pad_name);
+ if (!raw_pad) {
+ auto err_msg = wolf::format("couldn't find always pad: `{}`", p_pad_name);
+ return W_FAILURE(std::errc::operation_canceled, err_msg);
+ }
+ return internal::w_raw_access::from_raw(raw_pad);
+ }
+
+ /**
+ * @brief get element's clock.
+ */
+ w_nonowning clock() noexcept
+ {
+ return internal::w_raw_access::from_raw>(raw()->clock);
+ }
+
+private:
+ explicit w_element(internal::w_raw_tag, GstElement* p_element) noexcept
+ : w_wrapper(p_element)
+ {}
+};
+
+} // namespace wolf::media::gst
diff --git a/wolf/media/gst/core/w_element_factory.cpp b/wolf/media/gst/core/w_element_factory.cpp
new file mode 100644
index 000000000..60cf086a7
--- /dev/null
+++ b/wolf/media/gst/core/w_element_factory.cpp
@@ -0,0 +1 @@
+#include "media/gst/core/w_element_factory.hpp"
diff --git a/wolf/media/gst/core/w_element_factory.hpp b/wolf/media/gst/core/w_element_factory.hpp
new file mode 100644
index 000000000..2da45f481
--- /dev/null
+++ b/wolf/media/gst/core/w_element_factory.hpp
@@ -0,0 +1,56 @@
+#pragma once
+
+#include "media/gst/internal/w_common.hpp"
+#include "media/gst/core/w_element.hpp"
+
+#include
+
+#include
+#include
+#include
+
+namespace wolf::media::gst {
+
+/**
+ * @brief gstreamer's element factory to create elements.
+ */
+class w_element_factory
+{
+public:
+ w_element_factory() = delete;
+
+ /**
+ * @brief make an element from given gstreamer launch command.
+ * @param p_launch_str a gstreamer lauch command.
+ */
+ [[nodiscard]] static auto make_from_launch_str(const char* p_launch_str)
+ -> boost::leaf::result
+ {
+ auto element_raw = gst_parse_launch(p_launch_str, nullptr);
+ if (!element_raw) {
+ return W_FAILURE(std::errc::operation_canceled,
+ "couldn't create element from launch string.");
+ }
+
+ return internal::w_raw_access::from_raw(element_raw);
+ }
+
+ /**
+ * @brief make a an element of given factory name, with given element name.
+ * @param p_factory_name name of element module.
+ * @param p_element_name custom name for element to reference later. (nullable)
+ * @return an element on success.
+ */
+ [[nodiscard]] static auto make_simple(const char* p_factory_name, const char* p_element_name)
+ -> boost::leaf::result
+ {
+ auto element_raw = gst_element_factory_make(p_factory_name, p_element_name);
+ if (!element_raw) {
+ auto err_msg = wolf::format("couldn't create element with factory name: {}", p_factory_name);
+ return W_FAILURE(std::errc::operation_canceled, err_msg);
+ }
+ return internal::w_raw_access::from_raw(element_raw);
+ }
+};
+
+} // namespace wolf::media::gst
diff --git a/wolf/media/gst/core/w_format.cpp b/wolf/media/gst/core/w_format.cpp
new file mode 100644
index 000000000..75a7e6775
--- /dev/null
+++ b/wolf/media/gst/core/w_format.cpp
@@ -0,0 +1 @@
+#include "media/gst/core/w_format.hpp"
diff --git a/wolf/media/gst/core/w_format.hpp b/wolf/media/gst/core/w_format.hpp
new file mode 100644
index 000000000..35b88edd8
--- /dev/null
+++ b/wolf/media/gst/core/w_format.hpp
@@ -0,0 +1,18 @@
+#pragma once
+
+#include
+
+namespace wolf::media::gst {
+
+/** wrapper of GstFormat */
+enum class w_format
+{
+ Undefined = GST_FORMAT_UNDEFINED,
+ Default = GST_FORMAT_DEFAULT,
+ Bytes = GST_FORMAT_BYTES,
+ Time = GST_FORMAT_TIME,
+ Buffers = GST_FORMAT_BUFFERS,
+ Percent = GST_FORMAT_PERCENT
+};
+
+} // namespace wolf::media::gst
diff --git a/wolf/media/gst/core/w_mainloop.cpp b/wolf/media/gst/core/w_mainloop.cpp
new file mode 100644
index 000000000..8082386b3
--- /dev/null
+++ b/wolf/media/gst/core/w_mainloop.cpp
@@ -0,0 +1 @@
+#include "media/gst/core/w_mainloop.hpp"
diff --git a/wolf/media/gst/core/w_mainloop.hpp b/wolf/media/gst/core/w_mainloop.hpp
new file mode 100644
index 000000000..f07bf5a20
--- /dev/null
+++ b/wolf/media/gst/core/w_mainloop.hpp
@@ -0,0 +1,83 @@
+#pragma once
+
+#include "wolf.hpp"
+
+#include "media/gst/internal/w_wrapper.hpp"
+
+#include
+
+#include
+#include
+#include
+#include
+
+namespace wolf::media::gst {
+
+/**
+ * @brief wrapper of GMainLoop, GLib's mainloop/eventloop facility.
+ */
+class w_mainloop : public w_wrapper
+{
+public:
+ /**
+ * @brief create a simple instance of mainloop.
+ * @return a w_mainloop instance on success.
+ */
+ [[nodiscard]] static auto make() -> boost::leaf::result
+ {
+ auto main_loop_raw = g_main_loop_new(nullptr, false);
+ if (!main_loop_raw) {
+ return W_FAILURE(std::errc::operation_canceled,
+ "mainloop construction error.");
+ }
+
+ return w_mainloop(internal::w_raw_tag{}, main_loop_raw);
+ }
+
+ /**
+ * @brief add a callable to be called on event loop idle times.
+ * @param p_func callable to be called on idle times.
+ * @return a sourceid to remove it later.
+ * @note `p_func` must have a consistent lifetime and address
+ * until either being removed by `idle_remove` or the instance
+ * of this class be destructed.
+ */
+ template
+ std::size_t idle_add(F& p_func)
+ {
+ constexpr auto invoker = +[](F* p_funcptr) { (*p_funcptr)(); };
+ return g_idle_add(GSourceFunc(invoker), std::addressof(p_func));
+ }
+
+ bool idle_remove(std::size_t p_sourceid)
+ {
+ if (!p_sourceid) {
+ return false;
+ }
+
+ return g_source_remove(gsl::narrow_cast(p_sourceid));
+ }
+
+ /**
+ * @brief run the main/event loop.
+ */
+ void run()
+ {
+ g_main_loop_run(raw());
+ }
+
+ /**
+ * @brief stop the main/event loop.
+ */
+ void stop()
+ {
+ g_main_loop_quit(raw());
+ }
+
+private:
+ w_mainloop(internal::w_raw_tag, GMainLoop* p_main_loop_raw) noexcept
+ : w_wrapper(p_main_loop_raw)
+ {}
+};
+
+} // namespace wolf::media::gst
diff --git a/wolf/media/gst/core/w_message.cpp b/wolf/media/gst/core/w_message.cpp
new file mode 100644
index 000000000..8d892575d
--- /dev/null
+++ b/wolf/media/gst/core/w_message.cpp
@@ -0,0 +1 @@
+#include "media/gst/core/w_message.hpp"
diff --git a/wolf/media/gst/core/w_message.hpp b/wolf/media/gst/core/w_message.hpp
new file mode 100644
index 000000000..5ddd29c76
--- /dev/null
+++ b/wolf/media/gst/core/w_message.hpp
@@ -0,0 +1,143 @@
+#pragma once
+
+#include "media/gst/internal/w_common.hpp"
+#include "media/gst/internal/w_wrapper.hpp"
+
+#include
+
+#include
+
+namespace wolf::media::gst {
+
+class w_message;
+
+/** type of GstMessage */
+enum class w_message_type : std::size_t
+{
+ Unknown = 0,
+ EOS,
+ Error,
+ Warning,
+ Info
+};
+
+//- message structs
+
+/** the unknown variant of GstMessage */
+struct w_message_unknown {};
+
+/** the end-of-stream variant of GstMessage */
+struct w_message_eos {};
+
+/** the error message variant of GstMessage */
+struct w_message_error
+{
+ friend class w_message;
+
+public:
+ w_message_error() = delete;
+
+ w_message_error(const w_message_error&) = delete;
+
+ ~w_message_error() noexcept
+ {
+ if (_error) {
+ g_clear_error(&_error);
+ }
+
+ if (_debug_info) {
+ g_free(_debug_info);
+ }
+ }
+
+ /**
+ * @brief print the error message to standard output.
+ */
+ void print() const
+ {
+ g_printerr(
+ "Error received from element %s: %s\n",
+ GST_OBJECT_NAME(_msg->src),
+ _error->message
+ );
+ g_printerr(
+ "Debugging information: %s\n",
+ _debug_info ? _debug_info : "none"
+ );
+ }
+
+private:
+ /**
+ * @brief parse the message to extract error info.
+ */
+ explicit w_message_error(internal::w_raw_tag, GstMessage* p_msg_raw)
+ : _msg(p_msg_raw)
+ {
+ gst_message_parse_error(p_msg_raw, &_error, &_debug_info);
+ }
+
+ GstMessage* _msg = nullptr; //< non-owning.
+ GError* _error = nullptr;
+ gchar* _debug_info = nullptr;
+};
+
+/**
+ * @brief wrapper of GstMessage. gstreamer's ultimate message struct.
+ *
+ * there are so many different kinds and variants of message,
+ * and all are represented by w_message.
+ *
+ * for ease of use, there are helper message representative classes,
+ * which on visit the appropriate one will be created and passed to
+ * given visitor.
+ */
+class w_message : public w_wrapper
+{
+ friend class internal::w_raw_access;
+
+public:
+ // not supported yet.
+ w_message() = delete;
+
+// w_message_type type() const noexcept
+// {
+// return convert_message_type(GST_MESSAGE_TYPE(msg_));
+// }
+
+ /**
+ * @brief visit the message based on its type as the helper representative type.
+ * @param p_visitor visitor to visit message variant.
+ */
+ template
+ auto visit(VisitorF&& p_visitor)
+ {
+ return raw_visit(std::forward(p_visitor), raw());
+ }
+
+private:
+ explicit w_message(internal::w_raw_tag, GstMessage* p_msg_raw)
+ : w_wrapper(p_msg_raw)
+ {}
+
+ /**
+ * @brief create and visit appropriate repr variant by given raw message and visitor.
+ * @param p_visitor visitor to visit message variant.
+ * @param p_msg_raw raw message pointer.
+ */
+ template
+ static auto raw_visit(VisitorF&& p_visitor, GstMessage* p_msg_raw)
+ {
+ switch (GST_MESSAGE_TYPE(p_msg_raw)) {
+ case GST_MESSAGE_EOS:
+ return std::forward(w_message_eos{});
+ case GST_MESSAGE_ERROR:
+ return std::forward(p_visitor)(
+ w_message_error(internal::w_raw_tag{}, p_msg_raw)
+ );
+ default: // GST_MESSAGE_UNKNOWN
+ return std::forward(p_visitor)(w_message_unknown{});
+ }
+ }
+};
+
+} // namespace wolf::media::gst
diff --git a/wolf/media/gst/core/w_nonowning.cpp b/wolf/media/gst/core/w_nonowning.cpp
new file mode 100644
index 000000000..0f4014720
--- /dev/null
+++ b/wolf/media/gst/core/w_nonowning.cpp
@@ -0,0 +1 @@
+#include "media/gst/core/w_nonowning.hpp"
diff --git a/wolf/media/gst/core/w_nonowning.hpp b/wolf/media/gst/core/w_nonowning.hpp
new file mode 100644
index 000000000..d2d14fe09
--- /dev/null
+++ b/wolf/media/gst/core/w_nonowning.hpp
@@ -0,0 +1,71 @@
+#pragma once
+
+#include "media/gst/internal/w_common.hpp"
+
+#include
+
+namespace wolf::media::gst {
+
+/**
+ * @brief nonowning view of `T` pointing to same resource
+ * given to make nonowning view of. similar to std::string_view/std::span.
+ *
+ * it's for sharing things like element.clock() returning Clock instance,
+ * which the instance does not own the clock's lifetime, but
+ * points to same resource to keep track of.
+ *
+ * this class is wrapper acting as a pointer to an instance of `T`
+ * with shared resource beneath.
+ *
+ * @tparam T underlying type of nonowning view.
+ *
+ * @note make sure the given object lives longer than this nonowning view of it,
+ * or any access will be undefined behavior.
+ * similar to std::string_view or std::span.
+ */
+template
+class w_nonowning
+{
+ friend class internal::w_raw_access;
+
+ using value_type = std::remove_cvref_t;
+
+public:
+ /**
+ * @brief implicit constructor from a value of wrapping type.
+ * @param p_value value to make nonowing view of.
+ */
+ w_nonowning(value_type& p_value)
+ : w_nonowning(internal::w_raw_access::raw(p_value))
+ {}
+
+ w_nonowning(const w_nonowning&) = default;
+ w_nonowning(w_nonowning&&) noexcept = default;
+
+ w_nonowning& operator=(const w_nonowning&) = default;
+ w_nonowning& operator=(w_nonowning&&) noexcept = default;
+
+ ~w_nonowning() noexcept
+ {
+ // disown raw resource before resource
+ // be released/free'ed by T's destructor.
+ internal::w_raw_access::disown_raw(_value);
+ }
+
+ value_type* operator->() noexcept { return std::addressof(_value); }
+ const value_type* operator->() const noexcept { return std::addressof(_value); }
+
+ value_type& operator*() noexcept { return _value; }
+ const value_type& operator*() const noexcept { return _value; }
+
+private:
+ template
+ w_nonowning(internal::w_raw_tag, RawT* p_rawptr) noexcept
+ : _value(internal::w_raw_access::from_raw(p_rawptr))
+ {}
+
+ value_type _value;
+};
+
+
+} // namespace wolf::media::gst
diff --git a/wolf/media/gst/core/w_pad.cpp b/wolf/media/gst/core/w_pad.cpp
new file mode 100644
index 000000000..fe5335021
--- /dev/null
+++ b/wolf/media/gst/core/w_pad.cpp
@@ -0,0 +1 @@
+#include "media/gst/core/w_pad.hpp"
diff --git a/wolf/media/gst/core/w_pad.hpp b/wolf/media/gst/core/w_pad.hpp
new file mode 100644
index 000000000..bd7f8958b
--- /dev/null
+++ b/wolf/media/gst/core/w_pad.hpp
@@ -0,0 +1,39 @@
+#pragma once
+
+#include "media/gst/internal/w_common.hpp"
+#include "media/gst/internal/w_wrapper.hpp"
+
+#include
+
+#include
+
+namespace wolf::media::gst {
+
+/** gstreamer pad availablity */
+enum class w_availability
+{
+ Always,
+ Request,
+ Sometimes
+};
+
+/**
+ * @brief wrapper of GstPad.
+ *
+ * each gstreamer element has source and/or sink pads,
+ * which they connect by to each other with.
+ */
+class w_pad : public w_wrapper
+{
+ friend class internal::w_raw_access;
+
+public:
+ // so far nothing...
+
+private:
+ explicit w_pad(internal::w_raw_tag, GstPad* p_pad) noexcept
+ : w_wrapper(p_pad)
+ {}
+};
+
+} // namespace wolf::media::gst
diff --git a/wolf/media/gst/core/w_pipeline.cpp b/wolf/media/gst/core/w_pipeline.cpp
new file mode 100644
index 000000000..681ec3850
--- /dev/null
+++ b/wolf/media/gst/core/w_pipeline.cpp
@@ -0,0 +1,51 @@
+#include "media/gst/core/w_pipeline.hpp"
+
+namespace wolf::media::gst {
+
+auto w_pipeline::make(const char *p_name)
+ -> boost::leaf::result
+{
+ auto pipeline_raw = gst_pipeline_new(p_name);
+ if (!pipeline_raw) {
+ return W_FAILURE(std::errc::operation_canceled,
+ wolf::format("couldn't create pipeline: {}", p_name));
+ }
+ return w_pipeline(internal::w_raw_tag{}, pipeline_raw);
+}
+
+auto w_pipeline::get_bus() -> boost::leaf::result
+{
+ auto bus_raw = gst_element_get_bus(raw());
+ if (!bus_raw) {
+ return W_FAILURE(std::errc::operation_canceled,
+ "couldn't get pipeline's bus.");
+ }
+ return internal::w_raw_access::from_raw(bus_raw);
+}
+
+bool w_pipeline::bin(w_flow_path &p_flow)
+{
+ for (auto& element : p_flow) {
+ if (!bin(*element)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool w_pipeline::link(w_flow_path &p_flow)
+{
+ auto* last = p_flow.first().get();
+ for (int i = 1; i < p_flow.size(); ++i) {
+ if (!link(*last, *p_flow[i])) {
+ return false;
+ }
+
+ last = p_flow[i].get();
+ }
+
+ return true;
+}
+
+} // namespace wolf::media::gst
diff --git a/wolf/media/gst/core/w_pipeline.hpp b/wolf/media/gst/core/w_pipeline.hpp
new file mode 100644
index 000000000..18c7a6699
--- /dev/null
+++ b/wolf/media/gst/core/w_pipeline.hpp
@@ -0,0 +1,144 @@
+#pragma once
+
+#include "wolf.hpp"
+
+#include "media/gst/w_flow.hpp"
+#include "media/gst/core/w_element.hpp"
+#include "media/gst/core/w_bus.hpp"
+#include "media/gst/internal/w_wrapper.hpp"
+
+#include
+
+#include
+#include
+
+namespace wolf::media::gst {
+
+/**
+ * @brief wrapper of GstElement, providing gstreamer pipeline concept.
+ */
+class w_pipeline : public w_wrapper
+{
+public:
+ /**
+ * @brief make an instance of pipeline with given name.
+ * @param p_name pipeline's name.
+ * @return an instance of pipeline on success.
+ */
+ [[nodiscard]] static auto make(const char* p_name)
+ -> boost::leaf::result;
+
+ /**
+ * @brief get pipeline's bus.
+ */
+ [[nodiscard]] auto get_bus() -> boost::leaf::result;
+
+ /**
+ * @brief add given elements to pipeline's bin.
+ * @param p_args pack of elements.
+ * @return boolean indicating success or failure.
+ */
+ template
+ requires (sizeof...(Args) > 1)
+ bool bin(Args&& ...p_args)
+ {
+ return (true && ... && bin(std::forward(p_args)));
+ }
+
+ /**
+ * @brief add elements from given flow path to pipeline's bin.
+ * @param p_flow elements flow path.
+ * @return boolean indicating success or failure.
+ */
+ bool bin(w_flow_path& p_flow);
+
+ /**
+ * @brief add given single element to pipeline's bin.
+ * @param p_element single element.
+ * @return boolean indicating success or failure.
+ */
+ bool bin(w_element& p_element)
+ {
+ auto element_raw = internal::w_raw_access::raw(p_element);
+ p_element.parented();
+ return gst_bin_add(GST_BIN(raw()), element_raw);
+ }
+
+ /**
+ * @brief link a pack of given linkables (element or pad) together after each other.
+ * @param p_a source linkable to link with `p_b`.
+ * @param p_b sink linkable to link with `p_a`.
+ * @param p_rest pack of rest of linkables to be linked after `p_b` as source.
+ * @return boolean indicating success or failure.
+ */
+ template
+ bool link(T&& p_a, U&& p_b, Rest&& ...p_rest)
+ {
+ auto raw_a = internal::w_raw_access::raw(p_a);
+ auto raw_b = internal::w_raw_access::raw(p_b);
+
+ if (!gst_element_link(raw_a, raw_b)) {
+ return false;
+ }
+
+ if constexpr (sizeof...(Rest) > 0) {
+ return link(std::forward(p_b), std::forward(p_rest)...);
+ }
+
+ return true;
+ }
+
+ /**
+ * @brief link two elements.
+ * @param p_src source element.
+ * @param p_sink sink element.
+ * @return boolean indicating success or failure.
+ */
+ bool link(w_element& p_src, w_element& p_sink)
+ {
+ return gst_element_link(
+ internal::w_raw_access::raw(p_src),
+ internal::w_raw_access::raw(p_sink)
+ );
+ }
+
+ /**
+ * @brief link a flow path's elements after each other.
+ * @param p_flow flow path set of elements.
+ * @return boolean indicating success or failure.
+ */
+ bool link(w_flow_path& p_flow);
+
+ /**
+ * @brief set the pipeline to play state.
+ * @return boolean indicating success or failure.
+ */
+ bool play()
+ {
+ return static_cast(gst_element_set_state(raw(), GST_STATE_PLAYING));
+ }
+
+ /**
+ * @brief set the pipeline to pause state.
+ * @return boolean indicating success or failure.
+ */
+ bool pause()
+ {
+ return static_cast(gst_element_set_state(raw(), GST_STATE_PAUSED));
+ }
+
+ /**
+ * @brief set the pipeline to stop state.
+ * @return boolean indicating success or failure.
+ */
+ bool stop() {
+ return static_cast